Refining the skin a little
[pandora-libraries.git] / minimenu / mmui.c
1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <limits.h>
5 #include <time.h>
6 #include <unistd.h>
7 #include "SDL.h"
8 #include "SDL_audio.h"
9 #include "SDL_image.h"
10 #include "SDL_ttf.h"
11 #include "SDL_gfxPrimitives.h"
12 #include "SDL_rotozoom.h"
13 #include "SDL_thread.h"
14
15 #include "pnd_conf.h"
16 #include "pnd_logger.h"
17 #include "pnd_pxml.h"
18 #include "pnd_container.h"
19 #include "pnd_discovery.h"
20 #include "pnd_apps.h"
21 #include "pnd_device.h"
22
23 #include "mmenu.h"
24 #include "mmcat.h"
25 #include "mmcache.h"
26 #include "mmui.h"
27 #include "mmwrapcmd.h"
28
29 /* SDL
30  */
31 SDL_Surface *sdl_realscreen = NULL;
32 unsigned int sdl_ticks = 0;
33 SDL_Thread *g_preview_thread = NULL;
34
35 enum { sdl_user_ticker = 0, sdl_user_finishedpreview = 1 };
36
37 /* app state
38  */
39 unsigned short int g_scale = 1; // 1 == noscale
40
41 SDL_Surface *g_imgcache [ IMG_MAX ];
42
43 TTF_Font *g_big_font = NULL;
44 TTF_Font *g_grid_font = NULL;
45 TTF_Font *g_detailtext_font = NULL;
46 TTF_Font *g_tab_font = NULL;
47
48 extern pnd_conf_handle g_conf;
49
50 /* current display state
51  */
52 int ui_rows_scrolled_down = 0;          // number of rows that should be missing from top of the display
53 mm_appref_t *ui_selected = NULL;
54 unsigned char ui_category = 0;          // current category
55 unsigned char ui_catshift = 0;          // how many cats are offscreen to the left
56
57 extern mm_category_t g_categories [ MAX_CATS ];
58 extern unsigned char g_categorycount;
59
60 static SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ); // height -1 means ignore
61 static int ui_selected_index ( void );
62
63 unsigned char ui_setup ( void ) {
64
65   /* set up SDL
66    */
67
68   SDL_Init ( SDL_INIT_EVERYTHING | SDL_INIT_NOPARACHUTE );
69
70   SDL_JoystickOpen ( 0 ); // turn on joy-0
71
72   SDL_WM_SetCaption ( "mmenu", "mmenu" );
73
74   // hide the mouse cursor if we can
75   if ( SDL_ShowCursor ( -1 ) == 1 ) {
76     SDL_ShowCursor ( 0 );
77   }
78
79   atexit ( SDL_Quit );
80
81   // open up a surface
82   unsigned int svm = SDL_SWSURFACE /*| SDL_FULLSCREEN*/ /* 0*/;
83   if ( pnd_conf_get_as_int_d ( g_conf, "display.fullscreen", 0 ) > 0 ) {
84     svm |= SDL_FULLSCREEN;
85   }
86
87   sdl_realscreen =
88     SDL_SetVideoMode ( 800 * g_scale, 480 * g_scale, 16 /*bpp*/, svm );
89
90   if ( ! sdl_realscreen ) {
91     pnd_log ( pndn_error, "ERROR: Couldn't open SDL real screen; dieing." );
92     return ( 0 );
93   }
94
95   pnd_log ( pndn_debug, "Pixel format:" );
96   pnd_log ( pndn_debug, "bpp b: %u\n", sdl_realscreen -> format -> BitsPerPixel );
97   pnd_log ( pndn_debug, "bpp B: %u\n", sdl_realscreen -> format -> BytesPerPixel );
98   pnd_log ( pndn_debug, "R mask: %08x\n", sdl_realscreen -> format -> Rmask );
99   pnd_log ( pndn_debug, "G mask: %08x\n", sdl_realscreen -> format -> Gmask );
100   pnd_log ( pndn_debug, "B mask: %08x\n", sdl_realscreen -> format -> Bmask );
101
102 #if 0 // audio
103   {
104     SDL_AudioSpec fmt;
105
106     /* Set 16-bit stereo audio at 22Khz */
107     fmt.freq = 44100; //22050;
108     fmt.format = AUDIO_S16; //AUDIO_S16;
109     fmt.channels = 1;
110     fmt.samples = 2048;        /* A good value for games */
111     fmt.callback = mixaudio;
112     fmt.userdata = NULL;
113
114     /* Open the audio device and start playing sound! */
115     if ( SDL_OpenAudio ( &fmt, NULL ) < 0 ) {
116       zotlog ( "Unable to open audio: %s\n", SDL_GetError() );
117       exit ( 1 );
118     }
119
120     SDL_PauseAudio ( 0 );
121   }
122 #endif
123
124   // images
125   //IMG_Init ( IMG_INIT_JPG | IMG_INIT_PNG );
126
127   /* fonts
128    */
129
130   // init
131   if ( TTF_Init() == -1 ) {
132     pnd_log ( pndn_error, "ERROR: Couldn't set up SDL TTF lib\n" );
133     return ( 0 ); // couldn't set up SDL TTF
134   }
135
136   char fullpath [ PATH_MAX ];
137   // big font
138   sprintf ( fullpath, "%s/%s", g_skinpath, pnd_conf_get_as_char ( g_conf, "minimenu.font" ) );
139   g_big_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, "minimenu.font_ptsize", 24 ) );
140   if ( ! g_big_font ) {
141     pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n",
142               pnd_conf_get_as_char ( g_conf, "minimenu.font" ), pnd_conf_get_as_int_d ( g_conf, "minimenu.font_ptsize", 24 ) );
143     return ( 0 ); // couldn't set up SDL TTF
144   }
145
146   // grid font
147   sprintf ( fullpath, "%s/%s", g_skinpath, pnd_conf_get_as_char ( g_conf, MMENU_GRID_FONT ) );
148   g_grid_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, MMENU_GRID_FONTSIZE, 10 ) );
149   if ( ! g_grid_font ) {
150     pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n",
151               pnd_conf_get_as_char ( g_conf, MMENU_GRID_FONT ), pnd_conf_get_as_int_d ( g_conf, MMENU_GRID_FONTSIZE, 10 ) );
152     return ( 0 ); // couldn't set up SDL TTF
153   }
154
155   // detailtext font
156   sprintf ( fullpath, "%s/%s", g_skinpath, pnd_conf_get_as_char ( g_conf, "detailtext.font" ) );
157   g_detailtext_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, "detailtext.font_ptsize", 10 ) );
158   if ( ! g_detailtext_font ) {
159     pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n",
160               pnd_conf_get_as_char ( g_conf, "detailtext.font" ), pnd_conf_get_as_int_d ( g_conf, "detailtext.font_ptsize", 10 ) );
161     return ( 0 ); // couldn't set up SDL TTF
162   }
163
164   // tab font
165   sprintf ( fullpath, "%s/%s", g_skinpath, pnd_conf_get_as_char ( g_conf, "tabs.font" ) );
166   g_tab_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, "tabs.font_ptsize", 10 ) );
167   if ( ! g_tab_font ) {
168     pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n",
169               pnd_conf_get_as_char ( g_conf, "tabs.font" ), pnd_conf_get_as_int_d ( g_conf, "tabs.font_ptsize", 10 ) );
170     return ( 0 ); // couldn't set up SDL TTF
171   }
172
173   return ( 1 );
174 }
175
176 mm_imgcache_t g_imagecache [ IMG_TRUEMAX ] = {
177   { IMG_BACKGROUND_800480,    "graphics.IMG_BACKGROUND_800480" },
178   { IMG_BACKGROUND_TABMASK,   "graphics.IMG_BACKGROUND_TABMASK" },
179   { IMG_DETAIL_PANEL,         "graphics.IMG_DETAIL_PANEL" },
180   { IMG_DETAIL_BG,            "graphics.IMG_DETAIL_BG" },
181   { IMG_SELECTED_ALPHAMASK,   "graphics.IMG_SELECTED_ALPHAMASK" },
182   { IMG_TAB_SEL,              "graphics.IMG_TAB_SEL" },
183   { IMG_TAB_UNSEL,            "graphics.IMG_TAB_UNSEL" },
184   { IMG_TAB_LINE,             "graphics.IMG_TAB_LINE" },
185   { IMG_TAB_LINEL,            "graphics.IMG_TAB_LINEL" },
186   { IMG_TAB_LINER,            "graphics.IMG_TAB_LINER" },
187   { IMG_ICON_MISSING,         "graphics.IMG_ICON_MISSING" },
188   { IMG_SELECTED_HILITE,      "graphics.IMG_SELECTED_HILITE" },
189   { IMG_PREVIEW_MISSING,      "graphics.IMG_PREVIEW_MISSING" },
190   { IMG_ARROW_UP,             "graphics.IMG_ARROW_UP", },
191   { IMG_ARROW_DOWN,           "graphics.IMG_ARROW_DOWN", },
192   { IMG_ARROW_SCROLLBAR,      "graphics.IMG_ARROW_SCROLLBAR", },
193   { IMG_MAX,                  NULL },
194 };
195
196 unsigned char ui_imagecache ( char *basepath ) {
197   unsigned int i;
198   char fullpath [ PATH_MAX ];
199
200   // loaded
201
202   for ( i = 0; i < IMG_MAX; i++ ) {
203
204     if ( g_imagecache [ i ].id != i ) {
205       pnd_log ( pndn_error, "ERROR: Internal table mismatch during caching [%u]\n", i );
206       exit ( -1 );
207     }
208
209     char *filename = pnd_conf_get_as_char ( g_conf, g_imagecache [ i ].confname );
210
211     if ( ! filename ) {
212       pnd_log ( pndn_error, "ERROR: Missing filename in conf for key: %s\n", g_imagecache [ i ].confname );
213       return ( 0 );
214     }
215
216     sprintf ( fullpath, "%s/%s", basepath, filename );
217
218     if ( ! ( g_imagecache [ i ].i = IMG_Load ( fullpath ) ) ) {
219       pnd_log ( pndn_error, "ERROR: Couldn't load static cache image: %s\n", fullpath );
220       return ( 0 );
221     }
222
223   } // for
224
225   // generated
226   //g_imagecache [ IMG_SELECTED_ALPHAMASK ].i = SDL_CreateRGBSurface ( SDL_SWSURFACE, 60, 60, 32, 0xFF0000, 0x00FF00, 0xFF, 0xFF000000 );
227   //boxRGBA ( g_imagecache [ IMG_SELECTED_ALPHAMASK ].i, 0, 0, 60, 60, 100, 100, 100, 250 );
228
229   // post processing
230   //
231
232   // scale icons
233   g_imagecache [ IMG_SELECTED_ALPHAMASK ].i = ui_scale_image ( g_imagecache [ IMG_SELECTED_ALPHAMASK ].i, pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_width", 50 ), -1 );
234   g_imagecache [ IMG_ICON_MISSING ].i = ui_scale_image ( g_imagecache [ IMG_ICON_MISSING ].i, pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_width", 50 ), -1 );
235   // scale text hilight
236   g_imagecache [ IMG_SELECTED_HILITE ].i = ui_scale_image ( g_imagecache [ IMG_SELECTED_HILITE ].i, pnd_conf_get_as_int_d ( g_conf, "grid.text_width", 50 ), -1 );
237   // scale preview no-pic
238   g_imagecache [ IMG_PREVIEW_MISSING ].i = ui_scale_image ( g_imagecache [ IMG_PREVIEW_MISSING ].i,
239                                                             pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 50 ),
240                                                             pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_height", 50 ) );
241
242   // set alpha on detail panel
243   SDL_SetAlpha ( g_imagecache [ IMG_DETAIL_BG ].i, SDL_SRCALPHA, pnd_conf_get_as_int_d ( g_conf, "display.detail_bg_alpha", 50 ) );
244
245   return ( 1 );
246 } // ui_imagecache
247
248 void ui_render ( unsigned int render_mask ) {
249
250   // 800x480:
251   // divide width: 550 / 250
252   // divide left side: 5 columns == 110px width
253   //   20px buffer either side == 70px wide icon + 20 + 20?
254
255   unsigned int icon_rows;
256
257 #define MAXRECTS 200
258   SDL_Rect rects [ MAXRECTS ], src;
259   SDL_Rect *dest = rects;
260   bzero ( dest, sizeof(SDL_Rect)*MAXRECTS );
261
262   unsigned int row, displayrow, col;
263   mm_appref_t *appiter;
264
265   unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 );
266
267   unsigned char row_max = pnd_conf_get_as_int_d ( g_conf, MMENU_DISP_ROWMAX, 4 );
268   unsigned char col_max = pnd_conf_get_as_int_d ( g_conf, MMENU_DISP_COLMAX, 5 );
269
270   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
271   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
272   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
273   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
274
275   unsigned int grid_offset_x = pnd_conf_get_as_int ( g_conf, "grid.grid_offset_x" );
276   unsigned int grid_offset_y = pnd_conf_get_as_int ( g_conf, "grid.grid_offset_y" );
277
278   unsigned int icon_offset_x = pnd_conf_get_as_int ( g_conf, "grid.icon_offset_x" );
279   unsigned int icon_offset_y = pnd_conf_get_as_int ( g_conf, "grid.icon_offset_y" );
280   unsigned int icon_max_width = pnd_conf_get_as_int ( g_conf, "grid.icon_max_width" );
281
282   unsigned int text_width = pnd_conf_get_as_int ( g_conf, "grid.text_width" );
283   unsigned int text_clip_x = pnd_conf_get_as_int ( g_conf, "grid.text_clip_x" );
284   unsigned int text_offset_x = pnd_conf_get_as_int ( g_conf, "grid.text_offset_x" );
285   unsigned int text_offset_y = pnd_conf_get_as_int ( g_conf, "grid.text_offset_y" );
286
287   unsigned int cell_width = pnd_conf_get_as_int ( g_conf, "grid.cell_width" );
288   unsigned int cell_height = pnd_conf_get_as_int ( g_conf, "grid.cell_height" );
289
290   // how many total rows do we need?
291   icon_rows = g_categories [ ui_category ].refcount / col_max;
292   if ( g_categories [ ui_category ].refcount % col_max > 0 ) {
293     icon_rows++;
294   }
295
296   // if no selected app yet, select the first one
297 #if 0
298   if ( ! ui_selected ) {
299     ui_selected = g_categories [ ui_category ].refs;
300   }
301 #endif
302
303   // reset touchscreen regions
304   ui_register_reset();
305
306   // ensure selection is visible
307   if ( ui_selected ) {
308
309     int index = ui_selected_index();
310     int topleft = col_max * ui_rows_scrolled_down;
311     int botright = ( col_max * ( ui_rows_scrolled_down + row_max ) - 1 );
312
313     //pnd_log ( PND_LOG_DEFAULT, "index %u tl %u br %u\n", index, topleft, botright );
314
315     if ( index < topleft ) {
316       ui_rows_scrolled_down -= pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 );
317     } else if ( index > botright ) {
318       ui_rows_scrolled_down += pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 );
319     }
320
321     if ( ui_rows_scrolled_down < 0 ) {
322       ui_rows_scrolled_down = 0;
323     } else if ( ui_rows_scrolled_down > icon_rows ) {
324       ui_rows_scrolled_down = icon_rows;
325     }
326
327   } // ensire visible
328
329   // render background
330   if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
331     dest -> x = 0;
332     dest -> y = 0;
333     dest -> w = sdl_realscreen -> w;
334     dest -> h = sdl_realscreen -> h;
335     SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, dest /* 0,0 */ );
336     dest++;
337   }
338
339   // tabmask
340   if ( g_imagecache [ IMG_BACKGROUND_TABMASK ].i ) {
341     dest -> x = 0;
342     dest -> y = 0;
343     dest -> w = sdl_realscreen -> w;
344     dest -> h = sdl_realscreen -> h;
345     SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_TABMASK ].i, NULL /* whole image */, sdl_realscreen, dest /* 0,0 */ );
346     dest++;
347   }
348
349   // tabs
350   if ( g_imagecache [ IMG_TAB_SEL ].i && g_imagecache [ IMG_TAB_UNSEL ].i ) {
351     unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
352     unsigned int tab_height = pnd_conf_get_as_int ( g_conf, "tabs.tab_height" );
353     unsigned int tab_selheight = pnd_conf_get_as_int ( g_conf, "tabs.tab_selheight" );
354     unsigned int tab_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_x" );
355     unsigned int tab_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_y" );
356     unsigned int text_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_x" );
357     unsigned int text_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_y" );
358     unsigned int text_width = pnd_conf_get_as_int ( g_conf, "tabs.text_width" );
359     unsigned int maxtab = ( screen_width / tab_width ) < g_categorycount ? ( screen_width / tab_width ) + ui_catshift : g_categorycount + ui_catshift;
360
361     for ( col = ui_catshift;
362           col < maxtab;
363           col++ )
364     {
365
366       SDL_Surface *s;
367       if ( col == ui_category ) {
368         s = g_imagecache [ IMG_TAB_SEL ].i;
369       } else {
370         s = g_imagecache [ IMG_TAB_UNSEL ].i;
371       }
372
373       // draw tab
374       src.x = 0;
375       src.y = 0;
376       src.w = tab_width;
377       if ( col == ui_category ) {
378         src.h = tab_selheight;
379       } else {
380         src.h = tab_height;
381       }
382       dest -> x = tab_offset_x + ( (col-ui_catshift) * tab_width );
383       dest -> y = tab_offset_y;
384       //pnd_log ( pndn_debug, "tab %u at %ux%u\n", col, dest.x, dest.y );
385       SDL_BlitSurface ( s, &src, sdl_realscreen, dest );
386
387       // store touch info
388       ui_register_tab ( col, dest -> x, dest -> y, tab_width, tab_height );
389
390       dest++;
391
392       // draw tab line
393       if ( col == ui_category ) {
394         // no line for selected tab
395         printf ( "skipselevting L\n" );
396       } else {
397         if ( col - ui_catshift == 0 ) {
398           s = g_imagecache [ IMG_TAB_LINEL ].i;
399           printf ( "selevting L\n" );
400         } else if ( col - ui_catshift == maxtab - 1 ) {
401           s = g_imagecache [ IMG_TAB_LINER ].i;
402           printf ( "selevting R\n" );
403         } else {
404           s = g_imagecache [ IMG_TAB_LINE ].i;
405           printf ( "selevting M\n" );
406         }
407         dest -> x = tab_offset_x + ( (col-ui_catshift) * tab_width );
408         dest -> y = tab_offset_y + tab_height;
409         SDL_BlitSurface ( s, NULL /* whole image */, sdl_realscreen, dest );
410         dest++;
411       }
412
413       // draw text
414       SDL_Surface *rtext;
415       SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
416       rtext = TTF_RenderText_Blended ( g_tab_font, g_categories [ col ].catname, tmpfontcolor );
417       src.x = 0;
418       src.y = 0;
419       src.w = rtext -> w < text_width ? rtext -> w : text_width;
420       src.h = rtext -> h;
421       dest -> x = tab_offset_x + ( (col-ui_catshift) * tab_width ) + text_offset_x;
422       dest -> y = tab_offset_y + text_offset_y;
423       SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
424       SDL_FreeSurface ( rtext );
425       dest++;
426
427     } // for
428
429   } // tabs
430
431   // scroll bars and arrows
432   {
433     unsigned char show_bar = 0;
434
435     // up?
436     if ( ui_rows_scrolled_down && g_imagecache [ IMG_ARROW_UP ].i ) {
437       dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_x", 450 );
438       dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_y", 80 );
439       SDL_BlitSurface ( g_imagecache [ IMG_ARROW_UP ].i, NULL /* whole image */, sdl_realscreen, dest );
440       dest++;
441
442       show_bar = 1;
443     }
444
445     // down?
446     if ( ui_rows_scrolled_down + row_max < icon_rows && g_imagecache [ IMG_ARROW_DOWN ].i ) {
447       dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_x", 450 );
448       dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_y", 80 );
449       SDL_BlitSurface ( g_imagecache [ IMG_ARROW_DOWN ].i, NULL /* whole image */, sdl_realscreen, dest );
450       dest++;
451
452       show_bar = 1;
453     }
454
455     if ( show_bar ) {
456       // show scrollbar as well
457       src.x = 0;
458       src.y = 0;
459       src.w = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_w", 10 );
460       src.h = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_h", 100 );
461       dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_x", 450 );
462       dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_y", 100 );
463       SDL_BlitSurface ( g_imagecache [ IMG_ARROW_SCROLLBAR ].i, &src /* whole image */, sdl_realscreen, dest );
464       dest++;
465     } // bar
466
467   } // scroll bars
468
469   // render detail pane bg
470   if ( pnd_conf_get_as_int_d ( g_conf, "detailpane.show", 1 ) ) {
471
472     if ( g_imagecache [ IMG_DETAIL_BG ].i ) {
473       src.x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
474       src.y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
475       src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> w;
476       src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> h;
477       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
478       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
479       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
480       dest++;
481     }
482
483     // render detail pane
484     if ( g_imagecache [ IMG_DETAIL_PANEL ].i ) {
485       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
486       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
487       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_PANEL ].i, NULL /* whole image */, sdl_realscreen, dest );
488       dest++;
489     }
490
491   } // detailpane frame/bg
492
493   // anything to render?
494   if ( g_categories [ ui_category ].refs ) {
495
496     appiter = g_categories [ ui_category ].refs;
497     row = 0;
498     displayrow = 0;
499
500     // until we run out of apps, or run out of space
501     while ( appiter != NULL ) {
502
503       for ( col = 0; col < col_max && appiter != NULL; col++ ) {
504
505         // do we even need to render it? or are we suppressing it due to rows scrolled off the top?
506         if ( row >= ui_rows_scrolled_down ) {
507
508           // selected? show hilights
509           if ( appiter == ui_selected ) {
510             // icon
511             dest -> x = grid_offset_x + ( col * cell_width ) + icon_offset_x;
512             dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y;
513             SDL_BlitSurface ( g_imagecache [ IMG_SELECTED_ALPHAMASK ].i, NULL /* all */, sdl_realscreen, dest );
514             dest++;
515             // text
516             dest -> x = grid_offset_x + ( col * cell_width ) + text_clip_x;
517             dest -> y = grid_offset_y + ( displayrow * cell_height ) + pnd_conf_get_as_int ( g_conf, "grid.text_hilite_offset_y" );
518             SDL_BlitSurface ( g_imagecache [ IMG_SELECTED_HILITE ].i, NULL /* all */, sdl_realscreen, dest );
519             dest++;
520           } // selected?
521
522           // show icon
523           mm_cache_t *ic = cache_query_icon ( appiter -> ref -> unique_id );
524           SDL_Surface *iconsurface;
525           if ( ic ) {
526             iconsurface = ic -> i;
527           } else {
528             //pnd_log ( pndn_warning, "WARNING: TBD: Need Missin-icon icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") );
529             iconsurface = g_imagecache [ IMG_ICON_MISSING ].i;
530           }
531           if ( iconsurface ) {
532             //pnd_log ( pndn_debug, "Got an icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") );
533
534             src.x = 0;
535             src.y = 0;
536             src.w = 60;
537             src.h = 60;
538             dest -> x = grid_offset_x + ( col * cell_width ) + icon_offset_x + (( icon_max_width - iconsurface -> w ) / 2);
539             dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y;
540
541             SDL_BlitSurface ( iconsurface, &src, sdl_realscreen, dest );
542
543             // store touch info
544             ui_register_app ( appiter, dest -> x, dest -> y, src.w, src.h );
545
546             dest++;
547
548           }
549
550           // show text
551           if ( appiter -> ref -> title_en ) {
552             SDL_Surface *rtext;
553             SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
554             rtext = TTF_RenderText_Blended ( g_grid_font, appiter -> ref -> title_en, tmpfontcolor );
555             src.x = 0;
556             src.y = 0;
557             src.w = text_width < rtext -> w ? text_width : rtext -> w;
558             src.h = rtext -> h;
559             if ( rtext -> w > text_width ) {
560               dest -> x = grid_offset_x + ( col * cell_width ) + text_clip_x;
561             } else {
562               dest -> x = grid_offset_x + ( col * cell_width ) + text_offset_x - ( rtext -> w / 2 );
563             }
564             dest -> y = grid_offset_y + ( displayrow * cell_height ) + text_offset_y;
565             SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
566             SDL_FreeSurface ( rtext );
567             dest++;
568           }
569
570         } // display now? or scrolled away..
571
572         // next
573         appiter = appiter -> next;
574
575       } // for column 1...X
576
577       if ( row >= ui_rows_scrolled_down ) {
578         displayrow++;
579       }
580
581       row ++;
582
583       // are we done displaying rows?
584       if ( displayrow >= row_max ) {
585         break;
586       }
587
588     } // while
589
590   } else {
591     // no apps to render?
592     pnd_log ( pndn_rem, "No applications to render?\n" );
593   } // apps to renser?
594
595   // detail panel
596   if ( ui_selected ) {
597
598     unsigned int cell_offset_x = pnd_conf_get_as_int ( g_conf, "detailtext.cell_offset_x" );
599     unsigned int cell_offset_y = pnd_conf_get_as_int ( g_conf, "detailtext.cell_offset_y" );
600     unsigned int cell_width = pnd_conf_get_as_int ( g_conf, "detailtext.cell_width" );
601
602     unsigned int desty = cell_offset_y;
603
604     char buffer [ 256 ];
605
606     // full name
607     if ( ui_selected -> ref -> title_en ) {
608       SDL_Surface *rtext;
609       SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
610       rtext = TTF_RenderText_Blended ( g_detailtext_font, ui_selected -> ref -> title_en, tmpfontcolor );
611       src.x = 0;
612       src.y = 0;
613       src.w = rtext -> w < cell_width ? rtext -> w : cell_width;
614       src.h = rtext -> h;
615       dest -> x = cell_offset_x;
616       dest -> y = desty;
617       SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
618       SDL_FreeSurface ( rtext );
619       dest++;
620       desty += src.h;
621     }
622
623     // category
624     if ( ui_selected -> ref -> main_category ) {
625
626       sprintf ( buffer, "Category: %s", ui_selected -> ref -> main_category );
627
628       SDL_Surface *rtext;
629       SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
630       rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, tmpfontcolor );
631       src.x = 0;
632       src.y = 0;
633       src.w = rtext -> w < cell_width ? rtext -> w : cell_width;
634       src.h = rtext -> h;
635       dest -> x = cell_offset_x;
636       dest -> y = desty;
637       SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
638       SDL_FreeSurface ( rtext );
639       dest++;
640       desty += src.h;
641     }
642
643     // clock
644     if ( ui_selected -> ref -> clockspeed ) {
645
646       sprintf ( buffer, "CPU Clock: %s", ui_selected -> ref -> clockspeed );
647
648       SDL_Surface *rtext;
649       SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
650       rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, tmpfontcolor );
651       src.x = 0;
652       src.y = 0;
653       src.w = rtext -> w < cell_width ? rtext -> w : cell_width;
654       src.h = rtext -> h;
655       dest -> x = cell_offset_x;
656       dest -> y = desty;
657       SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
658       SDL_FreeSurface ( rtext );
659       dest++;
660       desty += src.h;
661     }
662
663     // info hint
664     if ( ui_selected -> ref -> clockspeed && ui_selected -> ref -> info_filename ) {
665
666       sprintf ( buffer, "Documentation - hit Y" );
667
668       SDL_Surface *rtext;
669       SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
670       rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, tmpfontcolor );
671       src.x = 0;
672       src.y = 0;
673       src.w = rtext -> w < cell_width ? rtext -> w : cell_width;
674       src.h = rtext -> h;
675       dest -> x = cell_offset_x;
676       dest -> y = desty;
677       SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
678       SDL_FreeSurface ( rtext );
679       dest++;
680       desty += src.h;
681     }
682
683     // preview pic
684     mm_cache_t *ic = cache_query_preview ( ui_selected -> ref -> unique_id );
685     SDL_Surface *previewpic;
686
687     if ( ic ) {
688       previewpic = ic -> i;
689     } else {
690       previewpic = g_imagecache [ IMG_PREVIEW_MISSING ].i;
691     }
692
693     if ( previewpic ) {
694       dest -> x = pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_offset_x", 50 ) +
695         ( ( pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 50 ) - previewpic -> w ) / 2 );
696       dest -> y = pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_offset_y", 50 );
697       SDL_BlitSurface ( previewpic, NULL /* whole image */, sdl_realscreen, dest );
698       dest++;
699     }
700
701   } // selected?
702
703   // extras
704   //
705
706   // battery
707   if ( 1 ) {
708     static int last_battlevel = 0;
709     static unsigned char batterylevel = 0;
710     char buffer [ 100 ];
711
712     if ( time ( NULL ) - last_battlevel > 60 ) {
713       batterylevel = pnd_device_get_battery_gauge_perc();
714       last_battlevel = time ( NULL );
715     }
716
717     sprintf ( buffer, "Battery: %u%%", batterylevel );
718
719     SDL_Surface *rtext;
720     SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
721     rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor );
722     dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.battery_x", 20 );
723     dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.battery_y", 450 );
724     SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
725     SDL_FreeSurface ( rtext );
726     dest++;
727   }
728
729   // hints
730   if ( pnd_conf_get_as_char ( g_conf, "display.hintline" ) ) {
731     char *buffer = pnd_conf_get_as_char ( g_conf, "display.hintline" );
732     SDL_Surface *rtext;
733     SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
734     rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor );
735     dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.hint_x", 40 );
736     dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.hint_y", 450 );
737     SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
738     SDL_FreeSurface ( rtext );
739     dest++;
740   }
741
742   // hints
743   if ( pnd_conf_get_as_int_d ( g_conf, "display.clock_x", -1 ) != -1 ) {
744     char buffer [ 50 ];
745
746     time_t t = time ( NULL );
747     struct tm *tm = localtime ( &t );
748     strftime ( buffer, 50, "%a %H:%M %F", tm );
749
750     SDL_Surface *rtext;
751     SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
752     rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor );
753     dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.clock_x", 700 );
754     dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.clock_y", 450 );
755     SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
756     SDL_FreeSurface ( rtext );
757     dest++;
758   }
759
760   // update all the rects and send it all to sdl
761   SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
762
763 } // ui_render
764
765 void ui_process_input ( unsigned char block_p ) {
766   SDL_Event event;
767
768   unsigned char ui_event = 0; // if we get a ui event, flip to 1 and break
769   //static ui_sdl_button_e ui_mask = uisb_none; // current buttons down
770
771   while ( ! ui_event &&
772           block_p ? SDL_WaitEvent ( &event ) : SDL_PollEvent ( &event ) )
773   {
774
775     switch ( event.type ) {
776
777     case SDL_USEREVENT:
778       // update something
779
780       if ( event.user.code == sdl_user_ticker ) {
781
782         // timer went off, time to load something
783         if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) {
784
785           pnd_log ( pndn_debug, "Deferred preview pic load ----------\n" );
786
787           // load the preview pics now!
788           pnd_disco_t *iter = ui_selected -> ref;
789
790           if ( iter -> preview_pic1 ) {
791
792             if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.threaded_preview", 0 ) ) {
793
794               g_preview_thread = SDL_CreateThread ( (void*)ui_threaded_defered_preview, iter );
795
796               if ( ! g_preview_thread ) {
797                 pnd_log ( pndn_error, "ERROR: Couldn't create preview thread\n" );
798               }
799
800             } else {
801
802               if ( ! cache_preview ( iter, pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 200 ),
803                                      pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_height", 180 ) )
804                  )
805               {
806                 pnd_log ( pndn_debug, "  Couldn't load preview pic: '%s' -> '%s'\n",
807                           IFNULL(iter->title_en,"No Name"), iter -> preview_pic1 );
808               }
809
810             } // threaded?
811
812           } // got a preview at all?
813
814           pnd_log ( pndn_debug, "Deferred preview pic load finish ---\n" );
815
816           ui_event++;
817         }
818
819       } else if ( event.user.code == sdl_user_finishedpreview ) {
820
821         // if we just finished the one we happen to be looking at, better redraw now; otherwise, if
822         // we finished another, no big woop
823         if ( ui_selected && event.user.data1 == ui_selected -> ref ) {
824           ui_event++;
825         }
826
827       }
828
829       break;
830
831 #if 0 // joystick motion
832     case SDL_JOYAXISMOTION:
833
834       pnd_log ( PND_LOG_DEFAULT, "joystick axis\n" );
835
836         if ( event.jaxis.axis == 0 ) {
837           // horiz
838           if ( event.jaxis.value < 0 ) {
839             ui_push_left ( 0 );
840             pnd_log ( PND_LOG_DEFAULT, "joystick axis - LEFT\n" );
841           } else if ( event.jaxis.value > 0 ) {
842             ui_push_right ( 0 );
843             pnd_log ( PND_LOG_DEFAULT, "joystick axis - RIGHT\n" );
844           }
845         } else if ( event.jaxis.axis == 1 ) {
846           // vert
847           if ( event.jaxis.value < 0 ) {
848             ui_push_up();
849           } else if ( event.jaxis.value > 0 ) {
850             ui_push_down();
851           }
852         }
853
854         ui_event++;
855
856         break;
857 #endif
858
859 #if 0 // joystick buttons
860     case SDL_JOYBUTTONDOWN:
861
862       pnd_log ( PND_LOG_DEFAULT, "joystick button down %u\n", event.jbutton.button );
863
864       if ( event.jbutton.button == 0 ) { // B
865         ui_mask |= uisb_b;
866       } else if ( event.jbutton.button == 1 ) { // Y
867         ui_mask |= uisb_y;
868       } else if ( event.jbutton.button == 2 ) { // X
869         ui_mask |= uisb_x;
870       } else if ( event.jbutton.button == 3 ) { // A
871         ui_mask |= uisb_a;
872
873       } else if ( event.jbutton.button == 4 ) { // Select
874         ui_mask |= uisb_select;
875       } else if ( event.jbutton.button == 5 ) { // Start
876         ui_mask |= uisb_start;
877
878       } else if ( event.jbutton.button == 7 ) { // L
879         ui_mask |= uisb_l;
880       } else if ( event.jbutton.button == 8 ) { // R
881         ui_mask |= uisb_r;
882
883       }
884
885       ui_event++;
886
887       break;
888
889     case SDL_JOYBUTTONUP:
890
891       pnd_log ( PND_LOG_DEFAULT, "joystick button up %u\n", event.jbutton.button );
892
893       if ( event.jbutton.button == 0 ) { // B
894         ui_mask &= ~uisb_b;
895         ui_push_exec();
896       } else if ( event.jbutton.button == 1 ) { // Y
897         ui_mask &= ~uisb_y;
898       } else if ( event.jbutton.button == 2 ) { // X
899         ui_mask &= ~uisb_x;
900       } else if ( event.jbutton.button == 3 ) { // A
901         ui_mask &= ~uisb_a;
902
903       } else if ( event.jbutton.button == 4 ) { // Select
904         ui_mask &= ~uisb_select;
905       } else if ( event.jbutton.button == 5 ) { // Start
906         ui_mask &= ~uisb_start;
907
908       } else if ( event.jbutton.button == 7 ) { // L
909         ui_mask &= ~uisb_l;
910         ui_push_ltrigger();
911       } else if ( event.jbutton.button == 8 ) { // R
912         ui_mask &= ~uisb_r;
913         ui_push_rtrigger();
914
915       }
916
917       ui_event++;
918
919       break;
920 #endif
921
922 #if 1 // keyboard events
923     case SDL_KEYUP:
924
925       //pnd_log ( pndn_debug, "key up %u\n", event.key.keysym.sym );
926
927       // SDLK_LALT -> Start
928
929       // directional
930       if ( event.key.keysym.sym == SDLK_RIGHT ) {
931         ui_push_right ( 0 );
932         ui_event++;
933       } else if ( event.key.keysym.sym == SDLK_LEFT ) {
934         ui_push_left ( 0 );
935         ui_event++;
936       } else if ( event.key.keysym.sym == SDLK_UP ) {
937         ui_push_up();
938         ui_event++;
939       } else if ( event.key.keysym.sym == SDLK_DOWN ) {
940         ui_push_down();
941         ui_event++;
942       } else if ( event.key.keysym.sym == SDLK_SPACE || event.key.keysym.sym == SDLK_END ) {
943         ui_push_exec();
944         ui_event++;
945       } else if ( event.key.keysym.sym == SDLK_z || event.key.keysym.sym == SDLK_RSHIFT ) {
946         ui_push_ltrigger();
947         ui_event++;
948       } else if ( event.key.keysym.sym == SDLK_x || event.key.keysym.sym == SDLK_RCTRL ) {
949         ui_push_rtrigger();
950         ui_event++;
951       } else if ( event.key.keysym.sym == SDLK_y || event.key.keysym.sym == SDLK_PAGEUP ) {
952         // info
953         if ( ui_selected ) {
954           ui_show_info ( pnd_run_script, ui_selected -> ref );
955           ui_event++;
956         }
957
958       } else if ( event.key.keysym.sym == SDLK_LALT ) { // start button
959         ui_push_exec();
960         ui_event++;
961
962       } else if ( event.key.keysym.sym == SDLK_LCTRL /*LALT*/ ) { // select button
963         char *opts [ 20 ] = {
964           "Return to Minimenu",
965           "Shutdown Pandora",
966           "Rescan for Applications",
967           "Run xfce4 from Minimenu",
968           "Run a terminal/console",
969           "Exit and run xfce4",
970           "Exit and run pmenu",
971           "Quit (<- beware)",
972           "About Minimenu"
973         };
974         int sel = ui_modal_single_menu ( opts, 9, "Minimenu", "Enter to select; other to return." );
975
976         char buffer [ 100 ];
977         if ( sel == 0 ) {
978           // do nothing
979         } else if ( sel == 1 ) {
980           // shutdown
981           sprintf ( buffer, "sudo poweroff" );
982           system ( buffer );
983         } else if ( sel == 2 ) {
984           // rescan apps
985           pnd_log ( pndn_debug, "Freeing up applications\n" );
986           applications_free();
987           pnd_log ( pndn_debug, "Rescanning applications\n" );
988           applications_scan();
989           // reset view
990           ui_selected = NULL;
991           ui_rows_scrolled_down = 0;
992         } else if ( sel == 3 ) {
993           // run xfce
994           char buffer [ PATH_MAX ];
995           sprintf ( buffer, "%s %s\n", MM_RUN, "/usr/bin/startxfce4" );
996           emit_and_quit ( buffer );
997         } else if ( sel == 4 ) {
998           // run terminal
999           char *argv[5];
1000           argv [ 0 ] = pnd_conf_get_as_char ( g_conf, "utility.terminal" );
1001           argv [ 1 ] = NULL;
1002
1003           if ( argv [ 0 ] ) {
1004             ui_forkexec ( argv );
1005           }
1006
1007         } else if ( sel == 5 ) {
1008           // set env to xfce
1009           sprintf ( buffer, "echo startxfce4 > /tmp/gui.load" );
1010           system ( buffer );
1011           //sprintf ( buffer, "sudo poweroff" );
1012           //system ( buffer );
1013           exit ( 0 );
1014         } else if ( sel == 6 ) {
1015           // set env to pmenu
1016           sprintf ( buffer, "echo pmenu > /tmp/gui.load" );
1017           system ( buffer );
1018           //sprintf ( buffer, "sudo poweroff" );
1019           //system ( buffer );
1020           exit ( 0 );
1021         } else if ( sel == 7 ) {
1022           emit_and_quit ( MM_QUIT );
1023         } else if ( sel == 8 ) {
1024           // about
1025         }
1026
1027         ui_event++;
1028       }
1029
1030       // extras
1031       if ( event.key.keysym.sym == SDLK_q ) {
1032         emit_and_quit ( MM_QUIT );
1033       }
1034
1035       break;
1036 #endif
1037
1038 #if 1 // mouse / touchscreen
1039 #if 0
1040     case SDL_MOUSEBUTTONDOWN:
1041       if ( event.button.button == SDL_BUTTON_LEFT ) {
1042         cb_pointer_press ( gc, event.button.x / g_scale, event.button.y / g_scale );
1043         ui_event++;
1044       }
1045       break;
1046 #endif
1047
1048     case SDL_MOUSEBUTTONUP:
1049       if ( event.button.button == SDL_BUTTON_LEFT ) {
1050         ui_touch_act ( event.button.x, event.button.y );
1051         ui_event++;
1052       }
1053       break;
1054 #endif
1055
1056     case SDL_QUIT:
1057       exit ( 0 );
1058       break;
1059
1060     default:
1061       break;
1062
1063     } // switch event type
1064
1065   } // while poll
1066
1067   return;
1068 }
1069
1070 void ui_push_left ( unsigned char forcecoil ) {
1071
1072   if ( ! ui_selected ) {
1073     ui_push_right ( 0 );
1074     return;
1075   }
1076
1077   // what column we in?
1078   unsigned int col = ui_determine_screen_col ( ui_selected );
1079
1080   // are we alreadt at first item?
1081   if ( forcecoil == 0 &&
1082        pnd_conf_get_as_int_d ( g_conf, "grid.wrap_horiz_samerow", 0 ) &&
1083        col == 0 )
1084   {
1085     unsigned int i = pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1;
1086     while ( i ) {
1087       ui_push_right ( 0 );
1088       i--;
1089     }
1090
1091   } else if ( g_categories [ ui_category ].refs == ui_selected ) {
1092     // can't go any more left, we're at the head
1093
1094   } else {
1095     // figure out the previous item; yay for singly linked list :/
1096     mm_appref_t *i = g_categories [ ui_category ].refs;
1097     while ( i ) {
1098       if ( i -> next == ui_selected ) {
1099         ui_selected = i;
1100         break;
1101       }
1102       i = i -> next;
1103     }
1104   }
1105
1106   ui_set_selected ( ui_selected );
1107
1108   return;
1109 }
1110
1111 void ui_push_right ( unsigned char forcecoil ) {
1112
1113   if ( ui_selected ) {
1114
1115     // what column we in?
1116     unsigned int col = ui_determine_screen_col ( ui_selected );
1117
1118     // wrap same or no?
1119     if ( forcecoil == 0 &&
1120          pnd_conf_get_as_int_d ( g_conf, "grid.wrap_horiz_samerow", 0 ) &&
1121          col == pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1 )
1122     {
1123       // same wrap
1124       unsigned int i = pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1;
1125       while ( i ) {
1126         ui_push_left ( 0 );
1127         i--;
1128       }
1129
1130     } else {
1131       // just go to the next
1132
1133       if ( ui_selected -> next ) {
1134         ui_selected = ui_selected -> next;
1135       }
1136
1137     }
1138
1139   } else {
1140     ui_selected = g_categories [ ui_category ].refs;
1141   }
1142
1143   ui_set_selected ( ui_selected );
1144
1145   return;
1146 }
1147
1148 void ui_push_up ( void ) {
1149   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
1150
1151   if ( ! ui_selected ) {
1152     return;
1153   }
1154
1155   // what row we in?
1156   unsigned int row = ui_determine_row ( ui_selected );
1157
1158   if ( row == 0 &&
1159        pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 1 ) == 0 )
1160   {
1161     // wrap around instead
1162
1163     unsigned int col = ui_determine_screen_col ( ui_selected );
1164
1165     // go to end
1166     ui_selected = g_categories [ ui_category ].refs;
1167     while ( ui_selected -> next ) {
1168       ui_selected = ui_selected -> next;
1169     }
1170
1171     // try to move to same column
1172     unsigned int newcol = ui_determine_screen_col ( ui_selected );
1173     if ( newcol > col ) {
1174       col = newcol - col;
1175       while ( col ) {
1176         ui_push_left ( 0 );
1177         col--;
1178       }
1179     }
1180
1181     // scroll down to show it
1182     int r = ui_determine_row ( ui_selected ) - 1;
1183     if ( r - pnd_conf_get_as_int ( g_conf, MMENU_DISP_ROWMAX ) > 0 ) {
1184       ui_rows_scrolled_down = (unsigned int) r;
1185     }
1186
1187   } else {
1188     // stop at top/bottom
1189
1190     while ( col_max ) {
1191       ui_push_left ( 1 );
1192       col_max--;
1193     }
1194
1195   }
1196
1197   return;
1198 }
1199
1200 void ui_push_down ( void ) {
1201   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
1202
1203   if ( ui_selected ) {
1204
1205     // what row we in?
1206     unsigned int row = ui_determine_row ( ui_selected );
1207
1208     // max rows?
1209     unsigned int icon_rows = g_categories [ ui_category ].refcount / col_max;
1210     if ( g_categories [ ui_category ].refcount % col_max > 0 ) {
1211       icon_rows++;
1212     }
1213
1214     // we at the end?
1215     if ( row == ( icon_rows - 1 ) &&
1216          pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 1 ) == 0 )
1217     {
1218
1219       unsigned char col = ui_determine_screen_col ( ui_selected );
1220
1221       ui_selected = g_categories [ ui_category ].refs;
1222
1223       while ( col ) {
1224         ui_selected = ui_selected -> next;
1225         col--;
1226       }
1227
1228       ui_rows_scrolled_down = 0;
1229
1230     } else {
1231
1232       while ( col_max ) {
1233         ui_push_right ( 1 );
1234         col_max--;
1235       }
1236
1237     }
1238
1239   } else {
1240     ui_push_right ( 0 );
1241   }
1242
1243   return;
1244 }
1245
1246 void ui_push_exec ( void ) {
1247
1248   if ( ui_selected ) {
1249     char buffer [ PATH_MAX ];
1250     sprintf ( buffer, "%s/%s", ui_selected -> ref -> object_path, ui_selected -> ref -> object_filename );
1251     pnd_apps_exec ( pnd_run_script,
1252                     buffer,
1253                     ui_selected -> ref -> unique_id,
1254                     ui_selected -> ref -> exec,
1255                     ui_selected -> ref -> startdir,
1256                     ui_selected -> ref -> execargs,
1257                     atoi ( ui_selected -> ref -> clockspeed ),
1258                     PND_EXEC_OPTION_NORUN );
1259     sprintf ( buffer, "%s %s\n", MM_RUN, pnd_apps_exec_runline() );
1260     emit_and_quit ( buffer );
1261   }
1262
1263   return;
1264 }
1265
1266 void ui_push_ltrigger ( void ) {
1267   unsigned char oldcat = ui_category;
1268
1269   if ( ui_category > 0 ) {
1270     ui_category--;
1271   } else {
1272     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1273       ui_category = g_categorycount - 1;
1274     }
1275   }
1276
1277   if ( oldcat != ui_category ) {
1278     ui_selected = NULL;
1279     ui_set_selected ( ui_selected );
1280   }
1281
1282   // make tab visible?
1283   if ( ui_catshift > 0 && ui_category == ui_catshift - 1 ) {
1284     ui_catshift--;
1285   }
1286
1287   // unscroll
1288   ui_rows_scrolled_down = 0;
1289
1290   return;
1291 }
1292
1293 void ui_push_rtrigger ( void ) {
1294   unsigned char oldcat = ui_category;
1295
1296   unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 );
1297   unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
1298
1299   if ( ui_category < ( g_categorycount - 1 ) ) {
1300     ui_category++;
1301   } else {
1302     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1303       ui_category = 0;
1304     }
1305   }
1306
1307   if ( oldcat != ui_category ) {
1308     ui_selected = NULL;
1309     ui_set_selected ( ui_selected );
1310   }
1311
1312   // make tab visible?
1313   if ( ui_category > ui_catshift + ( screen_width / tab_width ) - 1 ) {
1314     ui_catshift++;
1315   }
1316
1317   // unscroll
1318   ui_rows_scrolled_down = 0;
1319
1320   return;
1321 }
1322
1323 SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ) {
1324   double scale = 1000000.0;
1325   double scalex = 1000000.0;
1326   double scaley = 1000000.0;
1327   SDL_Surface *scaled;
1328
1329   scalex = (double)maxwidth / (double)s -> w;
1330
1331   if ( maxheight == -1 ) {
1332     scale = scalex;
1333   } else {
1334     scaley = (double)maxheight / (double)s -> h;
1335
1336     if ( scaley < scalex ) {
1337       scale = scaley;
1338     } else {
1339       scale = scalex;
1340     }
1341
1342   }
1343
1344   scaled = rotozoomSurface ( s, 0 /* angle*/, scale /* scale */, 1 /* smooth==1*/ );
1345   SDL_FreeSurface ( s );
1346   s = scaled;
1347
1348   return ( s );
1349 }
1350
1351 void ui_loadscreen ( void ) {
1352
1353   SDL_Rect dest;
1354
1355   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1356   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1357   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1358   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1359
1360   // clear the screen
1361   SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1362
1363   // render text
1364   SDL_Surface *rtext;
1365   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1366   rtext = TTF_RenderText_Blended ( g_big_font, "Setting up menu...", tmpfontcolor );
1367   dest.x = 20;
1368   dest.y = 20;
1369   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1370   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1371   SDL_FreeSurface ( rtext );
1372
1373   return;
1374 }
1375
1376 void ui_discoverscreen ( unsigned char clearscreen ) {
1377
1378   SDL_Rect dest;
1379
1380   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1381   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1382   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1383   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1384
1385   // clear the screen
1386   if ( clearscreen ) {
1387     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1388
1389     // render background
1390     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1391       dest.x = 0;
1392       dest.y = 0;
1393       dest.w = sdl_realscreen -> w;
1394       dest.h = sdl_realscreen -> h;
1395       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1396       SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1397     }
1398
1399   }
1400
1401   // render text
1402   SDL_Surface *rtext;
1403   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1404   rtext = TTF_RenderText_Blended ( g_big_font, "Looking for applications...", tmpfontcolor );
1405   if ( clearscreen ) {
1406     dest.x = 20;
1407     dest.y = 20;
1408   } else {
1409     dest.x = 20;
1410     dest.y = 40;
1411   }
1412   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1413   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1414   SDL_FreeSurface ( rtext );
1415
1416   // render icon
1417   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1418     dest.x = rtext -> w + 30;
1419     dest.y = 20;
1420     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, &dest );
1421     SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1422   }
1423
1424   return;
1425 }
1426
1427 void ui_cachescreen ( unsigned char clearscreen, char *filename ) {
1428
1429   SDL_Rect rects [ 4 ];
1430   SDL_Rect *dest = rects;
1431   SDL_Rect src;
1432   bzero ( dest, sizeof(SDL_Rect)* 4 );
1433
1434   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1435   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1436   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1437   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1438
1439   static unsigned int stepx = 0;
1440
1441   // clear the screen
1442   if ( clearscreen ) {
1443     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1444
1445     // render background
1446     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1447       dest -> x = 0;
1448       dest -> y = 0;
1449       dest -> w = sdl_realscreen -> w;
1450       dest -> h = sdl_realscreen -> h;
1451       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1452       dest++;
1453     }
1454
1455   } else {
1456
1457     // render background
1458     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1459       src.x = 0;
1460       src.y = 0;
1461       src.w = sdl_realscreen -> w;
1462       src.h = 100;
1463       dest -> x = 0;
1464       dest -> y = 0;
1465       dest -> w = sdl_realscreen -> w;
1466       dest -> h = sdl_realscreen -> h;
1467       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, &src, sdl_realscreen, dest );
1468       dest++;
1469     }
1470
1471   } // clear it
1472
1473   // render text
1474   SDL_Surface *rtext;
1475   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1476   rtext = TTF_RenderText_Blended ( g_big_font, "Caching applications artwork...", tmpfontcolor );
1477   dest -> x = 20;
1478   dest -> y = 20;
1479   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1480   SDL_FreeSurface ( rtext );
1481   dest++;
1482
1483   // render icon
1484   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1485     dest -> x = rtext -> w + 30 + stepx;
1486     dest -> y = 20;
1487     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, dest );
1488     dest++;
1489   }
1490
1491   // filename
1492   if ( filename ) {
1493     rtext = TTF_RenderText_Blended ( g_tab_font, filename, tmpfontcolor );
1494     dest -> x = 20;
1495     dest -> y = 50;
1496     SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1497     SDL_FreeSurface ( rtext );
1498     dest++;
1499   }
1500
1501   // move across
1502   stepx += 20;
1503
1504   if ( stepx > 350 ) {
1505     stepx = 0;
1506   }
1507
1508   SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1509
1510   return;
1511 }
1512
1513 int ui_selected_index ( void ) {
1514
1515   if ( ! ui_selected ) {
1516     return ( -1 ); // no index
1517   }
1518
1519   mm_appref_t *r = g_categories [ ui_category ].refs;
1520   int counter = 0;
1521   while ( r ) {
1522     if ( r == ui_selected ) {
1523       return ( counter );
1524     }
1525     r = r -> next;
1526     counter++;
1527   }
1528
1529   return ( -1 );
1530 }
1531
1532 static mm_appref_t *timer_ref = NULL;
1533 void ui_set_selected ( mm_appref_t *r ) {
1534
1535   if ( ! pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) {
1536     return; // no desire to defer anything
1537   }
1538
1539   if ( ! r ) {
1540     // cancel timer
1541     SDL_SetTimer ( 0, NULL );
1542     timer_ref = NULL;
1543     return;
1544   }
1545
1546   SDL_SetTimer ( pnd_conf_get_as_int_d ( g_conf, "previewpic.defer_timer_ms", 1000 ), ui_callback_f );
1547   timer_ref = r;
1548
1549   return;
1550 }
1551
1552 unsigned int ui_callback_f ( unsigned int t ) {
1553
1554   if ( ui_selected != timer_ref ) {
1555     return ( 0 ); // user has moved it, who cares
1556   }
1557
1558   SDL_Event e;
1559   bzero ( &e, sizeof(SDL_Event) );
1560   e.type = SDL_USEREVENT;
1561   e.user.code = sdl_user_ticker;
1562   SDL_PushEvent ( &e );
1563
1564   return ( 0 );
1565 }
1566
1567 int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *footer ) {
1568   SDL_Rect rects [ 40 ];
1569   SDL_Rect *dest = rects;
1570   SDL_Rect src;
1571   SDL_Surface *rtext;
1572
1573   bzero ( rects, sizeof(SDL_Rect) * 40 );
1574
1575   unsigned int sel = 0;
1576
1577   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1578   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1579   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1580   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1581
1582   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1583
1584   SDL_Color selfontcolor = { 0/*font_rgba_r*/, font_rgba_g, font_rgba_b, font_rgba_a };
1585
1586   unsigned int i;
1587   SDL_Event event;
1588
1589   while ( 1 ) {
1590
1591     // clear
1592     dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1593     dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1594     dest -> w = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> w;
1595     dest -> h = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h;
1596     SDL_FillRect( sdl_realscreen, dest, 0 );
1597
1598     // show dialog background
1599     if ( g_imagecache [ IMG_DETAIL_BG ].i ) {
1600       src.x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1601       src.y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1602       src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> w;
1603       src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> h;
1604       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1605       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1606       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1607       // repeat for darken?
1608       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1609       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1610       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1611       dest++;
1612     }
1613
1614     // show dialog frame
1615     if ( g_imagecache [ IMG_DETAIL_PANEL ].i ) {
1616       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1617       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1618       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_PANEL ].i, NULL /* whole image */, sdl_realscreen, dest );
1619       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1620       dest++;
1621     }
1622
1623     // show header
1624     if ( title ) {
1625       rtext = TTF_RenderText_Blended ( g_tab_font, title, tmpfontcolor );
1626       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1627       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 20;
1628       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1629       SDL_FreeSurface ( rtext );
1630       dest++;
1631     }
1632
1633     // show footer
1634     if ( footer ) {
1635       rtext = TTF_RenderText_Blended ( g_tab_font, footer, tmpfontcolor );
1636       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1637       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) +
1638         ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h
1639         - 60;
1640       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1641       SDL_FreeSurface ( rtext );
1642       dest++;
1643     }
1644
1645     // show options
1646     for ( i = 0; i < argc; i++ ) {
1647
1648       // show options
1649       if ( sel == i ) {
1650         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], selfontcolor );
1651       } else {
1652         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], tmpfontcolor );
1653       }
1654       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1655       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 40 + ( 20 * ( i + 1 ) );
1656       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1657       SDL_FreeSurface ( rtext );
1658       dest++;
1659
1660     } // for
1661
1662     // update all the rects and send it all to sdl
1663     SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1664     dest = rects;
1665
1666     // check for input
1667     while ( SDL_WaitEvent ( &event ) ) {
1668
1669       switch ( event.type ) {
1670
1671       case SDL_KEYUP:
1672
1673         if ( event.key.keysym.sym == SDLK_UP ) {
1674           if ( sel ) {
1675             sel--;
1676           }
1677         } else if ( event.key.keysym.sym == SDLK_DOWN ) {
1678           if ( sel < argc - 1 ) {
1679             sel++;
1680           }
1681
1682         } else if ( event.key.keysym.sym == SDLK_RETURN ) {
1683           return ( sel );
1684
1685         } else if ( event.key.keysym.sym == SDLK_q ) {
1686           exit ( 0 );
1687
1688         } else {
1689           return ( -1 ); // nada
1690         }
1691
1692         break;
1693
1694       } // switch
1695
1696       break;
1697     } // while
1698
1699   } // while
1700
1701   return ( -1 );
1702 }
1703
1704 unsigned char ui_determine_row ( mm_appref_t *a ) {
1705   unsigned int row = 0;
1706
1707   mm_appref_t *i = g_categories [ ui_category ].refs;
1708   while ( i != a ) {
1709     i = i -> next;
1710     row++;
1711   } // while
1712   row /= pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 );
1713
1714   return ( row );
1715 }
1716
1717 unsigned char ui_determine_screen_row ( mm_appref_t *a ) {
1718   return ( ui_determine_row ( a ) % pnd_conf_get_as_int_d ( g_conf, "grid.row_max", 5 ) );
1719 }
1720
1721 unsigned char ui_determine_screen_col ( mm_appref_t *a ) {
1722   unsigned int col = 0;
1723
1724   mm_appref_t *i = g_categories [ ui_category ].refs;
1725   while ( i != a ) {
1726     i = i -> next;
1727     col++;
1728   } // while
1729   col %= pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 );
1730
1731   return ( col );
1732 }
1733
1734 unsigned char ui_show_info ( char *pndrun, pnd_disco_t *p ) {
1735   char *viewer, *searchpath;
1736   pnd_conf_handle desktoph;
1737
1738   // viewer
1739   searchpath = pnd_conf_query_searchpath();
1740
1741   desktoph = pnd_conf_fetch_by_id ( pnd_conf_desktop, searchpath );
1742
1743   if ( ! desktoph ) {
1744     return ( 0 );
1745   }
1746
1747   viewer = pnd_conf_get_as_char ( desktoph, "info.viewer" );
1748
1749   if ( ! viewer ) {
1750     return ( 0 ); // no way to view the file
1751   }
1752
1753   // etc
1754   if ( ! p -> unique_id ) {
1755     return ( 0 );
1756   }
1757
1758   if ( ! p -> info_filename ) {
1759     return ( 0 );
1760   }
1761
1762   if ( ! p -> info_name ) {
1763     return ( 0 );
1764   }
1765
1766   if ( ! pndrun ) {
1767     return ( 0 );
1768   }
1769
1770   // exec line
1771   char args [ 1001 ];
1772   char *pargs = args;
1773   if ( pnd_conf_get_as_char ( desktoph, "info.viewer_args" ) ) {
1774     snprintf ( pargs, 1001, "%s %s",
1775                pnd_conf_get_as_char ( desktoph, "info.viewer_args" ), p -> info_filename );
1776   } else {
1777     pargs = NULL;
1778   }
1779
1780   char pndfile [ 1024 ];
1781   if ( p -> object_type == pnd_object_type_directory ) {
1782     // for PXML-app-dir, pnd_run.sh doesn't want the PXML.xml.. it just wants the dir-name
1783     strncpy ( pndfile, p -> object_path, 1000 );
1784   } else if ( p -> object_type == pnd_object_type_pnd ) {
1785     // pnd_run.sh wants the full path and filename for the .pnd file
1786     snprintf ( pndfile, 1020, "%s/%s", p -> object_path, p -> object_filename );
1787   }
1788
1789   if ( ! pnd_apps_exec ( pndrun, pndfile, p -> unique_id, viewer, p -> startdir, pargs,
1790                          p -> clockspeed ? atoi ( p -> clockspeed ) : 0, PND_EXEC_OPTION_NORUN ) )
1791   {
1792     return ( 0 );
1793   }
1794
1795   pnd_log ( pndn_debug, "Info Exec=%s\n", pnd_apps_exec_runline() );
1796
1797   // try running it
1798   int x;
1799   if ( ( x = fork() ) < 0 ) {
1800     pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" );
1801     return ( 0 );
1802   }
1803
1804   if ( x == 0 ) {
1805     execl ( "/bin/sh", "/bin/sh", "-c", pnd_apps_exec_runline(), (char*)NULL );
1806     pnd_log ( pndn_error, "ERROR: Couldn't exec(%s)\n", pnd_apps_exec_runline() );
1807     return ( 0 );
1808   }
1809
1810   return ( 1 );
1811 }
1812
1813 typedef struct {
1814   SDL_Rect r;
1815   int catnum;
1816   mm_appref_t *ref;
1817 } ui_touch_t;
1818 #define MAXTOUCH 100
1819 ui_touch_t ui_touchrects [ MAXTOUCH ];
1820 unsigned char ui_touchrect_count = 0;
1821
1822 void ui_register_reset ( void ) {
1823   bzero ( ui_touchrects, sizeof(ui_touch_t)*MAXTOUCH );
1824   ui_touchrect_count = 0;
1825   return;
1826 }
1827
1828 void ui_register_tab ( unsigned char catnum, unsigned int x, unsigned int y, unsigned int w, unsigned int h ) {
1829
1830   if ( ui_touchrect_count == MAXTOUCH ) {
1831     return;
1832   }
1833
1834   ui_touchrects [ ui_touchrect_count ].r.x = x;
1835   ui_touchrects [ ui_touchrect_count ].r.y = y;
1836   ui_touchrects [ ui_touchrect_count ].r.w = w;
1837   ui_touchrects [ ui_touchrect_count ].r.h = h;
1838   ui_touchrects [ ui_touchrect_count ].catnum = catnum;
1839   ui_touchrect_count++;
1840
1841   return;
1842 }
1843
1844 void ui_register_app ( mm_appref_t *app, unsigned int x, unsigned int y, unsigned int w, unsigned int h ) {
1845
1846   if ( ui_touchrect_count == MAXTOUCH ) {
1847     return;
1848   }
1849
1850   ui_touchrects [ ui_touchrect_count ].r.x = x;
1851   ui_touchrects [ ui_touchrect_count ].r.y = y;
1852   ui_touchrects [ ui_touchrect_count ].r.w = w;
1853   ui_touchrects [ ui_touchrect_count ].r.h = h;
1854   ui_touchrects [ ui_touchrect_count ].ref = app;
1855   ui_touchrect_count++;
1856
1857   return;
1858 }
1859
1860 void ui_touch_act ( unsigned int x, unsigned int y ) {
1861
1862   unsigned char i;
1863   ui_touch_t *t;
1864
1865   for ( i = 0; i < ui_touchrect_count; i++ ) {
1866     t = &(ui_touchrects [ i ]);
1867
1868     if ( x >= t -> r.x &&
1869          x <= t -> r.x + t -> r.w &&
1870          y >= t -> r.y &&
1871          y <= t -> r.y + t -> r.h
1872        )
1873     {
1874
1875       if ( t -> ref ) {
1876         ui_selected = t -> ref;
1877         ui_push_exec();
1878       } else {
1879         ui_category = t -> catnum;
1880       }
1881
1882       break;
1883     }
1884
1885   } // for
1886
1887   return;
1888 }
1889
1890 unsigned char ui_forkexec ( char *argv[] ) {
1891   char *fooby = argv[0];
1892   int x;
1893
1894   if ( ( x = fork() ) < 0 ) {
1895     pnd_log ( pndn_error, "ERROR: Couldn't fork() for '%s'\n", fooby );
1896     return ( 0 );
1897   }
1898
1899   if ( x == 0 ) { // child
1900     execv ( fooby, argv );
1901     pnd_log ( pndn_error, "ERROR: Couldn't exec(%s)\n", fooby );
1902     return ( 0 );
1903   }
1904
1905   // parent, success
1906   return ( 1 );
1907 }
1908
1909 unsigned char ui_threaded_defered_preview ( pnd_disco_t *p ) {
1910
1911   if ( ! cache_preview ( p, pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 200 ),
1912                          pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_height", 180 ) )
1913      )
1914   {
1915     pnd_log ( pndn_debug, "THREAD: Couldn't load preview pic: '%s' -> '%s'\n",
1916               IFNULL(p->title_en,"No Name"), p -> preview_pic1 );
1917   }
1918
1919   // trigger that we completed
1920   SDL_Event e;
1921   bzero ( &e, sizeof(SDL_Event) );
1922   e.type = SDL_USEREVENT;
1923   e.user.code = sdl_user_finishedpreview;
1924   e.user.data1 = p;
1925   SDL_PushEvent ( &e );
1926
1927   return ( 0 );
1928 }