user32/tests: Restructure the DDE end-to-end tests to make it easier to add new cases.
[wine/hacks.git] / dlls / winex11.drv / event.c
blob64d1992ea9fbd1fdba5e6b6ac73b804be972bdf8
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( "%lu %s for hwnd/window %p/%lx\n",
312 event->xany.serial, 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)(Display*, XEvent*,XPointer), 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 if (prev_event.type) call_event_handler( display, &prev_event );
383 XFlush( gdi_display );
384 wine_tsx11_unlock();
385 if (count) TRACE( "processed %d events\n", count );
386 return count;
390 /***********************************************************************
391 * MsgWaitForMultipleObjectsEx (X11DRV.@)
393 DWORD CDECL 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 (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 /**********************************************************************
473 * set_focus
475 static void set_focus( Display *display, HWND hwnd, Time time )
477 HWND focus;
478 Window win;
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);
489 if (win)
491 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
492 wine_tsx11_lock();
493 XSetInputFocus( display, win, RevertToParent, time );
494 wine_tsx11_unlock();
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);
515 return;
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))
524 HMENU hSysMenu;
526 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
527 hSysMenu = GetSystemMenu(hwnd, FALSE);
528 if (hSysMenu)
530 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
531 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
532 return;
534 if (GetActiveWindow() != hwnd)
536 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
537 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
538 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
539 switch(ma)
541 case MA_NOACTIVATEANDEAT:
542 case MA_ACTIVATEANDEAT:
543 return;
544 case MA_NOACTIVATE:
545 break;
546 case MA_ACTIVATE:
547 case 0:
548 SetActiveWindow(hwnd);
549 break;
550 default:
551 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
552 break;
556 PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
559 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
561 Time event_time = (Time)event->data.l[1];
562 HWND last_focus = x11drv_thread_data()->last_focus;
564 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
565 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
566 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
568 if (can_activate_window(hwnd))
570 /* simulate a mouse click on the caption to find out
571 * whether the window wants to be activated */
572 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
573 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
574 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
575 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
577 set_focus( event->display, hwnd, event_time );
578 return;
581 else if (hwnd == GetDesktopWindow())
583 hwnd = GetForegroundWindow();
584 if (!hwnd) hwnd = last_focus;
585 if (!hwnd) hwnd = GetDesktopWindow();
586 set_focus( event->display, hwnd, event_time );
587 return;
589 /* try to find some other window to give the focus to */
590 hwnd = GetFocus();
591 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
592 if (!hwnd) hwnd = GetActiveWindow();
593 if (!hwnd) hwnd = last_focus;
594 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
596 else if (protocol == x11drv_atom(_NET_WM_PING))
598 XClientMessageEvent xev;
599 xev = *event;
601 TRACE("NET_WM Ping\n");
602 wine_tsx11_lock();
603 xev.window = DefaultRootWindow(xev.display);
604 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
605 wine_tsx11_unlock();
606 /* this line is semi-stolen from gtk2 */
607 TRACE("NET_WM Pong\n");
612 static const char * const focus_details[] =
614 "NotifyAncestor",
615 "NotifyVirtual",
616 "NotifyInferior",
617 "NotifyNonlinear",
618 "NotifyNonlinearVirtual",
619 "NotifyPointer",
620 "NotifyPointerRoot",
621 "NotifyDetailNone"
624 /**********************************************************************
625 * X11DRV_FocusIn
627 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
629 XFocusChangeEvent *event = &xev->xfocus;
630 XIC xic;
632 if (!hwnd) return;
634 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
636 if (event->detail == NotifyPointer) return;
638 if ((xic = X11DRV_get_ic( hwnd )))
640 wine_tsx11_lock();
641 XSetICFocus( xic );
642 wine_tsx11_unlock();
644 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
646 if (!can_activate_window(hwnd))
648 HWND hwnd = GetFocus();
649 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
650 if (!hwnd) hwnd = GetActiveWindow();
651 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
652 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
654 else SetForegroundWindow( hwnd );
658 /**********************************************************************
659 * X11DRV_FocusOut
661 * Note: only top-level windows get FocusOut events.
663 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
665 XFocusChangeEvent *event = &xev->xfocus;
666 HWND hwnd_tmp;
667 Window focus_win;
668 int revert;
669 XIC xic;
671 if (!hwnd) return;
673 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
675 if (event->detail == NotifyPointer) return;
676 if (ximInComposeMode) return;
678 x11drv_thread_data()->last_focus = hwnd;
679 if ((xic = X11DRV_get_ic( hwnd )))
681 wine_tsx11_lock();
682 XUnsetICFocus( xic );
683 wine_tsx11_unlock();
685 if (hwnd != GetForegroundWindow()) return;
686 if (root_window != DefaultRootWindow(event->display)) return;
687 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
689 /* don't reset the foreground window, if the window which is
690 getting the focus is a Wine window */
692 wine_tsx11_lock();
693 XGetInputFocus( event->display, &focus_win, &revert );
694 if (focus_win)
696 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
697 focus_win = 0;
699 wine_tsx11_unlock();
701 if (!focus_win)
703 /* Abey : 6-Oct-99. Check again if the focus out window is the
704 Foreground window, because in most cases the messages sent
705 above must have already changed the foreground window, in which
706 case we don't have to change the foreground window to 0 */
707 if (hwnd == GetForegroundWindow())
709 TRACE( "lost focus, setting fg to desktop\n" );
710 SetForegroundWindow( GetDesktopWindow() );
716 /***********************************************************************
717 * X11DRV_Expose
719 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
721 XExposeEvent *event = &xev->xexpose;
722 RECT rect;
723 struct x11drv_win_data *data;
724 int flags = RDW_INVALIDATE | RDW_ERASE;
726 TRACE( "win %p (%lx) %d,%d %dx%d\n",
727 hwnd, event->window, event->x, event->y, event->width, event->height );
729 if (!(data = X11DRV_get_win_data( hwnd ))) return;
731 if (event->window == data->whole_window)
733 rect.left = data->whole_rect.left + event->x;
734 rect.top = data->whole_rect.top + event->y;
735 flags |= RDW_FRAME;
737 else
739 rect.left = data->client_rect.left + event->x;
740 rect.top = data->client_rect.top + event->y;
742 rect.right = rect.left + event->width;
743 rect.bottom = rect.top + event->height;
745 if (event->window != root_window)
747 SERVER_START_REQ( update_window_zorder )
749 req->window = wine_server_user_handle( hwnd );
750 req->rect.left = rect.left;
751 req->rect.top = rect.top;
752 req->rect.right = rect.right;
753 req->rect.bottom = rect.bottom;
754 wine_server_call( req );
756 SERVER_END_REQ;
758 /* make position relative to client area instead of parent */
759 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
760 flags |= RDW_ALLCHILDREN;
763 RedrawWindow( hwnd, &rect, 0, flags );
767 /**********************************************************************
768 * X11DRV_MapNotify
770 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
772 struct x11drv_win_data *data;
774 if (!(data = X11DRV_get_win_data( hwnd ))) return;
775 if (!data->mapped) return;
777 if (!data->managed)
779 HWND hwndFocus = GetFocus();
780 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
785 /***********************************************************************
786 * is_net_wm_state_maximized
788 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
790 Atom type, *state;
791 int format, ret = 0;
792 unsigned long i, count, remaining;
794 wine_tsx11_lock();
795 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
796 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
797 &remaining, (unsigned char **)&state ))
799 if (type == XA_ATOM && format == 32)
801 for (i = 0; i < count; i++)
803 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
804 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
805 ret++;
808 XFree( state );
810 wine_tsx11_unlock();
811 return (ret == 2);
815 /***********************************************************************
816 * X11DRV_ConfigureNotify
818 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
820 XConfigureEvent *event = &xev->xconfigure;
821 struct x11drv_win_data *data;
822 RECT rect;
823 UINT flags;
824 int cx, cy, x = event->x, y = event->y;
826 if (!hwnd) return;
827 if (!(data = X11DRV_get_win_data( hwnd ))) return;
828 if (!data->mapped || data->iconic || !data->managed) return;
829 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
831 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
832 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
833 event->serial, data->configure_serial );
834 return;
837 /* Get geometry */
839 if (!event->send_event) /* normal event, need to map coordinates to the root */
841 Window child;
842 wine_tsx11_lock();
843 XTranslateCoordinates( event->display, data->whole_window, root_window,
844 0, 0, &x, &y, &child );
845 wine_tsx11_unlock();
847 rect.left = x;
848 rect.top = y;
849 rect.right = x + event->width;
850 rect.bottom = y + event->height;
851 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
852 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
853 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
854 event->x, event->y, event->width, event->height );
855 X11DRV_X_to_window_rect( data, &rect );
857 if (is_net_wm_state_maximized( event->display, data ))
859 if (!IsZoomed( data->hwnd ))
861 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
862 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
863 return;
866 else
868 if (IsZoomed( data->hwnd ))
870 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
871 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
872 return;
876 /* Compare what has changed */
878 x = rect.left;
879 y = rect.top;
880 cx = rect.right - rect.left;
881 cy = rect.bottom - rect.top;
882 flags = SWP_NOACTIVATE | SWP_NOZORDER;
884 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
885 else
886 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
887 hwnd, data->window_rect.left, data->window_rect.top, x, y );
889 if ((data->window_rect.right - data->window_rect.left == cx &&
890 data->window_rect.bottom - data->window_rect.top == cy) ||
891 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
893 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
894 flags |= SWP_NOSIZE;
896 else
897 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
898 hwnd, data->window_rect.right - data->window_rect.left,
899 data->window_rect.bottom - data->window_rect.top, cx, cy );
901 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
905 /***********************************************************************
906 * get_window_wm_state
908 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
910 struct
912 CARD32 state;
913 XID icon;
914 } *state;
915 Atom type;
916 int format, ret = -1;
917 unsigned long count, remaining;
919 wine_tsx11_lock();
920 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
921 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
922 &type, &format, &count, &remaining, (unsigned char **)&state ))
924 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
925 ret = state->state;
926 XFree( state );
928 wine_tsx11_unlock();
929 return ret;
933 /***********************************************************************
934 * handle_wm_state_notify
936 * Handle a PropertyNotify for WM_STATE.
938 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
939 BOOL update_window )
941 DWORD style;
943 switch(event->state)
945 case PropertyDelete:
946 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
947 data->wm_state = WithdrawnState;
948 break;
949 case PropertyNewValue:
951 int old_state = data->wm_state;
952 int new_state = get_window_wm_state( event->display, data );
953 if (new_state != -1 && new_state != data->wm_state)
955 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
956 data->hwnd, data->whole_window, new_state, old_state );
957 data->wm_state = new_state;
958 /* ignore the initial state transition out of withdrawn state */
959 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
960 if (!old_state) return;
963 break;
966 if (!update_window || !data->managed || !data->mapped) return;
968 style = GetWindowLongW( data->hwnd, GWL_STYLE );
970 if (data->iconic && data->wm_state == NormalState) /* restore window */
972 data->iconic = FALSE;
973 if (is_net_wm_state_maximized( event->display, data ))
975 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
977 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
978 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
980 else TRACE( "not restoring to max win %p/%lx style %08x\n",
981 data->hwnd, data->whole_window, style );
983 else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
985 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
986 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
988 else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
990 else if (!data->iconic && data->wm_state == IconicState)
992 data->iconic = TRUE;
993 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
995 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
996 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
998 else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1003 /***********************************************************************
1004 * X11DRV_PropertyNotify
1006 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1008 XPropertyEvent *event = &xev->xproperty;
1009 struct x11drv_win_data *data;
1011 if (!hwnd) return;
1012 if (!(data = X11DRV_get_win_data( hwnd ))) return;
1014 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1018 /* event filter to wait for a WM_STATE change notification on a window */
1019 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1021 if (event->xany.window != (Window)arg) return 0;
1022 return (event->type == DestroyNotify ||
1023 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1026 /***********************************************************************
1027 * wait_for_withdrawn_state
1029 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1031 DWORD end = GetTickCount() + 2000;
1033 if (!data->managed) return;
1035 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1036 data->hwnd, data->whole_window, set ? "" : "not " );
1038 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1040 XEvent event;
1041 int count = 0;
1043 wine_tsx11_lock();
1044 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1046 count++;
1047 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1048 if (event.type == DestroyNotify) call_event_handler( display, &event );
1049 else
1051 wine_tsx11_unlock();
1052 handle_wm_state_notify( data, &event.xproperty, FALSE );
1053 wine_tsx11_lock();
1056 wine_tsx11_unlock();
1058 if (!count)
1060 struct pollfd pfd;
1061 int timeout = end - GetTickCount();
1063 pfd.fd = ConnectionNumber(display);
1064 pfd.events = POLLIN;
1065 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1067 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1068 break;
1072 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1076 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1078 RECT tempRect;
1080 if (!IsWindowEnabled(hQueryWnd)) return 0;
1082 GetWindowRect(hQueryWnd, &tempRect);
1084 if(!PtInRect(&tempRect, *lpPt)) return 0;
1086 if (!IsIconic( hQueryWnd ))
1088 POINT pt = *lpPt;
1089 ScreenToClient( hQueryWnd, &pt );
1090 GetClientRect( hQueryWnd, &tempRect );
1092 if (PtInRect( &tempRect, pt))
1094 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1095 if (ret && ret != hQueryWnd)
1097 ret = find_drop_window( ret, lpPt );
1098 if (ret) return ret;
1103 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1105 ScreenToClient(hQueryWnd, lpPt);
1107 return hQueryWnd;
1110 /**********************************************************************
1111 * EVENT_DropFromOffix
1113 * don't know if it still works (last Changelog is from 96/11/04)
1115 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1117 struct x11drv_win_data *data;
1118 unsigned long data_length;
1119 unsigned long aux_long;
1120 unsigned char* p_data = NULL;
1121 Atom atom_aux;
1122 int x, y, dummy;
1123 BOOL bAccept;
1124 Window win, w_aux_root, w_aux_child;
1126 win = X11DRV_get_whole_window(hWnd);
1127 wine_tsx11_lock();
1128 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1129 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1130 x += virtual_screen_rect.left;
1131 y += virtual_screen_rect.top;
1132 wine_tsx11_unlock();
1134 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1136 /* find out drop point and drop window */
1137 if( x < 0 || y < 0 ||
1138 x > (data->whole_rect.right - data->whole_rect.left) ||
1139 y > (data->whole_rect.bottom - data->whole_rect.top) )
1141 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1142 x = 0;
1143 y = 0;
1145 else
1147 POINT pt = { x, y };
1148 HWND hwndDrop = find_drop_window( hWnd, &pt );
1149 if (hwndDrop)
1151 x = pt.x;
1152 y = pt.y;
1153 bAccept = TRUE;
1155 else
1157 bAccept = FALSE;
1161 if (!bAccept) return;
1163 wine_tsx11_lock();
1164 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1165 x11drv_atom(DndSelection), 0, 65535, FALSE,
1166 AnyPropertyType, &atom_aux, &dummy,
1167 &data_length, &aux_long, &p_data);
1168 wine_tsx11_unlock();
1170 if( !aux_long && p_data) /* don't bother if > 64K */
1172 char *p = (char *)p_data;
1173 char *p_drop;
1175 aux_long = 0;
1176 while( *p ) /* calculate buffer size */
1178 INT len = GetShortPathNameA( p, NULL, 0 );
1179 if (len) aux_long += len + 1;
1180 p += strlen(p) + 1;
1182 if( aux_long && aux_long < 65535 )
1184 HDROP hDrop;
1185 DROPFILES *lpDrop;
1187 aux_long += sizeof(DROPFILES) + 1;
1188 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1189 lpDrop = GlobalLock( hDrop );
1191 if( lpDrop )
1193 lpDrop->pFiles = sizeof(DROPFILES);
1194 lpDrop->pt.x = x;
1195 lpDrop->pt.y = y;
1196 lpDrop->fNC = FALSE;
1197 lpDrop->fWide = FALSE;
1198 p_drop = (char *)(lpDrop + 1);
1199 p = (char *)p_data;
1200 while(*p)
1202 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1203 p_drop += strlen( p_drop ) + 1;
1204 p += strlen(p) + 1;
1206 *p_drop = '\0';
1207 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1211 wine_tsx11_lock();
1212 if( p_data ) XFree(p_data);
1213 wine_tsx11_unlock();
1216 /**********************************************************************
1217 * EVENT_DropURLs
1219 * drop items are separated by \n
1220 * each item is prefixed by its mime type
1222 * event->data.l[3], event->data.l[4] contains drop x,y position
1224 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1226 struct x11drv_win_data *win_data;
1227 unsigned long data_length;
1228 unsigned long aux_long, drop_len = 0;
1229 unsigned char *p_data = NULL; /* property data */
1230 char *p_drop = NULL;
1231 char *p, *next;
1232 int x, y;
1233 DROPFILES *lpDrop;
1234 HDROP hDrop;
1235 union {
1236 Atom atom_aux;
1237 int i;
1238 Window w_aux;
1239 unsigned int u;
1240 } u; /* unused */
1242 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1244 wine_tsx11_lock();
1245 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1246 x11drv_atom(DndSelection), 0, 65535, FALSE,
1247 AnyPropertyType, &u.atom_aux, &u.i,
1248 &data_length, &aux_long, &p_data);
1249 wine_tsx11_unlock();
1250 if (aux_long)
1251 WARN("property too large, truncated!\n");
1252 TRACE("urls=%s\n", p_data);
1254 if( !aux_long && p_data) { /* don't bother if > 64K */
1255 /* calculate length */
1256 p = (char*) p_data;
1257 next = strchr(p, '\n');
1258 while (p) {
1259 if (next) *next=0;
1260 if (strncmp(p,"file:",5) == 0 ) {
1261 INT len = GetShortPathNameA( p+5, NULL, 0 );
1262 if (len) drop_len += len + 1;
1264 if (next) {
1265 *next = '\n';
1266 p = next + 1;
1267 next = strchr(p, '\n');
1268 } else {
1269 p = NULL;
1273 if( drop_len && drop_len < 65535 ) {
1274 wine_tsx11_lock();
1275 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1276 &x, &y, &u.i, &u.i, &u.u);
1277 x += virtual_screen_rect.left;
1278 y += virtual_screen_rect.top;
1279 wine_tsx11_unlock();
1281 drop_len += sizeof(DROPFILES) + 1;
1282 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1283 lpDrop = GlobalLock( hDrop );
1285 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1287 lpDrop->pFiles = sizeof(DROPFILES);
1288 lpDrop->pt.x = x;
1289 lpDrop->pt.y = y;
1290 lpDrop->fNC =
1291 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1292 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1293 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1294 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1295 lpDrop->fWide = FALSE;
1296 p_drop = (char*)(lpDrop + 1);
1299 /* create message content */
1300 if (p_drop) {
1301 p = (char*) p_data;
1302 next = strchr(p, '\n');
1303 while (p) {
1304 if (next) *next=0;
1305 if (strncmp(p,"file:",5) == 0 ) {
1306 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1307 if (len) {
1308 TRACE("drop file %s as %s\n", p+5, p_drop);
1309 p_drop += len+1;
1310 } else {
1311 WARN("can't convert file %s to dos name\n", p+5);
1313 } else {
1314 WARN("unknown mime type %s\n", p);
1316 if (next) {
1317 *next = '\n';
1318 p = next + 1;
1319 next = strchr(p, '\n');
1320 } else {
1321 p = NULL;
1323 *p_drop = '\0';
1326 GlobalUnlock(hDrop);
1327 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1330 wine_tsx11_lock();
1331 if( p_data ) XFree(p_data);
1332 wine_tsx11_unlock();
1336 /**********************************************************************
1337 * handle_dnd_protocol
1339 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1341 Window root, child;
1342 int root_x, root_y, child_x, child_y;
1343 unsigned int u;
1345 /* query window (drag&drop event contains only drag window) */
1346 wine_tsx11_lock();
1347 XQueryPointer( event->display, root_window, &root, &child,
1348 &root_x, &root_y, &child_x, &child_y, &u);
1349 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1350 wine_tsx11_unlock();
1351 if (!hwnd) return;
1352 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1353 EVENT_DropFromOffiX(hwnd, event);
1354 else if (event->data.l[0] == DndURL)
1355 EVENT_DropURLs(hwnd, event);
1359 struct client_message_handler
1361 int atom; /* protocol atom */
1362 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1365 static const struct client_message_handler client_messages[] =
1367 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1368 { XATOM_DndProtocol, handle_dnd_protocol },
1369 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1370 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1371 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1372 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1376 /**********************************************************************
1377 * X11DRV_ClientMessage
1379 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1381 XClientMessageEvent *event = &xev->xclient;
1382 unsigned int i;
1384 if (!hwnd) return;
1386 if (event->format != 32)
1388 WARN( "Don't know how to handle format %d\n", event->format );
1389 return;
1392 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1394 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1396 client_messages[i].handler( hwnd, event );
1397 return;
1400 TRACE( "no handler found for %ld\n", event->message_type );
1404 /***********************************************************************
1405 * X11DRV_SendInput (X11DRV.@)
1407 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1409 UINT i;
1411 for (i = 0; i < count; i++, inputs++)
1413 switch(inputs->type)
1415 case INPUT_MOUSE:
1416 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1417 inputs->u.mi.mouseData, inputs->u.mi.time,
1418 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1419 break;
1420 case INPUT_KEYBOARD:
1421 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1422 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1423 break;
1424 case INPUT_HARDWARE:
1425 FIXME( "INPUT_HARDWARE not supported\n" );
1426 break;
1429 return count;