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