A bug to move gtk+ window from dp to hdmi

Hello,

• Hardware Platform: Jetson Xavier NX
• JetPack Version: 4.6 [L4T 32.6.1]

I integrate GStreamer in GTK+. Two displays (DP(1920x1080) as main display; HDMI(1920X1080) as secondary display) are connected to the NX. When I try to move gtk+ window from DP to HDMI using gtk_window_move (GTK_WINDOW (data->main_window), 1920, 0), it dosen’t work.
Here is the whole code:

#include <string.h>

#include <gtk/gtk.h>
#include <gst/gst.h>
#include <gst/video/videooverlay.h>

#include <gdk/gdk.h>
#if defined (GDK_WINDOWING_X11)
#include <gdk/gdkx.h>
#elif defined (GDK_WINDOWING_WIN32)
#include <gdk/gdkwin32.h>
#elif defined (GDK_WINDOWING_QUARTZ)
#include <gdk/gdkquartz.h>
#endif

/* Structure to contain all our information, so we can pass it around */
typedef struct _CustomData {
  GstElement *playbin;            /* Our one and only pipeline */
  GtkWidget *main_window;         /* The uppermost window, containing all other windows */
  GtkWidget *video_window; /* The drawing area where the video will be shown */ 

  GstState state;                 /* Current state of the pipeline */
  gint64 duration;                /* Duration of the clip, in nanoseconds */
} CustomData;

/* This function is called periodically to refresh the GUI */
static gboolean refresh_ui (CustomData *data) {
  static gboolean flag = FALSE;
  static gint count = FALSE;

  if (flag) {
    gtk_window_resize (GTK_WINDOW (data->main_window), 1920, 1080);
    gtk_window_move (GTK_WINDOW (data->main_window), 0, 0);
  } else {
    gtk_window_resize (GTK_WINDOW (data->main_window), 1920, 1080);
    gtk_window_move (GTK_WINDOW (data->main_window), 1920, 0);
  }
  flag = !flag;

  if (count++ > 20) {
    return FALSE;
  }

  return TRUE;
}

/* This function is called when the GUI toolkit creates the physical window that will hold the video.
 * At this point we can retrieve its handler (which has a different meaning depending on the windowing system)
 * and pass it to GStreamer through the VideoOverlay interface. */
static void realize_cb (GtkWidget *widget, CustomData *data) {
  GdkWindow *window = gtk_widget_get_window (widget);

  if (!gdk_window_ensure_native (window))
    g_error ("Couldn't create native window needed for GstVideoOverlay!");
}

/* This function is called when the STOP button is clicked */
static void stop_cb (GtkButton *button, CustomData *data) {
  gst_element_set_state (data->playbin, GST_STATE_READY);
}

/* This function is called when the main window is closed */
static void delete_event_cb (GtkWidget *widget, GdkEvent *event, CustomData *data) {
  stop_cb (NULL, data);
  gtk_main_quit ();
}

/* This function is called everytime the video window needs to be redrawn (due to damage/exposure,
 * rescaling, etc). GStreamer takes care of this in the PAUSED and PLAYING states, otherwise,
 * we simply draw a black rectangle to avoid garbage showing up. */
static gboolean draw_cb (GtkWidget *widget, cairo_t *cr, CustomData *data) {
  g_print("Enter draw_cb.\n");
  static gboolean flag = FALSE;
  if (data->state == GST_STATE_PAUSED) {
    if (!flag) {
        g_print("Skip draw background.\n");
        flag = !flag;
        return FALSE;
    }
    GtkAllocation allocation;

    /* Cairo is a 2D graphics library which we use here to clean the video window.
     * It is used by GStreamer for other reasons, so it will always be available to us. */
    gtk_widget_get_allocation (widget, &allocation);
    cairo_set_source_rgb (cr, 0, 0, 0);
    cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
    cairo_fill (cr);

    GdkWindow *window = gtk_widget_get_window (widget);
    guintptr window_handle;

    window_handle = GDK_WINDOW_XID (window);

    g_print("The background of window handle with window id 0x%lx.\n", window_handle);
    
    GstElement *render = gst_bin_get_by_name (GST_BIN(data->playbin), "render");
    gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (render), window_handle);
    g_print("Video set window handle with window id 0x%lx.\n", window_handle);
    gst_object_unref (render);
  }

  return FALSE;
}

/* This creates all the GTK+ widgets that compose our application, and registers the callbacks */
static void create_ui (CustomData *data) {

  data->main_window = gtk_window_new (GTK_WINDOW_POPUP);
  g_signal_connect (G_OBJECT (data->main_window), "delete-event", G_CALLBACK (delete_event_cb), data);

  data->video_window = gtk_drawing_area_new ();
  g_signal_connect (data->video_window, "realize", G_CALLBACK (realize_cb), data);
  g_signal_connect (data->video_window, "draw", G_CALLBACK (draw_cb), data);

  gtk_container_add(GTK_CONTAINER(data->main_window), data->video_window);
  gtk_window_set_default_size(GTK_WINDOW(data->main_window), 1920, 1080);
  gtk_window_move(GTK_WINDOW (data->main_window), 0, 0);

  gtk_widget_show_all (data->main_window);
}

/* This function is called when an error message is posted on the bus */
static void error_cb (GstBus *bus, GstMessage *msg, CustomData *data) {
  GError *err;
  gchar *debug_info;

  /* Print error details on the screen */
  gst_message_parse_error (msg, &err, &debug_info);
  g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
  g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
  g_clear_error (&err);
  g_free (debug_info);

  /* Set the pipeline to READY (which stops playback) */
  gst_element_set_state (data->playbin, GST_STATE_READY);
}

/* This function is called when an End-Of-Stream message is posted on the bus.
 * We just set the pipeline to READY (which stops playback) */
static void eos_cb (GstBus *bus, GstMessage *msg, CustomData *data) {
  g_print ("End-Of-Stream reached.\n");
  gst_element_set_state (data->playbin, GST_STATE_READY);
}

/* This function is called when the pipeline changes states. We use it to
 * keep track of the current state. */
static void state_changed_cb (GstBus *bus, GstMessage *msg, CustomData *data) {
  GstState old_state, new_state, pending_state;
  gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
  if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data->playbin)) {
    data->state = new_state;
    g_print ("State set to %s\n", gst_element_state_get_name (new_state));
  }
}

int main(int argc, char *argv[]) {
  CustomData data;
  GstStateChangeReturn ret;
  GstBus *bus;

  /* Initialize GTK */
  gtk_init (&argc, &argv);

  /* Initialize GStreamer */
  gst_init (&argc, &argv);

  /* Initialize our data structure */
  memset (&data, 0, sizeof (data));
  data.duration = GST_CLOCK_TIME_NONE;

  /* Create the elements */
  data.playbin = gst_parse_launch ("videotestsrc is-live=true ! video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 "
                                   "! videoconvert ! xvimagesink name=render sync=false", NULL);
  if (!data.playbin) {
    g_printerr ("Not all elements could be created.\n");
    return -1;
  }

  /* Create the GUI */
  create_ui (&data);

  /* Instruct the bus to emit signals for each received message, and connect to the interesting signals */
  bus = gst_element_get_bus (data.playbin);
  gst_bus_add_signal_watch (bus);
  g_signal_connect (G_OBJECT (bus), "message::error", (GCallback)error_cb, &data);
  g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback)eos_cb, &data);
  g_signal_connect (G_OBJECT (bus), "message::state-changed", (GCallback)state_changed_cb, &data);
  gst_object_unref (bus);

  /* Start playing */
  ret = gst_element_set_state (data.playbin, GST_STATE_PLAYING);
  if (ret == GST_STATE_CHANGE_FAILURE) {
    g_printerr ("Unable to set the pipeline to the playing state.\n");
    gst_object_unref (data.playbin);
    return -1;
  }

  /* Register a function that GLib will call every second */
  g_timeout_add_seconds (3, (GSourceFunc)refresh_ui, &data);

  /* Start the GTK main loop. We will not regain control until gtk_main_quit is called. */
  gtk_main ();

  /* Free resources */
  gst_element_set_state (data.playbin, GST_STATE_NULL);
  gst_object_unref (data.playbin);
  return 0;
}

Compile with the command

gcc gtk_test.c -o gtk_test `pkg-config --cflags --libs gstreamer-video-1.0 gtk±3.0 gstreamer-1.0`

Howerver, when I try to change 1920 → 1921, using gtk_window_move (GTK_WINDOW (data->main_window), 1921, 0), move to the hdmi display successfully. It seems a bug about the position point 1920. Any help would be appreciated.

Thank you

Hi,
Could you try latest Jetpack 4.6.3? The latest Jetpack 4 release is 4.6.3 and we would suggest give it a try.

Hi,
First of all, thank you for your prompt reply. However, considering stability, it is unrealistic to upgrade in the short term.

Addition:
I tried newer Jetpack version (jetpack 5.0.2) and this version still has the issue above

Hi,
Could you share the latest development about the issus, thanks.

Hi DaneLLL, any update on this issue?

Hi @zhang.dongchuan please try Jetpack 5.1.1 and check if you observe the issue. If yes, please share us the method so that we can replicate it and check further.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.