1 /* Licence
2 * =======
3 *
4 * 9menu is free software, and is Copyright (c) 1994 by David Hogan and
5 * Arnold Robbins. Permission is granted to all sentient beings to use
6 * this software, to make copies of it, and to distribute those copies,
7 * provided that:
8 *
9 * (1) the copyright and licence notices are left intact
10 * (2) the recipients are aware that it is free software
11 * (3) any unapproved changes in functionality are either
12 * (i) only distributed as patches
13 * or (ii) distributed as a new program which is not called 9menu
14 * and whose documentation gives credit where it is due
15 * (4) the authors are not held responsible for any defects
16 * or shortcomings in the software, or damages caused by it.
17 *
18 * There is no warranty for this software. Have a nice day.
19 *
20 * --
21 * Arnold Robbins
22 * arnold@skeeve.com
23 *
24 * 9menu.c
25 *
26 * This program puts up a window that is just a menu, and executes
27 * commands that correspond to the items selected.
28 *
29 * Initial idea: Arnold Robbins
30 * Version using libXg: Matty Farrow (some ideas borrowed)
31 * This code by: David Hogan and Arnold Robbins
32 */
33
34 /*
35 * Heavily modified by Kris Maglione for use with wmii.
36 */
37
38 #define EXTERN
39 #define IXP_NO_P9_
40 #define IXP_P9_STRUCTS
41 #include <fmt.h>
42 #include <ixp.h>
43 #include <stdarg.h>
44 #include <stdbool.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <clientutil.h>
49 #include <util.h>
50 #include <x11.h>
51
52 char version[] = "wmii9menu-" VERSION " ©2010 Kris Maglione, ©1994 David Hogan, Arnold Robbins";
53
54 static Window* menuwin;
55
56 static CTuple cnorm;
57 static CTuple csel;
58 static Font* font;
59
60 static int wborder;
61
62 char buffer[8092];
63 char* _buffer;
64
65 /* for XSetWMProperties to use */
66 int g_argc;
67 char **g_argv;
68
69 char *initial = "";
70 int cur;
71
72 static char** labels; /* list of labels and commands */
73 static char** commands;
74 static int numitems;
75
76 void usage(void);
77 void run_menu(void);
78 void create_window(void);
79 void size_window(int, int);
80 void redraw(int, int);
81 void warpmouse(int, int);
82 void memory(void);
83 int args(void);
84
85 ErrorCode ignored_xerrors[] = {
86 { 0, }
87 };
88
89 /* xext.c */
90 void xext_init(void);
91 Rectangle* xinerama_screens(int*);
92 /* geom.c */
93 bool rect_haspoint_p(Point, Rectangle);
94
95 Cursor cursor[1];
96 Visual* render_visual;
97
98 void init_screens(void);
99 void
init_screens(void)100 init_screens(void) {
101 Rectangle *rects;
102 Point p;
103 int i, n;
104
105 rects = xinerama_screens(&n);
106 p = querypointer(&scr.root);
107 for(i=0; i < n; i++) {
108 if(rect_haspoint_p(p, rects[i]))
109 break;
110 }
111 if(i == n)
112 i = 0;
113 scr.rect = rects[i];
114 }
115
116 /* main --- crack arguments, set up X stuff, run the main menu loop */
117
118 int
main(int argc,char ** argv)119 main(int argc, char **argv)
120 {
121 static char *address;
122 char *cp;
123 int i;
124
125 g_argc = argc;
126 g_argv = argv;
127
128 ARGBEGIN{
129 case 'v':
130 print("%s\n", version);
131 return 0;
132 case 'a':
133 address = EARGF(usage());
134 break;
135 case 'i':
136 initial = EARGF(usage());
137 break;
138 default:
139 usage();
140 }ARGEND;
141
142 if(argc == 0)
143 usage();
144
145 initdisplay();
146 xext_init();
147 init_screens();
148 create_window();
149
150 numitems = argc;
151
152 labels = emalloc(numitems * sizeof *labels);
153 commands = emalloc(numitems * sizeof *labels);
154
155 for(i = 0; i < numitems; i++) {
156 labels[i] = argv[i];
157 if((cp = strchr(labels[i], ':')) != nil) {
158 *cp++ = '\0';
159 commands[i] = cp;
160 } else
161 commands[i] = labels[i];
162 if(strcmp(labels[i], initial) == 0)
163 cur = i;
164 }
165
166 client_init(address);
167
168 wborder = strtol(readctl("border "), nil, 10);
169 loadcolor(&cnorm, readctl("normcolors "));
170 loadcolor(&csel, readctl("focuscolors "));
171 font = loadfont(readctl("font "));
172 if(!font)
173 fatal("Can't load font");
174
175 run_menu();
176
177 XCloseDisplay(display);
178 return 0;
179 }
180
181 /* usage --- print a usage message and die */
182
183 void
usage(void)184 usage(void)
185 {
186 fprintf(stderr, "usage: %s -v\n", argv0);
187 fprintf(stderr, " %s [-a <address>] [-i <arg>] menitem[:command] ...\n", argv0);
188 exit(0);
189 }
190
191 /* run_menu --- put up the window, execute selected commands */
192
193 enum {
194 MouseMask =
195 ButtonPressMask
196 | ButtonReleaseMask
197 | ButtonMotionMask
198 | PointerMotionMask,
199 MenuMask =
200 MouseMask
201 | StructureNotifyMask
202 | ExposureMask
203 };
204
205 void
run_menu(void)206 run_menu(void)
207 {
208 XEvent ev;
209 int i, old, wide, high;
210
211 wide = 0;
212 high = labelh(font);
213 for(i = 0; i < numitems; i++)
214 wide = max(wide, textwidth(font, labels[i]));
215 wide += font->height & ~1;
216
217 size_window(wide, high);
218 warpmouse(wide, high);
219
220 for(;;) {
221 XNextEvent(display, &ev);
222 switch (ev.type) {
223 default:
224 fprintf(stderr, "%s: unknown ev.type %d\n",
225 argv0, ev.type);
226 break;
227 case ButtonRelease:
228 i = ev.xbutton.y / high;
229 if(ev.xbutton.x < 0 || ev.xbutton.x > wide)
230 return;
231 else if(i < 0 || i >= numitems)
232 return;
233
234 printf("%s\n", commands[i]);
235 return;
236 case ButtonPress:
237 case MotionNotify:
238 old = cur;
239 cur = ev.xbutton.y / high;
240 if(ev.xbutton.x < 0 || ev.xbutton.x > wide)
241 cur = ~0;
242 if(cur == old)
243 break;
244 redraw(high, wide);
245 break;
246 case MapNotify:
247 redraw(high, wide);
248 break;
249 case Expose:
250 redraw(high, wide);
251 break;
252 case ConfigureNotify:
253 case MappingNotify:
254 break;
255 }
256 }
257 }
258
259 /* set_wm_hints --- set all the window manager hints */
260
261 void
create_window(void)262 create_window(void)
263 {
264 WinAttr wa = { 0 };
265 XEvent e;
266
267 wa.override_redirect = true;
268 menuwin = createwindow(&scr.root, Rect(-1, -1, 0, 0),
269 scr.depth, InputOutput,
270 &wa, CWOverrideRedirect);
271 selectinput(menuwin, MenuMask);
272 mapwin(menuwin);
273 XMaskEvent(display, StructureNotifyMask, &e);
274 if(!grabpointer(menuwin, nil, 0, MouseMask))
275 fatal("Failed to grab the mouse\n");
276 XSetCommand(display, menuwin->xid, g_argv, g_argc);
277 }
278
279 void
size_window(int wide,int high)280 size_window(int wide, int high)
281 {
282 Rectangle r;
283 Point p;
284 int h;
285
286 h = high * numitems;
287 r = Rect(0, 0, wide, h);
288
289 p = querypointer(&scr.root);
290 p.x -= wide / 2;
291 p.x = max(p.x, scr.rect.min.x);
292 p.x = min(p.x, scr.rect.max.x - wide);
293
294 p.y -= cur * high + high / 2;
295 p.y = max(p.y, scr.rect.min.y);
296 p.y = min(p.y, scr.rect.max.y - h);
297
298 reshapewin(menuwin, rectaddpt(r, p));
299
300 //XSetWindowBackground(display, menuwin->xid, cnorm.bg);
301 setborder(menuwin, 1, cnorm.border);
302 }
303
304 /* redraw --- actually redraw the menu */
305
306 void
redraw(int high,int wide)307 redraw(int high, int wide)
308 {
309 Rectangle r;
310 CTuple *c;
311 int i;
312
313 r = Rect(0, 0, wide, high);
314 for(i = 0; i < numitems; i++) {
315 if(cur == i)
316 c = &csel;
317 else
318 c = &cnorm;
319 r = rectsetorigin(r, Pt(0, i * high));
320 fill(menuwin, r, c->bg);
321 drawstring(menuwin, font, r, Center, labels[i], c->fg);
322 }
323 }
324
325 /* warpmouse --- bring the mouse to the menu */
326
327 void
warpmouse(int wide,int high)328 warpmouse(int wide, int high)
329 {
330 Point p;
331 int offset;
332
333 /* move tip of pointer into middle of menu item */
334 offset = labelh(font) / 2;
335 offset += 6; /* fudge factor */
336
337 p = Pt(wide / 2, cur*high - high/2 + offset);
338 p = addpt(p, menuwin->r.min);
339
340 warppointer(p);
341 }
342
343