Skip to content

Commit

Permalink
JBR-6212 Wayland: app does not terminate upon Wayland protocol error
Browse files Browse the repository at this point in the history
  • Loading branch information
mkartashev authored and jbrbot committed Nov 8, 2024
1 parent 0aecd42 commit 5ad4f8a
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 24 deletions.
17 changes: 15 additions & 2 deletions src/java.desktop/unix/classes/sun/awt/wl/WLToolkit.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
Expand Down Expand Up @@ -228,7 +228,7 @@ public void run() {
int result = readEvents();
if (result == READ_RESULT_ERROR) {
log.severe("Wayland protocol I/O error");
// TODO: display disconnect handling here?
shutDownAfterServerError();
break;
} else if (result == READ_RESULT_FINISHED_WITH_EVENTS) {
AWTAutoShutdown.notifyToolkitThreadBusy(); // busy processing events
Expand All @@ -250,6 +250,19 @@ public void run() {
}
}
}

private static void shutDownAfterServerError() {
EventQueue.invokeLater(() -> {
var frames = Arrays.asList(Frame.getFrames());
Collections.reverse(frames);
frames.forEach(frame -> frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)));

// They've had their chance to exit from the "window closing" handler code. If we have gotten here,
// that chance wasn't taken. But we cannot continue because the connection with the server is
// no longer available, so let's exit forcibly.
System.exit(0);
});
}

/**
* If more than this amount milliseconds has passed since the same mouse button click,
Expand Down
17 changes: 16 additions & 1 deletion src/java.desktop/unix/native/libawt_wlawt/WLClipboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -461,15 +461,30 @@ Java_sun_awt_wl_WLClipboard_initNative(
if (payload == NULL) {
(*env)->DeleteGlobalRef(env, clipboardGlobalRef);
}
CHECK_NULL_THROW_OOME_RETURN(env, payload, "failed to allocate memory for DataOfferPayload", 0);
CHECK_NULL_THROW_OOME_RETURN(env, payload, "Failed to allocate memory for DataOfferPayload", 0);

if (!isPrimary) {
// TODO: may be needed by DnD also, initialize in a common place
wl_data_device = wl_data_device_manager_get_data_device(wl_ddm, wl_seat);
if (wl_data_device == NULL) {
(*env)->DeleteGlobalRef(env, clipboardGlobalRef);
JNU_ThrowByName(env,
"java/awt/AWTError",
"wl_data_device_manager_get_data_device() failed");
return 0;
}

wl_data_device_add_listener(wl_data_device, &wl_data_device_listener, payload);
} else {
if (zwp_selection_dm != NULL) {
zwp_selection_device = zwp_primary_selection_device_manager_v1_get_device(zwp_selection_dm, wl_seat);
if (zwp_selection_device == NULL) {
(*env)->DeleteGlobalRef(env, clipboardGlobalRef);
JNU_ThrowByName(env,
"java/awt/AWTError",
"zwp_primary_selection_device_manager_v1_get_device() failed");
return 0;
}
zwp_primary_selection_device_v1_add_listener(zwp_selection_device, &zwp_selection_device_listener, payload);
} else {
(*env)->DeleteGlobalRef(env, clipboardGlobalRef);
Expand Down
23 changes: 18 additions & 5 deletions src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ struct activation_token_list_item {
struct activation_token_list_item *next_item;
};

static struct activation_token_list_item *add_token(struct activation_token_list_item *list,
struct xdg_activation_token_v1 *token_to_add) {
static struct activation_token_list_item *add_token(JNIEnv* env,
struct activation_token_list_item *list,
struct xdg_activation_token_v1* token_to_add) {
struct activation_token_list_item *new_item =
(struct activation_token_list_item *) calloc(1, sizeof(struct activation_token_list_item));
CHECK_NULL_THROW_OOME_RETURN(env, new_item, "Failed to allocate a Wayland activation token", NULL);
new_item->token = token_to_add;
new_item->next_item = list;
return new_item;
Expand Down Expand Up @@ -119,7 +121,7 @@ xdg_surface_configure(void *data,

if (wlFrame->configuredPending) {
wlFrame->configuredPending = JNI_FALSE;

JNIEnv *env = getEnv();
const jobject nativeFramePeer = (*env)->NewLocalRef(env, wlFrame->nativeFramePeer);
if (nativeFramePeer) {
Expand Down Expand Up @@ -430,15 +432,19 @@ Java_sun_awt_wl_WLComponentPeer_nativeCreateWLSurface
struct WLFrame *parentFrame = jlong_to_ptr(parentPtr);
if (frame->wl_surface) return;
frame->wl_surface = wl_compositor_create_surface(wl_compositor);
CHECK_NULL(frame->wl_surface);
frame->xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, frame->wl_surface);
CHECK_NULL(frame->xdg_surface);
if (gtk_shell1 != NULL) {
frame->gtk_surface = gtk_shell1_get_gtk_surface(gtk_shell1, frame->wl_surface);
CHECK_NULL(frame->gtk_surface);
}

wl_surface_add_listener(frame->wl_surface, &wl_surface_listener, frame);
xdg_surface_add_listener(frame->xdg_surface, &xdg_surface_listener, frame);
frame->toplevel = JNI_TRUE;
frame->xdg_toplevel = xdg_surface_get_toplevel(frame->xdg_surface);
CHECK_NULL(frame->xdg_toplevel);
xdg_toplevel_add_listener(frame->xdg_toplevel, &xdg_toplevel_listener, frame);
if (title) {
FrameSetTitle(env, frame, title);
Expand Down Expand Up @@ -472,6 +478,7 @@ newPositioner
jint width, jint height, jint offsetX, jint offsetY)
{
struct xdg_positioner *xdg_positioner = xdg_wm_base_create_positioner(xdg_wm_base);
CHECK_NULL_RETURN(xdg_positioner, NULL);

// "For an xdg_positioner object to be considered complete, it must have
// a non-zero size set by set_size, and a non-zero anchor rectangle
Expand All @@ -498,7 +505,9 @@ Java_sun_awt_wl_WLComponentPeer_nativeCreateWLPopup
struct WLFrame *parentFrame = (struct WLFrame*) parentPtr;
if (frame->wl_surface) return;
frame->wl_surface = wl_compositor_create_surface(wl_compositor);
CHECK_NULL(frame->wl_surface);
frame->xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, frame->wl_surface);
CHECK_NULL(frame->xdg_surface);

wl_surface_add_listener(frame->wl_surface, &wl_surface_listener, frame);
xdg_surface_add_listener(frame->xdg_surface, &xdg_surface_listener, frame);
Expand All @@ -507,7 +516,9 @@ Java_sun_awt_wl_WLComponentPeer_nativeCreateWLPopup
assert(parentFrame);
struct xdg_positioner *xdg_positioner = newPositioner(parentX, parentY, parentWidth, parentHeight,
width, height, offsetX, offsetY);
CHECK_NULL(xdg_positioner);
frame->xdg_popup = xdg_surface_get_popup(frame->xdg_surface, parentFrame->xdg_surface, xdg_positioner);
CHECK_NULL(frame->xdg_popup);
xdg_popup_add_listener(frame->xdg_popup, &xdg_popup_listener, frame);
xdg_positioner_destroy(xdg_positioner);

Expand All @@ -530,6 +541,7 @@ Java_sun_awt_wl_WLComponentPeer_nativeRepositionWLPopup

struct xdg_positioner *xdg_positioner = newPositioner(parentX, parentY, parentWidth, parentHeight,
width, height, offsetX, offsetY);
CHECK_NULL(xdg_positioner);
static int token = 42; // This will be received by xdg_popup_repositioned(); unused for now.
xdg_popup_reposition(frame->xdg_popup, xdg_positioner, token++);
xdg_positioner_destroy(xdg_positioner);
Expand Down Expand Up @@ -599,7 +611,7 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeStartResize
(JNIEnv *env, jobject obj, jlong ptr, jint edges)
{
struct WLFrame *frame = jlong_to_ptr(ptr);
if (frame->toplevel && wl_seat) {
if (frame->toplevel && wl_seat && frame->xdg_toplevel != NULL) {
xdg_toplevel_resize(frame->xdg_toplevel, wl_seat, last_mouse_pressed_serial, edges);
}
}
Expand Down Expand Up @@ -647,12 +659,13 @@ Java_sun_awt_wl_WLComponentPeer_nativeActivate
struct WLFrame *frame = jlong_to_ptr(ptr);
if (frame->wl_surface && xdg_activation_v1 && wl_seat) {
struct xdg_activation_token_v1 *token = xdg_activation_v1_get_activation_token(xdg_activation_v1);
CHECK_NULL(token);
xdg_activation_token_v1_add_listener(token, &xdg_activation_token_v1_listener, frame);
xdg_activation_token_v1_set_serial(token, last_input_or_focus_serial, wl_seat);
if (wl_surface_in_focus) {
xdg_activation_token_v1_set_surface(token, wl_surface_in_focus);
}
xdg_activation_token_v1_commit(token);
frame->activation_token_list = add_token(frame->activation_token_list, token);
frame->activation_token_list = add_token(env, frame->activation_token_list, token);
}
}
2 changes: 2 additions & 0 deletions src/java.desktop/unix/native/libawt_wlawt/WLCursor.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLCustomCursor_nativeCreateCustomCursor

struct WLCursor *cursor = (struct WLCursor*) malloc(sizeof(struct WLCursor));
if (!cursor) {
JNU_ThrowOutOfMemoryError(env, "Failed to allocate WLCursor");
wl_buffer_destroy(buffer);
return 0;
}
Expand Down Expand Up @@ -156,6 +157,7 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetCursor
if (!wl_cursor_surface)
wl_cursor_surface = wl_compositor_create_surface(wl_compositor);

CHECK_NULL(wl_cursor_surface);
if (buffer != last_buffer) {
last_buffer = buffer;
wl_surface_attach(wl_cursor_surface, buffer, 0, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,15 @@ void
WLOutputRegister(struct wl_registry *wl_registry, uint32_t id)
{
WLOutput * output = calloc(1, sizeof(WLOutput));
JNIEnv * env = getEnv();
CHECK_NULL_THROW_OOME(env, output, "Failed to allocate WLOutput");

output->id = id;
output->wl_output = wl_registry_bind(wl_registry, id, &wl_output_interface, 2);
if (output->wl_output == NULL) {
JNU_ThrowByName(env, "java/awt/AWTError", "wl_registry_bind() failed");
}
wl_output_add_listener(output->wl_output, &wl_output_listener, output);

output->next = outputList;
outputList = output;
}
Expand Down
43 changes: 28 additions & 15 deletions src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,8 @@ process_new_listener_before_end_of_init() {
// are delivered in-order, this can be used as a barrier to ensure all previous
// requests and the resulting events have been handled."
struct wl_callback *callback = wl_display_sync(wl_display);
if (callback == NULL) return;

wl_callback_add_listener(callback,
&display_sync_listener,
callback);
Expand All @@ -679,12 +681,16 @@ registry_global(void *data, struct wl_registry *wl_registry,
wl_compositor = wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4);
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
xdg_wm_base = wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 3);
xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL);
process_new_listener_before_end_of_init();
if (xdg_wm_base != NULL) {
xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL);
process_new_listener_before_end_of_init();
}
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 5);
wl_seat_add_listener(wl_seat, &wl_seat_listener, NULL);
process_new_listener_before_end_of_init();
if (wl_seat != NULL) {
wl_seat_add_listener(wl_seat, &wl_seat_listener, NULL);
process_new_listener_before_end_of_init();
}
} else if (strcmp(interface, wl_output_interface.name) == 0) {
WLOutputRegister(wl_registry, name);
process_new_listener_before_end_of_init();
Expand All @@ -695,23 +701,25 @@ registry_global(void *data, struct wl_registry *wl_registry,
} else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
wl_ddm = wl_registry_bind(wl_registry, name,&wl_data_device_manager_interface, 3);
} else if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) {
zwp_selection_dm = wl_registry_bind(wl_registry, name,&zwp_primary_selection_device_manager_v1_interface, 1);
zwp_selection_dm = wl_registry_bind(wl_registry, name, &zwp_primary_selection_device_manager_v1_interface, 1);
}

#ifdef WAKEFIELD_ROBOT
else if (strcmp(interface, wakefield_interface.name) == 0) {
wakefield = wl_registry_bind(wl_registry, name, &wakefield_interface, 1);
wakefield_add_listener(wakefield, &wakefield_listener, NULL);
robot_queue = wl_display_create_queue(wl_display);
if (robot_queue == NULL) {
J2dTrace(J2D_TRACE_ERROR, "WLToolkit: Failed to create wakefield robot queue\n");
wakefield_destroy(wakefield);
wakefield = NULL;
} else {
wl_proxy_set_queue((struct wl_proxy*)wakefield, robot_queue);
if (wakefield != NULL) {
wakefield_add_listener(wakefield, &wakefield_listener, NULL);
robot_queue = wl_display_create_queue(wl_display);
if (robot_queue == NULL) {
J2dTrace(J2D_TRACE_ERROR, "WLToolkit: Failed to create wakefield robot queue\n");
wakefield_destroy(wakefield);
wakefield = NULL;
} else {
wl_proxy_set_queue((struct wl_proxy*)wakefield, robot_queue);
}
// TODO: call before destroying the display:
// wl_event_queue_destroy(robot_queue);
}
// TODO: call before destroying the display:
// wl_event_queue_destroy(robot_queue);
}
#endif
}
Expand Down Expand Up @@ -931,6 +939,11 @@ Java_sun_awt_wl_WLToolkit_initIDs
}

struct wl_registry *wl_registry = wl_display_get_registry(wl_display);
if (wl_registry == NULL) {
JNU_ThrowByName(env, "java/awt/AWTError", "Failed to obtain Wayland registry");
return;
}

wl_registry_add_listener(wl_registry, &wl_registry_listener, NULL);
// Process info about Wayland globals here; maybe register more handlers that
// will have to be processed later in finalize_init().
Expand Down

0 comments on commit 5ad4f8a

Please sign in to comment.