[Testcase] Re: 32bit scrolling problems in Gtk+ 1.3.1



I've isolated the problem to the way the innermost scrolling window's
size was being set.

I was calling gdk_window_new () with a width of 1 and afterwards resizing
the window to a larger width via gtk_queue_resize ().  All this is done in
the realize function.

The attached program demonstrates the problem.  GtkFoo is a simple widget
with two locations of interest: the #defines and the
gtk_foo_realize () function.  You can compile it by using the command:

  gcc -o gtkfoo gtkfoo.c `gtk-config-2.0 --cflags --libs`

As is, it should crash immediately and report an Xlib BadValue error
(integer parameter out of range).  By changing the INITIAL_WINDOW_WIDTH
#define and recompiling, you should get different behaviors:
  1 - 32767:     Doesn't refresh properly when scrolling
  32768 - 65535: Scrolling works 
  65536:         Xlib BadValue error (XCreateWindow is being passed
                   a width parameter of 65536)
  65537+:        Scrolling works okay I'm guessing (entire range not
                   tested)

The refresh problems were what I was noticing.  If you change
INITIAL_WINDOW_WIDTH to 1, recompile, then drag the scrollbar to
the center and click on the arrows back and forth, it's apparent
scrolling is not working properly.

GtkLayout didn't exhibit the refresh problem because it was calling
gdk_window_new () with the actual full width.  If you modify
testgtk.c's gtk_layout_set_size () call in create_layout () so that the
width = 65536, the same BadValue crash will occur in the Layout test.

- David

On 4 Sep 2000, Owen Taylor wrote:

> 
> [ BTW - questions about GTK+-1.3.1 might be better or gtk-devel-list. ]
> 
> If you can reproduce it with a sample program, I'll take a look.
> I haven't seen any such problem, but there should be nothing
> you have to do special - it is supposed to "just work".
> 
> Regards,
>                                         Owen
> 
> "David A. Bartold" <foxx mail utexas edu> writes:
> 
> > I am trying to port an audio waveform widget to Gtk+ 1.3.1 and in the
> > process, convert its scrolling method to take advantage of the 32-bit
> > window size emulation for X11. (i.e. create a big window inside of a
> > smaller one and move the inner window around to scroll).
> > 
> > I am only scrolling in the horizontal direction.
> > 
> > Unfortunately, the scrolling only works properly near the beginning and
> > end of the range (about 16384 from either extent of the large
> > window).  In the middle of those extremes, scrolling the window using
> > gdk_window_move causes the widget contents to move to the left
> > (regardless of the actual direction of the scroll), possibly causes a
> > refresh of exposed portions, then moves the contents back to the right,
> > and finally possibly causing another refresh.  If I refresh the window
> > contents (i.e. by switching workspaces), the widget refreshes properly.
#define INITIAL_WINDOW_WIDTH 65536
#define WINDOW_WIDTH         200000

#include <gtk/gtk.h>


#define GTK_TYPE_FOO            (gtk_foo_get_type ())
#define GTK_FOO(obj)            (GTK_CHECK_CAST ((obj), GTK_TYPE_FOO, GtkFoo))
#define GTK_FOO_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_FOO, GtkFooClass))
#define GTK_IS_FOO(obj)         (GTK_CHECK_TYPE ((obj), GTK_TYPE_FOO))
#define GTK_IS_FOO_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FOO))


typedef struct _GtkFoo      GtkFoo;
typedef struct _GtkFooClass GtkFooClass;


struct _GtkFoo
{
  GtkWidget       parent;

  /* Just inside of bevel */
  GdkWindow      *middle_window;

  /* The innermost window that is moved to scroll its contents */
  GdkWindow      *inner_window;

  gint            inner_x;
  gint            inner_y;
  gint            inner_width;
  gint            inner_height;
};

struct _GtkFooClass
{
  GtkWidgetClass  parent_class;
};


GtkType    gtk_foo_get_type      (void);
GtkWidget *gtk_foo_new           (void);
void       gtk_foo_set_position  (GtkFoo *foo,
                                  gint32  position);


static void gtk_foo_class_init   (GtkFooClass *klass);
static void gtk_foo_init         (GtkFoo      *foo);
static void gtk_foo_realize      (GtkWidget   *widget);
static void gtk_foo_resize_event (GtkFoo      *foo,
                                  gpointer     data);
static gint gtk_foo_expose_event (GtkWidget   *widget,
                                  GdkEventExpose *event,
                                  gpointer     data);

static GtkWidgetClass *parent_class;


GtkType
gtk_foo_get_type (void)
{
  static GtkType foo_type = 0;
  
  if (!foo_type)
    {
      static const GtkTypeInfo foo_info =
      {
        "GtkFoo",
        sizeof (GtkFoo),
        sizeof (GtkFooClass),
        (GtkClassInitFunc) gtk_foo_class_init,
        (GtkObjectInitFunc) gtk_foo_init,
	/* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
        (GtkClassInitFunc) NULL
      };
      
      foo_type = gtk_type_unique (GTK_TYPE_WIDGET, &foo_info);
    }
  
  return foo_type;
}


GtkWidget *
gtk_foo_new (void)
{
  return GTK_WIDGET (gtk_type_new (GTK_TYPE_FOO));
}


void
gtk_foo_set_position (GtkFoo *foo,
                      gint32  position)
{
  foo->inner_x = -position;

  g_print ("gtk_foo_set_position (): Move event: x = %i, y = %i\n", foo->inner_x, foo->inner_y);

  gdk_window_move (foo->inner_window, foo->inner_x, foo->inner_y);
}


static void
gtk_foo_class_init (GtkFooClass *klass)
{
  GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  g_print ("gtk_foo_class_init ()\n");

  parent_class = gtk_type_class (GTK_TYPE_WIDGET);
  widget_class->realize = gtk_foo_realize;
  /* widget_class->unrealize = gtk_foo_unrealize; */
}


/* Fill rectangle (x1, y) -> (x2, y + height - 1), clip if necessary. */
static void
fill_rect (GdkWindow *win, GdkGC *gc, gint32 start, gint32 stop,
           gint32 x1, gint32 x2, gint32 y, gint32 height)
{
  if (x2 < x1 || x1 > stop || x2 < start)
    return;

  x1 = MAX (x1, start);
  x2 = MIN (x2, stop);

  gdk_draw_rectangle (win, gc, TRUE, x1, y, x2 - x1 + 1, height);
}


static void
gtk_foo_realize (GtkWidget *widget)
{
  GtkFoo *foo = GTK_FOO (widget);
  GdkWindowAttr attr;
  gint attr_mask;

  g_print ("gtk_foo_realize ()\n");

  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

  attr.x = widget->allocation.x;
  attr.y = widget->allocation.y;
  attr.width = widget->allocation.width;
  attr.height = widget->allocation.height;
  attr.wclass = GDK_INPUT_OUTPUT;
  attr.visual = gtk_widget_get_visual (widget);
  attr.colormap = gtk_widget_get_colormap (widget);
  attr.window_type = GDK_WINDOW_CHILD;
  attr.event_mask = GDK_EXPOSURE_MASK;
  attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;

  widget->window =
    gdk_window_new (gtk_widget_get_parent_window (widget), &attr, attr_mask);
  gdk_window_set_user_data (widget->window, foo);
  widget->style = gtk_style_attach (widget->style, widget->window);
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);

  attr.x += widget->style->xthickness;
  attr.y += widget->style->ythickness;
  attr.width -= widget->style->xthickness * 2;
  attr.height -= widget->style->ythickness * 2;
  attr.event_mask = gtk_widget_get_events (widget);
  foo->middle_window =
    gdk_window_new (widget->window, &attr, attr_mask);
  gdk_window_set_user_data (foo->middle_window, foo);
  widget->style = gtk_style_attach (widget->style, foo->middle_window);
  gtk_style_set_background (widget->style, foo->middle_window, GTK_STATE_NORMAL);

  attr.x = 0;
  attr.y = 0;
  attr.width = INITIAL_WINDOW_WIDTH;
  attr.height = widget->allocation.height;
  attr.event_mask = gtk_widget_get_events (widget);
  attr.event_mask |= GDK_EXPOSURE_MASK |
                     GDK_SCROLL_MASK;
  foo->inner_window =
    gdk_window_new (foo->middle_window, &attr, attr_mask);
  gdk_window_set_user_data (foo->inner_window, foo);
  widget->style = gtk_style_attach (widget->style, foo->inner_window);
  gtk_style_set_background (widget->style, foo->inner_window, GTK_STATE_NORMAL);

  gdk_window_show (foo->inner_window);
  gdk_window_show (foo->middle_window);
  gdk_window_show (widget->window);

  /* Resize window to full size. */
  gtk_widget_queue_resize (widget);

  gtk_signal_connect (GTK_OBJECT (foo),
                      "size_allocate", gtk_foo_resize_event,
                      GTK_OBJECT (foo));

  gtk_signal_connect (GTK_OBJECT (foo),
                      "expose_event", GTK_SIGNAL_FUNC (gtk_foo_expose_event),
                      GTK_OBJECT (foo));
}


static void
gtk_foo_resize_event (GtkFoo *foo, gpointer data)
{
  gint width, height;

  g_print ("gtk_foo_resize_event ()\n");

  foo->inner_height = GTK_WIDGET (foo)->allocation.height;

  g_print ("Resize inner: width: %i height: %i\n",
           foo->inner_width, foo->inner_height);

  gdk_window_move_resize (foo->inner_window,
                          foo->inner_x, foo->inner_y,
                          foo->inner_width, foo->inner_height);

  width = GTK_WIDGET (foo)->allocation.width -
          GTK_WIDGET (foo)->style->xthickness * 2;
  height = GTK_WIDGET (foo)->allocation.height -
           GTK_WIDGET (foo)->style->ythickness * 2;

  g_print ("Resize middle: width: %i height: %i\n", width, height);

  gdk_window_move_resize (foo->middle_window,
                          GTK_WIDGET (foo)->style->xthickness,
                          GTK_WIDGET (foo)->style->ythickness,
                          width, height);
}


/* Paint selection areas then call a function to paint the wave data. */
static gint
gtk_foo_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer userdata)
{
  GtkFoo *foo = GTK_FOO (widget);
  GdkGC *white_gc, *black_gc;
  gint32 scroll_width;
  gint32 start, stop, height;
  gint32 start_x, stop_x, start_y, stop_y;
  gint32 i, j;

  if (event->window == widget->window)
    gtk_draw_shadow (widget->style, widget->window,
                     GTK_STATE_NORMAL, GTK_SHADOW_IN,
                     0, 0,
                     widget->allocation.width,
                     widget->allocation.height);

  if (event->window != foo->inner_window)
    return FALSE;

  start = event->area.x;
  stop = start + event->area.width - 1;

  if (stop < -foo->inner_x)
    return FALSE;

  scroll_width = widget->allocation.width - widget->style->xthickness * 2;

  if (start > scroll_width - foo->inner_x - 1)
    return FALSE;

  start = MAX (start, -foo->inner_x);
  stop = MIN (stop, scroll_width - foo->inner_x - 1);

  g_print ("gtk_foo_expose(): Drawing from [x | %i <= x <= %i]\n", start, stop);

  white_gc = widget->style->white_gc;
  black_gc = widget->style->black_gc;

  height = GTK_WIDGET (foo)->allocation.height -
           GTK_WIDGET (foo)->style->ythickness * 2;

  start_x = start / 20;
  stop_x = (stop + 19) / 20;
  start_y = 0;
  stop_y = (height + 19) / 20;

  for (i = start_y; i <= stop_y; i++)
    for (j = start_x; j <= stop_x; j++)
      fill_rect (foo->inner_window, ((i+j)&1) ? white_gc : black_gc,
                 start, stop, j * 20, j * 20 + 19, i * 20, 20);

  return FALSE;
}
 

static void
gtk_foo_init (GtkFoo *foo)
{
  g_print ("gtk_foo_init ()\n");

  foo->inner_window = NULL;
  foo->middle_window = NULL;

  foo->inner_x = 0;
  foo->inner_y = 0;
  foo->inner_width = WINDOW_WIDTH;
  foo->inner_height = 10;
}


void
changed_cb (GtkWidget *widget, gpointer userdata)
{
  GtkFoo *foo = GTK_FOO (userdata);

  g_print ("changed_cb ()\n");

  gtk_foo_set_position (foo, (gint) GTK_ADJUSTMENT (widget)->value);
}


gint
quit_cb (GtkWidget *widget, gpointer userdata)
{
  g_print ("quit_cb ()\n");

  gtk_main_quit ();

  return FALSE;
}


main (int argc, char *argv[])
{
  GtkObject *adjust;
  GtkWidget *window;
  GtkWidget *foo;
  GtkWidget *hscroll;
  GtkWidget *vbox;

  gtk_init (&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  vbox = gtk_vbox_new (FALSE, 0);
  foo = gtk_foo_new ();
  gtk_widget_set_usize (GTK_WIDGET (foo), 100, 100);
  adjust = gtk_adjustment_new (0.0, 0.0, (double) WINDOW_WIDTH, 10.0, 100.0, 1.0);
  hscroll = gtk_hscrollbar_new (GTK_ADJUSTMENT (adjust));

  gtk_container_add (GTK_CONTAINER (window), vbox);
  gtk_box_pack_start (GTK_BOX (vbox), foo, TRUE, TRUE, 0);
  gtk_box_pack_end (GTK_BOX (vbox), hscroll, FALSE, FALSE, 0);

  gtk_widget_show_all (window);
  gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                      GTK_SIGNAL_FUNC (quit_cb), NULL);

  gtk_signal_connect (GTK_OBJECT (adjust), "value_changed",
                      GTK_SIGNAL_FUNC (changed_cb), foo);

  gtk_main ();

  return 0;
}


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