push c6bab2db4fc296bd36abf8f7d9cf443d4a73048e
[wine/hacks.git] / dlls / winex11.drv / event.c
blobc746546f71b483c388266be597931acf538367c7
1 /*
2 * X11 event driver
4 * Copyright 1993 Alexandre Julliard
5 * 1999 Noel Borthwick
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "config.h"
24 #ifdef HAVE_POLL_H
25 #include <poll.h>
26 #endif
27 #ifdef HAVE_SYS_POLL_H
28 #include <sys/poll.h>
29 #endif
30 #include <X11/Xatom.h>
31 #include <X11/keysym.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xresource.h>
34 #include <X11/Xutil.h>
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <string.h>
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h"
45 #include "wingdi.h"
47 #include "x11drv.h"
49 /* avoid conflict with field names in included win32 headers */
50 #undef Status
51 #include "shlobj.h" /* DROPFILES */
52 #include "shellapi.h"
54 #include "wine/server.h"
55 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(event);
59 extern BOOL ximInComposeMode;
61 #define DndNotDnd -1 /* OffiX drag&drop */
62 #define DndUnknown 0
63 #define DndRawData 1
64 #define DndFile 2
65 #define DndFiles 3
66 #define DndText 4
67 #define DndDir 5
68 #define DndLink 6
69 #define DndExe 7
71 #define DndEND 8
73 #define DndURL 128 /* KDE drag&drop */
75 /* Event handlers */
76 static void X11DRV_FocusIn( HWND hwnd, XEvent *event );
77 static void X11DRV_FocusOut( HWND hwnd, XEvent *event );
78 static void X11DRV_Expose( HWND hwnd, XEvent *event );
79 static void X11DRV_MapNotify( HWND hwnd, XEvent *event );
80 static void X11DRV_ConfigureNotify( HWND hwnd, XEvent *event );
81 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *event );
82 static void X11DRV_ClientMessage( HWND hwnd, XEvent *event );
84 struct event_handler
86 int type; /* event type */
87 x11drv_event_handler handler; /* corresponding handler function */
90 #define MAX_EVENT_HANDLERS 64
92 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
94 /* list must be sorted by event type */
95 { KeyPress, X11DRV_KeyEvent },
96 { KeyRelease, X11DRV_KeyEvent },
97 { ButtonPress, X11DRV_ButtonPress },
98 { ButtonRelease, X11DRV_ButtonRelease },
99 { MotionNotify, X11DRV_MotionNotify },
100 { EnterNotify, X11DRV_EnterNotify },
101 /* LeaveNotify */
102 { FocusIn, X11DRV_FocusIn },
103 { FocusOut, X11DRV_FocusOut },
104 { KeymapNotify, X11DRV_KeymapNotify },
105 { Expose, X11DRV_Expose },
106 /* GraphicsExpose */
107 /* NoExpose */
108 /* VisibilityNotify */
109 /* CreateNotify */
110 { DestroyNotify, X11DRV_DestroyNotify },
111 /* UnmapNotify */
112 { MapNotify, X11DRV_MapNotify },
113 /* MapRequest */
114 /* ReparentNotify */
115 { ConfigureNotify, X11DRV_ConfigureNotify },
116 /* ConfigureRequest */
117 /* GravityNotify */
118 /* ResizeRequest */
119 /* CirculateNotify */
120 /* CirculateRequest */
121 { PropertyNotify, X11DRV_PropertyNotify },
122 { SelectionClear, X11DRV_SelectionClear },
123 { SelectionRequest, X11DRV_SelectionRequest },
124 /* SelectionNotify */
125 /* ColormapNotify */
126 { ClientMessage, X11DRV_ClientMessage },
127 { MappingNotify, X11DRV_MappingNotify },
130 static int nb_event_handlers = 18; /* change this if you add handlers above */
133 /* return the name of an X event */
134 static const char *dbgstr_event( int type )
136 static const char * const event_names[] =
138 "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
139 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
140 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
141 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
142 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
143 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
144 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
145 "ClientMessage", "MappingNotify"
148 if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
149 return wine_dbg_sprintf( "Extension event %d", type );
153 /***********************************************************************
154 * find_handler
156 * Find the handler for a given event type. Caller must hold the x11 lock.
158 static inline x11drv_event_handler find_handler( int type )
160 int min = 0, max = nb_event_handlers - 1;
162 while (min <= max)
164 int pos = (min + max) / 2;
165 if (handlers[pos].type == type) return handlers[pos].handler;
166 if (handlers[pos].type > type) max = pos - 1;
167 else min = pos + 1;
169 return NULL;
173 /***********************************************************************
174 * X11DRV_register_event_handler
176 * Register a handler for a given event type.
177 * If already registered, overwrite the previous handler.
179 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
181 int min, max;
183 wine_tsx11_lock();
184 min = 0;
185 max = nb_event_handlers - 1;
186 while (min <= max)
188 int pos = (min + max) / 2;
189 if (handlers[pos].type == type)
191 handlers[pos].handler = handler;
192 goto done;
194 if (handlers[pos].type > type) max = pos - 1;
195 else min = pos + 1;
197 /* insert it between max and min */
198 memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
199 handlers[min].type = type;
200 handlers[min].handler = handler;
201 nb_event_handlers++;
202 assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
203 done:
204 wine_tsx11_unlock();
205 TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
209 /***********************************************************************
210 * filter_event
212 static Bool filter_event( Display *display, XEvent *event, char *arg )
214 ULONG_PTR mask = (ULONG_PTR)arg;
216 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
218 switch(event->type)
220 case KeyPress:
221 case KeyRelease:
222 case KeymapNotify:
223 case MappingNotify:
224 return (mask & QS_KEY) != 0;
225 case ButtonPress:
226 case ButtonRelease:
227 return (mask & QS_MOUSEBUTTON) != 0;
228 case MotionNotify:
229 case EnterNotify:
230 case LeaveNotify:
231 return (mask & QS_MOUSEMOVE) != 0;
232 case Expose:
233 return (mask & QS_PAINT) != 0;
234 case FocusIn:
235 case FocusOut:
236 case MapNotify:
237 case UnmapNotify:
238 case ConfigureNotify:
239 case PropertyNotify:
240 case ClientMessage:
241 return (mask & QS_POSTMESSAGE) != 0;
242 default:
243 return (mask & QS_SENDMESSAGE) != 0;
248 enum event_merge_action
250 MERGE_DISCARD, /* discard the old event */
251 MERGE_HANDLE, /* handle the old event */
252 MERGE_KEEP /* keep the old event for future merging */
255 /***********************************************************************
256 * merge_events
258 * Try to merge 2 consecutive events.
260 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
262 switch (prev->type)
264 case ConfigureNotify:
265 switch (next->type)
267 case ConfigureNotify:
268 if (prev->xany.window == next->xany.window)
270 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
271 return MERGE_DISCARD;
273 break;
274 case Expose:
275 case PropertyNotify:
276 return MERGE_KEEP;
278 break;
279 case MotionNotify:
280 if (prev->xany.window == next->xany.window && next->type == MotionNotify)
282 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
283 return MERGE_DISCARD;
285 break;
287 return MERGE_HANDLE;
291 /***********************************************************************
292 * call_event_handler
294 static inline void call_event_handler( Display *display, XEvent *event )
296 HWND hwnd;
297 x11drv_event_handler handler;
298 XEvent *prev;
299 struct x11drv_thread_data *thread_data;
301 if (!(handler = find_handler( event->type )))
303 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
304 return; /* no handler, ignore it */
307 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
308 hwnd = 0; /* not for a registered window */
309 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
311 TRACE( "%s for hwnd/window %p/%lx\n",
312 dbgstr_event( event->type ), hwnd, event->xany.window );
313 wine_tsx11_unlock();
314 thread_data = x11drv_thread_data();
315 prev = thread_data->current_event;
316 thread_data->current_event = event;
317 handler( hwnd, event );
318 thread_data->current_event = prev;
319 wine_tsx11_lock();
323 /***********************************************************************
324 * process_events
326 static int process_events( Display *display, Bool (*filter)(), ULONG_PTR arg )
328 XEvent event, prev_event;
329 int count = 0;
330 enum event_merge_action action = MERGE_DISCARD;
332 prev_event.type = 0;
333 wine_tsx11_lock();
334 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
336 count++;
337 if (XFilterEvent( &event, None ))
340 * SCIM on linux filters key events strangely. It does not filter the
341 * KeyPress events for these keys however it does filter the
342 * KeyRelease events. This causes wine to become very confused as
343 * to the keyboard state.
345 * We need to let those KeyRelease events be processed so that the
346 * keyboard state is correct.
348 if (event.type == KeyRelease)
350 KeySym keysym = 0;
351 XKeyEvent *keyevent = &event.xkey;
353 XLookupString(keyevent, NULL, 0, &keysym, NULL);
354 if (!(keysym == XK_Shift_L ||
355 keysym == XK_Shift_R ||
356 keysym == XK_Control_L ||
357 keysym == XK_Control_R ||
358 keysym == XK_Alt_R ||
359 keysym == XK_Alt_L ||
360 keysym == XK_Meta_R ||
361 keysym == XK_Meta_L))
362 continue; /* not a key we care about, ignore it */
364 else
365 continue; /* filtered, ignore it */
367 if (prev_event.type) action = merge_events( &prev_event, &event );
368 switch( action )
370 case MERGE_DISCARD: /* discard prev, keep new */
371 prev_event = event;
372 break;
373 case MERGE_HANDLE: /* handle prev, keep new */
374 call_event_handler( display, &prev_event );
375 prev_event = event;
376 break;
377 case MERGE_KEEP: /* handle new, keep prev for future merging */
378 call_event_handler( display, &event );
379 break;
382 XFlush( gdi_display );
383 if (prev_event.type) call_event_handler( display, &prev_event );
384 wine_tsx11_unlock();
385 if (count) TRACE( "processed %d events\n", count );
386 return count;
390 /***********************************************************************
391 * MsgWaitForMultipleObjectsEx (X11DRV.@)
393 DWORD 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 (hwnd == GetDesktopWindow()) return FALSE;
467 return !(style & WS_DISABLED);
471 /**********************************************************************
472 * set_focus
474 static void set_focus( Display *display, HWND hwnd, Time time )
476 HWND focus;
477 Window win;
479 TRACE( "setting foreground window to %p\n", hwnd );
480 SetForegroundWindow( hwnd );
482 focus = GetFocus();
483 if (focus) focus = GetAncestor( focus, GA_ROOT );
484 win = X11DRV_get_whole_window(focus);
486 if (win)
488 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
489 wine_tsx11_lock();
490 XSetInputFocus( display, win, RevertToParent, time );
491 wine_tsx11_unlock();
496 /**********************************************************************
497 * handle_wm_protocols
499 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
501 Atom protocol = (Atom)event->data.l[0];
503 if (!protocol) return;
505 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
507 /* Ignore the delete window request if the window has been disabled
508 * and we are in managed mode. This is to disallow applications from
509 * being closed by the window manager while in a modal state.
511 if (IsWindowEnabled(hwnd))
513 HMENU hSysMenu;
515 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
516 hSysMenu = GetSystemMenu(hwnd, FALSE);
517 if (hSysMenu)
519 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
520 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
521 return;
523 if (GetActiveWindow() != hwnd)
525 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
526 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
527 MAKELONG(HTCLOSE,WM_LBUTTONDOWN) );
528 switch(ma)
530 case MA_NOACTIVATEANDEAT:
531 case MA_ACTIVATEANDEAT:
532 return;
533 case MA_NOACTIVATE:
534 break;
535 case MA_ACTIVATE:
536 case 0:
537 SetActiveWindow(hwnd);
538 break;
539 default:
540 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
541 break;
544 PostMessageW( hwnd, WM_X11DRV_DELETE_WINDOW, 0, 0 );
547 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
549 Time event_time = (Time)event->data.l[1];
550 HWND last_focus = x11drv_thread_data()->last_focus;
552 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
553 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
554 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
556 if (can_activate_window(hwnd))
558 /* simulate a mouse click on the caption to find out
559 * whether the window wants to be activated */
560 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
561 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
562 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
563 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
565 set_focus( event->display, hwnd, event_time );
566 return;
569 /* try to find some other window to give the focus to */
570 hwnd = GetFocus();
571 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
572 if (!hwnd) hwnd = GetActiveWindow();
573 if (!hwnd) hwnd = last_focus;
574 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
576 else if (protocol == x11drv_atom(_NET_WM_PING))
578 XClientMessageEvent xev;
579 xev = *event;
581 TRACE("NET_WM Ping\n");
582 wine_tsx11_lock();
583 xev.window = DefaultRootWindow(xev.display);
584 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
585 wine_tsx11_unlock();
586 /* this line is semi-stolen from gtk2 */
587 TRACE("NET_WM Pong\n");
592 static const char * const focus_details[] =
594 "NotifyAncestor",
595 "NotifyVirtual",
596 "NotifyInferior",
597 "NotifyNonlinear",
598 "NotifyNonlinearVirtual",
599 "NotifyPointer",
600 "NotifyPointerRoot",
601 "NotifyDetailNone"
604 /**********************************************************************
605 * X11DRV_FocusIn
607 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
609 XFocusChangeEvent *event = &xev->xfocus;
610 XIC xic;
612 if (!hwnd) return;
614 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
616 if (event->detail == NotifyPointer) return;
618 if ((xic = X11DRV_get_ic( hwnd )))
620 wine_tsx11_lock();
621 XSetICFocus( xic );
622 wine_tsx11_unlock();
624 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
626 if (!can_activate_window(hwnd))
628 HWND hwnd = GetFocus();
629 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
630 if (!hwnd) hwnd = GetActiveWindow();
631 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
632 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
634 else SetForegroundWindow( hwnd );
638 /**********************************************************************
639 * X11DRV_FocusOut
641 * Note: only top-level windows get FocusOut events.
643 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
645 XFocusChangeEvent *event = &xev->xfocus;
646 HWND hwnd_tmp;
647 Window focus_win;
648 int revert;
649 XIC xic;
651 if (!hwnd) return;
653 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
655 if (event->detail == NotifyPointer) return;
656 if (ximInComposeMode) return;
658 x11drv_thread_data()->last_focus = hwnd;
659 if ((xic = X11DRV_get_ic( hwnd )))
661 wine_tsx11_lock();
662 XUnsetICFocus( xic );
663 wine_tsx11_unlock();
665 if (hwnd != GetForegroundWindow()) return;
666 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
668 /* don't reset the foreground window, if the window which is
669 getting the focus is a Wine window */
671 wine_tsx11_lock();
672 XGetInputFocus( event->display, &focus_win, &revert );
673 if (focus_win)
675 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
676 focus_win = 0;
678 wine_tsx11_unlock();
680 if (!focus_win)
682 /* Abey : 6-Oct-99. Check again if the focus out window is the
683 Foreground window, because in most cases the messages sent
684 above must have already changed the foreground window, in which
685 case we don't have to change the foreground window to 0 */
686 if (hwnd == GetForegroundWindow())
688 TRACE( "lost focus, setting fg to desktop\n" );
689 SetForegroundWindow( GetDesktopWindow() );
695 /***********************************************************************
696 * X11DRV_Expose
698 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
700 XExposeEvent *event = &xev->xexpose;
701 RECT rect;
702 struct x11drv_win_data *data;
703 int flags = RDW_INVALIDATE | RDW_ERASE;
705 TRACE( "win %p (%lx) %d,%d %dx%d\n",
706 hwnd, event->window, event->x, event->y, event->width, event->height );
708 if (!(data = X11DRV_get_win_data( hwnd ))) return;
710 if (event->window == data->whole_window)
712 rect.left = data->whole_rect.left + event->x;
713 rect.top = data->whole_rect.top + event->y;
714 flags |= RDW_FRAME;
716 else
718 rect.left = data->client_rect.left + event->x;
719 rect.top = data->client_rect.top + event->y;
721 rect.right = rect.left + event->width;
722 rect.bottom = rect.top + event->height;
724 if (event->window != root_window)
726 SERVER_START_REQ( update_window_zorder )
728 req->window = wine_server_user_handle( hwnd );
729 req->rect.left = rect.left;
730 req->rect.top = rect.top;
731 req->rect.right = rect.right;
732 req->rect.bottom = rect.bottom;
733 wine_server_call( req );
735 SERVER_END_REQ;
737 /* make position relative to client area instead of parent */
738 OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
739 flags |= RDW_ALLCHILDREN;
742 RedrawWindow( hwnd, &rect, 0, flags );
746 /**********************************************************************
747 * X11DRV_MapNotify
749 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
751 struct x11drv_win_data *data;
753 if (!(data = X11DRV_get_win_data( hwnd ))) return;
754 if (!data->mapped) return;
756 if (!data->managed)
758 HWND hwndFocus = GetFocus();
759 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
764 /***********************************************************************
765 * is_net_wm_state_maximized
767 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
769 Atom type, *state;
770 int format, ret = 0;
771 unsigned long i, count, remaining;
773 wine_tsx11_lock();
774 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
775 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
776 &remaining, (unsigned char **)&state ))
778 if (type == XA_ATOM && format == 32)
780 for (i = 0; i < count; i++)
782 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
783 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
784 ret++;
787 XFree( state );
789 wine_tsx11_unlock();
790 return (ret == 2);
794 /***********************************************************************
795 * X11DRV_ConfigureNotify
797 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
799 XConfigureEvent *event = &xev->xconfigure;
800 struct x11drv_win_data *data;
801 RECT rect;
802 UINT flags;
803 int cx, cy, x = event->x, y = event->y;
805 if (!hwnd) return;
806 if (!(data = X11DRV_get_win_data( hwnd ))) return;
807 if (!data->mapped || data->iconic || !data->managed) return;
809 /* Get geometry */
811 if (!event->send_event) /* normal event, need to map coordinates to the root */
813 Window child;
814 wine_tsx11_lock();
815 XTranslateCoordinates( event->display, data->whole_window, root_window,
816 0, 0, &x, &y, &child );
817 wine_tsx11_unlock();
819 rect.left = x;
820 rect.top = y;
821 rect.right = x + event->width;
822 rect.bottom = y + event->height;
823 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
824 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
825 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
826 event->x, event->y, event->width, event->height );
827 X11DRV_X_to_window_rect( data, &rect );
829 if (is_net_wm_state_maximized( event->display, data ))
831 if (!IsZoomed( data->hwnd ))
833 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
834 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
835 return;
838 else
840 if (IsZoomed( data->hwnd ))
842 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
843 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
844 return;
848 /* Compare what has changed */
850 x = rect.left;
851 y = rect.top;
852 cx = rect.right - rect.left;
853 cy = rect.bottom - rect.top;
854 flags = SWP_NOACTIVATE | SWP_NOZORDER;
856 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
857 else
858 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
859 hwnd, data->window_rect.left, data->window_rect.top, x, y );
861 if ((data->window_rect.right - data->window_rect.left == cx &&
862 data->window_rect.bottom - data->window_rect.top == cy) ||
863 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
865 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
866 flags |= SWP_NOSIZE;
868 else
869 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
870 hwnd, data->window_rect.right - data->window_rect.left,
871 data->window_rect.bottom - data->window_rect.top, cx, cy );
873 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
877 /***********************************************************************
878 * get_window_wm_state
880 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
882 struct
884 CARD32 state;
885 XID icon;
886 } *state;
887 Atom type;
888 int format, ret = -1;
889 unsigned long count, remaining;
891 wine_tsx11_lock();
892 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
893 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
894 &type, &format, &count, &remaining, (unsigned char **)&state ))
896 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
897 ret = state->state;
898 XFree( state );
900 wine_tsx11_unlock();
901 return ret;
905 /***********************************************************************
906 * handle_wm_state_notify
908 * Handle a PropertyNotify for WM_STATE.
910 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
911 BOOL update_window )
913 switch(event->state)
915 case PropertyDelete:
916 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
917 data->wm_state = WithdrawnState;
918 break;
919 case PropertyNewValue:
921 int old_state = data->wm_state;
922 int new_state = get_window_wm_state( event->display, data );
923 if (new_state != -1 && new_state != data->wm_state)
925 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
926 data->hwnd, data->whole_window, new_state, old_state );
927 data->wm_state = new_state;
928 /* ignore the initial state transition out of withdrawn state */
929 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
930 if (!old_state) return;
933 break;
936 if (!update_window || !data->managed || !data->mapped) return;
938 if (data->iconic && data->wm_state == NormalState) /* restore window */
940 data->iconic = FALSE;
941 if (is_net_wm_state_maximized( event->display, data ))
943 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
944 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
946 else
948 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
949 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
952 else if (!data->iconic && data->wm_state == IconicState)
954 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
955 data->iconic = TRUE;
956 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
961 /***********************************************************************
962 * X11DRV_PropertyNotify
964 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
966 XPropertyEvent *event = &xev->xproperty;
967 struct x11drv_win_data *data;
969 if (!hwnd) return;
970 if (!(data = X11DRV_get_win_data( hwnd ))) return;
972 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
976 /* event filter to wait for a WM_STATE change notification on a window */
977 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
979 if (event->xany.window != (Window)arg) return 0;
980 return (event->type == DestroyNotify ||
981 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
984 /***********************************************************************
985 * wait_for_withdrawn_state
987 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
989 DWORD end = GetTickCount() + 2000;
991 if (!data->managed) return;
993 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
994 data->hwnd, data->whole_window, set ? "" : "not " );
996 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
998 XEvent event;
999 int count = 0;
1001 wine_tsx11_lock();
1002 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1004 count++;
1005 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1006 if (event.type == DestroyNotify) call_event_handler( display, &event );
1007 else
1009 wine_tsx11_unlock();
1010 handle_wm_state_notify( data, &event.xproperty, FALSE );
1011 wine_tsx11_lock();
1014 wine_tsx11_unlock();
1016 if (!count)
1018 struct pollfd pfd;
1019 int timeout = end - GetTickCount();
1021 pfd.fd = ConnectionNumber(display);
1022 pfd.events = POLLIN;
1023 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1025 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1026 break;
1030 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1034 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1036 RECT tempRect;
1038 if (!IsWindowEnabled(hQueryWnd)) return 0;
1040 GetWindowRect(hQueryWnd, &tempRect);
1042 if(!PtInRect(&tempRect, *lpPt)) return 0;
1044 if (!IsIconic( hQueryWnd ))
1046 POINT pt = *lpPt;
1047 ScreenToClient( hQueryWnd, &pt );
1048 GetClientRect( hQueryWnd, &tempRect );
1050 if (PtInRect( &tempRect, pt))
1052 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1053 if (ret && ret != hQueryWnd)
1055 ret = find_drop_window( ret, lpPt );
1056 if (ret) return ret;
1061 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1063 ScreenToClient(hQueryWnd, lpPt);
1065 return hQueryWnd;
1068 /**********************************************************************
1069 * EVENT_DropFromOffix
1071 * don't know if it still works (last Changelog is from 96/11/04)
1073 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1075 struct x11drv_win_data *data;
1076 unsigned long data_length;
1077 unsigned long aux_long;
1078 unsigned char* p_data = NULL;
1079 Atom atom_aux;
1080 int x, y, dummy;
1081 BOOL bAccept;
1082 Window win, w_aux_root, w_aux_child;
1084 win = X11DRV_get_whole_window(hWnd);
1085 wine_tsx11_lock();
1086 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1087 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1088 x += virtual_screen_rect.left;
1089 y += virtual_screen_rect.top;
1090 wine_tsx11_unlock();
1092 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1094 /* find out drop point and drop window */
1095 if( x < 0 || y < 0 ||
1096 x > (data->whole_rect.right - data->whole_rect.left) ||
1097 y > (data->whole_rect.bottom - data->whole_rect.top) )
1099 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1100 x = 0;
1101 y = 0;
1103 else
1105 POINT pt = { x, y };
1106 HWND hwndDrop = find_drop_window( hWnd, &pt );
1107 if (hwndDrop)
1109 x = pt.x;
1110 y = pt.y;
1111 bAccept = TRUE;
1113 else
1115 bAccept = FALSE;
1119 if (!bAccept) return;
1121 wine_tsx11_lock();
1122 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1123 x11drv_atom(DndSelection), 0, 65535, FALSE,
1124 AnyPropertyType, &atom_aux, &dummy,
1125 &data_length, &aux_long, &p_data);
1126 wine_tsx11_unlock();
1128 if( !aux_long && p_data) /* don't bother if > 64K */
1130 char *p = (char *)p_data;
1131 char *p_drop;
1133 aux_long = 0;
1134 while( *p ) /* calculate buffer size */
1136 INT len = GetShortPathNameA( p, NULL, 0 );
1137 if (len) aux_long += len + 1;
1138 p += strlen(p) + 1;
1140 if( aux_long && aux_long < 65535 )
1142 HDROP hDrop;
1143 DROPFILES *lpDrop;
1145 aux_long += sizeof(DROPFILES) + 1;
1146 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1147 lpDrop = GlobalLock( hDrop );
1149 if( lpDrop )
1151 lpDrop->pFiles = sizeof(DROPFILES);
1152 lpDrop->pt.x = x;
1153 lpDrop->pt.y = y;
1154 lpDrop->fNC = FALSE;
1155 lpDrop->fWide = FALSE;
1156 p_drop = (char *)(lpDrop + 1);
1157 p = (char *)p_data;
1158 while(*p)
1160 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1161 p_drop += strlen( p_drop ) + 1;
1162 p += strlen(p) + 1;
1164 *p_drop = '\0';
1165 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1169 wine_tsx11_lock();
1170 if( p_data ) XFree(p_data);
1171 wine_tsx11_unlock();
1174 /**********************************************************************
1175 * EVENT_DropURLs
1177 * drop items are separated by \n
1178 * each item is prefixed by its mime type
1180 * event->data.l[3], event->data.l[4] contains drop x,y position
1182 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1184 struct x11drv_win_data *win_data;
1185 unsigned long data_length;
1186 unsigned long aux_long, drop_len = 0;
1187 unsigned char *p_data = NULL; /* property data */
1188 char *p_drop = NULL;
1189 char *p, *next;
1190 int x, y;
1191 DROPFILES *lpDrop;
1192 HDROP hDrop;
1193 union {
1194 Atom atom_aux;
1195 int i;
1196 Window w_aux;
1197 unsigned int u;
1198 } u; /* unused */
1200 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1202 wine_tsx11_lock();
1203 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1204 x11drv_atom(DndSelection), 0, 65535, FALSE,
1205 AnyPropertyType, &u.atom_aux, &u.i,
1206 &data_length, &aux_long, &p_data);
1207 wine_tsx11_unlock();
1208 if (aux_long)
1209 WARN("property too large, truncated!\n");
1210 TRACE("urls=%s\n", p_data);
1212 if( !aux_long && p_data) { /* don't bother if > 64K */
1213 /* calculate length */
1214 p = (char*) p_data;
1215 next = strchr(p, '\n');
1216 while (p) {
1217 if (next) *next=0;
1218 if (strncmp(p,"file:",5) == 0 ) {
1219 INT len = GetShortPathNameA( p+5, NULL, 0 );
1220 if (len) drop_len += len + 1;
1222 if (next) {
1223 *next = '\n';
1224 p = next + 1;
1225 next = strchr(p, '\n');
1226 } else {
1227 p = NULL;
1231 if( drop_len && drop_len < 65535 ) {
1232 wine_tsx11_lock();
1233 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1234 &x, &y, &u.i, &u.i, &u.u);
1235 x += virtual_screen_rect.left;
1236 y += virtual_screen_rect.top;
1237 wine_tsx11_unlock();
1239 drop_len += sizeof(DROPFILES) + 1;
1240 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1241 lpDrop = GlobalLock( hDrop );
1243 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1245 lpDrop->pFiles = sizeof(DROPFILES);
1246 lpDrop->pt.x = x;
1247 lpDrop->pt.y = y;
1248 lpDrop->fNC =
1249 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1250 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1251 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1252 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1253 lpDrop->fWide = FALSE;
1254 p_drop = (char*)(lpDrop + 1);
1257 /* create message content */
1258 if (p_drop) {
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, p_drop, 65535 );
1265 if (len) {
1266 TRACE("drop file %s as %s\n", p+5, p_drop);
1267 p_drop += len+1;
1268 } else {
1269 WARN("can't convert file %s to dos name\n", p+5);
1271 } else {
1272 WARN("unknown mime type %s\n", p);
1274 if (next) {
1275 *next = '\n';
1276 p = next + 1;
1277 next = strchr(p, '\n');
1278 } else {
1279 p = NULL;
1281 *p_drop = '\0';
1284 GlobalUnlock(hDrop);
1285 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1288 wine_tsx11_lock();
1289 if( p_data ) XFree(p_data);
1290 wine_tsx11_unlock();
1294 /**********************************************************************
1295 * handle_dnd_protocol
1297 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1299 Window root, child;
1300 int root_x, root_y, child_x, child_y;
1301 unsigned int u;
1303 /* query window (drag&drop event contains only drag window) */
1304 wine_tsx11_lock();
1305 XQueryPointer( event->display, root_window, &root, &child,
1306 &root_x, &root_y, &child_x, &child_y, &u);
1307 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1308 wine_tsx11_unlock();
1309 if (!hwnd) return;
1310 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1311 EVENT_DropFromOffiX(hwnd, event);
1312 else if (event->data.l[0] == DndURL)
1313 EVENT_DropURLs(hwnd, event);
1317 struct client_message_handler
1319 int atom; /* protocol atom */
1320 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1323 static const struct client_message_handler client_messages[] =
1325 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1326 { XATOM_DndProtocol, handle_dnd_protocol },
1327 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1328 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1329 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1330 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1334 /**********************************************************************
1335 * X11DRV_ClientMessage
1337 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1339 XClientMessageEvent *event = &xev->xclient;
1340 unsigned int i;
1342 if (!hwnd) return;
1344 if (event->format != 32)
1346 WARN( "Don't know how to handle format %d\n", event->format );
1347 return;
1350 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1352 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1354 client_messages[i].handler( hwnd, event );
1355 return;
1358 TRACE( "no handler found for %ld\n", event->message_type );
1362 /***********************************************************************
1363 * X11DRV_SendInput (X11DRV.@)
1365 UINT CDECL X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1367 UINT i;
1369 for (i = 0; i < count; i++, inputs++)
1371 switch(inputs->type)
1373 case INPUT_MOUSE:
1374 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1375 inputs->u.mi.mouseData, inputs->u.mi.time,
1376 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1377 break;
1378 case INPUT_KEYBOARD:
1379 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1380 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1381 break;
1382 case INPUT_HARDWARE:
1383 FIXME( "INPUT_HARDWARE not supported\n" );
1384 break;
1387 return count;