Merge branch 'master' of git://git.openpandora.org/pandora-libraries
[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           "About Minimenu"
852         };
853         int sel = ui_modal_single_menu ( opts, 7, "Minimenu", "Enter to select; other to return." );
854
855         char buffer [ 100 ];
856         if ( sel == 0 ) {
857           // do nothing
858         } else if ( sel == 1 ) {
859           sprintf ( buffer, "sudo poweroff" );
860           system ( buffer );
861         } else if ( sel == 2 ) {
862           // rescan apps
863         } else if ( sel == 3 ) {
864           // set env to xfce
865           sprintf ( buffer, "echo startxfce4 > /tmp/gui.load" );
866           system ( buffer );
867           sprintf ( buffer, "sudo poweroff" );
868           system ( buffer );
869         } else if ( sel == 4 ) {
870           // set env to pmenu
871           sprintf ( buffer, "echo pmenu > /tmp/gui.load" );
872           system ( buffer );
873           sprintf ( buffer, "sudo poweroff" );
874           system ( buffer );
875         } else if ( sel == 5 ) {
876           emit_and_quit ( MM_QUIT );
877         } else if ( sel == 6 ) {
878           // about
879         }
880
881         ui_event++;
882       }
883
884       // extras
885       if ( event.key.keysym.sym == SDLK_q ) {
886         emit_and_quit ( MM_QUIT );
887       }
888
889       break;
890 #endif
891
892 #if 0 // mouse / touchscreen
893     case SDL_MOUSEBUTTONDOWN:
894       if ( event.button.button == SDL_BUTTON_LEFT ) {
895         cb_pointer_press ( gc, event.button.x / g_scale, event.button.y / g_scale );
896         ui_event++;
897       }
898       break;
899
900     case SDL_MOUSEBUTTONUP:
901       if ( event.button.button == SDL_BUTTON_LEFT ) {
902         cb_pointer_release ( gc, event.button.x / g_scale, event.button.y / g_scale );
903         retval |= STAT_pen;
904         ui_event++;
905       }
906       break;
907 #endif
908
909     case SDL_QUIT:
910       exit ( 0 );
911       break;
912
913     default:
914       break;
915
916     } // switch event type
917
918   } // while poll
919
920   return;
921 }
922
923 void ui_push_left ( void ) {
924
925   if ( ! ui_selected ) {
926     ui_push_right();
927     return;
928   }
929
930   // are we alreadt at first item?
931   if ( g_categories [ ui_category ].refs == ui_selected ) {
932     // can't go any more left, we're at the head
933   } else {
934     // figure out the previous item; yay for singly linked list :/
935     mm_appref_t *i = g_categories [ ui_category ].refs;
936     while ( i ) {
937       if ( i -> next == ui_selected ) {
938         ui_selected = i;
939         break;
940       }
941       i = i -> next;
942     }
943   }
944
945   ui_set_selected ( ui_selected );
946
947   return;
948 }
949
950 void ui_push_right ( void ) {
951
952   if ( ui_selected ) {
953
954     if ( ui_selected -> next ) {
955       ui_selected = ui_selected -> next;
956     }
957
958   } else {
959     ui_selected = g_categories [ ui_category ].refs;
960   }
961
962   ui_set_selected ( ui_selected );
963
964   return;
965 }
966
967 void ui_push_up ( void ) {
968   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
969
970   while ( col_max ) {
971     ui_push_left();
972     col_max--;
973   }
974
975   return;
976 }
977
978 void ui_push_down ( void ) {
979   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
980
981   if ( ui_selected ) {
982     while ( col_max ) {
983       ui_push_right();
984       col_max--;
985     }
986   } else {
987     ui_push_right();
988   }
989
990   return;
991 }
992
993 void ui_push_exec ( void ) {
994
995   if ( ui_selected ) {
996     char buffer [ PATH_MAX ];
997     sprintf ( buffer, "%s/%s", ui_selected -> ref -> object_path, ui_selected -> ref -> object_filename );
998     pnd_apps_exec ( pnd_run_script,
999                     buffer,
1000                     ui_selected -> ref -> unique_id,
1001                     ui_selected -> ref -> exec,
1002                     ui_selected -> ref -> startdir,
1003                     ui_selected -> ref -> execargs,
1004                     atoi ( ui_selected -> ref -> clockspeed ),
1005                     PND_EXEC_OPTION_NORUN );
1006     sprintf ( buffer, "%s %s\n", MM_RUN, pnd_apps_exec_runline() );
1007     emit_and_quit ( buffer );
1008   }
1009
1010   return;
1011 }
1012
1013 void ui_push_ltrigger ( void ) {
1014   unsigned char oldcat = ui_category;
1015
1016   if ( ui_category > 0 ) {
1017     ui_category--;
1018   } else {
1019     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1020       ui_category = g_categorycount - 1;
1021     }
1022   }
1023
1024   if ( oldcat != ui_category ) {
1025     ui_selected = NULL;
1026     ui_set_selected ( ui_selected );
1027   }
1028
1029   // make tab visible?
1030   if ( ui_catshift > 0 && ui_category == ui_catshift - 1 ) {
1031     ui_catshift--;
1032   }
1033
1034   // unscroll
1035   ui_rows_scrolled_down = 0;
1036
1037   return;
1038 }
1039
1040 void ui_push_rtrigger ( void ) {
1041   unsigned char oldcat = ui_category;
1042
1043   unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 );
1044   unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
1045
1046   if ( ui_category < ( g_categorycount - 1 ) ) {
1047     ui_category++;
1048   } else {
1049     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1050       ui_category = 0;
1051     }
1052   }
1053
1054   if ( oldcat != ui_category ) {
1055     ui_selected = NULL;
1056     ui_set_selected ( ui_selected );
1057   }
1058
1059   // make tab visible?
1060   if ( ui_category > ui_catshift + ( screen_width / tab_width ) - 1 ) {
1061     ui_catshift++;
1062   }
1063
1064   // unscroll
1065   ui_rows_scrolled_down = 0;
1066
1067   return;
1068 }
1069
1070 SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ) {
1071   double scale = 1000000.0;
1072   double scalex = 1000000.0;
1073   double scaley = 1000000.0;
1074   SDL_Surface *scaled;
1075
1076   scalex = (double)maxwidth / (double)s -> w;
1077
1078   if ( maxheight == -1 ) {
1079     scale = scalex;
1080   } else {
1081     scaley = (double)maxheight / (double)s -> h;
1082
1083     if ( scaley < scalex ) {
1084       scale = scaley;
1085     } else {
1086       scale = scalex;
1087     }
1088
1089   }
1090
1091   pnd_log ( pndn_debug, "  Upscaling; scale factor %f\n", scale );
1092   scaled = rotozoomSurface ( s, 0 /* angle*/, scale /* scale */, 1 /* smooth==1*/ );
1093   SDL_FreeSurface ( s );
1094   s = scaled;
1095
1096   return ( s );
1097 }
1098
1099 void ui_loadscreen ( void ) {
1100
1101   SDL_Rect dest;
1102
1103   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1104   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1105   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1106   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1107
1108   // clear the screen
1109   SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1110
1111   // render text
1112   SDL_Surface *rtext;
1113   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1114   rtext = TTF_RenderText_Blended ( g_big_font, "Setting up menu...", tmpfontcolor );
1115   dest.x = 20;
1116   dest.y = 20;
1117   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1118   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1119   SDL_FreeSurface ( rtext );
1120
1121   return;
1122 }
1123
1124 void ui_discoverscreen ( unsigned char clearscreen ) {
1125
1126   SDL_Rect dest;
1127
1128   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1129   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1130   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1131   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1132
1133   // clear the screen
1134   if ( clearscreen ) {
1135     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1136
1137     // render background
1138     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1139       dest.x = 0;
1140       dest.y = 0;
1141       dest.w = sdl_realscreen -> w;
1142       dest.h = sdl_realscreen -> h;
1143       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1144       SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1145     }
1146
1147   }
1148
1149   // render text
1150   SDL_Surface *rtext;
1151   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1152   rtext = TTF_RenderText_Blended ( g_big_font, "Looking for applications...", tmpfontcolor );
1153   if ( clearscreen ) {
1154     dest.x = 20;
1155     dest.y = 20;
1156   } else {
1157     dest.x = 20;
1158     dest.y = 40;
1159   }
1160   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1161   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1162   SDL_FreeSurface ( rtext );
1163
1164   // render icon
1165   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1166     dest.x = rtext -> w + 30;
1167     dest.y = 20;
1168     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, &dest );
1169     SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1170   }
1171
1172   return;
1173 }
1174
1175 void ui_cachescreen ( unsigned char clearscreen, char *filename ) {
1176
1177   SDL_Rect rects [ 4 ];
1178   SDL_Rect *dest = rects;
1179   bzero ( dest, sizeof(SDL_Rect)* 4 );
1180
1181   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1182   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1183   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1184   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1185
1186   static unsigned int stepx = 0;
1187
1188   // clear the screen
1189   if ( clearscreen ) {
1190     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1191
1192     // render background
1193     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1194       dest -> x = 0;
1195       dest -> y = 0;
1196       dest -> w = sdl_realscreen -> w;
1197       dest -> h = sdl_realscreen -> h;
1198       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1199       dest++;
1200     }
1201
1202   }
1203
1204   // render text
1205   SDL_Surface *rtext;
1206   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1207   rtext = TTF_RenderText_Blended ( g_big_font, "Caching applications artwork...", tmpfontcolor );
1208   if ( clearscreen ) {
1209     dest -> x = 20;
1210     dest -> y = 20;
1211   } else {
1212     dest -> x = 20;
1213     dest -> y = 40;
1214   }
1215   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1216   SDL_FreeSurface ( rtext );
1217   dest++;
1218
1219   // render icon
1220   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1221     dest -> x = rtext -> w + 30 + stepx;
1222     dest -> y = 20;
1223     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, dest );
1224     dest++;
1225   }
1226
1227   // filename
1228   if ( filename ) {
1229     rtext = TTF_RenderText_Blended ( g_tab_font, filename, tmpfontcolor );
1230     dest -> x = 20;
1231     dest -> y = 50;
1232     SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1233     SDL_FreeSurface ( rtext );
1234     dest++;
1235   }
1236
1237   // move across
1238   stepx += 20;
1239
1240   if ( stepx > 350 ) {
1241     stepx = 0;
1242   }
1243
1244   SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1245
1246   return;
1247 }
1248
1249 int ui_selected_index ( void ) {
1250
1251   if ( ! ui_selected ) {
1252     return ( -1 ); // no index
1253   }
1254
1255   mm_appref_t *r = g_categories [ ui_category ].refs;
1256   int counter = 0;
1257   while ( r ) {
1258     if ( r == ui_selected ) {
1259       return ( counter );
1260     }
1261     r = r -> next;
1262     counter++;
1263   }
1264
1265   return ( -1 );
1266 }
1267
1268 static mm_appref_t *timer_ref = NULL;
1269 void ui_set_selected ( mm_appref_t *r ) {
1270
1271   if ( ! pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) {
1272     return; // no desire to defer anything
1273   }
1274
1275   if ( ! r ) {
1276     // cancel timer
1277     SDL_SetTimer ( 0, NULL );
1278     timer_ref = NULL;
1279     return;
1280   }
1281
1282   SDL_SetTimer ( pnd_conf_get_as_int_d ( g_conf, "previewpic.defer_timer_ms", 1000 ), ui_callback_f );
1283   timer_ref = r;
1284
1285   return;
1286 }
1287
1288 unsigned int ui_callback_f ( unsigned int t ) {
1289
1290   if ( ui_selected != timer_ref ) {
1291     return ( 0 ); // user has moved it, who cares
1292   }
1293
1294   SDL_Event e;
1295   e.type = SDL_USEREVENT;
1296   SDL_PushEvent ( &e );
1297
1298   return ( 0 );
1299 }
1300
1301 int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *footer ) {
1302   SDL_Rect rects [ 40 ];
1303   SDL_Rect *dest = rects;
1304   SDL_Rect src;
1305   SDL_Surface *rtext;
1306
1307   bzero ( rects, sizeof(SDL_Rect) * 40 );
1308
1309   unsigned int sel = 0;
1310
1311   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1312   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1313   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1314   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1315
1316   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1317
1318   SDL_Color selfontcolor = { 0/*font_rgba_r*/, font_rgba_g, font_rgba_b, font_rgba_a };
1319
1320   unsigned int i;
1321   SDL_Event event;
1322
1323   while ( 1 ) {
1324
1325     // clear
1326     dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1327     dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1328     dest -> w = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> w;
1329     dest -> h = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h;
1330     SDL_FillRect( sdl_realscreen, dest, 0 );
1331
1332     // show dialog background
1333     if ( g_imagecache [ IMG_DETAIL_BG ].i ) {
1334       src.x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1335       src.y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1336       src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> w;
1337       src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> h;
1338       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1339       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1340       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1341       // repeat for darken?
1342       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1343       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1344       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1345       dest++;
1346     }
1347
1348     // show dialog frame
1349     if ( g_imagecache [ IMG_DETAIL_PANEL ].i ) {
1350       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1351       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1352       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_PANEL ].i, NULL /* whole image */, sdl_realscreen, dest );
1353       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1354       dest++;
1355     }
1356
1357     // show header
1358     if ( title ) {
1359       rtext = TTF_RenderText_Blended ( g_tab_font, title, tmpfontcolor );
1360       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1361       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 20;
1362       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1363       SDL_FreeSurface ( rtext );
1364       dest++;
1365     }
1366
1367     // show footer
1368     if ( footer ) {
1369       rtext = TTF_RenderText_Blended ( g_tab_font, footer, tmpfontcolor );
1370       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1371       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) +
1372         ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h
1373         - 60;
1374       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1375       SDL_FreeSurface ( rtext );
1376       dest++;
1377     }
1378
1379     // show options
1380     for ( i = 0; i < argc; i++ ) {
1381
1382       // show options
1383       if ( sel == i ) {
1384         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], selfontcolor );
1385       } else {
1386         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], tmpfontcolor );
1387       }
1388       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1389       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 40 + ( 20 * ( i + 1 ) );
1390       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1391       SDL_FreeSurface ( rtext );
1392       dest++;
1393
1394     } // for
1395
1396     // update all the rects and send it all to sdl
1397     SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1398     dest = rects;
1399
1400     // check for input
1401     while ( SDL_WaitEvent ( &event ) ) {
1402
1403       switch ( event.type ) {
1404
1405       case SDL_KEYUP:
1406
1407         if ( event.key.keysym.sym == SDLK_UP ) {
1408           if ( sel ) {
1409             sel--;
1410           }
1411         } else if ( event.key.keysym.sym == SDLK_DOWN ) {
1412           if ( sel < argc - 1 ) {
1413             sel++;
1414           }
1415
1416         } else if ( event.key.keysym.sym == SDLK_RETURN ) {
1417           return ( sel );
1418
1419         } else if ( event.key.keysym.sym == SDLK_q ) {
1420           exit ( 0 );
1421
1422         } else {
1423           return ( -1 ); // nada
1424         }
1425
1426         break;
1427
1428       } // switch
1429
1430       break;
1431     } // while
1432
1433   } // while
1434
1435   return ( -1 );
1436 }