sys/: When we create our own window, indicate that we handle the
authorJan Schmidt <thaytan@mad.scientist.com>
Thu, 17 May 2007 17:35:46 +0000 (17:35 +0000)
committerJan Schmidt <thaytan@mad.scientist.com>
Thu, 17 May 2007 17:35:46 +0000 (17:35 +0000)
Original commit message from CVS:
* 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.

ChangeLog
sys/ximage/ximagesink.c
sys/xvimage/xvimagesink.c

index 39880eea84af23710206773898df1a691147dac2..6d01cc992e3bca688482a3de5e974f7adfd75b88 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2007-05-17  Jan Schmidt  <thaytan@mad.scientist.com>
+
+       * 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  <wim@fluendo.com>
 
        * gst/playback/gstdecodebin2.c: (gst_decode_bin_factory_filter):
index 321a1fc017386a3496ef703545eb41b1c0e2cdfe..7f2a1d37de3b5d112796cec1b400f1d7de96179c 100644 (file)
@@ -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);
 
-    /* Then we get all pointer motion events, only the last position is
-       interesting. */
+    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);
-    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);
-    }
+  if (pointer_moved) {
     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);
-    }
+    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
index b225ce37c6c2ca7f61887b787bb61b00b157454e..2f19443a0361d8a327ad44bb6d02e8ebb56fa5fd 100644 (file)
@@ -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 */