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