configure: Changes from running autconf after previous patch.
[wine/hacks.git] / dlls / winex11.drv / event.c
blob8fc955af777654c1e932a79bafdb19bebeecbf37
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 = threadinfo.hwndActive;
487 if (focus) focus = GetAncestor( focus, GA_ROOT );
488 win = X11DRV_get_whole_window(focus);
490 if (win)
492 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
493 wine_tsx11_lock();
494 XSetInputFocus( display, win, RevertToParent, time );
495 wine_tsx11_unlock();
500 /**********************************************************************
501 * handle_wm_protocols
503 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
505 Atom protocol = (Atom)event->data.l[0];
506 Time event_time = (Time)event->data.l[1];
508 if (!protocol) return;
510 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
512 update_user_time( event_time );
514 if (hwnd == GetDesktopWindow())
516 /* The desktop window does not have a close button that we can
517 * pretend to click. Therefore, we simply send it a close command. */
518 SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
519 return;
522 /* Ignore the delete window request if the window has been disabled
523 * and we are in managed mode. This is to disallow applications from
524 * being closed by the window manager while in a modal state.
526 if (IsWindowEnabled(hwnd))
528 HMENU hSysMenu;
530 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
531 hSysMenu = GetSystemMenu(hwnd, FALSE);
532 if (hSysMenu)
534 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
535 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
536 return;
538 if (GetActiveWindow() != hwnd)
540 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
541 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
542 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
543 switch(ma)
545 case MA_NOACTIVATEANDEAT:
546 case MA_ACTIVATEANDEAT:
547 return;
548 case MA_NOACTIVATE:
549 break;
550 case MA_ACTIVATE:
551 case 0:
552 SetActiveWindow(hwnd);
553 break;
554 default:
555 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
556 break;
560 PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
563 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
565 HWND last_focus = x11drv_thread_data()->last_focus;
567 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
568 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
569 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
571 if (can_activate_window(hwnd))
573 /* simulate a mouse click on the caption to find out
574 * whether the window wants to be activated */
575 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
576 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
577 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
578 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
580 set_focus( event->display, hwnd, event_time );
581 return;
584 else if (hwnd == GetDesktopWindow())
586 hwnd = GetForegroundWindow();
587 if (!hwnd) hwnd = last_focus;
588 if (!hwnd) hwnd = GetDesktopWindow();
589 set_focus( event->display, hwnd, event_time );
590 return;
592 /* try to find some other window to give the focus to */
593 hwnd = GetFocus();
594 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
595 if (!hwnd) hwnd = GetActiveWindow();
596 if (!hwnd) hwnd = last_focus;
597 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
599 else if (protocol == x11drv_atom(_NET_WM_PING))
601 XClientMessageEvent xev;
602 xev = *event;
604 TRACE("NET_WM Ping\n");
605 wine_tsx11_lock();
606 xev.window = DefaultRootWindow(xev.display);
607 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
608 wine_tsx11_unlock();
609 /* this line is semi-stolen from gtk2 */
610 TRACE("NET_WM Pong\n");
615 static const char * const focus_details[] =
617 "NotifyAncestor",
618 "NotifyVirtual",
619 "NotifyInferior",
620 "NotifyNonlinear",
621 "NotifyNonlinearVirtual",
622 "NotifyPointer",
623 "NotifyPointerRoot",
624 "NotifyDetailNone"
627 /**********************************************************************
628 * X11DRV_FocusIn
630 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
632 XFocusChangeEvent *event = &xev->xfocus;
633 XIC xic;
635 if (!hwnd) return;
637 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
639 if (event->detail == NotifyPointer) return;
641 if ((xic = X11DRV_get_ic( hwnd )))
643 wine_tsx11_lock();
644 XSetICFocus( xic );
645 wine_tsx11_unlock();
647 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
649 if (!can_activate_window(hwnd))
651 HWND hwnd = GetFocus();
652 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
653 if (!hwnd) hwnd = GetActiveWindow();
654 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
655 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
657 else SetForegroundWindow( hwnd );
661 /**********************************************************************
662 * X11DRV_FocusOut
664 * Note: only top-level windows get FocusOut events.
666 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
668 XFocusChangeEvent *event = &xev->xfocus;
669 HWND hwnd_tmp;
670 Window focus_win;
671 int revert;
672 XIC xic;
674 if (!hwnd) return;
676 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
678 if (event->detail == NotifyPointer) return;
679 if (ximInComposeMode) return;
681 x11drv_thread_data()->last_focus = hwnd;
682 if ((xic = X11DRV_get_ic( hwnd )))
684 wine_tsx11_lock();
685 XUnsetICFocus( xic );
686 wine_tsx11_unlock();
688 if (hwnd != GetForegroundWindow()) return;
689 if (root_window != DefaultRootWindow(event->display)) return;
690 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
692 /* don't reset the foreground window, if the window which is
693 getting the focus is a Wine window */
695 wine_tsx11_lock();
696 XGetInputFocus( event->display, &focus_win, &revert );
697 if (focus_win)
699 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
700 focus_win = 0;
702 wine_tsx11_unlock();
704 if (!focus_win)
706 /* Abey : 6-Oct-99. Check again if the focus out window is the
707 Foreground window, because in most cases the messages sent
708 above must have already changed the foreground window, in which
709 case we don't have to change the foreground window to 0 */
710 if (hwnd == GetForegroundWindow())
712 TRACE( "lost focus, setting fg to desktop\n" );
713 SetForegroundWindow( GetDesktopWindow() );
719 /***********************************************************************
720 * X11DRV_Expose
722 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
724 XExposeEvent *event = &xev->xexpose;
725 RECT rect;
726 struct x11drv_win_data *data;
727 int flags = RDW_INVALIDATE | RDW_ERASE;
729 TRACE( "win %p (%lx) %d,%d %dx%d\n",
730 hwnd, event->window, event->x, event->y, event->width, event->height );
732 if (!(data = X11DRV_get_win_data( hwnd ))) return;
734 if (event->window == data->whole_window)
736 rect.left = data->whole_rect.left + event->x;
737 rect.top = data->whole_rect.top + event->y;
738 flags |= RDW_FRAME;
740 else
742 rect.left = data->client_rect.left + event->x;
743 rect.top = data->client_rect.top + event->y;
745 rect.right = rect.left + event->width;
746 rect.bottom = rect.top + event->height;
748 if (event->window != root_window)
750 SERVER_START_REQ( update_window_zorder )
752 req->window = wine_server_user_handle( hwnd );
753 req->rect.left = rect.left;
754 req->rect.top = rect.top;
755 req->rect.right = rect.right;
756 req->rect.bottom = rect.bottom;
757 wine_server_call( req );
759 SERVER_END_REQ;
761 /* make position relative to client area instead of parent */
762 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
763 flags |= RDW_ALLCHILDREN;
766 RedrawWindow( hwnd, &rect, 0, flags );
770 /**********************************************************************
771 * X11DRV_MapNotify
773 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
775 struct x11drv_win_data *data;
777 if (!(data = X11DRV_get_win_data( hwnd ))) return;
778 if (!data->mapped) return;
780 if (!data->managed)
782 HWND hwndFocus = GetFocus();
783 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
788 /***********************************************************************
789 * is_net_wm_state_maximized
791 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
793 Atom type, *state;
794 int format, ret = 0;
795 unsigned long i, count, remaining;
797 wine_tsx11_lock();
798 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
799 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
800 &remaining, (unsigned char **)&state ))
802 if (type == XA_ATOM && format == 32)
804 for (i = 0; i < count; i++)
806 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
807 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
808 ret++;
811 XFree( state );
813 wine_tsx11_unlock();
814 return (ret == 2);
818 /***********************************************************************
819 * X11DRV_ConfigureNotify
821 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
823 XConfigureEvent *event = &xev->xconfigure;
824 struct x11drv_win_data *data;
825 RECT rect;
826 UINT flags;
827 int cx, cy, x = event->x, y = event->y;
829 if (!hwnd) return;
830 if (!(data = X11DRV_get_win_data( hwnd ))) return;
831 if (!data->mapped || data->iconic || !data->managed) return;
832 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
834 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
835 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
836 event->serial, data->configure_serial );
837 return;
840 /* Get geometry */
842 if (!event->send_event) /* normal event, need to map coordinates to the root */
844 Window child;
845 wine_tsx11_lock();
846 XTranslateCoordinates( event->display, data->whole_window, root_window,
847 0, 0, &x, &y, &child );
848 wine_tsx11_unlock();
850 rect.left = x;
851 rect.top = y;
852 rect.right = x + event->width;
853 rect.bottom = y + event->height;
854 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
855 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
856 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
857 event->x, event->y, event->width, event->height );
858 X11DRV_X_to_window_rect( data, &rect );
860 if (is_net_wm_state_maximized( event->display, data ))
862 if (!IsZoomed( data->hwnd ))
864 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
865 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
866 return;
869 else
871 if (IsZoomed( data->hwnd ))
873 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
874 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
875 return;
879 /* Compare what has changed */
881 x = rect.left;
882 y = rect.top;
883 cx = rect.right - rect.left;
884 cy = rect.bottom - rect.top;
885 flags = SWP_NOACTIVATE | SWP_NOZORDER;
887 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
888 else
889 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
890 hwnd, data->window_rect.left, data->window_rect.top, x, y );
892 if ((data->window_rect.right - data->window_rect.left == cx &&
893 data->window_rect.bottom - data->window_rect.top == cy) ||
894 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
896 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
897 flags |= SWP_NOSIZE;
899 else
900 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
901 hwnd, data->window_rect.right - data->window_rect.left,
902 data->window_rect.bottom - data->window_rect.top, cx, cy );
904 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
908 /***********************************************************************
909 * get_window_wm_state
911 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
913 struct
915 CARD32 state;
916 XID icon;
917 } *state;
918 Atom type;
919 int format, ret = -1;
920 unsigned long count, remaining;
922 wine_tsx11_lock();
923 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
924 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
925 &type, &format, &count, &remaining, (unsigned char **)&state ))
927 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
928 ret = state->state;
929 XFree( state );
931 wine_tsx11_unlock();
932 return ret;
936 /***********************************************************************
937 * handle_wm_state_notify
939 * Handle a PropertyNotify for WM_STATE.
941 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
942 BOOL update_window )
944 DWORD style;
946 switch(event->state)
948 case PropertyDelete:
949 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
950 data->wm_state = WithdrawnState;
951 break;
952 case PropertyNewValue:
954 int old_state = data->wm_state;
955 int new_state = get_window_wm_state( event->display, data );
956 if (new_state != -1 && new_state != data->wm_state)
958 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
959 data->hwnd, data->whole_window, new_state, old_state );
960 data->wm_state = new_state;
961 /* ignore the initial state transition out of withdrawn state */
962 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
963 if (!old_state) return;
966 break;
969 if (!update_window || !data->managed || !data->mapped) return;
971 style = GetWindowLongW( data->hwnd, GWL_STYLE );
973 if (data->iconic && data->wm_state == NormalState) /* restore window */
975 data->iconic = FALSE;
976 if (is_net_wm_state_maximized( event->display, data ))
978 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
980 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
981 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
983 else TRACE( "not restoring to max win %p/%lx style %08x\n",
984 data->hwnd, data->whole_window, style );
986 else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
988 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
989 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
991 else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
993 else if (!data->iconic && data->wm_state == IconicState)
995 data->iconic = TRUE;
996 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
998 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
999 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1001 else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1006 /***********************************************************************
1007 * X11DRV_PropertyNotify
1009 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1011 XPropertyEvent *event = &xev->xproperty;
1012 struct x11drv_win_data *data;
1014 if (!hwnd) return;
1015 if (!(data = X11DRV_get_win_data( hwnd ))) return;
1017 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1021 /* event filter to wait for a WM_STATE change notification on a window */
1022 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1024 if (event->xany.window != (Window)arg) return 0;
1025 return (event->type == DestroyNotify ||
1026 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1029 /***********************************************************************
1030 * wait_for_withdrawn_state
1032 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1034 DWORD end = GetTickCount() + 2000;
1036 if (!data->managed) return;
1038 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1039 data->hwnd, data->whole_window, set ? "" : "not " );
1041 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1043 XEvent event;
1044 int count = 0;
1046 wine_tsx11_lock();
1047 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1049 count++;
1050 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1051 if (event.type == DestroyNotify) call_event_handler( display, &event );
1052 else
1054 wine_tsx11_unlock();
1055 handle_wm_state_notify( data, &event.xproperty, FALSE );
1056 wine_tsx11_lock();
1059 wine_tsx11_unlock();
1061 if (!count)
1063 struct pollfd pfd;
1064 int timeout = end - GetTickCount();
1066 pfd.fd = ConnectionNumber(display);
1067 pfd.events = POLLIN;
1068 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1070 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1071 break;
1075 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1079 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1081 RECT tempRect;
1083 if (!IsWindowEnabled(hQueryWnd)) return 0;
1085 GetWindowRect(hQueryWnd, &tempRect);
1087 if(!PtInRect(&tempRect, *lpPt)) return 0;
1089 if (!IsIconic( hQueryWnd ))
1091 POINT pt = *lpPt;
1092 ScreenToClient( hQueryWnd, &pt );
1093 GetClientRect( hQueryWnd, &tempRect );
1095 if (PtInRect( &tempRect, pt))
1097 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1098 if (ret && ret != hQueryWnd)
1100 ret = find_drop_window( ret, lpPt );
1101 if (ret) return ret;
1106 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1108 ScreenToClient(hQueryWnd, lpPt);
1110 return hQueryWnd;
1113 /**********************************************************************
1114 * EVENT_DropFromOffix
1116 * don't know if it still works (last Changelog is from 96/11/04)
1118 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1120 struct x11drv_win_data *data;
1121 unsigned long data_length;
1122 unsigned long aux_long;
1123 unsigned char* p_data = NULL;
1124 Atom atom_aux;
1125 int x, y, dummy;
1126 BOOL bAccept;
1127 Window win, w_aux_root, w_aux_child;
1129 win = X11DRV_get_whole_window(hWnd);
1130 wine_tsx11_lock();
1131 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1132 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1133 x += virtual_screen_rect.left;
1134 y += virtual_screen_rect.top;
1135 wine_tsx11_unlock();
1137 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1139 /* find out drop point and drop window */
1140 if( x < 0 || y < 0 ||
1141 x > (data->whole_rect.right - data->whole_rect.left) ||
1142 y > (data->whole_rect.bottom - data->whole_rect.top) )
1144 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1145 x = 0;
1146 y = 0;
1148 else
1150 POINT pt = { x, y };
1151 HWND hwndDrop = find_drop_window( hWnd, &pt );
1152 if (hwndDrop)
1154 x = pt.x;
1155 y = pt.y;
1156 bAccept = TRUE;
1158 else
1160 bAccept = FALSE;
1164 if (!bAccept) return;
1166 wine_tsx11_lock();
1167 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1168 x11drv_atom(DndSelection), 0, 65535, FALSE,
1169 AnyPropertyType, &atom_aux, &dummy,
1170 &data_length, &aux_long, &p_data);
1171 wine_tsx11_unlock();
1173 if( !aux_long && p_data) /* don't bother if > 64K */
1175 char *p = (char *)p_data;
1176 char *p_drop;
1178 aux_long = 0;
1179 while( *p ) /* calculate buffer size */
1181 INT len = GetShortPathNameA( p, NULL, 0 );
1182 if (len) aux_long += len + 1;
1183 p += strlen(p) + 1;
1185 if( aux_long && aux_long < 65535 )
1187 HDROP hDrop;
1188 DROPFILES *lpDrop;
1190 aux_long += sizeof(DROPFILES) + 1;
1191 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1192 lpDrop = GlobalLock( hDrop );
1194 if( lpDrop )
1196 lpDrop->pFiles = sizeof(DROPFILES);
1197 lpDrop->pt.x = x;
1198 lpDrop->pt.y = y;
1199 lpDrop->fNC = FALSE;
1200 lpDrop->fWide = FALSE;
1201 p_drop = (char *)(lpDrop + 1);
1202 p = (char *)p_data;
1203 while(*p)
1205 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1206 p_drop += strlen( p_drop ) + 1;
1207 p += strlen(p) + 1;
1209 *p_drop = '\0';
1210 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1214 wine_tsx11_lock();
1215 if( p_data ) XFree(p_data);
1216 wine_tsx11_unlock();
1219 /**********************************************************************
1220 * EVENT_DropURLs
1222 * drop items are separated by \n
1223 * each item is prefixed by its mime type
1225 * event->data.l[3], event->data.l[4] contains drop x,y position
1227 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1229 struct x11drv_win_data *win_data;
1230 unsigned long data_length;
1231 unsigned long aux_long, drop_len = 0;
1232 unsigned char *p_data = NULL; /* property data */
1233 char *p_drop = NULL;
1234 char *p, *next;
1235 int x, y;
1236 DROPFILES *lpDrop;
1237 HDROP hDrop;
1238 union {
1239 Atom atom_aux;
1240 int i;
1241 Window w_aux;
1242 unsigned int u;
1243 } u; /* unused */
1245 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1247 wine_tsx11_lock();
1248 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1249 x11drv_atom(DndSelection), 0, 65535, FALSE,
1250 AnyPropertyType, &u.atom_aux, &u.i,
1251 &data_length, &aux_long, &p_data);
1252 wine_tsx11_unlock();
1253 if (aux_long)
1254 WARN("property too large, truncated!\n");
1255 TRACE("urls=%s\n", p_data);
1257 if( !aux_long && p_data) { /* don't bother if > 64K */
1258 /* calculate length */
1259 p = (char*) p_data;
1260 next = strchr(p, '\n');
1261 while (p) {
1262 if (next) *next=0;
1263 if (strncmp(p,"file:",5) == 0 ) {
1264 INT len = GetShortPathNameA( p+5, NULL, 0 );
1265 if (len) drop_len += len + 1;
1267 if (next) {
1268 *next = '\n';
1269 p = next + 1;
1270 next = strchr(p, '\n');
1271 } else {
1272 p = NULL;
1276 if( drop_len && drop_len < 65535 ) {
1277 wine_tsx11_lock();
1278 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1279 &x, &y, &u.i, &u.i, &u.u);
1280 x += virtual_screen_rect.left;
1281 y += virtual_screen_rect.top;
1282 wine_tsx11_unlock();
1284 drop_len += sizeof(DROPFILES) + 1;
1285 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1286 lpDrop = GlobalLock( hDrop );
1288 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1290 lpDrop->pFiles = sizeof(DROPFILES);
1291 lpDrop->pt.x = x;
1292 lpDrop->pt.y = y;
1293 lpDrop->fNC =
1294 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1295 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1296 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1297 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1298 lpDrop->fWide = FALSE;
1299 p_drop = (char*)(lpDrop + 1);
1302 /* create message content */
1303 if (p_drop) {
1304 p = (char*) p_data;
1305 next = strchr(p, '\n');
1306 while (p) {
1307 if (next) *next=0;
1308 if (strncmp(p,"file:",5) == 0 ) {
1309 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1310 if (len) {
1311 TRACE("drop file %s as %s\n", p+5, p_drop);
1312 p_drop += len+1;
1313 } else {
1314 WARN("can't convert file %s to dos name\n", p+5);
1316 } else {
1317 WARN("unknown mime type %s\n", p);
1319 if (next) {
1320 *next = '\n';
1321 p = next + 1;
1322 next = strchr(p, '\n');
1323 } else {
1324 p = NULL;
1326 *p_drop = '\0';
1329 GlobalUnlock(hDrop);
1330 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1333 wine_tsx11_lock();
1334 if( p_data ) XFree(p_data);
1335 wine_tsx11_unlock();
1339 /**********************************************************************
1340 * handle_dnd_protocol
1342 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1344 Window root, child;
1345 int root_x, root_y, child_x, child_y;
1346 unsigned int u;
1348 /* query window (drag&drop event contains only drag window) */
1349 wine_tsx11_lock();
1350 XQueryPointer( event->display, root_window, &root, &child,
1351 &root_x, &root_y, &child_x, &child_y, &u);
1352 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1353 wine_tsx11_unlock();
1354 if (!hwnd) return;
1355 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1356 EVENT_DropFromOffiX(hwnd, event);
1357 else if (event->data.l[0] == DndURL)
1358 EVENT_DropURLs(hwnd, event);
1362 struct client_message_handler
1364 int atom; /* protocol atom */
1365 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1368 static const struct client_message_handler client_messages[] =
1370 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1371 { XATOM_DndProtocol, handle_dnd_protocol },
1372 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1373 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1374 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1375 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1379 /**********************************************************************
1380 * X11DRV_ClientMessage
1382 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1384 XClientMessageEvent *event = &xev->xclient;
1385 unsigned int i;
1387 if (!hwnd) return;
1389 if (event->format != 32)
1391 WARN( "Don't know how to handle format %d\n", event->format );
1392 return;
1395 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1397 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1399 client_messages[i].handler( hwnd, event );
1400 return;
1403 TRACE( "no handler found for %ld\n", event->message_type );
1407 /***********************************************************************
1408 * X11DRV_SendInput (X11DRV.@)
1410 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1412 UINT i;
1414 for (i = 0; i < count; i++, inputs++)
1416 switch(inputs->type)
1418 case INPUT_MOUSE:
1419 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1420 inputs->u.mi.mouseData, inputs->u.mi.time,
1421 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1422 break;
1423 case INPUT_KEYBOARD:
1424 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1425 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1426 break;
1427 case INPUT_HARDWARE:
1428 FIXME( "INPUT_HARDWARE not supported\n" );
1429 break;
1432 return count;