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