Better handling of xfce, pmenu from mmenu
[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           "Exit and run xfce4",
900           "Exit and run pmenu",
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           exit ( 0 );
934         } else if ( sel == 5 ) {
935           // set env to pmenu
936           sprintf ( buffer, "echo pmenu > /tmp/gui.load" );
937           system ( buffer );
938           //sprintf ( buffer, "sudo poweroff" );
939           //system ( buffer );
940           exit ( 0 );
941         } else if ( sel == 6 ) {
942           emit_and_quit ( MM_QUIT );
943         } else if ( sel == 7 ) {
944           // about
945         }
946
947         ui_event++;
948       }
949
950       // extras
951       if ( event.key.keysym.sym == SDLK_q ) {
952         emit_and_quit ( MM_QUIT );
953       }
954
955       break;
956 #endif
957
958 #if 1 // mouse / touchscreen
959 #if 0
960     case SDL_MOUSEBUTTONDOWN:
961       if ( event.button.button == SDL_BUTTON_LEFT ) {
962         cb_pointer_press ( gc, event.button.x / g_scale, event.button.y / g_scale );
963         ui_event++;
964       }
965       break;
966 #endif
967
968     case SDL_MOUSEBUTTONUP:
969       if ( event.button.button == SDL_BUTTON_LEFT ) {
970         ui_touch_act ( event.button.x, event.button.y );
971         ui_event++;
972       }
973       break;
974 #endif
975
976     case SDL_QUIT:
977       exit ( 0 );
978       break;
979
980     default:
981       break;
982
983     } // switch event type
984
985   } // while poll
986
987   return;
988 }
989
990 void ui_push_left ( unsigned char forcecoil ) {
991
992   if ( ! ui_selected ) {
993     ui_push_right ( 0 );
994     return;
995   }
996
997   // what column we in?
998   unsigned int col = ui_determine_screen_col ( ui_selected );
999
1000   // are we alreadt at first item?
1001   if ( forcecoil == 0 &&
1002        pnd_conf_get_as_int_d ( g_conf, "grid.wrap_horiz_samerow", 0 ) &&
1003        col == 0 )
1004   {
1005     unsigned int i = pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1;
1006     while ( i ) {
1007       ui_push_right ( 0 );
1008       i--;
1009     }
1010
1011   } else if ( g_categories [ ui_category ].refs == ui_selected ) {
1012     // can't go any more left, we're at the head
1013
1014   } else {
1015     // figure out the previous item; yay for singly linked list :/
1016     mm_appref_t *i = g_categories [ ui_category ].refs;
1017     while ( i ) {
1018       if ( i -> next == ui_selected ) {
1019         ui_selected = i;
1020         break;
1021       }
1022       i = i -> next;
1023     }
1024   }
1025
1026   ui_set_selected ( ui_selected );
1027
1028   return;
1029 }
1030
1031 void ui_push_right ( unsigned char forcecoil ) {
1032
1033   if ( ui_selected ) {
1034
1035     // what column we in?
1036     unsigned int col = ui_determine_screen_col ( ui_selected );
1037
1038     // wrap same or no?
1039     if ( forcecoil == 0 &&
1040          pnd_conf_get_as_int_d ( g_conf, "grid.wrap_horiz_samerow", 0 ) &&
1041          col == pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1 )
1042     {
1043       // same wrap
1044       unsigned int i = pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1;
1045       while ( i ) {
1046         ui_push_left ( 0 );
1047         i--;
1048       }
1049
1050     } else {
1051       // just go to the next
1052
1053       if ( ui_selected -> next ) {
1054         ui_selected = ui_selected -> next;
1055       }
1056
1057     }
1058
1059   } else {
1060     ui_selected = g_categories [ ui_category ].refs;
1061   }
1062
1063   ui_set_selected ( ui_selected );
1064
1065   return;
1066 }
1067
1068 void ui_push_up ( void ) {
1069   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
1070
1071   // what row we in?
1072   unsigned int row = ui_determine_row ( ui_selected );
1073
1074   if ( row == 0 &&
1075        pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 1 ) == 0 )
1076   {
1077     // wrap around instead
1078
1079     unsigned int col = ui_determine_screen_col ( ui_selected );
1080
1081     // go to end
1082     ui_selected = g_categories [ ui_category ].refs;
1083     while ( ui_selected -> next ) {
1084       ui_selected = ui_selected -> next;
1085     }
1086
1087     // try to move to same column
1088     unsigned int newcol = ui_determine_screen_col ( ui_selected );
1089     if ( newcol > col ) {
1090       col = newcol - col;
1091       while ( col ) {
1092         ui_push_left ( 0 );
1093         col--;
1094       }
1095     }
1096
1097     // scroll down to show it
1098     int r = ui_determine_row ( ui_selected ) - 1;
1099     if ( r - pnd_conf_get_as_int ( g_conf, MMENU_DISP_ROWMAX ) > 0 ) {
1100       ui_rows_scrolled_down = (unsigned int) r;
1101     }
1102
1103   } else {
1104     // stop at top/bottom
1105
1106     while ( col_max ) {
1107       ui_push_left ( 1 );
1108       col_max--;
1109     }
1110
1111   }
1112
1113   return;
1114 }
1115
1116 void ui_push_down ( void ) {
1117   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
1118
1119   if ( ui_selected ) {
1120
1121     // what row we in?
1122     unsigned int row = ui_determine_row ( ui_selected );
1123
1124     // max rows?
1125     unsigned int icon_rows = g_categories [ ui_category ].refcount / col_max;
1126     if ( g_categories [ ui_category ].refcount % col_max > 0 ) {
1127       icon_rows++;
1128     }
1129
1130     // we at the end?
1131     if ( row == ( icon_rows - 1 ) &&
1132          pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 1 ) == 0 )
1133     {
1134
1135       unsigned char col = ui_determine_screen_col ( ui_selected );
1136
1137       ui_selected = g_categories [ ui_category ].refs;
1138
1139       while ( col ) {
1140         ui_selected = ui_selected -> next;
1141         col--;
1142       }
1143
1144       ui_rows_scrolled_down = 0;
1145
1146     } else {
1147
1148       while ( col_max ) {
1149         ui_push_right ( 1 );
1150         col_max--;
1151       }
1152
1153     }
1154
1155   } else {
1156     ui_push_right ( 0 );
1157   }
1158
1159   return;
1160 }
1161
1162 void ui_push_exec ( void ) {
1163
1164   if ( ui_selected ) {
1165     char buffer [ PATH_MAX ];
1166     sprintf ( buffer, "%s/%s", ui_selected -> ref -> object_path, ui_selected -> ref -> object_filename );
1167     pnd_apps_exec ( pnd_run_script,
1168                     buffer,
1169                     ui_selected -> ref -> unique_id,
1170                     ui_selected -> ref -> exec,
1171                     ui_selected -> ref -> startdir,
1172                     ui_selected -> ref -> execargs,
1173                     atoi ( ui_selected -> ref -> clockspeed ),
1174                     PND_EXEC_OPTION_NORUN );
1175     sprintf ( buffer, "%s %s\n", MM_RUN, pnd_apps_exec_runline() );
1176     emit_and_quit ( buffer );
1177   }
1178
1179   return;
1180 }
1181
1182 void ui_push_ltrigger ( void ) {
1183   unsigned char oldcat = ui_category;
1184
1185   if ( ui_category > 0 ) {
1186     ui_category--;
1187   } else {
1188     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1189       ui_category = g_categorycount - 1;
1190     }
1191   }
1192
1193   if ( oldcat != ui_category ) {
1194     ui_selected = NULL;
1195     ui_set_selected ( ui_selected );
1196   }
1197
1198   // make tab visible?
1199   if ( ui_catshift > 0 && ui_category == ui_catshift - 1 ) {
1200     ui_catshift--;
1201   }
1202
1203   // unscroll
1204   ui_rows_scrolled_down = 0;
1205
1206   return;
1207 }
1208
1209 void ui_push_rtrigger ( void ) {
1210   unsigned char oldcat = ui_category;
1211
1212   unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 );
1213   unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
1214
1215   if ( ui_category < ( g_categorycount - 1 ) ) {
1216     ui_category++;
1217   } else {
1218     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1219       ui_category = 0;
1220     }
1221   }
1222
1223   if ( oldcat != ui_category ) {
1224     ui_selected = NULL;
1225     ui_set_selected ( ui_selected );
1226   }
1227
1228   // make tab visible?
1229   if ( ui_category > ui_catshift + ( screen_width / tab_width ) - 1 ) {
1230     ui_catshift++;
1231   }
1232
1233   // unscroll
1234   ui_rows_scrolled_down = 0;
1235
1236   return;
1237 }
1238
1239 SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ) {
1240   double scale = 1000000.0;
1241   double scalex = 1000000.0;
1242   double scaley = 1000000.0;
1243   SDL_Surface *scaled;
1244
1245   scalex = (double)maxwidth / (double)s -> w;
1246
1247   if ( maxheight == -1 ) {
1248     scale = scalex;
1249   } else {
1250     scaley = (double)maxheight / (double)s -> h;
1251
1252     if ( scaley < scalex ) {
1253       scale = scaley;
1254     } else {
1255       scale = scalex;
1256     }
1257
1258   }
1259
1260   scaled = rotozoomSurface ( s, 0 /* angle*/, scale /* scale */, 1 /* smooth==1*/ );
1261   SDL_FreeSurface ( s );
1262   s = scaled;
1263
1264   return ( s );
1265 }
1266
1267 void ui_loadscreen ( void ) {
1268
1269   SDL_Rect dest;
1270
1271   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1272   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1273   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1274   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1275
1276   // clear the screen
1277   SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1278
1279   // render text
1280   SDL_Surface *rtext;
1281   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1282   rtext = TTF_RenderText_Blended ( g_big_font, "Setting up menu...", tmpfontcolor );
1283   dest.x = 20;
1284   dest.y = 20;
1285   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1286   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1287   SDL_FreeSurface ( rtext );
1288
1289   return;
1290 }
1291
1292 void ui_discoverscreen ( unsigned char clearscreen ) {
1293
1294   SDL_Rect dest;
1295
1296   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1297   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1298   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1299   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1300
1301   // clear the screen
1302   if ( clearscreen ) {
1303     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1304
1305     // render background
1306     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1307       dest.x = 0;
1308       dest.y = 0;
1309       dest.w = sdl_realscreen -> w;
1310       dest.h = sdl_realscreen -> h;
1311       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1312       SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1313     }
1314
1315   }
1316
1317   // render text
1318   SDL_Surface *rtext;
1319   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1320   rtext = TTF_RenderText_Blended ( g_big_font, "Looking for applications...", tmpfontcolor );
1321   if ( clearscreen ) {
1322     dest.x = 20;
1323     dest.y = 20;
1324   } else {
1325     dest.x = 20;
1326     dest.y = 40;
1327   }
1328   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1329   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1330   SDL_FreeSurface ( rtext );
1331
1332   // render icon
1333   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1334     dest.x = rtext -> w + 30;
1335     dest.y = 20;
1336     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, &dest );
1337     SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1338   }
1339
1340   return;
1341 }
1342
1343 void ui_cachescreen ( unsigned char clearscreen, char *filename ) {
1344
1345   SDL_Rect rects [ 4 ];
1346   SDL_Rect *dest = rects;
1347   bzero ( dest, sizeof(SDL_Rect)* 4 );
1348
1349   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1350   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1351   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1352   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1353
1354   static unsigned int stepx = 0;
1355
1356   // clear the screen
1357   if ( clearscreen ) {
1358     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1359
1360     // render background
1361     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1362       dest -> x = 0;
1363       dest -> y = 0;
1364       dest -> w = sdl_realscreen -> w;
1365       dest -> h = sdl_realscreen -> h;
1366       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1367       dest++;
1368     }
1369
1370   }
1371
1372   // render text
1373   SDL_Surface *rtext;
1374   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1375   rtext = TTF_RenderText_Blended ( g_big_font, "Caching applications artwork...", tmpfontcolor );
1376   if ( clearscreen ) {
1377     dest -> x = 20;
1378     dest -> y = 20;
1379   } else {
1380     dest -> x = 20;
1381     dest -> y = 40;
1382   }
1383   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1384   SDL_FreeSurface ( rtext );
1385   dest++;
1386
1387   // render icon
1388   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1389     dest -> x = rtext -> w + 30 + stepx;
1390     dest -> y = 20;
1391     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, dest );
1392     dest++;
1393   }
1394
1395   // filename
1396   if ( filename ) {
1397     rtext = TTF_RenderText_Blended ( g_tab_font, filename, tmpfontcolor );
1398     dest -> x = 20;
1399     dest -> y = 50;
1400     SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1401     SDL_FreeSurface ( rtext );
1402     dest++;
1403   }
1404
1405   // move across
1406   stepx += 20;
1407
1408   if ( stepx > 350 ) {
1409     stepx = 0;
1410   }
1411
1412   SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1413
1414   return;
1415 }
1416
1417 int ui_selected_index ( void ) {
1418
1419   if ( ! ui_selected ) {
1420     return ( -1 ); // no index
1421   }
1422
1423   mm_appref_t *r = g_categories [ ui_category ].refs;
1424   int counter = 0;
1425   while ( r ) {
1426     if ( r == ui_selected ) {
1427       return ( counter );
1428     }
1429     r = r -> next;
1430     counter++;
1431   }
1432
1433   return ( -1 );
1434 }
1435
1436 static mm_appref_t *timer_ref = NULL;
1437 void ui_set_selected ( mm_appref_t *r ) {
1438
1439   if ( ! pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) {
1440     return; // no desire to defer anything
1441   }
1442
1443   if ( ! r ) {
1444     // cancel timer
1445     SDL_SetTimer ( 0, NULL );
1446     timer_ref = NULL;
1447     return;
1448   }
1449
1450   SDL_SetTimer ( pnd_conf_get_as_int_d ( g_conf, "previewpic.defer_timer_ms", 1000 ), ui_callback_f );
1451   timer_ref = r;
1452
1453   return;
1454 }
1455
1456 unsigned int ui_callback_f ( unsigned int t ) {
1457
1458   if ( ui_selected != timer_ref ) {
1459     return ( 0 ); // user has moved it, who cares
1460   }
1461
1462   SDL_Event e;
1463   e.type = SDL_USEREVENT;
1464   SDL_PushEvent ( &e );
1465
1466   return ( 0 );
1467 }
1468
1469 int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *footer ) {
1470   SDL_Rect rects [ 40 ];
1471   SDL_Rect *dest = rects;
1472   SDL_Rect src;
1473   SDL_Surface *rtext;
1474
1475   bzero ( rects, sizeof(SDL_Rect) * 40 );
1476
1477   unsigned int sel = 0;
1478
1479   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1480   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1481   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1482   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1483
1484   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1485
1486   SDL_Color selfontcolor = { 0/*font_rgba_r*/, font_rgba_g, font_rgba_b, font_rgba_a };
1487
1488   unsigned int i;
1489   SDL_Event event;
1490
1491   while ( 1 ) {
1492
1493     // clear
1494     dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1495     dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1496     dest -> w = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> w;
1497     dest -> h = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h;
1498     SDL_FillRect( sdl_realscreen, dest, 0 );
1499
1500     // show dialog background
1501     if ( g_imagecache [ IMG_DETAIL_BG ].i ) {
1502       src.x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1503       src.y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1504       src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> w;
1505       src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> h;
1506       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1507       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1508       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1509       // repeat for darken?
1510       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1511       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1512       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1513       dest++;
1514     }
1515
1516     // show dialog frame
1517     if ( g_imagecache [ IMG_DETAIL_PANEL ].i ) {
1518       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1519       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1520       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_PANEL ].i, NULL /* whole image */, sdl_realscreen, dest );
1521       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1522       dest++;
1523     }
1524
1525     // show header
1526     if ( title ) {
1527       rtext = TTF_RenderText_Blended ( g_tab_font, title, tmpfontcolor );
1528       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1529       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 20;
1530       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1531       SDL_FreeSurface ( rtext );
1532       dest++;
1533     }
1534
1535     // show footer
1536     if ( footer ) {
1537       rtext = TTF_RenderText_Blended ( g_tab_font, footer, 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 ) +
1540         ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h
1541         - 60;
1542       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1543       SDL_FreeSurface ( rtext );
1544       dest++;
1545     }
1546
1547     // show options
1548     for ( i = 0; i < argc; i++ ) {
1549
1550       // show options
1551       if ( sel == i ) {
1552         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], selfontcolor );
1553       } else {
1554         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], tmpfontcolor );
1555       }
1556       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1557       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 40 + ( 20 * ( i + 1 ) );
1558       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1559       SDL_FreeSurface ( rtext );
1560       dest++;
1561
1562     } // for
1563
1564     // update all the rects and send it all to sdl
1565     SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1566     dest = rects;
1567
1568     // check for input
1569     while ( SDL_WaitEvent ( &event ) ) {
1570
1571       switch ( event.type ) {
1572
1573       case SDL_KEYUP:
1574
1575         if ( event.key.keysym.sym == SDLK_UP ) {
1576           if ( sel ) {
1577             sel--;
1578           }
1579         } else if ( event.key.keysym.sym == SDLK_DOWN ) {
1580           if ( sel < argc - 1 ) {
1581             sel++;
1582           }
1583
1584         } else if ( event.key.keysym.sym == SDLK_RETURN ) {
1585           return ( sel );
1586
1587         } else if ( event.key.keysym.sym == SDLK_q ) {
1588           exit ( 0 );
1589
1590         } else {
1591           return ( -1 ); // nada
1592         }
1593
1594         break;
1595
1596       } // switch
1597
1598       break;
1599     } // while
1600
1601   } // while
1602
1603   return ( -1 );
1604 }
1605
1606 unsigned char ui_determine_row ( mm_appref_t *a ) {
1607   unsigned int row = 0;
1608
1609   mm_appref_t *i = g_categories [ ui_category ].refs;
1610   while ( i != a ) {
1611     i = i -> next;
1612     row++;
1613   } // while
1614   row /= pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 );
1615
1616   return ( row );
1617 }
1618
1619 unsigned char ui_determine_screen_row ( mm_appref_t *a ) {
1620   return ( ui_determine_row ( a ) % pnd_conf_get_as_int_d ( g_conf, "grid.row_max", 5 ) );
1621 }
1622
1623 unsigned char ui_determine_screen_col ( mm_appref_t *a ) {
1624   unsigned int col = 0;
1625
1626   mm_appref_t *i = g_categories [ ui_category ].refs;
1627   while ( i != a ) {
1628     i = i -> next;
1629     col++;
1630   } // while
1631   col %= pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 );
1632
1633   return ( col );
1634 }
1635
1636 unsigned char ui_show_info ( char *pndrun, pnd_disco_t *p ) {
1637   char *viewer, *searchpath;
1638   pnd_conf_handle desktoph;
1639
1640   // viewer
1641   searchpath = pnd_conf_query_searchpath();
1642
1643   desktoph = pnd_conf_fetch_by_id ( pnd_conf_desktop, searchpath );
1644
1645   if ( ! desktoph ) {
1646     return ( 0 );
1647   }
1648
1649   viewer = pnd_conf_get_as_char ( desktoph, "info.viewer" );
1650
1651   if ( ! viewer ) {
1652     return ( 0 ); // no way to view the file
1653   }
1654
1655   // etc
1656   if ( ! p -> unique_id ) {
1657     return ( 0 );
1658   }
1659
1660   if ( ! p -> info_filename ) {
1661     return ( 0 );
1662   }
1663
1664   if ( ! p -> info_name ) {
1665     return ( 0 );
1666   }
1667
1668   if ( ! pndrun ) {
1669     return ( 0 );
1670   }
1671
1672   // exec line
1673   char args [ 1001 ];
1674   char *pargs = args;
1675   if ( pnd_conf_get_as_char ( desktoph, "info.viewer_args" ) ) {
1676     snprintf ( pargs, 1001, "%s %s",
1677                pnd_conf_get_as_char ( desktoph, "info.viewer_args" ), p -> info_filename );
1678   } else {
1679     pargs = NULL;
1680   }
1681
1682   char pndfile [ 1024 ];
1683   if ( p -> object_type == pnd_object_type_directory ) {
1684     // for PXML-app-dir, pnd_run.sh doesn't want the PXML.xml.. it just wants the dir-name
1685     strncpy ( pndfile, p -> object_path, 1000 );
1686   } else if ( p -> object_type == pnd_object_type_pnd ) {
1687     // pnd_run.sh wants the full path and filename for the .pnd file
1688     snprintf ( pndfile, 1020, "%s/%s", p -> object_path, p -> object_filename );
1689   }
1690
1691   if ( ! pnd_apps_exec ( pndrun, pndfile, p -> unique_id, viewer, p -> startdir, pargs,
1692                          p -> clockspeed ? atoi ( p -> clockspeed ) : 0, PND_EXEC_OPTION_NORUN ) )
1693   {
1694     return ( 0 );
1695   }
1696
1697   pnd_log ( pndn_debug, "Info Exec=%s\n", pnd_apps_exec_runline() );
1698
1699   // try running it
1700   int x;
1701   if ( ( x = fork() ) < 0 ) {
1702     pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" );
1703     return ( 0 );
1704   }
1705
1706   if ( x == 0 ) {
1707     execl ( "/bin/sh", "/bin/sh", "-c", pnd_apps_exec_runline(), (char*)NULL );
1708     pnd_log ( pndn_error, "ERROR: Couldn't exec(%s)\n", pnd_apps_exec_runline() );
1709     return ( 0 );
1710   }
1711
1712   return ( 1 );
1713 }
1714
1715 typedef struct {
1716   SDL_Rect r;
1717   int catnum;
1718   mm_appref_t *ref;
1719 } ui_touch_t;
1720 #define MAXTOUCH 100
1721 ui_touch_t ui_touchrects [ MAXTOUCH ];
1722 unsigned char ui_touchrect_count = 0;
1723
1724 void ui_register_reset ( void ) {
1725   bzero ( ui_touchrects, sizeof(ui_touch_t)*MAXTOUCH );
1726   ui_touchrect_count = 0;
1727   return;
1728 }
1729
1730 void ui_register_tab ( unsigned char catnum, unsigned int x, unsigned int y, unsigned int w, unsigned int h ) {
1731
1732   if ( ui_touchrect_count == MAXTOUCH ) {
1733     return;
1734   }
1735
1736   ui_touchrects [ ui_touchrect_count ].r.x = x;
1737   ui_touchrects [ ui_touchrect_count ].r.y = y;
1738   ui_touchrects [ ui_touchrect_count ].r.w = w;
1739   ui_touchrects [ ui_touchrect_count ].r.h = h;
1740   ui_touchrects [ ui_touchrect_count ].catnum = catnum;
1741   ui_touchrect_count++;
1742
1743   return;
1744 }
1745
1746 void ui_register_app ( mm_appref_t *app, unsigned int x, unsigned int y, unsigned int w, unsigned int h ) {
1747
1748   if ( ui_touchrect_count == MAXTOUCH ) {
1749     return;
1750   }
1751
1752   ui_touchrects [ ui_touchrect_count ].r.x = x;
1753   ui_touchrects [ ui_touchrect_count ].r.y = y;
1754   ui_touchrects [ ui_touchrect_count ].r.w = w;
1755   ui_touchrects [ ui_touchrect_count ].r.h = h;
1756   ui_touchrects [ ui_touchrect_count ].ref = app;
1757   ui_touchrect_count++;
1758
1759   return;
1760 }
1761
1762 void ui_touch_act ( unsigned int x, unsigned int y ) {
1763
1764   unsigned char i;
1765   ui_touch_t *t;
1766
1767   for ( i = 0; i < ui_touchrect_count; i++ ) {
1768     t = &(ui_touchrects [ i ]);
1769
1770     if ( x >= t -> r.x &&
1771          x <= t -> r.x + t -> r.w &&
1772          y >= t -> r.y &&
1773          y <= t -> r.y + t -> r.h
1774        )
1775     {
1776
1777       if ( t -> ref ) {
1778         ui_selected = t -> ref;
1779         ui_push_exec();
1780       } else {
1781         ui_category = t -> catnum;
1782       }
1783
1784       break;
1785     }
1786
1787   } // for
1788
1789   return;
1790 }