Fix #635: Export in JPG
[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_rectangle(const gchar *text, GIOChannel *channel, gpointer data)
694 {
695         gchar *rectangle_info;
696         PixbufRenderer *pr;
697         LayoutWindow *lw = NULL;
698         gint x1, y1, x2, y2;
699
700         if (!options->draw_rectangle) return;
701         if (!layout_valid(&lw_id)) return;
702
703         pr = (PixbufRenderer*)lw_id->image->pr;
704
705         if (pr)
706                 {
707                 image_get_rectangle(&x1, &y1, &x2, &y2);
708                 rectangle_info = g_strdup_printf(_("%dx%d+%d+%d"),
709                                         (x2 > x1) ? x2 - x1 : x1 - x2,
710                                         (y2 > y1) ? y2 - y1 : y1 - y2,
711                                         (x2 > x1) ? x1 : x2,
712                                         (y2 > y1) ? y1 : y2);
713
714                 g_io_channel_write_chars(channel, rectangle_info, -1, NULL, NULL);
715                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
716
717                 g_free(rectangle_info);
718                 }
719 }
720
721 static void gr_render_intent(const gchar *text, GIOChannel *channel, gpointer data)
722 {
723         gchar *render_intent;
724
725         switch (options->color_profile.render_intent)
726                 {
727                 case 0:
728                         render_intent = g_strdup("Perceptual");
729                         break;
730                 case 1:
731                         render_intent = g_strdup("Relative Colorimetric");
732                         break;
733                 case 2:
734                         render_intent = g_strdup("Saturation");
735                         break;
736                 case 3:
737                         render_intent = g_strdup("Absolute Colorimetric");
738                         break;
739                 default:
740                         render_intent = g_strdup("none");
741                         break;
742                 }
743
744         g_io_channel_write_chars(channel, render_intent, -1, NULL, NULL);
745         g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
746
747         g_free(render_intent);
748 }
749
750 static void gr_file_tell(const gchar *text, GIOChannel *channel, gpointer data)
751 {
752         if (!layout_valid(&lw_id)) return;
753
754         if (image_get_path(lw_id->image))
755                 {
756                 g_io_channel_write_chars(channel, image_get_path(lw_id->image), -1, NULL, NULL);
757                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
758                 }
759 }
760
761 static void gr_config_load(const gchar *text, GIOChannel *channel, gpointer data)
762 {
763         gchar *filename = expand_tilde(text);
764
765         if (isfile(filename))
766                 {
767                 load_config_from_file(filename, FALSE);
768                 }
769         else
770                 {
771                 log_printf("remote sent filename that does not exist:\"%s\"\n", filename);
772                 layout_set_path(NULL, homedir());
773                 }
774
775         g_free(filename);
776 }
777
778 static void gr_get_sidecars(const gchar *text, GIOChannel *channel, gpointer data)
779 {
780         gchar *filename = expand_tilde(text);
781         FileData *fd = file_data_new_group(filename);
782
783         GList *work;
784         if (fd->parent) fd = fd->parent;
785
786         g_io_channel_write_chars(channel, fd->path, -1, NULL, NULL);
787         g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
788
789         work = fd->sidecar_files;
790
791         while (work)
792                 {
793                 fd = work->data;
794                 work = work->next;
795                 g_io_channel_write_chars(channel, fd->path, -1, NULL, NULL);
796                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
797                 }
798         g_free(filename);
799 }
800
801 static void gr_get_destination(const gchar *text, GIOChannel *channel, gpointer data)
802 {
803         gchar *filename = expand_tilde(text);
804         FileData *fd = file_data_new_group(filename);
805
806         if (fd->change && fd->change->dest)
807                 {
808                 g_io_channel_write_chars(channel, fd->change->dest, -1, NULL, NULL);
809                 g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
810                 }
811         g_free(filename);
812 }
813
814 static void gr_file_view(const gchar *text, GIOChannel *channel, gpointer data)
815 {
816         gchar *filename = expand_tilde(text);
817
818         view_window_new(file_data_new_group(filename));
819         g_free(filename);
820 }
821
822 static void gr_list_clear(const gchar *text, GIOChannel *channel, gpointer data)
823 {
824         RemoteData *remote_data = data;
825
826         if (remote_data->command_collection)
827                 {
828                 collection_unref(remote_data->command_collection);
829                 remote_data->command_collection = NULL;
830                 }
831 }
832
833 static void gr_list_add(const gchar *text, GIOChannel *channel, gpointer data)
834 {
835         RemoteData *remote_data = data;
836         gboolean new = TRUE;
837
838         if (!remote_data->command_collection)
839                 {
840                 CollectionData *cd;
841
842                 cd = collection_new("");
843
844                 g_free(cd->path);
845                 cd->path = NULL;
846                 g_free(cd->name);
847                 cd->name = g_strdup(_("Command line"));
848
849                 remote_data->command_collection = cd;
850                 }
851         else
852                 {
853                 new = (!collection_get_first(remote_data->command_collection));
854                 }
855
856         if (collection_add(remote_data->command_collection, file_data_new_group(text), FALSE) && new)
857                 {
858                 layout_image_set_collection(NULL, remote_data->command_collection,
859                                             collection_get_first(remote_data->command_collection));
860                 }
861 }
862
863 static void gr_raise(const gchar *text, GIOChannel *channel, gpointer data)
864 {
865         LayoutWindow *lw = NULL;
866
867         if (layout_valid(&lw_id))
868                 {
869                 gtk_window_present(GTK_WINDOW(lw_id->window));
870                 }
871 }
872
873 #ifdef HAVE_LUA
874 static void gr_lua(const gchar *text, GIOChannel *channel, gpointer data)
875 {
876         gchar *result = NULL;
877         gchar **lua_command;
878
879         lua_command = g_strsplit(text, ",", 2);
880
881         if (lua_command[0] && lua_command[1])
882                 {
883                 FileData *fd = file_data_new_group(lua_command[0]);
884                 result = g_strdup(lua_callvalue(fd, lua_command[1], NULL));
885                 if (result)
886                         {
887                         g_io_channel_write_chars(channel, result, -1, NULL, NULL);
888                         }
889                 else
890                         {
891                         g_io_channel_write_chars(channel, N_("lua error: no data"), -1, NULL, NULL);
892                         }
893                 }
894         else
895                 {
896                 g_io_channel_write_chars(channel, N_("lua error: no data"), -1, NULL, NULL);
897                 }
898
899         g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
900
901         g_strfreev(lua_command);
902         g_free(result);
903 }
904 #endif
905
906 typedef struct _RemoteCommandEntry RemoteCommandEntry;
907 struct _RemoteCommandEntry {
908         gchar *opt_s;
909         gchar *opt_l;
910         void (*func)(const gchar *text, GIOChannel *channel, gpointer data);
911         gboolean needs_extra;
912         gboolean prefer_command_line;
913         gchar *parameter;
914         gchar *description;
915 };
916
917 static RemoteCommandEntry remote_commands[] = {
918         /* short, long                  callback,               extra, prefer, parameter, description */
919         { "-n", "--next",               gr_image_next,          FALSE, FALSE, NULL, N_("next image") },
920         { "-b", "--back",               gr_image_prev,          FALSE, FALSE, NULL, N_("previous image") },
921         { NULL, "--first",              gr_image_first,         FALSE, FALSE, NULL, N_("first image") },
922         { NULL, "--last",               gr_image_last,          FALSE, FALSE, NULL, N_("last image") },
923         { "-f", "--fullscreen",         gr_fullscreen_toggle,   FALSE, TRUE,  NULL, N_("toggle full screen") },
924         { "-fs","--fullscreen-start",   gr_fullscreen_start,    FALSE, FALSE, NULL, N_("start full screen") },
925         { "-fS","--fullscreen-stop",    gr_fullscreen_stop,     FALSE, FALSE, NULL, N_("stop full screen") },
926         { "-s", "--slideshow",          gr_slideshow_toggle,    FALSE, TRUE,  NULL, N_("toggle slide show") },
927         { "-ss","--slideshow-start",    gr_slideshow_start,     FALSE, FALSE, NULL, N_("start slide show") },
928         { "-sS","--slideshow-stop",     gr_slideshow_stop,      FALSE, FALSE, NULL, N_("stop slide show") },
929         { NULL, "--slideshow-recurse:", gr_slideshow_start_rec, TRUE,  FALSE, N_("<FOLDER>"), N_("start recursive slide show in FOLDER") },
930         { "-d", "--delay=",             gr_slideshow_delay,     TRUE,  FALSE, N_("<[H:][M:][N][.M]>"), N_("set slide show delay to Hrs Mins N.M seconds") },
931         { "+t", "--tools-show",         gr_tools_show,          FALSE, TRUE,  NULL, N_("show tools") },
932         { "-t", "--tools-hide",         gr_tools_hide,          FALSE, TRUE,  NULL, N_("hide tools") },
933         { "-q", "--quit",               gr_quit,                FALSE, FALSE, NULL, N_("quit") },
934         { NULL, "--config-load:",       gr_config_load,         TRUE,  FALSE, N_("<FILE>"), N_("load configuration from FILE") },
935         { NULL, "--get-sidecars:",      gr_get_sidecars,        TRUE,  FALSE, N_("<FILE>"), N_("get list of sidecars of FILE") },
936         { NULL, "--get-destination:",   gr_get_destination,     TRUE,  FALSE, N_("<FILE>"), N_("get destination path of FILE") },
937         { NULL, "file:",                gr_file_load,           TRUE,  FALSE, N_("<FILE>"), N_("open FILE, bring Geeqie window to the top") },
938         { NULL, "File:",                gr_file_load_no_raise,  TRUE,  FALSE, N_("<FILE>"), N_("open FILE, do not bring Geeqie window to the top") },
939         { NULL, "--tell",               gr_file_tell,           FALSE, FALSE, NULL, N_("print filename of current image") },
940         { NULL, "--pixel-info",         gr_pixel_info,          FALSE, FALSE, NULL, N_("print pixel info of mouse pointer on current image") },
941         { NULL, "--get-rectangle",      gr_rectangle,           FALSE, FALSE, NULL, N_("get rectangle co-ordinates") },
942         { NULL, "--get-render-intent",  gr_render_intent,       FALSE, FALSE, NULL, N_("get render intent") },
943         { NULL, "view:",                gr_file_view,           TRUE,  FALSE, N_("<FILE>"), N_("open FILE in new window") },
944         { NULL, "--list-clear",         gr_list_clear,          FALSE, FALSE, NULL, N_("clear command line collection list") },
945         { NULL, "--list-add:",          gr_list_add,            TRUE,  FALSE, N_("<FILE>"), N_("add FILE to command line collection list") },
946         { NULL, "raise",                gr_raise,               FALSE, FALSE, NULL, N_("bring the Geeqie window to the top") },
947         { NULL, "--id:",                gr_lw_id,               TRUE, FALSE, N_("<ID>"), N_("window id for following commands") },
948         { NULL, "--new-window",         gr_new_window,          FALSE, FALSE, NULL, N_("new window") },
949         { NULL, "--close-window",       gr_close_window,        FALSE, FALSE, NULL, N_("close window") },
950         { "-ct:", "--cache-thumbs:",    gr_cache_thumb,         TRUE, FALSE, N_("clear|clean"), N_("clear or clean thumbnail cache") },
951         { "-cs:", "--cache-shared:",    gr_cache_shared,        TRUE, FALSE, N_("clear|clean"), N_("clear or clean shared thumbnail cache") },
952         { "-cm","--cache-metadata",      gr_cache_metadata,               FALSE, FALSE, NULL, N_("    clean the metadata cache") },
953         { "-cr:", "--cache-render:",    gr_cache_render,        TRUE, FALSE, N_("<folder>  "), N_(" render thumbnails") },
954         { "-crr:", "--cache-render-recurse:", gr_cache_render_recurse, TRUE, FALSE, N_("<folder> "), N_("render thumbnails recursively") },
955         { "-crs:", "--cache-render-shared:", gr_cache_render_standard, TRUE, FALSE, N_("<folder> "), N_(" render thumbnails (see Help)") },
956         { "-crsr:", "--cache-render-shared-recurse:", gr_cache_render_standard_recurse, TRUE, FALSE, N_("<folder>"), N_(" render thumbnails recursively (see Help)") },
957 #ifdef HAVE_LUA
958         { NULL, "--lua:",               gr_lua,                 TRUE, FALSE, N_("<FILE>,<lua script>"), N_("run lua script on FILE") },
959 #endif
960         { NULL, NULL, NULL, FALSE, FALSE, NULL, NULL }
961 };
962
963 static RemoteCommandEntry *remote_command_find(const gchar *text, const gchar **offset)
964 {
965         gboolean match = FALSE;
966         gint i;
967
968         i = 0;
969         while (!match && remote_commands[i].func != NULL)
970                 {
971                 if (remote_commands[i].needs_extra)
972                         {
973                         if (remote_commands[i].opt_s &&
974                             strncmp(remote_commands[i].opt_s, text, strlen(remote_commands[i].opt_s)) == 0)
975                                 {
976                                 if (offset) *offset = text + strlen(remote_commands[i].opt_s);
977                                 return &remote_commands[i];
978                                 }
979                         else if (remote_commands[i].opt_l &&
980                                  strncmp(remote_commands[i].opt_l, text, strlen(remote_commands[i].opt_l)) == 0)
981                                 {
982                                 if (offset) *offset = text + strlen(remote_commands[i].opt_l);
983                                 return &remote_commands[i];
984                                 }
985                         }
986                 else
987                         {
988                         if ((remote_commands[i].opt_s && strcmp(remote_commands[i].opt_s, text) == 0) ||
989                             (remote_commands[i].opt_l && strcmp(remote_commands[i].opt_l, text) == 0))
990                                 {
991                                 if (offset) *offset = text;
992                                 return &remote_commands[i];
993                                 }
994                         }
995
996                 i++;
997                 }
998
999         return NULL;
1000 }
1001
1002 static void remote_cb(RemoteConnection *rc, const gchar *text, GIOChannel *channel, gpointer data)
1003 {
1004         RemoteCommandEntry *entry;
1005         const gchar *offset;
1006
1007         entry = remote_command_find(text, &offset);
1008         if (entry && entry->func)
1009                 {
1010                 entry->func(offset, channel, data);
1011                 }
1012         else
1013                 {
1014                 log_printf("unknown remote command:%s\n", text);
1015                 }
1016 }
1017
1018 void remote_help(void)
1019 {
1020         gint i;
1021         gchar *s_opt_param;
1022         gchar *l_opt_param;
1023
1024         print_term(FALSE, _("Remote command list:\n"));
1025
1026         i = 0;
1027         while (remote_commands[i].func != NULL)
1028                 {
1029                 if (remote_commands[i].description)
1030                         {
1031                         s_opt_param = g_strconcat(remote_commands[i].opt_s, remote_commands[i].parameter, NULL);
1032                         l_opt_param = g_strconcat(remote_commands[i].opt_l, remote_commands[i].parameter, NULL);
1033                         printf_term(FALSE, "  %-11s%-1s %-30s%-s\n",
1034                                     (remote_commands[i].opt_s) ? s_opt_param : "",
1035                                     (remote_commands[i].opt_s && remote_commands[i].opt_l) ? "," : " ",
1036                                     (remote_commands[i].opt_l) ? l_opt_param : "",
1037                                     _(remote_commands[i].description));
1038                         g_free(s_opt_param);
1039                         g_free(l_opt_param);
1040                         }
1041                 i++;
1042                 }
1043         printf_term(FALSE, N_("\n  All other command line parameters are used as plain files if they exists.\n"));
1044 }
1045
1046 GList *remote_build_list(GList *list, gint argc, gchar *argv[], GList **errors)
1047 {
1048         gint i;
1049
1050         i = 1;
1051         while (i < argc)
1052                 {
1053                 RemoteCommandEntry *entry;
1054
1055                 entry = remote_command_find(argv[i], NULL);
1056                 if (entry)
1057                         {
1058                         list = g_list_append(list, argv[i]);
1059                         }
1060                 else if (errors && !isfile(argv[i]))
1061                         {
1062                         *errors = g_list_append(*errors, argv[i]);
1063                         }
1064                 i++;
1065                 }
1066
1067         return list;
1068 }
1069
1070 /**
1071  * \param arg_exec Binary (argv0)
1072  * \param remote_list Evaluated and recognized remote commands
1073  * \param path The current path
1074  * \param cmd_list List of all non collections in Path
1075  * \param collection_list List of all collections in argv
1076  */
1077 void remote_control(const gchar *arg_exec, GList *remote_list, const gchar *path,
1078                     GList *cmd_list, GList *collection_list)
1079 {
1080         RemoteConnection *rc;
1081         gboolean started = FALSE;
1082         gchar *buf;
1083
1084         buf = g_build_filename(get_rc_dir(), ".command", NULL);
1085         rc = remote_client_open(buf);
1086         if (!rc)
1087                 {
1088                 GString *command;
1089                 GList *work;
1090                 gint retry_count = 12;
1091                 gboolean blank = FALSE;
1092
1093                 printf_term(FALSE, _("Remote %s not running, starting..."), GQ_APPNAME);
1094
1095                 command = g_string_new(arg_exec);
1096
1097                 work = remote_list;
1098                 while (work)
1099                         {
1100                         gchar *text;
1101                         RemoteCommandEntry *entry;
1102
1103                         text = work->data;
1104                         work = work->next;
1105
1106                         entry = remote_command_find(text, NULL);
1107                         if (entry)
1108                                 {
1109                                 if (entry->prefer_command_line)
1110                                         {
1111                                         remote_list = g_list_remove(remote_list, text);
1112                                         g_string_append(command, " ");
1113                                         g_string_append(command, text);
1114                                         }
1115                                 if (entry->opt_l && strcmp(entry->opt_l, "file:") == 0)
1116                                         {
1117                                         blank = TRUE;
1118                                         }
1119                                 }
1120                         }
1121
1122                 if (blank || cmd_list || path) g_string_append(command, " --blank");
1123                 if (get_debug_level()) g_string_append(command, " --debug");
1124
1125                 g_string_append(command, " &");
1126                 runcmd(command->str);
1127                 g_string_free(command, TRUE);
1128
1129                 while (!rc && retry_count > 0)
1130                         {
1131                         usleep((retry_count > 10) ? 500000 : 1000000);
1132                         rc = remote_client_open(buf);
1133                         if (!rc) print_term(FALSE, ".");
1134                         retry_count--;
1135                         }
1136
1137                 print_term(FALSE, "\n");
1138
1139                 started = TRUE;
1140                 }
1141         g_free(buf);
1142
1143         if (rc)
1144                 {
1145                 GList *work;
1146                 const gchar *prefix;
1147                 gboolean use_path = TRUE;
1148                 gboolean sent = FALSE;
1149
1150                 work = remote_list;
1151                 while (work)
1152                         {
1153                         gchar *text;
1154                         RemoteCommandEntry *entry;
1155
1156                         text = work->data;
1157                         work = work->next;
1158
1159                         entry = remote_command_find(text, NULL);
1160                         if (entry &&
1161                             entry->opt_l &&
1162                             strcmp(entry->opt_l, "file:") == 0) use_path = FALSE;
1163
1164                         remote_client_send(rc, text);
1165
1166                         sent = TRUE;
1167                         }
1168
1169                 if (cmd_list && cmd_list->next)
1170                         {
1171                         prefix = "--list-add:";
1172                         remote_client_send(rc, "--list-clear");
1173                         }
1174                 else
1175                         {
1176                         prefix = "file:";
1177                         }
1178
1179                 work = cmd_list;
1180                 while (work)
1181                         {
1182                         FileData *fd;
1183                         gchar *text;
1184
1185                         fd = work->data;
1186                         work = work->next;
1187
1188                         text = g_strconcat(prefix, fd->path, NULL);
1189                         remote_client_send(rc, text);
1190                         g_free(text);
1191
1192                         sent = TRUE;
1193                         }
1194
1195                 if (path && !cmd_list && use_path)
1196                         {
1197                         gchar *text;
1198
1199                         text = g_strdup_printf("file:%s", path);
1200                         remote_client_send(rc, text);
1201                         g_free(text);
1202
1203                         sent = TRUE;
1204                         }
1205
1206                 work = collection_list;
1207                 while (work)
1208                         {
1209                         const gchar *name;
1210                         gchar *text;
1211
1212                         name = work->data;
1213                         work = work->next;
1214
1215                         text = g_strdup_printf("file:%s", name);
1216                         remote_client_send(rc, text);
1217                         g_free(text);
1218
1219                         sent = TRUE;
1220                         }
1221
1222                 if (!started && !sent)
1223                         {
1224                         remote_client_send(rc, "raise");
1225                         }
1226                 }
1227         else
1228                 {
1229                 print_term(TRUE, _("Remote not available\n"));
1230                 }
1231
1232         _exit(0);
1233 }
1234
1235 RemoteConnection *remote_server_init(gchar *path, CollectionData *command_collection)
1236 {
1237         RemoteConnection *remote_connection = remote_server_open(path);
1238         RemoteData *remote_data = g_new(RemoteData, 1);
1239
1240         remote_data->command_collection = command_collection;
1241
1242         remote_server_subscribe(remote_connection, remote_cb, remote_data);
1243         return remote_connection;
1244 }
1245 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */