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