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