eecef61f71432479573aede15381dc1d1ae17147
[pandora-libraries.git] / minimenu / mmui.c
1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <limits.h>
5 #include "SDL.h"
6 #include "SDL_audio.h"
7 #include "SDL_image.h"
8 #include "SDL_ttf.h"
9 #include "SDL_gfxPrimitives.h"
10 #include "SDL_rotozoom.h"
11
12 #include "pnd_conf.h"
13 #include "pnd_logger.h"
14 #include "pnd_pxml.h"
15 #include "pnd_container.h"
16 #include "pnd_discovery.h"
17 #include "pnd_apps.h"
18 #include "pnd_device.h"
19
20 #include "mmenu.h"
21 #include "mmcat.h"
22 #include "mmcache.h"
23 #include "mmui.h"
24 #include "mmwrapcmd.h"
25
26 /* SDL
27  */
28 SDL_Surface *sdl_realscreen = NULL;
29 unsigned int sdl_ticks = 0;
30
31 /* app state
32  */
33 unsigned short int g_scale = 1; // 1 == noscale
34
35 SDL_Surface *g_imgcache [ IMG_MAX ];
36
37 TTF_Font *g_big_font = NULL;
38 TTF_Font *g_grid_font = NULL;
39 TTF_Font *g_detailtext_font = NULL;
40 TTF_Font *g_tab_font = NULL;
41
42 extern pnd_conf_handle g_conf;
43
44 /* current display state
45  */
46 int ui_rows_scrolled_down = 0;          // number of rows that should be missing from top of the display
47 mm_appref_t *ui_selected = NULL;
48 unsigned char ui_category = 0;          // current category
49 unsigned char ui_catshift = 0;          // how many cats are offscreen to the left
50
51 extern mm_category_t g_categories [ MAX_CATS ];
52 extern unsigned char g_categorycount;
53
54 static SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ); // height -1 means ignore
55 static int ui_selected_index ( void );
56
57 unsigned char ui_setup ( void ) {
58
59   /* set up SDL
60    */
61
62   SDL_Init ( SDL_INIT_EVERYTHING | SDL_INIT_NOPARACHUTE );
63
64   SDL_JoystickOpen ( 0 ); // turn on joy-0
65
66   SDL_WM_SetCaption ( "mmenu", "mmenu" );
67
68   // hide the mouse cursor if we can
69   if ( SDL_ShowCursor ( -1 ) == 1 ) {
70     SDL_ShowCursor ( 0 );
71   }
72
73   atexit ( SDL_Quit );
74
75   // open up a surface
76   unsigned int svm = SDL_SWSURFACE /*| SDL_FULLSCREEN*/ /* 0*/;
77   if ( pnd_conf_get_as_int_d ( g_conf, "display.fullscreen", 0 ) > 0 ) {
78     svm |= SDL_FULLSCREEN;
79   }
80
81   sdl_realscreen =
82     SDL_SetVideoMode ( 800 * g_scale, 480 * g_scale, 16 /*bpp*/, svm );
83
84   if ( ! sdl_realscreen ) {
85     pnd_log ( pndn_error, "ERROR: Couldn't open SDL real screen; dieing." );
86     return ( 0 );
87   }
88
89   pnd_log ( pndn_debug, "Pixel format:" );
90   pnd_log ( pndn_debug, "bpp b: %u\n", sdl_realscreen -> format -> BitsPerPixel );
91   pnd_log ( pndn_debug, "bpp B: %u\n", sdl_realscreen -> format -> BytesPerPixel );
92   pnd_log ( pndn_debug, "R mask: %08x\n", sdl_realscreen -> format -> Rmask );
93   pnd_log ( pndn_debug, "G mask: %08x\n", sdl_realscreen -> format -> Gmask );
94   pnd_log ( pndn_debug, "B mask: %08x\n", sdl_realscreen -> format -> Bmask );
95
96 #if 0 // audio
97   {
98     SDL_AudioSpec fmt;
99
100     /* Set 16-bit stereo audio at 22Khz */
101     fmt.freq = 44100; //22050;
102     fmt.format = AUDIO_S16; //AUDIO_S16;
103     fmt.channels = 1;
104     fmt.samples = 2048;        /* A good value for games */
105     fmt.callback = mixaudio;
106     fmt.userdata = NULL;
107
108     /* Open the audio device and start playing sound! */
109     if ( SDL_OpenAudio ( &fmt, NULL ) < 0 ) {
110       zotlog ( "Unable to open audio: %s\n", SDL_GetError() );
111       exit ( 1 );
112     }
113
114     SDL_PauseAudio ( 0 );
115   }
116 #endif
117
118   // images
119   //IMG_Init ( IMG_INIT_JPG | IMG_INIT_PNG );
120
121   /* fonts
122    */
123
124   // init
125   if ( TTF_Init() == -1 ) {
126     pnd_log ( pndn_error, "ERROR: Couldn't set up SDL TTF lib\n" );
127     return ( 0 ); // couldn't set up SDL TTF
128   }
129
130   char fullpath [ PATH_MAX ];
131   // big font
132   sprintf ( fullpath, "%s/%s", g_skinpath, pnd_conf_get_as_char ( g_conf, "minimenu.font" ) );
133   g_big_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, "minimenu.font_ptsize", 24 ) );
134   if ( ! g_big_font ) {
135     pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n",
136               pnd_conf_get_as_char ( g_conf, "minimenu.font" ), pnd_conf_get_as_int_d ( g_conf, "minimenu.font_ptsize", 24 ) );
137     return ( 0 ); // couldn't set up SDL TTF
138   }
139
140   // grid font
141   sprintf ( fullpath, "%s/%s", g_skinpath, pnd_conf_get_as_char ( g_conf, MMENU_GRID_FONT ) );
142   g_grid_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, MMENU_GRID_FONTSIZE, 10 ) );
143   if ( ! g_grid_font ) {
144     pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n",
145               pnd_conf_get_as_char ( g_conf, MMENU_GRID_FONT ), pnd_conf_get_as_int_d ( g_conf, MMENU_GRID_FONTSIZE, 10 ) );
146     return ( 0 ); // couldn't set up SDL TTF
147   }
148
149   // detailtext font
150   sprintf ( fullpath, "%s/%s", g_skinpath, pnd_conf_get_as_char ( g_conf, "detailtext.font" ) );
151   g_detailtext_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, "detailtext.font_ptsize", 10 ) );
152   if ( ! g_detailtext_font ) {
153     pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n",
154               pnd_conf_get_as_char ( g_conf, "detailtext.font" ), pnd_conf_get_as_int_d ( g_conf, "detailtext.font_ptsize", 10 ) );
155     return ( 0 ); // couldn't set up SDL TTF
156   }
157
158   // tab font
159   sprintf ( fullpath, "%s/%s", g_skinpath, pnd_conf_get_as_char ( g_conf, "tabs.font" ) );
160   g_tab_font = TTF_OpenFont ( fullpath, pnd_conf_get_as_int_d ( g_conf, "tabs.font_ptsize", 10 ) );
161   if ( ! g_tab_font ) {
162     pnd_log ( pndn_error, "ERROR: Couldn't load font '%s' for size %u\n",
163               pnd_conf_get_as_char ( g_conf, "tabs.font" ), pnd_conf_get_as_int_d ( g_conf, "tabs.font_ptsize", 10 ) );
164     return ( 0 ); // couldn't set up SDL TTF
165   }
166
167   return ( 1 );
168 }
169
170 mm_imgcache_t g_imagecache [ IMG_TRUEMAX ] = {
171   { IMG_BACKGROUND_800480,    "graphics.IMG_BACKGROUND_800480" },
172   { IMG_BACKGROUND_TABMASK,   "graphics.IMG_BACKGROUND_TABMASK" },
173   { IMG_DETAIL_PANEL,         "graphics.IMG_DETAIL_PANEL" },
174   { IMG_DETAIL_BG,            "graphics.IMG_DETAIL_BG" },
175   { IMG_SELECTED_ALPHAMASK,   "graphics.IMG_SELECTED_ALPHAMASK" },
176   { IMG_TAB_SEL,              "graphics.IMG_TAB_SEL" },
177   { IMG_TAB_UNSEL,            "graphics.IMG_TAB_UNSEL" },
178   { IMG_ICON_MISSING,         "graphics.IMG_ICON_MISSING" },
179   { IMG_SELECTED_HILITE,      "graphics.IMG_SELECTED_HILITE" },
180   { IMG_PREVIEW_MISSING,      "graphics.IMG_PREVIEW_MISSING" },
181   { IMG_ARROW_UP,             "graphics.IMG_ARROW_UP", },
182   { IMG_ARROW_DOWN,           "graphics.IMG_ARROW_DOWN", },
183   { IMG_ARROW_SCROLLBAR,      "graphics.IMG_ARROW_SCROLLBAR", },
184   { IMG_MAX,                  NULL },
185 };
186
187 unsigned char ui_imagecache ( char *basepath ) {
188   unsigned int i;
189   char fullpath [ PATH_MAX ];
190
191   // loaded
192
193   for ( i = 0; i < IMG_MAX; i++ ) {
194
195     if ( g_imagecache [ i ].id != i ) {
196       pnd_log ( pndn_error, "ERROR: Internal table mismatch during caching [%u]\n", i );
197       exit ( -1 );
198     }
199
200     char *filename = pnd_conf_get_as_char ( g_conf, g_imagecache [ i ].confname );
201
202     if ( ! filename ) {
203       pnd_log ( pndn_error, "ERROR: Missing filename in conf for key: %s\n", g_imagecache [ i ].confname );
204       return ( 0 );
205     }
206
207     sprintf ( fullpath, "%s/%s", basepath, filename );
208
209     if ( ! ( g_imagecache [ i ].i = IMG_Load ( fullpath ) ) ) {
210       pnd_log ( pndn_error, "ERROR: Couldn't load static cache image: %s\n", fullpath );
211       return ( 0 );
212     }
213
214   } // for
215
216   // generated
217   //g_imagecache [ IMG_SELECTED_ALPHAMASK ].i = SDL_CreateRGBSurface ( SDL_SWSURFACE, 60, 60, 32, 0xFF0000, 0x00FF00, 0xFF, 0xFF000000 );
218   //boxRGBA ( g_imagecache [ IMG_SELECTED_ALPHAMASK ].i, 0, 0, 60, 60, 100, 100, 100, 250 );
219
220   // post processing
221   //
222
223   // scale icons
224   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 );
225   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 );
226   // scale text hilight
227   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 );
228   // scale preview no-pic
229   g_imagecache [ IMG_PREVIEW_MISSING ].i = ui_scale_image ( g_imagecache [ IMG_PREVIEW_MISSING ].i,
230                                                             pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 50 ),
231                                                             pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_height", 50 ) );
232
233   // set alpha on detail panel
234   SDL_SetAlpha ( g_imagecache [ IMG_DETAIL_BG ].i, SDL_SRCALPHA, pnd_conf_get_as_int_d ( g_conf, "display.detail_bg_alpha", 50 ) );
235
236   return ( 1 );
237 } // ui_imagecache
238
239 void ui_render ( unsigned int render_mask ) {
240
241   // 800x480:
242   // divide width: 550 / 250
243   // divide left side: 5 columns == 110px width
244   //   20px buffer either side == 70px wide icon + 20 + 20?
245
246   unsigned int icon_rows;
247
248 #define MAXRECTS 200
249   SDL_Rect rects [ MAXRECTS ], src;
250   SDL_Rect *dest = rects;
251   bzero ( dest, sizeof(SDL_Rect)*MAXRECTS );
252
253   unsigned int row, displayrow, col;
254   mm_appref_t *appiter;
255
256   unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 );
257
258   unsigned char row_max = pnd_conf_get_as_int_d ( g_conf, MMENU_DISP_ROWMAX, 4 );
259   unsigned char col_max = pnd_conf_get_as_int_d ( g_conf, MMENU_DISP_COLMAX, 5 );
260
261   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
262   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
263   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
264   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
265
266   unsigned int grid_offset_x = pnd_conf_get_as_int ( g_conf, "grid.grid_offset_x" );
267   unsigned int grid_offset_y = pnd_conf_get_as_int ( g_conf, "grid.grid_offset_y" );
268
269   unsigned int icon_offset_x = pnd_conf_get_as_int ( g_conf, "grid.icon_offset_x" );
270   unsigned int icon_offset_y = pnd_conf_get_as_int ( g_conf, "grid.icon_offset_y" );
271
272   unsigned int text_width = pnd_conf_get_as_int ( g_conf, "grid.text_width" );
273   unsigned int text_clip_x = pnd_conf_get_as_int ( g_conf, "grid.text_clip_x" );
274   unsigned int text_offset_x = pnd_conf_get_as_int ( g_conf, "grid.text_offset_x" );
275   unsigned int text_offset_y = pnd_conf_get_as_int ( g_conf, "grid.text_offset_y" );
276
277   unsigned int cell_width = pnd_conf_get_as_int ( g_conf, "grid.cell_width" );
278   unsigned int cell_height = pnd_conf_get_as_int ( g_conf, "grid.cell_height" );
279
280   // how many total rows do we need?
281   icon_rows = g_categories [ ui_category ].refcount / col_max;
282   if ( g_categories [ ui_category ].refcount % col_max > 0 ) {
283     icon_rows++;
284   }
285
286   // if no selected app yet, select the first one
287 #if 0
288   if ( ! ui_selected ) {
289     ui_selected = g_categories [ ui_category ].refs;
290   }
291 #endif
292
293   // ensure selection is visible
294   if ( ui_selected ) {
295
296     int index = ui_selected_index();
297     int topleft = col_max * ui_rows_scrolled_down;
298     int botright = ( col_max * ( ui_rows_scrolled_down + row_max ) - 1 );
299
300     //pnd_log ( PND_LOG_DEFAULT, "index %u tl %u br %u\n", index, topleft, botright );
301
302     if ( index < topleft ) {
303       ui_rows_scrolled_down -= pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 );
304     } else if ( index > botright ) {
305       ui_rows_scrolled_down += pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 );
306     }
307
308     if ( ui_rows_scrolled_down < 0 ) {
309       ui_rows_scrolled_down = 0;
310     } else if ( ui_rows_scrolled_down > icon_rows ) {
311       ui_rows_scrolled_down = icon_rows;
312     }
313
314   } // ensire visible
315
316   // render background
317   if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
318     dest -> x = 0;
319     dest -> y = 0;
320     dest -> w = sdl_realscreen -> w;
321     dest -> h = sdl_realscreen -> h;
322     SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, dest /* 0,0 */ );
323     //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
324     dest++;
325   }
326
327   // tabmask
328   if ( g_imagecache [ IMG_BACKGROUND_TABMASK ].i ) {
329     dest -> x = 0;
330     dest -> y = 0;
331     dest -> w = sdl_realscreen -> w;
332     dest -> h = sdl_realscreen -> h;
333     SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_TABMASK ].i, NULL /* whole image */, sdl_realscreen, dest /* 0,0 */ );
334     //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
335     dest++;
336   }
337
338   // tabs
339   if ( g_imagecache [ IMG_TAB_SEL ].i && g_imagecache [ IMG_TAB_UNSEL ].i ) {
340     unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
341     unsigned int tab_height = pnd_conf_get_as_int ( g_conf, "tabs.tab_height" );
342     unsigned int tab_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_x" );
343     unsigned int tab_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_y" );
344     unsigned int text_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_x" );
345     unsigned int text_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_y" );
346     unsigned int text_width = pnd_conf_get_as_int ( g_conf, "tabs.text_width" );
347
348     for ( col = ui_catshift;
349           col < ( 
350                    ( screen_width / tab_width ) < g_categorycount ? ( screen_width / tab_width ) + ui_catshift : g_categorycount + ui_catshift
351                 );
352           col++ )
353     {
354
355       SDL_Surface *s;
356       if ( col == ui_category ) {
357         s = g_imagecache [ IMG_TAB_SEL ].i;
358       } else {
359         s = g_imagecache [ IMG_TAB_UNSEL ].i;
360       }
361
362       // draw tab
363       src.x = 0;
364       src.y = 0;
365       src.w = tab_width;
366       src.h = tab_height;
367       dest -> x = tab_offset_x + ( (col-ui_catshift) * tab_width );
368       dest -> y = tab_offset_y;
369       //pnd_log ( pndn_debug, "tab %u at %ux%u\n", col, dest.x, dest.y );
370       SDL_BlitSurface ( s, &src, sdl_realscreen, dest );
371       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
372       dest++;
373
374       // draw text
375       SDL_Surface *rtext;
376       SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
377       rtext = TTF_RenderText_Blended ( g_tab_font, g_categories [ col ].catname, tmpfontcolor );
378       src.x = 0;
379       src.y = 0;
380       src.w = rtext -> w < text_width ? rtext -> w : text_width;
381       src.h = rtext -> h;
382       dest -> x = tab_offset_x + ( (col-ui_catshift) * tab_width ) + text_offset_x;
383       dest -> y = tab_offset_y + text_offset_y;
384       SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
385       SDL_FreeSurface ( rtext );
386       dest++;
387
388     } // for
389
390   } // tabs
391
392   // scroll bars and arrows
393   {
394     unsigned char show_bar = 0;
395
396     // up?
397     if ( ui_rows_scrolled_down && g_imagecache [ IMG_ARROW_UP ].i ) {
398       dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_x", 450 );
399       dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_y", 80 );
400       SDL_BlitSurface ( g_imagecache [ IMG_ARROW_UP ].i, NULL /* whole image */, sdl_realscreen, dest );
401       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
402       dest++;
403
404       show_bar = 1;
405     }
406
407     // down?
408     if ( ui_rows_scrolled_down + row_max < icon_rows && g_imagecache [ IMG_ARROW_DOWN ].i ) {
409       dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_x", 450 );
410       dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_y", 80 );
411       SDL_BlitSurface ( g_imagecache [ IMG_ARROW_DOWN ].i, NULL /* whole image */, sdl_realscreen, dest );
412       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
413       dest++;
414
415       show_bar = 1;
416     }
417
418     if ( show_bar ) {
419       // show scrollbar as well
420       src.x = 0;
421       src.y = 0;
422       src.w = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_w", 10 );
423       src.h = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_h", 100 );
424       dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_x", 450 );
425       dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_y", 100 );
426       SDL_BlitSurface ( g_imagecache [ IMG_ARROW_SCROLLBAR ].i, &src /* whole image */, sdl_realscreen, dest );
427       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
428       dest++;
429     } // bar
430
431   } // scroll bars
432
433   // render detail pane bg
434   if ( pnd_conf_get_as_int_d ( g_conf, "detailpane.show", 1 ) ) {
435
436     if ( g_imagecache [ IMG_DETAIL_BG ].i ) {
437       src.x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
438       src.y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
439       src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> w;
440       src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> h;
441       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
442       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
443       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
444       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
445       dest++;
446     }
447
448     // render detail pane
449     if ( g_imagecache [ IMG_DETAIL_PANEL ].i ) {
450       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
451       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
452       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_PANEL ].i, NULL /* whole image */, sdl_realscreen, dest );
453       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
454       dest++;
455     }
456
457   } // detailpane frame/bg
458
459   // anything to render?
460   if ( g_categories [ ui_category ].refs ) {
461
462     appiter = g_categories [ ui_category ].refs;
463     row = 0;
464     displayrow = 0;
465
466     // until we run out of apps, or run out of space
467     while ( appiter != NULL ) {
468
469       for ( col = 0; col < col_max && appiter != NULL; col++ ) {
470
471         // do we even need to render it? or are we suppressing it due to rows scrolled off the top?
472         if ( row >= ui_rows_scrolled_down ) {
473
474           // selected? show hilights
475           if ( appiter == ui_selected ) {
476             // icon
477             dest -> x = grid_offset_x + ( col * cell_width ) + icon_offset_x;
478             dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y;
479             SDL_BlitSurface ( g_imagecache [ IMG_SELECTED_ALPHAMASK ].i, NULL /* all */, sdl_realscreen, dest );
480             //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
481             dest++;
482             // text
483             dest -> x = grid_offset_x + ( col * cell_width ) + text_clip_x;
484             dest -> y = grid_offset_y + ( displayrow * cell_height ) + pnd_conf_get_as_int ( g_conf, "grid.text_hilite_offset_y" );
485             SDL_BlitSurface ( g_imagecache [ IMG_SELECTED_HILITE ].i, NULL /* all */, sdl_realscreen, dest );
486             //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
487             dest++;
488           } // selected?
489
490           // show icon
491           mm_cache_t *ic = cache_query_icon ( appiter -> ref -> unique_id );
492           SDL_Surface *iconsurface;
493           if ( ic ) {
494             iconsurface = ic -> i;
495           } else {
496             //pnd_log ( pndn_warning, "WARNING: TBD: Need Missin-icon icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") );
497             iconsurface = g_imagecache [ IMG_ICON_MISSING ].i;
498           }
499           if ( iconsurface ) {
500             //pnd_log ( pndn_debug, "Got an icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") );
501
502             src.x = 0;
503             src.y = 0;
504             src.w = 60;
505             src.h = 60;
506             dest -> x = grid_offset_x + ( col * cell_width ) + icon_offset_x;
507             dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y;
508
509             SDL_BlitSurface ( iconsurface, &src, sdl_realscreen, dest );
510             //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
511             dest++;
512
513           }
514
515           // show text
516           if ( appiter -> ref -> title_en ) {
517             SDL_Surface *rtext;
518             SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
519             rtext = TTF_RenderText_Blended ( g_grid_font, appiter -> ref -> title_en, tmpfontcolor );
520             src.x = 0;
521             src.y = 0;
522             src.w = text_width < rtext -> w ? text_width : rtext -> w;
523             src.h = rtext -> h;
524             if ( rtext -> w > text_width ) {
525               dest -> x = grid_offset_x + ( col * cell_width ) + text_clip_x;
526             } else {
527               dest -> x = grid_offset_x + ( col * cell_width ) + text_offset_x - ( rtext -> w / 2 );
528             }
529             dest -> y = grid_offset_y + ( displayrow * cell_height ) + text_offset_y;
530             SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
531             SDL_FreeSurface ( rtext );
532             dest++;
533           }
534
535         } // display now? or scrolled away..
536
537         // next
538         appiter = appiter -> next;
539
540       } // for column 1...X
541
542       if ( row >= ui_rows_scrolled_down ) {
543         displayrow++;
544       }
545
546       row ++;
547
548       // are we done displaying rows?
549       if ( displayrow >= row_max ) {
550         break;
551       }
552
553     } // while
554
555   } else {
556     // no apps to render?
557     pnd_log ( pndn_rem, "No applications to render?\n" );
558   } // apps to renser?
559
560   // detail panel
561   if ( ui_selected ) {
562
563     unsigned int cell_offset_x = pnd_conf_get_as_int ( g_conf, "detailtext.cell_offset_x" );
564     unsigned int cell_offset_y = pnd_conf_get_as_int ( g_conf, "detailtext.cell_offset_y" );
565     unsigned int cell_width = pnd_conf_get_as_int ( g_conf, "detailtext.cell_width" );
566
567     unsigned int desty = cell_offset_y;
568
569     char buffer [ 256 ];
570
571     // full name
572     if ( ui_selected -> ref -> title_en ) {
573       SDL_Surface *rtext;
574       SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
575       rtext = TTF_RenderText_Blended ( g_detailtext_font, ui_selected -> ref -> title_en, tmpfontcolor );
576       src.x = 0;
577       src.y = 0;
578       src.w = rtext -> w < cell_width ? rtext -> w : cell_width;
579       src.h = rtext -> h;
580       dest -> x = cell_offset_x;
581       dest -> y = desty;
582       SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
583       SDL_FreeSurface ( rtext );
584       dest++;
585       desty += src.h;
586     }
587
588     // category
589     if ( ui_selected -> ref -> main_category ) {
590
591       sprintf ( buffer, "Category: %s", ui_selected -> ref -> main_category );
592
593       SDL_Surface *rtext;
594       SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
595       rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, tmpfontcolor );
596       src.x = 0;
597       src.y = 0;
598       src.w = rtext -> w < cell_width ? rtext -> w : cell_width;
599       src.h = rtext -> h;
600       dest -> x = cell_offset_x;
601       dest -> y = desty;
602       SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
603       SDL_FreeSurface ( rtext );
604       dest++;
605       desty += src.h;
606     }
607
608     // clock
609     if ( ui_selected -> ref -> clockspeed ) {
610
611       sprintf ( buffer, "CPU Clock: %s", ui_selected -> ref -> clockspeed );
612
613       SDL_Surface *rtext;
614       SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
615       rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, tmpfontcolor );
616       src.x = 0;
617       src.y = 0;
618       src.w = rtext -> w < cell_width ? rtext -> w : cell_width;
619       src.h = rtext -> h;
620       dest -> x = cell_offset_x;
621       dest -> y = desty;
622       SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
623       SDL_FreeSurface ( rtext );
624       dest++;
625       desty += src.h;
626     }
627
628     // preview pic
629     mm_cache_t *ic = cache_query_preview ( ui_selected -> ref -> unique_id );
630     SDL_Surface *previewpic;
631
632     if ( ic ) {
633       previewpic = ic -> i;
634     } else {
635       previewpic = g_imagecache [ IMG_PREVIEW_MISSING ].i;
636     }
637
638     if ( previewpic ) {
639       dest -> x = pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_offset_x", 50 ) +
640         ( ( pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 50 ) - previewpic -> w ) / 2 );
641       dest -> y = pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_offset_y", 50 );
642       SDL_BlitSurface ( previewpic, NULL /* whole image */, sdl_realscreen, dest );
643       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
644       dest++;
645     }
646
647   } // selected?
648
649   // extras
650   //
651
652   // battery
653   if ( 1 ) {
654     unsigned char batterylevel = pnd_device_get_battery_gauge_perc();
655     char buffer [ 100 ];
656
657     sprintf ( buffer, "Battery: %u%%", batterylevel );
658
659     SDL_Surface *rtext;
660     SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
661     rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor );
662     dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.battery_x", 20 );
663     dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.battery_y", 450 );
664     SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
665     SDL_FreeSurface ( rtext );
666     dest++;
667   }
668
669   // hints
670   if ( pnd_conf_get_as_char ( g_conf, "display.hintline" ) ) {
671     char *buffer = pnd_conf_get_as_char ( g_conf, "display.hintline" );
672     SDL_Surface *rtext;
673     SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
674     rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor );
675     dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.hint_x", 40 );
676     dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.hint_y", 450 );
677     SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
678     SDL_FreeSurface ( rtext );
679     dest++;
680   }
681
682   // update all the rects and send it all to sdl
683   SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
684
685 } // ui_render
686
687 void ui_process_input ( unsigned char block_p ) {
688   SDL_Event event;
689
690   unsigned char ui_event = 0; // if we get a ui event, flip to 1 and break
691   static ui_sdl_button_e ui_mask = uisb_none; // current buttons down
692
693   while ( ! ui_event &&
694           block_p ? SDL_WaitEvent ( &event ) : SDL_PollEvent ( &event ) )
695   {
696
697     switch ( event.type ) {
698
699     case SDL_USEREVENT:
700       // update something
701
702       if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) {
703
704         pnd_log ( pndn_debug, "Deferred preview pic load ----------\n" );
705
706         // load the preview pics now!
707         pnd_disco_t *iter = ui_selected -> ref;
708
709         if ( iter -> preview_pic1 &&
710              ! cache_preview ( iter, pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 200 ), pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_height", 180 ) ) )
711         {
712           pnd_log ( pndn_debug, "  Couldn't load preview pic: '%s' -> '%s'\n", IFNULL(iter->title_en,"No Name"), iter -> preview_pic1 );
713         }
714
715         pnd_log ( pndn_debug, "Deferred preview pic load finish ---\n" );
716
717         ui_event++;
718       }
719
720       break;
721
722 #if 0 // joystick motion
723     case SDL_JOYAXISMOTION:
724
725       pnd_log ( PND_LOG_DEFAULT, "joystick axis\n" );
726
727         if ( event.jaxis.axis == 0 ) {
728           // horiz
729           if ( event.jaxis.value < 0 ) {
730             ui_push_left();
731             pnd_log ( PND_LOG_DEFAULT, "joystick axis - LEFT\n" );
732           } else if ( event.jaxis.value > 0 ) {
733             ui_push_right();
734             pnd_log ( PND_LOG_DEFAULT, "joystick axis - RIGHT\n" );
735           }
736         } else if ( event.jaxis.axis == 1 ) {
737           // vert
738           if ( event.jaxis.value < 0 ) {
739             ui_push_up();
740           } else if ( event.jaxis.value > 0 ) {
741             ui_push_down();
742           }
743         }
744
745         ui_event++;
746
747         break;
748 #endif
749
750 #if 0 // joystick buttons
751     case SDL_JOYBUTTONDOWN:
752
753       pnd_log ( PND_LOG_DEFAULT, "joystick button down %u\n", event.jbutton.button );
754
755       if ( event.jbutton.button == 0 ) { // B
756         ui_mask |= uisb_b;
757       } else if ( event.jbutton.button == 1 ) { // Y
758         ui_mask |= uisb_y;
759       } else if ( event.jbutton.button == 2 ) { // X
760         ui_mask |= uisb_x;
761       } else if ( event.jbutton.button == 3 ) { // A
762         ui_mask |= uisb_a;
763
764       } else if ( event.jbutton.button == 4 ) { // Select
765         ui_mask |= uisb_select;
766       } else if ( event.jbutton.button == 5 ) { // Start
767         ui_mask |= uisb_start;
768
769       } else if ( event.jbutton.button == 7 ) { // L
770         ui_mask |= uisb_l;
771       } else if ( event.jbutton.button == 8 ) { // R
772         ui_mask |= uisb_r;
773
774       }
775
776       ui_event++;
777
778       break;
779
780     case SDL_JOYBUTTONUP:
781
782       pnd_log ( PND_LOG_DEFAULT, "joystick button up %u\n", event.jbutton.button );
783
784       if ( event.jbutton.button == 0 ) { // B
785         ui_mask &= ~uisb_b;
786         ui_push_exec();
787       } else if ( event.jbutton.button == 1 ) { // Y
788         ui_mask &= ~uisb_y;
789       } else if ( event.jbutton.button == 2 ) { // X
790         ui_mask &= ~uisb_x;
791       } else if ( event.jbutton.button == 3 ) { // A
792         ui_mask &= ~uisb_a;
793
794       } else if ( event.jbutton.button == 4 ) { // Select
795         ui_mask &= ~uisb_select;
796       } else if ( event.jbutton.button == 5 ) { // Start
797         ui_mask &= ~uisb_start;
798
799       } else if ( event.jbutton.button == 7 ) { // L
800         ui_mask &= ~uisb_l;
801         ui_push_ltrigger();
802       } else if ( event.jbutton.button == 8 ) { // R
803         ui_mask &= ~uisb_r;
804         ui_push_rtrigger();
805
806       }
807
808       ui_event++;
809
810       break;
811 #endif
812
813 #if 1 // keyboard events
814     case SDL_KEYUP:
815
816       //pnd_log ( pndn_debug, "key up %u\n", event.key.keysym.sym );
817
818       // SDLK_LALT -> Start
819
820       // directional
821       if ( event.key.keysym.sym == SDLK_RIGHT ) {
822         ui_push_right();
823         ui_event++;
824       } else if ( event.key.keysym.sym == SDLK_LEFT ) {
825         ui_push_left();
826         ui_event++;
827       } else if ( event.key.keysym.sym == SDLK_UP ) {
828         ui_push_up();
829         ui_event++;
830       } else if ( event.key.keysym.sym == SDLK_DOWN ) {
831         ui_push_down();
832         ui_event++;
833       } else if ( event.key.keysym.sym == SDLK_SPACE || event.key.keysym.sym == SDLK_END ) {
834         ui_push_exec();
835         ui_event++;
836       } else if ( event.key.keysym.sym == SDLK_z || event.key.keysym.sym == SDLK_RSHIFT ) {
837         ui_push_ltrigger();
838         ui_event++;
839       } else if ( event.key.keysym.sym == SDLK_x || event.key.keysym.sym == SDLK_RCTRL ) {
840         ui_push_rtrigger();
841         ui_event++;
842
843       } else if ( event.key.keysym.sym == SDLK_LALT ) { // start button
844         char *opts [ 10 ] = {
845           "Return to Minimenu",
846           "Shutdown Pandora",
847           "Rescan for Applications",
848           "Set to full desktop and reboot",
849           "Set to pmenu and reboot",
850           "Quit (<- beware)"
851         };
852         int sel = ui_modal_single_menu ( opts, 6, "Minimenu", "Enter to select; other to return." );
853
854         char buffer [ 100 ];
855         if ( sel == 0 ) {
856           return ( -1 ); // return
857         } else if ( sel == 1 ) {
858           sprintf ( buffer, "sudo poweroff" );
859           system ( buffer );
860         } else if ( sel == 2 ) {
861           // rescan apps
862         } else if ( sel == 3 ) {
863           // set env to xfce
864           sprintf ( buffer, "echo startxfce4 > /tmp/gui.load" );
865           system ( buffer );
866           sprintf ( buffer, "sudo poweroff" );
867           system ( buffer );
868         } else if ( sel == 4 ) {
869           // set env to pmenu
870           sprintf ( buffer, "echo pmenu > /tmp/gui.load" );
871           system ( buffer );
872           sprintf ( buffer, "sudo poweroff" );
873           system ( buffer );
874         } else if ( sel == 5 ) {
875           emit_and_quit ( MM_QUIT );
876         }
877
878         ui_event++;
879       }
880
881       // extras
882       if ( event.key.keysym.sym == SDLK_q ) {
883         emit_and_quit ( MM_QUIT );
884       }
885
886       break;
887 #endif
888
889 #if 0 // mouse / touchscreen
890     case SDL_MOUSEBUTTONDOWN:
891       if ( event.button.button == SDL_BUTTON_LEFT ) {
892         cb_pointer_press ( gc, event.button.x / g_scale, event.button.y / g_scale );
893         ui_event++;
894       }
895       break;
896
897     case SDL_MOUSEBUTTONUP:
898       if ( event.button.button == SDL_BUTTON_LEFT ) {
899         cb_pointer_release ( gc, event.button.x / g_scale, event.button.y / g_scale );
900         retval |= STAT_pen;
901         ui_event++;
902       }
903       break;
904 #endif
905
906     case SDL_QUIT:
907       exit ( 0 );
908       break;
909
910     default:
911       break;
912
913     } // switch event type
914
915   } // while poll
916
917   return;
918 }
919
920 void ui_push_left ( void ) {
921
922   if ( ! ui_selected ) {
923     ui_push_right();
924     return;
925   }
926
927   // are we alreadt at first item?
928   if ( g_categories [ ui_category ].refs == ui_selected ) {
929     // can't go any more left, we're at the head
930   } else {
931     // figure out the previous item; yay for singly linked list :/
932     mm_appref_t *i = g_categories [ ui_category ].refs;
933     while ( i ) {
934       if ( i -> next == ui_selected ) {
935         ui_selected = i;
936         break;
937       }
938       i = i -> next;
939     }
940   }
941
942   ui_set_selected ( ui_selected );
943
944   return;
945 }
946
947 void ui_push_right ( void ) {
948
949   if ( ui_selected ) {
950
951     if ( ui_selected -> next ) {
952       ui_selected = ui_selected -> next;
953     }
954
955   } else {
956     ui_selected = g_categories [ ui_category ].refs;
957   }
958
959   ui_set_selected ( ui_selected );
960
961   return;
962 }
963
964 void ui_push_up ( void ) {
965   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
966
967   while ( col_max ) {
968     ui_push_left();
969     col_max--;
970   }
971
972   return;
973 }
974
975 void ui_push_down ( void ) {
976   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
977
978   if ( ui_selected ) {
979     while ( col_max ) {
980       ui_push_right();
981       col_max--;
982     }
983   } else {
984     ui_push_right();
985   }
986
987   return;
988 }
989
990 void ui_push_exec ( void ) {
991
992   if ( ui_selected ) {
993     char buffer [ PATH_MAX ];
994     sprintf ( buffer, "%s/%s", ui_selected -> ref -> object_path, ui_selected -> ref -> object_filename );
995     pnd_apps_exec ( pnd_run_script,
996                     buffer,
997                     ui_selected -> ref -> unique_id,
998                     ui_selected -> ref -> exec,
999                     ui_selected -> ref -> startdir,
1000                     ui_selected -> ref -> execargs,
1001                     atoi ( ui_selected -> ref -> clockspeed ),
1002                     PND_EXEC_OPTION_NORUN );
1003     sprintf ( buffer, "%s %s\n", MM_RUN, pnd_apps_exec_runline() );
1004     emit_and_quit ( buffer );
1005   }
1006
1007   return;
1008 }
1009
1010 void ui_push_ltrigger ( void ) {
1011   unsigned char oldcat = ui_category;
1012
1013   if ( ui_category > 0 ) {
1014     ui_category--;
1015   } else {
1016     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1017       ui_category = g_categorycount - 1;
1018     }
1019   }
1020
1021   if ( oldcat != ui_category ) {
1022     ui_selected = NULL;
1023     ui_set_selected ( ui_selected );
1024   }
1025
1026   // make tab visible?
1027   if ( ui_catshift > 0 && ui_category == ui_catshift - 1 ) {
1028     ui_catshift--;
1029   }
1030
1031   // unscroll
1032   ui_rows_scrolled_down = 0;
1033
1034   return;
1035 }
1036
1037 void ui_push_rtrigger ( void ) {
1038   unsigned char oldcat = ui_category;
1039
1040   unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 );
1041   unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
1042
1043   if ( ui_category < ( g_categorycount - 1 ) ) {
1044     ui_category++;
1045   } else {
1046     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1047       ui_category = 0;
1048     }
1049   }
1050
1051   if ( oldcat != ui_category ) {
1052     ui_selected = NULL;
1053     ui_set_selected ( ui_selected );
1054   }
1055
1056   // make tab visible?
1057   if ( ui_category > ui_catshift + ( screen_width / tab_width ) - 1 ) {
1058     ui_catshift++;
1059   }
1060
1061   // unscroll
1062   ui_rows_scrolled_down = 0;
1063
1064   return;
1065 }
1066
1067 SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ) {
1068   double scale = 1000000.0;
1069   double scalex = 1000000.0;
1070   double scaley = 1000000.0;
1071   SDL_Surface *scaled;
1072
1073   scalex = (double)maxwidth / (double)s -> w;
1074
1075   if ( maxheight == -1 ) {
1076     scale = scalex;
1077   } else {
1078     scaley = (double)maxheight / (double)s -> h;
1079
1080     if ( scaley < scalex ) {
1081       scale = scaley;
1082     } else {
1083       scale = scalex;
1084     }
1085
1086   }
1087
1088   pnd_log ( pndn_debug, "  Upscaling; scale factor %f\n", scale );
1089   scaled = rotozoomSurface ( s, 0 /* angle*/, scale /* scale */, 1 /* smooth==1*/ );
1090   SDL_FreeSurface ( s );
1091   s = scaled;
1092
1093   return ( s );
1094 }
1095
1096 void ui_loadscreen ( void ) {
1097
1098   SDL_Rect dest;
1099
1100   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1101   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1102   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1103   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1104
1105   // clear the screen
1106   SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1107
1108   // render text
1109   SDL_Surface *rtext;
1110   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1111   rtext = TTF_RenderText_Blended ( g_big_font, "Setting up menu...", tmpfontcolor );
1112   dest.x = 20;
1113   dest.y = 20;
1114   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1115   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1116   SDL_FreeSurface ( rtext );
1117
1118   return;
1119 }
1120
1121 void ui_discoverscreen ( unsigned char clearscreen ) {
1122
1123   SDL_Rect dest;
1124
1125   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1126   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1127   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1128   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1129
1130   // clear the screen
1131   if ( clearscreen ) {
1132     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1133
1134     // render background
1135     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1136       dest.x = 0;
1137       dest.y = 0;
1138       dest.w = sdl_realscreen -> w;
1139       dest.h = sdl_realscreen -> h;
1140       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1141       SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1142     }
1143
1144   }
1145
1146   // render text
1147   SDL_Surface *rtext;
1148   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1149   rtext = TTF_RenderText_Blended ( g_big_font, "Looking for applications...", tmpfontcolor );
1150   if ( clearscreen ) {
1151     dest.x = 20;
1152     dest.y = 20;
1153   } else {
1154     dest.x = 20;
1155     dest.y = 40;
1156   }
1157   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1158   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1159   SDL_FreeSurface ( rtext );
1160
1161   // render icon
1162   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1163     dest.x = rtext -> w + 30;
1164     dest.y = 20;
1165     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, &dest );
1166     SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1167   }
1168
1169   return;
1170 }
1171
1172 void ui_cachescreen ( unsigned char clearscreen, char *filename ) {
1173
1174   SDL_Rect rects [ 4 ];
1175   SDL_Rect *dest = rects;
1176   bzero ( dest, sizeof(SDL_Rect)* 4 );
1177
1178   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1179   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1180   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1181   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1182
1183   static unsigned int stepx = 0;
1184
1185   // clear the screen
1186   if ( clearscreen ) {
1187     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1188
1189     // render background
1190     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1191       dest -> x = 0;
1192       dest -> y = 0;
1193       dest -> w = sdl_realscreen -> w;
1194       dest -> h = sdl_realscreen -> h;
1195       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1196       dest++;
1197     }
1198
1199   }
1200
1201   // render text
1202   SDL_Surface *rtext;
1203   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1204   rtext = TTF_RenderText_Blended ( g_big_font, "Caching applications artwork...", tmpfontcolor );
1205   if ( clearscreen ) {
1206     dest -> x = 20;
1207     dest -> y = 20;
1208   } else {
1209     dest -> x = 20;
1210     dest -> y = 40;
1211   }
1212   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1213   SDL_FreeSurface ( rtext );
1214   dest++;
1215
1216   // render icon
1217   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1218     dest -> x = rtext -> w + 30 + stepx;
1219     dest -> y = 20;
1220     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, dest );
1221     dest++;
1222   }
1223
1224   // filename
1225   if ( filename ) {
1226     rtext = TTF_RenderText_Blended ( g_tab_font, filename, tmpfontcolor );
1227     dest -> x = 20;
1228     dest -> y = 50;
1229     SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1230     SDL_FreeSurface ( rtext );
1231     dest++;
1232   }
1233
1234   // move across
1235   stepx += 20;
1236
1237   if ( stepx > 350 ) {
1238     stepx = 0;
1239   }
1240
1241   SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1242
1243   return;
1244 }
1245
1246 int ui_selected_index ( void ) {
1247
1248   if ( ! ui_selected ) {
1249     return ( -1 ); // no index
1250   }
1251
1252   mm_appref_t *r = g_categories [ ui_category ].refs;
1253   int counter = 0;
1254   while ( r ) {
1255     if ( r == ui_selected ) {
1256       return ( counter );
1257     }
1258     r = r -> next;
1259     counter++;
1260   }
1261
1262   return ( -1 );
1263 }
1264
1265 static mm_appref_t *timer_ref = NULL;
1266 void ui_set_selected ( mm_appref_t *r ) {
1267
1268   if ( ! pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) {
1269     return; // no desire to defer anything
1270   }
1271
1272   if ( ! r ) {
1273     // cancel timer
1274     SDL_SetTimer ( 0, NULL );
1275     timer_ref = NULL;
1276     return;
1277   }
1278
1279   SDL_SetTimer ( pnd_conf_get_as_int_d ( g_conf, "previewpic.defer_timer_ms", 1000 ), ui_callback_f );
1280   timer_ref = r;
1281
1282   return;
1283 }
1284
1285 unsigned int ui_callback_f ( unsigned int t ) {
1286
1287   if ( ui_selected != timer_ref ) {
1288     return ( 0 ); // user has moved it, who cares
1289   }
1290
1291   SDL_Event e;
1292   e.type = SDL_USEREVENT;
1293   SDL_PushEvent ( &e );
1294
1295   return ( 0 );
1296 }
1297
1298 int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *footer ) {
1299   SDL_Rect rects [ 40 ];
1300   SDL_Rect *dest = rects;
1301   SDL_Rect src;
1302   SDL_Surface *rtext;
1303
1304   bzero ( rects, sizeof(SDL_Rect) * 40 );
1305
1306   unsigned int sel = 0;
1307
1308   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1309   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1310   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1311   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1312
1313   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1314
1315   SDL_Color selfontcolor = { 0/*font_rgba_r*/, font_rgba_g, font_rgba_b, font_rgba_a };
1316
1317   unsigned int i;
1318   SDL_Event event;
1319
1320   while ( 1 ) {
1321
1322     // clear
1323     dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1324     dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1325     dest -> w = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> w;
1326     dest -> h = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h;
1327     SDL_FillRect( sdl_realscreen, dest, 0 );
1328
1329     // show dialog background
1330     if ( g_imagecache [ IMG_DETAIL_BG ].i ) {
1331       src.x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1332       src.y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1333       src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> w;
1334       src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> h;
1335       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1336       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1337       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1338       // repeat for darken?
1339       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1340       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1341       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1342       dest++;
1343     }
1344
1345     // show dialog frame
1346     if ( g_imagecache [ IMG_DETAIL_PANEL ].i ) {
1347       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1348       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1349       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_PANEL ].i, NULL /* whole image */, sdl_realscreen, dest );
1350       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1351       dest++;
1352     }
1353
1354     // show header
1355     if ( title ) {
1356       rtext = TTF_RenderText_Blended ( g_tab_font, title, tmpfontcolor );
1357       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1358       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 20;
1359       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1360       SDL_FreeSurface ( rtext );
1361       dest++;
1362     }
1363
1364     // show footer
1365     if ( footer ) {
1366       rtext = TTF_RenderText_Blended ( g_tab_font, footer, tmpfontcolor );
1367       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1368       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) +
1369         ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h
1370         - 60;
1371       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1372       SDL_FreeSurface ( rtext );
1373       dest++;
1374     }
1375
1376     // show options
1377     for ( i = 0; i < argc; i++ ) {
1378
1379       // show options
1380       if ( sel == i ) {
1381         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], selfontcolor );
1382       } else {
1383         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], tmpfontcolor );
1384       }
1385       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1386       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 40 + ( 20 * ( i + 1 ) );
1387       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1388       SDL_FreeSurface ( rtext );
1389       dest++;
1390
1391     } // for
1392
1393     // update all the rects and send it all to sdl
1394     SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1395     dest = rects;
1396
1397     // check for input
1398     while ( SDL_WaitEvent ( &event ) ) {
1399
1400       switch ( event.type ) {
1401
1402       case SDL_KEYUP:
1403
1404         if ( event.key.keysym.sym == SDLK_UP ) {
1405           if ( sel ) {
1406             sel--;
1407           }
1408         } else if ( event.key.keysym.sym == SDLK_DOWN ) {
1409           if ( sel < argc - 1 ) {
1410             sel++;
1411           }
1412
1413         } else if ( event.key.keysym.sym == SDLK_RETURN ) {
1414           return ( sel );
1415
1416         } else if ( event.key.keysym.sym == SDLK_q ) {
1417           exit ( 0 );
1418
1419         } else {
1420           return ( -1 ); // nada
1421         }
1422
1423         break;
1424
1425       } // switch
1426
1427       break;
1428     } // while
1429
1430   } // while
1431
1432   return ( -1 );
1433 }