# Net wm properties

773pages on
this wiki
 Browse all patches [−] Patches

## Author Edit

Rodrigo Gallardo <rodrigo@nul-unu.com>

## Synopsis Edit

This patch makes sawfish use the _NET_WM_NAME properties for window names, as mandated by the EWMH spec (http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html)

This patch is a bit more invasive that it seems requiered, because, to make it work properly in my system I had to make sawfish assume all strings are UTF8 encoded (see the pieces of the patch touching fonts.c)

Please test it and tell me what breaks. If there are no objections to the substance of the patch, I'd still like some feedback on what to do with text rendering with "normal" xlfd fonts.

## Patch Edit

```diff --git a/src/display.c b/src/display.c
index c1bbc02..e8893cf 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 8111dd6..6072f31 100644
--- a/src/events.c
+++ b/src/events.c
@@ -475,6 +475,111 @@ 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))
+    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)
+    {
+      if ( str != Qnil && Fequal (w->net_name, str) == Qnil)
+	{
+	  w->net_name = str;
+	  return TRUE;
+	}
+    }
+
+  if (xproperty.atom == xa_wm_net_icon_name)
+    {
+      if ( str != Qnil && Fequal (w->net_icon_name, str) == Qnil)
+	{
+	  w->net_icon_name = str;
+	  return TRUE;
+	}
+    }
+
+  /* No point in updating the rest if we have the _NET ones. They won't
+     be used anyways. */
+  if (w->net_name != Qnil)
+    return FALSE;
+
+  if (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 (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 +593,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 +613,16 @@ 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 732706f..01afda9 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,7 +396,7 @@ 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,
+    XftDrawStringUtf8 (draw, &xft_color, f->font,
x, y, string, length);
}

@@ -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 4de5504..ac46451 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 6243451..aaa329e 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 e00d062..9e9ca78 100644
--- a/src/windows.c
+++ b/src/windows.c
@@ -356,6 +356,85 @@ remove_window_frame (Lisp_Window *w)
}
}

+/* Queries X properties to get the window {icon,}name */
+static void
+get_window_name(Lisp_Window * w)
+{
+  char *tem;
+  XTextProperty prop;
+
+  /* We only try to use the utf8 properties if our xlib supports them */
+#ifdef X_HAVE_UTF8_STRING
+  if (XGetTextProperty (dpy, w->id, &prop, xa_wm_net_name) && prop.value) {
+    if (prop.nitems > 0)
+      {
+        char **list;
+	int count;
+	prop.nitems = strlen(prop.value);
+	if (Xutf8TextPropertyToTextList (dpy, &prop, &list, &count)
+	    >= Success)
+	  {
+	    if (count > 0)
+	      w->net_name = rep_string_dup (list[0]);
+	    XFreeStringList (list);
+	  }
+      }
+  }
+
+  if (XGetTextProperty (dpy, w->id, &prop, xa_wm_net_icon_name) && prop.value) {
+    if (prop.nitems > 0)
+      {
+        char **list;
+	int count;
+	prop.nitems = strlen(prop.value);
+	if (Xutf8TextPropertyToTextList (dpy, &prop, &list, &count)
+	    >= Success)
+	  {
+	    if (count > 0)
+	      w->net_icon_name = rep_string_dup (list[0]);
+	    XFreeStringList (list);
+	  }
+      }
+  }
+
+  /* If we got the _NET names, there's no point in querying the others,
+     as they won't be used anyways. */
+  if (w->net_name != Qnil)
+    return;
+
+#endif
+
+  if (XGetWMName (dpy, w->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 (g_locale_to_utf8(list[0],
+							   -1, NULL, NULL, NULL));
+	      XFreeStringList (list);
+	    }
+	}
+      XFree (prop.value);
+    }
+  w->full_name = w->name;
+
+  if (XGetIconName (dpy, w->id, &tem))
+    {
+      w->icon_name = rep_string_dup (g_locale_to_utf8(tem,
+						      -1, NULL, NULL, NULL));
+      XFree (tem);
+    }
+  else
+    w->icon_name = w->name;
+
+}
+
/* Add the top-level window ID to the manager's data structures */
Lisp_Window *
@@ -369,7 +448,6 @@ add_window (Window id)
XWindowChanges xwc;
u_int xwcm;
long supplied;
-	XTextProperty prop;

@@ -389,6 +467,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 +481,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 +755,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 +781,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 +1479,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);
}

```

## Community's reasons for inclusion or rejection Edit

• vote: pondering. See this message and subsequent discussion (also in November). - Tkorvola 13:15, 2 December 2007 (UTC)
• vote: yes. Helps to fix the problem with Bugzilla pages not displaying titles.
• vote: yes. See this message for testing results, doesn't seem to break anything and fixes what it should Janek Kozicki 23:28, 16 January 2008 (UTC)
patch applied, thanks Janek Kozicki 12:04, 19 January 2008 (UTC)