4 * Copyright 1993 Alexandre Julliard
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
27 #ifdef HAVE_SYS_POLL_H
30 #include <X11/Xatom.h>
31 #include <X11/keysym.h>
33 #include <X11/Xresource.h>
34 #include <X11/Xutil.h>
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
49 /* avoid conflict with field names in included win32 headers */
51 #include "shlobj.h" /* DROPFILES */
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 */
73 #define DndURL 128 /* KDE drag&drop */
75 #define XEMBED_EMBEDDED_NOTIFY 0
76 #define XEMBED_WINDOW_ACTIVATE 1
77 #define XEMBED_WINDOW_DEACTIVATE 2
78 #define XEMBED_REQUEST_FOCUS 3
79 #define XEMBED_FOCUS_IN 4
80 #define XEMBED_FOCUS_OUT 5
81 #define XEMBED_FOCUS_NEXT 6
82 #define XEMBED_FOCUS_PREV 7
83 #define XEMBED_MODALITY_ON 10
84 #define XEMBED_MODALITY_OFF 11
85 #define XEMBED_REGISTER_ACCELERATOR 12
86 #define XEMBED_UNREGISTER_ACCELERATOR 13
87 #define XEMBED_ACTIVATE_ACCELERATOR 14
90 static void X11DRV_FocusIn( HWND hwnd
, XEvent
*event
);
91 static void X11DRV_FocusOut( HWND hwnd
, XEvent
*event
);
92 static void X11DRV_Expose( HWND hwnd
, XEvent
*event
);
93 static void X11DRV_MapNotify( HWND hwnd
, XEvent
*event
);
94 static void X11DRV_UnmapNotify( HWND hwnd
, XEvent
*event
);
95 static void X11DRV_ReparentNotify( HWND hwnd
, XEvent
*event
);
96 static void X11DRV_ConfigureNotify( HWND hwnd
, XEvent
*event
);
97 static void X11DRV_PropertyNotify( HWND hwnd
, XEvent
*event
);
98 static void X11DRV_ClientMessage( HWND hwnd
, XEvent
*event
);
99 static void X11DRV_GravityNotify( HWND hwnd
, XEvent
*event
);
103 int type
; /* event type */
104 x11drv_event_handler handler
; /* corresponding handler function */
107 #define MAX_EVENT_HANDLERS 64
109 static struct event_handler handlers
[MAX_EVENT_HANDLERS
] =
111 /* list must be sorted by event type */
112 { KeyPress
, X11DRV_KeyEvent
},
113 { KeyRelease
, X11DRV_KeyEvent
},
114 { ButtonPress
, X11DRV_ButtonPress
},
115 { ButtonRelease
, X11DRV_ButtonRelease
},
116 { MotionNotify
, X11DRV_MotionNotify
},
117 { EnterNotify
, X11DRV_EnterNotify
},
119 { FocusIn
, X11DRV_FocusIn
},
120 { FocusOut
, X11DRV_FocusOut
},
121 { KeymapNotify
, X11DRV_KeymapNotify
},
122 { Expose
, X11DRV_Expose
},
125 /* VisibilityNotify */
127 { DestroyNotify
, X11DRV_DestroyNotify
},
128 { UnmapNotify
, X11DRV_UnmapNotify
},
129 { MapNotify
, X11DRV_MapNotify
},
131 { ReparentNotify
, X11DRV_ReparentNotify
},
132 { ConfigureNotify
, X11DRV_ConfigureNotify
},
133 /* ConfigureRequest */
134 { GravityNotify
, X11DRV_GravityNotify
},
136 /* CirculateNotify */
137 /* CirculateRequest */
138 { PropertyNotify
, X11DRV_PropertyNotify
},
139 { SelectionClear
, X11DRV_SelectionClear
},
140 { SelectionRequest
, X11DRV_SelectionRequest
},
141 /* SelectionNotify */
143 { ClientMessage
, X11DRV_ClientMessage
},
144 { MappingNotify
, X11DRV_MappingNotify
},
147 static int nb_event_handlers
= 20; /* change this if you add handlers above */
150 /* return the name of an X event */
151 static const char *dbgstr_event( int type
)
153 static const char * const event_names
[] =
155 "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
156 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
157 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
158 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
159 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
160 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
161 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
162 "ClientMessage", "MappingNotify"
165 if (type
>= KeyPress
&& type
<= MappingNotify
) return event_names
[type
- KeyPress
];
166 return wine_dbg_sprintf( "Extension event %d", type
);
170 /***********************************************************************
173 * Find the handler for a given event type. Caller must hold the x11 lock.
175 static inline x11drv_event_handler
find_handler( int type
)
177 int min
= 0, max
= nb_event_handlers
- 1;
181 int pos
= (min
+ max
) / 2;
182 if (handlers
[pos
].type
== type
) return handlers
[pos
].handler
;
183 if (handlers
[pos
].type
> type
) max
= pos
- 1;
190 /***********************************************************************
191 * X11DRV_register_event_handler
193 * Register a handler for a given event type.
194 * If already registered, overwrite the previous handler.
196 void X11DRV_register_event_handler( int type
, x11drv_event_handler handler
)
202 max
= nb_event_handlers
- 1;
205 int pos
= (min
+ max
) / 2;
206 if (handlers
[pos
].type
== type
)
208 handlers
[pos
].handler
= handler
;
211 if (handlers
[pos
].type
> type
) max
= pos
- 1;
214 /* insert it between max and min */
215 memmove( &handlers
[min
+1], &handlers
[min
], (nb_event_handlers
- min
) * sizeof(handlers
[0]) );
216 handlers
[min
].type
= type
;
217 handlers
[min
].handler
= handler
;
219 assert( nb_event_handlers
<= MAX_EVENT_HANDLERS
);
222 TRACE("registered handler %p for event %d count %d\n", handler
, type
, nb_event_handlers
);
226 /***********************************************************************
229 static Bool
filter_event( Display
*display
, XEvent
*event
, char *arg
)
231 ULONG_PTR mask
= (ULONG_PTR
)arg
;
233 if ((mask
& QS_ALLINPUT
) == QS_ALLINPUT
) return 1;
241 return (mask
& QS_KEY
) != 0;
244 return (mask
& QS_MOUSEBUTTON
) != 0;
248 return (mask
& QS_MOUSEMOVE
) != 0;
250 return (mask
& QS_PAINT
) != 0;
255 case ConfigureNotify
:
258 return (mask
& QS_POSTMESSAGE
) != 0;
260 return (mask
& QS_SENDMESSAGE
) != 0;
265 enum event_merge_action
267 MERGE_DISCARD
, /* discard the old event */
268 MERGE_HANDLE
, /* handle the old event */
269 MERGE_KEEP
/* keep the old event for future merging */
272 /***********************************************************************
275 * Try to merge 2 consecutive events.
277 static enum event_merge_action
merge_events( XEvent
*prev
, XEvent
*next
)
281 case ConfigureNotify
:
284 case ConfigureNotify
:
285 if (prev
->xany
.window
== next
->xany
.window
)
287 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev
->xany
.window
);
288 return MERGE_DISCARD
;
297 if (prev
->xany
.window
== next
->xany
.window
&& next
->type
== MotionNotify
)
299 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev
->xany
.window
);
300 return MERGE_DISCARD
;
308 /***********************************************************************
311 static inline void call_event_handler( Display
*display
, XEvent
*event
)
314 x11drv_event_handler handler
;
316 struct x11drv_thread_data
*thread_data
;
318 if (!(handler
= find_handler( event
->type
)))
320 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event
->type
), event
->xany
.window
);
321 return; /* no handler, ignore it */
324 if (XFindContext( display
, event
->xany
.window
, winContext
, (char **)&hwnd
) != 0)
325 hwnd
= 0; /* not for a registered window */
326 if (!hwnd
&& event
->xany
.window
== root_window
) hwnd
= GetDesktopWindow();
328 TRACE( "%lu %s for hwnd/window %p/%lx\n",
329 event
->xany
.serial
, dbgstr_event( event
->type
), hwnd
, event
->xany
.window
);
331 thread_data
= x11drv_thread_data();
332 prev
= thread_data
->current_event
;
333 thread_data
->current_event
= event
;
334 handler( hwnd
, event
);
335 thread_data
->current_event
= prev
;
340 /***********************************************************************
343 static int process_events( Display
*display
, Bool (*filter
)(Display
*, XEvent
*,XPointer
), ULONG_PTR arg
)
345 XEvent event
, prev_event
;
347 enum event_merge_action action
= MERGE_DISCARD
;
351 while (XCheckIfEvent( display
, &event
, filter
, (char *)arg
))
354 if (XFilterEvent( &event
, None
))
357 * SCIM on linux filters key events strangely. It does not filter the
358 * KeyPress events for these keys however it does filter the
359 * KeyRelease events. This causes wine to become very confused as
360 * to the keyboard state.
362 * We need to let those KeyRelease events be processed so that the
363 * keyboard state is correct.
365 if (event
.type
== KeyRelease
)
368 XKeyEvent
*keyevent
= &event
.xkey
;
370 XLookupString(keyevent
, NULL
, 0, &keysym
, NULL
);
371 if (!(keysym
== XK_Shift_L
||
372 keysym
== XK_Shift_R
||
373 keysym
== XK_Control_L
||
374 keysym
== XK_Control_R
||
375 keysym
== XK_Alt_R
||
376 keysym
== XK_Alt_L
||
377 keysym
== XK_Meta_R
||
378 keysym
== XK_Meta_L
))
379 continue; /* not a key we care about, ignore it */
382 continue; /* filtered, ignore it */
384 if (prev_event
.type
) action
= merge_events( &prev_event
, &event
);
387 case MERGE_DISCARD
: /* discard prev, keep new */
390 case MERGE_HANDLE
: /* handle prev, keep new */
391 call_event_handler( display
, &prev_event
);
394 case MERGE_KEEP
: /* handle new, keep prev for future merging */
395 call_event_handler( display
, &event
);
399 if (prev_event
.type
) call_event_handler( display
, &prev_event
);
400 XFlush( gdi_display
);
402 if (count
) TRACE( "processed %d events\n", count
);
407 /***********************************************************************
408 * MsgWaitForMultipleObjectsEx (X11DRV.@)
410 DWORD CDECL
X11DRV_MsgWaitForMultipleObjectsEx( DWORD count
, const HANDLE
*handles
,
411 DWORD timeout
, DWORD mask
, DWORD flags
)
414 struct x11drv_thread_data
*data
= TlsGetValue( thread_data_tls_index
);
418 if (!count
&& !timeout
) return WAIT_TIMEOUT
;
419 return WaitForMultipleObjectsEx( count
, handles
, flags
& MWMO_WAITALL
,
420 timeout
, flags
& MWMO_ALERTABLE
);
423 if (data
->current_event
) mask
= 0; /* don't process nested events */
425 if (process_events( data
->display
, filter_event
, mask
)) ret
= count
- 1;
426 else if (count
|| timeout
)
428 ret
= WaitForMultipleObjectsEx( count
, handles
, flags
& MWMO_WAITALL
,
429 timeout
, flags
& MWMO_ALERTABLE
);
430 if (ret
== count
- 1) process_events( data
->display
, filter_event
, mask
);
432 else ret
= WAIT_TIMEOUT
;
437 /***********************************************************************
438 * EVENT_x11_time_to_win32_time
440 * Make our timer and the X timer line up as best we can
441 * Pass 0 to retrieve the current adjustment value (times -1)
443 DWORD
EVENT_x11_time_to_win32_time(Time time
)
445 static DWORD adjust
= 0;
446 DWORD now
= GetTickCount();
449 if (! adjust
&& time
!= 0)
456 /* If we got an event in the 'future', then our clock is clearly wrong.
457 If we got it more than 10000 ms in the future, then it's most likely
458 that the clock has wrapped. */
461 if (ret
> now
&& ((ret
- now
) < 10000) && time
!= 0)
472 /*******************************************************************
473 * can_activate_window
475 * Check if we can activate the specified window.
477 static inline BOOL
can_activate_window( HWND hwnd
)
479 LONG style
= GetWindowLongW( hwnd
, GWL_STYLE
);
480 if (!(style
& WS_VISIBLE
)) return FALSE
;
481 if ((style
& (WS_POPUP
|WS_CHILD
)) == WS_CHILD
) return FALSE
;
482 if (style
& WS_MINIMIZE
) return FALSE
;
483 if (GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_NOACTIVATE
) return FALSE
;
484 if (hwnd
== GetDesktopWindow()) return FALSE
;
485 return !(style
& WS_DISABLED
);
489 /**********************************************************************
492 static void set_focus( Display
*display
, HWND hwnd
, Time time
)
496 GUITHREADINFO threadinfo
;
498 TRACE( "setting foreground window to %p\n", hwnd
);
499 SetForegroundWindow( hwnd
);
501 threadinfo
.cbSize
= sizeof(threadinfo
);
502 GetGUIThreadInfo(0, &threadinfo
);
503 focus
= threadinfo
.hwndFocus
;
504 if (!focus
) focus
= threadinfo
.hwndActive
;
505 if (focus
) focus
= GetAncestor( focus
, GA_ROOT
);
506 win
= X11DRV_get_whole_window(focus
);
510 TRACE( "setting focus to %p (%lx) time=%ld\n", focus
, win
, time
);
512 XSetInputFocus( display
, win
, RevertToParent
, time
);
518 /**********************************************************************
519 * handle_manager_message
521 static void handle_manager_message( HWND hwnd
, XClientMessageEvent
*event
)
523 if (hwnd
!= GetDesktopWindow()) return;
524 if (systray_atom
&& event
->data
.l
[1] == systray_atom
)
525 change_systray_owner( event
->display
, event
->data
.l
[2] );
529 /**********************************************************************
530 * handle_wm_protocols
532 static void handle_wm_protocols( HWND hwnd
, XClientMessageEvent
*event
)
534 Atom protocol
= (Atom
)event
->data
.l
[0];
535 Time event_time
= (Time
)event
->data
.l
[1];
537 if (!protocol
) return;
539 if (protocol
== x11drv_atom(WM_DELETE_WINDOW
))
541 update_user_time( event_time
);
543 if (hwnd
== GetDesktopWindow())
545 /* The desktop window does not have a close button that we can
546 * pretend to click. Therefore, we simply send it a close command. */
547 SendMessageW(hwnd
, WM_SYSCOMMAND
, SC_CLOSE
, 0);
551 /* Ignore the delete window request if the window has been disabled
552 * and we are in managed mode. This is to disallow applications from
553 * being closed by the window manager while in a modal state.
555 if (IsWindowEnabled(hwnd
))
559 if (GetClassLongW(hwnd
, GCL_STYLE
) & CS_NOCLOSE
) return;
560 hSysMenu
= GetSystemMenu(hwnd
, FALSE
);
563 UINT state
= GetMenuState(hSysMenu
, SC_CLOSE
, MF_BYCOMMAND
);
564 if (state
== 0xFFFFFFFF || (state
& (MF_DISABLED
| MF_GRAYED
)))
567 if (GetActiveWindow() != hwnd
)
569 LRESULT ma
= SendMessageW( hwnd
, WM_MOUSEACTIVATE
,
570 (WPARAM
)GetAncestor( hwnd
, GA_ROOT
),
571 MAKELPARAM( HTCLOSE
, WM_NCLBUTTONDOWN
) );
574 case MA_NOACTIVATEANDEAT
:
575 case MA_ACTIVATEANDEAT
:
581 SetActiveWindow(hwnd
);
584 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma
);
589 PostMessageW( hwnd
, WM_SYSCOMMAND
, SC_CLOSE
, 0 );
592 else if (protocol
== x11drv_atom(WM_TAKE_FOCUS
))
594 HWND last_focus
= x11drv_thread_data()->last_focus
;
596 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
597 hwnd
, IsWindowEnabled(hwnd
), IsWindowVisible(hwnd
), GetWindowLongW(hwnd
, GWL_STYLE
),
598 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus
);
600 if (can_activate_window(hwnd
))
602 /* simulate a mouse click on the caption to find out
603 * whether the window wants to be activated */
604 LRESULT ma
= SendMessageW( hwnd
, WM_MOUSEACTIVATE
,
605 (WPARAM
)GetAncestor( hwnd
, GA_ROOT
),
606 MAKELONG(HTCAPTION
,WM_LBUTTONDOWN
) );
607 if (ma
!= MA_NOACTIVATEANDEAT
&& ma
!= MA_NOACTIVATE
)
609 set_focus( event
->display
, hwnd
, event_time
);
613 else if (hwnd
== GetDesktopWindow())
615 hwnd
= GetForegroundWindow();
616 if (!hwnd
) hwnd
= last_focus
;
617 if (!hwnd
) hwnd
= GetDesktopWindow();
618 set_focus( event
->display
, hwnd
, event_time
);
621 /* try to find some other window to give the focus to */
623 if (hwnd
) hwnd
= GetAncestor( hwnd
, GA_ROOT
);
624 if (!hwnd
) hwnd
= GetActiveWindow();
625 if (!hwnd
) hwnd
= last_focus
;
626 if (hwnd
&& can_activate_window(hwnd
)) set_focus( event
->display
, hwnd
, event_time
);
628 else if (protocol
== x11drv_atom(_NET_WM_PING
))
630 XClientMessageEvent xev
;
633 TRACE("NET_WM Ping\n");
635 xev
.window
= DefaultRootWindow(xev
.display
);
636 XSendEvent(xev
.display
, xev
.window
, False
, SubstructureRedirectMask
| SubstructureNotifyMask
, (XEvent
*)&xev
);
638 /* this line is semi-stolen from gtk2 */
639 TRACE("NET_WM Pong\n");
644 static const char * const focus_details
[] =
650 "NotifyNonlinearVirtual",
656 /**********************************************************************
659 static void X11DRV_FocusIn( HWND hwnd
, XEvent
*xev
)
661 XFocusChangeEvent
*event
= &xev
->xfocus
;
666 TRACE( "win %p xwin %lx detail=%s\n", hwnd
, event
->window
, focus_details
[event
->detail
] );
668 if (event
->detail
== NotifyPointer
) return;
670 if ((xic
= X11DRV_get_ic( hwnd
)))
676 if (use_take_focus
) return; /* ignore FocusIn if we are using take focus */
678 if (!can_activate_window(hwnd
))
680 HWND hwnd
= GetFocus();
681 if (hwnd
) hwnd
= GetAncestor( hwnd
, GA_ROOT
);
682 if (!hwnd
) hwnd
= GetActiveWindow();
683 if (!hwnd
) hwnd
= x11drv_thread_data()->last_focus
;
684 if (hwnd
&& can_activate_window(hwnd
)) set_focus( event
->display
, hwnd
, CurrentTime
);
686 else SetForegroundWindow( hwnd
);
690 /**********************************************************************
693 * Note: only top-level windows get FocusOut events.
695 static void X11DRV_FocusOut( HWND hwnd
, XEvent
*xev
)
697 XFocusChangeEvent
*event
= &xev
->xfocus
;
705 TRACE( "win %p xwin %lx detail=%s\n", hwnd
, event
->window
, focus_details
[event
->detail
] );
707 if (event
->detail
== NotifyPointer
) return;
708 if (ximInComposeMode
) return;
710 x11drv_thread_data()->last_focus
= hwnd
;
711 if ((xic
= X11DRV_get_ic( hwnd
)))
714 XUnsetICFocus( xic
);
717 if (hwnd
!= GetForegroundWindow()) return;
718 if (root_window
!= DefaultRootWindow(event
->display
)) return;
719 SendMessageW( hwnd
, WM_CANCELMODE
, 0, 0 );
721 /* don't reset the foreground window, if the window which is
722 getting the focus is a Wine window */
725 XGetInputFocus( event
->display
, &focus_win
, &revert
);
728 if (XFindContext( event
->display
, focus_win
, winContext
, (char **)&hwnd_tmp
) != 0)
735 /* Abey : 6-Oct-99. Check again if the focus out window is the
736 Foreground window, because in most cases the messages sent
737 above must have already changed the foreground window, in which
738 case we don't have to change the foreground window to 0 */
739 if (hwnd
== GetForegroundWindow())
741 TRACE( "lost focus, setting fg to desktop\n" );
742 SetForegroundWindow( GetDesktopWindow() );
748 /***********************************************************************
751 static void X11DRV_Expose( HWND hwnd
, XEvent
*xev
)
753 XExposeEvent
*event
= &xev
->xexpose
;
755 struct x11drv_win_data
*data
;
756 int flags
= RDW_INVALIDATE
| RDW_ERASE
;
758 TRACE( "win %p (%lx) %d,%d %dx%d\n",
759 hwnd
, event
->window
, event
->x
, event
->y
, event
->width
, event
->height
);
761 if (!(data
= X11DRV_get_win_data( hwnd
))) return;
763 rect
.left
= event
->x
;
765 rect
.right
= event
->x
+ event
->width
;
766 rect
.bottom
= event
->y
+ event
->height
;
767 if (event
->window
== data
->whole_window
)
769 OffsetRect( &rect
, data
->whole_rect
.left
- data
->client_rect
.left
,
770 data
->whole_rect
.top
- data
->client_rect
.top
);
774 if (event
->window
!= root_window
)
776 if (GetWindowLongW( data
->hwnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
)
777 mirror_rect( &data
->client_rect
, &rect
);
779 SERVER_START_REQ( update_window_zorder
)
781 req
->window
= wine_server_user_handle( hwnd
);
782 req
->rect
.left
= rect
.left
;
783 req
->rect
.top
= rect
.top
;
784 req
->rect
.right
= rect
.right
;
785 req
->rect
.bottom
= rect
.bottom
;
786 wine_server_call( req
);
790 flags
|= RDW_ALLCHILDREN
;
792 else OffsetRect( &rect
, virtual_screen_rect
.left
, virtual_screen_rect
.top
);
794 RedrawWindow( hwnd
, &rect
, 0, flags
);
798 /**********************************************************************
801 static void X11DRV_MapNotify( HWND hwnd
, XEvent
*event
)
803 struct x11drv_win_data
*data
;
805 if (event
->xany
.window
== clip_window
)
810 if (!(data
= X11DRV_get_win_data( hwnd
))) return;
811 if (!data
->mapped
|| data
->embedded
) return;
815 HWND hwndFocus
= GetFocus();
816 if (hwndFocus
&& IsChild( hwnd
, hwndFocus
)) X11DRV_SetFocus(hwndFocus
); /* FIXME */
821 /**********************************************************************
824 static void X11DRV_UnmapNotify( HWND hwnd
, XEvent
*event
)
826 if (event
->xany
.window
== clip_window
) clipping_cursor
= 0;
830 /***********************************************************************
831 * is_net_wm_state_maximized
833 static BOOL
is_net_wm_state_maximized( Display
*display
, struct x11drv_win_data
*data
)
837 unsigned long i
, count
, remaining
;
839 if (!data
->whole_window
) return FALSE
;
842 if (!XGetWindowProperty( display
, data
->whole_window
, x11drv_atom(_NET_WM_STATE
), 0,
843 65536/sizeof(CARD32
), False
, XA_ATOM
, &type
, &format
, &count
,
844 &remaining
, (unsigned char **)&state
))
846 if (type
== XA_ATOM
&& format
== 32)
848 for (i
= 0; i
< count
; i
++)
850 if (state
[i
] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT
) ||
851 state
[i
] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ
))
862 /***********************************************************************
863 * X11DRV_ReparentNotify
865 static void X11DRV_ReparentNotify( HWND hwnd
, XEvent
*xev
)
867 XReparentEvent
*event
= &xev
->xreparent
;
868 struct x11drv_win_data
*data
;
869 HWND parent
, old_parent
;
872 if (!(data
= X11DRV_get_win_data( hwnd
))) return;
873 if (!data
->embedded
) return;
875 if (data
->whole_window
)
877 if (event
->parent
== root_window
)
879 TRACE( "%p/%lx reparented to root\n", hwnd
, data
->whole_window
);
881 SendMessageW( hwnd
, WM_CLOSE
, 0, 0 );
884 data
->embedder
= event
->parent
;
887 TRACE( "%p/%lx reparented to %lx\n", hwnd
, data
->whole_window
, event
->parent
);
889 style
= GetWindowLongW( hwnd
, GWL_STYLE
);
890 if (event
->parent
== root_window
)
892 parent
= GetDesktopWindow();
893 style
= (style
& ~WS_CHILD
) | WS_POPUP
;
897 if (!(parent
= create_foreign_window( event
->display
, event
->parent
))) return;
898 style
= (style
& ~WS_POPUP
) | WS_CHILD
;
901 ShowWindow( hwnd
, SW_HIDE
);
902 old_parent
= SetParent( hwnd
, parent
);
903 SetWindowLongW( hwnd
, GWL_STYLE
, style
);
904 SetWindowPos( hwnd
, HWND_TOP
, event
->x
, event
->y
, 0, 0,
905 SWP_NOACTIVATE
| SWP_NOSIZE
| SWP_NOCOPYBITS
|
906 ((style
& WS_VISIBLE
) ? SWP_SHOWWINDOW
: 0) );
908 /* make old parent destroy itself if it no longer has children */
909 if (old_parent
!= GetDesktopWindow()) PostMessageW( old_parent
, WM_CLOSE
, 0, 0 );
913 /***********************************************************************
914 * X11DRV_ConfigureNotify
916 void X11DRV_ConfigureNotify( HWND hwnd
, XEvent
*xev
)
918 XConfigureEvent
*event
= &xev
->xconfigure
;
919 struct x11drv_win_data
*data
;
924 int cx
, cy
, x
= event
->x
, y
= event
->y
;
927 if (!(data
= X11DRV_get_win_data( hwnd
))) return;
928 if (!data
->mapped
|| data
->iconic
) return;
929 if (data
->whole_window
&& !data
->managed
) return;
930 /* ignore synthetic events on foreign windows */
931 if (event
->send_event
&& !data
->whole_window
) return;
932 if (data
->configure_serial
&& (long)(data
->configure_serial
- event
->serial
) > 0)
934 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
935 hwnd
, data
->whole_window
, event
->x
, event
->y
, event
->width
, event
->height
,
936 event
->serial
, data
->configure_serial
);
942 parent
= GetAncestor( hwnd
, GA_PARENT
);
943 root_coords
= event
->send_event
; /* synthetic events are always in root coords */
945 if (!root_coords
&& parent
== GetDesktopWindow()) /* normal event, map coordinates to the root */
949 XTranslateCoordinates( event
->display
, event
->window
, root_window
,
950 0, 0, &x
, &y
, &child
);
956 rect
.right
= x
+ event
->width
;
957 rect
.bottom
= y
+ event
->height
;
958 if (root_coords
) OffsetRect( &rect
, virtual_screen_rect
.left
, virtual_screen_rect
.top
);
959 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
960 hwnd
, data
->whole_window
, rect
.left
, rect
.top
, rect
.right
-rect
.left
, rect
.bottom
-rect
.top
,
961 event
->x
, event
->y
, event
->width
, event
->height
);
963 if (is_net_wm_state_maximized( event
->display
, data
))
965 if (!IsZoomed( data
->hwnd
))
967 TRACE( "win %p/%lx is maximized\n", data
->hwnd
, data
->whole_window
);
968 SendMessageW( data
->hwnd
, WM_SYSCOMMAND
, SC_MAXIMIZE
, 0 );
974 if (IsZoomed( data
->hwnd
))
976 TRACE( "window %p/%lx is no longer maximized\n", data
->hwnd
, data
->whole_window
);
977 SendMessageW( data
->hwnd
, WM_SYSCOMMAND
, SC_RESTORE
, 0 );
982 X11DRV_X_to_window_rect( data
, &rect
);
983 if (root_coords
) MapWindowPoints( 0, parent
, (POINT
*)&rect
, 2 );
985 /* Compare what has changed */
989 cx
= rect
.right
- rect
.left
;
990 cy
= rect
.bottom
- rect
.top
;
991 flags
= SWP_NOACTIVATE
| SWP_NOZORDER
;
993 if (!data
->whole_window
) flags
|= SWP_NOCOPYBITS
; /* we can't copy bits of foreign windows */
995 if (data
->window_rect
.left
== x
&& data
->window_rect
.top
== y
) flags
|= SWP_NOMOVE
;
997 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
998 hwnd
, data
->window_rect
.left
, data
->window_rect
.top
, x
, y
);
1000 if ((data
->window_rect
.right
- data
->window_rect
.left
== cx
&&
1001 data
->window_rect
.bottom
- data
->window_rect
.top
== cy
) ||
1002 (IsRectEmpty( &data
->window_rect
) && event
->width
== 1 && event
->height
== 1))
1004 if (flags
& SWP_NOMOVE
) return; /* if nothing changed, don't do anything */
1005 flags
|= SWP_NOSIZE
;
1008 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
1009 hwnd
, data
->window_rect
.right
- data
->window_rect
.left
,
1010 data
->window_rect
.bottom
- data
->window_rect
.top
, cx
, cy
);
1012 SetWindowPos( hwnd
, 0, x
, y
, cx
, cy
, flags
);
1016 /**********************************************************************
1017 * X11DRV_GravityNotify
1019 static void X11DRV_GravityNotify( HWND hwnd
, XEvent
*xev
)
1021 XGravityEvent
*event
= &xev
->xgravity
;
1022 struct x11drv_win_data
*data
= X11DRV_get_win_data( hwnd
);
1025 if (!data
|| data
->whole_window
) return; /* only handle this for foreign windows */
1027 rect
.left
= event
->x
;
1028 rect
.top
= event
->y
;
1029 rect
.right
= rect
.left
+ data
->whole_rect
.right
- data
->whole_rect
.left
;
1030 rect
.bottom
= rect
.top
+ data
->whole_rect
.bottom
- data
->whole_rect
.top
;
1032 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d)\n",
1033 hwnd
, data
->whole_window
, rect
.left
, rect
.top
, rect
.right
-rect
.left
, rect
.bottom
-rect
.top
,
1034 event
->x
, event
->y
);
1036 X11DRV_X_to_window_rect( data
, &rect
);
1038 if (data
->window_rect
.left
!= rect
.left
|| data
->window_rect
.top
!= rect
.top
)
1039 SetWindowPos( hwnd
, 0, rect
.left
, rect
.top
, 0, 0,
1040 SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOCOPYBITS
);
1044 /***********************************************************************
1045 * get_window_wm_state
1047 static int get_window_wm_state( Display
*display
, struct x11drv_win_data
*data
)
1055 int format
, ret
= -1;
1056 unsigned long count
, remaining
;
1059 if (!XGetWindowProperty( display
, data
->whole_window
, x11drv_atom(WM_STATE
), 0,
1060 sizeof(*state
)/sizeof(CARD32
), False
, x11drv_atom(WM_STATE
),
1061 &type
, &format
, &count
, &remaining
, (unsigned char **)&state
))
1063 if (type
== x11drv_atom(WM_STATE
) && get_property_size( format
, count
) >= sizeof(*state
))
1067 wine_tsx11_unlock();
1072 /***********************************************************************
1073 * handle_wm_state_notify
1075 * Handle a PropertyNotify for WM_STATE.
1077 static void handle_wm_state_notify( struct x11drv_win_data
*data
, XPropertyEvent
*event
,
1078 BOOL update_window
)
1082 switch(event
->state
)
1084 case PropertyDelete
:
1085 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data
->hwnd
, data
->whole_window
, data
->wm_state
);
1086 data
->wm_state
= WithdrawnState
;
1088 case PropertyNewValue
:
1090 int old_state
= data
->wm_state
;
1091 int new_state
= get_window_wm_state( event
->display
, data
);
1092 if (new_state
!= -1 && new_state
!= data
->wm_state
)
1094 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1095 data
->hwnd
, data
->whole_window
, new_state
, old_state
);
1096 data
->wm_state
= new_state
;
1097 /* ignore the initial state transition out of withdrawn state */
1098 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1099 if (!old_state
) return;
1105 if (!update_window
|| !data
->managed
|| !data
->mapped
) return;
1107 style
= GetWindowLongW( data
->hwnd
, GWL_STYLE
);
1109 if (data
->iconic
&& data
->wm_state
== NormalState
) /* restore window */
1111 data
->iconic
= FALSE
;
1112 if (is_net_wm_state_maximized( event
->display
, data
))
1114 if ((style
& WS_MAXIMIZEBOX
) && !(style
& WS_DISABLED
))
1116 TRACE( "restoring to max %p/%lx\n", data
->hwnd
, data
->whole_window
);
1117 SendMessageW( data
->hwnd
, WM_SYSCOMMAND
, SC_MAXIMIZE
, 0 );
1119 else TRACE( "not restoring to max win %p/%lx style %08x\n",
1120 data
->hwnd
, data
->whole_window
, style
);
1122 else if (style
& (WS_MINIMIZE
| WS_MAXIMIZE
))
1124 TRACE( "restoring win %p/%lx\n", data
->hwnd
, data
->whole_window
);
1125 SendMessageW( data
->hwnd
, WM_SYSCOMMAND
, SC_RESTORE
, 0 );
1127 else TRACE( "not restoring win %p/%lx style %08x\n", data
->hwnd
, data
->whole_window
, style
);
1129 else if (!data
->iconic
&& data
->wm_state
== IconicState
)
1131 data
->iconic
= TRUE
;
1132 if ((style
& WS_MINIMIZEBOX
) && !(style
& WS_DISABLED
))
1134 TRACE( "minimizing win %p/%lx\n", data
->hwnd
, data
->whole_window
);
1135 SendMessageW( data
->hwnd
, WM_SYSCOMMAND
, SC_MINIMIZE
, 0 );
1137 else TRACE( "not minimizing win %p/%lx style %08x\n", data
->hwnd
, data
->whole_window
, style
);
1142 /***********************************************************************
1143 * X11DRV_PropertyNotify
1145 static void X11DRV_PropertyNotify( HWND hwnd
, XEvent
*xev
)
1147 XPropertyEvent
*event
= &xev
->xproperty
;
1148 struct x11drv_win_data
*data
;
1151 if (!(data
= X11DRV_get_win_data( hwnd
))) return;
1153 if (event
->atom
== x11drv_atom(WM_STATE
)) handle_wm_state_notify( data
, event
, TRUE
);
1157 /* event filter to wait for a WM_STATE change notification on a window */
1158 static Bool
is_wm_state_notify( Display
*display
, XEvent
*event
, XPointer arg
)
1160 if (event
->xany
.window
!= (Window
)arg
) return 0;
1161 return (event
->type
== DestroyNotify
||
1162 (event
->type
== PropertyNotify
&& event
->xproperty
.atom
== x11drv_atom(WM_STATE
)));
1165 /***********************************************************************
1166 * wait_for_withdrawn_state
1168 void wait_for_withdrawn_state( Display
*display
, struct x11drv_win_data
*data
, BOOL set
)
1170 DWORD end
= GetTickCount() + 2000;
1172 if (!data
->managed
) return;
1174 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1175 data
->hwnd
, data
->whole_window
, set
? "" : "not " );
1177 while (data
->whole_window
&& ((data
->wm_state
== WithdrawnState
) == !set
))
1183 while (XCheckIfEvent( display
, &event
, is_wm_state_notify
, (char *)data
->whole_window
))
1186 if (XFilterEvent( &event
, None
)) continue; /* filtered, ignore it */
1187 if (event
.type
== DestroyNotify
) call_event_handler( display
, &event
);
1190 wine_tsx11_unlock();
1191 handle_wm_state_notify( data
, &event
.xproperty
, FALSE
);
1195 wine_tsx11_unlock();
1200 int timeout
= end
- GetTickCount();
1202 pfd
.fd
= ConnectionNumber(display
);
1203 pfd
.events
= POLLIN
;
1204 if (timeout
<= 0 || poll( &pfd
, 1, timeout
) != 1)
1206 FIXME( "window %p/%lx wait timed out\n", data
->hwnd
, data
->whole_window
);
1211 TRACE( "window %p/%lx state now %d\n", data
->hwnd
, data
->whole_window
, data
->wm_state
);
1215 static HWND
find_drop_window( HWND hQueryWnd
, LPPOINT lpPt
)
1219 if (!IsWindowEnabled(hQueryWnd
)) return 0;
1221 GetWindowRect(hQueryWnd
, &tempRect
);
1223 if(!PtInRect(&tempRect
, *lpPt
)) return 0;
1225 if (!IsIconic( hQueryWnd
))
1228 ScreenToClient( hQueryWnd
, &pt
);
1229 GetClientRect( hQueryWnd
, &tempRect
);
1231 if (PtInRect( &tempRect
, pt
))
1233 HWND ret
= ChildWindowFromPointEx( hQueryWnd
, pt
, CWP_SKIPINVISIBLE
|CWP_SKIPDISABLED
);
1234 if (ret
&& ret
!= hQueryWnd
)
1236 ret
= find_drop_window( ret
, lpPt
);
1237 if (ret
) return ret
;
1242 if(!(GetWindowLongA( hQueryWnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
)) return 0;
1244 ScreenToClient(hQueryWnd
, lpPt
);
1249 /**********************************************************************
1250 * EVENT_DropFromOffix
1252 * don't know if it still works (last Changelog is from 96/11/04)
1254 static void EVENT_DropFromOffiX( HWND hWnd
, XClientMessageEvent
*event
)
1256 struct x11drv_win_data
*data
;
1257 unsigned long data_length
;
1258 unsigned long aux_long
;
1259 unsigned char* p_data
= NULL
;
1263 Window win
, w_aux_root
, w_aux_child
;
1265 win
= X11DRV_get_whole_window(hWnd
);
1267 XQueryPointer( event
->display
, win
, &w_aux_root
, &w_aux_child
,
1268 &x
, &y
, &dummy
, &dummy
, (unsigned int*)&aux_long
);
1269 x
+= virtual_screen_rect
.left
;
1270 y
+= virtual_screen_rect
.top
;
1271 wine_tsx11_unlock();
1273 if (!(data
= X11DRV_get_win_data( hWnd
))) return;
1275 /* find out drop point and drop window */
1276 if( x
< 0 || y
< 0 ||
1277 x
> (data
->whole_rect
.right
- data
->whole_rect
.left
) ||
1278 y
> (data
->whole_rect
.bottom
- data
->whole_rect
.top
) )
1280 bAccept
= GetWindowLongW( hWnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
;
1286 POINT pt
= { x
, y
};
1287 HWND hwndDrop
= find_drop_window( hWnd
, &pt
);
1300 if (!bAccept
) return;
1303 XGetWindowProperty( event
->display
, DefaultRootWindow(event
->display
),
1304 x11drv_atom(DndSelection
), 0, 65535, FALSE
,
1305 AnyPropertyType
, &atom_aux
, &dummy
,
1306 &data_length
, &aux_long
, &p_data
);
1307 wine_tsx11_unlock();
1309 if( !aux_long
&& p_data
) /* don't bother if > 64K */
1311 char *p
= (char *)p_data
;
1315 while( *p
) /* calculate buffer size */
1317 INT len
= GetShortPathNameA( p
, NULL
, 0 );
1318 if (len
) aux_long
+= len
+ 1;
1321 if( aux_long
&& aux_long
< 65535 )
1326 aux_long
+= sizeof(DROPFILES
) + 1;
1327 hDrop
= GlobalAlloc( GMEM_SHARE
, aux_long
);
1328 lpDrop
= GlobalLock( hDrop
);
1332 lpDrop
->pFiles
= sizeof(DROPFILES
);
1335 lpDrop
->fNC
= FALSE
;
1336 lpDrop
->fWide
= FALSE
;
1337 p_drop
= (char *)(lpDrop
+ 1);
1341 if (GetShortPathNameA( p
, p_drop
, aux_long
- (p_drop
- (char *)lpDrop
) ))
1342 p_drop
+= strlen( p_drop
) + 1;
1346 PostMessageA( hWnd
, WM_DROPFILES
, (WPARAM
)hDrop
, 0L );
1351 if( p_data
) XFree(p_data
);
1352 wine_tsx11_unlock();
1355 /**********************************************************************
1358 * drop items are separated by \n
1359 * each item is prefixed by its mime type
1361 * event->data.l[3], event->data.l[4] contains drop x,y position
1363 static void EVENT_DropURLs( HWND hWnd
, XClientMessageEvent
*event
)
1365 struct x11drv_win_data
*win_data
;
1366 unsigned long data_length
;
1367 unsigned long aux_long
, drop_len
= 0;
1368 unsigned char *p_data
= NULL
; /* property data */
1369 char *p_drop
= NULL
;
1381 if (!(GetWindowLongW( hWnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
)) return;
1384 XGetWindowProperty( event
->display
, DefaultRootWindow(event
->display
),
1385 x11drv_atom(DndSelection
), 0, 65535, FALSE
,
1386 AnyPropertyType
, &u
.atom_aux
, &u
.i
,
1387 &data_length
, &aux_long
, &p_data
);
1388 wine_tsx11_unlock();
1390 WARN("property too large, truncated!\n");
1391 TRACE("urls=%s\n", p_data
);
1393 if( !aux_long
&& p_data
) { /* don't bother if > 64K */
1394 /* calculate length */
1396 next
= strchr(p
, '\n');
1399 if (strncmp(p
,"file:",5) == 0 ) {
1400 INT len
= GetShortPathNameA( p
+5, NULL
, 0 );
1401 if (len
) drop_len
+= len
+ 1;
1406 next
= strchr(p
, '\n');
1412 if( drop_len
&& drop_len
< 65535 ) {
1414 XQueryPointer( event
->display
, root_window
, &u
.w_aux
, &u
.w_aux
,
1415 &x
, &y
, &u
.i
, &u
.i
, &u
.u
);
1416 x
+= virtual_screen_rect
.left
;
1417 y
+= virtual_screen_rect
.top
;
1418 wine_tsx11_unlock();
1420 drop_len
+= sizeof(DROPFILES
) + 1;
1421 hDrop
= GlobalAlloc( GMEM_SHARE
, drop_len
);
1422 lpDrop
= GlobalLock( hDrop
);
1424 if( lpDrop
&& (win_data
= X11DRV_get_win_data( hWnd
)))
1426 lpDrop
->pFiles
= sizeof(DROPFILES
);
1430 ( x
< (win_data
->client_rect
.left
- win_data
->whole_rect
.left
) ||
1431 y
< (win_data
->client_rect
.top
- win_data
->whole_rect
.top
) ||
1432 x
> (win_data
->client_rect
.right
- win_data
->whole_rect
.left
) ||
1433 y
> (win_data
->client_rect
.bottom
- win_data
->whole_rect
.top
) );
1434 lpDrop
->fWide
= FALSE
;
1435 p_drop
= (char*)(lpDrop
+ 1);
1438 /* create message content */
1441 next
= strchr(p
, '\n');
1444 if (strncmp(p
,"file:",5) == 0 ) {
1445 INT len
= GetShortPathNameA( p
+5, p_drop
, 65535 );
1447 TRACE("drop file %s as %s\n", p
+5, p_drop
);
1450 WARN("can't convert file %s to dos name\n", p
+5);
1453 WARN("unknown mime type %s\n", p
);
1458 next
= strchr(p
, '\n');
1465 GlobalUnlock(hDrop
);
1466 PostMessageA( hWnd
, WM_DROPFILES
, (WPARAM
)hDrop
, 0L );
1470 if( p_data
) XFree(p_data
);
1471 wine_tsx11_unlock();
1476 /**********************************************************************
1477 * handle_xembed_protocol
1479 static void handle_xembed_protocol( HWND hwnd
, XClientMessageEvent
*event
)
1481 struct x11drv_win_data
*data
= X11DRV_get_win_data( hwnd
);
1485 switch (event
->data
.l
[1])
1487 case XEMBED_EMBEDDED_NOTIFY
:
1488 TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd
, event
->window
, event
->data
.l
[3] );
1489 data
->embedder
= event
->data
.l
[3];
1492 TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1493 hwnd
, event
->window
, event
->data
.l
[1], event
->data
.l
[2] );
1499 /**********************************************************************
1500 * handle_dnd_protocol
1502 static void handle_dnd_protocol( HWND hwnd
, XClientMessageEvent
*event
)
1505 int root_x
, root_y
, child_x
, child_y
;
1508 /* query window (drag&drop event contains only drag window) */
1510 XQueryPointer( event
->display
, root_window
, &root
, &child
,
1511 &root_x
, &root_y
, &child_x
, &child_y
, &u
);
1512 if (XFindContext( event
->display
, child
, winContext
, (char **)&hwnd
) != 0) hwnd
= 0;
1513 wine_tsx11_unlock();
1515 if (event
->data
.l
[0] == DndFile
|| event
->data
.l
[0] == DndFiles
)
1516 EVENT_DropFromOffiX(hwnd
, event
);
1517 else if (event
->data
.l
[0] == DndURL
)
1518 EVENT_DropURLs(hwnd
, event
);
1522 struct client_message_handler
1524 int atom
; /* protocol atom */
1525 void (*handler
)(HWND
, XClientMessageEvent
*); /* corresponding handler function */
1528 static const struct client_message_handler client_messages
[] =
1530 { XATOM_MANAGER
, handle_manager_message
},
1531 { XATOM_WM_PROTOCOLS
, handle_wm_protocols
},
1532 { XATOM__XEMBED
, handle_xembed_protocol
},
1533 { XATOM_DndProtocol
, handle_dnd_protocol
},
1534 { XATOM_XdndEnter
, X11DRV_XDND_EnterEvent
},
1535 { XATOM_XdndPosition
, X11DRV_XDND_PositionEvent
},
1536 { XATOM_XdndDrop
, X11DRV_XDND_DropEvent
},
1537 { XATOM_XdndLeave
, X11DRV_XDND_LeaveEvent
}
1541 /**********************************************************************
1542 * X11DRV_ClientMessage
1544 static void X11DRV_ClientMessage( HWND hwnd
, XEvent
*xev
)
1546 XClientMessageEvent
*event
= &xev
->xclient
;
1551 if (event
->format
!= 32)
1553 WARN( "Don't know how to handle format %d\n", event
->format
);
1557 for (i
= 0; i
< sizeof(client_messages
)/sizeof(client_messages
[0]); i
++)
1559 if (event
->message_type
== X11DRV_Atoms
[client_messages
[i
].atom
- FIRST_XATOM
])
1561 client_messages
[i
].handler( hwnd
, event
);
1565 TRACE( "no handler found for %ld\n", event
->message_type
);