winex11: Ignore the initial WM_STATE transition out of withdrawn state.
[wine/multimedia.git] / dlls / winex11.drv / event.c
blob56abd7d6bd95ae1d5fabcafbc68e9ddd9efbaa7e
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/server.h"
55 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(event);
59 extern BOOL ximInComposeMode;
61 #define DndNotDnd -1 /* OffiX drag&drop */
62 #define DndUnknown 0
63 #define DndRawData 1
64 #define DndFile 2
65 #define DndFiles 3
66 #define DndText 4
67 #define DndDir 5
68 #define DndLink 6
69 #define DndExe 7
71 #define DndEND 8
73 #define DndURL 128 /* KDE drag&drop */
75 /* Event handlers */
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 );
84 struct event_handler
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 },
101 /* LeaveNotify */
102 { FocusIn, X11DRV_FocusIn },
103 { FocusOut, X11DRV_FocusOut },
104 { KeymapNotify, X11DRV_KeymapNotify },
105 { Expose, X11DRV_Expose },
106 /* GraphicsExpose */
107 /* NoExpose */
108 /* VisibilityNotify */
109 /* CreateNotify */
110 { DestroyNotify, X11DRV_DestroyNotify },
111 /* UnmapNotify */
112 { MapNotify, X11DRV_MapNotify },
113 /* MapRequest */
114 /* ReparentNotify */
115 { ConfigureNotify, X11DRV_ConfigureNotify },
116 /* ConfigureRequest */
117 /* GravityNotify */
118 /* ResizeRequest */
119 /* CirculateNotify */
120 /* CirculateRequest */
121 { PropertyNotify, X11DRV_PropertyNotify },
122 { SelectionClear, X11DRV_SelectionClear },
123 { SelectionRequest, X11DRV_SelectionRequest },
124 /* SelectionNotify */
125 /* ColormapNotify */
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 /***********************************************************************
154 * find_handler
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;
162 while (min <= max)
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;
167 else min = pos + 1;
169 return NULL;
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 )
181 int min, max;
183 wine_tsx11_lock();
184 min = 0;
185 max = nb_event_handlers - 1;
186 while (min <= max)
188 int pos = (min + max) / 2;
189 if (handlers[pos].type == type)
191 handlers[pos].handler = handler;
192 goto done;
194 if (handlers[pos].type > type) max = pos - 1;
195 else min = 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;
201 nb_event_handlers++;
202 assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
203 done:
204 wine_tsx11_unlock();
205 TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
209 /***********************************************************************
210 * filter_event
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;
218 switch(event->type)
220 case KeyPress:
221 case KeyRelease:
222 case KeymapNotify:
223 case MappingNotify:
224 return (mask & QS_KEY) != 0;
225 case ButtonPress:
226 case ButtonRelease:
227 return (mask & QS_MOUSEBUTTON) != 0;
228 case MotionNotify:
229 case EnterNotify:
230 case LeaveNotify:
231 return (mask & QS_MOUSEMOVE) != 0;
232 case Expose:
233 return (mask & QS_PAINT) != 0;
234 case FocusIn:
235 case FocusOut:
236 case MapNotify:
237 case UnmapNotify:
238 case ConfigureNotify:
239 case PropertyNotify:
240 case ClientMessage:
241 return (mask & QS_POSTMESSAGE) != 0;
242 default:
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 /***********************************************************************
256 * merge_events
258 * Try to merge 2 consecutive events.
260 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
262 switch (prev->type)
264 case ConfigureNotify:
265 switch (next->type)
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;
273 break;
274 case Expose:
275 case PropertyNotify:
276 return MERGE_KEEP;
278 break;
279 case MotionNotify:
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;
285 break;
287 return MERGE_HANDLE;
291 /***********************************************************************
292 * call_event_handler
294 static inline void call_event_handler( Display *display, XEvent *event )
296 HWND hwnd;
297 x11drv_event_handler handler;
298 XEvent *prev;
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 );
313 wine_tsx11_unlock();
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;
319 wine_tsx11_lock();
323 /***********************************************************************
324 * process_events
326 static int process_events( Display *display, Bool (*filter)(), ULONG_PTR arg )
328 XEvent event, prev_event;
329 int count = 0;
330 enum event_merge_action action = MERGE_DISCARD;
332 prev_event.type = 0;
333 wine_tsx11_lock();
334 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
336 count++;
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)
350 KeySym keysym = 0;
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 */
364 else
365 continue; /* filtered, ignore it */
367 if (prev_event.type) action = merge_events( &prev_event, &event );
368 switch( action )
370 case MERGE_DISCARD: /* discard prev, keep new */
371 prev_event = event;
372 break;
373 case MERGE_HANDLE: /* handle prev, keep new */
374 call_event_handler( display, &prev_event );
375 prev_event = event;
376 break;
377 case MERGE_KEEP: /* handle new, keep prev for future merging */
378 call_event_handler( display, &event );
379 break;
382 XFlush( gdi_display );
383 if (prev_event.type) call_event_handler( display, &prev_event );
384 wine_tsx11_unlock();
385 if (count) TRACE( "processed %d events\n", count );
386 return count;
390 /***********************************************************************
391 * MsgWaitForMultipleObjectsEx (X11DRV.@)
393 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
394 DWORD timeout, DWORD mask, DWORD flags )
396 DWORD ret;
397 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
399 if (!data)
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;
417 return ret;
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();
430 DWORD ret;
432 if (! adjust && time != 0)
434 ret = now;
435 adjust = time - now;
437 else
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. */
443 ret = time - adjust;
444 if (ret > now && ((ret - now) < 10000) && time != 0)
446 adjust += ret - now;
447 ret -= ret - now;
451 return ret;
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 (hwnd == GetDesktopWindow()) return FALSE;
466 return !(style & WS_DISABLED);
470 /**********************************************************************
471 * set_focus
473 static void set_focus( Display *display, HWND hwnd, Time time )
475 HWND focus;
476 Window win;
478 TRACE( "setting foreground window to %p\n", hwnd );
479 SetForegroundWindow( hwnd );
481 focus = GetFocus();
482 if (focus) focus = GetAncestor( focus, GA_ROOT );
483 win = X11DRV_get_whole_window(focus);
485 if (win)
487 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
488 wine_tsx11_lock();
489 XSetInputFocus( display, win, RevertToParent, time );
490 wine_tsx11_unlock();
495 /**********************************************************************
496 * handle_wm_protocols
498 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
500 Atom protocol = (Atom)event->data.l[0];
502 if (!protocol) return;
504 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
506 /* Ignore the delete window request if the window has been disabled
507 * and we are in managed mode. This is to disallow applications from
508 * being closed by the window manager while in a modal state.
510 if (IsWindowEnabled(hwnd))
512 HMENU hSysMenu;
514 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
515 hSysMenu = GetSystemMenu(hwnd, FALSE);
516 if (hSysMenu)
518 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
519 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
520 return;
522 if (GetActiveWindow() != hwnd)
524 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
525 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
526 MAKELONG(HTCLOSE,WM_LBUTTONDOWN) );
527 switch(ma)
529 case MA_NOACTIVATEANDEAT:
530 case MA_ACTIVATEANDEAT:
531 return;
532 case MA_NOACTIVATE:
533 break;
534 case MA_ACTIVATE:
535 case 0:
536 SetActiveWindow(hwnd);
537 break;
538 default:
539 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
540 break;
543 PostMessageW( hwnd, WM_X11DRV_DELETE_WINDOW, 0, 0 );
546 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
548 Time event_time = (Time)event->data.l[1];
549 HWND last_focus = x11drv_thread_data()->last_focus;
551 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
552 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
553 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
555 if (can_activate_window(hwnd))
557 /* simulate a mouse click on the caption to find out
558 * whether the window wants to be activated */
559 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
560 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
561 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
562 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
564 set_focus( event->display, hwnd, event_time );
565 return;
568 /* try to find some other window to give the focus to */
569 hwnd = GetFocus();
570 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
571 if (!hwnd) hwnd = GetActiveWindow();
572 if (!hwnd) hwnd = last_focus;
573 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
575 else if (protocol == x11drv_atom(_NET_WM_PING))
577 XClientMessageEvent xev;
578 xev = *event;
580 TRACE("NET_WM Ping\n");
581 wine_tsx11_lock();
582 xev.window = DefaultRootWindow(xev.display);
583 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
584 wine_tsx11_unlock();
585 /* this line is semi-stolen from gtk2 */
586 TRACE("NET_WM Pong\n");
591 static const char * const focus_details[] =
593 "NotifyAncestor",
594 "NotifyVirtual",
595 "NotifyInferior",
596 "NotifyNonlinear",
597 "NotifyNonlinearVirtual",
598 "NotifyPointer",
599 "NotifyPointerRoot",
600 "NotifyDetailNone"
603 /**********************************************************************
604 * X11DRV_FocusIn
606 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
608 XFocusChangeEvent *event = &xev->xfocus;
609 XIC xic;
611 if (!hwnd) return;
613 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
615 if (event->detail == NotifyPointer) return;
617 if ((xic = X11DRV_get_ic( hwnd )))
619 wine_tsx11_lock();
620 XSetICFocus( xic );
621 wine_tsx11_unlock();
623 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
625 if (!can_activate_window(hwnd))
627 HWND hwnd = GetFocus();
628 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
629 if (!hwnd) hwnd = GetActiveWindow();
630 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
631 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
633 else SetForegroundWindow( hwnd );
637 /**********************************************************************
638 * X11DRV_FocusOut
640 * Note: only top-level windows get FocusOut events.
642 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
644 XFocusChangeEvent *event = &xev->xfocus;
645 HWND hwnd_tmp;
646 Window focus_win;
647 int revert;
648 XIC xic;
650 if (!hwnd) return;
652 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
654 if (event->detail == NotifyPointer) return;
655 if (ximInComposeMode) return;
657 x11drv_thread_data()->last_focus = hwnd;
658 if ((xic = X11DRV_get_ic( hwnd )))
660 wine_tsx11_lock();
661 XUnsetICFocus( xic );
662 wine_tsx11_unlock();
664 if (hwnd != GetForegroundWindow()) return;
665 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
667 /* don't reset the foreground window, if the window which is
668 getting the focus is a Wine window */
670 wine_tsx11_lock();
671 XGetInputFocus( event->display, &focus_win, &revert );
672 if (focus_win)
674 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
675 focus_win = 0;
677 wine_tsx11_unlock();
679 if (!focus_win)
681 /* Abey : 6-Oct-99. Check again if the focus out window is the
682 Foreground window, because in most cases the messages sent
683 above must have already changed the foreground window, in which
684 case we don't have to change the foreground window to 0 */
685 if (hwnd == GetForegroundWindow())
687 TRACE( "lost focus, setting fg to desktop\n" );
688 SetForegroundWindow( GetDesktopWindow() );
694 /***********************************************************************
695 * X11DRV_Expose
697 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
699 XExposeEvent *event = &xev->xexpose;
700 RECT rect;
701 struct x11drv_win_data *data;
702 int flags = RDW_INVALIDATE | RDW_ERASE;
704 TRACE( "win %p (%lx) %d,%d %dx%d\n",
705 hwnd, event->window, event->x, event->y, event->width, event->height );
707 if (!(data = X11DRV_get_win_data( hwnd ))) return;
709 if (event->window == data->whole_window)
711 rect.left = data->whole_rect.left + event->x;
712 rect.top = data->whole_rect.top + event->y;
713 flags |= RDW_FRAME;
715 else
717 rect.left = data->client_rect.left + event->x;
718 rect.top = data->client_rect.top + event->y;
720 rect.right = rect.left + event->width;
721 rect.bottom = rect.top + event->height;
723 if (event->window != root_window)
725 SERVER_START_REQ( update_window_zorder )
727 req->window = hwnd;
728 req->rect.left = rect.left;
729 req->rect.top = rect.top;
730 req->rect.right = rect.right;
731 req->rect.bottom = rect.bottom;
732 wine_server_call( req );
734 SERVER_END_REQ;
736 /* make position relative to client area instead of parent */
737 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
738 flags |= RDW_ALLCHILDREN;
741 RedrawWindow( hwnd, &rect, 0, flags );
745 /**********************************************************************
746 * X11DRV_MapNotify
748 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
750 struct x11drv_win_data *data;
752 if (!(data = X11DRV_get_win_data( hwnd ))) return;
753 if (!data->mapped) return;
755 if (!data->managed)
757 HWND hwndFocus = GetFocus();
758 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
763 /***********************************************************************
764 * is_net_wm_state_maximized
766 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
768 Atom type, *state;
769 int format, ret = 0;
770 unsigned long i, count, remaining;
772 wine_tsx11_lock();
773 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
774 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
775 &remaining, (unsigned char **)&state ))
777 if (type == XA_ATOM && format == 32)
779 for (i = 0; i < count; i++)
781 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
782 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
783 ret++;
786 XFree( state );
788 wine_tsx11_unlock();
789 return (ret == 2);
793 /***********************************************************************
794 * X11DRV_ConfigureNotify
796 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
798 XConfigureEvent *event = &xev->xconfigure;
799 struct x11drv_win_data *data;
800 RECT rect;
801 UINT flags;
802 int cx, cy, x = event->x, y = event->y;
804 if (!hwnd) return;
805 if (!(data = X11DRV_get_win_data( hwnd ))) return;
806 if (!data->mapped || data->iconic) return;
808 /* Get geometry */
810 if (!event->send_event) /* normal event, need to map coordinates to the root */
812 Window child;
813 wine_tsx11_lock();
814 XTranslateCoordinates( event->display, data->whole_window, root_window,
815 0, 0, &x, &y, &child );
816 wine_tsx11_unlock();
818 rect.left = x;
819 rect.top = y;
820 rect.right = x + event->width;
821 rect.bottom = y + event->height;
822 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
823 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
824 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
825 event->x, event->y, event->width, event->height );
826 X11DRV_X_to_window_rect( data, &rect );
828 x = rect.left;
829 y = rect.top;
830 cx = rect.right - rect.left;
831 cy = rect.bottom - rect.top;
832 flags = SWP_NOACTIVATE | SWP_NOZORDER;
834 if (is_net_wm_state_maximized( event->display, data ))
836 if (!IsZoomed( data->hwnd ))
838 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
839 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
840 return;
843 else
845 if (IsZoomed( data->hwnd ))
847 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
848 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
849 return;
853 /* Compare what has changed */
855 GetWindowRect( hwnd, &rect );
856 if (rect.left == x && rect.top == y) flags |= SWP_NOMOVE;
857 else
858 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
859 hwnd, rect.left, rect.top, x, y );
861 if ((rect.right - rect.left == cx && rect.bottom - rect.top == cy) ||
862 (IsRectEmpty( &rect ) && event->width == 1 && event->height == 1))
864 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
865 flags |= SWP_NOSIZE;
867 else
868 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
869 hwnd, rect.right - rect.left, rect.bottom - rect.top, cx, cy );
871 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
875 /***********************************************************************
876 * get_window_wm_state
878 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
880 struct
882 CARD32 state;
883 XID icon;
884 } *state;
885 Atom type;
886 int format, ret = -1;
887 unsigned long count, remaining;
889 wine_tsx11_lock();
890 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
891 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
892 &type, &format, &count, &remaining, (unsigned char **)&state ))
894 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
895 ret = state->state;
896 XFree( state );
898 wine_tsx11_unlock();
899 return ret;
903 /***********************************************************************
904 * handle_wm_state_notify
906 * Handle a PropertyNotify for WM_STATE.
908 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
909 BOOL update_window )
911 switch(event->state)
913 case PropertyDelete:
914 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
915 data->wm_state = WithdrawnState;
916 break;
917 case PropertyNewValue:
919 int old_state = data->wm_state;
920 int new_state = get_window_wm_state( event->display, data );
921 if (new_state != -1 && new_state != data->wm_state)
923 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
924 data->hwnd, data->whole_window, new_state, old_state );
925 data->wm_state = new_state;
926 /* ignore the initial state transition out of withdrawn state */
927 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
928 if (!old_state) return;
931 break;
934 if (!update_window || !data->managed || !data->mapped) return;
936 if (data->iconic && data->wm_state == NormalState) /* restore window */
938 data->iconic = FALSE;
939 if (is_net_wm_state_maximized( event->display, data ))
941 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
942 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
944 else
946 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
947 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
950 else if (!data->iconic && data->wm_state == IconicState)
952 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
953 data->iconic = TRUE;
954 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
959 /***********************************************************************
960 * X11DRV_PropertyNotify
962 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
964 XPropertyEvent *event = &xev->xproperty;
965 struct x11drv_win_data *data;
967 if (!hwnd) return;
968 if (!(data = X11DRV_get_win_data( hwnd ))) return;
970 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
974 /* event filter to wait for a WM_STATE change notification on a window */
975 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
977 if (event->xany.window != (Window)arg) return 0;
978 return (event->type == DestroyNotify ||
979 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
982 /***********************************************************************
983 * wait_for_withdrawn_state
985 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
987 DWORD end = GetTickCount() + 2000;
989 if (!data->managed) return;
991 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
992 data->hwnd, data->whole_window, set ? "" : "not " );
994 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
996 XEvent event;
997 int count = 0;
999 wine_tsx11_lock();
1000 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1002 count++;
1003 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1004 if (event.type == DestroyNotify) call_event_handler( display, &event );
1005 else
1007 wine_tsx11_unlock();
1008 handle_wm_state_notify( data, &event.xproperty, FALSE );
1009 wine_tsx11_lock();
1012 wine_tsx11_unlock();
1014 if (!count)
1016 struct pollfd pfd;
1017 int timeout = end - GetTickCount();
1019 pfd.fd = ConnectionNumber(display);
1020 pfd.events = POLLIN;
1021 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1023 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1024 break;
1028 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1032 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1034 RECT tempRect;
1036 if (!IsWindowEnabled(hQueryWnd)) return 0;
1038 GetWindowRect(hQueryWnd, &tempRect);
1040 if(!PtInRect(&tempRect, *lpPt)) return 0;
1042 if (!IsIconic( hQueryWnd ))
1044 POINT pt = *lpPt;
1045 ScreenToClient( hQueryWnd, &pt );
1046 GetClientRect( hQueryWnd, &tempRect );
1048 if (PtInRect( &tempRect, pt))
1050 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1051 if (ret && ret != hQueryWnd)
1053 ret = find_drop_window( ret, lpPt );
1054 if (ret) return ret;
1059 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1061 ScreenToClient(hQueryWnd, lpPt);
1063 return hQueryWnd;
1066 /**********************************************************************
1067 * EVENT_DropFromOffix
1069 * don't know if it still works (last Changelog is from 96/11/04)
1071 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1073 struct x11drv_win_data *data;
1074 unsigned long data_length;
1075 unsigned long aux_long;
1076 unsigned char* p_data = NULL;
1077 Atom atom_aux;
1078 int x, y, dummy;
1079 BOOL bAccept;
1080 Window win, w_aux_root, w_aux_child;
1082 win = X11DRV_get_whole_window(hWnd);
1083 wine_tsx11_lock();
1084 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1085 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1086 x += virtual_screen_rect.left;
1087 y += virtual_screen_rect.top;
1088 wine_tsx11_unlock();
1090 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1092 /* find out drop point and drop window */
1093 if( x < 0 || y < 0 ||
1094 x > (data->whole_rect.right - data->whole_rect.left) ||
1095 y > (data->whole_rect.bottom - data->whole_rect.top) )
1097 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1098 x = 0;
1099 y = 0;
1101 else
1103 POINT pt = { x, y };
1104 HWND hwndDrop = find_drop_window( hWnd, &pt );
1105 if (hwndDrop)
1107 x = pt.x;
1108 y = pt.y;
1109 bAccept = TRUE;
1111 else
1113 bAccept = FALSE;
1117 if (!bAccept) return;
1119 wine_tsx11_lock();
1120 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1121 x11drv_atom(DndSelection), 0, 65535, FALSE,
1122 AnyPropertyType, &atom_aux, &dummy,
1123 &data_length, &aux_long, &p_data);
1124 wine_tsx11_unlock();
1126 if( !aux_long && p_data) /* don't bother if > 64K */
1128 char *p = (char *)p_data;
1129 char *p_drop;
1131 aux_long = 0;
1132 while( *p ) /* calculate buffer size */
1134 INT len = GetShortPathNameA( p, NULL, 0 );
1135 if (len) aux_long += len + 1;
1136 p += strlen(p) + 1;
1138 if( aux_long && aux_long < 65535 )
1140 HDROP hDrop;
1141 DROPFILES *lpDrop;
1143 aux_long += sizeof(DROPFILES) + 1;
1144 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1145 lpDrop = (DROPFILES*)GlobalLock( hDrop );
1147 if( lpDrop )
1149 lpDrop->pFiles = sizeof(DROPFILES);
1150 lpDrop->pt.x = x;
1151 lpDrop->pt.y = y;
1152 lpDrop->fNC = FALSE;
1153 lpDrop->fWide = FALSE;
1154 p_drop = (char *)(lpDrop + 1);
1155 p = (char *)p_data;
1156 while(*p)
1158 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1159 p_drop += strlen( p_drop ) + 1;
1160 p += strlen(p) + 1;
1162 *p_drop = '\0';
1163 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1167 wine_tsx11_lock();
1168 if( p_data ) XFree(p_data);
1169 wine_tsx11_unlock();
1172 /**********************************************************************
1173 * EVENT_DropURLs
1175 * drop items are separated by \n
1176 * each item is prefixed by its mime type
1178 * event->data.l[3], event->data.l[4] contains drop x,y position
1180 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1182 struct x11drv_win_data *win_data;
1183 unsigned long data_length;
1184 unsigned long aux_long, drop_len = 0;
1185 unsigned char *p_data = NULL; /* property data */
1186 char *p_drop = NULL;
1187 char *p, *next;
1188 int x, y;
1189 DROPFILES *lpDrop;
1190 HDROP hDrop;
1191 union {
1192 Atom atom_aux;
1193 int i;
1194 Window w_aux;
1195 unsigned int u;
1196 } u; /* unused */
1198 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1200 wine_tsx11_lock();
1201 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1202 x11drv_atom(DndSelection), 0, 65535, FALSE,
1203 AnyPropertyType, &u.atom_aux, &u.i,
1204 &data_length, &aux_long, &p_data);
1205 wine_tsx11_unlock();
1206 if (aux_long)
1207 WARN("property too large, truncated!\n");
1208 TRACE("urls=%s\n", p_data);
1210 if( !aux_long && p_data) { /* don't bother if > 64K */
1211 /* calculate length */
1212 p = (char*) p_data;
1213 next = strchr(p, '\n');
1214 while (p) {
1215 if (next) *next=0;
1216 if (strncmp(p,"file:",5) == 0 ) {
1217 INT len = GetShortPathNameA( p+5, NULL, 0 );
1218 if (len) drop_len += len + 1;
1220 if (next) {
1221 *next = '\n';
1222 p = next + 1;
1223 next = strchr(p, '\n');
1224 } else {
1225 p = NULL;
1229 if( drop_len && drop_len < 65535 ) {
1230 wine_tsx11_lock();
1231 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1232 &x, &y, &u.i, &u.i, &u.u);
1233 x += virtual_screen_rect.left;
1234 y += virtual_screen_rect.top;
1235 wine_tsx11_unlock();
1237 drop_len += sizeof(DROPFILES) + 1;
1238 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1239 lpDrop = (DROPFILES *) GlobalLock( hDrop );
1241 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1243 lpDrop->pFiles = sizeof(DROPFILES);
1244 lpDrop->pt.x = x;
1245 lpDrop->pt.y = y;
1246 lpDrop->fNC =
1247 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1248 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1249 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1250 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1251 lpDrop->fWide = FALSE;
1252 p_drop = (char*)(lpDrop + 1);
1255 /* create message content */
1256 if (p_drop) {
1257 p = (char*) p_data;
1258 next = strchr(p, '\n');
1259 while (p) {
1260 if (next) *next=0;
1261 if (strncmp(p,"file:",5) == 0 ) {
1262 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1263 if (len) {
1264 TRACE("drop file %s as %s\n", p+5, p_drop);
1265 p_drop += len+1;
1266 } else {
1267 WARN("can't convert file %s to dos name\n", p+5);
1269 } else {
1270 WARN("unknown mime type %s\n", p);
1272 if (next) {
1273 *next = '\n';
1274 p = next + 1;
1275 next = strchr(p, '\n');
1276 } else {
1277 p = NULL;
1279 *p_drop = '\0';
1282 GlobalUnlock(hDrop);
1283 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1286 wine_tsx11_lock();
1287 if( p_data ) XFree(p_data);
1288 wine_tsx11_unlock();
1292 /**********************************************************************
1293 * handle_dnd_protocol
1295 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1297 Window root, child;
1298 int root_x, root_y, child_x, child_y;
1299 unsigned int u;
1301 /* query window (drag&drop event contains only drag window) */
1302 wine_tsx11_lock();
1303 XQueryPointer( event->display, root_window, &root, &child,
1304 &root_x, &root_y, &child_x, &child_y, &u);
1305 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1306 wine_tsx11_unlock();
1307 if (!hwnd) return;
1308 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1309 EVENT_DropFromOffiX(hwnd, event);
1310 else if (event->data.l[0] == DndURL)
1311 EVENT_DropURLs(hwnd, event);
1315 struct client_message_handler
1317 int atom; /* protocol atom */
1318 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1321 static const struct client_message_handler client_messages[] =
1323 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1324 { XATOM_DndProtocol, handle_dnd_protocol },
1325 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1326 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1327 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1328 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1332 /**********************************************************************
1333 * X11DRV_ClientMessage
1335 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1337 XClientMessageEvent *event = &xev->xclient;
1338 unsigned int i;
1340 if (!hwnd) return;
1342 if (event->format != 32)
1344 WARN( "Don't know how to handle format %d\n", event->format );
1345 return;
1348 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1350 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1352 client_messages[i].handler( hwnd, event );
1353 return;
1356 TRACE( "no handler found for %ld\n", event->message_type );
1360 /***********************************************************************
1361 * X11DRV_SendInput (X11DRV.@)
1363 UINT X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1365 UINT i;
1367 for (i = 0; i < count; i++, inputs++)
1369 switch(inputs->type)
1371 case INPUT_MOUSE:
1372 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1373 inputs->u.mi.mouseData, inputs->u.mi.time,
1374 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1375 break;
1376 case INPUT_KEYBOARD:
1377 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1378 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1379 break;
1380 case INPUT_HARDWARE:
1381 FIXME( "INPUT_HARDWARE not supported\n" );
1382 break;
1385 return count;