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:
• ./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.
-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
#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 IMGPADDING 8
/* macros */
#define LEN(x) (sizeof (x) / sizeof (x[0]))
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 */
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 setupmenusize(struct Geometry *geom, struct Menu *menu);
static void setupmenupos(struct Geometry *geom, struct Menu *menu);
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();
/* 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;
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->labellen = strlen(item->label);
item->next = NULL;
item->submenu = NULL;
+ item->image = NULL;
return item;
}
/* 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 */
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 */
{
struct Menu *rootmenu;
char *s, buf[BUFSIZ];
- char *label, *output;
+ char *file, *label, *output;
unsigned level = 0;
rootmenu = NULL;
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) {
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;
+}
+
/* setup the size of a menu and the position of its items */
static void
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);
- labelwidth = ext.xOff + dc.font->height * 2;
+ labelwidth = ext.xOff + dc.font->height * 2 + IMGPADDING * 2;
menu->w = MAX(menu->w, labelwidth);
+
+ /* create image */
+ if (item->file != NULL)
+ item->image = loadimage(item->file, dc.font->height);
}
}
{
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,
/* 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[] = {
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 */
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->file != NULL) {
+ free(tmp->file);
+ if (tmp->image != NULL) {
+ imlib_context_set_image(tmp->image);
+ imlib_free_image();
+ }
+ }
+ item = item->next;
free(tmp);
}