4 * Copyright 1993 Alexandre Julliard
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
27 #ifdef HAVE_SYS_POLL_H
30 #include <X11/Xatom.h>
31 #include <X11/keysym.h>
33 #include <X11/Xresource.h>
34 #include <X11/Xutil.h>
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
49 /* avoid conflict with field names in included win32 headers */
51 #include "shlobj.h" /* DROPFILES */
54 #include "wine/server.h"
55 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(event);
59 extern BOOL ximInComposeMode;
61 #define DndNotDnd -1 /* OffiX drag&drop */
73 #define DndURL 128 /* KDE drag&drop */
76 static void X11DRV_FocusIn( HWND hwnd, XEvent *event );
77 static void X11DRV_FocusOut( HWND hwnd, XEvent *event );
78 static void X11DRV_Expose( HWND hwnd, XEvent *event );
79 static void X11DRV_MapNotify( HWND hwnd, XEvent *event );
80 static void X11DRV_ConfigureNotify( HWND hwnd, XEvent *event );
81 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *event );
82 static void X11DRV_ClientMessage( HWND hwnd, XEvent *event );
86 int type; /* event type */
87 x11drv_event_handler handler; /* corresponding handler function */
90 #define MAX_EVENT_HANDLERS 64
92 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
94 /* list must be sorted by event type */
95 { KeyPress, X11DRV_KeyEvent },
96 { KeyRelease, X11DRV_KeyEvent },
97 { ButtonPress, X11DRV_ButtonPress },
98 { ButtonRelease, X11DRV_ButtonRelease },
99 { MotionNotify, X11DRV_MotionNotify },
100 { EnterNotify, X11DRV_EnterNotify },
102 { FocusIn, X11DRV_FocusIn },
103 { FocusOut, X11DRV_FocusOut },
104 { KeymapNotify, X11DRV_KeymapNotify },
105 { Expose, X11DRV_Expose },
108 /* VisibilityNotify */
110 { DestroyNotify, X11DRV_DestroyNotify },
112 { MapNotify, X11DRV_MapNotify },
115 { ConfigureNotify, X11DRV_ConfigureNotify },
116 /* ConfigureRequest */
119 /* CirculateNotify */
120 /* CirculateRequest */
121 { PropertyNotify, X11DRV_PropertyNotify },
122 { SelectionClear, X11DRV_SelectionClear },
123 { SelectionRequest, X11DRV_SelectionRequest },
124 /* SelectionNotify */
126 { ClientMessage, X11DRV_ClientMessage },
127 { MappingNotify, X11DRV_MappingNotify },
130 static int nb_event_handlers = 18; /* change this if you add handlers above */
133 /* return the name of an X event */
134 static const char *dbgstr_event( int type )
136 static const char * const event_names[] =
138 "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
139 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
140 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
141 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
142 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
143 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
144 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
145 "ClientMessage", "MappingNotify"
148 if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
149 return wine_dbg_sprintf( "Extension event %d", type );
153 /***********************************************************************
156 * Find the handler for a given event type. Caller must hold the x11 lock.
158 static inline x11drv_event_handler find_handler( int type )
160 int min = 0, max = nb_event_handlers - 1;
164 int pos = (min + max) / 2;
165 if (handlers[pos].type == type) return handlers[pos].handler;
166 if (handlers[pos].type > type) max = pos - 1;
173 /***********************************************************************
174 * X11DRV_register_event_handler
176 * Register a handler for a given event type.
177 * If already registered, overwrite the previous handler.
179 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
185 max = nb_event_handlers - 1;
188 int pos = (min + max) / 2;
189 if (handlers[pos].type == type)
191 handlers[pos].handler = handler;
194 if (handlers[pos].type > type) max = pos - 1;
197 /* insert it between max and min */
198 memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
199 handlers[min].type = type;
200 handlers[min].handler = handler;
202 assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
205 TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
209 /***********************************************************************
212 static Bool filter_event( Display *display, XEvent *event, char *arg )
214 ULONG_PTR mask = (ULONG_PTR)arg;
216 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
224 return (mask & QS_KEY) != 0;
227 return (mask & QS_MOUSEBUTTON) != 0;
231 return (mask & QS_MOUSEMOVE) != 0;
233 return (mask & QS_PAINT) != 0;
238 case ConfigureNotify:
241 return (mask & QS_POSTMESSAGE) != 0;
243 return (mask & QS_SENDMESSAGE) != 0;
248 enum event_merge_action
250 MERGE_DISCARD, /* discard the old event */
251 MERGE_HANDLE, /* handle the old event */
252 MERGE_KEEP /* keep the old event for future merging */
255 /***********************************************************************
258 * Try to merge 2 consecutive events.
260 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
264 case ConfigureNotify:
267 case ConfigureNotify:
268 if (prev->xany.window == next->xany.window)
270 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
271 return MERGE_DISCARD;
280 if (prev->xany.window == next->xany.window && next->type == MotionNotify)
282 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
283 return MERGE_DISCARD;
291 /***********************************************************************
294 static inline void call_event_handler( Display *display, XEvent *event )
297 x11drv_event_handler handler;
299 struct x11drv_thread_data *thread_data;
301 if (!(handler = find_handler( event->type )))
303 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
304 return; /* no handler, ignore it */
307 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
308 hwnd = 0; /* not for a registered window */
309 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
311 TRACE( "%s for hwnd/window %p/%lx\n",
312 dbgstr_event( event->type ), hwnd, event->xany.window );
314 thread_data = x11drv_thread_data();
315 prev = thread_data->current_event;
316 thread_data->current_event = event;
317 handler( hwnd, event );
318 thread_data->current_event = prev;
323 /***********************************************************************
326 static int process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
328 XEvent event, prev_event;
330 enum event_merge_action action = MERGE_DISCARD;
334 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
337 if (XFilterEvent( &event, None ))
340 * SCIM on linux filters key events strangely. It does not filter the
341 * KeyPress events for these keys however it does filter the
342 * KeyRelease events. This causes wine to become very confused as
343 * to the keyboard state.
345 * We need to let those KeyRelease events be processed so that the
346 * keyboard state is correct.
348 if (event.type == KeyRelease)
351 XKeyEvent *keyevent = &event.xkey;
353 XLookupString(keyevent, NULL, 0, &keysym, NULL);
354 if (!(keysym == XK_Shift_L ||
355 keysym == XK_Shift_R ||
356 keysym == XK_Control_L ||
357 keysym == XK_Control_R ||
358 keysym == XK_Alt_R ||
359 keysym == XK_Alt_L ||
360 keysym == XK_Meta_R ||
361 keysym == XK_Meta_L))
362 continue; /* not a key we care about, ignore it */
365 continue; /* filtered, ignore it */
367 if (prev_event.type) action = merge_events( &prev_event, &event );
370 case MERGE_DISCARD: /* discard prev, keep new */
373 case MERGE_HANDLE: /* handle prev, keep new */
374 call_event_handler( display, &prev_event );
377 case MERGE_KEEP: /* handle new, keep prev for future merging */
378 call_event_handler( display, &event );
382 XFlush( gdi_display );
383 if (prev_event.type) call_event_handler( display, &prev_event );
385 if (count) TRACE( "processed %d events\n", count );
390 /***********************************************************************
391 * MsgWaitForMultipleObjectsEx (X11DRV.@)
393 DWORD CDECL X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
394 DWORD timeout, DWORD mask, DWORD flags )
397 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
401 if (!count && !timeout) return WAIT_TIMEOUT;
402 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
403 timeout, flags & MWMO_ALERTABLE );
406 if (data->current_event) mask = 0; /* don't process nested events */
408 if (process_events( data->display, filter_event, mask )) ret = count - 1;
409 else if (count || timeout)
411 ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
412 timeout, flags & MWMO_ALERTABLE );
413 if (ret == count - 1) process_events( data->display, filter_event, mask );
415 else ret = WAIT_TIMEOUT;
420 /***********************************************************************
421 * EVENT_x11_time_to_win32_time
423 * Make our timer and the X timer line up as best we can
424 * Pass 0 to retrieve the current adjustment value (times -1)
426 DWORD EVENT_x11_time_to_win32_time(Time time)
428 static DWORD adjust = 0;
429 DWORD now = GetTickCount();
432 if (! adjust && time != 0)
439 /* If we got an event in the 'future', then our clock is clearly wrong.
440 If we got it more than 10000 ms in the future, then it's most likely
441 that the clock has wrapped. */
444 if (ret > now && ((ret - now) < 10000) && time != 0)
455 /*******************************************************************
456 * can_activate_window
458 * Check if we can activate the specified window.
460 static inline BOOL can_activate_window( HWND hwnd )
462 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
463 if (!(style & WS_VISIBLE)) return FALSE;
464 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
465 if (style & WS_MINIMIZE) return FALSE;
466 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
467 if (hwnd == GetDesktopWindow()) return FALSE;
468 return !(style & WS_DISABLED);
472 /**********************************************************************
475 static void set_focus( Display *display, HWND hwnd, Time time )
480 TRACE( "setting foreground window to %p\n", hwnd );
481 SetForegroundWindow( hwnd );
484 if (focus) focus = GetAncestor( focus, GA_ROOT );
485 win = X11DRV_get_whole_window(focus);
489 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
491 XSetInputFocus( display, win, RevertToParent, time );
497 /**********************************************************************
498 * handle_wm_protocols
500 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
502 Atom protocol = (Atom)event->data.l[0];
504 if (!protocol) return;
506 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
508 /* Ignore the delete window request if the window has been disabled
509 * and we are in managed mode. This is to disallow applications from
510 * being closed by the window manager while in a modal state.
512 if (IsWindowEnabled(hwnd))
517 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
518 hSysMenu = GetSystemMenu(hwnd, FALSE);
521 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
522 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
525 if (GetActiveWindow() != hwnd)
527 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
528 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
529 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
532 case MA_NOACTIVATEANDEAT:
533 case MA_ACTIVATEANDEAT:
539 SetActiveWindow(hwnd);
542 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
546 /* Simulate clicking the caption Close button */
548 PostMessageW( hwnd, WM_NCLBUTTONDOWN, HTCLOSE, MAKELPARAM( pt.x, pt.y ) );
549 PostMessageW( hwnd, WM_LBUTTONUP, HTCLOSE, MAKELPARAM( pt.x, pt.y ) );
552 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
554 Time event_time = (Time)event->data.l[1];
555 HWND last_focus = x11drv_thread_data()->last_focus;
557 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
558 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
559 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
561 if (can_activate_window(hwnd))
563 /* simulate a mouse click on the caption to find out
564 * whether the window wants to be activated */
565 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
566 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
567 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
568 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
570 set_focus( event->display, hwnd, event_time );
574 else if (hwnd == GetDesktopWindow())
576 hwnd = GetForegroundWindow();
577 if (!hwnd) hwnd = last_focus;
578 if (!hwnd) hwnd = GetDesktopWindow();
579 set_focus( event->display, hwnd, event_time );
582 /* try to find some other window to give the focus to */
584 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
585 if (!hwnd) hwnd = GetActiveWindow();
586 if (!hwnd) hwnd = last_focus;
587 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
589 else if (protocol == x11drv_atom(_NET_WM_PING))
591 XClientMessageEvent xev;
594 TRACE("NET_WM Ping\n");
596 xev.window = DefaultRootWindow(xev.display);
597 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
599 /* this line is semi-stolen from gtk2 */
600 TRACE("NET_WM Pong\n");
605 static const char * const focus_details[] =
611 "NotifyNonlinearVirtual",
617 /**********************************************************************
620 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
622 XFocusChangeEvent *event = &xev->xfocus;
627 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
629 if (event->detail == NotifyPointer) return;
631 if ((xic = X11DRV_get_ic( hwnd )))
637 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
639 if (!can_activate_window(hwnd))
641 HWND hwnd = GetFocus();
642 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
643 if (!hwnd) hwnd = GetActiveWindow();
644 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
645 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
647 else SetForegroundWindow( hwnd );
651 /**********************************************************************
654 * Note: only top-level windows get FocusOut events.
656 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
658 XFocusChangeEvent *event = &xev->xfocus;
666 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
668 if (event->detail == NotifyPointer) return;
669 if (ximInComposeMode) return;
671 x11drv_thread_data()->last_focus = hwnd;
672 if ((xic = X11DRV_get_ic( hwnd )))
675 XUnsetICFocus( xic );
678 if (hwnd != GetForegroundWindow()) return;
679 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
681 /* don't reset the foreground window, if the window which is
682 getting the focus is a Wine window */
685 XGetInputFocus( event->display, &focus_win, &revert );
688 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
695 /* Abey : 6-Oct-99. Check again if the focus out window is the
696 Foreground window, because in most cases the messages sent
697 above must have already changed the foreground window, in which
698 case we don't have to change the foreground window to 0 */
699 if (hwnd == GetForegroundWindow())
701 TRACE( "lost focus, setting fg to desktop\n" );
702 SetForegroundWindow( GetDesktopWindow() );
708 /***********************************************************************
711 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
713 XExposeEvent *event = &xev->xexpose;
715 struct x11drv_win_data *data;
716 int flags = RDW_INVALIDATE | RDW_ERASE;
718 TRACE( "win %p (%lx) %d,%d %dx%d\n",
719 hwnd, event->window, event->x, event->y, event->width, event->height );
721 if (!(data = X11DRV_get_win_data( hwnd ))) return;
723 if (event->window == data->whole_window)
725 rect.left = data->whole_rect.left + event->x;
726 rect.top = data->whole_rect.top + event->y;
731 rect.left = data->client_rect.left + event->x;
732 rect.top = data->client_rect.top + event->y;
734 rect.right = rect.left + event->width;
735 rect.bottom = rect.top + event->height;
737 if (event->window != root_window)
739 SERVER_START_REQ( update_window_zorder )
741 req->window = wine_server_user_handle( hwnd );
742 req->rect.left = rect.left;
743 req->rect.top = rect.top;
744 req->rect.right = rect.right;
745 req->rect.bottom = rect.bottom;
746 wine_server_call( req );
750 /* make position relative to client area instead of parent */
751 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
752 flags |= RDW_ALLCHILDREN;
755 RedrawWindow( hwnd, &rect, 0, flags );
759 /**********************************************************************
762 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
764 struct x11drv_win_data *data;
766 if (!(data = X11DRV_get_win_data( hwnd ))) return;
767 if (!data->mapped) return;
771 HWND hwndFocus = GetFocus();
772 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
777 /***********************************************************************
778 * is_net_wm_state_maximized
780 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
784 unsigned long i, count, remaining;
787 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
788 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
789 &remaining, (unsigned char **)&state ))
791 if (type == XA_ATOM && format == 32)
793 for (i = 0; i < count; i++)
795 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
796 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
807 /***********************************************************************
808 * X11DRV_ConfigureNotify
810 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
812 XConfigureEvent *event = &xev->xconfigure;
813 struct x11drv_win_data *data;
816 int cx, cy, x = event->x, y = event->y;
819 if (!(data = X11DRV_get_win_data( hwnd ))) return;
820 if (!data->mapped || data->iconic || !data->managed) return;
824 if (!event->send_event) /* normal event, need to map coordinates to the root */
828 XTranslateCoordinates( event->display, data->whole_window, root_window,
829 0, 0, &x, &y, &child );
834 rect.right = x + event->width;
835 rect.bottom = y + event->height;
836 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
837 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
838 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
839 event->x, event->y, event->width, event->height );
840 X11DRV_X_to_window_rect( data, &rect );
842 if (is_net_wm_state_maximized( event->display, data ))
844 if (!IsZoomed( data->hwnd ))
846 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
847 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
853 if (IsZoomed( data->hwnd ))
855 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
856 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
861 /* Compare what has changed */
865 cx = rect.right - rect.left;
866 cy = rect.bottom - rect.top;
867 flags = SWP_NOACTIVATE | SWP_NOZORDER;
869 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
871 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
872 hwnd, data->window_rect.left, data->window_rect.top, x, y );
874 if ((data->window_rect.right - data->window_rect.left == cx &&
875 data->window_rect.bottom - data->window_rect.top == cy) ||
876 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
878 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
882 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
883 hwnd, data->window_rect.right - data->window_rect.left,
884 data->window_rect.bottom - data->window_rect.top, cx, cy );
886 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
890 /***********************************************************************
891 * get_window_wm_state
893 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
901 int format, ret = -1;
902 unsigned long count, remaining;
905 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
906 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
907 &type, &format, &count, &remaining, (unsigned char **)&state ))
909 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
918 /***********************************************************************
919 * handle_wm_state_notify
921 * Handle a PropertyNotify for WM_STATE.
923 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
929 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
930 data->wm_state = WithdrawnState;
932 case PropertyNewValue:
934 int old_state = data->wm_state;
935 int new_state = get_window_wm_state( event->display, data );
936 if (new_state != -1 && new_state != data->wm_state)
938 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
939 data->hwnd, data->whole_window, new_state, old_state );
940 data->wm_state = new_state;
941 /* ignore the initial state transition out of withdrawn state */
942 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
943 if (!old_state) return;
949 if (!update_window || !data->managed || !data->mapped) return;
951 if (data->iconic && data->wm_state == NormalState) /* restore window */
953 data->iconic = FALSE;
954 if (is_net_wm_state_maximized( event->display, data ))
956 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
957 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
961 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
962 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
965 else if (!data->iconic && data->wm_state == IconicState)
967 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
969 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
974 /***********************************************************************
975 * X11DRV_PropertyNotify
977 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
979 XPropertyEvent *event = &xev->xproperty;
980 struct x11drv_win_data *data;
983 if (!(data = X11DRV_get_win_data( hwnd ))) return;
985 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
989 /* event filter to wait for a WM_STATE change notification on a window */
990 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
992 if (event->xany.window != (Window)arg) return 0;
993 return (event->type == DestroyNotify ||
994 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
997 /***********************************************************************
998 * wait_for_withdrawn_state
1000 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1002 DWORD end = GetTickCount() + 2000;
1004 if (!data->managed) return;
1006 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1007 data->hwnd, data->whole_window, set ? "" : "not " );
1009 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1015 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1018 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1019 if (event.type == DestroyNotify) call_event_handler( display, &event );
1022 wine_tsx11_unlock();
1023 handle_wm_state_notify( data, &event.xproperty, FALSE );
1027 wine_tsx11_unlock();
1032 int timeout = end - GetTickCount();
1034 pfd.fd = ConnectionNumber(display);
1035 pfd.events = POLLIN;
1036 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1038 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1043 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1047 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1051 if (!IsWindowEnabled(hQueryWnd)) return 0;
1053 GetWindowRect(hQueryWnd, &tempRect);
1055 if(!PtInRect(&tempRect, *lpPt)) return 0;
1057 if (!IsIconic( hQueryWnd ))
1060 ScreenToClient( hQueryWnd, &pt );
1061 GetClientRect( hQueryWnd, &tempRect );
1063 if (PtInRect( &tempRect, pt))
1065 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1066 if (ret && ret != hQueryWnd)
1068 ret = find_drop_window( ret, lpPt );
1069 if (ret) return ret;
1074 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1076 ScreenToClient(hQueryWnd, lpPt);
1081 /**********************************************************************
1082 * EVENT_DropFromOffix
1084 * don't know if it still works (last Changelog is from 96/11/04)
1086 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1088 struct x11drv_win_data *data;
1089 unsigned long data_length;
1090 unsigned long aux_long;
1091 unsigned char* p_data = NULL;
1095 Window win, w_aux_root, w_aux_child;
1097 win = X11DRV_get_whole_window(hWnd);
1099 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1100 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1101 x += virtual_screen_rect.left;
1102 y += virtual_screen_rect.top;
1103 wine_tsx11_unlock();
1105 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1107 /* find out drop point and drop window */
1108 if( x < 0 || y < 0 ||
1109 x > (data->whole_rect.right - data->whole_rect.left) ||
1110 y > (data->whole_rect.bottom - data->whole_rect.top) )
1112 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1118 POINT pt = { x, y };
1119 HWND hwndDrop = find_drop_window( hWnd, &pt );
1132 if (!bAccept) return;
1135 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1136 x11drv_atom(DndSelection), 0, 65535, FALSE,
1137 AnyPropertyType, &atom_aux, &dummy,
1138 &data_length, &aux_long, &p_data);
1139 wine_tsx11_unlock();
1141 if( !aux_long && p_data) /* don't bother if > 64K */
1143 char *p = (char *)p_data;
1147 while( *p ) /* calculate buffer size */
1149 INT len = GetShortPathNameA( p, NULL, 0 );
1150 if (len) aux_long += len + 1;
1153 if( aux_long && aux_long < 65535 )
1158 aux_long += sizeof(DROPFILES) + 1;
1159 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1160 lpDrop = GlobalLock( hDrop );
1164 lpDrop->pFiles = sizeof(DROPFILES);
1167 lpDrop->fNC = FALSE;
1168 lpDrop->fWide = FALSE;
1169 p_drop = (char *)(lpDrop + 1);
1173 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1174 p_drop += strlen( p_drop ) + 1;
1178 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1183 if( p_data ) XFree(p_data);
1184 wine_tsx11_unlock();
1187 /**********************************************************************
1190 * drop items are separated by \n
1191 * each item is prefixed by its mime type
1193 * event->data.l[3], event->data.l[4] contains drop x,y position
1195 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1197 struct x11drv_win_data *win_data;
1198 unsigned long data_length;
1199 unsigned long aux_long, drop_len = 0;
1200 unsigned char *p_data = NULL; /* property data */
1201 char *p_drop = NULL;
1213 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1216 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1217 x11drv_atom(DndSelection), 0, 65535, FALSE,
1218 AnyPropertyType, &u.atom_aux, &u.i,
1219 &data_length, &aux_long, &p_data);
1220 wine_tsx11_unlock();
1222 WARN("property too large, truncated!\n");
1223 TRACE("urls=%s\n", p_data);
1225 if( !aux_long && p_data) { /* don't bother if > 64K */
1226 /* calculate length */
1228 next = strchr(p, '\n');
1231 if (strncmp(p,"file:",5) == 0 ) {
1232 INT len = GetShortPathNameA( p+5, NULL, 0 );
1233 if (len) drop_len += len + 1;
1238 next = strchr(p, '\n');
1244 if( drop_len && drop_len < 65535 ) {
1246 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1247 &x, &y, &u.i, &u.i, &u.u);
1248 x += virtual_screen_rect.left;
1249 y += virtual_screen_rect.top;
1250 wine_tsx11_unlock();
1252 drop_len += sizeof(DROPFILES) + 1;
1253 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1254 lpDrop = GlobalLock( hDrop );
1256 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1258 lpDrop->pFiles = sizeof(DROPFILES);
1262 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1263 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1264 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1265 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1266 lpDrop->fWide = FALSE;
1267 p_drop = (char*)(lpDrop + 1);
1270 /* create message content */
1273 next = strchr(p, '\n');
1276 if (strncmp(p,"file:",5) == 0 ) {
1277 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1279 TRACE("drop file %s as %s\n", p+5, p_drop);
1282 WARN("can't convert file %s to dos name\n", p+5);
1285 WARN("unknown mime type %s\n", p);
1290 next = strchr(p, '\n');
1297 GlobalUnlock(hDrop);
1298 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1302 if( p_data ) XFree(p_data);
1303 wine_tsx11_unlock();
1307 /**********************************************************************
1308 * handle_dnd_protocol
1310 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1313 int root_x, root_y, child_x, child_y;
1316 /* query window (drag&drop event contains only drag window) */
1318 XQueryPointer( event->display, root_window, &root, &child,
1319 &root_x, &root_y, &child_x, &child_y, &u);
1320 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1321 wine_tsx11_unlock();
1323 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1324 EVENT_DropFromOffiX(hwnd, event);
1325 else if (event->data.l[0] == DndURL)
1326 EVENT_DropURLs(hwnd, event);
1330 struct client_message_handler
1332 int atom; /* protocol atom */
1333 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1336 static const struct client_message_handler client_messages[] =
1338 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1339 { XATOM_DndProtocol, handle_dnd_protocol },
1340 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1341 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1342 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1343 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1347 /**********************************************************************
1348 * X11DRV_ClientMessage
1350 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1352 XClientMessageEvent *event = &xev->xclient;
1357 if (event->format != 32)
1359 WARN( "Don't know how to handle format %d\n", event->format );
1363 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1365 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1367 client_messages[i].handler( hwnd, event );
1371 TRACE( "no handler found for %ld\n", event->message_type );
1375 /***********************************************************************
1376 * X11DRV_SendInput (X11DRV.@)
1378 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1382 for (i = 0; i < count; i++, inputs++)
1384 switch(inputs->type)
1387 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1388 inputs->u.mi.mouseData, inputs->u.mi.time,
1389 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1391 case INPUT_KEYBOARD:
1392 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1393 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1395 case INPUT_HARDWARE:
1396 FIXME( "INPUT_HARDWARE not supported\n" );