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( "%lu %s for hwnd/window %p/%lx\n",
312 event->xany.serial, 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 )
479 GUITHREADINFO threadinfo;
481 TRACE( "setting foreground window to %p\n", hwnd );
482 SetForegroundWindow( hwnd );
484 GetGUIThreadInfo(0, &threadinfo);
485 focus = threadinfo.hwndFocus;
486 if (focus) focus = GetAncestor( focus, GA_ROOT );
487 win = X11DRV_get_whole_window(focus);
491 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
493 XSetInputFocus( display, win, RevertToParent, time );
499 /**********************************************************************
500 * handle_wm_protocols
502 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
504 Atom protocol = (Atom)event->data.l[0];
506 if (!protocol) return;
508 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
510 if (hwnd == GetDesktopWindow())
512 /* The desktop window does not have a close button that we can
513 * pretend to click. Therefore, we simply send it a close command. */
514 SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
518 /* Ignore the delete window request if the window has been disabled
519 * and we are in managed mode. This is to disallow applications from
520 * being closed by the window manager while in a modal state.
522 if (IsWindowEnabled(hwnd))
527 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
528 hSysMenu = GetSystemMenu(hwnd, FALSE);
531 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
532 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
535 if (GetActiveWindow() != hwnd)
537 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
538 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
539 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
542 case MA_NOACTIVATEANDEAT:
543 case MA_ACTIVATEANDEAT:
549 SetActiveWindow(hwnd);
552 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
556 /* Simulate clicking the caption Close button */
558 PostMessageW( hwnd, WM_NCLBUTTONDOWN, HTCLOSE, MAKELPARAM( pt.x, pt.y ) );
559 PostMessageW( hwnd, WM_LBUTTONUP, HTCLOSE, MAKELPARAM( pt.x, pt.y ) );
562 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
564 Time event_time = (Time)event->data.l[1];
565 HWND last_focus = x11drv_thread_data()->last_focus;
567 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
568 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
569 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
571 if (can_activate_window(hwnd))
573 /* simulate a mouse click on the caption to find out
574 * whether the window wants to be activated */
575 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
576 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
577 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
578 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
580 set_focus( event->display, hwnd, event_time );
584 else if (hwnd == GetDesktopWindow())
586 hwnd = GetForegroundWindow();
587 if (!hwnd) hwnd = last_focus;
588 if (!hwnd) hwnd = GetDesktopWindow();
589 set_focus( event->display, hwnd, event_time );
592 /* try to find some other window to give the focus to */
594 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
595 if (!hwnd) hwnd = GetActiveWindow();
596 if (!hwnd) hwnd = last_focus;
597 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
599 else if (protocol == x11drv_atom(_NET_WM_PING))
601 XClientMessageEvent xev;
604 TRACE("NET_WM Ping\n");
606 xev.window = DefaultRootWindow(xev.display);
607 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
609 /* this line is semi-stolen from gtk2 */
610 TRACE("NET_WM Pong\n");
615 static const char * const focus_details[] =
621 "NotifyNonlinearVirtual",
627 /**********************************************************************
630 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
632 XFocusChangeEvent *event = &xev->xfocus;
637 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
639 if (event->detail == NotifyPointer) return;
641 if ((xic = X11DRV_get_ic( hwnd )))
647 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
649 if (!can_activate_window(hwnd))
651 HWND hwnd = GetFocus();
652 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
653 if (!hwnd) hwnd = GetActiveWindow();
654 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
655 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
657 else SetForegroundWindow( hwnd );
661 /**********************************************************************
664 * Note: only top-level windows get FocusOut events.
666 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
668 XFocusChangeEvent *event = &xev->xfocus;
676 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
678 if (event->detail == NotifyPointer) return;
679 if (ximInComposeMode) return;
681 x11drv_thread_data()->last_focus = hwnd;
682 if ((xic = X11DRV_get_ic( hwnd )))
685 XUnsetICFocus( xic );
688 if (hwnd != GetForegroundWindow()) return;
689 if (root_window != DefaultRootWindow(event->display)) return;
690 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
692 /* don't reset the foreground window, if the window which is
693 getting the focus is a Wine window */
696 XGetInputFocus( event->display, &focus_win, &revert );
699 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
706 /* Abey : 6-Oct-99. Check again if the focus out window is the
707 Foreground window, because in most cases the messages sent
708 above must have already changed the foreground window, in which
709 case we don't have to change the foreground window to 0 */
710 if (hwnd == GetForegroundWindow())
712 TRACE( "lost focus, setting fg to desktop\n" );
713 SetForegroundWindow( GetDesktopWindow() );
719 /***********************************************************************
722 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
724 XExposeEvent *event = &xev->xexpose;
726 struct x11drv_win_data *data;
727 int flags = RDW_INVALIDATE | RDW_ERASE;
729 TRACE( "win %p (%lx) %d,%d %dx%d\n",
730 hwnd, event->window, event->x, event->y, event->width, event->height );
732 if (!(data = X11DRV_get_win_data( hwnd ))) return;
734 if (event->window == data->whole_window)
736 rect.left = data->whole_rect.left + event->x;
737 rect.top = data->whole_rect.top + event->y;
742 rect.left = data->client_rect.left + event->x;
743 rect.top = data->client_rect.top + event->y;
745 rect.right = rect.left + event->width;
746 rect.bottom = rect.top + event->height;
748 if (event->window != root_window)
750 SERVER_START_REQ( update_window_zorder )
752 req->window = wine_server_user_handle( hwnd );
753 req->rect.left = rect.left;
754 req->rect.top = rect.top;
755 req->rect.right = rect.right;
756 req->rect.bottom = rect.bottom;
757 wine_server_call( req );
761 /* make position relative to client area instead of parent */
762 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
763 flags |= RDW_ALLCHILDREN;
766 RedrawWindow( hwnd, &rect, 0, flags );
770 /**********************************************************************
773 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
775 struct x11drv_win_data *data;
777 if (!(data = X11DRV_get_win_data( hwnd ))) return;
778 if (!data->mapped) return;
782 HWND hwndFocus = GetFocus();
783 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
788 /***********************************************************************
789 * is_net_wm_state_maximized
791 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
795 unsigned long i, count, remaining;
798 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
799 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
800 &remaining, (unsigned char **)&state ))
802 if (type == XA_ATOM && format == 32)
804 for (i = 0; i < count; i++)
806 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
807 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
818 /***********************************************************************
819 * X11DRV_ConfigureNotify
821 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
823 XConfigureEvent *event = &xev->xconfigure;
824 struct x11drv_win_data *data;
827 int cx, cy, x = event->x, y = event->y;
830 if (!(data = X11DRV_get_win_data( hwnd ))) return;
831 if (!data->mapped || data->iconic || !data->managed) return;
832 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
834 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
835 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
836 event->serial, data->configure_serial );
842 if (!event->send_event) /* normal event, need to map coordinates to the root */
846 XTranslateCoordinates( event->display, data->whole_window, root_window,
847 0, 0, &x, &y, &child );
852 rect.right = x + event->width;
853 rect.bottom = y + event->height;
854 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
855 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
856 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
857 event->x, event->y, event->width, event->height );
858 X11DRV_X_to_window_rect( data, &rect );
860 if (is_net_wm_state_maximized( event->display, data ))
862 if (!IsZoomed( data->hwnd ))
864 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
865 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
871 if (IsZoomed( data->hwnd ))
873 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
874 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
879 /* Compare what has changed */
883 cx = rect.right - rect.left;
884 cy = rect.bottom - rect.top;
885 flags = SWP_NOACTIVATE | SWP_NOZORDER;
887 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
889 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
890 hwnd, data->window_rect.left, data->window_rect.top, x, y );
892 if ((data->window_rect.right - data->window_rect.left == cx &&
893 data->window_rect.bottom - data->window_rect.top == cy) ||
894 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
896 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
900 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
901 hwnd, data->window_rect.right - data->window_rect.left,
902 data->window_rect.bottom - data->window_rect.top, cx, cy );
904 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
908 /***********************************************************************
909 * get_window_wm_state
911 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
919 int format, ret = -1;
920 unsigned long count, remaining;
923 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
924 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
925 &type, &format, &count, &remaining, (unsigned char **)&state ))
927 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
936 /***********************************************************************
937 * handle_wm_state_notify
939 * Handle a PropertyNotify for WM_STATE.
941 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
947 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
948 data->wm_state = WithdrawnState;
950 case PropertyNewValue:
952 int old_state = data->wm_state;
953 int new_state = get_window_wm_state( event->display, data );
954 if (new_state != -1 && new_state != data->wm_state)
956 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
957 data->hwnd, data->whole_window, new_state, old_state );
958 data->wm_state = new_state;
959 /* ignore the initial state transition out of withdrawn state */
960 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
961 if (!old_state) return;
967 if (!update_window || !data->managed || !data->mapped) return;
969 if (data->iconic && data->wm_state == NormalState) /* restore window */
971 data->iconic = FALSE;
972 if (is_net_wm_state_maximized( event->display, data ))
974 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
975 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
979 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
980 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
983 else if (!data->iconic && data->wm_state == IconicState)
985 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
987 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
992 /***********************************************************************
993 * X11DRV_PropertyNotify
995 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
997 XPropertyEvent *event = &xev->xproperty;
998 struct x11drv_win_data *data;
1001 if (!(data = X11DRV_get_win_data( hwnd ))) return;
1003 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1007 /* event filter to wait for a WM_STATE change notification on a window */
1008 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1010 if (event->xany.window != (Window)arg) return 0;
1011 return (event->type == DestroyNotify ||
1012 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1015 /***********************************************************************
1016 * wait_for_withdrawn_state
1018 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1020 DWORD end = GetTickCount() + 2000;
1022 if (!data->managed) return;
1024 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1025 data->hwnd, data->whole_window, set ? "" : "not " );
1027 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1033 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1036 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1037 if (event.type == DestroyNotify) call_event_handler( display, &event );
1040 wine_tsx11_unlock();
1041 handle_wm_state_notify( data, &event.xproperty, FALSE );
1045 wine_tsx11_unlock();
1050 int timeout = end - GetTickCount();
1052 pfd.fd = ConnectionNumber(display);
1053 pfd.events = POLLIN;
1054 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1056 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1061 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1065 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1069 if (!IsWindowEnabled(hQueryWnd)) return 0;
1071 GetWindowRect(hQueryWnd, &tempRect);
1073 if(!PtInRect(&tempRect, *lpPt)) return 0;
1075 if (!IsIconic( hQueryWnd ))
1078 ScreenToClient( hQueryWnd, &pt );
1079 GetClientRect( hQueryWnd, &tempRect );
1081 if (PtInRect( &tempRect, pt))
1083 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1084 if (ret && ret != hQueryWnd)
1086 ret = find_drop_window( ret, lpPt );
1087 if (ret) return ret;
1092 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1094 ScreenToClient(hQueryWnd, lpPt);
1099 /**********************************************************************
1100 * EVENT_DropFromOffix
1102 * don't know if it still works (last Changelog is from 96/11/04)
1104 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1106 struct x11drv_win_data *data;
1107 unsigned long data_length;
1108 unsigned long aux_long;
1109 unsigned char* p_data = NULL;
1113 Window win, w_aux_root, w_aux_child;
1115 win = X11DRV_get_whole_window(hWnd);
1117 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1118 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1119 x += virtual_screen_rect.left;
1120 y += virtual_screen_rect.top;
1121 wine_tsx11_unlock();
1123 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1125 /* find out drop point and drop window */
1126 if( x < 0 || y < 0 ||
1127 x > (data->whole_rect.right - data->whole_rect.left) ||
1128 y > (data->whole_rect.bottom - data->whole_rect.top) )
1130 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1136 POINT pt = { x, y };
1137 HWND hwndDrop = find_drop_window( hWnd, &pt );
1150 if (!bAccept) return;
1153 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1154 x11drv_atom(DndSelection), 0, 65535, FALSE,
1155 AnyPropertyType, &atom_aux, &dummy,
1156 &data_length, &aux_long, &p_data);
1157 wine_tsx11_unlock();
1159 if( !aux_long && p_data) /* don't bother if > 64K */
1161 char *p = (char *)p_data;
1165 while( *p ) /* calculate buffer size */
1167 INT len = GetShortPathNameA( p, NULL, 0 );
1168 if (len) aux_long += len + 1;
1171 if( aux_long && aux_long < 65535 )
1176 aux_long += sizeof(DROPFILES) + 1;
1177 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1178 lpDrop = GlobalLock( hDrop );
1182 lpDrop->pFiles = sizeof(DROPFILES);
1185 lpDrop->fNC = FALSE;
1186 lpDrop->fWide = FALSE;
1187 p_drop = (char *)(lpDrop + 1);
1191 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1192 p_drop += strlen( p_drop ) + 1;
1196 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1201 if( p_data ) XFree(p_data);
1202 wine_tsx11_unlock();
1205 /**********************************************************************
1208 * drop items are separated by \n
1209 * each item is prefixed by its mime type
1211 * event->data.l[3], event->data.l[4] contains drop x,y position
1213 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1215 struct x11drv_win_data *win_data;
1216 unsigned long data_length;
1217 unsigned long aux_long, drop_len = 0;
1218 unsigned char *p_data = NULL; /* property data */
1219 char *p_drop = NULL;
1231 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1234 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1235 x11drv_atom(DndSelection), 0, 65535, FALSE,
1236 AnyPropertyType, &u.atom_aux, &u.i,
1237 &data_length, &aux_long, &p_data);
1238 wine_tsx11_unlock();
1240 WARN("property too large, truncated!\n");
1241 TRACE("urls=%s\n", p_data);
1243 if( !aux_long && p_data) { /* don't bother if > 64K */
1244 /* calculate length */
1246 next = strchr(p, '\n');
1249 if (strncmp(p,"file:",5) == 0 ) {
1250 INT len = GetShortPathNameA( p+5, NULL, 0 );
1251 if (len) drop_len += len + 1;
1256 next = strchr(p, '\n');
1262 if( drop_len && drop_len < 65535 ) {
1264 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1265 &x, &y, &u.i, &u.i, &u.u);
1266 x += virtual_screen_rect.left;
1267 y += virtual_screen_rect.top;
1268 wine_tsx11_unlock();
1270 drop_len += sizeof(DROPFILES) + 1;
1271 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1272 lpDrop = GlobalLock( hDrop );
1274 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1276 lpDrop->pFiles = sizeof(DROPFILES);
1280 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1281 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1282 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1283 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1284 lpDrop->fWide = FALSE;
1285 p_drop = (char*)(lpDrop + 1);
1288 /* create message content */
1291 next = strchr(p, '\n');
1294 if (strncmp(p,"file:",5) == 0 ) {
1295 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1297 TRACE("drop file %s as %s\n", p+5, p_drop);
1300 WARN("can't convert file %s to dos name\n", p+5);
1303 WARN("unknown mime type %s\n", p);
1308 next = strchr(p, '\n');
1315 GlobalUnlock(hDrop);
1316 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1320 if( p_data ) XFree(p_data);
1321 wine_tsx11_unlock();
1325 /**********************************************************************
1326 * handle_dnd_protocol
1328 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1331 int root_x, root_y, child_x, child_y;
1334 /* query window (drag&drop event contains only drag window) */
1336 XQueryPointer( event->display, root_window, &root, &child,
1337 &root_x, &root_y, &child_x, &child_y, &u);
1338 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1339 wine_tsx11_unlock();
1341 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1342 EVENT_DropFromOffiX(hwnd, event);
1343 else if (event->data.l[0] == DndURL)
1344 EVENT_DropURLs(hwnd, event);
1348 struct client_message_handler
1350 int atom; /* protocol atom */
1351 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1354 static const struct client_message_handler client_messages[] =
1356 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1357 { XATOM_DndProtocol, handle_dnd_protocol },
1358 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1359 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1360 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1361 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1365 /**********************************************************************
1366 * X11DRV_ClientMessage
1368 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1370 XClientMessageEvent *event = &xev->xclient;
1375 if (event->format != 32)
1377 WARN( "Don't know how to handle format %d\n", event->format );
1381 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1383 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1385 client_messages[i].handler( hwnd, event );
1389 TRACE( "no handler found for %ld\n", event->message_type );
1393 /***********************************************************************
1394 * X11DRV_SendInput (X11DRV.@)
1396 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1400 for (i = 0; i < count; i++, inputs++)
1402 switch(inputs->type)
1405 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1406 inputs->u.mi.mouseData, inputs->u.mi.time,
1407 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1409 case INPUT_KEYBOARD:
1410 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1411 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1413 case INPUT_HARDWARE:
1414 FIXME( "INPUT_HARDWARE not supported\n" );