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