* Fix drawing bugs in desktop window.
[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);
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 }
328 return ;
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 {
19abb8bf 378 show_pref = FALSE;
f8f2bfad
HJYP
379 return TRUE;
380 }
381 else if(desktop_pref)
382 {
19abb8bf 383 desktop_pref = FALSE;
f8f2bfad
HJYP
384 return TRUE;
385 }
386 else if(set_wallpaper)
387 {
f970e846 388 /* g_debug("\'%s\'", set_wallpaper); */
f8f2bfad 389 /* Make sure this is a support image file. */
f970e846 390 if(gdk_pixbuf_get_file_info(set_wallpaper, NULL, NULL))
f8f2bfad 391 {
f970e846
HJYP
392 if(app_config->wallpaper)
393 g_free(app_config->wallpaper);
394 app_config->wallpaper = set_wallpaper;
f8f2bfad 395 set_wallpaper = NULL;
f970e846
HJYP
396 if(app_config->wallpaper_mode == FM_WP_COLOR)
397 app_config->wallpaper_mode = FM_WP_FULL;
f8f2bfad
HJYP
398 fm_config_emit_changed(app_config, "wallpaper");
399 fm_app_config_save(app_config, NULL);
400 }
401 return FALSE;
402 }
403 }
404
405 if(G_UNLIKELY(find_files))
406 {
407 /* FIXME: find files */
408 }
409 else
410 {
411 if(!files_to_open)
412 {
413 FmPath* path;
414 w = fm_main_win_new();
19abb8bf 415 gtk_window_set_default_size(w, app_config->win_width, app_config->win_height);
f8f2bfad
HJYP
416 gtk_widget_show(w);
417 path = fm_path_get_home();
418 fm_main_win_chdir(w, path);
f8f2bfad
HJYP
419 }
420 }
421 return ret;
422}
423
424/* After opening any window/dialog/tool, this should be called. */
425void pcmanfm_ref()
426{
427 ++n_pcmanfm_ref;
428 /* g_debug("ref: %d", n_pcmanfm_ref); */
429}
430
431/* After closing any window/dialog/tool, this should be called.
432 * If the last window is closed and we are not a deamon, pcmanfm will quit.
433 */
434void pcmanfm_unref()
435{
436 --n_pcmanfm_ref;
437 /* g_debug("unref: %d, daemon_mode=%d, desktop_running=%d", n_pcmanfm_ref, daemon_mode, desktop_running); */
438 if( 0 == n_pcmanfm_ref && !daemon_mode && !desktop_running )
439 gtk_main_quit();
b6e3c554 440}