Fix problems with updating wallpapers.
[lxde/pcmanfm.git] / src / pcmanfm.c
CommitLineData
b6e3c554
HJYP
1/*
2 * pcmanfm.c
3 *
f0ce8c37 4 * Copyright 2009 PCMan <pcman.tw@gmail.com>
b6e3c554
HJYP
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22#include <config.h>
23#include <gtk/gtk.h>
24#include <stdio.h>
f8f2bfad
HJYP
25#include <glib/gi18n.h>
26
27#include <stdlib.h>
28#include <string.h>
29/* socket is used to keep single instance */
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <sys/un.h>
33#include <signal.h>
34#include <unistd.h> /* for getcwd */
b6e3c554
HJYP
35
36#include <fm-gtk.h>
4d55886c 37#include "app-config.h"
b6e3c554 38#include "main-win.h"
8505a8ef
HJYP
39#include "desktop.h"
40
f8f2bfad
HJYP
41static int sock;
42GIOChannel* io_channel = NULL;
43
44gboolean daemon_mode = FALSE;
45
f8f2bfad 46static char** files_to_open = NULL;
f970e846
HJYP
47static char* profile = NULL;
48static char* config_name = NULL;
f8f2bfad
HJYP
49static gboolean no_desktop = FALSE;
50static gboolean show_desktop = FALSE;
51static gboolean desktop_off = FALSE;
52static gboolean desktop_running = FALSE;
53static gboolean new_tab = FALSE;
54static int show_pref = 0;
55static gboolean desktop_pref = FALSE;
56static char* set_wallpaper = NULL;
57static gboolean find_files = FALSE;
58
59static int n_pcmanfm_ref = 0;
60
61static GOptionEntry opt_entries[] =
8505a8ef 62{
f970e846
HJYP
63 { "new-tab", 't', 0, G_OPTION_ARG_NONE, &new_tab, N_("Open folders in new tabs of the last used window instead of creating new windows"), NULL },
64 { "profile", 'p', 0, G_OPTION_ARG_STRING, &profile, N_("Name of config profile"), "<profile name>" },
f8f2bfad
HJYP
65 { "desktop", '\0', 0, G_OPTION_ARG_NONE, &show_desktop, N_("Launch desktop manager"), NULL },
66 { "desktop-off", '\0', 0, G_OPTION_ARG_NONE, &desktop_off, N_("Turn off desktop manager if it's running"), NULL },
f8f2bfad 67 { "daemon-mode", 'd', 0, G_OPTION_ARG_NONE, &daemon_mode, N_("Run PCManFM as a daemon"), NULL },
f8f2bfad 68 { "desktop-pref", '\0', 0, G_OPTION_ARG_NONE, &desktop_pref, N_("Open desktop preference dialog"), NULL },
f970e846
HJYP
69 { "set-wallpaper", 'w', 0, G_OPTION_ARG_FILENAME, &set_wallpaper, N_("Set desktop wallpaper"), N_("<image file>") },
70 { "show-pref", '\0', 0, G_OPTION_ARG_INT, &show_pref, N_("Open preference dialog. 'n' is the number of page you want to show (1, 2, 3...)."), "n" },
f8f2bfad 71 { "find-files", 'f', 0, G_OPTION_ARG_NONE, &find_files, N_("Open Find Files utility"), NULL },
f970e846 72 { "no-desktop", '\0', 0, G_OPTION_ARG_NONE, &no_desktop, N_("No function. Just to be compatible with nautilus"), NULL },
f8f2bfad
HJYP
73 {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &files_to_open, NULL, N_("[FILE1, FILE2,...]")},
74 { NULL }
75};
76
77static gboolean single_instance_check();
78static void single_instance_finalize();
79static void get_socket_name(char* buf, int len);
80static gboolean pcmanfm_run();
81static gboolean on_socket_event(GIOChannel* ioc, GIOCondition cond, gpointer data);
b6e3c554
HJYP
82
83int main(int argc, char** argv)
84{
4d55886c 85 FmConfig* config;
f8f2bfad
HJYP
86 GError* err = NULL;
87
88#ifdef ENABLE_NLS
89 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
90 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
91 textdomain ( GETTEXT_PACKAGE );
92#endif
93
94 /* initialize GTK+ and parse the command line arguments */
95 if(G_UNLIKELY(!gtk_init_with_args(&argc, &argv, "", opt_entries, GETTEXT_PACKAGE, &err)))
96 {
97 g_printf("%s\n", err->message);
98 g_free(err);
99 return 1;
100 }
101
102 /* ensure that there is only one instance of pcmanfm.
103 if there is an existing instance, command line arguments
104 will be passed to the existing instance, and exit() will be called here. */
105 single_instance_check();
106
107 /* intercept signals */
108 signal( SIGPIPE, SIG_IGN );
109 /* signal( SIGHUP, gtk_main_quit ); */
110 signal( SIGINT, gtk_main_quit );
111 signal( SIGTERM, gtk_main_quit );
b6e3c554 112
f970e846
HJYP
113 config = fm_app_config_new(); /* this automatically load libfm config file. */
114 /* load pcmanfm-specific config file */
115 if(profile)
116 config_name = g_strconcat("pcmanfm/", profile, ".conf", NULL);
117 fm_app_config_load_from_file(config, config_name);
118
4d55886c 119 fm_gtk_init(config);
b6e3c554 120
f8f2bfad
HJYP
121 /* the main part */
122 if(pcmanfm_run())
123 {
124 gtk_main();
125 if(desktop_running)
126 fm_desktop_manager_finalize();
127
128 fm_config_save(config, NULL); /* save libfm config */
f970e846 129 fm_app_config_save((FmAppConfig*)config, config_name); /* save pcmanfm config */
f8f2bfad
HJYP
130 }
131 single_instance_finalize();
8505a8ef 132
f8f2bfad
HJYP
133 fm_gtk_finalize();
134 g_object_unref(config);
b6e3c554 135
f8f2bfad
HJYP
136 return 0;
137}
138
139
140inline static GString* args_to_keyfile()
141{
142 int i;
143 GString* buf = g_string_sized_new(1024);
144 g_string_assign(buf, "[a]\n");
145 for(i = 0; i < G_N_ELEMENTS(opt_entries)-1;++i)
146 {
147 GOptionEntry* ent = &opt_entries[i];
148 if(G_LIKELY(*ent->long_name))
149 g_string_append(buf, ent->long_name);
150 else /* G_OPTION_REMAINING */
151 g_string_append(buf, "@");
152 g_string_append_c(buf, '=');
153 switch(ent->arg)
154 {
155 case G_OPTION_ARG_NONE: /* bool */
156 g_string_append_c(buf, *(gboolean*)ent->arg_data ? '1' : '0');
157 break;
158 case G_OPTION_ARG_INT: /* int */
159 g_string_append_printf(buf, "%d", *(gint*)ent->arg_data);
160 break;
161 case G_OPTION_ARG_FILENAME_ARRAY: /* string array */
f970e846 162 case G_OPTION_ARG_STRING_ARRAY:
f8f2bfad
HJYP
163 {
164 char** files = *(char***)ent->arg_data;
165 if(files)
166 {
167 for(;*files;++files)
168 {
169 g_string_append(buf, *files);
170 g_string_append_c(buf, ';');
171 }
172 }
173 }
174 break;
175 case G_OPTION_ARG_FILENAME:
176 case G_OPTION_ARG_STRING: /* string */
177 if(*(gchar**)ent->arg_data)
178 g_string_append(buf, *(gchar**)ent->arg_data);
179 break;
180 }
181 g_string_append_c(buf, '\n');
182 }
183 return buf;
184}
185
186inline static void keyfile_to_args(GString* buf)
187{
188 GKeyFile* kf = g_key_file_new();
189 /* g_debug("\n%s", buf->str); */
190 if(g_key_file_load_from_data(kf, buf->str, buf->len, 0, NULL))
b6e3c554 191 {
f8f2bfad
HJYP
192 int i;
193 for(i = 0; i < G_N_ELEMENTS(opt_entries)-1;++i)
194 {
195 GOptionEntry* ent = &opt_entries[i];
196 switch(ent->arg)
197 {
198 case G_OPTION_ARG_NONE: /* bool */
199 *(gboolean*)ent->arg_data = g_key_file_get_boolean(kf, "a", ent->long_name, NULL);
200 break;
201 case G_OPTION_ARG_INT: /* int */
202 *(gint*)ent->arg_data = g_key_file_get_integer(kf, "a", ent->long_name, NULL);
203 break;
204 case G_OPTION_ARG_FILENAME_ARRAY: /* string array */
f970e846 205 case G_OPTION_ARG_STRING_ARRAY:
f8f2bfad
HJYP
206 if(*(char**)ent->arg_data)
207 g_strfreev(*(char***)ent->arg_data);
208 *(gchar***)ent->arg_data = g_key_file_get_string_list(kf, "a", *ent->long_name ? *ent->long_name : "@", NULL, NULL);
209 break;
210 case G_OPTION_ARG_FILENAME:
211 case G_OPTION_ARG_STRING: /* string */
212 if(*(char**)ent->arg_data)
213 g_free(*(char**)ent->arg_data);
214 *(char**)ent->arg_data = g_key_file_get_string(kf, "a", ent->long_name, NULL);
215 break;
216 }
217 }
b6e3c554 218 }
f8f2bfad
HJYP
219 g_key_file_free(kf);
220}
8505a8ef 221
f8f2bfad
HJYP
222gboolean on_socket_event( GIOChannel* ioc, GIOCondition cond, gpointer data )
223{
224 int client, r;
225 socklen_t addr_len = 0;
226 struct sockaddr_un client_addr ={ 0 };
227 static char buf[ 1024 ];
228 GString* args;
229 char** file;
b6e3c554 230
f8f2bfad
HJYP
231 if ( cond & G_IO_IN )
232 {
233 client = accept( g_io_channel_unix_get_fd( ioc ), (struct sockaddr *)&client_addr, &addr_len );
234 if ( client != -1 )
235 {
236 args = g_string_sized_new(1024);
237 while( (r = read( client, buf, sizeof(buf) )) > 0 )
238 g_string_append_len( args, buf, r);
239 shutdown( client, 2 );
240 close( client );
241 keyfile_to_args(args);
242 g_string_free( args, TRUE );
243 pcmanfm_run();
244 }
245 }
246 return TRUE;
247}
19fbd668 248
f8f2bfad
HJYP
249void get_socket_name( char* buf, int len )
250{
251 char* dpy = gdk_get_display();
252 g_snprintf( buf, len, "/tmp/.pcmanfm2-socket%s-%s", dpy, g_get_user_name() );
253 g_free( dpy );
254}
8505a8ef 255
f8f2bfad
HJYP
256gboolean single_instance_check()
257{
258 struct sockaddr_un addr;
259 int addr_len;
260 int ret;
261 int reuse;
b6e3c554 262
f8f2bfad
HJYP
263 if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
264 {
265 ret = 1;
266 goto _exit;
267 }
268
269 /* FIXME: use abstract socket */
270 addr.sun_family = AF_UNIX;
271 get_socket_name(addr.sun_path, sizeof( addr.sun_path ));
272#ifdef SUN_LEN
273 addr_len = SUN_LEN(&addr);
274#else
275 addr_len = strlen( addr.sun_path ) + sizeof( addr.sun_family );
276#endif
277
278 /* try to connect to existing instance */
279 if(connect(sock, (struct sockaddr*)&addr, addr_len) == 0)
280 {
281 /* connected successfully */
282 GString* buf = args_to_keyfile();
283 write(sock, buf->str, buf->len);
284 g_string_free(buf, TRUE);
285
286 shutdown( sock, 2 );
287 close( sock );
288 ret = 0;
289 goto _exit;
290 }
291
292 /* There is no existing server, and we are in the first instance. */
293 unlink( addr.sun_path ); /* delete old socket file if it exists. */
294 reuse = 1;
295 ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse) );
296 if(bind(sock, (struct sockaddr*)&addr, addr_len) == -1)
297 {
298 ret = 1;
299 goto _exit;
300 }
301
302 io_channel = g_io_channel_unix_new(sock);
303 g_io_channel_set_encoding(io_channel, NULL, NULL);
304 g_io_channel_set_buffered(io_channel, FALSE);
305 g_io_add_watch(io_channel, G_IO_IN,
306 (GIOFunc)on_socket_event, NULL);
307 if(listen(sock, 5) == -1)
308 {
309 ret = 1;
310 goto _exit;
311 }
312 return ;
313
314_exit:
315
316 gdk_notify_startup_complete();
317 exit( ret );
318}
319
320void single_instance_finalize()
321{
322 char lock_file[256];
323 shutdown(sock, 2);
324 g_io_channel_unref(io_channel);
325 close(sock);
326 get_socket_name(lock_file, sizeof( lock_file ));
327 unlink(lock_file);
328}
329
330
331gboolean pcmanfm_run()
332{
333 gboolean ret = TRUE;
334 char** file;
335 GtkWidget* w;
336
337 if(G_UNLIKELY(files_to_open && !files_to_open[0]))
338 {
339 g_strfreev(files_to_open);
340 files_to_open = NULL;
341 }
342
343 if(!files_to_open)
344 {
345 /* Launch desktop manager */
346 if(show_desktop)
347 {
348 if(!desktop_running)
349 {
350 fm_desktop_manager_init();
351 desktop_running = TRUE;
352 }
353 return TRUE;
354 }
355 else if(desktop_off)
356 {
357 if(desktop_running)
358 {
359 desktop_running = FALSE;
360 fm_desktop_manager_finalize();
361 }
362 return FALSE;
363 }
364 else if(show_pref > 0)
365 {
366 return TRUE;
367 }
368 else if(desktop_pref)
369 {
370 return TRUE;
371 }
372 else if(set_wallpaper)
373 {
f970e846 374 /* g_debug("\'%s\'", set_wallpaper); */
f8f2bfad 375 /* Make sure this is a support image file. */
f970e846 376 if(gdk_pixbuf_get_file_info(set_wallpaper, NULL, NULL))
f8f2bfad 377 {
f970e846
HJYP
378 if(app_config->wallpaper)
379 g_free(app_config->wallpaper);
380 app_config->wallpaper = set_wallpaper;
f8f2bfad 381 set_wallpaper = NULL;
f970e846
HJYP
382 if(app_config->wallpaper_mode == FM_WP_COLOR)
383 app_config->wallpaper_mode = FM_WP_FULL;
f8f2bfad
HJYP
384 fm_config_emit_changed(app_config, "wallpaper");
385 fm_app_config_save(app_config, NULL);
386 }
387 return FALSE;
388 }
389 }
390
391 if(G_UNLIKELY(find_files))
392 {
393 /* FIXME: find files */
394 }
395 else
396 {
397 if(!files_to_open)
398 {
399 FmPath* path;
400 w = fm_main_win_new();
401 gtk_window_set_default_size(w, 640, 480);
402 gtk_widget_show(w);
403 path = fm_path_get_home();
404 fm_main_win_chdir(w, path);
405 fm_path_unref(path);
406 }
407 }
408 return ret;
409}
410
411/* After opening any window/dialog/tool, this should be called. */
412void pcmanfm_ref()
413{
414 ++n_pcmanfm_ref;
415 /* g_debug("ref: %d", n_pcmanfm_ref); */
416}
417
418/* After closing any window/dialog/tool, this should be called.
419 * If the last window is closed and we are not a deamon, pcmanfm will quit.
420 */
421void pcmanfm_unref()
422{
423 --n_pcmanfm_ref;
424 /* g_debug("unref: %d, daemon_mode=%d, desktop_running=%d", n_pcmanfm_ref, daemon_mode, desktop_running); */
425 if( 0 == n_pcmanfm_ref && !daemon_mode && !desktop_running )
426 gtk_main_quit();
b6e3c554 427}