| |
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 } |