Updating README to mention patches
[xmenu] / patches / icons.diff
CommitLineData
0704f950 1diff --git a/config.h b/config.h
2index a3e4f95..ca7c903 100644
3--- a/config.h
4+++ b/config.h
5@@ -18,3 +18,6 @@ static int separator_pixels = 3; /* space around separator */
6 /* geometry of the right-pointing isoceles triangle for submenus */
7 static const int triangle_width = 3;
8 static const int triangle_height = 7;
9+
10+/* sum of padding around both sides of the image */
11+static const int imgpadding = 8;
12diff --git a/config.mk b/config.mk
13index f86aa34..0ffc8c5 100644
14--- a/config.mk
15+++ b/config.mk
16@@ -14,8 +14,8 @@ FREETYPELIB = -lfontconfig -lXft
17 #FREETYPEINC = $(X11INC)/freetype2
18
19 # includes and libs
20-INCS = -I${X11INC} -I${FREETYPEINC}
21-LIBS = -L${X11LIB} -L${FREETYPELIB} -lX11
22+INCS = -I/usr/local/include -I${X11INC} -I${FREETYPEINC}
23+LIBS = -L/usr/local/lib -L${X11LIB} -L${FREETYPELIB} -lX11 -lImlib2
24
25 # flags
26 CPPFLAGS =
27diff --git a/xmenu.1 b/xmenu.1
28index d114668..5201032 100644
29--- a/xmenu.1
30+++ b/xmenu.1
31@@ -13,17 +13,21 @@ and outputs the item selected to stdout.
32 Each item read from stdin has the following format:
33 .IP
34 .EX
35-ITEM := [TABS] [LABEL [TABS OUTPUT]] NEWLINE
36+ITEM := [TABS] [[IMAGE TABS] LABEL [TABS OUTPUT]] NEWLINE
37 .EE
38 .PP
39 That means that each item is composed by
40-tabs, followed by a label, followed by more tabs, followed by an output,
41+tabs, followed by an optional image specification, followed by tabs
42+followed by a label, followed by more tabs, followed by an output,
43 and ended by a newline. Brackets group optional elements.
44 .IP
45 The initial tabs indicate the menu hierarchy:
46 items indented with a tab is shown in a submenu of the preceding item not indented.
47 An item without initial tabs is a top-level item.
48 .IP
49+The image is a string of the form "IMG:/path/to/image.png".
50+It specifies a image to be shown as icon at the left of the entry.
51+.IP
52 The label is the string that will be shown as a item in the menu.
53 An item without label is considered a separator and is drawn as a thin line in the menu
54 separating the item above from the item below.
55@@ -104,14 +108,14 @@ creating a command to be run by the shell.
56
57 cat <<EOF | xmenu | sh &
58 Applications
59- Web Browser firefox
60- Image editor gimp
61-Terminal (xterm) xterm
62-Terminal (urxvt) urxvt
63-Terminal (st) st
64+ IMG:./web.png Web Browser firefox
65+ Image editor gimp
66+Terminal (xterm) xterm
67+Terminal (urxvt) urxvt
68+Terminal (st) st
69
70-Shutdown poweroff
71-Reboot reboot
72+Shutdown poweroff
73+Reboot reboot
74 EOF
75 .EE
76 .PP
77diff --git a/xmenu.c b/xmenu.c
78index abab13d..d70c6b8 100644
79--- a/xmenu.c
80+++ b/xmenu.c
81@@ -8,6 +8,7 @@
82 #include <X11/Xresource.h>
83 #include <X11/XKBlib.h>
84 #include <X11/Xft/Xft.h>
85+#include <Imlib2.h>
86
87 #define PROGNAME "xmenu"
88 #define ITEMPREV 0
89@@ -45,12 +46,14 @@ struct Geometry {
90 struct Item {
91 char *label; /* string to be drawed on menu */
92 char *output; /* string to be outputed when item is clicked */
93+ char *file; /* filename of the image */
94 int y; /* item y position relative to menu */
95 int h; /* item height */
96 size_t labellen; /* strlen(label) */
97 struct Item *prev; /* previous item */
98 struct Item *next; /* next item */
99 struct Menu *submenu; /* submenu spawned by clicking on item */
100+ Imlib_Image image;
101 };
102
103 /* menu structure */
104@@ -71,9 +74,9 @@ static void getresources(void);
105 static void getcolor(const char *s, XftColor *color);
106 static void setupdc(void);
107 static void calcgeom(struct Geometry *geom);
108-static struct Item *allocitem(const char *label, const char *output);
109+static struct Item *allocitem(const char *label, const char *output, char *file);
110 static struct Menu *allocmenu(struct Menu *parent, struct Item *list, unsigned level);
111-static struct Menu *buildmenutree(unsigned level, const char *label, const char *output);
112+static struct Menu *buildmenutree(unsigned level, const char *label, const char *output, char *file);
113 static struct Menu *parsestdin(void);
114 static void calcmenu(struct Geometry *geom, struct Menu *menu);
115 static void grabpointer(void);
116@@ -129,6 +132,13 @@ main(int argc, char *argv[])
117 rootwin = RootWindow(dpy, screen);
118 colormap = DefaultColormap(dpy, screen);
119
120+ /* imlib2 stuff */
121+ imlib_set_cache_size(2048 * 1024);
122+ imlib_context_set_dither(1);
123+ imlib_context_set_display(dpy);
124+ imlib_context_set_visual(visual);
125+ imlib_context_set_colormap(colormap);
126+
127 /* setup */
128 getresources();
129 setupdc();
130@@ -247,7 +257,7 @@ calcgeom(struct Geometry *geom)
131
132 /* allocate an item */
133 static struct Item *
134-allocitem(const char *label, const char *output)
135+allocitem(const char *label, const char *output, char *file)
136 {
137 struct Item *item;
138
139@@ -266,6 +276,12 @@ allocitem(const char *label, const char *output)
140 err(1, "strdup");
141 }
142 }
143+ if (file == NULL) {
144+ item->file = NULL;
145+ } else {
146+ if ((item->file = strdup(file)) == NULL)
147+ err(1, "strdup");
148+ }
149 item->y = 0;
150 item->h = 0;
151 if (item->label == NULL)
152@@ -274,6 +290,7 @@ allocitem(const char *label, const char *output)
153 item->labellen = strlen(item->label);
154 item->next = NULL;
155 item->submenu = NULL;
156+ item->image = NULL;
157
158 return item;
159 }
160@@ -314,7 +331,7 @@ allocmenu(struct Menu *parent, struct Item *list, unsigned level)
161
162 /* build the menu tree */
163 static struct Menu *
164-buildmenutree(unsigned level, const char *label, const char *output)
165+buildmenutree(unsigned level, const char *label, const char *output, char *file)
166 {
167 static struct Menu *prevmenu = NULL; /* menu the previous item was added to */
168 static struct Menu *rootmenu = NULL; /* menu to be returned */
169@@ -324,7 +341,7 @@ buildmenutree(unsigned level, const char *label, const char *output)
170 unsigned i;
171
172 /* create the item */
173- curritem = allocitem(label, output);
174+ curritem = allocitem(label, output, file);
175
176 /* put the item in the menu tree */
177 if (prevmenu == NULL) { /* there is no menu yet */
178@@ -377,7 +394,7 @@ parsestdin(void)
179 {
180 struct Menu *rootmenu;
181 char *s, buf[BUFSIZ];
182- char *label, *output;
183+ char *file, *label, *output;
184 unsigned level = 0;
185
186 rootmenu = NULL;
187@@ -390,6 +407,13 @@ parsestdin(void)
188 s = level + buf;
189 label = strtok(s, "\t\n");
190
191+ /* get the filename */
192+ file = NULL;
193+ if (label != NULL && strncmp(label, "IMG:", 4) == 0) {
194+ file = label + 4;
195+ label = strtok(NULL, "\t\n");
196+ }
197+
198 /* get the output */
199 output = strtok(NULL, "\n");
200 if (output == NULL) {
201@@ -399,12 +423,36 @@ parsestdin(void)
202 output++;
203 }
204
205- rootmenu = buildmenutree(level, label, output);
206+ rootmenu = buildmenutree(level, label, output, file);
207 }
208
209 return rootmenu;
210 }
211
212+/* load and scale image */
213+static Imlib_Image
214+loadimage(const char *file, int size)
215+{
216+ Imlib_Image image;
217+ int width;
218+ int height;
219+ int imgsize;
220+
221+ image = imlib_load_image(file);
222+ if (image == NULL)
223+ errx(1, "cannot load image %s", file);
224+
225+ imlib_context_set_image(image);
226+
227+ width = imlib_image_get_width();
228+ height = imlib_image_get_height();
229+ imgsize = MIN(width, height);
230+
231+ image = imlib_create_cropped_scaled_image(0, 0, imgsize, imgsize, size, size);
232+
233+ return image;
234+}
235+
236 /* recursivelly calculate menu geometry and set window hints */
237 static void
238 calcmenu(struct Geometry *geom, struct Menu *menu)
239@@ -430,8 +478,12 @@ calcmenu(struct Geometry *geom, struct Menu *menu)
240
241 XftTextExtentsUtf8(dpy, dc.font, (XftChar8 *)item->label,
242 item->labellen, &ext);
243- labelwidth = ext.xOff + dc.font->height * 2;
244+ labelwidth = ext.xOff + dc.font->height * 2 + imgpadding;
245 menu->w = MAX(menu->w, labelwidth);
246+
247+ /* create image */
248+ if (item->file != NULL)
249+ item->image = loadimage(item->file, dc.font->height);
250 }
251
252 /* calculate menu's x and y positions */
253@@ -621,7 +673,7 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color)
254 {
255 int x, y;
256
257- x = dc.font->height;
258+ x = dc.font->height + imgpadding;
259 y = item->y + item->h/2 + dc.font->ascent/2 - 1;
260 XSetForeground(dpy, dc.gc, color[ColorFG].pixel);
261 XftDrawStringUtf8(menu->draw, &color[ColorFG], dc.font,
262@@ -629,8 +681,8 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color)
263
264 /* draw triangle, if item contains a submenu */
265 if (item->submenu != NULL) {
266- x = menu->w - dc.font->height/2 - triangle_width/2;
267- y = item->y + item->h/2 - triangle_height/2 - 1;
268+ x = menu->w - (dc.font->height - triangle_width) / 2;
269+ y = item->y + (item->h - triangle_height) / 2;
270
271 XPoint triangle[] = {
272 {x, y},
273@@ -642,6 +694,15 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color)
274 XFillPolygon(dpy, menu->pixmap, dc.gc, triangle, LEN(triangle),
275 Convex, CoordModeOrigin);
276 }
277+
278+ /* draw image */
279+ if (item->file != NULL) {
280+ x = imgpadding / 2;
281+ y = item->y + (item->h - dc.font->height) / 2;
282+ imlib_context_set_drawable(menu->pixmap);
283+ imlib_context_set_image(item->image);
284+ imlib_render_image_on_drawable(x, y);
285+ }
286 }
287
288 /* draw items of the current menu and of its ancestors */
289@@ -831,6 +892,13 @@ freemenu(struct Menu *menu)
290 if (tmp->label != tmp->output)
291 free(tmp->label);
292 free(tmp->output);
293+ if (tmp->file != NULL) {
294+ free(tmp->file);
295+ if (item->image != NULL) {
296+ imlib_context_set_image(item->image);
297+ imlib_free_image();
298+ }
299+ }
300 free(tmp);
301 }
302
303diff --git a/xmenu.sh b/xmenu.sh
304index abd9a41..db08041 100755
305--- a/xmenu.sh
306+++ b/xmenu.sh
307@@ -2,7 +2,7 @@
308
309 cat <<EOF | xmenu | sh &
310 Applications
311- Web Browser firefox
312+ IMG:./web.png Web Browser firefox
313 Image editor gimp
314 Terminal (xterm) xterm
315 Terminal (urxvt) urxvt