Added basic touchscreen
[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     unsigned char batterylevel = pnd_device_get_battery_gauge_perc();
677     char buffer [ 100 ];
678
679     sprintf ( buffer, "Battery: %u%%", batterylevel );
680
681     SDL_Surface *rtext;
682     SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
683     rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor );
684     dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.battery_x", 20 );
685     dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.battery_y", 450 );
686     SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
687     SDL_FreeSurface ( rtext );
688     dest++;
689   }
690
691   // hints
692   if ( pnd_conf_get_as_char ( g_conf, "display.hintline" ) ) {
693     char *buffer = pnd_conf_get_as_char ( g_conf, "display.hintline" );
694     SDL_Surface *rtext;
695     SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
696     rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor );
697     dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.hint_x", 40 );
698     dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.hint_y", 450 );
699     SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
700     SDL_FreeSurface ( rtext );
701     dest++;
702   }
703
704   // hints
705   if ( pnd_conf_get_as_int_d ( g_conf, "display.clock_x", -1 ) != -1 ) {
706     char buffer [ 50 ];
707
708     time_t t = time ( NULL );
709     struct tm *tm = localtime ( &t );
710     strftime ( buffer, 50, "%a %H:%M %F", tm );
711
712     SDL_Surface *rtext;
713     SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
714     rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor );
715     dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.clock_x", 700 );
716     dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.clock_y", 450 );
717     SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
718     SDL_FreeSurface ( rtext );
719     dest++;
720   }
721
722   // update all the rects and send it all to sdl
723   SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
724
725 } // ui_render
726
727 void ui_process_input ( unsigned char block_p ) {
728   SDL_Event event;
729
730   unsigned char ui_event = 0; // if we get a ui event, flip to 1 and break
731   //static ui_sdl_button_e ui_mask = uisb_none; // current buttons down
732
733   while ( ! ui_event &&
734           block_p ? SDL_WaitEvent ( &event ) : SDL_PollEvent ( &event ) )
735   {
736
737     switch ( event.type ) {
738
739     case SDL_USEREVENT:
740       // update something
741
742       if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) {
743
744         pnd_log ( pndn_debug, "Deferred preview pic load ----------\n" );
745
746         // load the preview pics now!
747         pnd_disco_t *iter = ui_selected -> ref;
748
749         if ( iter -> preview_pic1 &&
750              ! 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 ) ) )
751         {
752           pnd_log ( pndn_debug, "  Couldn't load preview pic: '%s' -> '%s'\n", IFNULL(iter->title_en,"No Name"), iter -> preview_pic1 );
753         }
754
755         pnd_log ( pndn_debug, "Deferred preview pic load finish ---\n" );
756
757         ui_event++;
758       }
759
760       break;
761
762 #if 0 // joystick motion
763     case SDL_JOYAXISMOTION:
764
765       pnd_log ( PND_LOG_DEFAULT, "joystick axis\n" );
766
767         if ( event.jaxis.axis == 0 ) {
768           // horiz
769           if ( event.jaxis.value < 0 ) {
770             ui_push_left ( 0 );
771             pnd_log ( PND_LOG_DEFAULT, "joystick axis - LEFT\n" );
772           } else if ( event.jaxis.value > 0 ) {
773             ui_push_right ( 0 );
774             pnd_log ( PND_LOG_DEFAULT, "joystick axis - RIGHT\n" );
775           }
776         } else if ( event.jaxis.axis == 1 ) {
777           // vert
778           if ( event.jaxis.value < 0 ) {
779             ui_push_up();
780           } else if ( event.jaxis.value > 0 ) {
781             ui_push_down();
782           }
783         }
784
785         ui_event++;
786
787         break;
788 #endif
789
790 #if 0 // joystick buttons
791     case SDL_JOYBUTTONDOWN:
792
793       pnd_log ( PND_LOG_DEFAULT, "joystick button down %u\n", event.jbutton.button );
794
795       if ( event.jbutton.button == 0 ) { // B
796         ui_mask |= uisb_b;
797       } else if ( event.jbutton.button == 1 ) { // Y
798         ui_mask |= uisb_y;
799       } else if ( event.jbutton.button == 2 ) { // X
800         ui_mask |= uisb_x;
801       } else if ( event.jbutton.button == 3 ) { // A
802         ui_mask |= uisb_a;
803
804       } else if ( event.jbutton.button == 4 ) { // Select
805         ui_mask |= uisb_select;
806       } else if ( event.jbutton.button == 5 ) { // Start
807         ui_mask |= uisb_start;
808
809       } else if ( event.jbutton.button == 7 ) { // L
810         ui_mask |= uisb_l;
811       } else if ( event.jbutton.button == 8 ) { // R
812         ui_mask |= uisb_r;
813
814       }
815
816       ui_event++;
817
818       break;
819
820     case SDL_JOYBUTTONUP:
821
822       pnd_log ( PND_LOG_DEFAULT, "joystick button up %u\n", event.jbutton.button );
823
824       if ( event.jbutton.button == 0 ) { // B
825         ui_mask &= ~uisb_b;
826         ui_push_exec();
827       } else if ( event.jbutton.button == 1 ) { // Y
828         ui_mask &= ~uisb_y;
829       } else if ( event.jbutton.button == 2 ) { // X
830         ui_mask &= ~uisb_x;
831       } else if ( event.jbutton.button == 3 ) { // A
832         ui_mask &= ~uisb_a;
833
834       } else if ( event.jbutton.button == 4 ) { // Select
835         ui_mask &= ~uisb_select;
836       } else if ( event.jbutton.button == 5 ) { // Start
837         ui_mask &= ~uisb_start;
838
839       } else if ( event.jbutton.button == 7 ) { // L
840         ui_mask &= ~uisb_l;
841         ui_push_ltrigger();
842       } else if ( event.jbutton.button == 8 ) { // R
843         ui_mask &= ~uisb_r;
844         ui_push_rtrigger();
845
846       }
847
848       ui_event++;
849
850       break;
851 #endif
852
853 #if 1 // keyboard events
854     case SDL_KEYUP:
855
856       //pnd_log ( pndn_debug, "key up %u\n", event.key.keysym.sym );
857
858       // SDLK_LALT -> Start
859
860       // directional
861       if ( event.key.keysym.sym == SDLK_RIGHT ) {
862         ui_push_right ( 0 );
863         ui_event++;
864       } else if ( event.key.keysym.sym == SDLK_LEFT ) {
865         ui_push_left ( 0 );
866         ui_event++;
867       } else if ( event.key.keysym.sym == SDLK_UP ) {
868         ui_push_up();
869         ui_event++;
870       } else if ( event.key.keysym.sym == SDLK_DOWN ) {
871         ui_push_down();
872         ui_event++;
873       } else if ( event.key.keysym.sym == SDLK_SPACE || event.key.keysym.sym == SDLK_END ) {
874         ui_push_exec();
875         ui_event++;
876       } else if ( event.key.keysym.sym == SDLK_z || event.key.keysym.sym == SDLK_RSHIFT ) {
877         ui_push_ltrigger();
878         ui_event++;
879       } else if ( event.key.keysym.sym == SDLK_x || event.key.keysym.sym == SDLK_RCTRL ) {
880         ui_push_rtrigger();
881         ui_event++;
882       } else if ( event.key.keysym.sym == SDLK_y || event.key.keysym.sym == SDLK_PAGEUP ) {
883         // info
884         if ( ui_selected ) {
885           ui_show_info ( pnd_run_script, ui_selected -> ref );
886           ui_event++;
887         }
888
889       } else if ( event.key.keysym.sym == SDLK_LALT ) { // start button
890         ui_push_exec();
891         ui_event++;
892
893       } else if ( event.key.keysym.sym == SDLK_LCTRL /*LALT*/ ) { // select button
894         char *opts [ 20 ] = {
895           "Return to Minimenu",
896           "Shutdown Pandora",
897           "Rescan for Applications",
898           "Run xfce4 from Minimenu",
899           "Set to full desktop and reboot",
900           "Set to pmenu and reboot",
901           "Quit (<- beware)",
902           "About Minimenu"
903         };
904         int sel = ui_modal_single_menu ( opts, 8, "Minimenu", "Enter to select; other to return." );
905
906         char buffer [ 100 ];
907         if ( sel == 0 ) {
908           // do nothing
909         } else if ( sel == 1 ) {
910           // shutdown
911           sprintf ( buffer, "sudo poweroff" );
912           system ( buffer );
913         } else if ( sel == 2 ) {
914           // rescan apps
915           pnd_log ( pndn_debug, "Freeing up applications\n" );
916           applications_free();
917           pnd_log ( pndn_debug, "Rescanning applications\n" );
918           applications_scan();
919           // reset view
920           ui_selected = NULL;
921           ui_rows_scrolled_down = 0;
922         } else if ( sel == 3 ) {
923           // run xfce
924           char buffer [ PATH_MAX ];
925           sprintf ( buffer, "%s %s\n", MM_RUN, "/usr/bin/startxfce4" );
926           emit_and_quit ( buffer );
927         } else if ( sel == 4 ) {
928           // set env to xfce
929           sprintf ( buffer, "echo startxfce4 > /tmp/gui.load" );
930           system ( buffer );
931           sprintf ( buffer, "sudo poweroff" );
932           system ( buffer );
933         } else if ( sel == 5 ) {
934           // set env to pmenu
935           sprintf ( buffer, "echo pmenu > /tmp/gui.load" );
936           system ( buffer );
937           sprintf ( buffer, "sudo poweroff" );
938           system ( buffer );
939         } else if ( sel == 6 ) {
940           emit_and_quit ( MM_QUIT );
941         } else if ( sel == 7 ) {
942           // about
943         }
944
945         ui_event++;
946       }
947
948       // extras
949       if ( event.key.keysym.sym == SDLK_q ) {
950         emit_and_quit ( MM_QUIT );
951       }
952
953       break;
954 #endif
955
956 #if 1 // mouse / touchscreen
957 #if 0
958     case SDL_MOUSEBUTTONDOWN:
959       if ( event.button.button == SDL_BUTTON_LEFT ) {
960         cb_pointer_press ( gc, event.button.x / g_scale, event.button.y / g_scale );
961         ui_event++;
962       }
963       break;
964 #endif
965
966     case SDL_MOUSEBUTTONUP:
967       if ( event.button.button == SDL_BUTTON_LEFT ) {
968         ui_touch_act ( event.button.x, event.button.y );
969         ui_event++;
970       }
971       break;
972 #endif
973
974     case SDL_QUIT:
975       exit ( 0 );
976       break;
977
978     default:
979       break;
980
981     } // switch event type
982
983   } // while poll
984
985   return;
986 }
987
988 void ui_push_left ( unsigned char forcecoil ) {
989
990   if ( ! ui_selected ) {
991     ui_push_right ( 0 );
992     return;
993   }
994
995   // what column we in?
996   unsigned int col = ui_determine_screen_col ( ui_selected );
997
998   // are we alreadt at first item?
999   if ( forcecoil == 0 &&
1000        pnd_conf_get_as_int_d ( g_conf, "grid.wrap_horiz_samerow", 0 ) &&
1001        col == 0 )
1002   {
1003     unsigned int i = pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1;
1004     while ( i ) {
1005       ui_push_right ( 0 );
1006       i--;
1007     }
1008
1009   } else if ( g_categories [ ui_category ].refs == ui_selected ) {
1010     // can't go any more left, we're at the head
1011
1012   } else {
1013     // figure out the previous item; yay for singly linked list :/
1014     mm_appref_t *i = g_categories [ ui_category ].refs;
1015     while ( i ) {
1016       if ( i -> next == ui_selected ) {
1017         ui_selected = i;
1018         break;
1019       }
1020       i = i -> next;
1021     }
1022   }
1023
1024   ui_set_selected ( ui_selected );
1025
1026   return;
1027 }
1028
1029 void ui_push_right ( unsigned char forcecoil ) {
1030
1031   if ( ui_selected ) {
1032
1033     // what column we in?
1034     unsigned int col = ui_determine_screen_col ( ui_selected );
1035
1036     // wrap same or no?
1037     if ( forcecoil == 0 &&
1038          pnd_conf_get_as_int_d ( g_conf, "grid.wrap_horiz_samerow", 0 ) &&
1039          col == pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1 )
1040     {
1041       // same wrap
1042       unsigned int i = pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1;
1043       while ( i ) {
1044         ui_push_left ( 0 );
1045         i--;
1046       }
1047
1048     } else {
1049       // just go to the next
1050
1051       if ( ui_selected -> next ) {
1052         ui_selected = ui_selected -> next;
1053       }
1054
1055     }
1056
1057   } else {
1058     ui_selected = g_categories [ ui_category ].refs;
1059   }
1060
1061   ui_set_selected ( ui_selected );
1062
1063   return;
1064 }
1065
1066 void ui_push_up ( void ) {
1067   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
1068
1069   // what row we in?
1070   unsigned int row = ui_determine_row ( ui_selected );
1071
1072   if ( row == 0 &&
1073        pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 1 ) == 0 )
1074   {
1075     // wrap around instead
1076
1077     unsigned int col = ui_determine_screen_col ( ui_selected );
1078
1079     // go to end
1080     ui_selected = g_categories [ ui_category ].refs;
1081     while ( ui_selected -> next ) {
1082       ui_selected = ui_selected -> next;
1083     }
1084
1085     // try to move to same column
1086     unsigned int newcol = ui_determine_screen_col ( ui_selected );
1087     if ( newcol > col ) {
1088       col = newcol - col;
1089       while ( col ) {
1090         ui_push_left ( 0 );
1091         col--;
1092       }
1093     }
1094
1095     // scroll down to show it
1096     int r = ui_determine_row ( ui_selected ) - 1;
1097     if ( r - pnd_conf_get_as_int ( g_conf, MMENU_DISP_ROWMAX ) > 0 ) {
1098       ui_rows_scrolled_down = (unsigned int) r;
1099     }
1100
1101   } else {
1102     // stop at top/bottom
1103
1104     while ( col_max ) {
1105       ui_push_left ( 1 );
1106       col_max--;
1107     }
1108
1109   }
1110
1111   return;
1112 }
1113
1114 void ui_push_down ( void ) {
1115   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
1116
1117   if ( ui_selected ) {
1118
1119     // what row we in?
1120     unsigned int row = ui_determine_row ( ui_selected );
1121
1122     // max rows?
1123     unsigned int icon_rows = g_categories [ ui_category ].refcount / col_max;
1124     if ( g_categories [ ui_category ].refcount % col_max > 0 ) {
1125       icon_rows++;
1126     }
1127
1128     // we at the end?
1129     if ( row == ( icon_rows - 1 ) &&
1130          pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 1 ) == 0 )
1131     {
1132
1133       unsigned char col = ui_determine_screen_col ( ui_selected );
1134
1135       ui_selected = g_categories [ ui_category ].refs;
1136
1137       while ( col ) {
1138         ui_selected = ui_selected -> next;
1139         col--;
1140       }
1141
1142       ui_rows_scrolled_down = 0;
1143
1144     } else {
1145
1146       while ( col_max ) {
1147         ui_push_right ( 1 );
1148         col_max--;
1149       }
1150
1151     }
1152
1153   } else {
1154     ui_push_right ( 0 );
1155   }
1156
1157   return;
1158 }
1159
1160 void ui_push_exec ( void ) {
1161
1162   if ( ui_selected ) {
1163     char buffer [ PATH_MAX ];
1164     sprintf ( buffer, "%s/%s", ui_selected -> ref -> object_path, ui_selected -> ref -> object_filename );
1165     pnd_apps_exec ( pnd_run_script,
1166                     buffer,
1167                     ui_selected -> ref -> unique_id,
1168                     ui_selected -> ref -> exec,
1169                     ui_selected -> ref -> startdir,
1170                     ui_selected -> ref -> execargs,
1171                     atoi ( ui_selected -> ref -> clockspeed ),
1172                     PND_EXEC_OPTION_NORUN );
1173     sprintf ( buffer, "%s %s\n", MM_RUN, pnd_apps_exec_runline() );
1174     emit_and_quit ( buffer );
1175   }
1176
1177   return;
1178 }
1179
1180 void ui_push_ltrigger ( void ) {
1181   unsigned char oldcat = ui_category;
1182
1183   if ( ui_category > 0 ) {
1184     ui_category--;
1185   } else {
1186     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1187       ui_category = g_categorycount - 1;
1188     }
1189   }
1190
1191   if ( oldcat != ui_category ) {
1192     ui_selected = NULL;
1193     ui_set_selected ( ui_selected );
1194   }
1195
1196   // make tab visible?
1197   if ( ui_catshift > 0 && ui_category == ui_catshift - 1 ) {
1198     ui_catshift--;
1199   }
1200
1201   // unscroll
1202   ui_rows_scrolled_down = 0;
1203
1204   return;
1205 }
1206
1207 void ui_push_rtrigger ( void ) {
1208   unsigned char oldcat = ui_category;
1209
1210   unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 );
1211   unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
1212
1213   if ( ui_category < ( g_categorycount - 1 ) ) {
1214     ui_category++;
1215   } else {
1216     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1217       ui_category = 0;
1218     }
1219   }
1220
1221   if ( oldcat != ui_category ) {
1222     ui_selected = NULL;
1223     ui_set_selected ( ui_selected );
1224   }
1225
1226   // make tab visible?
1227   if ( ui_category > ui_catshift + ( screen_width / tab_width ) - 1 ) {
1228     ui_catshift++;
1229   }
1230
1231   // unscroll
1232   ui_rows_scrolled_down = 0;
1233
1234   return;
1235 }
1236
1237 SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ) {
1238   double scale = 1000000.0;
1239   double scalex = 1000000.0;
1240   double scaley = 1000000.0;
1241   SDL_Surface *scaled;
1242
1243   scalex = (double)maxwidth / (double)s -> w;
1244
1245   if ( maxheight == -1 ) {
1246     scale = scalex;
1247   } else {
1248     scaley = (double)maxheight / (double)s -> h;
1249
1250     if ( scaley < scalex ) {
1251       scale = scaley;
1252     } else {
1253       scale = scalex;
1254     }
1255
1256   }
1257
1258   scaled = rotozoomSurface ( s, 0 /* angle*/, scale /* scale */, 1 /* smooth==1*/ );
1259   SDL_FreeSurface ( s );
1260   s = scaled;
1261
1262   return ( s );
1263 }
1264
1265 void ui_loadscreen ( void ) {
1266
1267   SDL_Rect dest;
1268
1269   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1270   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1271   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1272   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1273
1274   // clear the screen
1275   SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1276
1277   // render text
1278   SDL_Surface *rtext;
1279   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1280   rtext = TTF_RenderText_Blended ( g_big_font, "Setting up menu...", tmpfontcolor );
1281   dest.x = 20;
1282   dest.y = 20;
1283   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1284   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1285   SDL_FreeSurface ( rtext );
1286
1287   return;
1288 }
1289
1290 void ui_discoverscreen ( unsigned char clearscreen ) {
1291
1292   SDL_Rect dest;
1293
1294   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1295   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1296   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1297   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1298
1299   // clear the screen
1300   if ( clearscreen ) {
1301     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1302
1303     // render background
1304     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1305       dest.x = 0;
1306       dest.y = 0;
1307       dest.w = sdl_realscreen -> w;
1308       dest.h = sdl_realscreen -> h;
1309       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1310       SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1311     }
1312
1313   }
1314
1315   // render text
1316   SDL_Surface *rtext;
1317   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1318   rtext = TTF_RenderText_Blended ( g_big_font, "Looking for applications...", tmpfontcolor );
1319   if ( clearscreen ) {
1320     dest.x = 20;
1321     dest.y = 20;
1322   } else {
1323     dest.x = 20;
1324     dest.y = 40;
1325   }
1326   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1327   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1328   SDL_FreeSurface ( rtext );
1329
1330   // render icon
1331   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1332     dest.x = rtext -> w + 30;
1333     dest.y = 20;
1334     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, &dest );
1335     SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1336   }
1337
1338   return;
1339 }
1340
1341 void ui_cachescreen ( unsigned char clearscreen, char *filename ) {
1342
1343   SDL_Rect rects [ 4 ];
1344   SDL_Rect *dest = rects;
1345   bzero ( dest, sizeof(SDL_Rect)* 4 );
1346
1347   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1348   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1349   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1350   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1351
1352   static unsigned int stepx = 0;
1353
1354   // clear the screen
1355   if ( clearscreen ) {
1356     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1357
1358     // render background
1359     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1360       dest -> x = 0;
1361       dest -> y = 0;
1362       dest -> w = sdl_realscreen -> w;
1363       dest -> h = sdl_realscreen -> h;
1364       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1365       dest++;
1366     }
1367
1368   }
1369
1370   // render text
1371   SDL_Surface *rtext;
1372   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1373   rtext = TTF_RenderText_Blended ( g_big_font, "Caching applications artwork...", tmpfontcolor );
1374   if ( clearscreen ) {
1375     dest -> x = 20;
1376     dest -> y = 20;
1377   } else {
1378     dest -> x = 20;
1379     dest -> y = 40;
1380   }
1381   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1382   SDL_FreeSurface ( rtext );
1383   dest++;
1384
1385   // render icon
1386   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1387     dest -> x = rtext -> w + 30 + stepx;
1388     dest -> y = 20;
1389     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, dest );
1390     dest++;
1391   }
1392
1393   // filename
1394   if ( filename ) {
1395     rtext = TTF_RenderText_Blended ( g_tab_font, filename, tmpfontcolor );
1396     dest -> x = 20;
1397     dest -> y = 50;
1398     SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1399     SDL_FreeSurface ( rtext );
1400     dest++;
1401   }
1402
1403   // move across
1404   stepx += 20;
1405
1406   if ( stepx > 350 ) {
1407     stepx = 0;
1408   }
1409
1410   SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1411
1412   return;
1413 }
1414
1415 int ui_selected_index ( void ) {
1416
1417   if ( ! ui_selected ) {
1418     return ( -1 ); // no index
1419   }
1420
1421   mm_appref_t *r = g_categories [ ui_category ].refs;
1422   int counter = 0;
1423   while ( r ) {
1424     if ( r == ui_selected ) {
1425       return ( counter );
1426     }
1427     r = r -> next;
1428     counter++;
1429   }
1430
1431   return ( -1 );
1432 }
1433
1434 static mm_appref_t *timer_ref = NULL;
1435 void ui_set_selected ( mm_appref_t *r ) {
1436
1437   if ( ! pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) {
1438     return; // no desire to defer anything
1439   }
1440
1441   if ( ! r ) {
1442     // cancel timer
1443     SDL_SetTimer ( 0, NULL );
1444     timer_ref = NULL;
1445     return;
1446   }
1447
1448   SDL_SetTimer ( pnd_conf_get_as_int_d ( g_conf, "previewpic.defer_timer_ms", 1000 ), ui_callback_f );
1449   timer_ref = r;
1450
1451   return;
1452 }
1453
1454 unsigned int ui_callback_f ( unsigned int t ) {
1455
1456   if ( ui_selected != timer_ref ) {
1457     return ( 0 ); // user has moved it, who cares
1458   }
1459
1460   SDL_Event e;
1461   e.type = SDL_USEREVENT;
1462   SDL_PushEvent ( &e );
1463
1464   return ( 0 );
1465 }
1466
1467 int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *footer ) {
1468   SDL_Rect rects [ 40 ];
1469   SDL_Rect *dest = rects;
1470   SDL_Rect src;
1471   SDL_Surface *rtext;
1472
1473   bzero ( rects, sizeof(SDL_Rect) * 40 );
1474
1475   unsigned int sel = 0;
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   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1483
1484   SDL_Color selfontcolor = { 0/*font_rgba_r*/, font_rgba_g, font_rgba_b, font_rgba_a };
1485
1486   unsigned int i;
1487   SDL_Event event;
1488
1489   while ( 1 ) {
1490
1491     // clear
1492     dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1493     dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1494     dest -> w = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> w;
1495     dest -> h = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h;
1496     SDL_FillRect( sdl_realscreen, dest, 0 );
1497
1498     // show dialog background
1499     if ( g_imagecache [ IMG_DETAIL_BG ].i ) {
1500       src.x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1501       src.y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1502       src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> w;
1503       src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> h;
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       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1507       // repeat for darken?
1508       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1509       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1510       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1511       dest++;
1512     }
1513
1514     // show dialog frame
1515     if ( g_imagecache [ IMG_DETAIL_PANEL ].i ) {
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_PANEL ].i, NULL /* whole image */, sdl_realscreen, dest );
1519       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1520       dest++;
1521     }
1522
1523     // show header
1524     if ( title ) {
1525       rtext = TTF_RenderText_Blended ( g_tab_font, title, tmpfontcolor );
1526       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1527       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 20;
1528       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1529       SDL_FreeSurface ( rtext );
1530       dest++;
1531     }
1532
1533     // show footer
1534     if ( footer ) {
1535       rtext = TTF_RenderText_Blended ( g_tab_font, footer, tmpfontcolor );
1536       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1537       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) +
1538         ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h
1539         - 60;
1540       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1541       SDL_FreeSurface ( rtext );
1542       dest++;
1543     }
1544
1545     // show options
1546     for ( i = 0; i < argc; i++ ) {
1547
1548       // show options
1549       if ( sel == i ) {
1550         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], selfontcolor );
1551       } else {
1552         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], tmpfontcolor );
1553       }
1554       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1555       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 40 + ( 20 * ( i + 1 ) );
1556       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1557       SDL_FreeSurface ( rtext );
1558       dest++;
1559
1560     } // for
1561
1562     // update all the rects and send it all to sdl
1563     SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1564     dest = rects;
1565
1566     // check for input
1567     while ( SDL_WaitEvent ( &event ) ) {
1568
1569       switch ( event.type ) {
1570
1571       case SDL_KEYUP:
1572
1573         if ( event.key.keysym.sym == SDLK_UP ) {
1574           if ( sel ) {
1575             sel--;
1576           }
1577         } else if ( event.key.keysym.sym == SDLK_DOWN ) {
1578           if ( sel < argc - 1 ) {
1579             sel++;
1580           }
1581
1582         } else if ( event.key.keysym.sym == SDLK_RETURN ) {
1583           return ( sel );
1584
1585         } else if ( event.key.keysym.sym == SDLK_q ) {
1586           exit ( 0 );
1587
1588         } else {
1589           return ( -1 ); // nada
1590         }
1591
1592         break;
1593
1594       } // switch
1595
1596       break;
1597     } // while
1598
1599   } // while
1600
1601   return ( -1 );
1602 }
1603
1604 unsigned char ui_determine_row ( mm_appref_t *a ) {
1605   unsigned int row = 0;
1606
1607   mm_appref_t *i = g_categories [ ui_category ].refs;
1608   while ( i != a ) {
1609     i = i -> next;
1610     row++;
1611   } // while
1612   row /= pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 );
1613
1614   return ( row );
1615 }
1616
1617 unsigned char ui_determine_screen_row ( mm_appref_t *a ) {
1618   return ( ui_determine_row ( a ) % pnd_conf_get_as_int_d ( g_conf, "grid.row_max", 5 ) );
1619 }
1620
1621 unsigned char ui_determine_screen_col ( mm_appref_t *a ) {
1622   unsigned int col = 0;
1623
1624   mm_appref_t *i = g_categories [ ui_category ].refs;
1625   while ( i != a ) {
1626     i = i -> next;
1627     col++;
1628   } // while
1629   col %= pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 );
1630
1631   return ( col );
1632 }
1633
1634 unsigned char ui_show_info ( char *pndrun, pnd_disco_t *p ) {
1635   char *viewer, *searchpath;
1636   pnd_conf_handle desktoph;
1637
1638   // viewer
1639   searchpath = pnd_conf_query_searchpath();
1640
1641   desktoph = pnd_conf_fetch_by_id ( pnd_conf_desktop, searchpath );
1642
1643   if ( ! desktoph ) {
1644     return ( 0 );
1645   }
1646
1647   viewer = pnd_conf_get_as_char ( desktoph, "info.viewer" );
1648
1649   if ( ! viewer ) {
1650     return ( 0 ); // no way to view the file
1651   }
1652
1653   // etc
1654   if ( ! p -> unique_id ) {
1655     return ( 0 );
1656   }
1657
1658   if ( ! p -> info_filename ) {
1659     return ( 0 );
1660   }
1661
1662   if ( ! p -> info_name ) {
1663     return ( 0 );
1664   }
1665
1666   if ( ! pndrun ) {
1667     return ( 0 );
1668   }
1669
1670   // exec line
1671   char args [ 1001 ];
1672   char *pargs = args;
1673   if ( pnd_conf_get_as_char ( desktoph, "info.viewer_args" ) ) {
1674     snprintf ( pargs, 1001, "%s %s",
1675                pnd_conf_get_as_char ( desktoph, "info.viewer_args" ), p -> info_filename );
1676   } else {
1677     pargs = NULL;
1678   }
1679
1680   char pndfile [ 1024 ];
1681   if ( p -> object_type == pnd_object_type_directory ) {
1682     // for PXML-app-dir, pnd_run.sh doesn't want the PXML.xml.. it just wants the dir-name
1683     strncpy ( pndfile, p -> object_path, 1000 );
1684   } else if ( p -> object_type == pnd_object_type_pnd ) {
1685     // pnd_run.sh wants the full path and filename for the .pnd file
1686     snprintf ( pndfile, 1020, "%s/%s", p -> object_path, p -> object_filename );
1687   }
1688
1689   if ( ! pnd_apps_exec ( pndrun, pndfile, p -> unique_id, viewer, p -> startdir, pargs,
1690                          p -> clockspeed ? atoi ( p -> clockspeed ) : 0, PND_EXEC_OPTION_NORUN ) )
1691   {
1692     return ( 0 );
1693   }
1694
1695   pnd_log ( pndn_debug, "Info Exec=%s\n", pnd_apps_exec_runline() );
1696
1697   // try running it
1698   int x;
1699   if ( ( x = fork() ) < 0 ) {
1700     pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" );
1701     return ( 0 );
1702   }
1703
1704   if ( x == 0 ) {
1705     execl ( "/bin/sh", "/bin/sh", "-c", pnd_apps_exec_runline(), (char*)NULL );
1706     pnd_log ( pndn_error, "ERROR: Couldn't exec(%s)\n", pnd_apps_exec_runline() );
1707     return ( 0 );
1708   }
1709
1710   return ( 1 );
1711 }
1712
1713 typedef struct {
1714   SDL_Rect r;
1715   int catnum;
1716   mm_appref_t *ref;
1717 } ui_touch_t;
1718 #define MAXTOUCH 100
1719 ui_touch_t ui_touchrects [ MAXTOUCH ];
1720 unsigned char ui_touchrect_count = 0;
1721
1722 void ui_register_reset ( void ) {
1723   bzero ( ui_touchrects, sizeof(ui_touch_t)*MAXTOUCH );
1724   ui_touchrect_count = 0;
1725   return;
1726 }
1727
1728 void ui_register_tab ( unsigned char catnum, unsigned int x, unsigned int y, unsigned int w, unsigned int h ) {
1729
1730   if ( ui_touchrect_count == MAXTOUCH ) {
1731     return;
1732   }
1733
1734   ui_touchrects [ ui_touchrect_count ].r.x = x;
1735   ui_touchrects [ ui_touchrect_count ].r.y = y;
1736   ui_touchrects [ ui_touchrect_count ].r.w = w;
1737   ui_touchrects [ ui_touchrect_count ].r.h = h;
1738   ui_touchrects [ ui_touchrect_count ].catnum = catnum;
1739   ui_touchrect_count++;
1740
1741   return;
1742 }
1743
1744 void ui_register_app ( mm_appref_t *app, unsigned int x, unsigned int y, unsigned int w, unsigned int h ) {
1745
1746   if ( ui_touchrect_count == MAXTOUCH ) {
1747     return;
1748   }
1749
1750   ui_touchrects [ ui_touchrect_count ].r.x = x;
1751   ui_touchrects [ ui_touchrect_count ].r.y = y;
1752   ui_touchrects [ ui_touchrect_count ].r.w = w;
1753   ui_touchrects [ ui_touchrect_count ].r.h = h;
1754   ui_touchrects [ ui_touchrect_count ].ref = app;
1755   ui_touchrect_count++;
1756
1757   return;
1758 }
1759
1760 void ui_touch_act ( unsigned int x, unsigned int y ) {
1761
1762   unsigned char i;
1763   ui_touch_t *t;
1764
1765   for ( i = 0; i < ui_touchrect_count; i++ ) {
1766     t = &(ui_touchrects [ i ]);
1767
1768     if ( x >= t -> r.x &&
1769          x <= t -> r.x + t -> r.w &&
1770          y >= t -> r.y &&
1771          y <= t -> r.y + t -> r.h
1772        )
1773     {
1774
1775       if ( t -> ref ) {
1776         ui_selected = t -> ref;
1777         ui_push_exec();
1778       } else {
1779         ui_category = t -> catnum;
1780       }
1781
1782       break;
1783     }
1784
1785   } // for
1786
1787   return;
1788 }