Merging icons branch on master branch.
authorphillbush <phillbush@cock.li>
Sun, 31 May 2020 23:29:46 +0000 (20:29 -0300)
committerphillbush <phillbush@cock.li>
Sun, 31 May 2020 23:29:46 +0000 (20:29 -0300)
And adjusting the geometry of items.

README
config.mk
xmenu.1
xmenu.c
xmenu.sh

diff --git a/README b/README
index 5e2e226..2ddaa18 100644 (file)
--- a/README
+++ b/README
@@ -11,6 +11,15 @@ The -w (windowed) option was removed from the master branch.  It was too
 buggy in tiled window managers and required more code to be maintained.
 
 
 buggy in tiled window managers and required more code to be maintained.
 
 
+§ Features
+
+XMenu comes with the following features:
+• XMenu reads something in and prints something out, the UNIX way.
+• Submenus (some menu entries can spawn another menu).
+• Separators (menu entries can be separated by a line).
+• Icons (menu entries can follow by an icon image).
+
+
 § Files
 
 The files are:
 § Files
 
 The files are:
@@ -22,21 +31,14 @@ The files are:
 • ./xmenu.1:    The manual file (man page) for XMenu.
 • ./xmenu.c:    The source code of XMenu.
 • ./xmenu.sh:   A sample script illustrating how to use XMenu.
 • ./xmenu.1:    The manual file (man page) for XMenu.
 • ./xmenu.c:    The source code of XMenu.
 • ./xmenu.sh:   A sample script illustrating how to use XMenu.
-
-
-§ Branches
-
-There are other branches in this git repository that adds novel
-functionalities to XMenu.
-
-• icons:    Add suport to image icons before menu entries.
+• ./icons/:     Icons for the sample script
 
 
 § Installation
 
 First, edit ./config.mk to match your local setup.
 
 
 
 § Installation
 
 First, edit ./config.mk to match your local setup.
 
-In order to build XMenu you need the Xlib and the Xft header files.
+In order to build XMenu you need the Imlib2, Xlib and Xft header files.
 The default configuration for XMenu is specified in the file config.h,
 you can edit it, but most configuration can be changed at runtime via
 X resources.  Enter the following command to build XMenu.  This command
 The default configuration for XMenu is specified in the file config.h,
 you can edit it, but most configuration can be changed at runtime via
 X resources.  Enter the following command to build XMenu.  This command
index f86aa34..e2118dd 100644 (file)
--- a/config.mk
+++ b/config.mk
@@ -9,13 +9,12 @@ X11INC = /usr/X11R6/include
 X11LIB = /usr/X11R6/lib
 
 FREETYPEINC = /usr/include/freetype2
 X11LIB = /usr/X11R6/lib
 
 FREETYPEINC = /usr/include/freetype2
-FREETYPELIB = -lfontconfig -lXft
 # OpenBSD (uncomment)
 # OpenBSD (uncomment)
-#FREETYPEINC = $(X11INC)/freetype2
+#FREETYPEINC = ${X11INC}/freetype2
 
 # includes and libs
 
 # 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} -lfontconfig -lXft -lX11 -lImlib2
 
 # flags
 CPPFLAGS =
 
 # flags
 CPPFLAGS =
diff --git a/xmenu.1 b/xmenu.1
index d114668..5201032 100644 (file)
--- 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
 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
 .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
 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.
 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 <<EOF | xmenu | sh &
 Applications
 
 cat <<EOF | xmenu | sh &
 Applications
-       Web Browser     firefox
-       Image editor    gimp
-Terminal (xterm)       xterm
-Terminal (urxvt)       urxvt
-Terminal (st)          st
+       IMG:./web.png   Web Browser     firefox
+       Image editor                            gimp
+Terminal (xterm)                               xterm
+Terminal (urxvt)                               urxvt
+Terminal (st)                                  st
 
 
-Shutdown                       poweroff
-Reboot                 reboot
+Shutdown                                               poweroff
+Reboot                                         reboot
 EOF
 .EE
 .PP
 EOF
 .EE
 .PP
diff --git a/xmenu.c b/xmenu.c
index 2e9f389..6f75c2c 100644 (file)
--- a/xmenu.c
+++ b/xmenu.c
@@ -8,10 +8,12 @@
 #include <X11/Xresource.h>
 #include <X11/XKBlib.h>
 #include <X11/Xft/Xft.h>
 #include <X11/Xresource.h>
 #include <X11/XKBlib.h>
 #include <X11/Xft/Xft.h>
+#include <Imlib2.h>
 
 #define PROGNAME "xmenu"
 #define ITEMPREV 0
 #define ITEMNEXT 1
 
 #define PROGNAME "xmenu"
 #define ITEMPREV 0
 #define ITEMNEXT 1
+#define IMGPADDING 8
 
 /* macros */
 #define LEN(x) (sizeof (x) / sizeof (x[0]))
 
 /* macros */
 #define LEN(x) (sizeof (x) / sizeof (x[0]))
@@ -45,12 +47,14 @@ struct Geometry {
 struct Item {
        char *label;            /* string to be drawed on menu */
        char *output;           /* string to be outputed when item is clicked */
 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 */
        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 */
 };
 
 /* menu structure */
@@ -71,9 +75,9 @@ static void getresources(void);
 static void getcolor(const char *s, XftColor *color);
 static void setupdc(void);
 static void calcgeom(struct Geometry *geom);
 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 *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 setupmenusize(struct Geometry *geom, struct Menu *menu);
 static void setupmenupos(struct Geometry *geom, struct Menu *menu);
 static struct Menu *parsestdin(void);
 static void setupmenusize(struct Geometry *geom, struct Menu *menu);
 static void setupmenupos(struct Geometry *geom, struct Menu *menu);
@@ -131,6 +135,13 @@ main(int argc, char *argv[])
        rootwin = RootWindow(dpy, screen);
        colormap = DefaultColormap(dpy, screen);
 
        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();
        /* setup */
        getresources();
        setupdc();
@@ -249,7 +260,7 @@ calcgeom(struct Geometry *geom)
 
 /* allocate an item */
 static struct Item *
 
 /* 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;
 
 {
        struct Item *item;
 
@@ -268,6 +279,12 @@ allocitem(const char *label, const char *output)
                                err(1, "strdup");
                }
        }
                                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)
        item->y = 0;
        item->h = 0;
        if (item->label == NULL)
@@ -276,6 +293,7 @@ allocitem(const char *label, const char *output)
                item->labellen = strlen(item->label);
        item->next = NULL;
        item->submenu = NULL;
                item->labellen = strlen(item->label);
        item->next = NULL;
        item->submenu = NULL;
+       item->image = NULL;
 
        return item;
 }
 
        return item;
 }
@@ -316,7 +334,7 @@ allocmenu(struct Menu *parent, struct Item *list, unsigned level)
 
 /* build the menu tree */
 static struct Menu *
 
 /* 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 */
 {
        static struct Menu *prevmenu = NULL;    /* menu the previous item was added to */
        static struct Menu *rootmenu = NULL;    /* menu to be returned */
@@ -326,7 +344,7 @@ buildmenutree(unsigned level, const char *label, const char *output)
        unsigned i;
 
        /* create the item */
        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 */
 
        /* put the item in the menu tree */
        if (prevmenu == NULL) {                 /* there is no menu yet */
@@ -379,7 +397,7 @@ parsestdin(void)
 {
        struct Menu *rootmenu;
        char *s, buf[BUFSIZ];
 {
        struct Menu *rootmenu;
        char *s, buf[BUFSIZ];
-       char *label, *output;
+       char *file, *label, *output;
        unsigned level = 0;
 
        rootmenu = NULL;
        unsigned level = 0;
 
        rootmenu = NULL;
@@ -392,6 +410,13 @@ parsestdin(void)
                s = level + buf;
                label = strtok(s, "\t\n");
 
                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) {
                /* get the output */
                output = strtok(NULL, "\n");
                if (output == NULL) {
@@ -401,12 +426,36 @@ parsestdin(void)
                                output++;
                }
 
                                output++;
                }
 
-               rootmenu = buildmenutree(level, label, output);
+               rootmenu = buildmenutree(level, label, output, file);
        }
 
        return rootmenu;
 }
 
        }
 
        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;
+}
+
 /* setup the size of a menu and the position of its items */
 static void
 setupmenusize(struct Geometry *geom, struct Menu *menu)
 /* setup the size of a menu and the position of its items */
 static void
 setupmenusize(struct Geometry *geom, struct Menu *menu)
@@ -428,8 +477,12 @@ setupmenusize(struct Geometry *geom, struct Menu *menu)
                /* get length of item->label rendered in the font */
                XftTextExtentsUtf8(dpy, dc.font, (XftChar8 *)item->label,
                                   item->labellen, &ext);
                /* get length of item->label rendered in the font */
                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 * 2;
                menu->w = MAX(menu->w, labelwidth);
                menu->w = MAX(menu->w, labelwidth);
+
+               /* create image */
+               if (item->file != NULL)
+                       item->image = loadimage(item->file, dc.font->height);
        }
 }
 
        }
 }
 
@@ -639,7 +692,7 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color)
 {
        int x, y;
 
 {
        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,
        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,
@@ -647,7 +700,7 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color)
 
        /* draw triangle, if item contains a submenu */
        if (item->submenu != NULL) {
 
        /* draw triangle, if item contains a submenu */
        if (item->submenu != NULL) {
-               x = menu->w - dc.font->height/2 - triangle_width/2;
+               x = menu->w - dc.font->height/2 - IMGPADDING/2 - triangle_width/2 - 1;
                y = item->y + item->h/2 - triangle_height/2 - 1;
 
                XPoint triangle[] = {
                y = item->y + item->h/2 - triangle_height/2 - 1;
 
                XPoint triangle[] = {
@@ -660,6 +713,15 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color)
                XFillPolygon(dpy, menu->pixmap, dc.gc, triangle, LEN(triangle),
                             Convex, CoordModeOrigin);
        }
                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 */
 }
 
 /* draw items of the current menu and of its ancestors */
@@ -845,10 +907,17 @@ freemenu(struct Menu *menu)
                if (item->submenu != NULL)
                        freemenu(item->submenu);
                tmp = item;
                if (item->submenu != NULL)
                        freemenu(item->submenu);
                tmp = item;
-               item = item->next;
                if (tmp->label != tmp->output)
                        free(tmp->label);
                free(tmp->output);
                if (tmp->label != tmp->output)
                        free(tmp->label);
                free(tmp->output);
+               if (tmp->file != NULL) {
+                       free(tmp->file);
+                       if (tmp->image != NULL) {
+                               imlib_context_set_image(tmp->image);
+                               imlib_free_image();
+                       }
+               }
+               item = item->next;
                free(tmp);
        }
 
                free(tmp);
        }
 
index abd9a41..c832bb7 100755 (executable)
--- a/xmenu.sh
+++ b/xmenu.sh
@@ -2,8 +2,8 @@
 
 cat <<EOF | xmenu | sh &
 Applications
 
 cat <<EOF | xmenu | sh &
 Applications
-       Web Browser     firefox
-       Image editor    gimp
+       IMG:./icons/web.png     Web Browser     firefox
+       IMG:./icons/gimp.png    Image editor    gimp
 Terminal (xterm)       xterm
 Terminal (urxvt)       urxvt
 Terminal (st)          st
 Terminal (xterm)       xterm
 Terminal (urxvt)       urxvt
 Terminal (st)          st