0704f950 |
1 | diff --git a/config.h b/config.h |
2 | index 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; |
12 | diff --git a/config.mk b/config.mk |
13 | index 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 = |
27 | diff --git a/xmenu.1 b/xmenu.1 |
28 | index 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 |
77 | diff --git a/xmenu.c b/xmenu.c |
78 | index 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 | |
303 | diff --git a/xmenu.sh b/xmenu.sh |
304 | index 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 |