cgtkgtk3

Access priv->children in GtkNotebook for debugging (GtkNotebookPrivate)


I am currently debugging an issue where gtk_notebook_remove_page(GTK_NOTEBOOK(nbook), page); is crashing the program.

Looking into the implementation of the function gtk_notebook_remove_page, I have found that it is implemented as such (from the Gnome repository):

void
gtk_notebook_remove_page (GtkNotebook *notebook,
                          gint         page_num)
{
  GtkNotebookPrivate *priv;
  GList *list = NULL;

  g_return_if_fail (GTK_IS_NOTEBOOK (notebook));

  priv = notebook->priv;

  if (page_num >= 0)
    list = g_list_nth (priv->children, page_num);
  else
    list = g_list_last (priv->children);

  if (list)
    gtk_container_remove (GTK_CONTAINER (notebook),
                          ((GtkNotebookPage *) list->data)->child);
}

I have checked with conditional breakpoints to ensure that nbook is a GTK_NOTEBOOK, that page is larger or equals to zero, and that nbook itself is not NULL. All of the above hold true, which means that the error occurs at: gtk_container_remove (GTK_CONTAINER (notebook), ((GtkNotebookPage *) list->data)->child);. Following this, I have found that the function gtk_container_remove is implemented as such (similarly from the Gnome repository):

void
gtk_container_remove (GtkContainer *container,
                      GtkWidget    *widget)
{
  g_return_if_fail (GTK_IS_CONTAINER (container));
  g_return_if_fail (GTK_IS_WIDGET (widget));

  g_object_ref (container);
  g_object_ref (widget);

  g_signal_emit (container, container_signals[REMOVE], 0, widget);

  _gtk_container_accessible_remove (GTK_WIDGET (container), widget);

  g_object_unref (widget);
  g_object_unref (container);
}

From here, we can see that the program will crash if either the condition GTK_IS_CONTAINER (container) or GTK_IS_WIDGET (widget) is false. Since we are performing GTK_CONTAINER(notebook) at the point of calling gtk_container_remove and nbook which is passed to GtkNotebook * notebook initially is determined not to be NULL, I suspect that the second condition is false (i.e., ((GtkNotebookPage *) list->data)->child is a dangling pointer/invalid). However, when I try to verify what exactly is notebook->priv->children, it will not allow me to access it as struct _GtkNotebookPrivate is an incomplete type. This is what I have attempted:

GtkNotebookPrivate *priv;
GList *list = NULL;
priv = nbook->priv;
list = g_list_nth(priv->children, page);
/* Check if list is valid here */
/* Futher check if list->data->child is valid */
gtk_notebook_remove_page(GTK_NOTEBOOK(nbook), page);

Error message:

pointer or reference to incomplete type "struct _GtkNotebookPrivate" is not allowed

Is there a way to check the validity of GtkNotebookPrivate outside of the GTK library's own implementation (i.e., check it inline)?

Additional information:

After further digging, I found the following as the implementation of the GtkNotebookPrivate structure (from the Gnome respository):

struct _GtkNotebookPrivate
{
  GtkNotebookDragOperation   operation;
  GtkNotebookPage           *cur_page;
  GtkNotebookPage           *detached_tab;
  GtkNotebookPage           *prelight_tab;
  GtkTargetList             *source_targets;
  GtkWidget                 *action_widget[N_ACTION_WIDGETS];
  GtkWidget                 *dnd_window;
  GtkWidget                 *menu;

  GdkWindow               *drag_window;
  GdkWindow               *event_window;

  GtkCssGadget              *gadget;
  GtkCssGadget              *stack_gadget;
  GtkCssGadget              *header_gadget;
  GtkCssGadget              *tabs_gadget;
  GtkCssGadget              *arrow_gadget[4];

  GList         *children;
  GList         *first_tab;             /* The first tab visible (for scrolling notebooks) */
  GList         *focus_tab;

  gint           drag_begin_x;
  gint           drag_begin_y;
  gint           drag_offset_x;
  gint           drag_offset_y;
  gint           drag_window_x;
  gint           drag_window_y;
  gint           mouse_x;
  gint           mouse_y;
  gint           pressed_button;

  GQuark         group;

  guint          dnd_timer;
  guint          switch_tab_timer;
  GList         *switch_tab;

  guint32        timer;

  guint          child_has_focus    : 1;
  guint          click_child        : 3;
  guint          remove_in_detach   : 1;
  guint          focus_out          : 1; /* Flag used by ::move-focus-out implementation */
  guint          has_scrolled       : 1;
  guint          in_child           : 3;
  guint          need_timer         : 1;
  guint          show_border        : 1;
  guint          show_tabs          : 1;
  guint          scrollable         : 1;
  guint          tab_pos            : 2;
  guint          tabs_reversed      : 1;
  guint          rootwindow_drop    : 1;
};

As per suggestions, I have tried to add in this structure declaration in my own source code as well, however, as evident, it requires dependencies such as GtkCssGadget which again, cannot be normally accessed through the standard Gtk+ library. I do have the choice of adding in the implementation of the GtkCssGadget and other needed structures, however, I do feel it is a very inefficient method and was wondering if any other methods were possible.

In addition, this is how GtkNotebook is defined in gtknotebook.h:

struct _GtkNotebook
{
  /*< private >*/
  GtkContainer container;

  GtkNotebookPrivate *priv;
};

Is there any way to access the *priv without adding in manually all the code for GtkNotebookPrivate and its dependencies?


Solution

  • After further analysis and searching the Gnome repository, I have determined that it is normally (and by right, since GtkNotebookPrivate is supposed to stay private), it is impossible to access GtkNotebook's priv's variables.

    A workaround for similar issues can be done via the use of this function:

    GtkWidget*
    gtk_notebook_get_nth_page (
      GtkNotebook* notebook,
      int page_num
    )
    

    which will return the n-th page of the GtkNotebook * notebook, which is basically the same thing as (GtkNotebookPage *) list->data)->child. From here, we can check if the return value is or is not NULL to check if there is a dangling pointer or check what widget the gtk_notebook_remove_page(GTK_NOTEBOOK(nbook), page); is trying to remove.

    For example, you can do this to check that the n-th page is valid:

    if (gtk_notebook_get_nth_page(GTK_NOTEBOOK(nbook), page))
    {
        // no error
    }
    else
    {
       // log for error, n-th page is not valid
    }
    gtk_notebook_remove_page(GTK_NOTEBOOK(nbook), page);