[otaylor redhat com] Re: Multiple Displays

To give some context to the email I just forwarded, here is
my original mail that gave my ideas about the situation.

--- Begin Message ---

>From an API standpoint, the first thing to do is to add a GdkDisplay *
object that represents a display.

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. 

This is the simplest from the point of view of the application
programmer, though it has the disadvantage that things like
X selections and fonts that are shared across displays don't
get handled as well as they could be.

The other way to do it would be to have some call like 
gdk_display_list_root_windows(), and associate things like
the default visual with the GdkWindow * for the root window.
I don't really want to have a separate GdkScreen object.

This object should be a GObject (in particular, setting user
data on this will probably be a pretty common operation as a 
way of storing per-display data.)

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

Then you need to decide whether the initialization API should

 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

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+.

(The amount of global data in GTK+ will decrease a lot with
GObject, but there will still be some there.)

I made a list of functions that need fixing for multiple displays
some time ago and put it into:


There are quite a few functions in there (49 of them) that need
a variant for multiple display use -

For instance:

  void gdk_beep (void);

Needs to have another function:

  void gdk_display_beep (GdkDisplay *display);

Added. And:

  GdkCursor* gdk_cursor_new		 (GdkCursorType	  cursor_type);

needs to have a function:

  GdkCursor* gdk_cursor_new_for_display (GdkDisplay    *display,
                                         GdkCursorType  cursor_type);
Added. Or possibly:

  gdk_display_create_cursor (GdkDisplay    *display,
                             GdkCursorType *cursor_type);

But I think that less nice, since it breaks up the logical
organization of all the GdkCursor functions together. I think my
general strategy would be that:

 * Functions like gdk_beep() that are not currently namespaced
   beyond "gdk" should map to gdk_display_*

 * Constructors should get a _for_display() variant.

 * Namespaced functions that are not constructors such as
   gdk_visual_get_best_depth() probably should get _for_display()
   variant, though I'm a little less sure about this then
   the previous bullet.

The list needs some updating. Scanning it over, the things I 
see are:

 GdkRegion*     gdk_region_new	    (void);

Regions were never per display

 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 

 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.

 gint         gdk_im_ready	   (void);
 void         gdk_im_end	   (void);
 GdkIC*       gdk_ic_new	   (GdkICAttr 		*attr,
				    GdkICAttributesType mask);

All this IM support code in GDK is going away, though the replacement
IM module code will need vetting for proper multiple display

 GList *gdk_input_list_devices		    (void);
 void gdk_input_set_source		    (guint32 deviceid,
					     GdkInputSource source);
 gint gdk_input_set_mode		    (guint32 deviceid,
 					     GdkInputMode mode);
 void gdk_input_set_axes                    (guint32 deviceid,
 					     GdkAxisUse *axes);
 void gdk_input_set_key			    (guint32 deviceid,
					     guint   index,
					     guint   keyval,

This gets much better with the new extended input device API 
which uses GdkDevice * instead of device IDs - you only need
to add to:

/* Returns a list of GdkDevice * */	  
GList *        gdk_devices_list         (void);

GList *        gdk_devices_list_for-display (GdkDisplay *display);

(Or, perhaps, simply have gdk_devices_list (GdkDisplay *display),
since there is no backwards compatibility concern here.)

gint	   gdk_text_property_to_text_list (GdkAtom encoding, gint format,
					   guchar *text, gint length,
					   gchar ***list);
void	   gdk_free_text_list		  (gchar **list);
gint	   gdk_string_to_compound_text	  (gchar *str,
					   GdkAtom *encoding, gint *format,
					   guchar **ctext, gint *length);
void	   gdk_free_compound_text	  (guchar *ctext);

I think the only dependency here is on the GdkAtom, so these
probably don't need per-display handling if GdkAtom is done
as above.

 void gdk_key_repeat_disable (void);
 void gdk_key_repeat_restore (void);

These functions are scheduled for death.

 void      gdk_error_trap_push           (void);
 gint      gdk_error_trap_pop            (void);

I'm sure there are also functions that need to be added to the
list - one that comes to mind is:

 PangoContext *gdk_pango_context_get          (void);

Though any additions are new API, and are thus candidates for
treating like discussed for gdk_devices_list() above.

So, that's an overview of what needs to be done at the GDK API
level. In GTK+, the API changes should be smaller:

*- You need to be able to create GtkWindow and all descendents,
   such as GtkDialog for particular displays. Also GtkInvisible.

   This could be done push/pop() like gtk_widget_push_colormap(),
   though I think an explicit API - something like:

     GtkWidget *gtk_window_new_for_root (GdkWidow *root);

   may be better.

 * GtkClipboard needs to become per-display. 

 * Selections need to be per-display. DND should be OK, since 
   we have GdkDragContext to hold the display pointer.

 * There probably should be convenience functions in GtkWidget for
   getting anything that is frequently per-display, such as:

    GtkClipboard *gtk_widget_widget_get_clipboard (GtkWidget *widget,
                                                   GdkAtom    selection);

   The display for a widget needs to be formalized - the simplest
   thing to say is that it is undefined/NULL until it is added into
   widget heirarchy, and then it is the display for the GtkWindow
   at the top of the hierarchy.

There probably is some more stuff which isn't occuring to me right

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.

 - All uses of such global variables such as gdk_display/GDK_DISPLAY(),
   GDK_ROOT_PARENT(), etc, needs to to be located and eliminated
   in one way or another.

 - All the GDK and GTK+ code needs to be examined for use of the
   functions that are getting per-display variants, and the
   per-display variants need to be used instead.

 - The assumption that a GdkPixmap can be used with any visual
   of the same depth needs to be rooted out. (It must be used
   with the same display and in fact screen.) 

   I know that the GtkStyle code, and also (the deprecated, perhaps
   going to be removed) GtkTree code have this assumption in them.

And of course, test cases need to be written that create widget on
different displays, different screens, with different depths, to find
all the assumptions that are being made that were missed on
the first pass.

OK, I know that is both a lot of stuff to take in and not nearly 
enough stuff to completely describe everything that needs to
be done, but I hope it will provide a start at getting this

I think the strategy I would suggest is;
 1) Write up detailed plan with a full list of planned API additions
    and changes. (Feel free to steal from this message as much as you
    want), send that to gtk-devel-list. I'll try to get back with
    comments on that quickly. 

    Going over GDK for this should provide some familiarity with
    the code.

    Feel free to ask me (or gtk-devel-list) questions as needed;
    as mentioned above, I'll be gone for the rest of this week,
    but may get a chance to check email occasionally.

 2) Make an initial pass at implementation, with a concentration
    on the public API.

 3) Finish up implementation, test.


P.S. - One thing to idly think about that for future versions of GTK+
       we might eventually want to have the ability to open multiple
       "displays" on different targets; e.g., Linux Framebuffer and
       X. This is a very long term goal, and I wouldn't worry about it
       much, but as a philosophical thing, it might be nice if the API
       didn't rule it out.

--- End Message ---

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