Buggy behaviour with widget resize in status bar



I have been working on a new game for Gnome for the last three weeks,
and have run into a few problems I could not solve.  Two of those
problems have been solved by two well known Gnome gurus, as they were
just gaps in my knowledge.

Unfortunately, the last problem might just be a bug.  I have written a
"small" test program that replicates the conditions under which the
problem occurs.

The game I am writing has a client / server architecture.  When the
client connects to the server, the signon message from the server
tells the client how many players are in the game.  In respose to that
message, I resize a widget in the status bar.  Only about one time in
50 does the widget in the status bar actually get resized.  For all of
the times that the resize does not work, a manual resize of the main
window triggers a resize of the status bar, and then the widget
assumes the correct size.

If you have read this far, I suspect that there is some sort of race
condition.  This is what the test program does:

1 - Builds a main window with either lots of child widgets (default),
    or with a single label (-s argument).

2 - Connects to the daytime tcp service and sets a gdk read ready
    handler for that connection.

    When gdk invokes the callback, the program shrinks the widget in
    the status bar and sets the message area to indicate that the
    resize has been performed.  For good measure, it also writes a
    message to stderr.

3 - Enters gtk_main().

4 - Allows you to manually grow and shrink the widget using ^G and ^S
    respectively.

Now, what I have observed is that the status bar never gets the
initial resize when building the complex main window.  If building a
simple main window, the resize works (it does for me anyway).

Now over to the gurus...

- Dave

P.S.  If you are wondering about the game, check this out:

	http://www.dccs.com.au/~dave/gnocatan/

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>

#include <gnome.h>

static int fd;
static gint read_tag;

static GtkWidget *app_bar;
static GtkWidget *status_area;

static gint status_area_cb(GtkWidget *area,
			   GdkEventExpose *event, gpointer user_data)
{
	gchar width[16];

	if (area->window == NULL)
		return FALSE;

	gdk_draw_rectangle(area->window, area->style->black_gc, FALSE, 2, 2,
			   area->allocation.width - 4,
			   area->allocation.height - 4);
	g_snprintf(width, sizeof(width), "%dpx", area->allocation.width);
	gdk_draw_text(area->window, area->style->font, area->style->black_gc,
		      4, area->allocation.height - 6, width, strlen(width));

	return FALSE;
}

static void show_status(gchar *fmt, ...)
{
	va_list ap;
	gchar buff[512];

	va_start(ap, fmt);
	g_vsnprintf(buff, sizeof(buff), fmt, ap);
	va_end(p);

	gnome_appbar_set_status(GNOME_APPBAR(app_bar), buff);
}

static void grow_cb(GtkWidget *widget, void *data)
{
	if (status_area->allocation.width > 90)
		return;
	gtk_widget_set_usize(status_area,
			     status_area->allocation.width + 10, -1);
	show_status(_("Grow"));
}

static void shrink_cb(GtkWidget *widget, void *data)
{
	if (status_area->allocation.width < 60)
		return;
	gtk_widget_set_usize(status_area,
			     status_area->allocation.width - 10, -1);
	show_status(_("Shrink"));
}

static void quit_cb(GtkWidget *widget, void *data)
{
	gtk_main_quit();
}

static GnomeUIInfo file_menu[] = {
	{ GNOME_APP_UI_ITEM, N_("_Grow"), N_("Grow status area"),
	  grow_cb, NULL, NULL, GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_NEW,
	  'g', GDK_CONTROL_MASK, NULL },
	{ GNOME_APP_UI_ITEM, N_("_Shrink"), N_("Shrink status area"),
	  shrink_cb, NULL, NULL, GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_NEW,
	  's', GDK_CONTROL_MASK, NULL },
	{ GNOME_APP_UI_ITEM, N_("E_xit"), N_("Exit the program"),
	  quit_cb, NULL, NULL, GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_NEW,
	  'q', GDK_CONTROL_MASK, NULL },

	GNOMEUIINFO_END
};

static GnomeUIInfo main_menu[] = {
	GNOMEUIINFO_SUBTREE(N_("_File"), file_menu),
	GNOMEUIINFO_END
};

static void read_ready()
{
	gchar buff[4096];

	read(fd, buff, sizeof(buff));
	gdk_input_remove(read_tag);
	close(fd);

	shrink_cb(status_area, NULL);
	fprintf(stderr, _("The status bar should say \"Shrink\"\n"));
}

gboolean start_connect(char *host, gint port)
{
	struct sockaddr_in addr;

	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	if (atoi(host) > 0) {
		addr.sin_addr.s_addr = inet_addr(host);
		if (addr.sin_addr.s_addr == -1) {
			show_status(_("Invalid address %s\n"), host);
			return FALSE;
		}
	} else {
		struct hostent *host_ent;

		host_ent = gethostbyname(host);
		if (host_ent == NULL) {
			show_status(_("Cannot resolve host name %s\n"), host);
			return FALSE;
		}
		memcpy(&addr.sin_addr, host_ent->h_addr, host_ent->h_length);
	}

	addr.sin_port = htons(port);

	fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd < 0) {
		show_status(_("Error creating socket: %s\n"), g_strerror(errno));
		return FALSE;
	}
	if (fcntl(fd, F_SETFD, 1) < 0) {
		show_status(_("Error setting socket close-on-exec: %s\n"),
			    g_strerror(errno));
		return FALSE;
	}
	if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
		show_status(_("Error connecting to host: %s\n"),
			    g_strerror(errno));
		close(fd);
		return FALSE;
	}
	read_tag = gdk_input_add(fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
				 (GdkInputFunction)read_ready, NULL);

	return TRUE;
}

static gint small_window;
struct poptOption options[] = {
	{ "simple", 's', POPT_ARG_NONE, &small_window, 0, NULL, "Simple main window" },
	POPT_AUTOHELP
	{ NULL, '\0', 0, NULL }
};

static GtkWidget *build_main_window()
{
	GtkWidget *app;
	gint y;

	if (small_window) {
		app = gtk_label_new(_("Simple Window"));
		gtk_widget_set_usize(app, 300, 200);
	} else {
		app = gtk_vbox_new(FALSE, 3);
		gtk_widget_show(app);
		for (y = 0; y < 10; y++) {
			GtkWidget *hbox;
			gint x;

			hbox = gtk_hbox_new(FALSE, 3);
			gtk_widget_show(hbox);
			gtk_box_pack_start(GTK_BOX(app), hbox, FALSE, TRUE, 0);
			for (x = 0; x < 20; x++) {
				GtkWidget *btn;
				gchar text[32];

				g_snprintf(text, sizeof(text), "%d:%d", x, y);
				btn = gtk_button_new_with_label(text);
				gtk_widget_show(btn);
				gtk_box_pack_start(GTK_BOX(hbox),
						   btn, FALSE, TRUE, 0);
			}
		}
	}
	return app;
}

int main(int argc, char *argv[])
{
	GtkWidget *app_window;
	GtkWidget *label;
	GtkWidget *vsep;

	bindtextdomain("statusbar", GNOMELOCALEDIR);
	textdomain("statusbar");

	gnome_init_with_popt_table("statusbar", "0.1",
				   argc, argv, options, 0, NULL);
	app_window = gnome_app_new("statusbar", _("Statusbar Bug?"));
	gtk_widget_realize(app_window);
	gtk_signal_connect(GTK_OBJECT(app_window), "delete_event",
			   GTK_SIGNAL_FUNC(quit_cb), NULL);
	gnome_app_create_menus(GNOME_APP(app_window), main_menu);

	gnome_app_set_contents(GNOME_APP(app_window), build_main_window());

	/* Build appbar with drawing area
	 */
	app_bar = gnome_appbar_new(FALSE, TRUE, GNOME_PREFERENCES_NEVER);
	gnome_app_set_statusbar(GNOME_APP(app_window), app_bar);
	gnome_app_install_menu_hints(GNOME_APP(app_window), main_menu);

	label = gtk_label_new("A Label");
	gtk_widget_show(label);
	gtk_box_pack_start(GTK_BOX(app_bar), label, FALSE, TRUE, 0);

	vsep = gtk_vseparator_new();
	gtk_widget_show(vsep);
	gtk_box_pack_start(GTK_BOX(app_bar), vsep, FALSE, TRUE, 0);

	status_area = gtk_drawing_area_new();
	gtk_signal_connect(GTK_OBJECT(status_area), "expose_event",
			   GTK_SIGNAL_FUNC(status_area_cb), NULL);
	gtk_widget_set_usize(status_area, 100, -1);
	gtk_widget_show(status_area);
	gtk_box_pack_start(GTK_BOX(app_bar), status_area, FALSE, TRUE, 0);

	/* Show application window
	 */
	gtk_widget_show(app_window);

	start_connect("localhost", 13);

	gtk_main();
	return 0;
}




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]