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