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