Adding upstream version 0.9.0.
[debian/lxpanel.git] / src / main.c
1 /*
2 * Copyright (C) 2006-2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
3 * 2006-2008 Jim Huang <jserv.tw@gmail.com>
4 * 2008 Fred Chien <fred@lxde.org>
5 * 2009 Jürgen Hötzel <juergen@archlinux.org>
6 * 2009-2010 Marty Jack <martyj19@comcast.net>
7 * 2010 Lajos Kamocsay <lajos@panka.com>
8 * 2012 Piotr Sipika <Piotr.Sipika@gmail.com>
9 * 2012-2013 Henry Gebhardt <hsggebhardt@gmail.com>
10 * 2012 Jack Chen <speed.up08311990@gmail.com>
11 * 2012 Rafał Mużyło <galtgendo@gmail.com>
12 * 2012 Michael Rawson <michaelrawson76@gmail.com>
13 * 2012 Julien Lavergne <julien.lavergne@gmail.com>
14 * 2013 Rouslan <rouslan-k@users.sourceforge.net>
15 * 2013 peadaredwards <peadaredwards@users.sourceforge.net>
16 * 2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
17 *
18 * This file is a part of LXPanel project.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software Foundation,
32 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
33 */
34
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #include <glib/gi18n.h>
40 #include <stdlib.h>
41 #include <glib/gstdio.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <unistd.h>
45 #include <errno.h>
46 #include <locale.h>
47 #include <string.h>
48 #include <gdk/gdkx.h>
49 #include <libfm/fm-gtk.h>
50 #include <keybinder.h>
51
52 #define __LXPANEL_INTERNALS__
53
54 #include "private.h"
55 #include "misc.h"
56
57 #include "lxpanelctl.h"
58 #include "dbg.h"
59 #include "space.h"
60
61 static gchar *cfgfile = NULL;
62 static gchar version[] = VERSION;
63 static int config = 0;
64
65 static gboolean is_restarting = FALSE;
66
67 Command commands[] = {
68 //{ "configure", N_("Preferences"), configure },
69 { "run", N_("Run"), gtk_run },
70 { "restart", N_("Restart"), restart },
71 { "logout", N_("Logout"), logout },
72 { NULL, NULL },
73 };
74
75 void restart(void)
76 {
77 ENTER;
78 is_restarting = TRUE;
79
80 gtk_main_quit();
81 RET();
82 }
83
84 static void process_client_msg ( XClientMessageEvent* ev )
85 {
86 int cmd = ev->data.b[0];
87 int monitor;
88 int edge;
89 char *plugin_type;
90 char *command;
91 switch( cmd )
92 {
93 #ifndef DISABLE_MENU
94 case LXPANEL_CMD_SYS_MENU:
95 {
96 GSList* l;
97 for( l = all_panels; l; l = l->next )
98 {
99 LXPanel* p = (LXPanel*)l->data;
100 GList *plugins, *pl;
101
102 if (p->priv->box == NULL)
103 continue;
104 plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
105 for (pl = plugins; pl; pl = pl->next)
106 {
107 const LXPanelPluginInit *init = PLUGIN_CLASS(pl->data);
108 if (init->show_system_menu)
109 /* queue to show system menu */
110 init->show_system_menu(pl->data);
111 }
112 g_list_free(plugins);
113 }
114 break;
115 }
116 #endif
117 case LXPANEL_CMD_RUN:
118 gtk_run();
119 break;
120 case LXPANEL_CMD_CONFIG:
121 {
122 LXPanel * p = ((all_panels != NULL) ? all_panels->data : NULL);
123 if (p != NULL)
124 panel_configure(p, 0);
125 }
126 break;
127 case LXPANEL_CMD_RESTART:
128 restart();
129 break;
130 case LXPANEL_CMD_EXIT:
131 gtk_main_quit();
132 break;
133 case LXPANEL_CMD_COMMAND:
134 monitor = (ev->data.b[1] & 0xf) - 1; /* 0 for no monitor */
135 edge = (ev->data.b[1] >> 4) & 0x7;
136 if ((ev->data.b[1] & 0x80) != 0)
137 /* some extension, not supported yet */
138 break;
139 plugin_type = g_strndup(&ev->data.b[2], 18);
140 command = strchr(plugin_type, '\t');
141 if (command) do /* use do{}while(0) to enable break */
142 {
143 LXPanel *p;
144 GSList *l;
145 GList *plugins, *pl;
146 const LXPanelPluginInit *init;
147 GtkWidget *plugin = NULL;
148
149 *command++ = '\0';
150 /* find the panel by monitor and edge */
151 for (l = all_panels; l; l = l->next)
152 {
153 p = (LXPanel*)l->data;
154 if (p->priv->box == NULL) /* inactive panel */
155 continue;
156 if (monitor >= 0 && p->priv->monitor != monitor)
157 continue;
158 if (edge == EDGE_NONE || p->priv->edge == edge)
159 break;
160 }
161 if (l == NULL) /* match not found */
162 break;
163 /* find the plugin */
164 init = g_hash_table_lookup(lxpanel_get_all_types(), plugin_type);
165 if (init == NULL) /* no such plugin known */
166 break;
167 plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
168 for (pl = plugins; pl; pl = pl->next)
169 {
170 if (init == PLUGIN_CLASS(pl->data))
171 {
172 plugin = pl->data;
173 break;
174 }
175 }
176 g_list_free(plugins);
177 /* test for built-in commands ADD and DEL */
178 if (strcmp(command, "ADD") == 0)
179 {
180 if (plugin == NULL)
181 {
182 config_setting_t *cfg;
183
184 cfg = config_group_add_subgroup(config_root_setting(p->priv->config),
185 "Plugin");
186 config_group_set_string(cfg, "type", plugin_type);
187 plugin = lxpanel_add_plugin(p, plugin_type, cfg, -1);
188 if (plugin == NULL) /* failed to create */
189 config_setting_destroy(cfg);
190 }
191 }
192 else if (strcmp(command, "DEL") == 0)
193 {
194 if (plugin != NULL)
195 lxpanel_remove_plugin(p, plugin);
196 }
197 /* send the command */
198 else if (plugin && init->control)
199 init->control(plugin, command);
200 } while(0);
201 g_free(plugin_type);
202 break;
203 }
204 }
205
206 static GdkFilterReturn
207 panel_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer not_used)
208 {
209 Atom at;
210 Window win;
211 XEvent *ev = (XEvent *) xevent;
212
213 ENTER;
214 DBG("win = 0x%x\n", ev->xproperty.window);
215 if (ev->type != PropertyNotify )
216 {
217 /* private client message from lxpanelctl */
218 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
219 {
220 process_client_msg( (XClientMessageEvent*)ev );
221 }
222 else if( ev->type == DestroyNotify )
223 {
224 fb_ev_emit_destroy( fbev, ((XDestroyWindowEvent*)ev)->window );
225 }
226 RET(GDK_FILTER_CONTINUE);
227 }
228
229 at = ev->xproperty.atom;
230 win = ev->xproperty.window;
231 if (win == GDK_ROOT_WINDOW())
232 {
233 if (at == a_NET_CLIENT_LIST)
234 {
235 fb_ev_emit(fbev, EV_CLIENT_LIST);
236 }
237 else if (at == a_NET_CURRENT_DESKTOP)
238 {
239 GSList* l;
240 for( l = all_panels; l; l = l->next )
241 ((LXPanel*)l->data)->priv->curdesk = get_net_current_desktop();
242 fb_ev_emit(fbev, EV_CURRENT_DESKTOP);
243 }
244 else if (at == a_NET_NUMBER_OF_DESKTOPS)
245 {
246 GSList* l;
247 for( l = all_panels; l; l = l->next )
248 ((LXPanel*)l->data)->priv->desknum = get_net_number_of_desktops();
249 fb_ev_emit(fbev, EV_NUMBER_OF_DESKTOPS);
250 }
251 else if (at == a_NET_DESKTOP_NAMES)
252 {
253 fb_ev_emit(fbev, EV_DESKTOP_NAMES);
254 }
255 else if (at == a_NET_ACTIVE_WINDOW)
256 {
257 fb_ev_emit(fbev, EV_ACTIVE_WINDOW );
258 }
259 else if (at == a_NET_CLIENT_LIST_STACKING)
260 {
261 fb_ev_emit(fbev, EV_CLIENT_LIST_STACKING);
262 }
263 else if (at == a_XROOTPMAP_ID)
264 {
265 GSList* l;
266 for( l = all_panels; l; l = l->next )
267 _panel_queue_update_background((LXPanel*)l->data);
268 }
269 else
270 return GDK_FILTER_CONTINUE;
271
272 return GDK_FILTER_REMOVE;
273 }
274 return GDK_FILTER_CONTINUE;
275 }
276
277 /* The same for new plugins type - they will be not unloaded by FmModule */
278 #define REGISTER_STATIC_MODULE(pc) do { \
279 extern LXPanelPluginInit lxpanel_static_plugin_##pc; \
280 lxpanel_register_plugin_type(#pc, &lxpanel_static_plugin_##pc); } while (0)
281
282 /* Initialize the static plugins. */
283 static void init_static_plugins(void)
284 {
285 #ifdef STATIC_SEPARATOR
286 REGISTER_STATIC_MODULE(separator);
287 #endif
288
289 #ifdef STATIC_LAUNCHTASKBAR
290 REGISTER_STATIC_MODULE(launchtaskbar);
291 #endif
292
293 #ifdef STATIC_DCLOCK
294 REGISTER_STATIC_MODULE(dclock);
295 #endif
296
297 #ifdef STATIC_WINCMD
298 REGISTER_STATIC_MODULE(wincmd);
299 #endif
300
301 #ifdef STATIC_DIRMENU
302 REGISTER_STATIC_MODULE(dirmenu);
303 #endif
304
305 #ifdef STATIC_PAGER
306 REGISTER_STATIC_MODULE(pager);
307 #endif
308
309 #ifdef STATIC_TRAY
310 REGISTER_STATIC_MODULE(tray);
311 #endif
312
313 #ifndef DISABLE_MENU
314 #ifdef STATIC_MENU
315 REGISTER_STATIC_MODULE(menu);
316 #endif
317 #endif
318 }
319
320 static void
321 usage()
322 {
323 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
324 g_print(_("Command line options:\n"));
325 g_print(_(" --help -- print this help and exit\n"));
326 g_print(_(" --version -- print version and exit\n"));
327 // g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
328 // g_print(_(" --configure -- launch configuration utility\n"));
329 g_print(_(" --profile name -- use specified profile\n"));
330 g_print("\n");
331 g_print(_(" -h -- same as --help\n"));
332 g_print(_(" -p -- same as --profile\n"));
333 g_print(_(" -v -- same as --version\n"));
334 // g_print(_(" -C -- same as --configure\n"));
335 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
336 }
337
338 /* Lightweight lock related functions - X clipboard hacks */
339
340 #define CLIPBOARD_NAME "LXPANEL_SELECTION"
341
342 /*
343 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
344 */
345 static void
346 clipboard_get_func(
347 GtkClipboard *clipboard G_GNUC_UNUSED,
348 GtkSelectionData *selection_data G_GNUC_UNUSED,
349 guint info G_GNUC_UNUSED,
350 gpointer user_data_or_owner G_GNUC_UNUSED)
351 {
352 }
353
354 /*
355 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
356 */
357 static void clipboard_clear_func(
358 GtkClipboard *clipboard G_GNUC_UNUSED,
359 gpointer user_data_or_owner G_GNUC_UNUSED)
360 {
361 }
362
363 /*
364 * Lightweight version for checking single instance.
365 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
366 *
367 * Returns TRUE if successfully retrieved and FALSE otherwise.
368 */
369 static gboolean check_main_lock()
370 {
371 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
372 gboolean retval = FALSE;
373 GtkClipboard *clipboard;
374 Atom atom;
375 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
376
377 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
378
379 XGrabServer(xdisplay);
380
381 if (XGetSelectionOwner(xdisplay, atom) != None)
382 goto out;
383
384 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
385
386 if (gtk_clipboard_set_with_data(clipboard, targets,
387 G_N_ELEMENTS (targets),
388 clipboard_get_func,
389 clipboard_clear_func, NULL))
390 retval = TRUE;
391
392 out:
393 XUngrabServer (xdisplay);
394 gdk_flush ();
395
396 return retval;
397 }
398 #undef CLIPBOARD_NAME
399
400 static void _start_panels_from_dir(const char *panel_dir)
401 {
402 GDir* dir = g_dir_open( panel_dir, 0, NULL );
403 const gchar* name;
404
405 if( ! dir )
406 {
407 return;
408 }
409
410 while((name = g_dir_read_name(dir)) != NULL)
411 {
412 char* panel_config = g_build_filename( panel_dir, name, NULL );
413 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
414 {
415 LXPanel* panel = panel_new( panel_config, name );
416 if( panel )
417 all_panels = g_slist_prepend( all_panels, panel );
418 }
419 g_free( panel_config );
420 }
421 g_dir_close( dir );
422 }
423
424 static gboolean start_all_panels( )
425 {
426 char *panel_dir;
427 const gchar * const * dir;
428
429 /* try user panels */
430 panel_dir = _user_config_file_name("panels", NULL);
431 _start_panels_from_dir(panel_dir);
432 g_free(panel_dir);
433 if (all_panels != NULL)
434 return TRUE;
435 /* else try XDG fallbacks */
436 dir = g_get_system_config_dirs();
437 if (dir) while (dir[0])
438 {
439 panel_dir = _system_config_file_name(dir[0], "panels");
440 _start_panels_from_dir(panel_dir);
441 g_free(panel_dir);
442 if (all_panels != NULL)
443 return TRUE;
444 dir++;
445 }
446 /* last try at old fallback for compatibility reasons */
447 panel_dir = _old_system_config_file_name("panels");
448 _start_panels_from_dir(panel_dir);
449 g_free(panel_dir);
450 return all_panels != NULL;
451 }
452
453 void load_global_config();
454 void free_global_config();
455
456 static void _ensure_user_config_dirs(void)
457 {
458 char *dir = g_build_filename(g_get_user_config_dir(), "lxpanel", cprofile,
459 "panels", NULL);
460
461 /* make sure the private profile and panels dir exists */
462 g_mkdir_with_parents(dir, 0700);
463 g_free(dir);
464 }
465
466 int main(int argc, char *argv[], char *env[])
467 {
468 int i;
469 const char* desktop_name;
470 char *file;
471
472 setlocale(LC_CTYPE, "");
473
474 #if !GLIB_CHECK_VERSION(2, 32, 0)
475 g_thread_init(NULL);
476 #endif
477 /* gdk_threads_init();
478 gdk_threads_enter(); */
479
480 gtk_init(&argc, &argv);
481 keybinder_init();
482
483 #ifdef ENABLE_NLS
484 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
485 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
486 textdomain ( GETTEXT_PACKAGE );
487 #endif
488
489 XSetLocaleModifiers("");
490 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
491
492 resolve_atoms();
493
494 desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
495 is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));
496
497 for (i = 1; i < argc; i++) {
498 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
499 usage();
500 exit(0);
501 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
502 printf("lxpanel %s\n", version);
503 exit(0);
504 } else if (!strcmp(argv[i], "--log")) {
505 i++;
506 if (i == argc) {
507 g_critical( "lxpanel: missing log level");
508 usage();
509 exit(1);
510 } else {
511 /* deprecated */
512 }
513 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
514 config = 1;
515 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
516 i++;
517 if (i == argc) {
518 g_critical( "lxpanel: missing profile name");
519 usage();
520 exit(1);
521 } else {
522 cprofile = g_strdup(argv[i]);
523 }
524 } else {
525 printf("lxpanel: unknown option - %s\n", argv[i]);
526 usage();
527 exit(1);
528 }
529 }
530
531 /* Add a gtkrc file to be parsed too. */
532 file = _user_config_file_name("gtkrc", NULL);
533 gtk_rc_parse(file);
534 g_free(file);
535
536 /* Check for duplicated lxpanel instances */
537 if (!check_main_lock() && !config) {
538 printf("There is already an instance of LXPanel. Now to exit\n");
539 exit(1);
540 }
541
542 _ensure_user_config_dirs();
543
544 /* Add our own icons to the search path of icon theme */
545 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/images" );
546
547 fbev = fb_ev_new();
548
549 is_restarting = FALSE;
550
551 /* init LibFM */
552 fm_gtk_init(NULL);
553
554 /* prepare modules data */
555 lxpanel_prepare_modules();
556 lxpanel_register_plugin_type("space", &_lxpanel_static_plugin_space);
557 init_static_plugins();
558
559 load_global_config();
560
561 /* NOTE: StructureNotifyMask is required by XRandR
562 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
563 */
564 gdk_window_set_events(gdk_get_default_root_window(), GDK_STRUCTURE_MASK |
565 GDK_SUBSTRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK);
566 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
567
568 if( G_UNLIKELY( ! start_all_panels() ) )
569 g_warning( "Config files are not found.\n" );
570 /*
571 * FIXME: configure??
572 if (config)
573 configure();
574 */
575 gtk_main();
576
577 XSelectInput (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), GDK_ROOT_WINDOW(), NoEventMask);
578 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
579
580 /* destroy all panels */
581 g_slist_foreach( all_panels, (GFunc) gtk_widget_destroy, NULL );
582 g_slist_free( all_panels );
583 all_panels = NULL;
584 g_free( cfgfile );
585
586 free_global_config();
587
588 lxpanel_unload_modules();
589 fm_gtk_finalize();
590
591 /* gdk_threads_leave(); */
592
593 g_object_unref(fbev);
594
595 if (!is_restarting)
596 return 0;
597 if (strchr(argv[0], G_DIR_SEPARATOR))
598 execve(argv[0], argv, env);
599 else
600 execve(g_find_program_in_path(argv[0]), argv, env);
601 return 1;
602 }