Support for WM spec



Here is a patch that adds support for some stuff in the WM spec. Compared 
to my previous proposal I removed everything that isn't generally usefull
for application authors or Gtk+ internals.

This version uses

* _NET_WM_WINDOW_TYPE
 Only NORMAL, DIALOG, MENU and TOOLBAR are exported.
* _NET_WM_STATE_MODAL
 The other states (the ones havoc isn't working on) are not generally
 usefull for apps i think.
* _NET_WM_ICON_NAME
* _NET_WM_ICON  
 Using the same packing as the trolltech sample implementation.

I didn't combine the MODAL state with the window type hint, because
separating it fit better with the Gtk changes. All classes deriving from
GtkDialog get a DIALOG type, and all modal GtkWindows gets the MODAL
state.

I haven't exported the icon setting to GtkWindow yet, as there is
currently some discussion on it.

The floating window in GtkHandleBox has a TOOLBAR window type, this isn't
100% correct, but it's probably good enough.

Currently both the popup and the tearoff windows for GtkMenu has the MENU
type hint. I'm not sure if this is correct usage of that hint.

Comments?

/ Alex

Index: gdk/gdkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.h,v
retrieving revision 1.15
diff -u -p -r1.15 gdkwindow.h
--- gdk/gdkwindow.h	2001/01/26 21:11:57	1.15
+++ gdk/gdkwindow.h	2001/02/23 15:01:50
@@ -84,6 +84,28 @@ typedef enum
   GDK_HINT_WIN_GRAVITY = 1 << 6
 } GdkWindowHints;
 
+
+/* Window type hints.
+ * These are hints for the window manager that indicate
+ * what type of function the window has. The window manager
+ * can use this when determining decoration and behaviour
+ * of the window. The hint must be set before mapping the
+ * window.
+ *
+ *   Normal: Normal toplevel window
+ *   Dialog: Dialog window
+ *   Menu: Window used to implement a menu.
+ *   Toolbar: Window used to implement toolbars.
+ */
+typedef enum
+{
+  GDK_WINDOW_TYPE_HINT_NORMAL,
+  GDK_WINDOW_TYPE_HINT_DIALOG,
+  GDK_WINDOW_TYPE_HINT_MENU,
+  GDK_WINDOW_TYPE_HINT_TOOLBAR
+} GdkWindowTypeHint;
+
+
 /* The next two enumeration values current match the
  * Motif constants. If this is changed, the implementation
  * of gdk_window_set_decorations/gdk_window_set_functions
@@ -321,6 +343,10 @@ void	      gdk_window_set_hints	 (GdkWin
 					  gint		   max_width,
 					  gint		   max_height,
 					  gint		   flags);
+void          gdk_window_set_type_hint   (GdkWindow       *window,
+					  GdkWindowTypeHint hint);
+void          gdk_window_set_modal_hint  (GdkWindow       *window,
+					  gboolean         modal);
 void          gdk_window_set_geometry_hints (GdkWindow        *window,
 					     GdkGeometry      *geometry,
 					     GdkWindowHints    flags);
@@ -378,6 +404,8 @@ GdkEventMask  gdk_window_get_events	 (Gd
 void	      gdk_window_set_events	 (GdkWindow	  *window,
 					  GdkEventMask	   event_mask);
 
+gboolean      gdk_window_set_icon_list   (GdkWindow       *window,
+					  GList           *pixbufs);
 void	      gdk_window_set_icon	 (GdkWindow	  *window, 
 					  GdkWindow	  *icon_window,
 					  GdkPixmap	  *pixmap,
Index: gdk/x11/gdkwindow-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkwindow-x11.c,v
retrieving revision 1.105
diff -u -p -r1.105 gdkwindow-x11.c
--- gdk/x11/gdkwindow-x11.c	2001/02/03 16:31:35	1.105
+++ gdk/x11/gdkwindow-x11.c	2001/02/23 15:01:53
@@ -993,6 +993,98 @@ gdk_window_set_hints (GdkWindow *window,
 		     &size_hints);
 }
 
+/**
+ * gdk_window_set_type_hint:
+ * @window: A #GdkWindow
+ * @hint: A hint of the function this window will have
+ *
+ * The application can use this call to hint to the window
+ * manager about the functionallity of a window. The window manager
+ * can use this information to decide the decoration and behaviour
+ * of the window.
+ *
+ * The hint must be set before the window is mapped.
+ **/
+void
+gdk_window_set_type_hint (GdkWindow        *window,
+			  GdkWindowTypeHint hint)
+{
+  GdkAtom atom;
+  
+  g_return_if_fail (window != NULL);
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  switch (hint)
+    {
+    case GDK_WINDOW_TYPE_HINT_DIALOG:
+      atom = gdk_atom_intern ("_NET_WM_WINDOW_TYPE_DIALOG", FALSE);
+      break;
+    case GDK_WINDOW_TYPE_HINT_MENU:
+      atom = gdk_atom_intern ("_NET_WM_WINDOW_TYPE_MENU", FALSE);
+      break;
+    case GDK_WINDOW_TYPE_HINT_TOOLBAR:
+      atom = gdk_atom_intern ("_NET_WM_WINDOW_TYPE_TOOLBAR", FALSE);
+      break;
+    default:
+      g_warning ("Unknown hint %d passed to gdk_window_set_type_hint", hint);
+      /* Fall thru */
+    case GDK_WINDOW_TYPE_HINT_NORMAL:
+      atom = gdk_atom_intern ("_NET_WM_WINDOW_TYPE_NORMAL", FALSE);
+      break;
+    }
+
+  XChangeProperty (GDK_WINDOW_XDISPLAY (window),
+		   GDK_WINDOW_XID (window),
+		   gdk_atom_intern ("_NET_WM_WINDOW_TYPE", FALSE),
+		   XA_ATOM, 32, PropModeReplace,
+		   (guchar *)&atom, 1);
+}
+
+/**
+ * gdk_window_set_modal_hint:
+ * @window: A #GdkWindow
+ * @modal: TRUE if the window is modal, FALSE otherwise.
+ *
+ * The application can use this hint to tell the window manager
+ * that a certain window has modal behaviour. The window manager
+ * can use this information to handle modal windows in a special
+ * way.
+ *
+ * The #gdk_window_set_transient_for() function must be used to
+ * tell the window manager which window the window is modal to.
+ **/
+void
+gdk_window_set_modal_hint (GdkWindow *window,
+			   gboolean   modal)
+{
+  XEvent xev;
+  
+  g_return_if_fail (window != NULL);
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+  
+  xev.xclient.type = ClientMessage;
+  xev.xclient.serial = 0;
+  xev.xclient.send_event = True;
+  xev.xclient.window = gdk_root_window;
+  xev.xclient.display = gdk_display;
+  xev.xclient.window = gdk_root_window;
+  xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE);
+  xev.xclient.format = 32;
+  xev.xclient.data.l[0] = (modal) ? gdk_atom_intern ("_NET_WM_STATE_ADD", FALSE) : gdk_atom_intern ("_NET_WM_STATE_REMOVE", FALSE);
+  xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_MODAL", FALSE);
+  xev.xclient.data.l[2] = 0;
+  
+  XSendEvent (gdk_display, gdk_root_window, False,
+	      SubstructureRedirectMask | SubstructureNotifyMask,
+	      &xev);
+}
+
 void 
 gdk_window_set_geometry_hints (GdkWindow      *window,
 			       GdkGeometry    *geometry,
@@ -1162,7 +1254,15 @@ gdk_window_set_title (GdkWindow   *windo
 
   set_text_property (window, gdk_atom_intern ("WM_NAME", FALSE), title);
   if (!gdk_window_icon_name_set (window))
-    set_text_property (window, gdk_atom_intern ("WM_ICON_NAME", FALSE), title);
+    {
+      XChangeProperty (GDK_WINDOW_XDISPLAY (window),
+		       GDK_WINDOW_XID (window),
+		       gdk_atom_intern ("_NET_WM_ICON_NAME", FALSE),
+		       gdk_atom_intern ("UTF8_STRING", FALSE), 8,
+		       PropModeReplace, title,
+		       strlen (title));
+      set_text_property (window, gdk_atom_intern ("WM_ICON_NAME", FALSE), title);
+    }
 }
 
 void          
@@ -1744,6 +1844,122 @@ gdk_window_set_override_redirect (GdkWin
     }
 }
 
+
+/**
+ * gdk_window_set_icon_list:
+ * @window: The #GdkWindow toplevel window to set the icon of.
+ * @pixbufs: A list of pixbufs, of different sizes.
+ * @Returns: TRUE if the icons where set, false otherwise
+ *
+ * Sets a list of icons for the window. These are used to represent
+ * the window when it has been iconized. It is usually shown in
+ * an icon box or some sort of task bar. Which icon size is shown
+ * depends on the window manager (and it can be scaled), but
+ * setting several size icons can give better image quality since
+ * the icons might not need to be scaled. Ownership of the pixbufs
+ * and the list is transfered to this function.
+ *
+ * On the X11 backend this call might fail if the window manager
+ * doesn't support the Extended Window Manager Hints. Then this
+ * function returns FALSE, and the application should fall back
+ * to #gdk_window_set_icon().
+ **/
+gboolean
+gdk_window_set_icon_list (GdkWindow *window,
+			  GList     *pixbufs)
+{
+  guint *data;
+  guchar *pixels;
+  guint *p;
+  gint size;
+  GList *l;
+  GdkPixbuf *pixbuf;
+  gint width, height, stride;
+  gint x, y;
+  gint n_channels;
+  
+  g_return_val_if_fail (window != NULL, FALSE);
+  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return FALSE;
+
+#ifdef NO_GDK_WMSPEC_SUPPORTED_YET
+  if (!_gdk_wmspec_supported (gdk_atom_intern ("_NET_WM_ICON", FALSE)))
+    return FALSE;
+#endif
+  
+  l = pixbufs;
+  size = 0;
+  
+  while (l)
+    {
+      pixbuf = l->data;
+      g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
+
+      width = gdk_pixbuf_get_width (pixbuf);
+      height = gdk_pixbuf_get_height (pixbuf);
+      
+      size += 2 + width * height;
+
+      l = g_list_next (l);
+    }
+
+  data = g_malloc (size*4);
+
+  l = pixbufs;
+  p = data;
+  while (l)
+    {
+      pixbuf = l->data;
+      
+      width = gdk_pixbuf_get_width (pixbuf);
+      hheight = gdk_pixbuf_get_height (pixbuf);
+      stride = gdk_pixbuf_get_rowstride (pixbuf);
+      n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+      
+      *p++ = width;
+      *p++ = height;
+
+      pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+      for (y = 0; y < height; y++)
+	{
+	  for (x = 0; x < width; x++)
+	    {
+	      guchar r, g, b, a;
+	      
+	      r = pixels[y*stride + x*n_channels + 0];
+	      g = pixels[y*stride + x*n_channels + 1];
+	      b = pixels[y*stride + x*n_channels + 2];
+	      if (n_channels >= 4)
+		a = pixels[y*stride + x*n_channels + 3];
+	      else
+		a = 255;
+	      
+	      *p++ = a << 24 | r << 16 | g << 8 | b ;
+	    }
+	}
+
+      /* The X11 backend need not store the pixbufs,
+       * other backends might. */
+      g_object_unref (pixbuf);
+      
+      l = g_list_next (l);
+    }
+
+  XChangeProperty (GDK_WINDOW_XDISPLAY (window),
+		   GDK_WINDOW_XID (window),
+		   gdk_atom_intern ("_NET_WM_ICON", FALSE),
+		   XA_CARDINAL, 32,
+		   PropModeReplace,
+		   (guchar*) data, size);
+
+  g_list_free (pixbufs);
+  
+  return TRUE;
+}
+
 void          
 gdk_window_set_icon (GdkWindow *window, 
 		     GdkWindow *icon_window,
@@ -1806,6 +2022,12 @@ gdk_window_set_icon_name (GdkWindow   *w
   g_object_set_qdata (G_OBJECT (window), g_quark_from_static_string ("gdk-icon-name-set"),
 		      GUINT_TO_POINTER (TRUE));
 
+  XChangeProperty (GDK_WINDOW_XDISPLAY (window),
+		   GDK_WINDOW_XID (window),
+		   gdk_atom_intern ("_NET_WM_ICON_NAME", FALSE),
+		   gdk_atom_intern ("UTF8_STRING", FALSE), 8,
+		   PropModeReplace, name,
+		   strlen (name));
   set_text_property (window, gdk_atom_intern ("WM_ICON_NAME", FALSE), name);
 }  
 
Index: gtk/gtkdialog.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkdialog.c,v
retrieving revision 1.16
diff -u -p -r1.16 gtkdialog.c
--- gtk/gtkdialog.c	2001/01/09 17:45:33	1.16
+++ gtk/gtkdialog.c	2001/02/23 15:01:54
@@ -139,6 +139,9 @@ gtk_dialog_init (GtkDialog *dialog)
   separator = gtk_hseparator_new ();
   gtk_box_pack_end (GTK_BOX (dialog->vbox), separator, FALSE, TRUE, 0);
   gtk_widget_show (separator);
+
+  gtk_window_set_type_hint (GTK_WINDOW (dialog),
+			    GDK_WINDOW_TYPE_HINT_DIALOG);
 }
 
 static gint
@@ -202,7 +205,7 @@ gtk_dialog_new_empty (const gchar     *t
 
   if (flags & GTK_DIALOG_MODAL)
     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
-
+  
   if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
 
Index: gtk/gtkhandlebox.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkhandlebox.c,v
retrieving revision 1.73
diff -u -p -r1.73 gtkhandlebox.c
--- gtk/gtkhandlebox.c	2001/02/02 17:53:29	1.73
+++ gtk/gtkhandlebox.c	2001/02/23 15:01:57
@@ -409,6 +409,7 @@ gtk_handle_box_realize (GtkWidget *widge
   hb->float_window = gdk_window_new (NULL, &attributes, attributes_mask);
   gdk_window_set_user_data (hb->float_window, widget);
   gdk_window_set_decorations (hb->float_window, 0);
+  gdk_window_set_type_hint (hb->float_window, GDK_WINDOW_TYPE_HINT_TOOLBAR);
   
   widget->style = gtk_style_attach (widget->style, widget->window);
   gtk_style_set_background (widget->style, widget->window, GTK_WIDGET_STATE (hb));
Index: gtk/gtkmenu.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenu.c,v
retrieving revision 1.54
diff -u -p -r1.54 gtkmenu.c
--- gtk/gtkmenu.c	2001/02/17 06:04:40	1.54
+++ gtk/gtkmenu.c	2001/02/23 15:02:02
@@ -246,6 +246,8 @@ gtk_menu_init (GtkMenu *menu)
 				   NULL);
   gtk_window_set_policy (GTK_WINDOW (menu->toplevel),
 			 FALSE, FALSE, TRUE);
+  gtk_window_set_type_hint (GTK_WINDOW (menu->toplevel),
+			    GDK_WINDOW_TYPE_HINT_MENU);
 
   /* Refloat the menu, so that reference counting for the menu isn't
    * affected by it being a child of the toplevel
@@ -845,6 +847,8 @@ gtk_menu_set_tearoff_state (GtkMenu  *me
 						     "type", GTK_WINDOW_TOPLEVEL,
 						     "signal::destroy", gtk_widget_destroyed, &menu->tearoff_window,
 						     NULL);
+	      gtk_window_set_type_hint (GTK_WINDOW (menu->tearoff_window),
+					GDK_WINDOW_TYPE_HINT_MENU);
 	      gtk_widget_set_app_paintable (menu->tearoff_window, TRUE);
 	      gtk_signal_connect (GTK_OBJECT (menu->tearoff_window),  
 				  "event",
Index: gtk/gtkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v
retrieving revision 1.97
diff -u -p -r1.97 gtkwindow.c
--- gtk/gtkwindow.c	2001/02/03 01:09:40	1.97
+++ gtk/gtkwindow.c	2001/02/23 15:02:06
@@ -314,6 +314,7 @@ gtk_window_init (GtkWindow *window)
   window->frame_right = 0;
   window->frame_top = 0;
   window->frame_bottom = 0;
+  window->type_hint = GDK_WINDOW_TYPE_HINT_NORMAL;
     
   gtk_widget_ref (GTK_WIDGET (window));
   gtk_object_sink (GTK_OBJECT (window));
@@ -974,6 +975,26 @@ gtk_window_set_transient_for  (GtkWindow
 }
 
 /**
+ * gtk_window_set_type_hint:
+ * @window: a #GtkWindow
+ * @hint: the window type
+ *
+ * By hinting about the intended function of the window you allow
+ * window managers to decorate and handle different kind of windows
+ * in a consistant way. gtk_dialog_new_with_buttons() and other
+ * convenience functions in GTK+ will sometimes call
+ * gtk_window_set_type_hint() on your behalf.
+ * 
+ **/
+void
+gtk_window_set_type_hint (GtkWindow           *window, 
+			  GdkWindowTypeHint    hint)
+{
+  g_return_if_fail (GTK_IS_WINDOW (window));
+  window->type_hint = hint;
+}
+
+/**
  * gtk_window_set_destroy_with_parent:
  * @window: a #GtkWindow
  * @setting: whether to destroy @window with its transient parent
@@ -1405,6 +1426,14 @@ gtk_window_realize (GtkWidget *widget)
       GTK_WIDGET_REALIZED (window->transient_parent))
     gdk_window_set_transient_for (widget->window,
 				  GTK_WIDGET (window->transient_parent)->window);
+
+  gdk_window_set_type_hint (widget->window, window->type_hint);
+
+  /* transient_for must be set to allow the modal hint */
+  if (window->transient_parent && window->modal)
+    gdk_window_set_modal_hint (widget->window, TRUE);
+  else
+    gdk_window_set_modal_hint (widget->window, FALSE);
 }
 
 
Index: gtk/gtkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.h,v
retrieving revision 1.25
diff -u -p -r1.25 gtkwindow.h
--- gtk/gtkwindow.h	2001/01/08 17:04:17	1.25
+++ gtk/gtkwindow.h	2001/02/23 15:02:06
@@ -89,6 +89,8 @@ struct _GtkWindow
   guint frame_top;
   guint frame_right;
   guint frame_bottom;
+
+  GdkWindowTypeHint type_hint;
 };
 
 struct _GtkWindowClass
@@ -124,6 +126,8 @@ gint	   gtk_window_activate_default	    
 
 void       gtk_window_set_transient_for        (GtkWindow           *window, 
 						GtkWindow           *parent);
+void       gtk_window_set_type_hint            (GtkWindow           *window, 
+						GdkWindowTypeHint    hint);
 void       gtk_window_set_destroy_with_parent  (GtkWindow           *window,
                                                 gboolean             setting);
 void       gtk_window_set_geometry_hints       (GtkWindow           *window,

 






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