Added reparenting helper classes to help apps to grab the windows
[wxWidgets.git] / src / x11 / reparent.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: reparent.cpp
3 // Purpose: wxWindow
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 2002-03-09
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "reparent.h"
22 #endif
23
24 #include "wx/x11/reparent.h"
25 #include "wx/evtloop.h"
26 #include "wx/log.h"
27 #include "wx/app.h"
28 #include "wx/timer.h"
29
30 #include "wx/x11/private.h"
31 #include "X11/Xatom.h"
32
33 /*
34
35 Adapted from code by Mike Yang, as follows.
36
37 From: Mike Yang (mikey@eukanuba.wpd.sgi.com)
38 Subject: Re: Wrapping new widget around existing windows
39 Newsgroups: comp.windows.x
40 View: Complete Thread (17 articles) | Original Format
41 Date: 1991-08-09 09:45:48 PST
42
43
44 Enough people asked, so here's my test program which reparents another
45 window. It's a single file (reparent.c), and will work with Motif or
46 Xaw. Xaw users should comment out the "#define MOTIF" line.
47
48 The reparent program first prompts for the application name of the
49 client that it will reparent. If you're going to start the
50 application override_redirect (e.g. -xrm "*overrideRedirect: true"),
51 then this name is ignored and the first override_redirect window is
52 assumed to be the one.
53
54 Input focus is supposed to be correctly handled, as is resizing with
55 window manager hints. If you have input focus problems, try launching
56 your application override_redirect instead. This method is preferred
57 anyway, since you can map it off-screen and then avoid the "window
58 flash" effect as the application's top-level window is reparented.
59
60 -----------------------------------------------------------------------
61 Mike Yang Silicon Graphics, Inc.
62 mikey@sgi.com 415/335-1786
63
64
65 ------------------------------- cut here ------------------------------
66 */
67
68 /*
69
70 Copyright 1991 by Mike Yang, mikey@sgi.com, Silicon Graphics, Inc.
71
72 Permission to use, copy, modify, distribute, and sell this software and its
73 documentation for any purpose is hereby granted without fee, provided that
74 the above copyright notice appear in all copies and that both that
75 copyright notice and this permission notice appear in supporting
76 documentation, and that the name of SGI not be used in advertising or
77 publicity pertaining to distribution of the software without specific,
78 written prior permission. SGI makes no representations about the
79 suitability of this software for any purpose. It is provided "as is"
80 without express or implied warranty.
81
82 */
83
84 /*
85 * wxAdoptedWindow
86 */
87
88 wxAdoptedWindow::wxAdoptedWindow()
89 {
90 }
91
92 wxAdoptedWindow::wxAdoptedWindow(WXWindow window)
93 {
94 m_mainWidget = window;
95 }
96
97 wxAdoptedWindow::~wxAdoptedWindow()
98 {
99 }
100 /*
101 * wxReparenter
102 */
103
104 static bool Xerror;
105 static Atom WM_STATE = 0;
106 bool wxReparenter::sm_done = FALSE;
107 wxAdoptedWindow* wxReparenter::sm_toReparent = NULL;
108 wxWindow* wxReparenter::sm_newParent = NULL;
109 wxString wxReparenter::sm_name;
110 bool wxReparenter::sm_exactMatch = FALSE;
111
112 static int
113 ErrorHandler(Display* dpy, XErrorEvent* event)
114 {
115 Xerror = True;
116 return False;
117 }
118
119 // We assume that toReparent has had its X window set
120 // appropriately.
121 bool wxReparenter::Reparent(wxWindow* newParent, wxAdoptedWindow* toReparent)
122 {
123 XWindowAttributes xwa;
124 Window *children;
125 unsigned int numchildren, each;
126 Window returnroot, returnparent;
127 XErrorHandler old;
128 int parentOffset = 0;
129
130 old = XSetErrorHandler(ErrorHandler);
131 XReparentWindow((Display*) newParent->GetXDisplay(),
132 (Window) toReparent->GetXWindow(),
133 (Window) newParent->GetXWindow(),
134 0, 0);
135
136 if (!XQueryTree((Display*) newParent->GetXDisplay(),
137 (Window) toReparent->GetXWindow(),
138 &returnroot, &returnparent,
139 &children, &numchildren) || Xerror)
140 {
141 XSetErrorHandler(old);
142 return TRUE;
143 }
144
145 if (numchildren > 0)
146 {
147 fprintf(stderr, "Reparenting %d children.\n", numchildren);
148 /* Stacking order is preserved since XQueryTree returns its children in
149 bottommost to topmost order
150 */
151 for (each=0; each<numchildren; each++)
152 {
153 XGetWindowAttributes((Display*) newParent->GetXDisplay(),
154 children[each], &xwa);
155 fprintf(stderr,
156 "Reparenting child at offset %d and position %d, %d.\n",
157 parentOffset, parentOffset+xwa.x, parentOffset+xwa.y);
158 XReparentWindow((Display*) newParent->GetXDisplay(),
159 children[each], (Window) newParent->GetXWindow(),
160 xwa.x, xwa.y);
161 }
162 }
163
164 XSetErrorHandler(old);
165 return TRUE;
166 }
167
168 // Wait for an appropriate window to be created.
169 // If exactMatch is FALSE, a substring match is OK.
170 // If windowName is empty, then wait for the next overrideRedirect window.
171 bool wxReparenter::WaitAndReparent(wxWindow* newParent, wxAdoptedWindow* toReparent,
172 const wxString& windowName,
173 bool exactMatch)
174 {
175 sm_newParent = newParent;
176 sm_toReparent = toReparent;
177 sm_exactMatch = exactMatch;
178 sm_name = windowName;
179
180 Display* display = (Display*) newParent->GetXDisplay() ;
181 XSelectInput(display,
182 RootWindowOfScreen(DefaultScreenOfDisplay(display)),
183 SubstructureNotifyMask);
184
185 if (!WM_STATE)
186 WM_STATE = XInternAtom(display, "WM_STATE", False);
187
188 #ifdef __WXDEBUG__
189 if (!windowName.IsEmpty())
190 wxLogDebug(_T("Waiting for window %s"), windowName.c_str());
191 #endif
192
193 sm_done = FALSE;
194
195 wxEventLoop eventLoop;
196 while (!sm_done)
197 {
198 if (eventLoop.Pending())
199 {
200 XEvent xevent;
201 XNextEvent(display, & xevent);
202 if (!wxTheApp->ProcessXEvent((WXEvent*) & xevent))
203 {
204 // Do the local event processing
205 ProcessXEvent((WXEvent*) & xevent);
206 }
207 }
208 else
209 {
210 #if wxUSE_TIMER
211 wxTimer::NotifyTimers();
212 wxTheApp->SendIdleEvents();
213 #endif
214 }
215 }
216 return TRUE;
217 }
218
219 bool wxReparenter::ProcessXEvent(WXEvent* event)
220 {
221 XEvent* xevent = (XEvent*) event;
222 Window client;
223
224 if (!sm_done)
225 {
226 if (xevent->type == MapNotify)
227 {
228 wxLogDebug(_T("Window was mapped"));
229 }
230
231 if (xevent->type == MapNotify && !xevent->xmap.override_redirect &&
232 (client = (Window) FindAClientWindow((WXWindow) xevent->xmap.window, sm_name)))
233 {
234 wxLogDebug(_T("Found a client window, about to reparent"));
235 wxASSERT(sm_toReparent->GetParent() == NULL);
236
237 sm_toReparent->SetHandle((WXWindow) client);
238 sm_newParent->AddChild(sm_toReparent);
239 sm_done = Reparent(sm_newParent, sm_toReparent);
240 return sm_done;
241 } else if (xevent->type == MapNotify &&
242 xevent->xmap.override_redirect &&
243 xevent->xmap.window)
244 {
245 wxLogDebug(_T("Found an override redirect window, about to reparent"));
246 sm_toReparent->SetHandle((WXWindow) xevent->xmap.window);
247 sm_newParent->AddChild(sm_toReparent);
248 wxASSERT(sm_toReparent->GetParent() == NULL);
249
250 sm_done = Reparent(sm_newParent, sm_toReparent);
251 return sm_done;
252 }
253 }
254 return FALSE;
255 }
256
257 WXWindow wxReparenter::FindAClientWindow(WXWindow window, const wxString& name)
258 {
259 int rvalue, i;
260 Atom actualtype;
261 int actualformat;
262 unsigned long nitems, bytesafter;
263 unsigned char *propreturn;
264 Window *children;
265 unsigned int numchildren;
266 Window returnroot, returnparent;
267 Window result = 0;
268 XErrorHandler old;
269 char *clientName;
270
271 Xerror = False;
272 old = XSetErrorHandler(ErrorHandler);
273 rvalue = XGetWindowProperty((Display*) wxGetDisplay(),
274 (Window) window, WM_STATE,
275 0, 1, False,
276 AnyPropertyType, &actualtype, &actualformat,
277 &nitems, &bytesafter, &propreturn);
278 XSetErrorHandler(old);
279 if (!Xerror && rvalue == Success && actualtype != None)
280 {
281 if (rvalue == Success)
282 {
283 XFree((char *) propreturn);
284 }
285 XFetchName((Display*) wxGetDisplay(), (Window) window, &clientName);
286
287 wxString str1(name);
288 wxString str2(clientName);
289 str1.Lower();
290 str2.Lower();
291
292 bool matches;
293 if (sm_exactMatch)
294 matches = (name == clientName);
295 else
296 matches = (str1.Contains(str2) || str2.Contains(str1));
297
298 XFree(clientName);
299
300 if (matches)
301 return (WXWindow) window;
302 else
303 return NULL;
304 }
305
306 old = XSetErrorHandler(ErrorHandler);
307 if (!XQueryTree((Display*) wxGetDisplay(), (Window) window, &returnroot, &returnparent,
308 &children, &numchildren) || Xerror) {
309 XSetErrorHandler(old);
310 return NULL;
311 }
312 XSetErrorHandler(old);
313
314 result = 0;
315 for (i=0; i<(int)numchildren && !result ;i++) {
316 result = (Window) FindAClientWindow((WXWindow) children[i], name);
317 }
318 if (numchildren) {
319 XFree((char *) children);
320 } return (WXWindow) result;
321 }
322