1 /*
2  * deskmenu - deskmenu.c
3  *
4  * Copyright (C) 2001 Ken Lynch
5  * Copyright (C) 2002 Stefan Pfetzing
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21 
22 /* some includes {{{1 */
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <getopt.h>
27 #include <signal.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <X11/keysym.h>
31 #include <unistd.h>
32 #include "deskmenu.h"
33 #include "system.h"
34 /* }}}1 */
35 
36 /* The name the program was run with, stripped of any leading path. */
37 char *program_name;
38 
39 struct stat stat_buf;
40 
41 /* Option flags and variables {{{1 */
42 int quit_menu;									/* --quit-menu */
43 int button;											/* --button */
44 int want_verbose;								/* --verbose */
45 char *rcfile;										/* --file */
46 Display *dpy;										/* the pointer to the display */
47 int scr;												/* the number of the used screen */
48 Atom wm_state;
49 Atom gnome[GNOME_HINT_COUNT];
50 Window root;										/* The root window */
51 Window proxy_win;								/* a proxy window */
52 char *locale;										/* the used locale */
53 KeyCode keycode;
54 int modifier;
55 
56 /* }}}1 */
57 
58 /* the structure of all long options for getopt {{{1 */
59 static struct option const long_options[] = {
60 	{"quit-menu", no_argument, 0, 'q'},
61 	{"verbose", no_argument, 0, 'v'},
62 	{"help", no_argument, 0, 'h'},
63 	{"version", no_argument, 0, 'V'},
64 	{"file", required_argument, 0, 'f'},
65 	{"button", required_argument, 0, 'b'},
66 	{NULL, 0, NULL, 0}
67 };
68 /* }}}1 */
69 
70 /* a signal handling function,
71  * SIGCHLD is currently totally ignored */
72 void
signal_handler(int signal)73 signal_handler (int signal)
74 {
75 #ifdef DEBUG
76 	printf ("signal_handler\n");
77 #endif
78 
79 	if (signal == SIGCHLD)
80 		wait (NULL);
81 }
82 
83 int
handle_xerror(Display * dpy,XErrorEvent * err)84 handle_xerror (Display * dpy, XErrorEvent * err)
85 {
86 	return 0;
87 }
88 
89 long
get_wm_state(Window w)90 get_wm_state (Window w)
91 {
92 	Atom real_type;
93 	int real_format;
94 	unsigned long items_read, items_left;
95 	long *data, state = WithdrawnState;
96 
97 #ifdef DEBUG
98 	printf ("get_wm_state\n");
99 #endif
100 
101 	if (XGetWindowProperty
102 			(dpy, w, wm_state, 0L, 2L, False, wm_state, &real_type, &real_format,
103 			 &items_read, &items_left, (unsigned char **) &data) == Success
104 			&& items_read)
105 		{
106 			state = *data;
107 			XFree (data);
108 		}
109 	return state;
110 }
111 
112 void
set_gnome_hint(Window w,int a,long value)113 set_gnome_hint (Window w, int a, long value)
114 {
115 #ifdef DEBUG
116 	printf ("set_gnome_hint\n");
117 #endif
118 
119 	if (a == WIN_WORKSPACE_COUNT && value <= 0)
120 		return;
121 
122 	XChangeProperty (dpy, w, gnome[a], XA_CARDINAL, 32, PropModeReplace,
123 									 (unsigned char *) &value, 1);
124 }
125 
126 long
get_gnome_hint(Window w,int a)127 get_gnome_hint (Window w, int a)
128 {
129 	Atom real_type;
130 	int real_format;
131 	unsigned long items_read, items_left;
132 	long *data, value = 0;
133 
134 #ifdef DEBUG
135 	printf ("get_gnome_hint\n");
136 #endif
137 
138 	if (XGetWindowProperty
139 			(dpy, w, gnome[a], 0L, 1L, False, XA_CARDINAL, &real_type, &real_format,
140 			 &items_read, &items_left, (unsigned char **) &data) == Success
141 			&& items_read)
142 		{
143 			value = *data;
144 			XFree (data);
145 		}
146 	return value;
147 }
148 
149 void
initialize(int argc,char * argv[])150 initialize (int argc, char *argv[])
151 {
152 	int i;
153 	struct sigaction act;
154 	FILE *rc;
155 
156 #ifdef DEBUG
157 	printf ("initialize\n");
158 #endif
159 
160 	program_name = argv[0];
161 
162 	locale = gtk_set_locale ();
163 	gtk_init (&argc, &argv);
164 
165 	i = decode_switches (argc, argv);
166 
167 	if (!rcfile)
168 		{
169 			size_t size =
170 				sizeof (char) * (strlen (getenv ("HOME")) + 1) +
171 				sizeof (char) * (strlen (".deskmenurc") + 1);
172 			rcfile = (char *) malloc (size);
173 			snprintf (rcfile, size, "%s/%s", getenv ("HOME"), ".deskmenurc");
174 		}
175 
176 	/* check if the given file is correct */
177 	if (stat (rcfile, &stat_buf))
178 		{
179 			/* an error while stating occured */
180 			fprintf (stderr, "%s: ", PACKAGE);
181 			perror (rcfile);
182 			free (rcfile);
183 			/* check if /usr/local/etc/deskmenurc is existant */
184 			rcfile = "/usr/local/etc/deskmenurc";
185 			if (stat (rcfile, &stat_buf))
186 				{
187 					/* an error while stating occured */
188 					fprintf (stderr, "%s: ", PACKAGE);
189 					perror (rcfile);
190 					exit (EXIT_FAILURE);
191 				}
192 		}
193 
194 	keycode = 0;
195 	modifier = 0;
196 
197 	if (!button)
198 		button = Button2;
199 
200 	dpy = GDK_DISPLAY ();
201 	root = GDK_ROOT_WINDOW ();
202 
203 	/* now since we have a usable rcfile, open it and read the key which to use
204 	 * for the popup. */
205 	if ((rc = fopen (rcfile, "r")))
206 		{
207 			char buf[1024], *lvalue, *rvalue, *k;
208 			while (fgets (buf, sizeof buf, rc))
209 				{
210 					char *comment;
211 					/* to make sure comments are ignored */
212 					comment = strchr (buf, '#');
213 					if (comment)
214 						{
215 							comment[0] = '\n';
216 							comment[1] = '\0';
217 						}
218 
219 					/* remove all spaces and tabs */
220 					while (comment = strpbrk (buf, " \t"))
221 						strcpy (comment, comment + 1);
222 
223 					lvalue = strtok (buf, "=");
224 					if (lvalue)
225 						{
226 							if (!strcmp (lvalue, "keycode"))
227 								{
228 									rvalue = strtok (NULL, "\n");
229 									if (rvalue)
230 										{
231 											if (!strcmp (rvalue, "none"))
232 												{
233 													fclose (rc);
234 													break;
235 												}
236 											k = strrchr (rvalue, '+');
237 											if (k)
238 												{
239 													keycode = XKeysymToKeycode (dpy,
240 																											XStringToKeysym (k +
241 																																			 1));
242 													if (strstr (rvalue, "Shift"))
243 														modifier = modifier | ShiftMask;
244 													if (strstr (rvalue, "Control"))
245 														modifier = modifier | ControlMask;
246 													if (strstr (rvalue, "Mod1"))
247 														modifier = modifier | Mod1Mask;
248 													if (strstr (rvalue, "Mod2"))
249 														modifier = modifier | Mod2Mask;
250 													if (strstr (rvalue, "Mod3"))
251 														modifier = modifier | Mod3Mask;
252 													if (strstr (rvalue, "Mod4"))
253 														modifier = modifier | Mod4Mask;
254 													if (strstr (rvalue, "Mod5"))
255 														modifier = modifier | Mod5Mask;
256 												}
257 										}
258 								}
259 						}
260 				}
261 			fclose (rc);
262 		}
263 
264 	XSetErrorHandler (handle_xerror);
265 
266 	init_keyboard ();
267 	grab_key (keycode, modifier, root);
268 
269 	act.sa_handler = signal_handler;
270 	act.sa_flags = 0;
271 	sigaction (SIGCHLD, &act, NULL);
272 	/* currently do *nothing* when getting a HUP, because reloading of
273 	 * the configfile happens everytime the menu is build */
274 	sigaction (SIGHUP, &act, NULL);
275 
276 	/* Wait until a GNOME compliant WM is running */
277 	if (!XInternAtom (dpy, "_WIN_DESKTOP_BUTTON_PROXY", True))
278 		{
279 			sleep(1);
280 			if (!XInternAtom (dpy, "_WIN_DESKTOP_BUTTON_PROXY", True))
281 				no_compatible_wm();
282 		}
283 
284 	wm_state = XInternAtom (dpy, "WM_STATE", False);
285 	gnome[WIN_HINTS] = XInternAtom (dpy, "_WIN_HINTS", False);
286 	gnome[WIN_WORKSPACE] = XInternAtom (dpy, "_WIN_WORKSPACE", False);
287 	gnome[WIN_WORKSPACE_COUNT] =
288 		XInternAtom (dpy, "_WIN_WORKSPACE_COUNT", False);
289 	gnome[WIN_DESKTOP_BUTTON_PROXY] =
290 		XInternAtom (dpy, "_WIN_DESKTOP_BUTTON_PROXY", False);
291 	gnome[WIN_CLIENT_LIST] = XInternAtom (dpy, "_WIN_CLIENT_LIST", False);
292 
293 	proxy_win = get_gnome_hint (root, WIN_DESKTOP_BUTTON_PROXY);
294 	XSelectInput (dpy, proxy_win, SubstructureNotifyMask);
295 	XSelectInput (dpy, root, PropertyChangeMask);
296 }
297 
298 int
decode_switches(int argc,char ** argv)299 decode_switches (int argc, char **argv)
300 {
301 	int c;
302 
303 	while ((c = getopt_long (argc, argv, "v"	/* verbose */
304 													 "h"	/* help */
305 													 "V"	/* version */
306 													 "f:"	/* file */
307 													 "b:"	/* button */
308 													 "q",	/* quit-menu */
309 													 long_options, (int *) 0)) != EOF)
310 		{
311 			switch (c)
312 				{
313 				case 'q':							/* --quit-menu */
314 					quit_menu = 1;
315 					break;
316 				case 'v':							/* --verbose */
317 					want_verbose = 1;
318 					break;
319 				case 'f':							/* --file */
320 					rcfile = optarg;
321 					break;
322 				case 'b':							/* --button */
323 					button = atoi (optarg);
324 					switch (button)
325 						{
326 						case 1:
327 							button = Button1;
328 							break;
329 						case 2:
330 							button = Button2;
331 							break;
332 						case 3:
333 							button = Button3;
334 							break;
335 						case 4:
336 							button = Button4;
337 							break;
338 						case 5:
339 							button = Button5;
340 							break;
341 						default:
342 							usage (EXIT_FAILURE);
343 						}
344 					break;
345 				case 'V':
346 					printf ("%s %s\n", PACKAGE, VERSION);
347 					exit (0);
348 
349 				case 'h':
350 					usage (0);
351 
352 				default:
353 					usage (EXIT_FAILURE);
354 				}
355 		}
356 
357 	return optind;
358 }
359 
360 void
usage(int status)361 usage (int status)
362 {
363 	printf (_("%s - A root menu for X.\n"), program_name);
364 	printf (_("Usage: %s [OPTION]... \n"), program_name);
365 	printf (_(
366 "Options:\n"
367 "  -b, --button=n             sets which mouse button is used\n"
368 "                             to invoke deskmenu\n"
369 "  -f, --file=FILE            use an alternative rcfile\n"
370 "                             instead of $HOME/.deskmenurc\n"
371 "  -q, --quit-menu            display a quit menu\n"
372 "  --verbose                  print more information\n"
373 "  -h, --help                 display this help and exit\n"
374 "  -V, --version              output version information and exit\n"
375 "  --display                  use an alternative display instead of,\n"
376 "                             $DISPLAY\n"
377 ));
378 	exit (status);
379 }
380 
381 int
main(int argc,char * argv[])382 main (int argc, char *argv[])
383 {
384 	fd_set readset;
385 	int x_filedescriptor;
386 
387 	initialize (argc, argv);
388 
389 	x_filedescriptor = XConnectionNumber(dpy);
390 
391 	while (1)
392 		{
393 			FD_ZERO(&readset);
394 			FD_SET(x_filedescriptor, &readset);
395 
396 #ifdef DEBUG
397 			printf ("select xfd:%d \n", x_filedescriptor);
398 #endif
399 
400 INT:	if (-1 == select(x_filedescriptor+1, &readset, 0, 0, 0))
401 				if (errno == EINTR)
402 					{
403 #ifdef DEBUG
404 						printf ("und n goto...\n");
405 #endif
406 						goto INT;
407 					}
408 				else
409 					{
410 						perror ("An unexpected error has occcured! (-");
411 						exit (1);
412 					}
413 
414 			XPending (dpy);
415 			XEvent ev;
416 			XNextEvent (dpy, &ev);
417 
418 			if ((ev.type == ButtonPress && ev.xbutton.button == button))
419 				{
420 					mode = MODE_MOUSE;
421 					popup_menu (&ev);
422 				}
423 			if (ev.type == KeyPress)
424 				{
425 					mode = MODE_CENTERED;
426 					popup_menu (&ev);
427 				}
428 			if (ev.type == PropertyNotify
429 					&& ev.xproperty.atom == gnome[WIN_DESKTOP_BUTTON_PROXY])
430 				{
431 					proxy_win = get_gnome_hint (root, WIN_DESKTOP_BUTTON_PROXY);
432 					XSelectInput (dpy, proxy_win, SubstructureNotifyMask);
433 				}
434 		}
435 	return 0;
436 }
437 
438 /***This must remain at the end of the file.*****
439  * vi:set sw=2 ts=2:                            *
440  * vi:set cindent cinoptions={1s,>2s,^-1s,n-1s: *
441  * vi:set foldmethod=marker:                    *
442  * vi:set foldmarker=���,���:                   *
443  ************************************************/
444 
445