1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:expandtab:shiftwidth=2:tabstop=2:
3 : */
4 : /* ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is mozilla.org code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Sun Microsystems, Inc.
21 : * Portions created by the Initial Developer are Copyright (C) 2003
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Robin Lu <robin.lu@sun.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /**
42 : * This file is the Gtk2 implementation of plugin native window.
43 : */
44 :
45 : #include "nsDebug.h"
46 : #include "nsPluginNativeWindow.h"
47 : #include "npapi.h"
48 : #include <gtk/gtk.h>
49 : #include <gdk/gdkx.h>
50 : #include <gdk/gdk.h>
51 :
52 : #include "gtk2xtbin.h"
53 :
54 : class nsPluginNativeWindowGtk2 : public nsPluginNativeWindow {
55 : public:
56 : nsPluginNativeWindowGtk2();
57 : virtual ~nsPluginNativeWindowGtk2();
58 :
59 : virtual nsresult CallSetWindow(nsRefPtr<nsNPAPIPluginInstance> &aPluginInstance);
60 : private:
61 : NPSetWindowCallbackStruct mWsInfo;
62 : /**
63 : * Either a GtkSocket or a special GtkXtBin widget (derived from GtkSocket)
64 : * that encapsulates the Xt toolkit within a Gtk Application.
65 : */
66 : GtkWidget* mSocketWidget;
67 : nsresult CreateXEmbedWindow();
68 : nsresult CreateXtWindow();
69 : void SetAllocation();
70 : };
71 :
72 : static gboolean plug_removed_cb (GtkWidget *widget, gpointer data);
73 : static void socket_unrealize_cb (GtkWidget *widget, gpointer data);
74 :
75 0 : nsPluginNativeWindowGtk2::nsPluginNativeWindowGtk2() : nsPluginNativeWindow()
76 : {
77 : // initialize the struct fields
78 0 : window = nsnull;
79 0 : x = 0;
80 0 : y = 0;
81 0 : width = 0;
82 0 : height = 0;
83 0 : memset(&clipRect, 0, sizeof(clipRect));
84 0 : ws_info = &mWsInfo;
85 0 : type = NPWindowTypeWindow;
86 0 : mSocketWidget = 0;
87 0 : mWsInfo.type = 0;
88 0 : mWsInfo.display = nsnull;
89 0 : mWsInfo.visual = nsnull;
90 0 : mWsInfo.colormap = 0;
91 0 : mWsInfo.depth = 0;
92 0 : }
93 :
94 0 : nsPluginNativeWindowGtk2::~nsPluginNativeWindowGtk2()
95 : {
96 0 : if(mSocketWidget) {
97 0 : gtk_widget_destroy(mSocketWidget);
98 : }
99 0 : }
100 :
101 0 : nsresult PLUG_NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
102 : {
103 0 : NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
104 0 : *aPluginNativeWindow = new nsPluginNativeWindowGtk2();
105 0 : return *aPluginNativeWindow ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
106 : }
107 :
108 0 : nsresult PLUG_DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow)
109 : {
110 0 : NS_ENSURE_ARG_POINTER(aPluginNativeWindow);
111 0 : nsPluginNativeWindowGtk2 *p = (nsPluginNativeWindowGtk2 *)aPluginNativeWindow;
112 0 : delete p;
113 0 : return NS_OK;
114 : }
115 :
116 0 : nsresult nsPluginNativeWindowGtk2::CallSetWindow(nsRefPtr<nsNPAPIPluginInstance> &aPluginInstance)
117 : {
118 0 : if (aPluginInstance) {
119 0 : if (type == NPWindowTypeWindow) {
120 0 : if (!mSocketWidget) {
121 : nsresult rv;
122 :
123 : // The documentation on the types for many variables in NP(N|P)_GetValue
124 : // is vague. Often boolean values are NPBool (1 byte), but
125 : // https://developer.mozilla.org/en/XEmbed_Extension_for_Mozilla_Plugins
126 : // treats NPPVpluginNeedsXEmbed as PRBool (int), and
127 : // on x86/32-bit, flash stores to this using |movl 0x1,&needsXEmbed|.
128 : // thus we can't use NPBool for needsXEmbed, or the three bytes above
129 : // it on the stack would get clobbered. so protect with the larger bool.
130 0 : int needsXEmbed = 0;
131 0 : rv = aPluginInstance->GetValueFromPlugin(NPPVpluginNeedsXEmbed, &needsXEmbed);
132 : // If the call returned an error code make sure we still use our default value.
133 0 : if (NS_FAILED(rv)) {
134 0 : needsXEmbed = 0;
135 : }
136 : #ifdef DEBUG
137 0 : printf("nsPluginNativeWindowGtk2: NPPVpluginNeedsXEmbed=%d\n", needsXEmbed);
138 : #endif
139 :
140 0 : if (needsXEmbed) {
141 0 : rv = CreateXEmbedWindow();
142 : }
143 : else {
144 0 : rv = CreateXtWindow();
145 : }
146 :
147 0 : if (NS_FAILED(rv)) {
148 0 : return NS_ERROR_FAILURE;
149 : }
150 : }
151 :
152 0 : if (!mSocketWidget) {
153 0 : return NS_ERROR_FAILURE;
154 : }
155 :
156 : // Make sure to resize and re-place the window if required.
157 : // Need to reset "window" each time as nsObjectFrame::DidReflow sets it
158 : // to the ancestor window.
159 0 : if (GTK_IS_XTBIN(mSocketWidget)) {
160 0 : gtk_xtbin_resize(mSocketWidget, width, height);
161 : // Point the NPWindow structures window to the actual X window
162 0 : window = (void*)GTK_XTBIN(mSocketWidget)->xtwindow;
163 : }
164 : else { // XEmbed
165 0 : SetAllocation();
166 0 : window = (void*)gtk_socket_get_id(GTK_SOCKET(mSocketWidget));
167 : }
168 : #ifdef DEBUG
169 0 : printf("nsPluginNativeWindowGtk2: call SetWindow with xid=%p\n", (void *)window);
170 : #endif
171 : } // NPWindowTypeWindow
172 0 : aPluginInstance->SetWindow(this);
173 : }
174 0 : else if (mPluginInstance)
175 0 : mPluginInstance->SetWindow(nsnull);
176 :
177 0 : SetPluginInstance(aPluginInstance);
178 0 : return NS_OK;
179 : }
180 :
181 0 : nsresult nsPluginNativeWindowGtk2::CreateXEmbedWindow() {
182 0 : NS_ASSERTION(!mSocketWidget,"Already created a socket widget!");
183 :
184 0 : GdkWindow *parent_win = gdk_window_lookup((XID)window);
185 0 : mSocketWidget = gtk_socket_new();
186 :
187 : //attach the socket to the container widget
188 0 : gtk_widget_set_parent_window(mSocketWidget, parent_win);
189 :
190 : // Make sure to handle the plug_removed signal. If we don't the
191 : // socket will automatically be destroyed when the plug is
192 : // removed, which means we're destroying it more than once.
193 : // SYNTAX ERROR.
194 0 : g_signal_connect(mSocketWidget, "plug_removed",
195 0 : G_CALLBACK(plug_removed_cb), NULL);
196 :
197 0 : g_signal_connect(mSocketWidget, "unrealize",
198 0 : G_CALLBACK(socket_unrealize_cb), NULL);
199 :
200 0 : g_signal_connect(mSocketWidget, "destroy",
201 0 : G_CALLBACK(gtk_widget_destroyed), &mSocketWidget);
202 :
203 0 : gpointer user_data = NULL;
204 0 : gdk_window_get_user_data(parent_win, &user_data);
205 :
206 0 : GtkContainer *container = GTK_CONTAINER(user_data);
207 0 : gtk_container_add(container, mSocketWidget);
208 0 : gtk_widget_realize(mSocketWidget);
209 :
210 : // The GtkSocket has a visible window, but the plugin's XEmbed plug will
211 : // cover this window. Normally GtkSockets let the X server paint their
212 : // background and this would happen immediately (before the plug is
213 : // created). Setting the background to None prevents the server from
214 : // painting this window, avoiding flicker.
215 0 : gdk_window_set_back_pixmap(mSocketWidget->window, NULL, FALSE);
216 :
217 : // Resize before we show
218 0 : SetAllocation();
219 :
220 0 : gtk_widget_show(mSocketWidget);
221 :
222 0 : gdk_flush();
223 0 : window = (void*)gtk_socket_get_id(GTK_SOCKET(mSocketWidget));
224 :
225 : // Fill out the ws_info structure.
226 : // (The windowless case is done in nsObjectFrame.cpp.)
227 0 : GdkWindow *gdkWindow = gdk_window_lookup((XID)window);
228 0 : if(!gdkWindow)
229 0 : return NS_ERROR_FAILURE;
230 :
231 0 : mWsInfo.display = GDK_WINDOW_XDISPLAY(gdkWindow);
232 0 : mWsInfo.colormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(gdkWindow));
233 0 : GdkVisual* gdkVisual = gdk_drawable_get_visual(gdkWindow);
234 0 : mWsInfo.visual = GDK_VISUAL_XVISUAL(gdkVisual);
235 0 : mWsInfo.depth = gdkVisual->depth;
236 :
237 0 : return NS_OK;
238 : }
239 :
240 0 : void nsPluginNativeWindowGtk2::SetAllocation() {
241 0 : if (!mSocketWidget)
242 0 : return;
243 :
244 : GtkAllocation new_allocation;
245 0 : new_allocation.x = 0;
246 0 : new_allocation.y = 0;
247 0 : new_allocation.width = width;
248 0 : new_allocation.height = height;
249 0 : gtk_widget_size_allocate(mSocketWidget, &new_allocation);
250 : }
251 :
252 0 : nsresult nsPluginNativeWindowGtk2::CreateXtWindow() {
253 0 : NS_ASSERTION(!mSocketWidget,"Already created a socket widget!");
254 :
255 : #ifdef NS_DEBUG
256 : printf("About to create new xtbin of %i X %i from %p...\n",
257 0 : width, height, (void*)window);
258 : #endif
259 0 : GdkWindow *gdkWindow = gdk_window_lookup((XID)window);
260 0 : mSocketWidget = gtk_xtbin_new(gdkWindow, 0);
261 : // Check to see if creating the xtbin failed for some reason.
262 : // if it did, we can't go any further.
263 0 : if (!mSocketWidget)
264 0 : return NS_ERROR_FAILURE;
265 :
266 0 : g_signal_connect(mSocketWidget, "destroy",
267 0 : G_CALLBACK(gtk_widget_destroyed), &mSocketWidget);
268 :
269 0 : gtk_widget_set_size_request(mSocketWidget, width, height);
270 :
271 : #ifdef NS_DEBUG
272 0 : printf("About to show xtbin(%p)...\n", (void*)mSocketWidget); fflush(NULL);
273 : #endif
274 0 : gtk_widget_show(mSocketWidget);
275 : #ifdef NS_DEBUG
276 0 : printf("completed gtk_widget_show(%p)\n", (void*)mSocketWidget); fflush(NULL);
277 : #endif
278 :
279 : // Fill out the ws_info structure.
280 0 : GtkXtBin* xtbin = GTK_XTBIN(mSocketWidget);
281 : // The xtbin has its own Display structure.
282 0 : mWsInfo.display = xtbin->xtdisplay;
283 0 : mWsInfo.colormap = xtbin->xtclient.xtcolormap;
284 0 : mWsInfo.visual = xtbin->xtclient.xtvisual;
285 0 : mWsInfo.depth = xtbin->xtclient.xtdepth;
286 : // Leave mWsInfo.type = 0 - Who knows what this is meant to be?
287 :
288 0 : XFlush(mWsInfo.display);
289 :
290 0 : return NS_OK;
291 : }
292 :
293 : /* static */
294 : gboolean
295 0 : plug_removed_cb (GtkWidget *widget, gpointer data)
296 : {
297 : // Gee, thanks for the info!
298 0 : return TRUE;
299 : }
300 :
301 : static void
302 0 : socket_unrealize_cb(GtkWidget *widget, gpointer data)
303 : {
304 : // Unmap and reparent any child windows that GDK does not yet know about.
305 : // (See bug 540114 comment 10.)
306 0 : GdkWindow* socket_window = widget->window;
307 0 : Display* display = GDK_DISPLAY();
308 :
309 : // Ignore X errors that may happen if windows get destroyed (possibly
310 : // requested by the plugin) between XQueryTree and when we operate on them.
311 0 : gdk_error_trap_push();
312 :
313 : Window root, parent;
314 : Window* children;
315 : unsigned int nchildren;
316 0 : if (!XQueryTree(display, gdk_x11_drawable_get_xid(socket_window),
317 0 : &root, &parent, &children, &nchildren))
318 0 : return;
319 :
320 0 : for (unsigned int i = 0; i < nchildren; ++i) {
321 0 : Window child = children[i];
322 0 : if (!gdk_window_lookup(child)) {
323 : // This window is not known to GDK.
324 0 : XUnmapWindow(display, child);
325 0 : XReparentWindow(display, child, DefaultRootWindow(display), 0, 0);
326 : }
327 : }
328 :
329 0 : if (children) XFree(children);
330 :
331 0 : XSync(display, False);
332 0 : gdk_error_trap_pop();
333 : }
|