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