Subject: Latest Fancy Titlebars Patch

From: Suzanne Britton <[email-masked]>
Date: Fri, 26 Oct 2001 01:56:27 -0400

Greets!

Behold the latest fancy-titlebar patch. I've cleaned up the code a little,
added in a "Buttons" pixmap option (for tiling under the buttons in
UseTitleStyle if LeftButtons/RightButtons isn't defined), added the
ability to stretch pixmaps instead of tiling, and took a stab at documenting
this mess in fvwm2.1.

You can now put a "stretched" flag after a pixmap name, like this:

   TitleStyle MultiPixmap Main (stretched) foo.xpm, UnderText bar.xpm

Now the Main pixmap will be scaled, but UnderText will be tiled as usual.
Note that this flag will only affect Main, LeftMain, RightMain, and UnderText.

The primary drawback with stretching is that it can't (yet) be extended very
well with UseTitleStyle. This would be somewhat tricky since the buttons are
rendered separately from the rest of the titlebar. Instead, whatever pixmap is
rendered under the buttons will always be tiled (even if it were stretched, it
wouldn't necessarily be seamless because it would start anew with each
button). It may be best not to use UseTitleStyle at all in this case and just
build the background you want into the buttons--this is the way most themes
(IceWM, Sawfish, etc.) are distributed anyways.

It'd be nice if someone could figure out a way to stretch a pixmap *all* the
way across the titlebar, including under buttons, but it's a task to which I am
not up :-)

The patch is now about 19k and, on my system, adds 2368 bytes to the stripped
executable when FANCY_TITLEBARS is #defined :-) It seems to be reasonably
complete and stable, but due to continued absence of our new back-ordered
monitor, I haven't been able to test it extensively yet (it's not that X won't
come up...it just *hurts* to look at!)

Suzanne

-- 
[email-masked] - http://www.igs.net/~tril/
"I'm aware of that, sweetheart. It's just that when I wake up to a hissing
goat skull on my nightstand, and it hops off and runs across the floor on
spider legs, I sleep a lot better knowing where it ran off to."
                                                          - Ted (Red Meat)
--- ChangeLog.orig	Wed Oct 10 01:26:04 2001
+++ ChangeLog	Mon Oct 15 04:23:03 2001
@@ -1,3 +1,36 @@
+2001-10-10  Suzanne Britton  <[email-masked]>
+
+	* config.h.in: Added FANCY_TITLEBARS option, off by default
+
+	* fvwm/fvwm2.1:
+	Added info on TitleStyle MultiPixmap
+
+	* fvwm/screen.h:
+	[All changes #ifdeffed FANCY_TITLEBARS]
+	Added TITLE_PADDING define for fancy titlebar rendering
+	Added tb_pixmap_enum
+	Added MultiPixmap to DecorFaceType enum
+	Added Picture **multi_pixmaps and short multi_stretch_flags
+	to DecorFace structure
+
+	* fvwm/borders.c:
+	[All changes #ifdeffed FANCY_TITLEBARS except the fix to RedrawButtons]
+	(DrawButton): Handle UseTitleStyle where TitleStyle is MultiPixmap
+	(RenderIntoWindow): [new]
+	(DrawMultiPixmapTitlebar): [new]
+	(RedrawButtons): Pass the left1right0 parameter properly to DrawButton,
+	instead of always sending 1
+	(RedrawTitle):
+	Call DrawFancyTitlebar for MultiPixmap style
+	Skip DrawString calls for MultiPixmap style (title is drawn inside
+	DrawMultiPixmapTitlebar)
+
+	* fvwm/builtins.c:
+	[All changes #ifdeffed FANCY_TITLEBARS]
+	(FreeDecorFace): Free up multi_pixmaps array for a MultiPixmap decor
+	(ReadDecorFace): Call ReadMultiPixmapDecor for a MultiPixmap TitleStyle
+	(ReadMultiPixmapDecor): [new]
+
 2001-10-08  olicha  <[email-masked]>
 
 	* fvwm/borders.c (RedrawTitle):
--- AUTHORS.orig	Wed Oct 10 01:25:58 2001
+++ AUTHORS	Wed Oct 10 01:15:06 2001
@@ -182,6 +182,8 @@
 Echo. Improve modules: Make FvwmM4 pass args on to m4, font-related
 seg-fault bug fix in FvwmButtons.
 
+Suzanne Britton:
+Multi-pixmap titlebars patch.
 
 Along with a cast of thousands (well, dozens) mentioned in old
 ChangeLog entries.  If you find your name below, please send an entry
--- config.h.in.orig	Wed Oct 10 01:26:19 2001
+++ config.h.in	Wed Oct 10 01:15:06 2001
@@ -2,6 +2,9 @@
 /* Suffix for config filenames */
 #define FVWMRC ".fvwm2rc"
 
+/* Define for fancy, multi-pixmap-themeable titlebars */
+#undef FANCY_TITLEBARS
+
 /* Define if gdk-imlib is used */
 #undef GDK_IMLIB
 
--- fvwm/fvwm2.1.orig	Mon Oct 15 03:36:07 2001
+++ fvwm/fvwm2.1	Mon Oct 15 04:21:03 2001
@@ -6304,7 +6304,46 @@
 ButtonStyle All ActiveUp (-- flat) Inactive \\
   (-- flat)
 .EE
+If fvwm is compiled with the FANCY_TITLEBARS option enabled, an additional
+TitleStyle is available: MultiPixmap. This allows you to specify different
+pixmaps for different parts of the titlebar. Some of them are tiled or
+stretched to fit a particular space; others are discrete "transition" pixmaps
+which are not resized in any way. The definable pixmaps are:
+.EX
+- Main (tiled/stretched): The main titlebar pixmap
+- LeftMain (tiled/stretched): Left of title text
+- RightMain (tiled/stretched): Right of title text
+- UnderText (tiled/stretched): Underneath title text
+- LeftOfText: Pixmap just to the left of the title text
+- RightOfText: Pixmap just to the right of the title text
+- LeftEnd: Pixmap at the far left end of the titlebar
+  (just before buttons)
+- RightEnd: Pixmap at the far right end of the titlebar
+  (just before buttons)
+- Buttons (tiled): Tiled under buttons in case of
+  UseTitleStyle
+- LeftButtons (tiled): Tiled under left buttons in case of
+  UseTitleStyle
+- RightButtons (tiled): Tiled under right buttons in case of
+  UseTitleStyle
+.EE
+None of these are mandatory except for Main (or, if you don't define Main,
+you must define both LeftMain and RightMain). If no "Buttons" pixmaps are
+defined and UseTitleStyle is specified for one or more buttons, Main,
+LeftMain, or RightMain will be used as appropriate.
 
+The syntax for this style type is:
+.EX
+MultiPixmap <section> (<flag>) <pixmap filename>, ...
+.EE
+continuing for whatever pixmaps you want to define. By default, all
+non-transition pixmaps are tiled. If you want one stretched instead (this is
+allowed for Main, LeftMain, RightMain, and UnderText), add "(stretched)" after
+the section name:
+.EX
+MultiPixmap Main (stretched) foo.xpm, UnderText bar.xpm
+.EE
+Otherwise you can omit (<flag>), as shown with UnderText.
 .TP
 .BI "UpdateDecor [" decor "]"
 This command is kept mainly for backwards compatibility.  Since
--- fvwm/screen.h.orig	Wed Oct 10 01:26:43 2001
+++ fvwm/screen.h	Mon Oct 15 00:11:20 2001
@@ -54,6 +54,13 @@
 #define COLORMAP_FOLLOWS_MOUSE 1 /* default */
 #define COLORMAP_FOLLOWS_FOCUS 2
 
+#ifdef FANCY_TITLEBARS
+#define TITLE_PADDING 5
+enum tb_pixmap_enum {TBP_MAIN, TBP_LEFT_MAIN, TBP_RIGHT_MAIN, TBP_UNDER_TEXT,
+                     TBP_LEFT_OF_TEXT, TBP_RIGHT_OF_TEXT, TBP_LEFT_END,
+                     TBP_RIGHT_END, TBP_BUTTONS, TBP_LEFT_BUTTONS,
+                     TBP_RIGHT_BUTTONS, NUM_TB_PIXMAPS};
+#endif
 
 typedef struct
 {
@@ -70,6 +77,9 @@
     GradientButton              ,
     PixmapButton                ,
     TiledPixmapButton           ,
+#ifdef FANCY_TITLEBARS
+    MultiPixmap                 ,
+#endif
 #ifdef MINI_ICONS
     MiniIconButton              ,
 #endif
@@ -123,6 +133,10 @@
   struct
   {
     Picture *p;
+#ifdef FANCY_TITLEBARS
+    Picture **multi_pixmaps;
+    short multi_stretch_flags;
+#endif
     Pixel back;
     struct
     {
--- fvwm/borders.c.orig	Wed Oct 10 01:26:50 2001
+++ fvwm/borders.c	Mon Oct 15 02:51:57 2001
@@ -271,10 +271,29 @@
   case MiniIconButton:
   case PixmapButton:
   case TiledPixmapButton:
+#ifdef FANCY_TITLEBARS
+  case MultiPixmap:       /* in case of UseTitleStyle */
+#endif
     if (type == PixmapButton || type == TiledPixmapButton)
     {
       p = df->u.p;
     }
+#ifdef FANCY_TITLEBARS
+    else if (type == MultiPixmap) {
+      if (left1right0 && df->u.multi_pixmaps[TBP_LEFT_BUTTONS])
+        p = df->u.multi_pixmaps[TBP_LEFT_BUTTONS];
+      else if (!left1right0 && df->u.multi_pixmaps[TBP_RIGHT_BUTTONS])
+        p = df->u.multi_pixmaps[TBP_RIGHT_BUTTONS];
+      else if (df->u.multi_pixmaps[TBP_BUTTONS])
+        p = df->u.multi_pixmaps[TBP_BUTTONS];
+      else if (left1right0 && df->u.multi_pixmaps[TBP_LEFT_MAIN])
+        p = df->u.multi_pixmaps[TBP_LEFT_MAIN];
+      else if (!left1right0 && df->u.multi_pixmaps[TBP_RIGHT_MAIN])
+        p = df->u.multi_pixmaps[TBP_RIGHT_MAIN];
+      else
+        p = df->u.multi_pixmaps[TBP_MAIN];
+    }
+#endif
     else
     {
       if (!t->mini_icon)
@@ -298,6 +317,9 @@
     if (is_lowest && !p->mask && pbutton_background_pixmap)
     {
       if (type == TiledPixmapButton ||
+#ifdef FANCY_TITLEBARS
+          type == MultiPixmap ||
+#endif
 	  (p && p->width >= width && p->height >= height))
       {
 	/* better performance: set a background pixmap if possible, i.e. if the
@@ -326,7 +348,11 @@
 	XClearWindow(dpy, win);
       }
     }
-    if (type != TiledPixmapButton)
+    if (type != TiledPixmapButton
+#ifdef FANCY_TITLEBARS
+        && type != MultiPixmap
+#endif
+       )
     {
       if (DFS_H_JUSTIFICATION(df->style) == JUST_RIGHT)
 	x += (int)(width - p->width);
@@ -354,7 +380,11 @@
 
     XSetClipMask(dpy, Scr.TransMaskGC, p->mask);
     XSetClipOrigin(dpy, Scr.TransMaskGC, x, y);
-    if (type != TiledPixmapButton)
+    if (type != TiledPixmapButton
+#ifdef FANCY_TITLEBARS
+        && type != MultiPixmap
+#endif
+       )
     {
       int cx = x;
       int cy = y;
@@ -448,6 +478,159 @@
   return;
 }
 
+#ifdef FANCY_TITLEBARS
+
+/****************************************************************************
+ *
+ *  Tile or stretch src into dest, starting at the given location and
+ *  continuing for the given width and height. This is a utility function used
+ *  by DrawMultiPixmapTitlebar. ([email-masked])
+ *
+ ****************************************************************************/
+static void RenderIntoWindow(GC gc, Picture *src, Window dest, int x_start,
+                             int y_start, int width, int height, Bool stretch)
+{
+  Pixmap pm;
+  if (stretch)
+    pm = CreateStretchPixmap(dpy, src->picture, src->width, src->height,
+                             src->depth, width, height, gc);
+  else
+    pm = CreateTiledPixmap(dpy, src->picture, src->width, src->height,
+                           width, height, src->depth, gc);
+  XCopyArea(dpy, pm, dest, gc, 0, 0, width, height, x_start, y_start);
+  XFreePixmap(dpy, pm);
+}
+
+/****************************************************************************
+ *
+ *  Redraws multi-pixmap titlebar ([email-masked])
+ *
+ ****************************************************************************/
+static void DrawMultiPixmapTitlebar(FvwmWindow *window, DecorFace *df)
+{
+  Window       title_win;
+  GC           gc;
+  const char  *title;
+  Picture    **pm;
+  short        stretch_flags;
+  int          title_width, title_height, text_width, text_offset, text_y_pos;
+  int          left_space, right_space, under_offset, under_width;
+
+  pm = df->u.multi_pixmaps;
+  stretch_flags = df->u.multi_stretch_flags;
+  gc = Scr.TitleGC;
+  XSetClipMask(dpy, gc, None);
+  title_win = window->title_w;
+  title = window->name;
+  title_width = window->title_g.width;
+  title_height = window->title_g.height;
+
+  if (pm[TBP_MAIN])
+    RenderIntoWindow(gc, pm[TBP_MAIN], title_win, 0, 0, title_width,
+                     title_height, (stretch_flags & (1 << TBP_MAIN)));
+  else if (!title)
+    RenderIntoWindow(gc, pm[TBP_LEFT_MAIN], title_win, 0, 0, title_width,
+                     title_height, (stretch_flags & (1 << TBP_LEFT_MAIN)));
+
+  if (title) {
+#ifdef I18N_MB
+    text_width = XmbTextEscapement(window->title_font.fontset, title,
+                                   strlen(title));
+#else
+    text_width = XTextWidth(window->title_font.font, title, strlen(title));
+#endif
+    if (text_width > title_width)
+      text_width = title_width;
+    switch (TB_JUSTIFICATION(GetDecor(window, titlebar))) {
+    case JUST_LEFT:
+      text_offset = TITLE_PADDING;
+      if (pm[TBP_LEFT_OF_TEXT])
+        text_offset += pm[TBP_LEFT_OF_TEXT]->width;
+      if (pm[TBP_LEFT_END])
+        text_offset += pm[TBP_LEFT_END]->width;
+      if (text_offset > title_width - text_width)
+        text_offset = title_width - text_width;
+      break;
+    case JUST_RIGHT:
+      text_offset = title_width - text_width - TITLE_PADDING;
+      if (pm[TBP_RIGHT_OF_TEXT])
+        text_offset -= pm[TBP_RIGHT_OF_TEXT]->width;
+      if (pm[TBP_RIGHT_END])
+        text_offset -= pm[TBP_RIGHT_END]->width;
+      if (text_offset < 0)
+        text_offset = 0;
+      break;
+    default:
+      text_offset = (title_width - text_width) / 2;
+      break;
+    }
+    under_offset = text_offset;
+    under_width = text_width;
+    /* If there's title padding, put it *inside* the undertext area: */
+    if (under_offset >= TITLE_PADDING) {
+      under_offset -= TITLE_PADDING;
+      under_width += TITLE_PADDING;
+    }
+    if (under_offset + under_width + TITLE_PADDING <= title_width)
+      under_width += TITLE_PADDING;
+    left_space = under_offset;
+    right_space = title_width - left_space - under_width;
+
+    if (pm[TBP_LEFT_MAIN] && left_space > 0)
+      RenderIntoWindow(gc, pm[TBP_LEFT_MAIN], title_win, 0, 0, left_space,
+                       title_height, (stretch_flags & (1 << TBP_LEFT_MAIN)));
+    if (pm[TBP_RIGHT_MAIN] && right_space > 0)
+      RenderIntoWindow(gc, pm[TBP_RIGHT_MAIN], title_win,
+                       under_offset + under_width, 0, right_space,
+                       title_height, (stretch_flags & (1 << TBP_RIGHT_MAIN)));
+    if (pm[TBP_UNDER_TEXT] && under_width > 0)
+      RenderIntoWindow(gc, pm[TBP_UNDER_TEXT], title_win, under_offset, 0,
+                       under_width, title_height,
+                       (stretch_flags & (1 << TBP_UNDER_TEXT)));
+    if (pm[TBP_LEFT_OF_TEXT] &&
+        pm[TBP_LEFT_OF_TEXT]->width <= left_space) {
+      XCopyArea(dpy, pm[TBP_LEFT_OF_TEXT]->picture, title_win, gc, 0, 0,
+                pm[TBP_LEFT_OF_TEXT]->width, title_height,
+                under_offset - pm[TBP_LEFT_OF_TEXT]->width, 0);
+      left_space -= pm[TBP_LEFT_OF_TEXT]->width;
+    }
+    if (pm[TBP_RIGHT_OF_TEXT] &&
+        pm[TBP_RIGHT_OF_TEXT]->width <= right_space) {
+      XCopyArea(dpy, pm[TBP_RIGHT_OF_TEXT]->picture, title_win, gc, 0, 0,
+                pm[TBP_RIGHT_OF_TEXT]->width, title_height,
+                under_offset + under_width, 0);
+      right_space -= pm[TBP_RIGHT_OF_TEXT]->width;
+    }
+
+    text_y_pos = title_height
+                 - (title_height - window->title_font.font->ascent) / 2
+                 - 1;
+    if (title_height > 19)
+      text_y_pos--;
+    if (text_y_pos < 0)
+      text_y_pos = 0;
+#ifdef I18N_MB
+    XmbDrawString(dpy, title_win, window->title_font.fontset, gc, text_offset,
+                  gc, text_offset, text_y_pos, title, strlen(title));
+#else
+    XDrawString(dpy, title_win, gc, text_offset, text_y_pos, title,
+                strlen(title));
+#endif
+  }
+  else
+    left_space = right_space = title_width;
+
+  if (pm[TBP_LEFT_END] && pm[TBP_LEFT_END]->width <= left_space)
+    XCopyArea(dpy, pm[TBP_LEFT_END]->picture, title_win, gc, 0, 0,
+              pm[TBP_LEFT_END]->width, title_height, 0, 0);
+  if (pm[TBP_RIGHT_END] && pm[TBP_RIGHT_END]->width <= right_space)
+    XCopyArea(dpy, pm[TBP_RIGHT_END]->picture, title_win, gc, 0, 0,
+              pm[TBP_RIGHT_END]->width, title_height,
+              title_width - pm[TBP_RIGHT_END]->width, 0);
+}
+
+#endif /* FANCY_TITLEBARS */
+
 /* rules to get button state */
 static enum ButtonState get_button_state(
   Bool has_focus, Bool toggled, Window w)
@@ -1029,6 +1212,7 @@
   Window expose_win, XRectangle *rclip)
 {
   int i;
+  int left1right0;
   Bool is_lowest = True;
   Pixmap *pass_bg_pixmap;
 
@@ -1042,8 +1226,10 @@
 
   for (i = 0; i < NUMBER_OF_BUTTONS; i++)
   {
-    if (t->button_w[i] != None && (((i & 1) && i / 2 < Scr.nr_right_buttons) ||
-				   (!(i & 1) && i / 2 < Scr.nr_left_buttons)))
+    left1right0 = !(i&1);
+    if (t->button_w[i] != None &&
+        ((!left1right0 && i/2 < Scr.nr_right_buttons) ||
+         (left1right0  && i/2 < Scr.nr_left_buttons)))
     {
       mwm_flags stateflags =
 	TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i]));
@@ -1097,8 +1283,8 @@
 	    DrawButton(t, t->button_w[i], t->title_g.height, t->title_g.height,
 		       tsdf, cd->relief_gc, cd->shadow_gc,
 		       cd->fore_color, cd->back_color, is_lowest,
-		       TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])), 1, NULL,
-		       pass_bg_pixmap);
+		       TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])),
+                       left1right0, NULL, pass_bg_pixmap);
 	    is_lowest = False;
 	  }
 	}
@@ -1110,8 +1296,8 @@
 	  DrawButton(t, t->button_w[i], t->title_g.height, t->title_g.height,
 		     df, cd->relief_gc, cd->shadow_gc,
 		     cd->fore_color, cd->back_color, is_lowest,
-		     TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])), 1, NULL,
-		     pass_bg_pixmap);
+		     TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])), left1right0,
+                     NULL, pass_bg_pixmap);
 	  is_lowest = False;
 	}
 
@@ -1276,6 +1462,11 @@
     /* draw compound titlebar ([email-masked]) */
     Bool is_lowest = True;
 
+#ifdef FANCY_TITLEBARS
+    if (df->style.face_type == MultiPixmap)
+      DrawMultiPixmapTitlebar(t, df);
+    else
+#endif
     if (PressedW == t->title_w)
     {
 #ifdef MULTISTYLE
@@ -1319,6 +1510,11 @@
       break;
     }
 
+#ifdef FANCY_TITLEBARS
+    if (TB_STATE(GetDecor(t, titlebar))[title_state].style.face_type
+        != MultiPixmap)
+#endif
+    {
 #ifdef I18N_MB
     if(t->name != (char *)NULL)
       XmbDrawString(dpy, t->title_w, t->title_font.fontset,
@@ -1329,6 +1525,7 @@
       XDrawString(dpy, t->title_w, Scr.TitleGC, hor_off,
 		  t->title_font.y + 1, t->name, strlen(t->name));
 #endif
+    }
   }
 
   /*
--- fvwm/builtins.c.orig	Wed Oct 10 01:26:55 2001
+++ fvwm/builtins.c	Mon Oct 15 02:01:25 2001
@@ -63,6 +63,9 @@
 
 static char *ReadTitleButton(
     char *s, TitleButton *tb, Boolean append, int button);
+#ifdef FANCY_TITLEBARS
+static char *ReadMultiPixmapDecor(char *s, DecorFace *df);
+#endif
 static void DestroyFvwmDecor(FvwmDecor *decor);
 
 extern float rgpctMovementDefault[32];
@@ -1401,6 +1404,10 @@
 
 void FreeDecorFace(Display *dpy, DecorFace *df)
 {
+#ifdef FANCY_TITLEBARS
+  int i;
+#endif
+
   switch (DFS_FACE_TYPE(df->style))
   {
   case GradientButton:
@@ -1418,6 +1425,18 @@
       DestroyPicture(dpy, df->u.p);
     break;
 
+#ifdef FANCY_TITLEBARS
+  case MultiPixmap:
+    if (df->u.multi_pixmaps) {
+      for (i=0; i < NUM_TB_PIXMAPS; i++) {
+        if (df->u.multi_pixmaps[i])
+          DestroyPicture(dpy, df->u.multi_pixmaps[i]);
+      }
+      free(df->u.multi_pixmaps);
+    }
+    break;
+#endif
+
   case VectorButton:
   case DefaultVectorButton:
     if (df->u.vector.x)
@@ -1649,6 +1668,19 @@
       else
 	DFS_FACE_TYPE(df->style) = PixmapButton;
     }
+#ifdef FANCY_TITLEBARS
+    else if (strncasecmp(style,"MultiPixmap",11)==0) {
+      if (button != -1) {
+        if (verbose)
+          fvwm_msg(ERR, "ReadDecorFace",
+                   "MultiPixmap is only valid for TitleStyle");
+        return False;
+      }
+      s = ReadMultiPixmapDecor(s, df);
+      if (!s)
+        return False;
+    }
+#endif
 #ifdef MINI_ICONS
     else if (strncasecmp (style, "MiniIcon", 8) == 0)
     {
@@ -1956,6 +1988,78 @@
   return end;
 }
 
+#ifdef FANCY_TITLEBARS
+/*****************************************************************************
+ *
+ * Reads a multi-pixmap titlebar config. ([email-masked])
+ *
+ ****************************************************************************/
+static char *ReadMultiPixmapDecor(char *s, DecorFace *df)
+{
+  static char *pm_names[NUM_TB_PIXMAPS+1] = {
+    "Main",
+    "LeftMain",
+    "RightMain",
+    "UnderText",
+    "LeftOfText",
+    "RightOfText",
+    "LeftEnd",
+    "RightEnd",
+    "Buttons",
+    "LeftButtons",
+    "RightButtons",
+    NULL
+  };
+  Picture **pm;
+  char *token;
+  Bool stretched;
+  int pm_id, i;
+
+  df->style.face_type = MultiPixmap;
+  df->u.multi_pixmaps = pm =
+    (Picture**)safecalloc(NUM_TB_PIXMAPS, sizeof(Picture*));
+
+  s = GetNextTokenIndex(s, pm_names, 0, &pm_id);
+  while (pm_id >= 0) {
+    s = DoPeekToken(s, &token, ",()", NULL, NULL);
+    stretched = False;
+    if (StrEquals(token, "stretched")) {
+      stretched = True;
+      s = DoPeekToken(s, &token, ",", NULL, NULL);
+    }
+    else if (StrEquals(token, "tiled"))
+      s = DoPeekToken(s, &token, ",", NULL, NULL);
+    if (!token)
+      break;
+    if (pm[pm_id]) {
+      fvwm_msg(WARN, "ReadMultiPixmapDecor",
+               "Ignoring already-specified %s pixmap", pm_names[i]);
+      continue;
+    }
+    if (stretched)
+      df->u.multi_stretch_flags |= (1 << pm_id);
+    pm[pm_id] = CachePicture(dpy, Scr.NoFocusWin, NULL, token, Scr.ColorLimit);
+    if (!pm[pm_id])
+      fvwm_msg(ERR, "ReadMultiPixmapDecor", "Pixmap '%s' could not be loaded",
+               token);
+    s = GetNextTokenIndex(s, pm_names, 0, &pm_id);
+  }
+
+  if (!pm[TBP_MAIN] && !(pm[TBP_LEFT_MAIN] && pm[TBP_RIGHT_MAIN])) {
+    fvwm_msg(ERR, "ReadMultiPixmapDecor",
+             "No Main pixmap found for TitleStyle MultiPixmap "
+             "(you must specify either Main, or both LeftMain and RightMain)");
+    for (i=0; i < NUM_TB_PIXMAPS; i++) {
+      if (pm[i])
+        DestroyPicture(dpy, pm[i]);
+    }
+    free(pm);
+    return NULL;
+  }
+
+  return s;
+}
+#endif /* FANCY_TITLEBARS */
 
 #ifdef USEDECOR
 /*****************************************************************************