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