1 /*
2   Module       : main.c
3   Purpose      : GDK/Imlib Quick Image Viewer (qiv)
4   More         : see qiv README
5   Homepage     : http://qiv.spiegl.de/
6   Original     : http://www.klografx.net/qiv/
7   Policy       : GNU GPL
8 */
9 
10 #include <gdk/gdkx.h>
11 #include <stdio.h>
12 #include <signal.h>
13 #include <sys/time.h>
14 #include <ctype.h>
15 #include <string.h>
16 
17 #ifdef HAVE_MAGIC
18 #include <magic.h>
19 #endif
20 
21 #include "qiv.h"
22 #include "main.h"
23 
24 qiv_image main_img;
25 qiv_mgl   magnify_img; /* [lc] */
26 
27 static int check_extension(const char *);
28 static void qiv_signal_usr1();
29 static void qiv_signal_usr2();
30 static gboolean qiv_handle_timer(gpointer);
31 static void qiv_timer_restart(gpointer);
32 
33 #ifdef HAVE_MAGIC
34 static int check_magic(magic_t cookie, const char *name);
35 #endif
36 
main(int argc,char ** argv)37 int main(int argc, char **argv)
38 {
39   struct timeval tv;
40   int i;
41 
42   // [as] workaround for problem with X composite extension
43   // is this still needed with imlib2 ??
44   putenv("XLIB_SKIP_ARGB_VISUALS=1");
45 
46 /*
47   // [as] thinks that this is not portable enough
48   // [lc]
49   // I use a virtual screen of 1600x1200, and the resolution is 1024x768,
50   // so I changed how screen_[x,y] is obtained; it seems that gtk 1.2
51   // cannot give the geometry of viewport, so I borrowed from the source of
52   // xvidtune the code for calling XF86VidModeGetModeLine, this requires
53   // the linking option -lXxf86vm.
54   XF86VidModeModeLine modeline;
55   int dot_clock;
56 */
57 
58   /* Randomize seed for 'true' random */
59   gettimeofday(&tv,NULL);
60   srand(tv.tv_usec*1000000+tv.tv_sec);
61 
62   /* Initialize GDK */
63 
64   gdk_init(&argc,&argv);
65 
66   /* Set up our options, image list, etc */
67   strncpy(select_dir, SELECT_DIR, sizeof select_dir);
68   reset_mod(&main_img);
69 
70   options_read(argc, argv, &main_img);
71 
72 #ifdef SUPPORT_LCMS
73   /* read profiles if provided */
74   if (cms_transform)
75   {
76     if (source_profile) {
77       h_source_profile = cmsOpenProfileFromFile(source_profile, "r");
78     } else {
79       h_source_profile = cmsCreate_sRGBProfile();
80     }
81 
82     if (h_source_profile == NULL)
83     {
84       g_print("qiv: cannot create source color profile.\n");
85       usage(argv[0],1);
86     }
87 
88     if (display_profile) {
89       h_display_profile = cmsOpenProfileFromFile(display_profile, "r");
90     } else {
91       h_display_profile = cmsCreate_sRGBProfile();
92     }
93     if (h_display_profile == NULL)
94     {
95       g_print("qiv: cannot create display color profile.\n");
96       usage(argv[0],1);
97     }
98 
99   }
100   /* TYPE_BGRA_8 or TYPE_ARGB_8 depending on endianess */
101   h_cms_transform = cmsCreateTransform(h_source_profile,
102 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
103 				       TYPE_BGRA_8,
104 				       h_display_profile,
105 				       TYPE_BGRA_8,
106 #else
107 				       TYPE_ARGB_8,
108 				       h_display_profile,
109 				       TYPE_ARGB_8,
110 #endif
111 				       INTENT_PERCEPTUAL, 0);
112 #endif
113 
114   /* Load things from GDK/Imlib */
115 
116   qiv_main_loop = g_main_new(TRUE);
117   cmap = gdk_colormap_get_system();
118   screen_x = gdk_screen_width();
119   screen_y = gdk_screen_height();
120 
121   screen  = gdk_screen_get_default();
122   num_monitors = gdk_screen_get_n_monitors(screen);
123   monitor = malloc( num_monitors * sizeof(GdkRectangle));
124   for(i=0; i< num_monitors ; i++)
125   {
126       gdk_screen_get_monitor_geometry(screen, i, &monitor[i]);
127   }
128 
129   if(user_screen < num_monitors)
130   {
131     main_img.mon_id = user_screen;
132   }
133 
134  /* statusbar with pango */
135   layout = pango_layout_new(gdk_pango_context_get());
136   fontdesc = pango_font_description_from_string (STATUSBAR_FONT);
137 
138   /* set fontsize to 8 if no fontsize is given */
139   if(!pango_font_description_get_size(fontdesc))
140   {
141     pango_font_description_set_size(fontdesc,  PANGO_SCALE * STATUSBAR_FS);
142   }
143   metrics = pango_context_get_metrics (gdk_pango_context_get(), fontdesc, NULL);
144   pango_layout_set_font_description (layout, fontdesc);
145 
146  /* jpeg comment with pango */
147   layoutComment = pango_layout_new(gdk_pango_context_get());
148   fontdescComment = pango_font_description_from_string (COMMENT_FONT);
149 
150   /* set fontsize to 8 if no fontsize is given */
151   if(!pango_font_description_get_size(fontdescComment))
152   {
153     pango_font_description_set_size(fontdescComment,  PANGO_SCALE * COMMENT_FS);
154   }
155   metricsComment = pango_context_get_metrics (gdk_pango_context_get(), fontdescComment, NULL);
156   pango_layout_set_font_description (layoutComment, fontdescComment);
157 
158   max_rand_num = images;
159 
160   if (!images) { /* No images to display */
161     g_print("qiv: cannot load any images.\n");
162     usage(argv[0],1);
163   }
164 
165   /* get colors */
166 
167   color_alloc(STATUSBAR_BG, &text_bg);
168   color_alloc(ERROR_BG, &error_bg);
169   color_alloc(COMMENT_BG, &comment_bg);
170   color_alloc(image_bg_spec, &image_bg);
171 
172   /* Display first image first, except in random mode */
173 
174   if (random_order)
175     next_image(0);
176 
177   //disabled because 'params' is never used, see above
178   //if (to_root || to_root_t || to_root_s) {
179   //  params.flags |= PARAMS_VISUALID;
180   //  (GdkVisual*)params.visualid = gdk_window_get_visual(GDK_ROOT_PARENT());
181   //}
182 
183   /* Setup callbacks */
184 
185   gdk_event_handler_set(qiv_handle_event, &main_img, NULL);
186   qiv_timer_restart(NULL);
187 
188   /* And signal catchers */
189 
190   signal(SIGTERM, finish);
191   signal(SIGINT, finish);
192   signal(SIGUSR1, qiv_signal_usr1);
193   signal(SIGUSR2, qiv_signal_usr2);
194 
195   /* check for DPMS capability and disable it if slideshow
196      was started from command options */
197   dpms_check();
198   if(slide){
199     dpms_disable();
200   }
201 
202 
203   /* Load & display the first image */
204 
205   qiv_load_image(&main_img);
206 
207   if(watch_file){
208     g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 100, qiv_watch_file, &main_img, NULL);
209   }
210 
211   g_main_run(qiv_main_loop); /* will never return */
212   return 0;
213 }
214 
215 
qiv_exit(int code)216 void qiv_exit(int code)
217 {
218   if (cmap) gdk_colormap_unref(cmap);
219   destroy_image(&main_img);
220   dpms_enable();
221 
222   pango_font_description_free (fontdesc);
223   g_object_unref (layout);
224   pango_font_metrics_unref(metrics);
225 
226   g_main_destroy(qiv_main_loop);
227   finish(SIGTERM);        /* deprecated, subject to change */
228 }
229 
230 
231 /*
232  * functions for handling signals
233  */
234 
qiv_signal_usr1()235 static void qiv_signal_usr1()
236 {
237   next_image(1);
238   qiv_load_image(&main_img);
239 }
240 
qiv_signal_usr2()241 static void qiv_signal_usr2()
242 {
243   next_image(-1);
244   qiv_load_image(&main_img);
245 }
246 
247 
248 /*
249  * Slideshow timer function
250  *
251  * If this function returns false, the timer is destroyed
252  * and qiv_timer_restart() is automatically called, which
253  * then starts the timer again. Thus images which takes some
254  * time to load will still be displayed for "delay" seconds.
255  */
256 
qiv_handle_timer(gpointer data)257 static gboolean qiv_handle_timer(gpointer data)
258 {
259   if (*(int *)data || slide) {
260     next_image(0);
261     /* disable screensaver during slideshow */
262     XResetScreenSaver(GDK_DISPLAY());
263     qiv_load_image(&main_img);
264   }
265   return FALSE;
266 }
267 
268 
269 /*
270  *    Slideshow timer (re)start function
271  */
272 
qiv_timer_restart(gpointer dummy)273 static void qiv_timer_restart(gpointer dummy)
274 {
275   g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, delay,
276                      qiv_handle_timer, &slide,
277                      qiv_timer_restart);
278 }
279 
280 /* Filter images by extension */
281 
filter_images(int * images,char ** image_names)282 void filter_images(int *images, char **image_names)
283 {
284   int i = 0;
285 #ifdef HAVE_MAGIC
286   magic_t cookie;
287 
288   cookie = magic_open(MAGIC_SYMLINK);
289   magic_load(cookie,NULL);
290 #endif
291 
292   while(i < *images) {
293     if (check_extension(image_names[i])
294 #ifdef HAVE_MAGIC
295             || check_magic(cookie, image_names[i])
296 #endif
297        ) {
298       i++;
299     } else {
300       int j = i;
301       if (j < *images-1)
302           image_idx--;
303       while(j < *images-1) {
304         image_names[j] = image_names[j+1];
305         ++j;
306       }
307       --(*images);
308     }
309   }
310 #ifdef HAVE_MAGIC
311   magic_close(cookie);
312 #endif
313   if (image_idx < 0)
314     image_idx = 0;
315 }
316 
check_extension(const char * name)317 static int check_extension(const char *name)
318 {
319   char *extn = strrchr(name, '.');
320   int i;
321 
322   if (extn)
323     for (i=0; image_extensions[i]; i++)
324       if (strcasecmp(extn, image_extensions[i]) == 0)
325         return 1;
326 
327   return 0;
328 }
329 
330 #ifdef HAVE_MAGIC
check_magic(magic_t cookie,const char * name)331 static int check_magic(magic_t cookie, const char *name)
332 {
333   const char *description=NULL;
334   int i;
335   int ret=0;
336 
337   description = magic_file(cookie, name);
338   if(description)
339   {
340     for(i=0; image_magic[i]; i++ )
341       if (strncasecmp(description, image_magic[i], strlen(image_magic[i])) == 0)
342       {
343         ret = 1;
344         break;
345       }
346   }
347   return ret;
348 }
349 #endif
350