diff --git a/patches/icons.diff b/patches/icons.diff new file mode 100644 index 0000000..2663eff --- /dev/null +++ b/patches/icons.diff @@ -0,0 +1,315 @@ +diff --git a/config.h b/config.h +index a3e4f95..ca7c903 100644 +--- a/config.h ++++ b/config.h +@@ -18,3 +18,6 @@ static int separator_pixels = 3; /* space around separator */ + /* geometry of the right-pointing isoceles triangle for submenus */ + static const int triangle_width = 3; + static const int triangle_height = 7; ++ ++/* sum of padding around both sides of the image */ ++static const int imgpadding = 8; +diff --git a/config.mk b/config.mk +index f86aa34..0ffc8c5 100644 +--- a/config.mk ++++ b/config.mk +@@ -14,8 +14,8 @@ FREETYPELIB = -lfontconfig -lXft + #FREETYPEINC = $(X11INC)/freetype2 + + # includes and libs +-INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -L${FREETYPELIB} -lX11 ++INCS = -I/usr/local/include -I${X11INC} -I${FREETYPEINC} ++LIBS = -L/usr/local/lib -L${X11LIB} -L${FREETYPELIB} -lX11 -lImlib2 + + # flags + CPPFLAGS = +diff --git a/xmenu.1 b/xmenu.1 +index d114668..5201032 100644 +--- a/xmenu.1 ++++ b/xmenu.1 +@@ -13,17 +13,21 @@ and outputs the item selected to stdout. + Each item read from stdin has the following format: + .IP + .EX +-ITEM := [TABS] [LABEL [TABS OUTPUT]] NEWLINE ++ITEM := [TABS] [[IMAGE TABS] LABEL [TABS OUTPUT]] NEWLINE + .EE + .PP + That means that each item is composed by +-tabs, followed by a label, followed by more tabs, followed by an output, ++tabs, followed by an optional image specification, followed by tabs ++followed by a label, followed by more tabs, followed by an output, + and ended by a newline. Brackets group optional elements. + .IP + The initial tabs indicate the menu hierarchy: + items indented with a tab is shown in a submenu of the preceding item not indented. + An item without initial tabs is a top-level item. + .IP ++The image is a string of the form "IMG:/path/to/image.png". ++It specifies a image to be shown as icon at the left of the entry. ++.IP + The label is the string that will be shown as a item in the menu. + An item without label is considered a separator and is drawn as a thin line in the menu + separating the item above from the item below. +@@ -104,14 +108,14 @@ creating a command to be run by the shell. + + cat < + #include + #include ++#include + + #define PROGNAME "xmenu" + #define ITEMPREV 0 +@@ -45,12 +46,14 @@ struct Geometry { + struct Item { + char *label; /* string to be drawed on menu */ + char *output; /* string to be outputed when item is clicked */ ++ char *file; /* filename of the image */ + int y; /* item y position relative to menu */ + int h; /* item height */ + size_t labellen; /* strlen(label) */ + struct Item *prev; /* previous item */ + struct Item *next; /* next item */ + struct Menu *submenu; /* submenu spawned by clicking on item */ ++ Imlib_Image image; + }; + + /* menu structure */ +@@ -71,9 +74,9 @@ static void getresources(void); + static void getcolor(const char *s, XftColor *color); + static void setupdc(void); + static void calcgeom(struct Geometry *geom); +-static struct Item *allocitem(const char *label, const char *output); ++static struct Item *allocitem(const char *label, const char *output, char *file); + static struct Menu *allocmenu(struct Menu *parent, struct Item *list, unsigned level); +-static struct Menu *buildmenutree(unsigned level, const char *label, const char *output); ++static struct Menu *buildmenutree(unsigned level, const char *label, const char *output, char *file); + static struct Menu *parsestdin(void); + static void calcmenu(struct Geometry *geom, struct Menu *menu); + static void grabpointer(void); +@@ -129,6 +132,13 @@ main(int argc, char *argv[]) + rootwin = RootWindow(dpy, screen); + colormap = DefaultColormap(dpy, screen); + ++ /* imlib2 stuff */ ++ imlib_set_cache_size(2048 * 1024); ++ imlib_context_set_dither(1); ++ imlib_context_set_display(dpy); ++ imlib_context_set_visual(visual); ++ imlib_context_set_colormap(colormap); ++ + /* setup */ + getresources(); + setupdc(); +@@ -247,7 +257,7 @@ calcgeom(struct Geometry *geom) + + /* allocate an item */ + static struct Item * +-allocitem(const char *label, const char *output) ++allocitem(const char *label, const char *output, char *file) + { + struct Item *item; + +@@ -266,6 +276,12 @@ allocitem(const char *label, const char *output) + err(1, "strdup"); + } + } ++ if (file == NULL) { ++ item->file = NULL; ++ } else { ++ if ((item->file = strdup(file)) == NULL) ++ err(1, "strdup"); ++ } + item->y = 0; + item->h = 0; + if (item->label == NULL) +@@ -274,6 +290,7 @@ allocitem(const char *label, const char *output) + item->labellen = strlen(item->label); + item->next = NULL; + item->submenu = NULL; ++ item->image = NULL; + + return item; + } +@@ -314,7 +331,7 @@ allocmenu(struct Menu *parent, struct Item *list, unsigned level) + + /* build the menu tree */ + static struct Menu * +-buildmenutree(unsigned level, const char *label, const char *output) ++buildmenutree(unsigned level, const char *label, const char *output, char *file) + { + static struct Menu *prevmenu = NULL; /* menu the previous item was added to */ + static struct Menu *rootmenu = NULL; /* menu to be returned */ +@@ -324,7 +341,7 @@ buildmenutree(unsigned level, const char *label, const char *output) + unsigned i; + + /* create the item */ +- curritem = allocitem(label, output); ++ curritem = allocitem(label, output, file); + + /* put the item in the menu tree */ + if (prevmenu == NULL) { /* there is no menu yet */ +@@ -377,7 +394,7 @@ parsestdin(void) + { + struct Menu *rootmenu; + char *s, buf[BUFSIZ]; +- char *label, *output; ++ char *file, *label, *output; + unsigned level = 0; + + rootmenu = NULL; +@@ -390,6 +407,13 @@ parsestdin(void) + s = level + buf; + label = strtok(s, "\t\n"); + ++ /* get the filename */ ++ file = NULL; ++ if (label != NULL && strncmp(label, "IMG:", 4) == 0) { ++ file = label + 4; ++ label = strtok(NULL, "\t\n"); ++ } ++ + /* get the output */ + output = strtok(NULL, "\n"); + if (output == NULL) { +@@ -399,12 +423,36 @@ parsestdin(void) + output++; + } + +- rootmenu = buildmenutree(level, label, output); ++ rootmenu = buildmenutree(level, label, output, file); + } + + return rootmenu; + } + ++/* load and scale image */ ++static Imlib_Image ++loadimage(const char *file, int size) ++{ ++ Imlib_Image image; ++ int width; ++ int height; ++ int imgsize; ++ ++ image = imlib_load_image(file); ++ if (image == NULL) ++ errx(1, "cannot load image %s", file); ++ ++ imlib_context_set_image(image); ++ ++ width = imlib_image_get_width(); ++ height = imlib_image_get_height(); ++ imgsize = MIN(width, height); ++ ++ image = imlib_create_cropped_scaled_image(0, 0, imgsize, imgsize, size, size); ++ ++ return image; ++} ++ + /* recursivelly calculate menu geometry and set window hints */ + static void + calcmenu(struct Geometry *geom, struct Menu *menu) +@@ -430,8 +478,12 @@ calcmenu(struct Geometry *geom, struct Menu *menu) + + XftTextExtentsUtf8(dpy, dc.font, (XftChar8 *)item->label, + item->labellen, &ext); +- labelwidth = ext.xOff + dc.font->height * 2; ++ labelwidth = ext.xOff + dc.font->height * 2 + imgpadding; + menu->w = MAX(menu->w, labelwidth); ++ ++ /* create image */ ++ if (item->file != NULL) ++ item->image = loadimage(item->file, dc.font->height); + } + + /* calculate menu's x and y positions */ +@@ -621,7 +673,7 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color) + { + int x, y; + +- x = dc.font->height; ++ x = dc.font->height + imgpadding; + y = item->y + item->h/2 + dc.font->ascent/2 - 1; + XSetForeground(dpy, dc.gc, color[ColorFG].pixel); + XftDrawStringUtf8(menu->draw, &color[ColorFG], dc.font, +@@ -629,8 +681,8 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color) + + /* draw triangle, if item contains a submenu */ + if (item->submenu != NULL) { +- x = menu->w - dc.font->height/2 - triangle_width/2; +- y = item->y + item->h/2 - triangle_height/2 - 1; ++ x = menu->w - (dc.font->height - triangle_width) / 2; ++ y = item->y + (item->h - triangle_height) / 2; + + XPoint triangle[] = { + {x, y}, +@@ -642,6 +694,15 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color) + XFillPolygon(dpy, menu->pixmap, dc.gc, triangle, LEN(triangle), + Convex, CoordModeOrigin); + } ++ ++ /* draw image */ ++ if (item->file != NULL) { ++ x = imgpadding / 2; ++ y = item->y + (item->h - dc.font->height) / 2; ++ imlib_context_set_drawable(menu->pixmap); ++ imlib_context_set_image(item->image); ++ imlib_render_image_on_drawable(x, y); ++ } + } + + /* draw items of the current menu and of its ancestors */ +@@ -831,6 +892,13 @@ freemenu(struct Menu *menu) + if (tmp->label != tmp->output) + free(tmp->label); + free(tmp->output); ++ if (tmp->file != NULL) { ++ free(tmp->file); ++ if (item->image != NULL) { ++ imlib_context_set_image(item->image); ++ imlib_free_image(); ++ } ++ } + free(tmp); + } + +diff --git a/xmenu.sh b/xmenu.sh +index abd9a41..db08041 100755 +--- a/xmenu.sh ++++ b/xmenu.sh +@@ -2,7 +2,7 @@ + + cat <