From 030c499039a0db96d99b75b31e0ada6f87695c14 Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Thu, 15 Aug 2013 18:45:59 +0200 Subject: winex11: Add minimal XEmbed client support --- dlls/winex11.drv/event.c | 118 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 5e5b19e..e380e06 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -182,6 +182,33 @@ static inline void free_event_data( XEvent *event ) #endif } + +/*********************************************************************** + * sendXembedMessage + * + * Sends an XEMBED event according to the specification. + * See: http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html + */ +void sendXembedMessage(Display* display, Window win, long message, long detail, long data1, long data2){ + XEvent ev; + memset(&ev, 0, sizeof(ev)); + + ev.xclient.type = ClientMessage; + ev.xclient.window = win; + ev.xclient.message_type = XInternAtom(display, "_XEMBED", False); + ev.xclient.format = 32; + + ev.xclient.data.l[0] = CurrentTime; + ev.xclient.data.l[1] = message; + ev.xclient.data.l[2] = detail; + ev.xclient.data.l[3] = data1; + ev.xclient.data.l[4] = data2; + + /* TODO: Normally it would be useful to trap possible errors, but the rest of wine also doesnt do this */ + XSendEvent(display, win, False, NoEventMask, &ev); + XSync(display, False); +} + /*********************************************************************** * X11DRV_register_event_handler * @@ -562,6 +589,7 @@ static void set_focus( Display *display, HWND hwnd, Time time ) HWND focus; Window win; GUITHREADINFO threadinfo; + struct x11drv_win_data *data; TRACE( "setting foreground window to %p\n", hwnd ); SetForegroundWindow( hwnd ); @@ -576,7 +604,21 @@ static void set_focus( Display *display, HWND hwnd, Time time ) if (win) { TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time ); - XSetInputFocus( display, win, RevertToParent, time ); + + /* Note: When a window has an embedder it is not allowed to just take the XInputFocus, but instead + we have to request the embedder to redirect the messages for us. + + TODO: What about the case when embedded = True, but no embedder is set? */ + + data = get_win_data( hwnd ); + + if(!data || !data->embedder){ + XSetInputFocus( display, win, RevertToParent, time ); + }else{ + sendXembedMessage( display, data->embedder, XEMBED_REQUEST_FOCUS, 0, 0, 0); + } + + if(data) release_win_data(data); } } @@ -1337,7 +1379,19 @@ void CDECL X11DRV_SetFocus( HWND hwnd ) if (!(hwnd = GetAncestor( hwnd, GA_ROOT ))) return; if (!(data = get_win_data( hwnd ))) return; - if (!data->managed) set_input_focus( data->display, data->whole_window ); + + /* Note: When a window has an embedder it is not allowed to just take the XInputFocus, but instead + we have to request the embedder to redirect the messages for us. + + TODO: What about the case when embedded = True, but no embedder is set? */ + + if(data->embedder){ + sendXembedMessage( data->display, data->embedder, XEMBED_REQUEST_FOCUS, 0, 0, 0); + + }else if (!data->managed){ + set_input_focus( data->display, data->whole_window ); + } + release_win_data( data ); } @@ -1598,16 +1652,76 @@ static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event ) */ static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event ) { + XFocusChangeEvent focusEvent; struct x11drv_win_data *data = get_win_data( hwnd ); if (!data) return; switch (event->data.l[1]) { + case XEMBED_EMBEDDED_NOTIFY: + /* embedder -> client: reparenting the window finished */ TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] ); + data->embedder = event->data.l[3]; + make_window_embedded(data); + break; + + case XEMBED_FOCUS_OUT: + /* embedder -> client: client looses focus */ + case XEMBED_WINDOW_DEACTIVATE: + /* embedder -> client: client looses keyboard focus */ + TRACE( "win %p/%lx XEMBED_FOCUS_OUT or XEMBED_WINDOW_DEACTIVATE message\n", hwnd, event->window); + + /* we just simulate a FocusOut event */ + focusEvent.type = FocusOut; + focusEvent.serial = event->serial; + focusEvent.send_event = event->send_event; + focusEvent.display = event->display; + focusEvent.window = event->window; + focusEvent.mode = NotifyNormal; + focusEvent.detail = NotifyNonlinear; + X11DRV_FocusOut(hwnd, &focusEvent); + break; + + case XEMBED_MODALITY_ON: + /* embedder -> client: enable ignore mouse input */ + TRACE( "win %p/%lx XEMBED_MODALITY_ON message\n", hwnd, event->window); + + EnableWindow(hwnd, FALSE); // is this really necessary? + break; + + case XEMBED_MODALITY_OFF: + /* embedder -> client: disable ignore mouse input */ + TRACE( "win %p/%lx XEMBED_MODALITY_OFF message\n", hwnd, event->window); + + EnableWindow(hwnd, TRUE); // is this really necessary? break; + + case XEMBED_WINDOW_ACTIVATE: + /* embedder -> client: client gets keyboard focus */ + + case XEMBED_FOCUS_IN: + /* embedder -> client: client gets focus + + TODO: This isn't handled completely correct - as the focus can also be rejected, it might be possible that + wine incorrectly assumes that it has the focus, but this shouldn't cause any significant problems */ + + case XEMBED_REQUEST_FOCUS: + /* client -> embedder: client requests focus (as we are the client this should never occur) */ + + case XEMBED_FOCUS_NEXT: + case XEMBED_FOCUS_PREV: + /* client -> embedder: end of tab chain (as we are the client this should never occur) + + TODO: Where can we detect that we are at the end of the tab chain and send this event? */ + + case XEMBED_REGISTER_ACCELERATOR: + case XEMBED_UNREGISTER_ACCELERATOR: + case XEMBED_ACTIVATE_ACCELERATOR: + /* not implemented yet */ + default: TRACE( "win %p/%lx XEMBED message %lu(%lu)\n", hwnd, event->window, event->data.l[1], event->data.l[2] ); -- 1.7.9.5