diff --git a/ChangeLog b/ChangeLog index 39880eea84..6d01cc992e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2007-05-17 Jan Schmidt + + * sys/ximage/ximagesink.c: (gst_ximagesink_ximage_put), + (gst_ximagesink_xwindow_new), (gst_ximagesink_handle_xevents), + (gst_ximagesink_show_frame): + * sys/xvimage/xvimagesink.c: (gst_xvimagesink_xvimage_put), + (gst_xvimagesink_xwindow_new), (gst_xvimagesink_handle_xevents), + (gst_xvimagesink_show_frame): + When we create our own window, indicate that we handle the + WM_DELETE client message from the window manager, so that it won't + kill our window (and our app) along with it. Handle ClientMessage, + post an error on the bus, and close the window. Further buffers + arriving will result in a FlowError because the window has been + destroyed. + + Fixes: #393975 + + Clean up the X event handling loop and make them the same for + both xvimagesink and ximagesink while I'm at it. + 2007-05-17 Wim Taymans * gst/playback/gstdecodebin2.c: (gst_decode_bin_factory_filter): diff --git a/sys/ximage/ximagesink.c b/sys/ximage/ximagesink.c index 321a1fc017..7f2a1d37de 100644 --- a/sys/ximage/ximagesink.c +++ b/sys/ximage/ximagesink.c @@ -668,13 +668,13 @@ gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink, } /* This function puts a GstXImageBuffer on a GstXImageSink's window */ -static void +static gboolean gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage) { GstVideoRectangle src, dst, result; gboolean draw_border = FALSE; - g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); + g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE); /* We take the flow_lock. If expose is in there we don't want to run concurrently from the data flow thread */ @@ -682,7 +682,7 @@ gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage) if (G_UNLIKELY (ximagesink->xwindow == NULL)) { g_mutex_unlock (ximagesink->flow_lock); - return; + return FALSE; } /* Draw borders when displaying the first frame. After this @@ -709,7 +709,7 @@ gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage) ximage = ximagesink->cur_image; } else { g_mutex_unlock (ximagesink->flow_lock); - return; + return TRUE; } } @@ -754,6 +754,8 @@ gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage) g_mutex_unlock (ximagesink->x_lock); g_mutex_unlock (ximagesink->flow_lock); + + return TRUE; } static gboolean @@ -818,9 +820,18 @@ gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height) XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None); if (ximagesink->handle_events) { + Atom wm_delete; + XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask | StructureNotifyMask | PointerMotionMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask); + + /* Tell the window manager we'd like delete client messages instead of + * being killed */ + wm_delete = XInternAtom (ximagesink->xcontext->disp, + "WM_DELETE_WINDOW", False); + (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win, + &wm_delete, 1); } xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, @@ -911,48 +922,48 @@ static void gst_ximagesink_handle_xevents (GstXImageSink * ximagesink) { XEvent e; + guint pointer_x = 0, pointer_y = 0; + gboolean pointer_moved = FALSE; + gboolean exposed = FALSE, configured = FALSE; g_return_if_fail (GST_IS_XIMAGESINK (ximagesink)); - { - guint pointer_x = 0, pointer_y = 0; - gboolean pointer_moved = FALSE; - - /* Then we get all pointer motion events, only the last position is - interesting. */ - g_mutex_lock (ximagesink->flow_lock); - g_mutex_lock (ximagesink->x_lock); - while (XCheckWindowEvent (ximagesink->xcontext->disp, - ximagesink->xwindow->win, PointerMotionMask, &e)) { - g_mutex_unlock (ximagesink->x_lock); - g_mutex_unlock (ximagesink->flow_lock); - - switch (e.type) { - case MotionNotify: - pointer_x = e.xmotion.x; - pointer_y = e.xmotion.y; - pointer_moved = TRUE; - break; - default: - break; - } - g_mutex_lock (ximagesink->flow_lock); - g_mutex_lock (ximagesink->x_lock); - } + /* Then we get all pointer motion events, only the last position is + interesting. */ + g_mutex_lock (ximagesink->flow_lock); + g_mutex_lock (ximagesink->x_lock); + while (XCheckWindowEvent (ximagesink->xcontext->disp, + ximagesink->xwindow->win, PointerMotionMask, &e)) { g_mutex_unlock (ximagesink->x_lock); g_mutex_unlock (ximagesink->flow_lock); - if (pointer_moved) { - GST_DEBUG ("ximagesink pointer moved over window at %d,%d", - pointer_x, pointer_y); - gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink), - "mouse-move", 0, pointer_x, pointer_y); + switch (e.type) { + case MotionNotify: + pointer_x = e.xmotion.x; + pointer_y = e.xmotion.y; + pointer_moved = TRUE; + break; + default: + break; } + g_mutex_lock (ximagesink->flow_lock); + g_mutex_lock (ximagesink->x_lock); + } + + if (pointer_moved) { + g_mutex_unlock (ximagesink->x_lock); + g_mutex_unlock (ximagesink->flow_lock); + + GST_DEBUG ("ximagesink pointer moved over window at %d,%d", + pointer_x, pointer_y); + gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink), + "mouse-move", 0, pointer_x, pointer_y); + + g_mutex_lock (ximagesink->flow_lock); + g_mutex_lock (ximagesink->x_lock); } /* We get all remaining events on our window to throw them upstream */ - g_mutex_lock (ximagesink->flow_lock); - g_mutex_lock (ximagesink->x_lock); while (XCheckWindowEvent (ximagesink->xcontext->disp, ximagesink->xwindow->win, KeyPressMask | KeyReleaseMask | @@ -1009,36 +1020,60 @@ gst_ximagesink_handle_xevents (GstXImageSink * ximagesink) g_mutex_lock (ximagesink->flow_lock); g_mutex_lock (ximagesink->x_lock); } - g_mutex_unlock (ximagesink->x_lock); - g_mutex_unlock (ximagesink->flow_lock); - { - gboolean exposed = FALSE; - - g_mutex_lock (ximagesink->flow_lock); - g_mutex_lock (ximagesink->x_lock); - while (XCheckWindowEvent (ximagesink->xcontext->disp, - ximagesink->xwindow->win, ExposureMask, &e)) { - g_mutex_unlock (ximagesink->x_lock); - g_mutex_unlock (ximagesink->flow_lock); - - switch (e.type) { - case Expose: - exposed = TRUE; - break; - default: - break; - } - g_mutex_lock (ximagesink->flow_lock); - g_mutex_lock (ximagesink->x_lock); + while (XCheckWindowEvent (ximagesink->xcontext->disp, + ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) { + switch (e.type) { + case Expose: + exposed = TRUE; + break; + case ConfigureNotify: + configured = TRUE; + break; + default: + break; } + } + + if (exposed || configured) { g_mutex_unlock (ximagesink->x_lock); g_mutex_unlock (ximagesink->flow_lock); - if (exposed) { - gst_ximagesink_expose (GST_X_OVERLAY (ximagesink)); + gst_ximagesink_expose (GST_X_OVERLAY (ximagesink)); + + g_mutex_lock (ximagesink->x_lock); + g_mutex_lock (ximagesink->flow_lock); + } + + /* Handle Display events */ + while (XPending (ximagesink->xcontext->disp)) { + XNextEvent (ximagesink->xcontext->disp, &e); + + switch (e.type) { + case ClientMessage:{ + Atom wm_delete; + + wm_delete = XInternAtom (ximagesink->xcontext->disp, + "WM_DELETE_WINDOW", False); + if (wm_delete == (Atom) e.xclient.data.l[0]) { + /* Handle window deletion by posting an error on the bus */ + GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND, + ("Output window was closed"), (NULL)); + + g_mutex_unlock (ximagesink->x_lock); + gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow); + ximagesink->xwindow = NULL; + g_mutex_lock (ximagesink->x_lock); + } + break; + } + default: + break; } } + + g_mutex_unlock (ximagesink->x_lock); + g_mutex_unlock (ximagesink->flow_lock); } static gpointer @@ -1543,7 +1578,8 @@ gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf) put the ximage which is in the PRIVATE pointer */ if (GST_IS_XIMAGE_BUFFER (buf)) { GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly"); - gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf)); + if (!gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf))) + goto no_window; } else { /* Else we have to copy the data into our private image, */ /* if we have one... */ @@ -1569,7 +1605,8 @@ gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf) } memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf), MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size)); - gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage); + if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage)) + goto no_window; } return GST_FLOW_OK; @@ -1581,6 +1618,12 @@ no_ximage: GST_DEBUG ("could not create image"); return GST_FLOW_ERROR; } +no_window: + { + /* No Window available to put our image into */ + GST_WARNING_OBJECT (ximagesink, "could not output image - no window"); + return GST_FLOW_ERROR; + } } /* Buffer management diff --git a/sys/xvimage/xvimagesink.c b/sys/xvimage/xvimagesink.c index b225ce37c6..2f19443a03 100644 --- a/sys/xvimage/xvimagesink.c +++ b/sys/xvimage/xvimagesink.c @@ -719,15 +719,16 @@ gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink, } } -/* This function puts a GstXvImage on a GstXvImageSink's window */ -static void +/* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE + * if no window was available */ +static gboolean gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstXvImageBuffer * xvimage) { GstVideoRectangle src, dst, result; gboolean draw_border = FALSE; - g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); + g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE); /* We take the flow_lock. If expose is in there we don't want to run concurrently from the data flow thread */ @@ -735,7 +736,7 @@ gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, if (G_UNLIKELY (xvimagesink->xwindow == NULL)) { g_mutex_unlock (xvimagesink->flow_lock); - return; + return FALSE; } /* Draw borders when displaying the first frame. After this @@ -762,7 +763,7 @@ gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, xvimage = xvimagesink->cur_image; } else { g_mutex_unlock (xvimagesink->flow_lock); - return; + return TRUE; } } @@ -821,6 +822,8 @@ gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, g_mutex_unlock (xvimagesink->x_lock); g_mutex_unlock (xvimagesink->flow_lock); + + return TRUE; } static gboolean @@ -888,9 +891,18 @@ gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink, XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None); if (xvimagesink->handle_events) { + Atom wm_delete; + XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask | StructureNotifyMask | PointerMotionMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask); + + /* Tell the window manager we'd like delete client messages instead of + * being killed */ + wm_delete = XInternAtom (xvimagesink->xcontext->disp, + "WM_DELETE_WINDOW", False); + (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win, + &wm_delete, 1); } xwindow->gc = XCreateGC (xvimagesink->xcontext->disp, @@ -1048,6 +1060,7 @@ gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink) XEvent e; guint pointer_x = 0, pointer_y = 0; gboolean pointer_moved = FALSE; + gboolean exposed = FALSE, configured = FALSE; g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); @@ -1072,19 +1085,20 @@ gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink) g_mutex_lock (xvimagesink->flow_lock); g_mutex_lock (xvimagesink->x_lock); } - g_mutex_unlock (xvimagesink->x_lock); - g_mutex_unlock (xvimagesink->flow_lock); - if (pointer_moved) { + g_mutex_unlock (xvimagesink->x_lock); + g_mutex_unlock (xvimagesink->flow_lock); + GST_DEBUG ("xvimagesink pointer moved over window at %d,%d", pointer_x, pointer_y); gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink), "mouse-move", 0, e.xbutton.x, e.xbutton.y); + + g_mutex_lock (xvimagesink->flow_lock); + g_mutex_lock (xvimagesink->x_lock); } /* We get all events on our window to throw them upstream */ - g_mutex_lock (xvimagesink->flow_lock); - g_mutex_lock (xvimagesink->x_lock); while (XCheckWindowEvent (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask, @@ -1141,40 +1155,61 @@ gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink) g_mutex_lock (xvimagesink->flow_lock); g_mutex_lock (xvimagesink->x_lock); } - g_mutex_unlock (xvimagesink->x_lock); - g_mutex_unlock (xvimagesink->flow_lock); /* Handle Expose */ - { - gboolean exposed = FALSE, configured = FALSE; - - g_mutex_lock (xvimagesink->flow_lock); - g_mutex_lock (xvimagesink->x_lock); - while (XCheckWindowEvent (xvimagesink->xcontext->disp, - xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, - &e)) { - g_mutex_unlock (xvimagesink->x_lock); - g_mutex_unlock (xvimagesink->flow_lock); - - switch (e.type) { - case Expose: - exposed = TRUE; - break; - case ConfigureNotify: - configured = TRUE; - default: - break; - } - g_mutex_lock (xvimagesink->flow_lock); - g_mutex_lock (xvimagesink->x_lock); + while (XCheckWindowEvent (xvimagesink->xcontext->disp, + xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) { + switch (e.type) { + case Expose: + exposed = TRUE; + break; + case ConfigureNotify: + configured = TRUE; + break; + default: + break; } + } + + if (exposed || configured) { g_mutex_unlock (xvimagesink->x_lock); g_mutex_unlock (xvimagesink->flow_lock); - if (exposed || configured) { - gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink)); + gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink)); + + g_mutex_lock (xvimagesink->x_lock); + g_mutex_lock (xvimagesink->flow_lock); + } + + /* Handle Display events */ + while (XPending (xvimagesink->xcontext->disp)) { + XNextEvent (xvimagesink->xcontext->disp, &e); + + switch (e.type) { + case ClientMessage:{ + Atom wm_delete; + + wm_delete = XInternAtom (xvimagesink->xcontext->disp, + "WM_DELETE_WINDOW", False); + if (wm_delete == (Atom) e.xclient.data.l[0]) { + /* Handle window deletion by posting an error on the bus */ + GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND, + ("Output window was closed"), (NULL)); + + g_mutex_unlock (xvimagesink->x_lock); + gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow); + xvimagesink->xwindow = NULL; + g_mutex_lock (xvimagesink->x_lock); + } + break; + } + default: + break; } } + + g_mutex_unlock (xvimagesink->x_lock); + g_mutex_unlock (xvimagesink->flow_lock); } static void @@ -2114,7 +2149,8 @@ gst_xvimagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf) put the ximage which is in the PRIVATE pointer */ if (GST_IS_XVIMAGE_BUFFER (buf)) { GST_LOG_OBJECT (xvimagesink, "fast put of bufferpool buffer"); - gst_xvimagesink_xvimage_put (xvimagesink, GST_XVIMAGE_BUFFER (buf)); + if (!gst_xvimagesink_xvimage_put (xvimagesink, GST_XVIMAGE_BUFFER (buf))) + goto no_window; } else { GST_LOG_OBJECT (xvimagesink, "slow copy into bufferpool buffer"); /* Else we have to copy the data into our private image, */ @@ -2145,7 +2181,8 @@ gst_xvimagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf) GST_BUFFER_DATA (buf), MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size)); - gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage); + if (!gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage)) + goto no_window; } return GST_FLOW_OK; @@ -2157,6 +2194,12 @@ no_image: GST_WARNING_OBJECT (xvimagesink, "could not create image"); return GST_FLOW_ERROR; } +no_window: + { + /* No Window available to put our image into */ + GST_WARNING_OBJECT (xvimagesink, "could not output image - no window"); + return GST_FLOW_ERROR; + } } /* Buffer management */