Re: Multihead support for GTK



Erwann Chenede <Erwann Chenede ireland sun com> writes:

> Matt Keenan and myself (Erwann Chenede) are looking into multihead
> support for gtk. We are not gtk/gdk expert but we have a good amount
> of experience in Motif and XView toolkits.

Understood.

> Below, you'll find a quick description of how the multihead handling
> was implemented in XView & Motif and after my general idea on howto
> implement it in gdk.
> 
> Our intent, here, is to start the discussion (as we are not gdk/gtk
> expert) and try to reach a consensus on what could be a good design
> for multihead handling.
> 
> If you feel that this discussion belongs more to gtk-devel-list feel
> free to forward it to the list.

Done; we try to have almost all discussions about GTK+ design decisions
in public to encourage participation, and also so that there is a
record of what the motivivations are.

[ discussion of Xview and Motif trimmed ]

Another idea for handling display information is what Java does.
The Java model is:

 GraphicsEnvironment
   GraphicsDevice            == screen
     GraphicsConfiguration   == visual

The main thing to note here is that a JFrame is associated with
a particular GraphicsConfiguration, and the screen information
is derived from that.

Since every widget already has an associated Colormap, and colormaps
are associated with visuals, and visuals with display, we already
have sufficient information to know what display a widget is.
So, the GTK+ interfaces do not really need to be extended, though
they may fixed some.

(The one thing to be careful of here is that I think visuals _are_
allowed to be present on multiple screens though they don't
have to be. So you might have to have a number of GTK+ visuals
correspond to each X visuals in some cases.)

> Here is my idea on how multihead support could be design.  Yeap,
> there's not much details in it as I wanted to know if it made sense
> for you or if I've lost it completely...
> 
> For the "Multiheadization" of gdk, each GdkWindow we'll need to know
> which display and which screen the gdkDrawable is referring to.

Currently, drawables keep a pointer to the display, even though
it is the same for all drawables. 
 
> We'll need to keep track of all the display opened (with associated
> data, visual, num of screen, etc) and each gdk "object" (from beep
> to windows) while need info about both display and screen number to
> be displayed on.

Note this shouldn't be all that much extra information or (any
at all) for many GDK objects.

> One approached could be to create a GdkConnection to abstract the concept of
> Display and screen (and other info I guess). All screen-dependent gdk object 
> would inherit that GdkConnection.
> All the multihead support could be grouped around the GdkConnection object.
> All multihead interaction would go through this object.

> e.g to set the desired screen :
> 
> gdk_screen_set(GDK_WINDOW(window)->connection,0).
> 
> If not specified explicitly GdkConnection would default the current screen.
> This approach could minimize the API change needed.
> The API change would be concentrated at the gdk level and at the Gtk 
> level would not have to change much.

I don't think that adding the capability to move GDK objects between 
displays is really all that necessary. I think the current paradigm
where a GDK object corresponds closely to an X object is fine;
the separation layer that allows some independence is at the GTK+/GDK 
split.
 
> For the initialization, It could be good to have a list of X display open 
> to minimize redundancy within the GdkConnection objects.

> > I'm not sure what the best approach is to handling multiple screens
> > on the same display - one approach would be to simple force
> > a separate display connection for each screen.
> 
> I don't if it is a good idea as a display (according to the Xlib docs)
> is an abstraction representing the input (keyboard and pointing device)
> and output (one or more screen). 
> 
> So if multihead display is supported an app should be able to display
> widgets not only on multiple screens but also on multiple machine 
> as an Xserver usually only as one display...

Hmmm, I hadn't thought about the input part of it. (I've never
actually used a system with multiple screens and not Xinerama.)
You are probably right that we need to keep the idea of Displays
and screens somewhat separate, if we want to support multiple
screens at all. And the overhead of supporting multiple screens
isn't really that much that it is worth worrying about a lot.

The only real problem with multple screen support is that it
will make the APIs for things like listing visuals rather more
complicated.

[...]

> > The way it should probably work is that the object should be defined
> > completely in the port-specific directory and the structure should not
> > be public - this means that it won't be possible to derive from
> > GdkDisplay, but I don't think that really makes a lot of sense
> > anyways.
> 
> I agree with you but, should we try to support that for windows also 
> or will it need a specific design and implem. for each port ?
> Anyway I guess our main concern is to make it work on X...

I can't see what the relevance is to Windows. As far as I know,
the only support for multiple monitors is simply extending the
desktop area over multiple monitors. So, I think there will just
be a simple stub implementation of GdkDisplay in the windows
(etc.) port.

Basically, you only need to:

 - Design the API
 - Implement for X
 - Make sure that it is implementable for stubs for systems that 
   don't support the concept of multiple displays.

> > Then you need to decide whether the initialization API should
> > be:
> > 
> >  gtk_init (&argc, &argv);
> > 
> >  GdkDisplay *gdk_display_get_default ();
> >  GdkDisplay *gdk_display_open        (const char *display_name);
> >  void        gdk_display_close       (GdkDisplay *display);
> > 
> > Or, alternatively:
> > 
> >  GdkDisplay *gtk_init_for_display (&argc, &argv, const char *display_name);
> >  void        gdk_display_close       (GdkDisplay *display);
> > 
> > That is, whether there is a separate concept of initializating
> > GDK and opening a display or not. My guess would be that the
> > first is better, since there are some parts of GDK that are
> > cross display, such as:
> > 
> >  - Debug options
> >  - atom assignment
> >  - Xlib error handling
>
> Yes, the display opening has to be separated from the GDK initialization
> as a developer might went to open an extra display at anytime while the
> app is running.

Well, that's not really the issue, since the idea is that if
you needed to do that, you would just call gtk_init_for_display()
again to initialize another copy of the toolkit. But you are
right that calling gdk_init_* in the middle of an app may seem
pretty strange, and you may not have the argv/argc at that point.
 
> > At the same time, you need to decide whether you want to try
> > and make the GDK mutex used for threading lock all displays
> > or only a single one. My guess is that you need to lock all
> > displays, since it is also used to lock global data in GTK+.
> 
> I don't really understand the problem here, could you give me 
> some background on threading in GDK.

GDK threading is very simple right now. You might say that GTK+
is thread-aware rather than thread safe.

Basically the idea is that there is a global lock for all
of GDK and GTK+ you acquire manually. See question 5.2
in the faq (http://www.gtk.org/faq) for some more details.

As GObject moves into the GLib for 2.0, it will be following
the GLib rules for thread safety. (Internal structures locked
so that you only have to worry about locks on objects you
are sharing between threads.)

However, increased automatic thread safety for GDK and GTK+
is a long term goal, if one at all. The rich signal/callback
system in GTK+ I think would interfere with an Xt/Motif 
style approach where there is an automatically grabbed global
lock around everything, and anything more fine-grained would
have to be approached with a lot of caution.

Anyways, the simplest, and I think, for now, best approach
for multi-head support is simply to stick with the existing
global-lock strategy for now.

[...]

> >  GdkAtom gdk_atom_intern            (const gchar *atom_name,
> >                                      gint         only_if_exists);
> >  gchar*  gdk_atom_name              (GdkAtom      atom);
> > 
> > A suggestion of Havoc's that probably is the right way to do
> > it here is to do atom assignment within the library, and then
> > itern as necessary for each display, with an internal call such
> > as:
> > 
> >  Atom gdk_atom_to_x_atom (GdkDisplay *display,
> >                           GdkAtom    *atom);
> > 
> > This will maintain a lot more backwards compatibility than forcing
> > all code to worry about the fact that atoms are per-display and
> > don't map 1 <=> 1 onto strings.
> >
> 
>  I don't get it. Are you saying that we have to have only one set of 
>  atom for every screen of every display ? Why ? What are the problems 
>  with backward compatibility ? 

Current code tends to assume that an atom is simply an integer
representation of a string, not an integer representation of
a string on the current display. A lot of code caches atoms
with things like:

 static GdkAtom my_atom = None;
 
Now, such caching is not particularly usefull, since gdk_atom_intern()
keeps an internal cache, but it is also widespread.

Also, having to say:

 gdk_display_atom_intern (gdk_window_get_display (widget->window),
                          "MY_STRING", FALSE);

Compared to:

 gdk_atom_intern ("MY_STRING", FALSE);

Is fairly inconvenient. There is also the problem that in some cases
in the GTK+ API atoms are used in places that do not necessarily
correspond to a fixed display. For instance, to declare the target
types for a widget when it owns a selection, you use:

void     gtk_selection_add_target    (GtkWidget            *widget,
				      GdkAtom               selection,
				      GdkAtom               target,
				      guint                 info);

And the display for the widget could be changed at a later point.

(This basically deprecated as an public API as of GTK+-2.0, but
used pretty widely in existing code.)

The idea that Havoc had was that you have two layers of atom assignment;
the atoms that an application sees are assigned within the toolkit.
And then, these are translated on demand to atoms for the display.

The only real downside I can see for this is that people might 
occasionally expect atoms to correspond to the atoms they see 
not through GTK+, and they might get confused; it makes debugging
a little bit harder. But I think this is a smaller burden then
the increased inconvenience of having to deal with the fact 
that atoms are different for each display.

We can also design it so in the case when only a single display
is open, the GTK+ internal atoms match the atoms on the server,
though this might prevent potential problems from being caught
upfront.

> > The API changes are the part that is important to get done real
> > soon, but there obviously is a lot of underlying implementation
> > work to be done as well.
> > 
> >  - Every use of global data in GTK+ and GDK needs to be examined
> >    to see if the data is really per-display, not global.
> > 
> >    Per display data probably usually should be made user data
> >    on the GdkDisplay object; by that or some other mechanism it
> >    needs to be cleaned up when the display is closed.
> 
> What do you really mean by user data ?

User data is a mechanism in the GTK+ object system that allows
arbitrary data to be attached to a widget, keyed by a string ID
or quark. 

For GtkObject, the primary interfaces are:

void	 gtk_object_set_data	     (GtkObject	     *object,
				      const gchar    *key,
				      gpointer	      data);
void	 gtk_object_set_data_full    (GtkObject	     *object,
				      const gchar    *key,
				      gpointer	      data,
				      GtkDestroyNotify destroy);
void	 gtk_object_remove_data	     (GtkObject	     *object,
				      const gchar    *key);
gpointer gtk_object_get_data	     (GtkObject	     *object,
				      const gchar    *key);
void	 gtk_object_remove_no_notify (GtkObject	     *object,
				      const gchar    *key);

With _by_id() variants for using quarks instead of strings. There
are similar function for GObject, though naming is still a 
little bit in flux.

The relevance of object data to this particular situation is that
quite a few things things need to be per-display. By exploiting
object data on the GdkDisplay object, we avoid:

 1) Having to add stuff into the GdkDisplay structure from
    multiple unrelated bits of code.

 2) Having to have lots of places where you keep a list mapping
    from resource to display.

> >    I know that the GtkStyle code, and also (the deprecated, perhaps
> >    going to be removed) GtkTree code have this assumption in them.
> 
> Yes, GtkStyle could create interesting problems as the Style is not stored
> in the X Server as for example X Resources are !

Well, this assumes that you want the Style settings to be per-display.

But making that assumption, probably the right way to do it is to
make the global structures in gtkrc.c per-display and then have
the concept of parsing in an RC file in the context of a particular
display. We could then store the name of the theme file on the
display in some fashion; in fact, there will be, in GTK+-2.0 a mechanism
for configuring global settings via properties, so we can just use
that setting. 

(Storing the RC file itself on the display is
problematical due to references to local resources like image files.)

Luckily, the theme code in GTK+ has always been structured
to allow different themes for different widgets, so there is no
fundemental problem with having different styles/theme-engines
and so forth per display.

Regards,
                                        Owen





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