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