Additional remote commands for layout windows
[geeqie.git] / src / remote.c
1 /*
2  * Copyright (C) 2004 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "remote.h"
24
25 #include "cache_maint.h"
26 #include "collect.h"
27 #include "filedata.h"
28 #include "image.h"
29 #include "img-view.h"
30 #include "layout.h"
31 #include "layout_image.h"
32 #include "layout_util.h"
33 #include "misc.h"
34 #include "pixbuf-renderer.h"
35 #include "slideshow.h"
36 #include "ui_fileops.h"
37 #include "rcfile.h"
38
39 #include <sys/socket.h>
40 #include <sys/un.h>
41 #include <signal.h>
42 #include <errno.h>
43
44 #include "glua.h"
45
46 #define SERVER_MAX_CLIENTS 8
47
48 #define REMOTE_SERVER_BACKLOG 4
49
50
51 #ifndef UNIX_PATH_MAX
52 #define UNIX_PATH_MAX 108
53 #endif
54
55
56 static RemoteConnection *remote_client_open(const gchar *path);
57 static gint remote_client_send(RemoteConnection *rc, const gchar *text);
58 static void gr_raise(const gchar *text, GIOChannel *channel, gpointer data);
59
60 static LayoutWindow *lw_id = NULL; /* points to the window set by the --id option */
61
62 typedef struct _RemoteClient RemoteClient;
63 struct _RemoteClient {
64         gint fd;
65         guint channel_id; /* event source id */
66         RemoteConnection *rc;
67 };
68
69 typedef struct _RemoteData RemoteData;
70 struct _RemoteData {
71         CollectionData *command_collection;
72 };
73
74
75 static gboolean remote_server_client_cb(GIOChannel *source, GIOCondition condition, gpointer data)
76 {
77         RemoteClient *client = data;
78         RemoteConnection *rc;
79         GIOStatus status = G_IO_STATUS_NORMAL;
80
81         lw_id = NULL;
82         rc = client->rc;
83
84         if (condition & G_IO_IN)
85                 {
86                 gchar *buffer = NULL;
87                 GError *error = NULL;
88                 gsize termpos;
89
90                 while ((status = g_io_channel_read_line(source, &buffer, NULL, &termpos, &error)) == G_IO_STATUS_NORMAL)
91                         {
92                         if (buffer)
93                                 {
94                                 buffer[termpos] = '\0';
95
96                                 if (strlen(buffer) > 0)
97                                         {
98                                         if (rc->read_func) rc->read_func(rc, buffer, source, rc->read_data);
99                                         g_io_channel_write_chars(source, "\n", -1, NULL, NULL); /* empty line finishes the command */
100                                         g_io_channel_flush(source, NULL);
101                                         }
102                                 g_free(buffer);
103
104                                 buffer = NULL;
105                                 }
106                         }
107
108                 if (error)
109                         {
110                         log_printf("error reading socket: %s\n", error->message);
111                         g_error_free(error);
112                         }
113                 }
114
115         if (condition & G_IO_HUP || status == G_IO_STATUS_EOF || status == G_IO_STATUS_ERROR)
116                 {
117                 rc->clients = g_list_remove(rc->clients, client);
118
119                 DEBUG_1("HUP detected, closing client.");
120                 DEBUG_1("client count %d", g_list_length(rc->clients));
121
122                 g_source_remove(client->channel_id);
123                 close(client->fd);
124                 g_free(client);
125                 }
126
127         return TRUE;
128 }
129
130 static void remote_server_client_add(RemoteConnection *rc, gint fd)
131 {
132         RemoteClient *client;
133         GIOChannel *channel;
134
135         if (g_list_length(rc->clients) > SERVER_MAX_CLIENTS)
136                 {
137                 log_printf("maximum remote clients of %d exceeded, closing connection\n", SERVER_MAX_CLIENTS);
138                 close(fd);
139                 return;
140                 }
141
142         client = g_new0(RemoteClient, 1);
143         client->rc = rc;
144         client->fd = fd;
145
146         channel = g_io_channel_unix_new(fd);
147         client->channel_id = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP,
148                                                  remote_server_client_cb, client, NULL);
149         g_io_channel_unref(channel);
150
151         rc->clients = g_list_append(rc->clients, client);
152         DEBUG_1("client count %d", g_list_length(rc->clients));
153 }
154
155 static void remote_server_clients_close(RemoteConnection *rc)
156 {
157         while (rc->clients)
158                 {
159                 RemoteClient *client = rc->clients->data;
160
161                 rc->clients = g_list_remove(rc->clients, client);
162
163                 g_source_remove(client->channel_id);
164                 close(client->fd);
165                 g_free(client);
166                 }
167 }
168
169 static gboolean remote_server_read_cb(GIOChannel *source, GIOCondition condition, gpointer data)
170 {
171         RemoteConnection *rc = data;
172         gint fd;
173         guint alen;
174
175         fd = accept(rc->fd, NULL, &alen);
176         if (fd == -1)
177                 {
178                 log_printf("error accepting socket: %s\n", strerror(errno));
179                 return TRUE;
180                 }
181
182         remote_server_client_add(rc, fd);
183
184         return TRUE;
185 }
186
187 gboolean remote_server_exists(const gchar *path)
188 {
189         RemoteConnection *rc;
190
191         /* verify server up */
192         rc = remote_client_open(path);
193         remote_close(rc);
194
195         if (rc) return TRUE;
196
197         /* unable to connect, remove socket file to free up address */
198         unlink(path);
199         return FALSE;
200 }
201
202 static RemoteConnection *remote_server_open(const gchar *path)
203 {
204         RemoteConnection *rc;
205         struct sockaddr_un addr;
206         gint sun_path_len;
207         gint fd;
208         GIOChannel *channel;
209
210         if (remote_server_exists(path))
211                 {
212                 log_printf("Address already in use: %s\n", path);
213                 return NULL;
214                 }
215
216         fd = socket(PF_UNIX, SOCK_STREAM, 0);
217         if (fd == -1) return NULL;
218
219         addr.sun_family = AF_UNIX;
220         sun_path_len = MIN(strlen(path) + 1, UNIX_PATH_MAX);
221         strncpy(addr.sun_path, path, sun_path_len);
222         if (bind(fd, &addr, sizeof(addr)) == -1 ||
223             listen(fd, REMOTE_SERVER_BACKLOG) == -1)
224                 {
225                 log_printf("error subscribing to socket: %s\n", strerror(errno));
226                 close(fd);
227                 return NULL;
228                 }
229
230         rc = g_new0(RemoteConnection, 1);
231
232         rc->server = TRUE;
233         rc->fd = fd;
234         rc->path = g_strdup(path);
235
236         channel = g_io_channel_unix_new(rc->fd);
237         g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL);
238
239         rc->channel_id = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN,
240                                              remote_server_read_cb, rc, NULL);
241         g_io_channel_unref(channel);
242
243         return rc;
244 }
245
246 static void remote_server_subscribe(RemoteConnection *rc, RemoteReadFunc *func, gpointer data)
247 {
248         if (!rc || !rc->server) return;
249
250         rc->read_func = func;
251         rc->read_data = data;
252 }
253
254
255 static RemoteConnection *remote_client_open(const gchar *path)
256 {
257         RemoteConnection *rc;
258         struct stat st;
259         struct sockaddr_un addr;
260         gint sun_path_len;
261         gint fd;
262
263         if (stat(path, &st) != 0 || !S_ISSOCK(st.st_mode)) return NULL;
264
265         fd = socket(PF_UNIX, SOCK_STREAM, 0);
266         if (fd == -1) return NULL;
267
268         addr.sun_family = AF_UNIX;
269         sun_path_len = MIN(strlen(path) + 1, UNIX_PATH_MAX);
270         strncpy(addr.sun_path, path, sun_path_len);
271         if (connect(fd, &addr, sizeof(addr)) == -1)
272                 {
273                 DEBUG_1("error connecting to socket: %s", strerror(errno));
274                 close(fd);
275                 return NULL;
276                 }
277
278         rc = g_new0(RemoteConnection, 1);
279         rc->server = FALSE;
280         rc->fd = fd;
281         rc->path = g_strdup(path);
282
283         return rc;
284 }
285
286 static sig_atomic_t sigpipe_occurred = FALSE;
287
288 static void sighandler_sigpipe(gint sig)
289 {
290         sigpipe_occurred = TRUE;
291 }
292
293 static gboolean remote_client_send(RemoteConnection *rc, const gchar *text)
294 {
295         struct sigaction new_action, old_action;
296         gboolean ret = FALSE;
297         GError *error = NULL;
298         GIOChannel *channel;
299
300         if (!rc || rc->server) return FALSE;
301         if (!text) return TRUE;
302
303         sigpipe_occurred = FALSE;
304
305         new_action.sa_handler = sighandler_sigpipe;
306         sigemptyset(&new_action.sa_mask);
307         new_action.sa_flags = 0;
308
309         /* setup our signal handler */
310         sigaction(SIGPIPE, &new_action, &old_action);
311
312         channel = g_io_channel_unix_new(rc->fd);
313
314         g_io_channel_write_chars(channel, text, -1, NULL, &error);
315         g_io_channel_write_chars(channel, "\n", -1, NULL, &error);
316         g_io_channel_flush(channel, &error);
317
318         if (error)
319                 {
320                 log_printf("error reading socket: %s\n", error->message);
321                 g_error_free(error);
322                 ret = FALSE;;
323                 }
324         else
325                 {
326                 ret = TRUE;
327                 }
328
329         if (ret)
330                 {
331                 gchar *buffer = NULL;
332                 gsize termpos;
333                 while (g_io_channel_read_line(channel, &buffer, NULL, &termpos, &error) == G_IO_STATUS_NORMAL)
334                         {
335                         if (buffer)
336                                 {
337                                 if (buffer[0] == '\n') /* empty line finishes the command */
338                                         {
339                                         g_free(buffer);
340                                         fflush(stdout);
341                                         break;
342                                         }
343                                 buffer[termpos] = '\0';
344                                 printf("%s\n", buffer);
345                                 g_free(buffer);
346                                 buffer = NULL;
347                                 }
348                         }
349
350                 if (error)
351                         {
352                         log_printf("error reading socket: %s\n", error->message);
353                         g_error_free(error);
354                         ret = FALSE;
355                         }
356                 }
357
358
359         /* restore the original signal handler */
360         sigaction(SIGPIPE, &old_action, NULL);
361         g_io_channel_unref(channel);
362         return ret;
363 }
364
365 void remote_close(RemoteConnection *rc)
366 {
367         if (!rc) return;
368
369         if (rc->server)
370                 {
371                 remote_server_clients_close(rc);
372
373                 g_source_remove(rc->channel_id);
374                 unlink(rc->path);
375                 }
376
377         if (rc->read_data)
378                 g_free(rc->read_data);
379
380         close(rc->fd);
381
382         g_free(rc->path);
383         g_free(rc);
384 }
385
386 /*
387  *-----------------------------------------------------------------------------
388  * remote functions
389  *-----------------------------------------------------------------------------
390  */
391
392 static void gr_image_next(const gchar *text, GIOChannel *channel, gpointer data)
393 {
394         layout_image_next(lw_id);
395 }
396
397 static void gr_new_window(const gchar *text, GIOChannel *channel, gpointer data)
398 {
399         LayoutWindow *lw = NULL;
400
401         if (!layout_valid(&lw)) return;
402
403         lw_id = layout_menu_new_window(NULL, lw);
404 }
405
406 static gboolean gr_close_window_cb()
407 {
408         if (!layout_valid(&lw_id)) return FALSE;
409
410         layout_menu_close_cb(NULL, lw_id);
411
412         return FALSE;
413 }
414
415 static void gr_close_window(const gchar *text, GIOChannel *channel, gpointer data)
416 {
417         g_idle_add(gr_close_window_cb, NULL);
418 }
419
420 static void gr_image_prev(const gchar *text, GIOChannel *channel, gpointer data)
421 {
422         layout_image_prev(lw_id);
423 }
424
425 static void gr_image_first(const gchar *text, GIOChannel *channel, gpointer data)
426 {
427         layout_image_first(lw_id);
428 }
429
430 static void gr_image_last(const gchar *text, GIOChannel *channel, gpointer data)
431 {
432         layout_image_last(lw_id);
433 }
434
435 static void gr_fullscreen_toggle(const gchar *text, GIOChannel *channel, gpointer data)
436 {
437         layout_image_full_screen_toggle(lw_id);
438 }
439
440 static void gr_fullscreen_start(const gchar *text, GIOChannel *channel, gpointer data)
441 {
442         layout_image_full_screen_start(lw_id);
443 }
444
445 static void gr_fullscreen_stop(const gchar *text, GIOChannel *channel, gpointer data)
446 {
447         layout_image_full_screen_stop(lw_id);
448 }
449
450 static void gr_lw_id(const gchar *text, GIOChannel *channel, gpointer data)
451 {
452         lw_id = layout_find_by_layout_id(text);
453         if (!lw_id)
454                 {
455                 log_printf("remote sent window ID that does not exist:\"%s\"\n",text);
456                 }
457         layout_valid(&lw_id);
458 }
459
460 static void gr_slideshow_start_rec(const gchar *text, GIOChannel *channel, gpointer data)
461 {
462         GList *list;
463         FileData *dir_fd = file_data_new_dir(text);
464         list = filelist_recursive(dir_fd);
465         file_data_unref(dir_fd);
466         if (!list) return;
467 //printf("length: %d\n", g_list_length(list));
468         layout_image_slideshow_stop(lw_id);
469         layout_image_slideshow_start_from_list(lw_id, list);
470 }
471
472 static void gr_cache_thumb(const gchar *text, GIOChannel *channel, gpointer data)
473 {
474         if (!g_strcmp0(text, "clear"))
475                 cache_maintain_home_remote(FALSE, TRUE);
476         else if (!g_strcmp0(text, "clean"))
477                 cache_maintain_home_remote(FALSE, FALSE);
478 }
479
480 static void gr_cache_shared(const gchar *text, GIOChannel *channel, gpointer data)
481 {
482         if (!g_strcmp0(text, "clear"))
483                 cache_manager_standard_process_remote(TRUE);
484         else if (!g_strcmp0(text, "clean"))
485                 cache_manager_standard_process_remote(FALSE);
486 }
487
488 static void gr_cache_metadata(const gchar *text, GIOChannel *channel, gpointer data)
489 {
490         cache_maintain_home_remote(TRUE, FALSE);
491 }
492
493 static void gr_cache_render(const gchar *text, GIOChannel *channel, gpointer data)
494 {
495         cache_manager_render_remote(text, FALSE, FALSE);
496 }
497
498 static void gr_cache_render_recurse(const gchar *text, GIOChannel *channel, gpointer data)
499 {
500         cache_manager_render_remote(text, TRUE, FALSE);
501 }
502
503 static void gr_cache_render_standard(const gchar *text, GIOChannel *channel, gpointer data)
504 {
505         if(options->thumbnails.spec_standard)
506                 cache_manager_render_remote(text, FALSE, TRUE);
507 }
508
509 static void gr_cache_render_standard_recurse(const gchar *text, GIOChannel *channel, gpointer data)
510 {
511         if(options->thumbnails.spec_standard)
512                 cache_manager_render_remote(text, TRUE, TRUE);
513 }
514
515 static void gr_slideshow_toggle(const gchar *text, GIOChannel *channel, gpointer data)
516 {
517         layout_image_slideshow_toggle(lw_id);
518 }
519
520 static void gr_slideshow_start(const gchar *text, GIOChannel *channel, gpointer data)
521 {
522         layout_image_slideshow_start(lw_id);
523 }
524
525 static void gr_slideshow_stop(const gchar *text, GIOChannel *channel, gpointer data)
526 {
527         layout_image_slideshow_stop(lw_id);
528 }
529
530 static void gr_slideshow_delay(const gchar *text, GIOChannel *channel, gpointer data)
531 {
532         gdouble t1, t2, t3, n;
533         gint res;
534
535         res = sscanf(text, "%lf:%lf:%lf", &t1, &t2, &t3);
536         if (res == 3)
537                 {
538                 n = (t1 * 3600) + (t2 * 60) + t3;
539                 if (n < SLIDESHOW_MIN_SECONDS || n > SLIDESHOW_MAX_SECONDS ||
540                                 t1 >= 24 || t2 >= 60 || t3 >= 60)
541                         {
542                         printf_term(TRUE, "Remote slideshow delay out of range (%.1f to %.1f)\n",
543                                                                 SLIDESHOW_MIN_SECONDS, SLIDESHOW_MAX_SECONDS);
544                         return;
545                         }
546                 }
547         else if (res == 2)
548                 {
549                 n = t1 * 60 + t2;
550                 if (n < SLIDESHOW_MIN_SECONDS || n > SLIDESHOW_MAX_SECONDS ||
551                                 t1 >= 60 || t2 >= 60)
552                         {
553                         printf_term(TRUE, "Remote slideshow delay out of range (%.1f to %.1f)\n",
554                                                                 SLIDESHOW_MIN_SECONDS, SLIDESHOW_MAX_SECONDS);
555                         return;
556                         }
557                 }
558         else if (res == 1)
559                 {
560                 n = t1;
561                 if (n < SLIDESHOW_MIN_SECONDS || n > SLIDESHOW_MAX_SECONDS)
562                         {
563                         printf_term(TRUE, "Remote slideshow delay out of range (%.1f to %.1f)\n",
564                                                                 SLIDESHOW_MIN_SECONDS, SLIDESHOW_MAX_SECONDS);
565                         return;
566                         }
567                 }
568         else
569                 {
570                 n = 0;
571                 }
572
573         options->slideshow.delay = (gint)(n * 10.0 + 0.01);
574 }
575
576 static void gr_tools_show(const gchar *text, GIOChannel *channel, gpointer data)
577 {
578         gboolean popped;
579         gboolean hidden;
580
581         if (layout_tools_float_get(lw_id, &popped, &hidden) && hidden)
582                 {
583                 layout_tools_float_set(lw_id, popped, FALSE);
584                 }
585 }
586
587 static void gr_tools_hide(const gchar *text, GIOChannel *channel, gpointer data)
588 {
589         gboolean popped;
590         gboolean hidden;
591
592         if (layout_tools_float_get(lw_id, &popped, &hidden) && !hidden)
593                 {
594                 layout_tools_float_set(lw_id, popped, TRUE);
595                 }
596 }
597
598 static gboolean gr_quit_idle_cb(gpointer data)
599 {
600         exit_program();
601
602         return FALSE;
603 }
604
605 static void gr_quit(const gchar *text, GIOChannel *channel, gpointer data)
606 {
607         /* schedule exit when idle, if done from within a
608          * remote handler remote_close will crash
609          */
610         g_idle_add(gr_quit_idle_cb, NULL);
611 }
612
613 static void gr_file_load_no_raise(const gchar *text, GIOChannel *channel, gpointer data)
614 {
615         gchar *filename = expand_tilde(text);
616
617         if (isfile(filename))
618                 {
619                 if (file_extension_match(filename, GQ_COLLECTION_EXT))
620                         {
621                         collection_window_new(filename);
622                         }
623                 else
624                         {
625                         layout_set_path(lw_id, filename);
626                         }
627                 }
628         else if (isdir(filename))
629                 {
630                 layout_set_path(lw_id, filename);
631                 }
632         else
633                 {
634                 log_printf("remote sent filename that does not exist:\"%s\"\n", filename);
635                 layout_set_path(lw_id, homedir());
636                 }
637
638         g_free(filename);
639 }
640
641 static void gr_file_load(const gchar *text, GIOChannel *channel, gpointer data)
642 {
643         gr_file_load_no_raise(text, channel, data);
644
645         gr_raise(text, channel, data);
646 }
647
648 static void gr_pixel_info(const gchar *text, GIOChannel *channel, gpointer data)
649 {
650         gchar *pixel_info;
651         gint x_pixel, y_pixel;
652         gint width, height;
653         gint r_mouse, g_mouse, b_mouse;
654         PixbufRenderer *pr;
655         LayoutWindow *lw = NULL;
656
657         if (!layout_valid(&lw_id)) return;
658
659         pr = (PixbufRenderer*)lw_id->image->pr;
660
661         if (pr)
662                 {
663                 pixbuf_renderer_get_image_size(pr, &width, &height);
664                 if (width < 1 || height < 1) return;
665
666                 pixbuf_renderer_get_mouse_position(pr, &x_pixel, &y_pixel);
667
668                 if (x_pixel >= 0 && y_pixel >= 0)
669                         {
670                         pixbuf_renderer_get_pixel_colors(pr, x_pixel, y_pixel,
671                                                          &r_mouse, &g_mouse, &b_mouse);
672
673                         pixel_info = g_strdup_printf(_("[%d,%d]: RGB(%3d,%3d,%3d)"),
674                                                  x_pixel, y_pixel,
675                                                  r_mouse, g_mouse, b_mouse);
676
677                         g_io_channel_write_chars(channel, pixel_info, -1, NULL, NULL);
678                         g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
679
680                         g_free(pixel_info);
681                         }
682                 else
683                         {
684                         return;
685                         }
686                 }
687         else
688                 {
689                 return;
690                 }
691 }
692
693 static void gr_file_tell(const gchar *text, GIOChannel *channel, gpointer data)
694 {
695         if (!layout_valid(&lw_id)) return;
696
697         if (image_get_path(lw_id->image))
698                 {
699                 g_io_channel_write_chars(channel, image_get_path(lw_id->image), -1, NULL, NULL);
700                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
701                 }
702 }
703
704 static void gr_config_load(const gchar *text, GIOChannel *channel, gpointer data)
705 {
706         gchar *filename = expand_tilde(text);
707
708         if (isfile(filename))
709                 {
710                 load_config_from_file(filename, FALSE);
711                 }
712         else
713                 {
714                 log_printf("remote sent filename that does not exist:\"%s\"\n", filename);
715                 layout_set_path(NULL, homedir());
716                 }
717
718         g_free(filename);
719 }
720
721 static void gr_get_sidecars(const gchar *text, GIOChannel *channel, gpointer data)
722 {
723         gchar *filename = expand_tilde(text);
724         FileData *fd = file_data_new_group(filename);
725
726         GList *work;
727         if (fd->parent) fd = fd->parent;
728
729         g_io_channel_write_chars(channel, fd->path, -1, NULL, NULL);
730         g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
731
732         work = fd->sidecar_files;
733
734         while (work)
735                 {
736                 fd = work->data;
737                 work = work->next;
738                 g_io_channel_write_chars(channel, fd->path, -1, NULL, NULL);
739                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
740                 }
741         g_free(filename);
742 }
743
744 static void gr_get_destination(const gchar *text, GIOChannel *channel, gpointer data)
745 {
746         gchar *filename = expand_tilde(text);
747         FileData *fd = file_data_new_group(filename);
748
749         if (fd->change && fd->change->dest)
750                 {
751                 g_io_channel_write_chars(channel, fd->change->dest, -1, NULL, NULL);
752                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
753                 }
754         g_free(filename);
755 }
756
757 static void gr_file_view(const gchar *text, GIOChannel *channel, gpointer data)
758 {
759         gchar *filename = expand_tilde(text);
760
761         view_window_new(file_data_new_group(filename));
762         g_free(filename);
763 }
764
765 static void gr_list_clear(const gchar *text, GIOChannel *channel, gpointer data)
766 {
767         RemoteData *remote_data = data;
768
769         if (remote_data->command_collection)
770                 {
771                 collection_unref(remote_data->command_collection);
772                 remote_data->command_collection = NULL;
773                 }
774 }
775
776 static void gr_list_add(const gchar *text, GIOChannel *channel, gpointer data)
777 {
778         RemoteData *remote_data = data;
779         gboolean new = TRUE;
780
781         if (!remote_data->command_collection)
782                 {
783                 CollectionData *cd;
784
785                 cd = collection_new("");
786
787                 g_free(cd->path);
788                 cd->path = NULL;
789                 g_free(cd->name);
790                 cd->name = g_strdup(_("Command line"));
791
792                 remote_data->command_collection = cd;
793                 }
794         else
795                 {
796                 new = (!collection_get_first(remote_data->command_collection));
797                 }
798
799         if (collection_add(remote_data->command_collection, file_data_new_group(text), FALSE) && new)
800                 {
801                 layout_image_set_collection(NULL, remote_data->command_collection,
802                                             collection_get_first(remote_data->command_collection));
803                 }
804 }
805
806 static void gr_raise(const gchar *text, GIOChannel *channel, gpointer data)
807 {
808         LayoutWindow *lw = NULL;
809
810         if (layout_valid(&lw_id))
811                 {
812                 gtk_window_present(GTK_WINDOW(lw_id->window));
813                 }
814 }
815
816 #ifdef HAVE_LUA
817 static void gr_lua(const gchar *text, GIOChannel *channel, gpointer data)
818 {
819         gchar *result = NULL;
820         gchar **lua_command;
821
822         lua_command = g_strsplit(text, ",", 2);
823
824         if (lua_command[0] && lua_command[1])
825                 {
826                 FileData *fd = file_data_new_group(lua_command[0]);
827                 result = g_strdup(lua_callvalue(fd, lua_command[1], NULL));
828                 if (result)
829                         {
830                         g_io_channel_write_chars(channel, result, -1, NULL, NULL);
831                         }
832                 else
833                         {
834                         g_io_channel_write_chars(channel, N_("lua error: no data"), -1, NULL, NULL);
835                         }
836                 }
837         else
838                 {
839                 g_io_channel_write_chars(channel, N_("lua error: no data"), -1, NULL, NULL);
840                 }
841
842         g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
843
844         g_strfreev(lua_command);
845         g_free(result);
846 }
847 #endif
848
849 typedef struct _RemoteCommandEntry RemoteCommandEntry;
850 struct _RemoteCommandEntry {
851         gchar *opt_s;
852         gchar *opt_l;
853         void (*func)(const gchar *text, GIOChannel *channel, gpointer data);
854         gboolean needs_extra;
855         gboolean prefer_command_line;
856         gchar *parameter;
857         gchar *description;
858 };
859
860 static RemoteCommandEntry remote_commands[] = {
861         /* short, long                  callback,               extra, prefer, parameter, description */
862         { "-n", "--next",               gr_image_next,          FALSE, FALSE, NULL, N_("next image") },
863         { "-b", "--back",               gr_image_prev,          FALSE, FALSE, NULL, N_("previous image") },
864         { NULL, "--first",              gr_image_first,         FALSE, FALSE, NULL, N_("first image") },
865         { NULL, "--last",               gr_image_last,          FALSE, FALSE, NULL, N_("last image") },
866         { "-f", "--fullscreen",         gr_fullscreen_toggle,   FALSE, TRUE,  NULL, N_("toggle full screen") },
867         { "-fs","--fullscreen-start",   gr_fullscreen_start,    FALSE, FALSE, NULL, N_("start full screen") },
868         { "-fS","--fullscreen-stop",    gr_fullscreen_stop,     FALSE, FALSE, NULL, N_("stop full screen") },
869         { "-s", "--slideshow",          gr_slideshow_toggle,    FALSE, TRUE,  NULL, N_("toggle slide show") },
870         { "-ss","--slideshow-start",    gr_slideshow_start,     FALSE, FALSE, NULL, N_("start slide show") },
871         { "-sS","--slideshow-stop",     gr_slideshow_stop,      FALSE, FALSE, NULL, N_("stop slide show") },
872         { NULL, "--slideshow-recurse:", gr_slideshow_start_rec, TRUE,  FALSE, N_("<FOLDER>"), N_("start recursive slide show in FOLDER") },
873         { "-d", "--delay=",             gr_slideshow_delay,     TRUE,  FALSE, N_("<[H:][M:][N][.M]>"), N_("set slide show delay to Hrs Mins N.M seconds") },
874         { "+t", "--tools-show",         gr_tools_show,          FALSE, TRUE,  NULL, N_("show tools") },
875         { "-t", "--tools-hide",         gr_tools_hide,          FALSE, TRUE,  NULL, N_("hide tools") },
876         { "-q", "--quit",               gr_quit,                FALSE, FALSE, NULL, N_("quit") },
877         { NULL, "--config-load:",       gr_config_load,         TRUE,  FALSE, N_("<FILE>"), N_("load configuration from FILE") },
878         { NULL, "--get-sidecars:",      gr_get_sidecars,        TRUE,  FALSE, N_("<FILE>"), N_("get list of sidecars of FILE") },
879         { NULL, "--get-destination:",   gr_get_destination,     TRUE,  FALSE, N_("<FILE>"), N_("get destination path of FILE") },
880         { NULL, "file:",                gr_file_load,           TRUE,  FALSE, N_("<FILE>"), N_("open FILE, bring Geeqie window to the top") },
881         { NULL, "File:",                gr_file_load_no_raise,  TRUE,  FALSE, N_("<FILE>"), N_("open FILE, do not bring Geeqie window to the top") },
882         { NULL, "--tell",               gr_file_tell,           FALSE, FALSE, NULL, N_("print filename of current image") },
883         { NULL, "--pixel-info",         gr_pixel_info,          FALSE, FALSE, NULL, N_("print pixel info of mouse pointer on current image") },
884         { NULL, "view:",                gr_file_view,           TRUE,  FALSE, N_("<FILE>"), N_("open FILE in new window") },
885         { NULL, "--list-clear",         gr_list_clear,          FALSE, FALSE, NULL, N_("clear command line collection list") },
886         { NULL, "--list-add:",          gr_list_add,            TRUE,  FALSE, N_("<FILE>"), N_("add FILE to command line collection list") },
887         { NULL, "raise",                gr_raise,               FALSE, FALSE, NULL, N_("bring the Geeqie window to the top") },
888         { NULL, "--id:",                gr_lw_id,               TRUE, FALSE, N_("<ID>"), N_("window id for following commands") },
889         { NULL, "--new-window",         gr_new_window,          FALSE, FALSE, NULL, N_("new window") },
890         { NULL, "--close-window",       gr_close_window,        FALSE, FALSE, NULL, N_("close window") },
891         { "-ct:", "--cache-thumbs:",    gr_cache_thumb,         TRUE, FALSE, N_("clear|clean"), N_("clear or clean thumbnail cache") },
892         { "-cs:", "--cache-shared:",    gr_cache_shared,        TRUE, FALSE, N_("clear|clean"), N_("clear or clean shared thumbnail cache") },
893         { "-cm","--cache-metadata",      gr_cache_metadata,               FALSE, FALSE, NULL, N_("    clean the metadata cache") },
894         { "-cr:", "--cache-render:",    gr_cache_render,        TRUE, FALSE, N_("<folder>  "), N_(" render thumbnails") },
895         { "-crr:", "--cache-render-recurse:", gr_cache_render_recurse, TRUE, FALSE, N_("<folder> "), N_("render thumbnails recursively") },
896         { "-crs:", "--cache-render-shared:", gr_cache_render_standard, TRUE, FALSE, N_("<folder> "), N_(" render thumbnails (see Help)") },
897         { "-crsr:", "--cache-render-shared-recurse:", gr_cache_render_standard_recurse, TRUE, FALSE, N_("<folder>"), N_(" render thumbnails recursively (see Help)") },
898 #ifdef HAVE_LUA
899         { NULL, "--lua:",               gr_lua,                 TRUE, FALSE, N_("<FILE>,<lua script>"), N_("run lua script on FILE") },
900 #endif
901         { NULL, NULL, NULL, FALSE, FALSE, NULL, NULL }
902 };
903
904 static RemoteCommandEntry *remote_command_find(const gchar *text, const gchar **offset)
905 {
906         gboolean match = FALSE;
907         gint i;
908
909         i = 0;
910         while (!match && remote_commands[i].func != NULL)
911                 {
912                 if (remote_commands[i].needs_extra)
913                         {
914                         if (remote_commands[i].opt_s &&
915                             strncmp(remote_commands[i].opt_s, text, strlen(remote_commands[i].opt_s)) == 0)
916                                 {
917                                 if (offset) *offset = text + strlen(remote_commands[i].opt_s);
918                                 return &remote_commands[i];
919                                 }
920                         else if (remote_commands[i].opt_l &&
921                                  strncmp(remote_commands[i].opt_l, text, strlen(remote_commands[i].opt_l)) == 0)
922                                 {
923                                 if (offset) *offset = text + strlen(remote_commands[i].opt_l);
924                                 return &remote_commands[i];
925                                 }
926                         }
927                 else
928                         {
929                         if ((remote_commands[i].opt_s && strcmp(remote_commands[i].opt_s, text) == 0) ||
930                             (remote_commands[i].opt_l && strcmp(remote_commands[i].opt_l, text) == 0))
931                                 {
932                                 if (offset) *offset = text;
933                                 return &remote_commands[i];
934                                 }
935                         }
936
937                 i++;
938                 }
939
940         return NULL;
941 }
942
943 static void remote_cb(RemoteConnection *rc, const gchar *text, GIOChannel *channel, gpointer data)
944 {
945         RemoteCommandEntry *entry;
946         const gchar *offset;
947
948         entry = remote_command_find(text, &offset);
949         if (entry && entry->func)
950                 {
951                 entry->func(offset, channel, data);
952                 }
953         else
954                 {
955                 log_printf("unknown remote command:%s\n", text);
956                 }
957 }
958
959 void remote_help(void)
960 {
961         gint i;
962         gchar *s_opt_param;
963         gchar *l_opt_param;
964
965         print_term(FALSE, _("Remote command list:\n"));
966
967         i = 0;
968         while (remote_commands[i].func != NULL)
969                 {
970                 if (remote_commands[i].description)
971                         {
972                         s_opt_param = g_strconcat(remote_commands[i].opt_s, remote_commands[i].parameter, NULL);
973                         l_opt_param = g_strconcat(remote_commands[i].opt_l, remote_commands[i].parameter, NULL);
974                         printf_term(FALSE, "  %-11s%-1s %-30s%-s\n",
975                                     (remote_commands[i].opt_s) ? s_opt_param : "",
976                                     (remote_commands[i].opt_s && remote_commands[i].opt_l) ? "," : " ",
977                                     (remote_commands[i].opt_l) ? l_opt_param : "",
978                                     _(remote_commands[i].description));
979                         g_free(s_opt_param);
980                         g_free(l_opt_param);
981                         }
982                 i++;
983                 }
984         printf_term(FALSE, N_("\n  All other command line parameters are used as plain files if they exists.\n"));
985 }
986
987 GList *remote_build_list(GList *list, gint argc, gchar *argv[], GList **errors)
988 {
989         gint i;
990
991         i = 1;
992         while (i < argc)
993                 {
994                 RemoteCommandEntry *entry;
995
996                 entry = remote_command_find(argv[i], NULL);
997                 if (entry)
998                         {
999                         list = g_list_append(list, argv[i]);
1000                         }
1001                 else if (errors && !isfile(argv[i]))
1002                         {
1003                         *errors = g_list_append(*errors, argv[i]);
1004                         }
1005                 i++;
1006                 }
1007
1008         return list;
1009 }
1010
1011 /**
1012  * \param arg_exec Binary (argv0)
1013  * \param remote_list Evaluated and recognized remote commands
1014  * \param path The current path
1015  * \param cmd_list List of all non collections in Path
1016  * \param collection_list List of all collections in argv
1017  */
1018 void remote_control(const gchar *arg_exec, GList *remote_list, const gchar *path,
1019                     GList *cmd_list, GList *collection_list)
1020 {
1021         RemoteConnection *rc;
1022         gboolean started = FALSE;
1023         gchar *buf;
1024
1025         buf = g_build_filename(get_rc_dir(), ".command", NULL);
1026         rc = remote_client_open(buf);
1027         if (!rc)
1028                 {
1029                 GString *command;
1030                 GList *work;
1031                 gint retry_count = 12;
1032                 gboolean blank = FALSE;
1033
1034                 printf_term(FALSE, _("Remote %s not running, starting..."), GQ_APPNAME);
1035
1036                 command = g_string_new(arg_exec);
1037
1038                 work = remote_list;
1039                 while (work)
1040                         {
1041                         gchar *text;
1042                         RemoteCommandEntry *entry;
1043
1044                         text = work->data;
1045                         work = work->next;
1046
1047                         entry = remote_command_find(text, NULL);
1048                         if (entry)
1049                                 {
1050                                 if (entry->prefer_command_line)
1051                                         {
1052                                         remote_list = g_list_remove(remote_list, text);
1053                                         g_string_append(command, " ");
1054                                         g_string_append(command, text);
1055                                         }
1056                                 if (entry->opt_l && strcmp(entry->opt_l, "file:") == 0)
1057                                         {
1058                                         blank = TRUE;
1059                                         }
1060                                 }
1061                         }
1062
1063                 if (blank || cmd_list || path) g_string_append(command, " --blank");
1064                 if (get_debug_level()) g_string_append(command, " --debug");
1065
1066                 g_string_append(command, " &");
1067                 runcmd(command->str);
1068                 g_string_free(command, TRUE);
1069
1070                 while (!rc && retry_count > 0)
1071                         {
1072                         usleep((retry_count > 10) ? 500000 : 1000000);
1073                         rc = remote_client_open(buf);
1074                         if (!rc) print_term(FALSE, ".");
1075                         retry_count--;
1076                         }
1077
1078                 print_term(FALSE, "\n");
1079
1080                 started = TRUE;
1081                 }
1082         g_free(buf);
1083
1084         if (rc)
1085                 {
1086                 GList *work;
1087                 const gchar *prefix;
1088                 gboolean use_path = TRUE;
1089                 gboolean sent = FALSE;
1090
1091                 work = remote_list;
1092                 while (work)
1093                         {
1094                         gchar *text;
1095                         RemoteCommandEntry *entry;
1096
1097                         text = work->data;
1098                         work = work->next;
1099
1100                         entry = remote_command_find(text, NULL);
1101                         if (entry &&
1102                             entry->opt_l &&
1103                             strcmp(entry->opt_l, "file:") == 0) use_path = FALSE;
1104
1105                         remote_client_send(rc, text);
1106
1107                         sent = TRUE;
1108                         }
1109
1110                 if (cmd_list && cmd_list->next)
1111                         {
1112                         prefix = "--list-add:";
1113                         remote_client_send(rc, "--list-clear");
1114                         }
1115                 else
1116                         {
1117                         prefix = "file:";
1118                         }
1119
1120                 work = cmd_list;
1121                 while (work)
1122                         {
1123                         FileData *fd;
1124                         gchar *text;
1125
1126                         fd = work->data;
1127                         work = work->next;
1128
1129                         text = g_strconcat(prefix, fd->path, NULL);
1130                         remote_client_send(rc, text);
1131                         g_free(text);
1132
1133                         sent = TRUE;
1134                         }
1135
1136                 if (path && !cmd_list && use_path)
1137                         {
1138                         gchar *text;
1139
1140                         text = g_strdup_printf("file:%s", path);
1141                         remote_client_send(rc, text);
1142                         g_free(text);
1143
1144                         sent = TRUE;
1145                         }
1146
1147                 work = collection_list;
1148                 while (work)
1149                         {
1150                         const gchar *name;
1151                         gchar *text;
1152
1153                         name = work->data;
1154                         work = work->next;
1155
1156                         text = g_strdup_printf("file:%s", name);
1157                         remote_client_send(rc, text);
1158                         g_free(text);
1159
1160                         sent = TRUE;
1161                         }
1162
1163                 if (!started && !sent)
1164                         {
1165                         remote_client_send(rc, "raise");
1166                         }
1167                 }
1168         else
1169                 {
1170                 print_term(TRUE, _("Remote not available\n"));
1171                 }
1172
1173         _exit(0);
1174 }
1175
1176 RemoteConnection *remote_server_init(gchar *path, CollectionData *command_collection)
1177 {
1178         RemoteConnection *remote_connection = remote_server_open(path);
1179         RemoteData *remote_data = g_new(RemoteData, 1);
1180
1181         remote_data->command_collection = command_collection;
1182
1183         remote_server_subscribe(remote_connection, remote_cb, remote_data);
1184         return remote_connection;
1185 }
1186 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */