Skip to content

Commit 8e56a4c

Browse files
committed
[GTK3] Fix segfault when taking SWT screen shot on Wayland
Given the known issues when using WindowBuilder with Wayland, I think it's for the best if we try to move as much of the interaction with GDK3 to Java space. This should greatly decrease the complexity that we have to implement in C code, as well as reduce the number of Cairo methods we have to call directly, which could greatly improve maintainability and ability, given that we can instead call high-level SWT methods instead. This includes both the traversal through the GTK widgets, as well as the screen-shot creation. We currently draw the snapshot image by calling the Cairo methods directly. The Image instance is then created by reflectively calling gtk_new(). Instead of this low-level function call, we should instead use the public SWT API and let them do the heavy lifting. By taking the screen shot directly in Java, we are also able to capture the decorations of SWT widgets, rendering the current workaround of manually drawing them obsolete.
1 parent a6f9368 commit 8e56a4c

File tree

6 files changed

+233
-307
lines changed

6 files changed

+233
-307
lines changed

org.eclipse.wb.os.linux/native/gtk/common/utils.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2011 Google, Inc.
2+
* Copyright (c) 2011, 2023 Google, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -64,4 +64,15 @@ jobject wrap_pointer(JNIEnv *env, const void* ptr) {
6464
#endif
6565
(*env)->DeleteLocalRef(env, clazz);
6666
return newObject;
67+
}
68+
jobjectArray create_pointer_array(JNIEnv *env, const jsize size) {
69+
jclass clazz;
70+
#ifdef WBP_ARCH64
71+
clazz = (*env)->FindClass(env, "java/lang/Long");
72+
#else
73+
clazz = (*env)->FindClass(env, "java/lang/Integer");
74+
#endif
75+
jobjectArray newObjectArray = (*env) -> NewObjectArray(env, size, clazz, NULL);
76+
(*env)->DeleteLocalRef(env, clazz);
77+
return newObjectArray;
6778
}

org.eclipse.wb.os.linux/native/gtk/common/utils.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2011 Google, Inc.
2+
* Copyright (c) 2011, 2023 Google, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -23,4 +23,8 @@ extern "C"
2323
#endif
2424
jobject wrap_pointer(JNIEnv *env, const void* ptr);
2525

26+
#ifdef __cplusplus
27+
extern "C"
28+
#endif
29+
jobjectArray create_pointer_array(JNIEnv *env, const jsize length);
2630
#endif // __UTILS_H_

org.eclipse.wb.os.linux/native/gtk/gtk3/rcp.c

Lines changed: 86 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2011 Google, Inc.
2+
* Copyright (c) 2011, 2023 Google, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -34,90 +34,6 @@ static void getWidgetBounds(GtkWidget* widget, JNIEnv *envir, jintArray jsizes)
3434
free(sizes);
3535
}
3636

37-
////////////////////////////////////////////////////////////////////////////
38-
//
39-
// Screenshot
40-
//
41-
////////////////////////////////////////////////////////////////////////////
42-
JNIEnv *m_envir;
43-
jobject m_callback;
44-
jmethodID m_IScreenshotCallback_storeImage;
45-
46-
/* for debug purposes uncomment this and add void log(String msg) method to callback object class
47-
jmethodID m_IScreenshotCallback_log;
48-
static void logJava(char *str, void *p) {
49-
char buf[1024];
50-
memset(buf, 0, sizeof(buf));
51-
sprintf(buf, "%s = %p", str, p);
52-
jobject jstr = (*m_envir)->NewStringUTF(m_envir, buf);
53-
(*m_envir)->CallVoidMethod(m_envir, m_callback, m_IScreenshotCallback_log, jstr);
54-
(*m_envir)->DeleteLocalRef(m_envir, jstr);
55-
} */
56-
57-
static cairo_surface_t* copyImageSurface(GdkWindow *sourceWindow, gint width, gint height) {
58-
cairo_surface_t *targetSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
59-
cairo_t *cr = cairo_create(targetSurface);
60-
gdk_cairo_set_source_window(cr, sourceWindow, 0, 0);
61-
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
62-
cairo_paint(cr);
63-
cairo_destroy(cr);
64-
cairo_surface_flush(targetSurface);
65-
return targetSurface;
66-
}
67-
68-
static cairo_surface_t* getImageSurface(GdkWindow *window) {
69-
if (!gdk_window_is_visible(window)) {
70-
// don't deal with unmapped windows
71-
return NULL;
72-
}
73-
gint width, height;
74-
gdk_window_get_geometry(window, NULL, NULL, &width, &height);
75-
// force paint. Note, not all widgets do this completely, known so far is GtkTreeViewer.
76-
GdkRectangle rect;
77-
rect.x = 0; rect.y = 0; rect.width = width; rect.height = height;
78-
gdk_window_begin_paint_rect(window, &rect);
79-
gdk_window_invalidate_rect(window, &rect, TRUE);
80-
// access a widget registered with the window
81-
gpointer widget = NULL;
82-
gdk_window_get_user_data(window, &widget);
83-
// end force paint and copy image
84-
gdk_window_process_updates(window, TRUE);
85-
gdk_window_end_paint(window);
86-
cairo_surface_t *surface = copyImageSurface(window, width, height);
87-
// get Java code notified
88-
if (m_callback) {
89-
(*m_envir)->CallVoidMethod(m_envir, m_callback, m_IScreenshotCallback_storeImage, wrap_pointer(m_envir, widget), wrap_pointer(m_envir, surface));
90-
}
91-
// done
92-
return surface;
93-
}
94-
95-
static cairo_surface_t* traverse(GdkWindow *window) {
96-
cairo_surface_t *surface = getImageSurface(window);
97-
if (surface == NULL) {
98-
return NULL;
99-
}
100-
GList *children = gdk_window_get_children(window);
101-
guint length = g_list_length(children);
102-
guint i;
103-
for (i = 0; i < length; i++) {
104-
GdkWindow *win = g_list_nth_data(children, i);
105-
cairo_surface_t* sur = traverse(win);
106-
if (sur == NULL) {
107-
continue;
108-
}
109-
if (!m_callback) {
110-
cairo_surface_destroy(sur);
111-
}
112-
}
113-
return surface;
114-
}
115-
116-
static cairo_surface_t *makeShot(GtkWidget* shellWidget) {
117-
GdkWindow *window = gtk_widget_get_window(shellWidget);
118-
return traverse(window);
119-
}
120-
12137
////////////////////////////////////////////////////////////////////////////
12238
//
12339
// Menu
@@ -147,8 +63,84 @@ typedef struct _GtkMenuPrivateCopy
14763
GtkWidget *toplevel;
14864
} GtkMenuPrivateCopy;
14965

150-
static cairo_surface_t* fetchMenuVisualData(GtkMenu *menu, JNIEnv *envir, jintArray jsizes) {
151-
m_callback = NULL;
66+
////////////////////////////////////////////////////////////////////////////
67+
//
68+
// JNI
69+
//
70+
////////////////////////////////////////////////////////////////////////////
71+
JNIEXPORT jboolean JNICALL
72+
OS_NATIVE(_1toggle_1above)
73+
(JNIEnv *envir, jobject that, JHANDLE widgetHandle, jboolean forceToggle) {
74+
// NOT IMPLEMENTED
75+
return JNI_TRUE;
76+
}
77+
JNIEXPORT jboolean JNICALL
78+
OS_NATIVE(_1begin_1shot)
79+
(JNIEnv *envir, jobject that, JHANDLE widgetHandle) {
80+
// just show it
81+
gtk_widget_show_now((GtkWidget*)unwrap_pointer(envir, widgetHandle));
82+
return JNI_TRUE;
83+
}
84+
JNIEXPORT jboolean JNICALL
85+
OS_NATIVE(_1end_1shot)
86+
(JNIEnv *envir, jobject that, JHANDLE widgetHandle) {
87+
// hide then
88+
gtk_widget_hide((GtkWidget*)unwrap_pointer(envir, widgetHandle));
89+
return JNI_TRUE;
90+
}
91+
92+
JNIEXPORT JHANDLE JNICALL OS_NATIVE(_1getWidgetWindow)
93+
(JNIEnv *envir, jobject that, JHANDLE shellHandle) {
94+
GdkWindow *window = gtk_widget_get_window((GtkWidget*)unwrap_pointer(envir, shellHandle));
95+
return wrap_pointer(envir, window);
96+
}
97+
JNIEXPORT JHANDLE JNICALL OS_NATIVE(_1getWindowUserData)
98+
(JNIEnv *envir, jobject that, JHANDLE windowHandle) {
99+
gpointer widget = NULL;
100+
gdk_window_get_user_data((GdkWindow*)unwrap_pointer(envir, windowHandle), &widget);
101+
return wrap_pointer(envir, widget);
102+
}
103+
JNIEXPORT jobjectArray JNICALL OS_NATIVE(_1getWindowChildren)
104+
(JNIEnv *envir, jobject that, JHANDLE windowHandle) {
105+
GList *children = gdk_window_get_children((GdkWindow*)unwrap_pointer(envir, windowHandle));
106+
guint length = g_list_length(children);
107+
guint i;
108+
109+
jobjectArray pointerArray = create_pointer_array(envir, length);
110+
111+
for (i = 0; i < length; i++) {
112+
GdkWindow *child = g_list_nth_data(children, i);
113+
(*envir) -> SetObjectArrayElement(envir, pointerArray, i, wrap_pointer(envir, child));
114+
}
115+
116+
g_list_free(children);
117+
118+
return pointerArray;
119+
}
120+
JNIEXPORT jboolean JNICALL OS_NATIVE(_1requestWindowRepaint)
121+
(JNIEnv *envir, jobject that, JHANDLE windowHandle) {
122+
GdkWindow* window = (GdkWindow*)unwrap_pointer(envir, windowHandle);
123+
if (!gdk_window_is_visible(window)) {
124+
// don't deal with unmapped windows
125+
return JNI_FALSE;
126+
}
127+
gint width, height;
128+
gdk_window_get_geometry(window, NULL, NULL, &width, &height);
129+
// force paint. Note, not all widgets do this completely, known so far is GtkTreeViewer.
130+
GdkRectangle rect;
131+
rect.x = 0; rect.y = 0; rect.width = width; rect.height = height;
132+
gdk_window_begin_paint_rect(window, &rect);
133+
gdk_window_invalidate_rect(window, &rect, TRUE);
134+
// end force paint
135+
gdk_window_end_paint(window);
136+
// done
137+
return JNI_TRUE;
138+
}
139+
// menu
140+
JNIEXPORT jboolean JNICALL
141+
OS_NATIVE(_1begin_1menu_1shot)
142+
(JNIEnv *envir, jobject that, JHANDLE menuHandle, jintArray jsizes) {
143+
GtkMenu *menu = (GtkMenu*)unwrap_pointer(envir, menuHandle);
152144
GtkWidget *menuWidget = GTK_WIDGET (menu);
153145
GtkMenuPrivateCopy *priv = (GtkMenuPrivateCopy*)menu->priv;
154146
// try to move menu window outside of the screen
@@ -180,79 +172,24 @@ static cairo_surface_t* fetchMenuVisualData(GtkMenu *menu, JNIEnv *envir, jintAr
180172
// copy dimensions into java array
181173
(*envir)->SetIntArrayRegion(envir, jsizes, 0, sizesSize, sizes);
182174
free(sizes);
183-
// make screenshot
184-
cairo_surface_t *surface = NULL;
185-
GdkWindow *window = gtk_widget_get_window(menuWidget);
186-
surface = traverse(window);
187-
// hide menu
188-
gtk_widget_hide(priv->toplevel);
189-
gtk_widget_hide(GTK_WIDGET (menu));
190-
// all done
191-
return surface;
192-
}
193-
194-
////////////////////////////////////////////////////////////////////////////
195-
//
196-
// JNI
197-
//
198-
////////////////////////////////////////////////////////////////////////////
199-
JNIEXPORT jboolean JNICALL
200-
OS_NATIVE(_1toggle_1above)
201-
(JNIEnv *envir, jobject that, JHANDLE widgetHandle, jboolean forceToggle) {
202-
// NOT IMPLEMENTED
203175
return JNI_TRUE;
204176
}
205177
JNIEXPORT jboolean JNICALL
206-
OS_NATIVE(_1begin_1shot)
207-
(JNIEnv *envir, jobject that, JHANDLE widgetHandle) {
208-
// just show it
209-
gtk_widget_show_now((GtkWidget*)unwrap_pointer(envir, widgetHandle));
210-
return JNI_TRUE;
211-
}
212-
JNIEXPORT jboolean JNICALL
213-
OS_NATIVE(_1end_1shot)
214-
(JNIEnv *envir, jobject that, JHANDLE widgetHandle) {
178+
OS_NATIVE(_1end_1menu_1shot)
179+
(JNIEnv *envir, jobject that, JHANDLE menuHandle) {
215180
// hide then
216-
gtk_widget_hide((GtkWidget*)unwrap_pointer(envir, widgetHandle));
181+
GtkMenu *menu = (GtkMenu*)unwrap_pointer(envir, menuHandle);
182+
GtkWidget *menuWidget = GTK_WIDGET (menu);
183+
GtkMenuPrivateCopy *priv = (GtkMenuPrivateCopy*)menu->priv;
184+
gtk_widget_hide(priv->toplevel);
185+
gtk_widget_hide(menuWidget);
217186
return JNI_TRUE;
218187
}
219-
// shot
220-
JNIEXPORT JHANDLE JNICALL OS_NATIVE(_1makeShot)(
221-
JNIEnv *envir, jobject that, JHANDLE widgetHandle, jobject callback) {
222-
m_envir = envir;
223-
if (callback != NULL) {
224-
m_callback = (*envir)->NewGlobalRef(envir, callback);
225-
jclass clazz = (*envir)->GetObjectClass(envir, m_callback);
226-
m_IScreenshotCallback_storeImage = (*envir)->GetMethodID(envir, clazz, "storeImage", CALLBACK_SIG);
227-
/* uncomment this for debug purposes
228-
m_IScreenshotCallback_log = (*envir)->GetMethodID(envir, clazz, "log", "(Ljava/lang/String;)V"); */
229-
}
230-
// make shot
231-
cairo_surface_t* surface = makeShot((GtkWidget*)unwrap_pointer(envir, widgetHandle));
232-
// clean up
233-
if (callback != NULL) {
234-
(*envir)->DeleteGlobalRef(envir, m_callback);
235-
}
236-
m_callback = NULL;
237-
return (JHANDLE)wrap_pointer(envir, surface);
238-
}
239-
// menu
240-
JNIEXPORT JHANDLE JNICALL OS_NATIVE(_1fetchMenuVisualData)(
241-
JNIEnv *envir, jobject that, JHANDLE jmenuHandle, jintArray jsizes) {
242-
GtkWidget* menu = (GtkWidget*)unwrap_pointer(envir, jmenuHandle);
243-
cairo_surface_t* surface = fetchMenuVisualData(GTK_MENU(menu), envir, jsizes);
244-
return wrap_pointer(envir, surface);
245-
}
246188
// tab item bounds
247189
JNIEXPORT void JNICALL OS_NATIVE(_1getWidgetBounds)(
248190
JNIEnv *envir, jobject that, JHANDLE jhandle, jintArray jsizes) {
249191
getWidgetBounds((GtkWidget*)unwrap_pointer(envir, jhandle), envir, jsizes);
250192
}
251-
// unref
252-
JNIEXPORT void JNICALL OS_NATIVE(_1disposeImageHandle)(
253-
JNIEnv *envir, jobject that, JHANDLE jhandle) {
254-
cairo_surface_destroy((cairo_surface_t*)unwrap_pointer(envir, jhandle));
255-
}
256193
// other
257194
static int isValidVersion() {
258195
return gtk_major_version == 3 && gtk_minor_version >= 0;
Binary file not shown.

org.eclipse.wb.os.linux/src/org/eclipse/wb/internal/os/linux/IScreenshotCallback.java

Lines changed: 0 additions & 30 deletions
This file was deleted.

0 commit comments

Comments
 (0)