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