Re: Using _NET_WM properties for window names



Luis Rodrigo Gallardo Cruz <rodrigo nul-unu com> writes:
> The idea itself ought to be uncontroversial.

Actually, I am not entirely convinced of the usefulness of these
properties.  Are there characters in UTF-8 that cannot be
represented as COMPOUND_TEXT?

> In order to get everything working I made sawfish assume
> all strings to be rendered to the screen are encoded as UTF-8.

That is useful regardless of the _NET_WM properties, because the host
encoding Sawfish happens to be using may not be able to represent all
necessary characters even if COMPOUND_TEXT is.  Unfortunately this
change is clumsy to implement and without benefit if
Xutf8TextPropertyToTextList is not available.  With
XmbTextPropertyToTextList conversion has to go via the local encoding
anyway.

> Anyways, I'm requesting feedback on the patch.

What version is it against?  It did not apply cleanly to the trunk.
Anyway, I applied the rejects by hand, fixed some errors and just
cleaned it up a bit.  This patch is against the current trunk (r4204).
Alternatively you can pull branch utf-8-names from
http://www.iki.fi/tkorvola/sawfish.git.

-- 
	Timo Korvola		<URL:http://www.iki.fi/tkorvola>

diff --git a/src/display.c b/src/display.c
index 4e79b6a..48fdce5 100644
--- a/src/display.c
+++ b/src/display.c
@@ -55,7 +55,8 @@ int preferred_depth;
 
 /* some atoms that may be useful.. */
 Atom xa_wm_state, xa_wm_change_state, xa_wm_protocols, xa_wm_delete_window,
-    xa_wm_colormap_windows, xa_wm_take_focus, xa_compound_text;
+  xa_wm_colormap_windows, xa_wm_take_focus, xa_compound_text,
+  xa_wm_net_name, xa_wm_net_icon_name, xa_utf8_string;
 
 DEFSYM(display_name, "display-name");
 DEFSYM(canonical_display_name, "canonical-display-name");
@@ -335,6 +336,9 @@ sys_init(char *program_name)
 	    xa_wm_colormap_windows = XInternAtom (dpy, "WM_COLORMAP_WINDOWS", False);
 	    xa_wm_take_focus = XInternAtom (dpy, "WM_TAKE_FOCUS", False);
 	    xa_compound_text = XInternAtom (dpy, "COMPOUND_TEXT", False);
+            xa_wm_net_name = XInternAtom (dpy, "_NET_WM_NAME", False);
+            xa_wm_net_icon_name = XInternAtom (dpy, "_NET_WM_ICON_NAME", False);
+            xa_utf8_string = XInternAtom (dpy, "UTF8_STRING", False);
 
 	    if (!XShapeQueryExtension (dpy, &shape_event_base,
 				       &shape_error_base))
diff --git a/src/events.c b/src/events.c
index 34f753e..14bb15e 100644
--- a/src/events.c
+++ b/src/events.c
@@ -475,6 +475,108 @@ motion_notify (XEvent *ev)
        a motion event is actually evaluated) */
 }
 
+
+static bool
+update_window_name(Lisp_Window * w, XPropertyEvent xproperty) {
+    u_char *prop;
+    Atom actual;
+    int format;
+    long nitems, bytes_after;
+    char **text_list;
+    XTextProperty tprop;
+    int count;
+    repv str = Qnil;
+    int convert_status;
+
+    if (xproperty.state != PropertyNewValue
+        || XGetWindowProperty (dpy, w->id, xproperty.atom,
+                               0, 200, False, AnyPropertyType, &actual,
+                               &format, &nitems,
+                               &bytes_after, &prop) != Success
+        || actual == None)
+        return FALSE;
+
+    if (format != 8 || WINDOW_IS_GONE_P (w))
+    {
+        XFree (prop);
+        return FALSE;
+    }
+
+    tprop.value = prop;
+    tprop.encoding = actual;
+    tprop.format = format;
+    tprop.nitems = strlen (prop);
+
+    if (actual == xa_compound_text || actual == XA_STRING) 
+    {
+        convert_status = XmbTextPropertyToTextList (dpy, &tprop, &text_list,
+                                                    &count);
+        if (convert_status >= Success && count > 0) 
+        {
+            char * utf8str = g_locale_to_utf8(text_list[0], -1,
+                                              NULL, NULL, NULL);
+            if (utf8str)
+                str = rep_string_dup (utf8str);
+        }
+        XFreeStringList(text_list);
+    }
+
+#ifdef X_HAVE_UTF8_STRING
+    if (actual == xa_utf8_string) 
+    {
+        convert_status = Xutf8TextPropertyToTextList (dpy, &tprop, &text_list,
+                                                      &count);
+        if (convert_status >= Success && count > 0)
+            str = rep_string_dup (text_list[0]);
+        XFreeStringList(text_list);
+    }
+#endif
+
+    XFree (prop);
+  
+    if (str == Qnil)
+        return FALSE;
+
+    if (xproperty.atom == xa_wm_net_name
+        && str != Qnil && Fequal (w->net_name, str) == Qnil)
+    {
+        w->net_name = str;
+        return TRUE;
+    }
+  
+    if (xproperty.atom == xa_wm_net_icon_name
+        && str != Qnil && Fequal (w->net_icon_name, str) == Qnil)
+    {
+        w->net_icon_name = str;
+        return TRUE;
+    }
+  
+    if (w->net_name == Qnil && xproperty.atom == XA_WM_NAME)
+    {
+        if (str == Qnil)
+            str = rep_null_string ();
+        if (Fequal (w->name, str) == Qnil
+            || Fequal (w->full_name, str) == Qnil)
+        {
+            w->full_name = w->name = str;
+            return TRUE;
+        }
+    }
+  
+    if (w->net_icon_name == Qnil && xproperty.atom == XA_WM_ICON_NAME)
+    {
+        if (str == Qnil)
+            str = rep_null_string ();
+        if (Fequal (w->icon_name, str) == Qnil)
+        {
+            w->icon_name = str;
+            return TRUE;
+        }
+    }    
+
+    return FALSE;
+}
+
 static void
 property_notify (XEvent *ev)
 {
@@ -488,72 +590,9 @@ property_notify (XEvent *ev)
 
 	switch (ev->xproperty.atom)
 	{
-	    u_char *prop;
-	    Atom actual;
-	    int format;
-	    long nitems, bytes_after;
 	    long supplied;
 	    bool old_urgency, new_urgency;
 
-	case XA_WM_NAME:
-	case XA_WM_ICON_NAME:
-	    if (ev->xproperty.state == PropertyNewValue
-		&& XGetWindowProperty (dpy, w->id, ev->xproperty.atom,
-				       0, 200, False, AnyPropertyType, &actual,
-				       &format, &nitems,
-				       &bytes_after, &prop) == Success
-		&& actual != None)
-	    {
-		if (format == 8 && !WINDOW_IS_GONE_P (w))
-		{
-		    repv str = Qnil;
-		    if (actual == xa_compound_text || actual == XA_STRING)
-		    {
-			char **text_list;
-			XTextProperty tprop;
-			int count;
-			tprop.value = prop;
-			tprop.encoding = actual;
-			tprop.format = format;
-			tprop.nitems = strlen (prop);
-			if (XmbTextPropertyToTextList (dpy, &tprop,
-						       &text_list, &count)
-			    >= Success)
-			{
-			    if (count > 0)
-			    	str = rep_string_dup (text_list[0]);
-			    XFreeStringList(text_list);
-			}
-		    }
-		    if (str == Qnil)
-			str = rep_null_string ();
-
-		    if (ev->xproperty.atom == XA_WM_NAME)
-		    {
-			if (Fequal (w->name, str) == Qnil
-			    || Fequal (w->full_name, str) == Qnil)
-			{
-			    w->full_name = w->name = str;
-			    need_refresh = TRUE;
-			}
-			else
-			    changed = FALSE;
-		    }
-		    else
-		    {
-			if (Fequal (w->icon_name, str) == Qnil)
-			{
-			    w->icon_name = str;
-			    need_refresh = TRUE;
-			}
-			else
-			    changed = FALSE;
-		    }
-		}
-		XFree (prop);
-	    }
-	    break;
-
 	case XA_WM_HINTS:
 	    old_urgency = w->wmhints != 0 && w->wmhints->flags & XUrgencyHint;
 	    if (w->wmhints != 0)
@@ -571,7 +610,15 @@ property_notify (XEvent *ev)
 	    break;
 
 	default:
-	    if (ev->xproperty.atom == xa_wm_colormap_windows)
+            if (ev->xproperty.atom == XA_WM_NAME ||
+                ev->xproperty.atom == XA_WM_ICON_NAME ||
+                ev->xproperty.atom == xa_wm_net_name ||
+                ev->xproperty.atom == xa_wm_net_icon_name ) 
+            {
+                need_refresh = changed = 
+                    update_window_name(w, ev->xproperty);
+            }
+            else if (ev->xproperty.atom == xa_wm_colormap_windows)
 	    {
 		if (w->n_cmap_windows > 0)
 		    XFree (w->cmap_windows);
diff --git a/src/fonts.c b/src/fonts.c
index 474aa62..9195733 100644
--- a/src/fonts.c
+++ b/src/fonts.c
@@ -311,7 +311,11 @@ fontset_finalize (Lisp_Font *f)
 static int
 fontset_measure (Lisp_Font *f, u_char *string, size_t length)
 {
+#ifdef X_HAVE_UTF8_STRING
+    return Xutf8TextEscapement (f->font, string, length);
+#else
     return XmbTextEscapement (f->font, string, length);
+#endif
 }
 
 static void
@@ -322,8 +326,11 @@ fontset_draw (Lisp_Font *f, u_char *string, size_t length,
 
     gcv.foreground = fg->pixel;
     XChangeGC (dpy, gc, GCForeground, &gcv);
-
+#ifdef X_HAVE_UTF8_STRING
+    Xutf8DrawString (dpy, id, f->font, gc, x, y, string, length);
+#else
     XmbDrawString (dpy, id, f->font, gc, x, y, string, length);
+#endif
 }
 
 static const Lisp_Font_Class fontset_class = {
@@ -365,7 +372,7 @@ xft_measure (Lisp_Font *f, u_char *string, size_t length)
 {
     XGlyphInfo info;
 
-    XftTextExtents8 (dpy, f->font, string, length, &info);
+    XftTextExtentsUtf8 (dpy, f->font, string, length, &info);
 
     return info.xOff; 
 }
@@ -389,8 +396,8 @@ xft_draw (Lisp_Font *f, u_char *string, size_t length,
     xft_color.color.blue = fg->blue;
     xft_color.color.alpha = fg->alpha;
 
-    XftDrawString8 (draw, &xft_color, f->font,
-		    x, y, string, length);
+    XftDrawStringUtf8 (draw, &xft_color, f->font,
+                        x, y, string, length);
 }
 
 static const Lisp_Font_Class xft_class = {
@@ -475,24 +482,14 @@ pango_finalize (Lisp_Font *f)
 static int
 pango_measure (Lisp_Font *f, u_char *string, size_t length)
 {
-    gsize r, w;
-    u_char *utf8str;
     PangoLayout *layout;
     PangoRectangle rect;
 
-    utf8str = g_locale_to_utf8 (string, length, &r, &w, NULL);
-    if (utf8str != NULL)
-    {
-	string = utf8str;
-	length = w;
-    }
-
     layout = pango_layout_new (pango_context);
     pango_layout_set_text (layout, string, length);
 
     pango_layout_get_extents (layout, NULL, &rect);
 
-    g_free (utf8str);
     g_object_unref (layout);
  
     return rect.width / PANGO_SCALE;
@@ -529,8 +526,6 @@ pango_draw (Lisp_Font *f, u_char *string, size_t length,
 {
     static XftDraw *draw;
     XftColor xft_color;
-    gsize r, w;
-    u_char *utf8str;
     PangoLayout *layout;
     PangoLayoutIter *iter;
 
@@ -545,13 +540,6 @@ pango_draw (Lisp_Font *f, u_char *string, size_t length,
     xft_color.color.blue = fg->blue;
     xft_color.color.alpha = fg->alpha;
 
-    utf8str = g_locale_to_utf8 (string, length, &r, &w, NULL);
-    if (utf8str != NULL)
-    {
-	string = utf8str;
-	length = w;
-    }
-
     layout = pango_layout_new (pango_context);
     pango_layout_set_text (layout, string, length);
     iter = pango_layout_get_iter (layout);
@@ -565,7 +553,6 @@ pango_draw (Lisp_Font *f, u_char *string, size_t length,
 			 line, x + rect.x / PANGO_SCALE, y);
     } while (pango_layout_iter_next_line (iter));
 
-    g_free (utf8str);
     g_object_unref (layout);
     pango_layout_iter_free (iter);
 }
diff --git a/src/sawmill.h b/src/sawmill.h
index 2cff9f3..08caa31 100644
--- a/src/sawmill.h
+++ b/src/sawmill.h
@@ -147,6 +147,7 @@ typedef struct lisp_window {
     Window *cmap_windows;
     int n_cmap_windows;
     repv full_name, name, icon_name;
+    repv net_name, net_icon_name;
     int frame_vis;
     repv icon_image;
 
diff --git a/src/sawmill_subrs.h b/src/sawmill_subrs.h
index 19c7a50..4dfa62d 100644
--- a/src/sawmill_subrs.h
+++ b/src/sawmill_subrs.h
@@ -57,7 +57,7 @@ extern Visual *preferred_visual;
 extern Window root_window, no_focus_window;
 extern Atom xa_wm_state, xa_wm_change_state, xa_wm_protocols,
     xa_wm_delete_window, xa_wm_colormap_windows, xa_wm_take_focus,
-    xa_compound_text;
+  xa_compound_text, xa_wm_net_name, xa_wm_net_icon_name, xa_utf8_string;
 extern int shape_event_base, shape_error_base;
 extern repv Qdisplay_name, Qcanonical_display_name;
 extern bool sys_init (char *program_name);
diff --git a/src/windows.c b/src/windows.c
index 0c4f925..d35ab6f 100644
--- a/src/windows.c
+++ b/src/windows.c
@@ -356,6 +356,80 @@ remove_window_frame (Lisp_Window *w)
     }
 }
 
+
+#ifdef X_HAVE_UTF8_STRING
+static repv
+text_prop_to_utf8 (XTextProperty *prop)
+{
+    repv rval = Qnil;
+    if (prop->value && prop->nitems > 0)
+    {
+        char **list;
+        int count;
+        prop->nitems = strlen(prop->value);
+        if (Xutf8TextPropertyToTextList (dpy, prop, &list, &count) >= Success)
+        {
+            if (count > 0)
+                rval = rep_string_dup (list[0]);
+            XFreeStringList (list);
+        }
+    }
+    return rval;
+}
+#else
+static repv
+text_prop_to_utf8 (XTextProperty *prop)
+{
+    if (prop->value && prop->nitems > 0)
+    {
+        repv rval = Qnil;
+        char **list;
+        int count;
+        prop.nitems = strlen(prop.value);
+        if (XmbTextPropertyToTextList (dpy, &prop, &list, &count)
+            >= Success)
+        {
+            if (count > 0) {
+                gchar *ustr = g_locale_to_utf8(list[0], -1, NULL, NULL, NULL);
+                if (ustr)
+                {
+                    rval = rep_string_dup(ustr);
+                    g_free (ustr);
+                }
+            }
+            XFreeStringList (list);
+        }
+    }
+    return rval;
+}
+#endif
+
+
+/* Queries X properties to get the window {icon,}name */
+static void
+get_window_name(Lisp_Window *w)
+{
+    XTextProperty prop;
+
+    /* We only try to use the utf8 properties if our xlib supports them.
+       Otherwise conversion would have to go via the current locale, which
+       might lose some characters. */
+#ifdef X_HAVE_UTF8_STRING
+    if (XGetTextProperty (dpy, w->id, &prop, xa_wm_net_name))
+        w->net_name = text_prop_to_utf8 (&prop);
+    if (XGetTextProperty (dpy, w->id, &prop, xa_wm_net_icon_name))
+        w->net_icon_name = text_prop_to_utf8 (&prop);
+#endif
+
+    if (w->net_name == Qnil && XGetWMName (dpy, w->id, &prop))
+        w->net_name = text_prop_to_utf8 (&prop);
+    w->full_name = w->name;
+  
+    if (w->net_icon_name == Qnil && XGetWMIconName (dpy, w->id, &prop))
+        w->icon_name = text_prop_to_utf8 (&prop);
+}
+
+
 /* Add the top-level window ID to the manager's data structures */
 Lisp_Window *
 add_window (Window id)
@@ -369,7 +443,6 @@ add_window (Window id)
 	XWindowChanges xwc;
 	u_int xwcm;
 	long supplied;
-	XTextProperty prop;
 
 	DB(("add_window (%lx)\n", id));
 
@@ -389,6 +462,8 @@ add_window (Window id)
 	w->frame_style = Qnil;;
 	w->icon_image = rep_NULL;
 	w->name = rep_null_string ();
+        w->net_name = Qnil;
+        w->net_icon_name = Qnil;
 
 	/* have to put it somewhere until it finds the right place */
 	insert_in_stacking_list_above_all (w);
@@ -401,31 +476,7 @@ add_window (Window id)
 	DB(("  orig: width=%d height=%d x=%d y=%d\n",
 	    w->attr.width, w->attr.height, w->attr.x, w->attr.y));
 
-	if (XGetWMName (dpy, id, &prop) && prop.value)
-	{
-	    if (prop.nitems > 0)
-	    {
-		char **list;
-		int count;
-		prop.nitems = strlen(prop.value);
-		if (XmbTextPropertyToTextList (dpy, &prop, &list, &count)
-		    >= Success)
-		{
-		    if (count > 0)
-			w->name = rep_string_dup (list[0]);
-		    XFreeStringList (list);
-		}
-	    }
-	    XFree (prop.value);
-	}
-	w->full_name = w->name;
-	if (XGetIconName (dpy, id, &tem))
-	{
-	    w->icon_name = rep_string_dup (tem);
-	    XFree (tem);
-	}
-	else
-	    w->icon_name = w->name;
+        get_window_name(w);
 
 	w->wmhints = XGetWMHints (dpy, id);
 	if (!XGetWMNormalHints (dpy, w->id, &w->hints, &supplied))
@@ -699,8 +750,10 @@ window-name WINDOW
 Return the name of window object WINDOW.
 ::end:: */
 {
+    Lisp_Window * w;
     rep_DECLARE1(win, WINDOWP);
-    return VWIN(win)->name;
+    w = VWIN(win);
+    return w->net_name != Qnil ? w->net_name : w->name;
 }
 
 DEFUN("window-full-name", Fwindow_full_name, Swindow_full_name,
@@ -723,8 +776,10 @@ window-icon-name WINDOW
 Return the name of window object WINDOW's icon.
 ::end:: */
 {
+    Lisp_Window * w;
     rep_DECLARE1(win, WINDOWP);
-    return VWIN(win)->icon_name;
+    w = VWIN(win);
+    return w->net_icon_name != Qnil ? w->net_icon_name : w->icon_name;
 }
 
 DEFUN("window-mapped-p", Fwindow_mapped_p, Swindow_mapped_p,
@@ -1419,6 +1474,8 @@ window_mark (repv win)
     rep_MARKVAL(VWIN(win)->name);
     rep_MARKVAL(VWIN(win)->full_name);
     rep_MARKVAL(VWIN(win)->icon_name);
+    rep_MARKVAL(VWIN(win)->net_name);
+    rep_MARKVAL(VWIN(win)->net_icon_name);
     rep_MARKVAL(VWIN(win)->icon_image);
 }
 


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