push 38a8c0aff9170390b5a3ded41e7cf5b02f3a73d8
[wine/hacks.git] / dlls / winex11.drv / event.c
blobe8e4522dfe5f995e7795ec14a1ed7e298853c04a
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)(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 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 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 /* Ignore the delete window request if the window has been disabled
511 * and we are in managed mode. This is to disallow applications from
512 * being closed by the window manager while in a modal state.
514 if (IsWindowEnabled(hwnd))
516 HMENU hSysMenu;
517 POINT pt;
519 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
520 hSysMenu = GetSystemMenu(hwnd, FALSE);
521 if (hSysMenu)
523 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
524 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
525 return;
527 if (GetActiveWindow() != hwnd)
529 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
530 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
531 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
532 switch(ma)
534 case MA_NOACTIVATEANDEAT:
535 case MA_ACTIVATEANDEAT:
536 return;
537 case MA_NOACTIVATE:
538 break;
539 case MA_ACTIVATE:
540 case 0:
541 SetActiveWindow(hwnd);
542 break;
543 default:
544 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
545 break;
548 /* Simulate clicking the caption Close button */
549 GetCursorPos( &pt );
550 PostMessageW( hwnd, WM_NCLBUTTONDOWN, HTCLOSE, MAKELPARAM( pt.x, pt.y ) );
551 PostMessageW( hwnd, WM_LBUTTONUP, HTCLOSE, MAKELPARAM( pt.x, pt.y ) );
554 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
556 Time event_time = (Time)event->data.l[1];
557 HWND last_focus = x11drv_thread_data()->last_focus;
559 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
560 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
561 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
563 if (can_activate_window(hwnd))
565 /* simulate a mouse click on the caption to find out
566 * whether the window wants to be activated */
567 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
568 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
569 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
570 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
572 set_focus( event->display, hwnd, event_time );
573 return;
576 else if (hwnd == GetDesktopWindow())
578 hwnd = GetForegroundWindow();
579 if (!hwnd) hwnd = last_focus;
580 if (!hwnd) hwnd = GetDesktopWindow();
581 set_focus( event->display, hwnd, event_time );
582 return;
584 /* try to find some other window to give the focus to */
585 hwnd = GetFocus();
586 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
587 if (!hwnd) hwnd = GetActiveWindow();
588 if (!hwnd) hwnd = last_focus;
589 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
591 else if (protocol == x11drv_atom(_NET_WM_PING))
593 XClientMessageEvent xev;
594 xev = *event;
596 TRACE("NET_WM Ping\n");
597 wine_tsx11_lock();
598 xev.window = DefaultRootWindow(xev.display);
599 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
600 wine_tsx11_unlock();
601 /* this line is semi-stolen from gtk2 */
602 TRACE("NET_WM Pong\n");
607 static const char * const focus_details[] =
609 "NotifyAncestor",
610 "NotifyVirtual",
611 "NotifyInferior",
612 "NotifyNonlinear",
613 "NotifyNonlinearVirtual",
614 "NotifyPointer",
615 "NotifyPointerRoot",
616 "NotifyDetailNone"
619 /**********************************************************************
620 * X11DRV_FocusIn
622 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
624 XFocusChangeEvent *event = &xev->xfocus;
625 XIC xic;
627 if (!hwnd) return;
629 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
631 if (event->detail == NotifyPointer) return;
633 if ((xic = X11DRV_get_ic( hwnd )))
635 wine_tsx11_lock();
636 XSetICFocus( xic );
637 wine_tsx11_unlock();
639 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
641 if (!can_activate_window(hwnd))
643 HWND hwnd = GetFocus();
644 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
645 if (!hwnd) hwnd = GetActiveWindow();
646 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
647 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
649 else SetForegroundWindow( hwnd );
653 /**********************************************************************
654 * X11DRV_FocusOut
656 * Note: only top-level windows get FocusOut events.
658 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
660 XFocusChangeEvent *event = &xev->xfocus;
661 HWND hwnd_tmp;
662 Window focus_win;
663 int revert;
664 XIC xic;
666 if (!hwnd) return;
668 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
670 if (event->detail == NotifyPointer) return;
671 if (ximInComposeMode) return;
673 x11drv_thread_data()->last_focus = hwnd;
674 if ((xic = X11DRV_get_ic( hwnd )))
676 wine_tsx11_lock();
677 XUnsetICFocus( xic );
678 wine_tsx11_unlock();
680 if (hwnd != GetForegroundWindow()) return;
681 if (root_window != DefaultRootWindow(event->display)) return;
682 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
684 /* don't reset the foreground window, if the window which is
685 getting the focus is a Wine window */
687 wine_tsx11_lock();
688 XGetInputFocus( event->display, &focus_win, &revert );
689 if (focus_win)
691 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
692 focus_win = 0;
694 wine_tsx11_unlock();
696 if (!focus_win)
698 /* Abey : 6-Oct-99. Check again if the focus out window is the
699 Foreground window, because in most cases the messages sent
700 above must have already changed the foreground window, in which
701 case we don't have to change the foreground window to 0 */
702 if (hwnd == GetForegroundWindow())
704 TRACE( "lost focus, setting fg to desktop\n" );
705 SetForegroundWindow( GetDesktopWindow() );
711 /***********************************************************************
712 * X11DRV_Expose
714 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
716 XExposeEvent *event = &xev->xexpose;
717 RECT rect;
718 struct x11drv_win_data *data;
719 int flags = RDW_INVALIDATE | RDW_ERASE;
721 TRACE( "win %p (%lx) %d,%d %dx%d\n",
722 hwnd, event->window, event->x, event->y, event->width, event->height );
724 if (!(data = X11DRV_get_win_data( hwnd ))) return;
726 if (event->window == data->whole_window)
728 rect.left = data->whole_rect.left + event->x;
729 rect.top = data->whole_rect.top + event->y;
730 flags |= RDW_FRAME;
732 else
734 rect.left = data->client_rect.left + event->x;
735 rect.top = data->client_rect.top + event->y;
737 rect.right = rect.left + event->width;
738 rect.bottom = rect.top + event->height;
740 if (event->window != root_window)
742 SERVER_START_REQ( update_window_zorder )
744 req->window = wine_server_user_handle( hwnd );
745 req->rect.left = rect.left;
746 req->rect.top = rect.top;
747 req->rect.right = rect.right;
748 req->rect.bottom = rect.bottom;
749 wine_server_call( req );
751 SERVER_END_REQ;
753 /* make position relative to client area instead of parent */
754 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
755 flags |= RDW_ALLCHILDREN;
758 RedrawWindow( hwnd, &rect, 0, flags );
762 /**********************************************************************
763 * X11DRV_MapNotify
765 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
767 struct x11drv_win_data *data;
769 if (!(data = X11DRV_get_win_data( hwnd ))) return;
770 if (!data->mapped) return;
772 if (!data->managed)
774 HWND hwndFocus = GetFocus();
775 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
780 /***********************************************************************
781 * is_net_wm_state_maximized
783 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
785 Atom type, *state;
786 int format, ret = 0;
787 unsigned long i, count, remaining;
789 wine_tsx11_lock();
790 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
791 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
792 &remaining, (unsigned char **)&state ))
794 if (type == XA_ATOM && format == 32)
796 for (i = 0; i < count; i++)
798 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
799 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
800 ret++;
803 XFree( state );
805 wine_tsx11_unlock();
806 return (ret == 2);
810 /***********************************************************************
811 * X11DRV_ConfigureNotify
813 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
815 XConfigureEvent *event = &xev->xconfigure;
816 struct x11drv_win_data *data;
817 RECT rect;
818 UINT flags;
819 int cx, cy, x = event->x, y = event->y;
821 if (!hwnd) return;
822 if (!(data = X11DRV_get_win_data( hwnd ))) return;
823 if (!data->mapped || data->iconic || !data->managed) return;
825 /* Get geometry */
827 if (!event->send_event) /* normal event, need to map coordinates to the root */
829 Window child;
830 wine_tsx11_lock();
831 XTranslateCoordinates( event->display, data->whole_window, root_window,
832 0, 0, &x, &y, &child );
833 wine_tsx11_unlock();
835 rect.left = x;
836 rect.top = y;
837 rect.right = x + event->width;
838 rect.bottom = y + event->height;
839 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
840 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
841 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
842 event->x, event->y, event->width, event->height );
843 X11DRV_X_to_window_rect( data, &rect );
845 if (is_net_wm_state_maximized( event->display, data ))
847 if (!IsZoomed( data->hwnd ))
849 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
850 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
851 return;
854 else
856 if (IsZoomed( data->hwnd ))
858 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
859 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
860 return;
864 /* Compare what has changed */
866 x = rect.left;
867 y = rect.top;
868 cx = rect.right - rect.left;
869 cy = rect.bottom - rect.top;
870 flags = SWP_NOACTIVATE | SWP_NOZORDER;
872 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
873 else
874 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
875 hwnd, data->window_rect.left, data->window_rect.top, x, y );
877 if ((data->window_rect.right - data->window_rect.left == cx &&
878 data->window_rect.bottom - data->window_rect.top == cy) ||
879 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
881 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
882 flags |= SWP_NOSIZE;
884 else
885 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
886 hwnd, data->window_rect.right - data->window_rect.left,
887 data->window_rect.bottom - data->window_rect.top, cx, cy );
889 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
893 /***********************************************************************
894 * get_window_wm_state
896 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
898 struct
900 CARD32 state;
901 XID icon;
902 } *state;
903 Atom type;
904 int format, ret = -1;
905 unsigned long count, remaining;
907 wine_tsx11_lock();
908 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
909 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
910 &type, &format, &count, &remaining, (unsigned char **)&state ))
912 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
913 ret = state->state;
914 XFree( state );
916 wine_tsx11_unlock();
917 return ret;
921 /***********************************************************************
922 * handle_wm_state_notify
924 * Handle a PropertyNotify for WM_STATE.
926 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
927 BOOL update_window )
929 switch(event->state)
931 case PropertyDelete:
932 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
933 data->wm_state = WithdrawnState;
934 break;
935 case PropertyNewValue:
937 int old_state = data->wm_state;
938 int new_state = get_window_wm_state( event->display, data );
939 if (new_state != -1 && new_state != data->wm_state)
941 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
942 data->hwnd, data->whole_window, new_state, old_state );
943 data->wm_state = new_state;
944 /* ignore the initial state transition out of withdrawn state */
945 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
946 if (!old_state) return;
949 break;
952 if (!update_window || !data->managed || !data->mapped) return;
954 if (data->iconic && data->wm_state == NormalState) /* restore window */
956 data->iconic = FALSE;
957 if (is_net_wm_state_maximized( event->display, data ))
959 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
960 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
962 else
964 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
965 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
968 else if (!data->iconic && data->wm_state == IconicState)
970 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
971 data->iconic = TRUE;
972 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
977 /***********************************************************************
978 * X11DRV_PropertyNotify
980 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
982 XPropertyEvent *event = &xev->xproperty;
983 struct x11drv_win_data *data;
985 if (!hwnd) return;
986 if (!(data = X11DRV_get_win_data( hwnd ))) return;
988 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
992 /* event filter to wait for a WM_STATE change notification on a window */
993 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
995 if (event->xany.window != (Window)arg) return 0;
996 return (event->type == DestroyNotify ||
997 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1000 /***********************************************************************
1001 * wait_for_withdrawn_state
1003 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1005 DWORD end = GetTickCount() + 2000;
1007 if (!data->managed) return;
1009 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1010 data->hwnd, data->whole_window, set ? "" : "not " );
1012 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1014 XEvent event;
1015 int count = 0;
1017 wine_tsx11_lock();
1018 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1020 count++;
1021 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1022 if (event.type == DestroyNotify) call_event_handler( display, &event );
1023 else
1025 wine_tsx11_unlock();
1026 handle_wm_state_notify( data, &event.xproperty, FALSE );
1027 wine_tsx11_lock();
1030 wine_tsx11_unlock();
1032 if (!count)
1034 struct pollfd pfd;
1035 int timeout = end - GetTickCount();
1037 pfd.fd = ConnectionNumber(display);
1038 pfd.events = POLLIN;
1039 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1041 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1042 break;
1046 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1050 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1052 RECT tempRect;
1054 if (!IsWindowEnabled(hQueryWnd)) return 0;
1056 GetWindowRect(hQueryWnd, &tempRect);
1058 if(!PtInRect(&tempRect, *lpPt)) return 0;
1060 if (!IsIconic( hQueryWnd ))
1062 POINT pt = *lpPt;
1063 ScreenToClient( hQueryWnd, &pt );
1064 GetClientRect( hQueryWnd, &tempRect );
1066 if (PtInRect( &tempRect, pt))
1068 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1069 if (ret && ret != hQueryWnd)
1071 ret = find_drop_window( ret, lpPt );
1072 if (ret) return ret;
1077 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1079 ScreenToClient(hQueryWnd, lpPt);
1081 return hQueryWnd;
1084 /**********************************************************************
1085 * EVENT_DropFromOffix
1087 * don't know if it still works (last Changelog is from 96/11/04)
1089 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1091 struct x11drv_win_data *data;
1092 unsigned long data_length;
1093 unsigned long aux_long;
1094 unsigned char* p_data = NULL;
1095 Atom atom_aux;
1096 int x, y, dummy;
1097 BOOL bAccept;
1098 Window win, w_aux_root, w_aux_child;
1100 win = X11DRV_get_whole_window(hWnd);
1101 wine_tsx11_lock();
1102 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1103 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1104 x += virtual_screen_rect.left;
1105 y += virtual_screen_rect.top;
1106 wine_tsx11_unlock();
1108 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1110 /* find out drop point and drop window */
1111 if( x < 0 || y < 0 ||
1112 x > (data->whole_rect.right - data->whole_rect.left) ||
1113 y > (data->whole_rect.bottom - data->whole_rect.top) )
1115 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1116 x = 0;
1117 y = 0;
1119 else
1121 POINT pt = { x, y };
1122 HWND hwndDrop = find_drop_window( hWnd, &pt );
1123 if (hwndDrop)
1125 x = pt.x;
1126 y = pt.y;
1127 bAccept = TRUE;
1129 else
1131 bAccept = FALSE;
1135 if (!bAccept) return;
1137 wine_tsx11_lock();
1138 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1139 x11drv_atom(DndSelection), 0, 65535, FALSE,
1140 AnyPropertyType, &atom_aux, &dummy,
1141 &data_length, &aux_long, &p_data);
1142 wine_tsx11_unlock();
1144 if( !aux_long && p_data) /* don't bother if > 64K */
1146 char *p = (char *)p_data;
1147 char *p_drop;
1149 aux_long = 0;
1150 while( *p ) /* calculate buffer size */
1152 INT len = GetShortPathNameA( p, NULL, 0 );
1153 if (len) aux_long += len + 1;
1154 p += strlen(p) + 1;
1156 if( aux_long && aux_long < 65535 )
1158 HDROP hDrop;
1159 DROPFILES *lpDrop;
1161 aux_long += sizeof(DROPFILES) + 1;
1162 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1163 lpDrop = GlobalLock( hDrop );
1165 if( lpDrop )
1167 lpDrop->pFiles = sizeof(DROPFILES);
1168 lpDrop->pt.x = x;
1169 lpDrop->pt.y = y;
1170 lpDrop->fNC = FALSE;
1171 lpDrop->fWide = FALSE;
1172 p_drop = (char *)(lpDrop + 1);
1173 p = (char *)p_data;
1174 while(*p)
1176 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1177 p_drop += strlen( p_drop ) + 1;
1178 p += strlen(p) + 1;
1180 *p_drop = '\0';
1181 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1185 wine_tsx11_lock();
1186 if( p_data ) XFree(p_data);
1187 wine_tsx11_unlock();
1190 /**********************************************************************
1191 * EVENT_DropURLs
1193 * drop items are separated by \n
1194 * each item is prefixed by its mime type
1196 * event->data.l[3], event->data.l[4] contains drop x,y position
1198 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1200 struct x11drv_win_data *win_data;
1201 unsigned long data_length;
1202 unsigned long aux_long, drop_len = 0;
1203 unsigned char *p_data = NULL; /* property data */
1204 char *p_drop = NULL;
1205 char *p, *next;
1206 int x, y;
1207 DROPFILES *lpDrop;
1208 HDROP hDrop;
1209 union {
1210 Atom atom_aux;
1211 int i;
1212 Window w_aux;
1213 unsigned int u;
1214 } u; /* unused */
1216 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1218 wine_tsx11_lock();
1219 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1220 x11drv_atom(DndSelection), 0, 65535, FALSE,
1221 AnyPropertyType, &u.atom_aux, &u.i,
1222 &data_length, &aux_long, &p_data);
1223 wine_tsx11_unlock();
1224 if (aux_long)
1225 WARN("property too large, truncated!\n");
1226 TRACE("urls=%s\n", p_data);
1228 if( !aux_long && p_data) { /* don't bother if > 64K */
1229 /* calculate length */
1230 p = (char*) p_data;
1231 next = strchr(p, '\n');
1232 while (p) {
1233 if (next) *next=0;
1234 if (strncmp(p,"file:",5) == 0 ) {
1235 INT len = GetShortPathNameA( p+5, NULL, 0 );
1236 if (len) drop_len += len + 1;
1238 if (next) {
1239 *next = '\n';
1240 p = next + 1;
1241 next = strchr(p, '\n');
1242 } else {
1243 p = NULL;
1247 if( drop_len && drop_len < 65535 ) {
1248 wine_tsx11_lock();
1249 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1250 &x, &y, &u.i, &u.i, &u.u);
1251 x += virtual_screen_rect.left;
1252 y += virtual_screen_rect.top;
1253 wine_tsx11_unlock();
1255 drop_len += sizeof(DROPFILES) + 1;
1256 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1257 lpDrop = GlobalLock( hDrop );
1259 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1261 lpDrop->pFiles = sizeof(DROPFILES);
1262 lpDrop->pt.x = x;
1263 lpDrop->pt.y = y;
1264 lpDrop->fNC =
1265 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1266 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1267 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1268 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1269 lpDrop->fWide = FALSE;
1270 p_drop = (char*)(lpDrop + 1);
1273 /* create message content */
1274 if (p_drop) {
1275 p = (char*) p_data;
1276 next = strchr(p, '\n');
1277 while (p) {
1278 if (next) *next=0;
1279 if (strncmp(p,"file:",5) == 0 ) {
1280 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1281 if (len) {
1282 TRACE("drop file %s as %s\n", p+5, p_drop);
1283 p_drop += len+1;
1284 } else {
1285 WARN("can't convert file %s to dos name\n", p+5);
1287 } else {
1288 WARN("unknown mime type %s\n", p);
1290 if (next) {
1291 *next = '\n';
1292 p = next + 1;
1293 next = strchr(p, '\n');
1294 } else {
1295 p = NULL;
1297 *p_drop = '\0';
1300 GlobalUnlock(hDrop);
1301 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1304 wine_tsx11_lock();
1305 if( p_data ) XFree(p_data);
1306 wine_tsx11_unlock();
1310 /**********************************************************************
1311 * handle_dnd_protocol
1313 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1315 Window root, child;
1316 int root_x, root_y, child_x, child_y;
1317 unsigned int u;
1319 /* query window (drag&drop event contains only drag window) */
1320 wine_tsx11_lock();
1321 XQueryPointer( event->display, root_window, &root, &child,
1322 &root_x, &root_y, &child_x, &child_y, &u);
1323 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1324 wine_tsx11_unlock();
1325 if (!hwnd) return;
1326 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1327 EVENT_DropFromOffiX(hwnd, event);
1328 else if (event->data.l[0] == DndURL)
1329 EVENT_DropURLs(hwnd, event);
1333 struct client_message_handler
1335 int atom; /* protocol atom */
1336 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1339 static const struct client_message_handler client_messages[] =
1341 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1342 { XATOM_DndProtocol, handle_dnd_protocol },
1343 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1344 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1345 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1346 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1350 /**********************************************************************
1351 * X11DRV_ClientMessage
1353 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1355 XClientMessageEvent *event = &xev->xclient;
1356 unsigned int i;
1358 if (!hwnd) return;
1360 if (event->format != 32)
1362 WARN( "Don't know how to handle format %d\n", event->format );
1363 return;
1366 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1368 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1370 client_messages[i].handler( hwnd, event );
1371 return;
1374 TRACE( "no handler found for %ld\n", event->message_type );
1378 /***********************************************************************
1379 * X11DRV_SendInput (X11DRV.@)
1381 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1383 UINT i;
1385 for (i = 0; i < count; i++, inputs++)
1387 switch(inputs->type)
1389 case INPUT_MOUSE:
1390 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1391 inputs->u.mi.mouseData, inputs->u.mi.time,
1392 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1393 break;
1394 case INPUT_KEYBOARD:
1395 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1396 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1397 break;
1398 case INPUT_HARDWARE:
1399 FIXME( "INPUT_HARDWARE not supported\n" );
1400 break;
1403 return count;