1 /*
2  * Copyright (C) 2000-2019 the xine project
3  *
4  * This file is part of xine, a unix video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  */
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <sys/stat.h>
26 #include <errno.h>
27 
28 #include <X11/keysym.h>
29 
30 #include "common.h"
31 #include "skins_internal.h"
32 
33 
34 #define NORMAL_CURS   0
35 #define WAIT_CURS     1
36 
37 #define MAX_DISP_ENTRIES  5
38 
39 #define WINDOW_WIDTH      630
40 #define WINDOW_HEIGHT     446
41 
42 #define PREVIEW_WIDTH     (WINDOW_WIDTH - 30)
43 #define PREVIEW_HEIGHT    220
44 #define PREVIEW_RATIO     ((float)PREVIEW_WIDTH / (float)PREVIEW_HEIGHT)
45 
46 typedef struct {
47   char      *name;
48   struct {
49     char    *name;
50     char    *email;
51   } author;
52   struct {
53     char    *href;
54     char    *preview;
55     int      version;
56     int      maintained;
57   } skin;
58 } slx_entry_t;
59 
60 static struct {
61   xitk_window_t        *xwin;
62 
63   xitk_widget_t        *browser;
64 
65   xitk_image_t         *preview_image;
66 
67   xitk_widget_list_t   *widget_list;
68   char                **entries;
69   int                   num;
70 
71   slx_entry_t         **slxs;
72 
73   xitk_register_key_t   widget_key;
74 
75   int                   running;
76 } skdloader;
77 
skins_get_slx_entries(char * url)78 static slx_entry_t **skins_get_slx_entries(char *url) {
79   int              result;
80   slx_entry_t    **slxs = NULL, slx;
81   xml_node_t      *xml_tree, *skin_entry, *skin_ref;
82   xml_property_t  *skin_prop;
83   download_t       download;
84 
85   download.buf    = NULL;
86   download.error  = NULL;
87   download.size   = 0;
88   download.status = 0;
89 
90   if((network_download(url, &download))) {
91     int entries_slx = 0;
92 
93     xml_parser_init_R(xml_parser_t *xml, download.buf, download.size, XML_PARSER_CASE_INSENSITIVE);
94     if((result = xml_parser_build_tree_R(xml, &xml_tree)) != XML_PARSER_OK)
95       goto __failure;
96 
97     if(!strcasecmp(xml_tree->name, "SLX")) {
98 
99       skin_prop = xml_tree->props;
100 
101       while((skin_prop) && (strcasecmp(skin_prop->name, "VERSION")))
102 	skin_prop = skin_prop->next;
103 
104       if(skin_prop) {
105 	int  version_major, version_minor = 0;
106 
107 	if((((sscanf(skin_prop->value, "%d.%d", &version_major, &version_minor)) == 2) ||
108 	    ((sscanf(skin_prop->value, "%d", &version_major)) == 1)) &&
109 	   ((version_major >= 2) && (version_minor >= 0))) {
110 
111 	  skin_entry = xml_tree->child;
112 
113 	  slx.name = slx.author.name = slx.author.email = slx.skin.href = slx.skin.preview = NULL;
114 	  slx.skin.version = slx.skin.maintained = 0;
115 
116 	  while(skin_entry) {
117 
118 	    if(!strcasecmp(skin_entry->name, "SKIN")) {
119 	      skin_ref = skin_entry->child;
120 
121 	      while(skin_ref) {
122 
123 		if(!strcasecmp(skin_ref->name, "NAME")) {
124 		  slx.name = strdup(skin_ref->data);
125 		}
126 		else if(!strcasecmp(skin_ref->name, "AUTHOR")) {
127 		  for(skin_prop = skin_ref->props; skin_prop; skin_prop = skin_prop->next) {
128 		    if(!strcasecmp(skin_prop->name, "NAME")) {
129 		      slx.author.name = strdup(skin_prop->value);
130 		    }
131 		    else if(!strcasecmp(skin_prop->name, "EMAIL")) {
132 		      slx.author.email = strdup(skin_prop->value);
133 		    }
134 		  }
135 		}
136 		else if(!strcasecmp(skin_ref->name, "REF")) {
137 		  for(skin_prop = skin_ref->props; skin_prop; skin_prop = skin_prop->next) {
138 		    if(!strcasecmp(skin_prop->name, "HREF")) {
139 		      slx.skin.href = strdup(skin_prop->value);
140 		    }
141 		    else if(!strcasecmp(skin_prop->name, "VERSION")) {
142 		      slx.skin.version = atoi(skin_prop->value);
143 		    }
144 		    else if(!strcasecmp(skin_prop->name, "MAINTAINED")) {
145 		      slx.skin.maintained = get_bool_value(skin_prop->value);
146 		    }
147 		  }
148 		}
149 		else if(!strcasecmp(skin_ref->name, "PREVIEW")) {
150 		  for(skin_prop = skin_ref->props; skin_prop; skin_prop = skin_prop->next) {
151 		    if(!strcasecmp(skin_prop->name, "HREF")) {
152 		      slx.skin.preview = strdup(skin_prop->value);
153 		    }
154 		  }
155 		}
156 
157 		skin_ref = skin_ref->next;
158 	      }
159 
160 	      if(slx.name && slx.skin.href &&
161 		 ((slx.skin.version >= SKIN_IFACE_VERSION) && slx.skin.maintained)) {
162 
163 #if 0
164 		printf("get slx: Skin number %d:\n", entries_slx);
165 		printf("  Name: %s\n", slx.name);
166 		printf("  Author Name: %s\n", slx.author.name);
167 		printf("  Author email: %s\n", slx.author.email);
168 		printf("  Href: %s\n", slx.skin.href);
169 		printf("  Preview: %s\n", slx.skin.preview);
170 		printf("  Version: %d\n", slx.skin.version);
171 		printf("  Maintained: %d\n", slx.skin.maintained);
172 		printf("--\n");
173 #endif
174 
175 		slxs = (slx_entry_t **) realloc(slxs, sizeof(slx_entry_t *) * (entries_slx + 2));
176 
177 		slxs[entries_slx] = (slx_entry_t *) calloc(1, sizeof(slx_entry_t));
178 		slxs[entries_slx]->name            = strdup(slx.name);
179 		slxs[entries_slx]->author.name     = slx.author.name ? strdup(slx.author.name) : NULL;
180 		slxs[entries_slx]->author.email    = slx.author.email ? strdup(slx.author.email) : NULL;
181 		slxs[entries_slx]->skin.href       = strdup(slx.skin.href);
182 		slxs[entries_slx]->skin.preview    = slx.skin.preview ? strdup(slx.skin.preview) : NULL;
183 		slxs[entries_slx]->skin.version    = slx.skin.version;
184 		slxs[entries_slx]->skin.maintained = slx.skin.maintained;
185 
186 		entries_slx++;
187 
188 		slxs[entries_slx] = NULL;
189 
190 	      }
191 
192 	      SAFE_FREE(slx.name);
193 	      SAFE_FREE(slx.author.name);
194 	      SAFE_FREE(slx.author.email);
195 	      SAFE_FREE(slx.skin.href);
196 	      SAFE_FREE(slx.skin.preview);
197 	      slx.skin.version = 0;
198 	      slx.skin.maintained = 0;
199 	    }
200 
201 	    skin_entry = skin_entry->next;
202 	  }
203 	}
204       }
205     }
206 
207     xml_parser_free_tree(xml_tree);
208     xml_parser_finalize_R(xml);
209 
210     if(entries_slx)
211       slxs[entries_slx] = NULL;
212 
213   }
214   else
215     xine_error(_("Unable to retrieve skin list from %s: %s"), url, download.error);
216 
217  __failure:
218 
219   free(download.buf);
220   free(download.error);
221 
222   return slxs;
223 }
224 
225 /*
226  * Remote skin loader
227  */
download_set_cursor_state(int state)228 static void download_set_cursor_state(int state) {
229   if(state == WAIT_CURS)
230     xitk_cursors_define_window_cursor(gGui->display, (xitk_window_get_window(skdloader.xwin)), xitk_cursor_watch);
231   else
232     xitk_cursors_restore_window_cursor(gGui->display, (xitk_window_get_window(skdloader.xwin)));
233 }
234 
download_skin_exit(xitk_widget_t * w,void * data)235 static void download_skin_exit(xitk_widget_t *w, void *data) {
236   int i;
237 
238   if(skdloader.running) {
239     xitk_unregister_event_handler(&skdloader.widget_key);
240 
241     if(skdloader.preview_image)
242       xitk_image_free_image(gGui->imlib_data, &skdloader.preview_image);
243 
244     xitk_destroy_widgets(skdloader.widget_list);
245     xitk_window_destroy_window(gGui->imlib_data, skdloader.xwin);
246 
247     skdloader.xwin = NULL;
248 
249     /* xitk_dlist_init (&skdloader.widget_list->list); */
250 
251     gGui->x_lock_display (gGui->display);
252     XFreeGC(gGui->display, (XITK_WIDGET_LIST_GC(skdloader.widget_list)));
253     gGui->x_unlock_display (gGui->display);
254 
255     XITK_WIDGET_LIST_FREE(skdloader.widget_list);
256 
257     for(i = 0; i < skdloader.num; i++) {
258       SAFE_FREE(skdloader.slxs[i]->name);
259       SAFE_FREE(skdloader.slxs[i]->author.name);
260       SAFE_FREE(skdloader.slxs[i]->author.email);
261       SAFE_FREE(skdloader.slxs[i]->skin.href);
262       SAFE_FREE(skdloader.slxs[i]->skin.preview);
263       free(skdloader.slxs[i]);
264       free(skdloader.entries[i]);
265     }
266 
267     free(skdloader.slxs);
268     free(skdloader.entries);
269 
270     skdloader.num = 0;
271 
272     skdloader.running = 0;
273 
274     try_to_set_input_focus(gGui->video_window);
275   }
276 }
277 
download_update_blank_preview(void)278 static void download_update_blank_preview(void) {
279 
280   gGui->x_lock_display (gGui->display);
281   XSetForeground(gGui->display,(XITK_WIDGET_LIST_GC(skdloader.widget_list)),
282 		 (xitk_get_pixel_color_from_rgb(gGui->imlib_data, 52, 52, 52)));
283   XFillRectangle(gGui->display,
284 		 (XITK_WIDGET_LIST_WINDOW(skdloader.widget_list)),
285 		 (XITK_WIDGET_LIST_GC(skdloader.widget_list)), 15, 34, PREVIEW_WIDTH, PREVIEW_HEIGHT);
286   gGui->x_unlock_display (gGui->display);
287 
288 }
289 
redraw_preview(void)290 static void redraw_preview(void) {
291   int  x, y;
292 
293   x = 15 + ((PREVIEW_WIDTH - skdloader.preview_image->image->width) >> 1);
294   y = 34 + ((PREVIEW_HEIGHT - skdloader.preview_image->image->height) >> 1);
295 
296   if(skdloader.preview_image->mask && skdloader.preview_image->mask->pixmap) {
297     gGui->x_lock_display (gGui->display);
298     XSetClipOrigin(gGui->display, (XITK_WIDGET_LIST_GC(skdloader.widget_list)), x, y);
299     XSetClipMask(gGui->display, (XITK_WIDGET_LIST_GC(skdloader.widget_list)), skdloader.preview_image->mask->pixmap);
300     gGui->x_unlock_display (gGui->display);
301   }
302 
303   gGui->x_lock_display (gGui->display);
304   XCopyArea (gGui->display, skdloader.preview_image->image->pixmap,
305 	     (XITK_WIDGET_LIST_WINDOW(skdloader.widget_list)), (XITK_WIDGET_LIST_GC(skdloader.widget_list)), 0, 0,
306 	     skdloader.preview_image->image->width, skdloader.preview_image->image->height, x, y);
307   XSetClipMask(gGui->display, (XITK_WIDGET_LIST_GC(skdloader.widget_list)), None);
308   XSync(gGui->display, False);
309   gGui->x_unlock_display (gGui->display);
310 }
311 
download_update_preview(void)312 static void download_update_preview(void) {
313 
314   download_update_blank_preview();
315 
316   if(skdloader.preview_image && skdloader.preview_image->image && skdloader.preview_image->image->pixmap)
317     redraw_preview();
318 }
319 
download_skin_preview(xitk_widget_t * w,void * data,int selected)320 static void download_skin_preview(xitk_widget_t *w, void *data, int selected) {
321   download_t   download;
322 
323   if(skdloader.slxs[selected]->skin.preview == NULL)
324     return;
325 
326   download.buf    = NULL;
327   download.error  = NULL;
328   download.size   = 0;
329   download.status = 0;
330 
331   download_set_cursor_state(WAIT_CURS);
332 
333   if((network_download(skdloader.slxs[selected]->skin.preview, &download))) {
334     char          *skpname;
335     FILE          *fd;
336     char           tmpfile[XITK_PATH_MAX + XITK_NAME_MAX + 2];
337 
338     skpname = strrchr(skdloader.slxs[selected]->skin.preview, '/');
339 
340     if(skpname)
341       skpname++;
342     else
343       skpname = skdloader.slxs[selected]->skin.preview;
344 
345     snprintf(tmpfile, sizeof(tmpfile), "%s%u%s", "/tmp/", (unsigned int)time(NULL), skpname);
346 
347     if((fd = fopen(tmpfile, "w+b")) != NULL) {
348       ImlibImage    *img = NULL;
349 
350       fwrite(download.buf, download.size, 1, fd);
351       fflush(fd);
352       fclose(fd);
353 
354       gGui->x_lock_display (gGui->display);
355       img = Imlib_load_image(gGui->imlib_data, tmpfile);
356       gGui->x_unlock_display (gGui->display);
357 
358       if(img) {
359 	xitk_image_t *ximg, *oimg = skdloader.preview_image;
360 	int           preview_width, preview_height;
361 	float         ratio;
362 
363 	preview_width = img->rgb_width;
364 	preview_height = img->rgb_height;
365 
366 	ratio = ((float)preview_width / (float)preview_height);
367 
368 	if(ratio > PREVIEW_RATIO) {
369 	  if(preview_width > PREVIEW_WIDTH) {
370 	    preview_width = PREVIEW_WIDTH;
371 	    preview_height = (float)preview_width / ratio;
372 	  }
373 	}
374 	else {
375 	  if(preview_height > PREVIEW_HEIGHT) {
376 	    preview_height = PREVIEW_HEIGHT;
377 	    preview_width = (float)preview_height * ratio;
378 	  }
379 	}
380 
381 	/* Rescale preview */
382 	gGui->x_lock_display (gGui->display);
383 	Imlib_render(gGui->imlib_data, img, preview_width, preview_height);
384 	gGui->x_unlock_display (gGui->display);
385 
386 	ximg = (xitk_image_t *) xitk_xmalloc(sizeof(xitk_image_t));
387 	ximg->width  = preview_width;
388 	ximg->height = preview_height;
389 
390 	ximg->image = xitk_image_create_xitk_pixmap(gGui->imlib_data, preview_width, preview_height);
391 	ximg->mask = xitk_image_create_xitk_mask_pixmap(gGui->imlib_data, preview_width, preview_height);
392 
393 	gGui->x_lock_display (gGui->display);
394 	ximg->image->pixmap = Imlib_copy_image(gGui->imlib_data, img);
395 	ximg->mask->pixmap = Imlib_copy_mask(gGui->imlib_data, img);
396 	Imlib_destroy_image(gGui->imlib_data, img);
397 	gGui->x_unlock_display (gGui->display);
398 
399 	skdloader.preview_image = ximg;
400 
401 	if(oimg)
402 	  xitk_image_free_image(gGui->imlib_data, &oimg);
403 
404 	download_update_preview();
405       }
406 
407       unlink(tmpfile);
408     }
409   }
410   else {
411     xine_error(_("Unable to download '%s': %s"),
412 	       skdloader.slxs[selected]->skin.preview, download.error);
413     download_update_blank_preview();
414   }
415 
416   download_set_cursor_state(NORMAL_CURS);
417 
418   free(download.buf);
419   free(download.error);
420 }
421 
download_skin_select(xitk_widget_t * w,void * data)422 static void download_skin_select(xitk_widget_t *w, void *data) {
423   int          selected = xitk_browser_get_current_selected(skdloader.browser);
424   download_t   download;
425 
426   if(selected < 0)
427     return;
428 
429   download.buf    = NULL;
430   download.error  = NULL;
431   download.size   = 0;
432   download.status = 0;
433 
434   download_set_cursor_state(WAIT_CURS);
435 
436   if((network_download(skdloader.slxs[selected]->skin.href, &download))) {
437     char *filename;
438 
439     filename = strrchr(skdloader.slxs[selected]->skin.href, '/');
440 
441     if(filename)
442       filename++;
443     else
444       filename = skdloader.slxs[selected]->skin.href;
445 
446     if(filename[0]) {
447       struct stat  st;
448       char         skindir[XITK_PATH_MAX + 1];
449 
450       snprintf(skindir, sizeof(skindir), "%s%s", xine_get_homedir(), "/.xine/skins");
451 
452       /*
453        * Make sure to have the directory
454        */
455       if(stat(skindir, &st) < 0)
456 	(void) mkdir_safe(skindir);
457 
458       if(stat(skindir, &st) < 0) {
459 	xine_error(_("Unable to create '%s' directory: %s."), skindir, strerror(errno));
460       }
461       else {
462 	char   tmpskin[XITK_PATH_MAX + XITK_NAME_MAX + 2];
463 	FILE  *fd;
464 
465         snprintf(tmpskin, sizeof(tmpskin), "%s%u%s", "/tmp/", (unsigned int)time(NULL), filename);
466 
467 	if((fd = fopen(tmpskin, "w+b")) != NULL) {
468 	  char      buffer[2048];
469           char     *cmd;
470 	  char     *fskin_path;
471 	  int       i, skin_found = -1, len;
472 
473 	  fwrite(download.buf, download.size, 1, fd);
474 	  fflush(fd);
475 	  fclose(fd);
476 
477           cmd = xitk_asprintf("%s %s %s %s %s", "which tar > /dev/null 2>&1 && cd ", skindir, " && gunzip -c ", tmpskin, " | tar xf -");
478           if (cmd)
479             xine_system(0, cmd);
480           free(cmd);
481 	  unlink(tmpskin);
482 
483 	  len = strlen(filename) - strlen(".tar.gz");
484 	  if (len > sizeof(buffer) - 1) len = sizeof(buffer) - 1;
485 	  strncpy(buffer, filename, len);
486 	  buffer[len] = '\0';
487 
488           fskin_path = xitk_asprintf("%s/%s/%s", skindir, buffer, "doinst.sh");
489           if (fskin_path && is_a_file(fskin_path)) {
490 	    char *doinst;
491 
492 	    doinst = xitk_asprintf("%s %s/%s %s", "cd", skindir, buffer, "&& ./doinst.sh");
493             if (doinst)
494               xine_system(0, doinst);
495             free(doinst);
496 	  }
497           free(fskin_path);
498 
499 	  for(i = 0; i < skins_avail_num; i++) {
500 	    if((!strcmp(skins_avail[i]->pathname, skindir))
501 	       && (!strcmp(skins_avail[i]->skin, buffer))) {
502 	      skin_found = i;
503 	      break;
504 	    }
505 	  }
506 
507 	  if(skin_found == -1) {
508 	    skins_avail = (skins_locations_t **) realloc(skins_avail, (skins_avail_num + 2) * sizeof(skins_locations_t*));
509 	    skin_names = (char **) realloc(skin_names, (skins_avail_num + 2) * sizeof(char *));
510 
511 	    skins_avail[skins_avail_num] = (skins_locations_t *) calloc(1, sizeof(skins_locations_t));
512 	    skins_avail[skins_avail_num]->pathname = strdup(skindir);
513 	    skins_avail[skins_avail_num]->skin = strdup(buffer);
514 	    skins_avail[skins_avail_num]->number = skins_avail_num;
515 
516 	    skin_names[skins_avail_num] = strdup(skins_avail[skins_avail_num]->skin);
517 
518 	    skins_avail_num++;
519 
520 	    skins_avail[skins_avail_num] = NULL;
521 	    skin_names[skins_avail_num] = NULL;
522 
523 	    /* Re-register skin enum config, a new one has been added */
524 	    (void) xine_config_register_enum (__xineui_global_xine_instance, "gui.skin",
525 					      (get_skin_offset(DEFAULT_SKIN)),
526 					      skin_names,
527 					      _("gui skin theme"),
528 					      CONFIG_NO_HELP,
529 					      CONFIG_LEVEL_EXP,
530 					      skin_change_cb,
531 					      CONFIG_NO_DATA);
532 	  }
533 	  else
534 	    xine_info(_("Skin %s correctly installed"), buffer);
535 
536 	  /* Okay, load this skin */
537 	  select_new_skin((skin_found >= 0) ? skin_found : skins_avail_num - 1);
538 	}
539 	else
540 	  xine_error(_("Unable to create '%s'."), tmpskin);
541 
542       }
543     }
544   }
545   else
546     xine_error(_("Unable to download '%s': %s"),
547 	       skdloader.slxs[selected]->skin.href, download.error);
548 
549   download_set_cursor_state(NORMAL_CURS);
550 
551   free(download.buf);
552   free(download.error);
553 
554   download_skin_exit(w, NULL);
555 }
556 
download_skin_handle_event(XEvent * event,void * data)557 static void download_skin_handle_event(XEvent *event, void *data) {
558 
559   switch(event->type) {
560 
561   case Expose:
562     if(event->xexpose.count == 0)
563       download_update_preview();
564     break;
565 
566   case KeyPress:
567     if(xitk_get_key_pressed(event) == XK_Escape)
568       download_skin_exit(NULL, NULL);
569     else
570       gui_handle_event (event, gGui);
571     break;
572 
573   }
574 }
575 
download_skin_end(void)576 void download_skin_end(void) {
577   download_skin_exit(NULL, NULL);
578 }
579 
download_skin(char * url)580 void download_skin(char *url) {
581   slx_entry_t         **slxs;
582   xitk_window_t        *xwin;
583   xitk_pixmap_t        *bg;
584   int                   w, width, height;
585   xitk_widget_t        *widget;
586 
587   if(skdloader.running)
588   {
589     gGui->x_lock_display (gGui->display);
590     XRaiseWindow(gGui->display, xitk_window_get_window(skdloader.xwin));
591     XSetInputFocus(gGui->display, xitk_window_get_window(skdloader.xwin), RevertToParent, CurrentTime);
592     gGui->x_unlock_display (gGui->display);
593     return;
594   }
595 
596   w = 300;
597   gGui->x_lock_display (gGui->display);
598   gGui->x_unlock_display (gGui->display);
599 
600   xwin = xitk_window_dialog_button_free_with_width(gGui->imlib_data, _("Be patient..."),
601   						   w, ALIGN_CENTER,
602 						   _("Retrieving skin list from %s"), url);
603 
604   while(!xitk_is_window_visible(gGui->display, xitk_window_get_window(xwin)))
605     xine_usec_sleep(5000);
606 
607   if(!gGui->use_root_window && gGui->video_display == gGui->display) {
608     gGui->x_lock_display (gGui->display);
609     XSetTransientForHint (gGui->display,
610 			  xitk_window_get_window(xwin), gGui->video_window);
611     gGui->x_unlock_display (gGui->display);
612   }
613   layer_above_video(xitk_window_get_window(xwin));
614 
615   if((slxs = skins_get_slx_entries(url)) != NULL) {
616     int                        i;
617     xitk_browser_widget_t      br;
618     xitk_labelbutton_widget_t  lb;
619     GC                         gc;
620     int                        x, y;
621 
622     xitk_window_dialog_destroy(xwin);
623 
624     XITK_WIDGET_INIT(&br, gGui->imlib_data);
625     XITK_WIDGET_INIT(&lb, gGui->imlib_data);
626 
627 #if 0
628     for(i = 0; slxs[i]; i++) {
629       printf("download skins: Skin number %d:\n", i);
630       printf("  Name: %s\n", slxs[i]->name);
631       printf("  Author Name: %s\n", slxs[i]->author.name);
632       printf("  Author email: %s\n", slxs[i]->author.email);
633       printf("  Href: %s\n", slxs[i]->skin.href);
634       printf("  Version: %d\n", slxs[i]->skin.version);
635       printf("  Maintained: %d\n", slxs[i]->skin.maintained);
636       printf("--\n");
637     }
638 #endif
639 
640     skdloader.running = 1;
641 
642     gGui->x_lock_display (gGui->display);
643     x = ((DisplayWidth(gGui->display, gGui->screen)) >> 1) - (WINDOW_WIDTH >> 1);
644     y = ((DisplayHeight(gGui->display, gGui->screen)) >> 1) - (WINDOW_HEIGHT >> 1);
645     gGui->x_unlock_display (gGui->display);
646 
647     skdloader.xwin = xitk_window_create_dialog_window(gGui->imlib_data,
648 						       _("Choose a skin to download..."),
649 						       x, y, WINDOW_WIDTH, WINDOW_HEIGHT);
650 
651     set_window_states_start((xitk_window_get_window(skdloader.xwin)));
652 
653     gGui->x_lock_display (gGui->display);
654     gc = XCreateGC(gGui->display,
655     		   (xitk_window_get_window(skdloader.xwin)), None, None);
656     gGui->x_unlock_display (gGui->display);
657 
658     xitk_window_get_window_size(skdloader.xwin, &width, &height);
659     bg = xitk_image_create_xitk_pixmap(gGui->imlib_data, width, height);
660     gGui->x_lock_display (gGui->display);
661     XCopyArea(gGui->display, (xitk_window_get_background(skdloader.xwin)), bg->pixmap,
662 	      bg->gc, 0, 0, width, height, 0, 0);
663     gGui->x_unlock_display (gGui->display);
664 
665     skdloader.widget_list = xitk_widget_list_new();
666     xitk_widget_list_set(skdloader.widget_list,
667 			 WIDGET_LIST_WINDOW, (void *) (xitk_window_get_window(skdloader.xwin)));
668     xitk_widget_list_set(skdloader.widget_list, WIDGET_LIST_GC, gc);
669 
670     skdloader.slxs = slxs;
671 
672     i = 0;
673     while(slxs[i]) {
674 
675       skdloader.entries = (char **) realloc(skdloader.entries, sizeof(char *) * (i + 2));
676 
677       skdloader.entries[i] = strdup(slxs[i]->name);
678       i++;
679     }
680     skdloader.entries[i] = NULL;
681 
682     skdloader.num = i;
683 
684     skdloader.preview_image = NULL;
685 
686     x = 15;
687     y = 34 + PREVIEW_HEIGHT + 14;
688 
689     br.arrow_up.skin_element_name    = NULL;
690     br.slider.skin_element_name      = NULL;
691     br.arrow_dn.skin_element_name    = NULL;
692     br.browser.skin_element_name     = NULL;
693     br.browser.max_displayed_entries = MAX_DISP_ENTRIES;
694     br.browser.num_entries           = skdloader.num;
695     br.browser.entries               = (const char *const *)skdloader.entries;
696     br.callback                      = download_skin_preview;
697     br.dbl_click_callback            = NULL;
698     br.parent_wlist                  = skdloader.widget_list;
699     br.userdata                      = NULL;
700     skdloader.browser = xitk_noskin_browser_create (skdloader.widget_list, &br,
701       (XITK_WIDGET_LIST_GC(skdloader.widget_list)), x + 5, y + 5, WINDOW_WIDTH - (30 + 10 + 16), 20, 16, br_fontname);
702     xitk_add_widget (skdloader.widget_list, skdloader.browser);
703 
704     xitk_browser_update_list(skdloader.browser,
705     			     (const char *const *)skdloader.entries, NULL, skdloader.num, 0);
706 
707     xitk_enable_and_show_widget(skdloader.browser);
708 
709     draw_rectangular_inner_box(gGui->imlib_data, bg, x, y,
710 			       (WINDOW_WIDTH - 30 - 1), (MAX_DISP_ENTRIES * 20 + 16 + 10 - 1));
711 
712     xitk_window_change_background(gGui->imlib_data, skdloader.xwin, bg->pixmap, width, height);
713     xitk_image_destroy_xitk_pixmap(bg);
714 
715     y = WINDOW_HEIGHT - (23 + 15);
716     x = 15;
717 
718     lb.button_type       = CLICK_BUTTON;
719     lb.label             = _("Load");
720     lb.align             = ALIGN_CENTER;
721     lb.callback          = download_skin_select;
722     lb.state_callback    = NULL;
723     lb.userdata          = NULL;
724     lb.skin_element_name = NULL;
725     widget =  xitk_noskin_labelbutton_create (skdloader.widget_list,
726       &lb, x, y, 100, 23, "Black", "Black", "White", btnfontname);
727     xitk_add_widget (skdloader.widget_list, widget);
728     xitk_enable_and_show_widget(widget);
729 
730     x = WINDOW_WIDTH - (100 + 15);
731 
732     lb.button_type       = CLICK_BUTTON;
733     lb.label             = _("Cancel");
734     lb.align             = ALIGN_CENTER;
735     lb.callback          = download_skin_exit;
736     lb.state_callback    = NULL;
737     lb.userdata          = NULL;
738     lb.skin_element_name = NULL;
739     widget =  xitk_noskin_labelbutton_create (skdloader.widget_list,
740       &lb, x, y, 100, 23, "Black", "Black", "White", btnfontname);
741     xitk_add_widget (skdloader.widget_list, widget);
742     xitk_enable_and_show_widget(widget);
743 
744     skdloader.widget_key = xitk_register_event_handler("skdloader",
745 							(xitk_window_get_window(skdloader.xwin)),
746 							download_skin_handle_event,
747 							NULL,
748 							NULL,
749 							skdloader.widget_list,
750 							NULL);
751 
752     gGui->x_lock_display (gGui->display);
753     XRaiseWindow(gGui->display, xitk_window_get_window(skdloader.xwin));
754     XMapWindow(gGui->display, xitk_window_get_window(skdloader.xwin));
755     if(!gGui->use_root_window && gGui->video_display == gGui->display)
756       XSetTransientForHint (gGui->display,
757 			    xitk_window_get_window(skdloader.xwin), gGui->video_window);
758     gGui->x_unlock_display (gGui->display);
759     layer_above_video(xitk_window_get_window(skdloader.xwin));
760 
761     try_to_set_input_focus(xitk_window_get_window(skdloader.xwin));
762     download_update_blank_preview();
763   }
764   else
765     xitk_window_dialog_destroy(xwin);
766 }
767