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