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