Sponsored Content
Top Forums Programming GTK C development; question/concern wrt gtk_dialog_run Post 302564457 by DreamWarrior on Thursday 13th of October 2011 05:17:06 PM
Old 10-13-2011
GTK C development; question/concern wrt gtk_dialog_run

I'm new to GTK development, so I've been going through the examples just to capture the basics. I've done a lot of Unix GUI development before, but it was all Xt/Motif stuff. So, the GTK scheduler is something new to me.

That said, I was going through the color selection example here, and was pretty curious how it handled the gtk_dialog_run command. I was mostly interested if during the call (while it was waiting for a response on line 132) a timer would continue to run. I though, for sure, it wouldn't.

To my surprise, a timer (added via g_timeout_add) did continue on in the background. My first thought was, "oh, neat, they threaded the timers." But then I figured, "but, why not see." So I threw together the code below:

Code:
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

#include <glib.h>
#include <gdk/gdk.h>
#include <gtk/gtk.h>

GtkWidget *colorseldlg = NULL;
GtkWidget *drawingarea = NULL;
GdkColor color;

static pthread_key_t _g_uidPTK;

typedef struct
{
	int		tid;
	
} tspecific_t;

typedef struct
{
	int		 val;
	char	*txt;

} app_timer_t;

static tspecific_t *get_tsd()
{
	static pthread_mutex_t	 mx = PTHREAD_MUTEX_INITIALIZER;
	static int				 nextId = 1;
	
	tspecific_t				*tdata;
	
	if ((tdata = pthread_getspecific(_g_uidPTK)) == NULL)
	{
		tdata = malloc(sizeof(*tdata));
	
		pthread_setspecific(_g_uidPTK, tdata);
		
		pthread_mutex_lock(&mx);
		tdata->tid = nextId;
		nextId++;
		pthread_mutex_unlock(&mx);	
	}
	
	return tdata;
}

static int get_tid()
{
	tspecific_t				*tdata = get_tsd();
	
	return tdata->tid;
}

static gboolean progress_timeout( gpointer _timer )
{
  #define timer	((app_timer_t *) _timer)
	
  printf("[tid:%d] called %s [%d]...\n", get_tid(), timer->txt, ++(timer->val));
  
  /* As this is a timeout function, return TRUE so that it
   * continues to get called */
  return TRUE;
  
  #undef timer
} 

/* Color changed handler */

static void color_changed_cb( GtkWidget         *widget,
                              GtkColorSelection *colorsel )
{
  GdkColor ncolor;

  printf("[tid:%d] color changed...\n", get_tid());
  
  gtk_color_selection_get_current_color (colorsel, &ncolor);
  gtk_widget_modify_bg (drawingarea, GTK_STATE_NORMAL, &ncolor);       
}

/* Drawingarea event handler */

static gboolean area_event( GtkWidget *widget,
                            GdkEvent  *event,
                            gpointer   client_data )
{
  gint handled = FALSE;
  gint response;
  GtkColorSelection *colorsel;

  /* Check if we've received a button pressed event */
  
  printf("[tid:%d] Got event %d...\n", get_tid(), event->type);
  
  if (event->type == GDK_BUTTON_PRESS)
    {
      handled = TRUE;

       /* Create color selection dialog */
      if (colorseldlg == NULL)
        colorseldlg = gtk_color_selection_dialog_new ("Select background color");

      /* Get the ColorSelection widget */
      colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (colorseldlg)->colorsel);

      gtk_color_selection_set_previous_color (colorsel, &color);
      gtk_color_selection_set_current_color (colorsel, &color);
      gtk_color_selection_set_has_palette (colorsel, TRUE);

      /* Connect to the "color_changed" signal, set the client-data
       * to the colorsel widget */
      g_signal_connect (colorsel, "color_changed",
                        G_CALLBACK (color_changed_cb), (gpointer) colorsel);

      /* Show the dialog */
      printf("[tid:%d] Running dialog...\n", get_tid());
      
      response = gtk_dialog_run (GTK_DIALOG (colorseldlg));

      printf("[tid:%d] Got response.\n", get_tid());
      
      if (response == GTK_RESPONSE_OK)
        gtk_color_selection_get_current_color (colorsel, &color);
      else 
        gtk_widget_modify_bg (drawingarea, GTK_STATE_NORMAL, &color);

      gtk_widget_hide (colorseldlg);
    }

  return handled;
}

/* Close down and exit handler */

static gboolean destroy_window( GtkWidget *widget,
                                GdkEvent  *event,
                                gpointer   client_data )
{
  gtk_main_quit ();
  return TRUE;
}

/* Main */

gint main( gint   argc,
           gchar *argv[] )
{
  GtkWidget		*window;
  app_timer_t	 t1 = { 0, "T1" };
  app_timer_t	 t2 = { 0, "T2" };

  /* Initialize the toolkit, remove gtk-related commandline stuff */

  gtk_init (&argc, &argv);
  
  /* create key for thread assignment */
  
  pthread_key_create(&_g_uidPTK, free);

  /* Create toplevel window, set title and policies */

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Color selection test");
  gtk_window_set_policy (GTK_WINDOW (window), TRUE, TRUE, TRUE);

  /* Attach to the "delete" and "destroy" events so we can exit */

  g_signal_connect (window, "delete-event",
                    G_CALLBACK (destroy_window), (gpointer) window);
  
  /* Create drawingarea, set size and catch button events */

  drawingarea = gtk_drawing_area_new ();

  color.red = 0;
  color.blue = 65535;
  color.green = 0;
  gtk_widget_modify_bg (drawingarea, GTK_STATE_NORMAL, &color);       

  gtk_widget_set_size_request (GTK_WIDGET (drawingarea), 200, 200);

  gtk_widget_set_events (drawingarea, GDK_BUTTON_PRESS_MASK);

  g_signal_connect (GTK_OBJECT (drawingarea), "event", 
	            GTK_SIGNAL_FUNC (area_event), (gpointer) drawingarea);
  
  /* Add drawingarea to window, then show them both */

  gtk_container_add (GTK_CONTAINER (window), drawingarea);

  gtk_widget_show (drawingarea);
  gtk_widget_show (window);
  
  /* add a few timeouts to test how they are handled */
  
  g_timeout_add (2000, progress_timeout, &t1);
  g_timeout_add (3000, progress_timeout, &t2);
  
  /* Enter the gtk main loop (this never returns) */
    
  gtk_main ();

  /* Satisfy grumpy compilers */

  return 0;
}

Most of it is their code, I just added the stuff to assign and retrieve a unique ID for each thread.

Upon running it, I was further surprised! The timers and the call waiting for the dialog to come back are "running" in the same thread. Here's some output:

Code:
[tid:1] Got event 13...
[tid:1] Got event 2...
[tid:1] Got event 2...
[tid:1] called T1 [1]...
[tid:1] called T2 [1]...
[tid:1] Got event 4...
[tid:1] Running dialog...
[tid:1] called T1 [2]...
[tid:1] called T1 [3]...
[tid:1] called T2 [2]...
[tid:1] called T1 [4]...
[tid:1] called T2 [3]...
[tid:1] Got response.
[tid:1] called T1 [5]...
[tid:1] called T1 [6]...
[tid:1] called T2 [4]...

Humm...now, I know this can be accomplished with some funky long jumping and the like. A quick debugging session with GDB further proves that setting a breakpoint inside the timer and inspecting the stack have them both "running" together. See below:

Code:
(gdb) break 61
Breakpoint 1 at 0x80490b5: file gtktst.c, line 61.
(gdb) run
Starting program: /home/dreamwarrior/CStuff/gtkStuff/gtktst 
[Thread debugging using libthread_db enabled]
[tid:1] Got event 13...
[tid:1] Got event 2...
[tid:1] Got event 2...
[tid:1] Got event 4...
[tid:1] Running dialog...

Breakpoint 1, progress_timeout (_timer=0xbffff394) at gtktst.c:61
61	  printf("[tid:%d] called %s [%d]...\n", get_tid(), timer->txt, ++(timer->val));
(gdb) where
#0  progress_timeout (_timer=0xbffff394) at gtktst.c:61
#1  0x008b7d5c in ?? () from /lib/libglib-2.0.so.0
#2  0x008b75e5 in g_main_context_dispatch () from /lib/libglib-2.0.so.0
#3  0x008bb2d8 in ?? () from /lib/libglib-2.0.so.0
#4  0x008bb817 in g_main_loop_run () from /lib/libglib-2.0.so.0
#5  0x001deabf in gtk_dialog_run () from /usr/lib/libgtk-x11-2.0.so.0
#6  0x0804926d in area_event (widget=0x8077858, event=0x80774b0, 
    client_data=0x8077858) at gtktst.c:120
#7  0x0026b424 in ?? () from /usr/lib/libgtk-x11-2.0.so.0
#8  0x00834252 in g_closure_invoke () from /usr/lib/libgobject-2.0.so.0
#9  0x0084899d in ?? () from /usr/lib/libgobject-2.0.so.0
#10 0x00849c33 in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0
#11 0x0084a256 in g_signal_emit () from /usr/lib/libgobject-2.0.so.0
#12 0x003984da in ?? () from /usr/lib/libgtk-x11-2.0.so.0
#13 0x00263a5d in gtk_propagate_event () from /usr/lib/libgtk-x11-2.0.so.0
#14 0x002650c7 in gtk_main_do_event () from /usr/lib/libgtk-x11-2.0.so.0
#15 0x0055939a in ?? () from /usr/lib/libgdk-x11-2.0.so.0
#16 0x008b75e5 in g_main_context_dispatch () from /lib/libglib-2.0.so.0
#17 0x008bb2d8 in ?? () from /lib/libglib-2.0.so.0
#18 0x008bb817 in g_main_loop_run () from /lib/libglib-2.0.so.0
#19 0x002653c9 in gtk_main () from /usr/lib/libgtk-x11-2.0.so.0
#20 0x08049523 in main (argc=1, argv=0xbffff464) at gtktst.c:203
(gdb) quit

This sort of thing has me worried. What is safe to do in my timer with other code sitting on the stack? I certainly couldn't grab a mutex before the gtk_dialog_run call that any timer would grab, I'd deadlock! So...what else isn't safe? The documentation isn't really clear, and in fact, to me it implies that gtk_dialog_run "blocks" the event loop; but it obviously doesn't.
 
All times are GMT -4. The time now is 09:11 PM.
Unix & Linux Forums Content Copyright 1993-2022. All Rights Reserved.
Privacy Policy