Hello all, I've been tinkering with the FVWM 2.4.2 source code lately, trying to rectify one of its few remaining lacks of themability :-) I've developed a patch which adds a new type of TitleStyle. You can specify up to six pixmaps for the titlebar in each state: main, under text, left of text, right of text, far left, and far right (the first two are tiled, the rest aren't). This allows FVWM to do a quite pleasing rendition of themes like Eazel-Blue and Crux (though without the fancy borders).
The config syntax looks like this currently: TitleStyle Fancy <state> <main pixmap> <far left> <left of text> <under text> <right of text> <far right> You can specify "no pixmap" at any position by substituting '-' for a pixmap filename. The main pixmap will be tiled over any resulting empty spaces. Bearing in mind that the patch is currently buggy (titleunder pixmap often doesn't update properly), unpolished, and the work of a complete FVWM/X programming newbie, you can find it attached to this e-mail and apply it to 2.4.2 via "patch -p0 <patchfile" from the top source dir. You also have to #define FANCY_TITLEBARS in config.h to activate any of the new code. Would there be any interest in integrating this into the main source tree? Suzanne -- [EMAIL PROTECTED] - 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 3 20:36:03 2001 +++ ChangeLog Wed Oct 3 20:40:15 2001 @@ -1,3 +1,33 @@ +2001-10-03 Suzanne Britton <[EMAIL PROTECTED]> + + * config.h.in: Added FANCY_TITLEBARS option, off by default + + * fvwm/screen.h: + [All changes #ifdeffed FANCY_TITLEBARS] + Added TITLE_PADDING define for fancy titlebar rendering + Added FancyTitlebar to DecorFaceType + Added tb_pixmap_enum + Added Titlebar struct (identical to TitleButton except for + Picture **pixmaps member) + Changed "TitleButton titlebar" in FvwmDecor to "Titlebar titlebar" + + * fvwm/borders.c: + [All changes #ifdeffed FANCY_TITLEBARS] + (DrawFancyTitlebar): [new] + (RedrawTitle): + Call DrawFancyTitlebar for FancyTitlebar style + Skip DrawString calls for FancyTitlebar style (title is drawn inside + DrawFancyTitlebar) + + * fvwm/builtins.c: + [All changes #ifdeffed FANCY_TITLEBARS] + (ReadTitlebar): [new] + (ReadFancyTitlebar): [new] + (do_title_style): Call ReadFancyTitlebar or ReadTitlebar for + TitleStyle command (instead of ReadTitleButton) + (InitFvwmDecor): Init decor->titlebar.pixmaps to NULL + (DestroyFvwmDecor): Free up decor->titlebar.pixmaps + 2001-09-16 Dominik Vogt <[EMAIL PROTECTED]> * configure.in: --- AUTHORS.orig Wed Oct 3 20:59:12 2001 +++ AUTHORS Wed Oct 3 20:58:41 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 3 20:05:40 2001 +++ config.h.in Sat Sep 29 20:59:34 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/screen.h.orig Wed Oct 3 20:06:01 2001 +++ fvwm/screen.h Wed Oct 3 20:38:53 2001 @@ -50,6 +50,10 @@ #define SIZE_HINDENT 5 #define SIZE_VINDENT 3 +#ifdef FANCY_TITLEBARS +#define TITLE_PADDING 5 +#endif + /* colormap focus styes */ #define COLORMAP_FOLLOWS_MOUSE 1 /* default */ #define COLORMAP_FOLLOWS_FOCUS 2 @@ -70,6 +74,9 @@ GradientButton , PixmapButton , TiledPixmapButton , +#ifdef FANCY_TITLEBARS + FancyTitlebar , +#endif #ifdef MINI_ICONS MiniIconButton , #endif @@ -191,6 +198,22 @@ DecorFace state[BS_MaxButtonState]; } TitleButton; +#ifdef FANCY_TITLEBARS +enum tb_pixmap_enum {TBP_MAIN, TBP_LEFT, TBP_LOT, TBP_UNDER, TBP_ROT, + TBP_RIGHT, NUM_TB_PIXMAPS}; +typedef struct +{ + unsigned just : 2; /* was JustificationType : 2 */ + struct + { + unsigned has_changed : 1; + mwm_flags mwm_decor_flags : 9; + } flags; + DecorFace state[BS_MaxButtonState]; + Picture **pixmaps; +} Titlebar; +#endif + #define TB_FLAGS(tb) ((tb).flags) #define TB_STATE(tb) ((tb).state) #define TB_JUSTIFICATION(tb) ((tb).just) @@ -216,7 +239,11 @@ int title_height; /* explicitly specified title bar height */ /* titlebar buttons */ TitleButton buttons[NUMBER_OF_BUTTONS]; +#ifdef FANCY_TITLEBARS + Titlebar titlebar; +#else TitleButton titlebar; +#endif struct BorderStyle { DecorFace active, inactive; --- fvwm/borders.c.orig Wed Oct 3 20:06:21 2001 +++ fvwm/borders.c Wed Oct 3 20:38:42 2001 @@ -448,6 +448,173 @@ return; } +#ifdef FANCY_TITLEBARS +/**************************************************************************** + * + * Redraws multi-pixmap titlebar ([EMAIL PROTECTED]) + * + ****************************************************************************/ +static void DrawFancyTitlebar(FvwmWindow *window, Titlebar *decor) +{ + Window title_win; + Picture **pixmaps; + GC gc; + const char *title; + int title_width, title_height, text_width, text_offset; + int left_space, right_space; + int state = BS_Inactive; + int under_offset, under_width; + Bool toggled; + mwm_flags state_flags; + + if (!(decor->pixmaps)) { + fvwm_msg(ERR, "DrawFancyTitlebar", "no pixmaps defined"); + return; + } + + gc = Scr.TitleGC; + title_win = window->title_w; + title = window->name; + title_width = window->title_g.width; + title_height = window->title_g.height; + + if (Scr.Hilite == window) { + if (PressedW == title_win) + state = BS_ActiveDown; + else + state = BS_ActiveUp; + } + state_flags = TB_MWM_DECOR_FLAGS(*decor); + toggled = (HAS_MWM_BUTTONS(window) && + ((state_flags & MWM_DECOR_MAXIMIZE && IS_MAXIMIZED(window)) || + (state_flags & MWM_DECOR_SHADE && IS_SHADED(window)) || + (state_flags & MWM_DECOR_STICK && IS_STICKY(window)))); + if (toggled) + state += BS_ToggledActiveUp; + pixmaps = decor->pixmaps + (state * NUM_TB_PIXMAPS); + + 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(*decor)) { + case JUST_LEFT: + text_offset = TITLE_PADDING; + if (pixmaps[TBP_LEFT]) + text_offset += pixmaps[TBP_LEFT]->width; + if (pixmaps[TBP_LOT]) + text_offset += pixmaps[TBP_LOT]->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 (pixmaps[TBP_RIGHT]) + text_offset -= pixmaps[TBP_RIGHT]->width; + if (pixmaps[TBP_ROT]) + text_offset -= pixmaps[TBP_ROT]->width; + if (text_offset < 0) + text_offset = 0; + break; + default: + text_offset = (title_width - text_width) / 2; + break; + } + } + else { + text_width = 0; + text_offset = -1; + } + + if (pixmaps[TBP_MAIN]) { + Pixmap pm = CreateTiledPixmap(dpy, + pixmaps[TBP_MAIN]->picture, + pixmaps[TBP_MAIN]->width, + pixmaps[TBP_MAIN]->height, + title_width, + title_height, + pixmaps[TBP_MAIN]->depth, + gc); + XCopyArea(dpy, pm, title_win, gc, 0, 0, title_width, title_height, 0, 0); + XFreePixmap(dpy, pm); + } + + under_offset = text_offset; + under_width = text_width; + if (under_offset >= TITLE_PADDING) { + under_offset -= TITLE_PADDING; + under_width += TITLE_PADDING; + } + if (under_offset >= 0 && + under_offset + under_width + TITLE_PADDING <= title_width) + under_width += TITLE_PADDING; + left_space = ((under_offset < 0) ? title_width/2 : under_offset); + right_space = ((under_offset < 0) ? title_width/2 : + title_width - under_width - under_offset); + + if (pixmaps[TBP_UNDER] && text_width > 0) { + Pixmap pm = CreateTiledPixmap(dpy, + pixmaps[TBP_UNDER]->picture, + pixmaps[TBP_UNDER]->width, + pixmaps[TBP_UNDER]->height, + under_width, title_height, + pixmaps[TBP_UNDER]->depth, + gc); + XCopyArea(dpy, pm, title_win, gc, 0, 0, under_width, title_height, + under_offset, 0); + XFreePixmap(dpy, pm); + } + if (pixmaps[TBP_LEFT] && pixmaps[TBP_LEFT]->width <= left_space) { + XCopyArea(dpy, pixmaps[TBP_LEFT]->picture, title_win, gc, 0, 0, + pixmaps[TBP_LEFT]->width, title_height, 0, 0); + left_space -= pixmaps[TBP_LEFT]->width; + } + if (pixmaps[TBP_RIGHT] && pixmaps[TBP_RIGHT]->width <= right_space) { + XCopyArea(dpy, pixmaps[TBP_RIGHT]->picture, title_win, gc, 0, 0, + pixmaps[TBP_RIGHT]->width, title_height, + title_width - pixmaps[TBP_RIGHT]->width, 0); + right_space -= pixmaps[TBP_RIGHT]->width; + } + if (pixmaps[TBP_LOT] && pixmaps[TBP_LOT]->width <= left_space && + text_width > 0) { + XCopyArea(dpy, pixmaps[TBP_LOT]->picture, title_win, gc, 0, 0, + pixmaps[TBP_LOT]->width, title_height, + under_offset - pixmaps[TBP_LOT]->width, 0); + left_space -= pixmaps[TBP_LOT]->width; + } + if (pixmaps[TBP_ROT] && pixmaps[TBP_ROT]->width <= right_space && + text_width > 0 ) { + XCopyArea(dpy, pixmaps[TBP_ROT]->picture, title_win, gc, 0, 0, + pixmaps[TBP_ROT]->width, title_height, + under_offset + under_width, 0); + right_space -= pixmaps[TBP_ROT]->width; + } + + if (title) { + int title_y_pos = title_height + - (title_height - window->title_font.font->ascent) / 2 + - 1; + if (title_height > 19) + title_y_pos--; + if (title_y_pos < 0) + title_y_pos = 0; +#ifdef I18N_MB + XmbDrawString(dpy, title_win, window->title_font.fontset, gc, text_offset, + gc, text_offset, title_y_pos, title, strlen(title)); +#else + XDrawString(dpy, title_win, gc, text_offset, title_y_pos, title, + strlen(title)); +#endif + } +} +#endif /* FANCY_TITLEBARS */ + /* rules to get button state */ static enum ButtonState get_button_state( Bool has_focus, Bool toggled, Window w) @@ -1276,6 +1443,11 @@ /* draw compound titlebar ([EMAIL PROTECTED]) */ Bool is_lowest = True; +#ifdef FANCY_TITLEBARS + if (df->style.face_type == FancyTitlebar) + DrawFancyTitlebar(t, &(GetDecor(t, titlebar))); + else +#endif if (PressedW == t->title_w) { #ifdef MULTISTYLE @@ -1319,6 +1491,11 @@ break; } +#ifdef FANCY_TITLEBARS + if (TB_STATE(GetDecor(t, titlebar))[title_state].style.face_type + != FancyTitlebar) +#endif + { #ifdef I18N_MB if(t->name != (char *)NULL) XmbDrawString(dpy, t->title_w, t->title_font.fontset, @@ -1329,6 +1506,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 3 20:06:10 2001 +++ fvwm/builtins.c Wed Oct 3 21:07:51 2001 @@ -63,6 +63,10 @@ static char *ReadTitleButton( char *s, TitleButton *tb, Boolean append, int button); +#ifdef FANCY_TITLEBARS +static char *ReadTitlebar(char *s, Titlebar *tb, Boolean append, int button); +static char *ReadFancyTitlebar(char *s, Titlebar *tb, Boolean append); +#endif static void DestroyFvwmDecor(FvwmDecor *decor); extern float rgpctMovementDefault[32]; @@ -1164,10 +1168,20 @@ if (action) action += next; } +#ifdef FANCY_TITLEBARS + else if (StrEquals(parm, "fancy")) { + action = ReadFancyTitlebar(prev, &decor->titlebar, do_add); + } + else + { + action = ReadTitlebar(prev, &decor->titlebar, do_add, -1); + } +#else else { action = ReadTitleButton(prev, &decor->titlebar, do_add, -1); } +#endif } } @@ -1956,6 +1970,224 @@ return end; } +#ifdef FANCY_TITLEBARS + +/***************************************************************************** + * + * Reads a (non-FANCY) titlebar description. Nearly identical to + * ReadTitleButton. ([EMAIL PROTECTED]) + * + ****************************************************************************/ +static char *ReadTitlebar( + char *s, Titlebar *tb, Boolean append, int button) +{ + char *end = NULL; + char *spec; + char *t; + int i; + int bs; + int pstyle = 0; + DecorFace tmpdf; + Bool do_set_all = False; + + s = SkipSpaces(s, NULL, 0); + t = GetNextTokenIndex(s, button_states, 0, &bs); + if (bs != BS_All) + { + s = SkipSpaces(t, NULL, 0); + } + else + { + do_set_all = True; + } + + if (*s == '(') + { + int len; + pstyle = 1; + if (!(end = strchr(++s, ')'))) + { + fvwm_msg(ERR, "ReadTitlebar", "missing parenthesis: %s", s); + return NULL; + } + s = SkipSpaces(s, NULL, 0); + len = end - s + 1; + spec = safemalloc(len); + strncpy(spec, s, len - 1); + spec[len - 1] = 0; + } + else + { + spec = s; + } + + spec = SkipSpaces(spec, NULL, 0); + /* setup temporary in case button read fails */ + memset(&tmpdf, 0, sizeof(DecorFace)); + DFS_FACE_TYPE(tmpdf.style) = SimpleButton; + + if (strncmp(spec, "--",2)==0) + { + /* only change flags */ + if (do_set_all) + { + for (i = 0; i < BS_MaxButtonState; ++i) + { + ReadDecorFace(spec, &TB_STATE(*tb)[i], BS_All, !i); + } + } + else + { + ReadDecorFace(spec, &TB_STATE(*tb)[bs], button, True); + } + } + else if (ReadDecorFace(spec, &tmpdf, button, True)) + { + int b = (do_set_all) ? 0 : bs; +#ifdef MULTISTYLE + if (append) + { + DecorFace *head = &TB_STATE(*tb)[b]; + DecorFace *tail = head; + DecorFace *next; + + while (tail->next) + { + tail = tail->next; + } + tail->next = (DecorFace *)safemalloc(sizeof(DecorFace)); + memcpy(tail->next, &tmpdf, sizeof(DecorFace)); + if (DFS_FACE_TYPE(tail->next->style) == VectorButton && + DFS_FACE_TYPE((&TB_STATE(*tb)[b])->style) == DefaultVectorButton) + { + /* override the default vector style */ + memcpy(&tail->next->style, &head->style, sizeof(DecorFaceStyle)); + DFS_FACE_TYPE(tail->next->style) = VectorButton; + next = head->next; + head->next = NULL; + FreeDecorFace(dpy, head); + memcpy(head, next, sizeof(DecorFace)); + free(next); + } + if (do_set_all) + { + for (i = 1; i < BS_MaxButtonState; ++i) + { + if (i == b) + { + /* already done above */ + continue; + } + head = &TB_STATE(*tb)[i]; + tail = head; + while (tail->next) + { + tail = tail->next; + } + tail->next = (DecorFace *)safemalloc(sizeof(DecorFace)); + memset(&DFS_FLAGS(tail->next->style), 0, + sizeof(DFS_FLAGS(tail->next->style))); + DFS_FACE_TYPE(tail->next->style) = SimpleButton; + tail->next->next = NULL; + ReadDecorFace(spec, tail->next, button, False); + if (DFS_FACE_TYPE(tail->next->style) == VectorButton && + DFS_FACE_TYPE((&TB_STATE(*tb)[i])->style) == DefaultVectorButton) + { + /* override the default vector style */ + memcpy(&tail->next->style, &head->style, sizeof(DecorFaceStyle)); + DFS_FACE_TYPE(tail->next->style) = VectorButton; + next = head->next; + head->next = NULL; + FreeDecorFace(dpy, head); + memcpy(head, next, sizeof(DecorFace)); + free(next); + } + } + } + } + else +#endif + { + FreeDecorFace(dpy, &TB_STATE(*tb)[b]); + memcpy(&(TB_STATE(*tb)[b]), &tmpdf, sizeof(DecorFace)); + if (do_set_all) + { + for (i = 1; i < BS_MaxButtonState; ++i) + { + ReadDecorFace(spec, &TB_STATE(*tb)[i], button, False); + } + } + } + } + if (pstyle) + { + free(spec); + end++; + end = SkipSpaces(end, NULL, 0); + } + + return end; +} + +/***************************************************************************** + * + * Reads a multi-pixmap titlebar description ([EMAIL PROTECTED]) + * + ****************************************************************************/ +static char *ReadFancyTitlebar(char *s, Titlebar *tb, Boolean append) +{ + int state; + int i, j; + char *pixmap_filenames[NUM_TB_PIXMAPS]; + + s = SkipSpaces(s, NULL, 0); + s = SkipNTokens(s, 1); /* skip "Fancy" */ + s = SkipSpaces(s, NULL, 0); + s = GetNextTokenIndex(s, button_states, 0, &state); + if (state < 0) { + fvwm_msg(ERR, "ReadFancyTitlebar", "Expecting a state specification (e.g. Inactive) after TitleStyle Fancy"); + return s; + } + s = SkipSpaces(s, NULL, 0); + + for (i=0; i < NUM_TB_PIXMAPS; i++) { + s = GetNextToken(s, &pixmap_filenames[i]); + if (!pixmap_filenames[i]) { + fvwm_msg(ERR, "ReadFancyTitlebar", "Expecting %d pixmap filenames after TitleStyle Fancy (use '-' to specify no pixmap)", NUM_TB_PIXMAPS); + return s; + } + } + if (!(tb->pixmaps)) { + tb->pixmaps = (Picture**)safemalloc(NUM_TB_PIXMAPS * BS_MaxButtonState * + sizeof(Picture*)); + memset(tb->pixmaps, 0, NUM_TB_PIXMAPS * BS_MaxButtonState * + sizeof(Picture*)); + } + + tb->state[state].style.face_type = FancyTitlebar; + + for (i=0, j = state * NUM_TB_PIXMAPS; + i < NUM_TB_PIXMAPS; + i++, j++) { + if (tb->pixmaps[j]) + DestroyPicture(dpy, tb->pixmaps[j]); + if (StrEquals(pixmap_filenames[i], "-")) + tb->pixmaps[j] = NULL; + else { + tb->pixmaps[j] = CachePicture(dpy, Scr.NoFocusWin, NULL, + pixmap_filenames[i], Scr.ColorLimit); + if (!tb->pixmaps[j]) + fvwm_msg(WARN, "ReadFancyTitlebar", "Pixmap '%s' could not be loaded", + pixmap_filenames[i]); + } + } + + for (i=0; i < NUM_TB_PIXMAPS; i++) + free(pixmap_filenames[i]); + return s; +} + +#endif /* FANCY_TITLEBARS */ #ifdef USEDECOR /***************************************************************************** @@ -2138,6 +2370,9 @@ { DFS_FACE_TYPE(TB_STATE(decor->titlebar)[i].style) = SimpleButton; } +#ifdef FANCY_TITLEBARS + decor->titlebar.pixmaps = NULL; +#endif /* initialize border texture styles */ DFS_FACE_TYPE(decor->BorderStyle.active.style) = SimpleButton; @@ -2170,6 +2405,17 @@ { free(decor->tag); decor->tag = NULL; + } +#endif +#ifdef FANCY_TITLEBARS + if (decor->titlebar.pixmaps) { + int j; + for (j=0; j < NUM_TB_PIXMAPS * BS_MaxButtonState; j++) { + if (decor->titlebar.pixmaps[j]) + DestroyPicture(dpy, decor->titlebar.pixmaps[j]); + } + free(decor->titlebar.pixmaps); + decor->titlebar.pixmaps = NULL; } #endif }