abc4ef4ff76f76d08200f8f3fe29adfaa4553428
[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   // ensure selection is visible
297   if ( ui_selected ) {
298
299     int index = ui_selected_index();
300     int topleft = col_max * ui_rows_scrolled_down;
301     int botright = ( col_max * ( ui_rows_scrolled_down + row_max ) - 1 );
302
303     //pnd_log ( PND_LOG_DEFAULT, "index %u tl %u br %u\n", index, topleft, botright );
304
305     if ( index < topleft ) {
306       ui_rows_scrolled_down -= pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 );
307     } else if ( index > botright ) {
308       ui_rows_scrolled_down += pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 );
309     }
310
311     if ( ui_rows_scrolled_down < 0 ) {
312       ui_rows_scrolled_down = 0;
313     } else if ( ui_rows_scrolled_down > icon_rows ) {
314       ui_rows_scrolled_down = icon_rows;
315     }
316
317   } // ensire visible
318
319   // render background
320   if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
321     dest -> x = 0;
322     dest -> y = 0;
323     dest -> w = sdl_realscreen -> w;
324     dest -> h = sdl_realscreen -> h;
325     SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, dest /* 0,0 */ );
326     //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
327     dest++;
328   }
329
330   // tabmask
331   if ( g_imagecache [ IMG_BACKGROUND_TABMASK ].i ) {
332     dest -> x = 0;
333     dest -> y = 0;
334     dest -> w = sdl_realscreen -> w;
335     dest -> h = sdl_realscreen -> h;
336     SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_TABMASK ].i, NULL /* whole image */, sdl_realscreen, dest /* 0,0 */ );
337     //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
338     dest++;
339   }
340
341   // tabs
342   if ( g_imagecache [ IMG_TAB_SEL ].i && g_imagecache [ IMG_TAB_UNSEL ].i ) {
343     unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
344     unsigned int tab_height = pnd_conf_get_as_int ( g_conf, "tabs.tab_height" );
345     unsigned int tab_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_x" );
346     unsigned int tab_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_y" );
347     unsigned int text_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_x" );
348     unsigned int text_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_y" );
349     unsigned int text_width = pnd_conf_get_as_int ( g_conf, "tabs.text_width" );
350
351     for ( col = ui_catshift;
352           col < ( 
353                    ( screen_width / tab_width ) < g_categorycount ? ( screen_width / tab_width ) + ui_catshift : g_categorycount + ui_catshift
354                 );
355           col++ )
356     {
357
358       SDL_Surface *s;
359       if ( col == ui_category ) {
360         s = g_imagecache [ IMG_TAB_SEL ].i;
361       } else {
362         s = g_imagecache [ IMG_TAB_UNSEL ].i;
363       }
364
365       // draw tab
366       src.x = 0;
367       src.y = 0;
368       src.w = tab_width;
369       src.h = tab_height;
370       dest -> x = tab_offset_x + ( (col-ui_catshift) * tab_width );
371       dest -> y = tab_offset_y;
372       //pnd_log ( pndn_debug, "tab %u at %ux%u\n", col, dest.x, dest.y );
373       SDL_BlitSurface ( s, &src, sdl_realscreen, dest );
374       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
375       dest++;
376
377       // draw text
378       SDL_Surface *rtext;
379       SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
380       rtext = TTF_RenderText_Blended ( g_tab_font, g_categories [ col ].catname, tmpfontcolor );
381       src.x = 0;
382       src.y = 0;
383       src.w = rtext -> w < text_width ? rtext -> w : text_width;
384       src.h = rtext -> h;
385       dest -> x = tab_offset_x + ( (col-ui_catshift) * tab_width ) + text_offset_x;
386       dest -> y = tab_offset_y + text_offset_y;
387       SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest );
388       SDL_FreeSurface ( rtext );
389       dest++;
390
391     } // for
392
393   } // tabs
394
395   // scroll bars and arrows
396   {
397     unsigned char show_bar = 0;
398
399     // up?
400     if ( ui_rows_scrolled_down && g_imagecache [ IMG_ARROW_UP ].i ) {
401       dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_x", 450 );
402       dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_y", 80 );
403       SDL_BlitSurface ( g_imagecache [ IMG_ARROW_UP ].i, NULL /* whole image */, sdl_realscreen, dest );
404       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
405       dest++;
406
407       show_bar = 1;
408     }
409
410     // down?
411     if ( ui_rows_scrolled_down + row_max < icon_rows && g_imagecache [ IMG_ARROW_DOWN ].i ) {
412       dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_x", 450 );
413       dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_y", 80 );
414       SDL_BlitSurface ( g_imagecache [ IMG_ARROW_DOWN ].i, NULL /* whole image */, sdl_realscreen, dest );
415       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
416       dest++;
417
418       show_bar = 1;
419     }
420
421     if ( show_bar ) {
422       // show scrollbar as well
423       src.x = 0;
424       src.y = 0;
425       src.w = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_w", 10 );
426       src.h = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_h", 100 );
427       dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_x", 450 );
428       dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_y", 100 );
429       SDL_BlitSurface ( g_imagecache [ IMG_ARROW_SCROLLBAR ].i, &src /* whole image */, sdl_realscreen, dest );
430       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
431       dest++;
432     } // bar
433
434   } // scroll bars
435
436   // render detail pane bg
437   if ( pnd_conf_get_as_int_d ( g_conf, "detailpane.show", 1 ) ) {
438
439     if ( g_imagecache [ IMG_DETAIL_BG ].i ) {
440       src.x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
441       src.y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
442       src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> w;
443       src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> h;
444       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
445       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
446       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
447       //SDL_UpdateRects ( sdl_realscreen, 1, &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       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
457       dest++;
458     }
459
460   } // detailpane frame/bg
461
462   // anything to render?
463   if ( g_categories [ ui_category ].refs ) {
464
465     appiter = g_categories [ ui_category ].refs;
466     row = 0;
467     displayrow = 0;
468
469     // until we run out of apps, or run out of space
470     while ( appiter != NULL ) {
471
472       for ( col = 0; col < col_max && appiter != NULL; col++ ) {
473
474         // do we even need to render it? or are we suppressing it due to rows scrolled off the top?
475         if ( row >= ui_rows_scrolled_down ) {
476
477           // selected? show hilights
478           if ( appiter == ui_selected ) {
479             // icon
480             dest -> x = grid_offset_x + ( col * cell_width ) + icon_offset_x;
481             dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y;
482             SDL_BlitSurface ( g_imagecache [ IMG_SELECTED_ALPHAMASK ].i, NULL /* all */, sdl_realscreen, dest );
483             //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
484             dest++;
485             // text
486             dest -> x = grid_offset_x + ( col * cell_width ) + text_clip_x;
487             dest -> y = grid_offset_y + ( displayrow * cell_height ) + pnd_conf_get_as_int ( g_conf, "grid.text_hilite_offset_y" );
488             SDL_BlitSurface ( g_imagecache [ IMG_SELECTED_HILITE ].i, NULL /* all */, sdl_realscreen, dest );
489             //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
490             dest++;
491           } // selected?
492
493           // show icon
494           mm_cache_t *ic = cache_query_icon ( appiter -> ref -> unique_id );
495           SDL_Surface *iconsurface;
496           if ( ic ) {
497             iconsurface = ic -> i;
498           } else {
499             //pnd_log ( pndn_warning, "WARNING: TBD: Need Missin-icon icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") );
500             iconsurface = g_imagecache [ IMG_ICON_MISSING ].i;
501           }
502           if ( iconsurface ) {
503             //pnd_log ( pndn_debug, "Got an icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") );
504
505             src.x = 0;
506             src.y = 0;
507             src.w = 60;
508             src.h = 60;
509             dest -> x = grid_offset_x + ( col * cell_width ) + icon_offset_x + (( icon_max_width - iconsurface -> w ) / 2);
510             dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y;
511
512             SDL_BlitSurface ( iconsurface, &src, sdl_realscreen, dest );
513             //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
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       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
667       dest++;
668     }
669
670   } // selected?
671
672   // extras
673   //
674
675   // battery
676   if ( 1 ) {
677     unsigned char batterylevel = pnd_device_get_battery_gauge_perc();
678     char buffer [ 100 ];
679
680     sprintf ( buffer, "Battery: %u%%", batterylevel );
681
682     SDL_Surface *rtext;
683     SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
684     rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor );
685     dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.battery_x", 20 );
686     dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.battery_y", 450 );
687     SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
688     SDL_FreeSurface ( rtext );
689     dest++;
690   }
691
692   // hints
693   if ( pnd_conf_get_as_char ( g_conf, "display.hintline" ) ) {
694     char *buffer = pnd_conf_get_as_char ( g_conf, "display.hintline" );
695     SDL_Surface *rtext;
696     SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
697     rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor );
698     dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.hint_x", 40 );
699     dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.hint_y", 450 );
700     SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
701     SDL_FreeSurface ( rtext );
702     dest++;
703   }
704
705   // hints
706   if ( pnd_conf_get_as_int_d ( g_conf, "display.clock_x", -1 ) != -1 ) {
707     char buffer [ 50 ];
708
709     time_t t = time ( NULL );
710     struct tm *tm = localtime ( &t );
711     strftime ( buffer, 50, "%a %H:%M %F", tm );
712
713     SDL_Surface *rtext;
714     SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
715     rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor );
716     dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.clock_x", 700 );
717     dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.clock_y", 450 );
718     SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
719     SDL_FreeSurface ( rtext );
720     dest++;
721   }
722
723   // update all the rects and send it all to sdl
724   SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
725
726 } // ui_render
727
728 void ui_process_input ( unsigned char block_p ) {
729   SDL_Event event;
730
731   unsigned char ui_event = 0; // if we get a ui event, flip to 1 and break
732   //static ui_sdl_button_e ui_mask = uisb_none; // current buttons down
733
734   while ( ! ui_event &&
735           block_p ? SDL_WaitEvent ( &event ) : SDL_PollEvent ( &event ) )
736   {
737
738     switch ( event.type ) {
739
740     case SDL_USEREVENT:
741       // update something
742
743       if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) {
744
745         pnd_log ( pndn_debug, "Deferred preview pic load ----------\n" );
746
747         // load the preview pics now!
748         pnd_disco_t *iter = ui_selected -> ref;
749
750         if ( iter -> preview_pic1 &&
751              ! 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 ) ) )
752         {
753           pnd_log ( pndn_debug, "  Couldn't load preview pic: '%s' -> '%s'\n", IFNULL(iter->title_en,"No Name"), iter -> preview_pic1 );
754         }
755
756         pnd_log ( pndn_debug, "Deferred preview pic load finish ---\n" );
757
758         ui_event++;
759       }
760
761       break;
762
763 #if 0 // joystick motion
764     case SDL_JOYAXISMOTION:
765
766       pnd_log ( PND_LOG_DEFAULT, "joystick axis\n" );
767
768         if ( event.jaxis.axis == 0 ) {
769           // horiz
770           if ( event.jaxis.value < 0 ) {
771             ui_push_left ( 0 );
772             pnd_log ( PND_LOG_DEFAULT, "joystick axis - LEFT\n" );
773           } else if ( event.jaxis.value > 0 ) {
774             ui_push_right ( 0 );
775             pnd_log ( PND_LOG_DEFAULT, "joystick axis - RIGHT\n" );
776           }
777         } else if ( event.jaxis.axis == 1 ) {
778           // vert
779           if ( event.jaxis.value < 0 ) {
780             ui_push_up();
781           } else if ( event.jaxis.value > 0 ) {
782             ui_push_down();
783           }
784         }
785
786         ui_event++;
787
788         break;
789 #endif
790
791 #if 0 // joystick buttons
792     case SDL_JOYBUTTONDOWN:
793
794       pnd_log ( PND_LOG_DEFAULT, "joystick button down %u\n", event.jbutton.button );
795
796       if ( event.jbutton.button == 0 ) { // B
797         ui_mask |= uisb_b;
798       } else if ( event.jbutton.button == 1 ) { // Y
799         ui_mask |= uisb_y;
800       } else if ( event.jbutton.button == 2 ) { // X
801         ui_mask |= uisb_x;
802       } else if ( event.jbutton.button == 3 ) { // A
803         ui_mask |= uisb_a;
804
805       } else if ( event.jbutton.button == 4 ) { // Select
806         ui_mask |= uisb_select;
807       } else if ( event.jbutton.button == 5 ) { // Start
808         ui_mask |= uisb_start;
809
810       } else if ( event.jbutton.button == 7 ) { // L
811         ui_mask |= uisb_l;
812       } else if ( event.jbutton.button == 8 ) { // R
813         ui_mask |= uisb_r;
814
815       }
816
817       ui_event++;
818
819       break;
820
821     case SDL_JOYBUTTONUP:
822
823       pnd_log ( PND_LOG_DEFAULT, "joystick button up %u\n", event.jbutton.button );
824
825       if ( event.jbutton.button == 0 ) { // B
826         ui_mask &= ~uisb_b;
827         ui_push_exec();
828       } else if ( event.jbutton.button == 1 ) { // Y
829         ui_mask &= ~uisb_y;
830       } else if ( event.jbutton.button == 2 ) { // X
831         ui_mask &= ~uisb_x;
832       } else if ( event.jbutton.button == 3 ) { // A
833         ui_mask &= ~uisb_a;
834
835       } else if ( event.jbutton.button == 4 ) { // Select
836         ui_mask &= ~uisb_select;
837       } else if ( event.jbutton.button == 5 ) { // Start
838         ui_mask &= ~uisb_start;
839
840       } else if ( event.jbutton.button == 7 ) { // L
841         ui_mask &= ~uisb_l;
842         ui_push_ltrigger();
843       } else if ( event.jbutton.button == 8 ) { // R
844         ui_mask &= ~uisb_r;
845         ui_push_rtrigger();
846
847       }
848
849       ui_event++;
850
851       break;
852 #endif
853
854 #if 1 // keyboard events
855     case SDL_KEYUP:
856
857       //pnd_log ( pndn_debug, "key up %u\n", event.key.keysym.sym );
858
859       // SDLK_LALT -> Start
860
861       // directional
862       if ( event.key.keysym.sym == SDLK_RIGHT ) {
863         ui_push_right ( 0 );
864         ui_event++;
865       } else if ( event.key.keysym.sym == SDLK_LEFT ) {
866         ui_push_left ( 0 );
867         ui_event++;
868       } else if ( event.key.keysym.sym == SDLK_UP ) {
869         ui_push_up();
870         ui_event++;
871       } else if ( event.key.keysym.sym == SDLK_DOWN ) {
872         ui_push_down();
873         ui_event++;
874       } else if ( event.key.keysym.sym == SDLK_SPACE || event.key.keysym.sym == SDLK_END ) {
875         ui_push_exec();
876         ui_event++;
877       } else if ( event.key.keysym.sym == SDLK_z || event.key.keysym.sym == SDLK_RSHIFT ) {
878         ui_push_ltrigger();
879         ui_event++;
880       } else if ( event.key.keysym.sym == SDLK_x || event.key.keysym.sym == SDLK_RCTRL ) {
881         ui_push_rtrigger();
882         ui_event++;
883       } else if ( event.key.keysym.sym == SDLK_y || event.key.keysym.sym == SDLK_PAGEUP ) {
884         // info
885         if ( ui_selected ) {
886           ui_show_info ( pnd_run_script, ui_selected -> ref );
887           ui_event++;
888         }
889
890       } else if ( event.key.keysym.sym == SDLK_LALT ) { // start button
891         ui_push_exec();
892         ui_event++;
893
894       } else if ( event.key.keysym.sym == SDLK_LCTRL /*LALT*/ ) { // select button
895         char *opts [ 20 ] = {
896           "Return to Minimenu",
897           "Shutdown Pandora",
898           "Rescan for Applications",
899           "Run xfce4 from Minimenu",
900           "Set to full desktop and reboot",
901           "Set to pmenu and reboot",
902           "Quit (<- beware)",
903           "About Minimenu"
904         };
905         int sel = ui_modal_single_menu ( opts, 8, "Minimenu", "Enter to select; other to return." );
906
907         char buffer [ 100 ];
908         if ( sel == 0 ) {
909           // do nothing
910         } else if ( sel == 1 ) {
911           // shutdown
912           sprintf ( buffer, "sudo poweroff" );
913           system ( buffer );
914         } else if ( sel == 2 ) {
915           // rescan apps
916           pnd_log ( pndn_debug, "Freeing up applications\n" );
917           applications_free();
918           pnd_log ( pndn_debug, "Rescanning applications\n" );
919           applications_scan();
920           // reset view
921           ui_selected = NULL;
922           ui_rows_scrolled_down = 0;
923         } else if ( sel == 3 ) {
924           // run xfce
925           char buffer [ PATH_MAX ];
926           sprintf ( buffer, "%s %s\n", MM_RUN, "/usr/bin/startxfce4" );
927           emit_and_quit ( buffer );
928         } else if ( sel == 4 ) {
929           // set env to xfce
930           sprintf ( buffer, "echo startxfce4 > /tmp/gui.load" );
931           system ( buffer );
932           sprintf ( buffer, "sudo poweroff" );
933           system ( buffer );
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         } else if ( sel == 6 ) {
941           emit_and_quit ( MM_QUIT );
942         } else if ( sel == 7 ) {
943           // about
944         }
945
946         ui_event++;
947       }
948
949       // extras
950       if ( event.key.keysym.sym == SDLK_q ) {
951         emit_and_quit ( MM_QUIT );
952       }
953
954       break;
955 #endif
956
957 #if 0 // mouse / touchscreen
958     case SDL_MOUSEBUTTONDOWN:
959       if ( event.button.button == SDL_BUTTON_LEFT ) {
960         cb_pointer_press ( gc, event.button.x / g_scale, event.button.y / g_scale );
961         ui_event++;
962       }
963       break;
964
965     case SDL_MOUSEBUTTONUP:
966       if ( event.button.button == SDL_BUTTON_LEFT ) {
967         cb_pointer_release ( gc, event.button.x / g_scale, event.button.y / g_scale );
968         retval |= STAT_pen;
969         ui_event++;
970       }
971       break;
972 #endif
973
974     case SDL_QUIT:
975       exit ( 0 );
976       break;
977
978     default:
979       break;
980
981     } // switch event type
982
983   } // while poll
984
985   return;
986 }
987
988 void ui_push_left ( unsigned char forcecoil ) {
989
990   if ( ! ui_selected ) {
991     ui_push_right ( 0 );
992     return;
993   }
994
995   // what column we in?
996   unsigned int col = ui_determine_screen_col ( ui_selected );
997
998   // are we alreadt at first item?
999   if ( forcecoil == 0 &&
1000        pnd_conf_get_as_int_d ( g_conf, "grid.wrap_horiz_samerow", 0 ) &&
1001        col == 0 )
1002   {
1003     unsigned int i = pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1;
1004     while ( i ) {
1005       ui_push_right ( 0 );
1006       i--;
1007     }
1008
1009   } else if ( g_categories [ ui_category ].refs == ui_selected ) {
1010     // can't go any more left, we're at the head
1011
1012   } else {
1013     // figure out the previous item; yay for singly linked list :/
1014     mm_appref_t *i = g_categories [ ui_category ].refs;
1015     while ( i ) {
1016       if ( i -> next == ui_selected ) {
1017         ui_selected = i;
1018         break;
1019       }
1020       i = i -> next;
1021     }
1022   }
1023
1024   ui_set_selected ( ui_selected );
1025
1026   return;
1027 }
1028
1029 void ui_push_right ( unsigned char forcecoil ) {
1030
1031   if ( ui_selected ) {
1032
1033     // what column we in?
1034     unsigned int col = ui_determine_screen_col ( ui_selected );
1035
1036     // wrap same or no?
1037     if ( forcecoil == 0 &&
1038          pnd_conf_get_as_int_d ( g_conf, "grid.wrap_horiz_samerow", 0 ) &&
1039          col == pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1 )
1040     {
1041       // same wrap
1042       unsigned int i = pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1;
1043       while ( i ) {
1044         ui_push_left ( 0 );
1045         i--;
1046       }
1047
1048     } else {
1049       // just go to the next
1050
1051       if ( ui_selected -> next ) {
1052         ui_selected = ui_selected -> next;
1053       }
1054
1055     }
1056
1057   } else {
1058     ui_selected = g_categories [ ui_category ].refs;
1059   }
1060
1061   ui_set_selected ( ui_selected );
1062
1063   return;
1064 }
1065
1066 void ui_push_up ( void ) {
1067   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
1068
1069   // what row we in?
1070   unsigned int row = ui_determine_row ( ui_selected );
1071
1072   if ( row == 0 &&
1073        pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 1 ) == 0 )
1074   {
1075     // wrap around instead
1076
1077     unsigned int col = ui_determine_screen_col ( ui_selected );
1078
1079     // go to end
1080     ui_selected = g_categories [ ui_category ].refs;
1081     while ( ui_selected -> next ) {
1082       ui_selected = ui_selected -> next;
1083     }
1084
1085     // try to move to same column
1086     unsigned int newcol = ui_determine_screen_col ( ui_selected );
1087     if ( newcol > col ) {
1088       col = newcol - col;
1089       while ( col ) {
1090         ui_push_left ( 0 );
1091         col--;
1092       }
1093     }
1094
1095     // scroll down to show it
1096     int r = ui_determine_row ( ui_selected ) - 1;
1097     if ( r - pnd_conf_get_as_int ( g_conf, MMENU_DISP_ROWMAX ) > 0 ) {
1098       ui_rows_scrolled_down = (unsigned int) r;
1099     }
1100
1101   } else {
1102     // stop at top/bottom
1103
1104     while ( col_max ) {
1105       ui_push_left ( 1 );
1106       col_max--;
1107     }
1108
1109   }
1110
1111   return;
1112 }
1113
1114 void ui_push_down ( void ) {
1115   unsigned char col_max = pnd_conf_get_as_int ( g_conf, MMENU_DISP_COLMAX );
1116
1117   if ( ui_selected ) {
1118
1119     // what row we in?
1120     unsigned int row = ui_determine_row ( ui_selected );
1121
1122     // max rows?
1123     unsigned int icon_rows = g_categories [ ui_category ].refcount / col_max;
1124     if ( g_categories [ ui_category ].refcount % col_max > 0 ) {
1125       icon_rows++;
1126     }
1127
1128     // we at the end?
1129     if ( row == ( icon_rows - 1 ) &&
1130          pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 1 ) == 0 )
1131     {
1132
1133       unsigned char col = ui_determine_screen_col ( ui_selected );
1134
1135       ui_selected = g_categories [ ui_category ].refs;
1136
1137       while ( col ) {
1138         ui_selected = ui_selected -> next;
1139         col--;
1140       }
1141
1142       ui_rows_scrolled_down = 0;
1143
1144     } else {
1145
1146       while ( col_max ) {
1147         ui_push_right ( 1 );
1148         col_max--;
1149       }
1150
1151     }
1152
1153   } else {
1154     ui_push_right ( 0 );
1155   }
1156
1157   return;
1158 }
1159
1160 void ui_push_exec ( void ) {
1161
1162   if ( ui_selected ) {
1163     char buffer [ PATH_MAX ];
1164     sprintf ( buffer, "%s/%s", ui_selected -> ref -> object_path, ui_selected -> ref -> object_filename );
1165     pnd_apps_exec ( pnd_run_script,
1166                     buffer,
1167                     ui_selected -> ref -> unique_id,
1168                     ui_selected -> ref -> exec,
1169                     ui_selected -> ref -> startdir,
1170                     ui_selected -> ref -> execargs,
1171                     atoi ( ui_selected -> ref -> clockspeed ),
1172                     PND_EXEC_OPTION_NORUN );
1173     sprintf ( buffer, "%s %s\n", MM_RUN, pnd_apps_exec_runline() );
1174     emit_and_quit ( buffer );
1175   }
1176
1177   return;
1178 }
1179
1180 void ui_push_ltrigger ( void ) {
1181   unsigned char oldcat = ui_category;
1182
1183   if ( ui_category > 0 ) {
1184     ui_category--;
1185   } else {
1186     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1187       ui_category = g_categorycount - 1;
1188     }
1189   }
1190
1191   if ( oldcat != ui_category ) {
1192     ui_selected = NULL;
1193     ui_set_selected ( ui_selected );
1194   }
1195
1196   // make tab visible?
1197   if ( ui_catshift > 0 && ui_category == ui_catshift - 1 ) {
1198     ui_catshift--;
1199   }
1200
1201   // unscroll
1202   ui_rows_scrolled_down = 0;
1203
1204   return;
1205 }
1206
1207 void ui_push_rtrigger ( void ) {
1208   unsigned char oldcat = ui_category;
1209
1210   unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 );
1211   unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" );
1212
1213   if ( ui_category < ( g_categorycount - 1 ) ) {
1214     ui_category++;
1215   } else {
1216     if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) {
1217       ui_category = 0;
1218     }
1219   }
1220
1221   if ( oldcat != ui_category ) {
1222     ui_selected = NULL;
1223     ui_set_selected ( ui_selected );
1224   }
1225
1226   // make tab visible?
1227   if ( ui_category > ui_catshift + ( screen_width / tab_width ) - 1 ) {
1228     ui_catshift++;
1229   }
1230
1231   // unscroll
1232   ui_rows_scrolled_down = 0;
1233
1234   return;
1235 }
1236
1237 SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ) {
1238   double scale = 1000000.0;
1239   double scalex = 1000000.0;
1240   double scaley = 1000000.0;
1241   SDL_Surface *scaled;
1242
1243   scalex = (double)maxwidth / (double)s -> w;
1244
1245   if ( maxheight == -1 ) {
1246     scale = scalex;
1247   } else {
1248     scaley = (double)maxheight / (double)s -> h;
1249
1250     if ( scaley < scalex ) {
1251       scale = scaley;
1252     } else {
1253       scale = scalex;
1254     }
1255
1256   }
1257
1258   scaled = rotozoomSurface ( s, 0 /* angle*/, scale /* scale */, 1 /* smooth==1*/ );
1259   SDL_FreeSurface ( s );
1260   s = scaled;
1261
1262   return ( s );
1263 }
1264
1265 void ui_loadscreen ( void ) {
1266
1267   SDL_Rect dest;
1268
1269   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1270   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1271   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1272   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1273
1274   // clear the screen
1275   SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1276
1277   // render text
1278   SDL_Surface *rtext;
1279   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1280   rtext = TTF_RenderText_Blended ( g_big_font, "Setting up menu...", tmpfontcolor );
1281   dest.x = 20;
1282   dest.y = 20;
1283   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1284   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1285   SDL_FreeSurface ( rtext );
1286
1287   return;
1288 }
1289
1290 void ui_discoverscreen ( unsigned char clearscreen ) {
1291
1292   SDL_Rect dest;
1293
1294   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1295   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1296   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1297   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1298
1299   // clear the screen
1300   if ( clearscreen ) {
1301     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1302
1303     // render background
1304     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1305       dest.x = 0;
1306       dest.y = 0;
1307       dest.w = sdl_realscreen -> w;
1308       dest.h = sdl_realscreen -> h;
1309       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1310       SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1311     }
1312
1313   }
1314
1315   // render text
1316   SDL_Surface *rtext;
1317   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1318   rtext = TTF_RenderText_Blended ( g_big_font, "Looking for applications...", tmpfontcolor );
1319   if ( clearscreen ) {
1320     dest.x = 20;
1321     dest.y = 20;
1322   } else {
1323     dest.x = 20;
1324     dest.y = 40;
1325   }
1326   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest );
1327   SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1328   SDL_FreeSurface ( rtext );
1329
1330   // render icon
1331   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1332     dest.x = rtext -> w + 30;
1333     dest.y = 20;
1334     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, &dest );
1335     SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1336   }
1337
1338   return;
1339 }
1340
1341 void ui_cachescreen ( unsigned char clearscreen, char *filename ) {
1342
1343   SDL_Rect rects [ 4 ];
1344   SDL_Rect *dest = rects;
1345   bzero ( dest, sizeof(SDL_Rect)* 4 );
1346
1347   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1348   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1349   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1350   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1351
1352   static unsigned int stepx = 0;
1353
1354   // clear the screen
1355   if ( clearscreen ) {
1356     SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 );
1357
1358     // render background
1359     if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
1360       dest -> x = 0;
1361       dest -> y = 0;
1362       dest -> w = sdl_realscreen -> w;
1363       dest -> h = sdl_realscreen -> h;
1364       SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, NULL /* 0,0 */ );
1365       dest++;
1366     }
1367
1368   }
1369
1370   // render text
1371   SDL_Surface *rtext;
1372   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1373   rtext = TTF_RenderText_Blended ( g_big_font, "Caching applications artwork...", tmpfontcolor );
1374   if ( clearscreen ) {
1375     dest -> x = 20;
1376     dest -> y = 20;
1377   } else {
1378     dest -> x = 20;
1379     dest -> y = 40;
1380   }
1381   SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1382   SDL_FreeSurface ( rtext );
1383   dest++;
1384
1385   // render icon
1386   if ( g_imagecache [ IMG_ICON_MISSING ].i ) {
1387     dest -> x = rtext -> w + 30 + stepx;
1388     dest -> y = 20;
1389     SDL_BlitSurface ( g_imagecache [ IMG_ICON_MISSING ].i, NULL, sdl_realscreen, dest );
1390     dest++;
1391   }
1392
1393   // filename
1394   if ( filename ) {
1395     rtext = TTF_RenderText_Blended ( g_tab_font, filename, tmpfontcolor );
1396     dest -> x = 20;
1397     dest -> y = 50;
1398     SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1399     SDL_FreeSurface ( rtext );
1400     dest++;
1401   }
1402
1403   // move across
1404   stepx += 20;
1405
1406   if ( stepx > 350 ) {
1407     stepx = 0;
1408   }
1409
1410   SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1411
1412   return;
1413 }
1414
1415 int ui_selected_index ( void ) {
1416
1417   if ( ! ui_selected ) {
1418     return ( -1 ); // no index
1419   }
1420
1421   mm_appref_t *r = g_categories [ ui_category ].refs;
1422   int counter = 0;
1423   while ( r ) {
1424     if ( r == ui_selected ) {
1425       return ( counter );
1426     }
1427     r = r -> next;
1428     counter++;
1429   }
1430
1431   return ( -1 );
1432 }
1433
1434 static mm_appref_t *timer_ref = NULL;
1435 void ui_set_selected ( mm_appref_t *r ) {
1436
1437   if ( ! pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) {
1438     return; // no desire to defer anything
1439   }
1440
1441   if ( ! r ) {
1442     // cancel timer
1443     SDL_SetTimer ( 0, NULL );
1444     timer_ref = NULL;
1445     return;
1446   }
1447
1448   SDL_SetTimer ( pnd_conf_get_as_int_d ( g_conf, "previewpic.defer_timer_ms", 1000 ), ui_callback_f );
1449   timer_ref = r;
1450
1451   return;
1452 }
1453
1454 unsigned int ui_callback_f ( unsigned int t ) {
1455
1456   if ( ui_selected != timer_ref ) {
1457     return ( 0 ); // user has moved it, who cares
1458   }
1459
1460   SDL_Event e;
1461   e.type = SDL_USEREVENT;
1462   SDL_PushEvent ( &e );
1463
1464   return ( 0 );
1465 }
1466
1467 int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *footer ) {
1468   SDL_Rect rects [ 40 ];
1469   SDL_Rect *dest = rects;
1470   SDL_Rect src;
1471   SDL_Surface *rtext;
1472
1473   bzero ( rects, sizeof(SDL_Rect) * 40 );
1474
1475   unsigned int sel = 0;
1476
1477   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
1478   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
1479   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
1480   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
1481
1482   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
1483
1484   SDL_Color selfontcolor = { 0/*font_rgba_r*/, font_rgba_g, font_rgba_b, font_rgba_a };
1485
1486   unsigned int i;
1487   SDL_Event event;
1488
1489   while ( 1 ) {
1490
1491     // clear
1492     dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1493     dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1494     dest -> w = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> w;
1495     dest -> h = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h;
1496     SDL_FillRect( sdl_realscreen, dest, 0 );
1497
1498     // show dialog background
1499     if ( g_imagecache [ IMG_DETAIL_BG ].i ) {
1500       src.x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1501       src.y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1502       src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> w;
1503       src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> h;
1504       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1505       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1506       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1507       // repeat for darken?
1508       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1509       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest );
1510       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1511       dest++;
1512     }
1513
1514     // show dialog frame
1515     if ( g_imagecache [ IMG_DETAIL_PANEL ].i ) {
1516       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 );
1517       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 );
1518       SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_PANEL ].i, NULL /* whole image */, sdl_realscreen, dest );
1519       //SDL_UpdateRects ( sdl_realscreen, 1, &dest );
1520       dest++;
1521     }
1522
1523     // show header
1524     if ( title ) {
1525       rtext = TTF_RenderText_Blended ( g_tab_font, title, tmpfontcolor );
1526       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1527       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 20;
1528       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1529       SDL_FreeSurface ( rtext );
1530       dest++;
1531     }
1532
1533     // show footer
1534     if ( footer ) {
1535       rtext = TTF_RenderText_Blended ( g_tab_font, footer, tmpfontcolor );
1536       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1537       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) +
1538         ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h
1539         - 60;
1540       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1541       SDL_FreeSurface ( rtext );
1542       dest++;
1543     }
1544
1545     // show options
1546     for ( i = 0; i < argc; i++ ) {
1547
1548       // show options
1549       if ( sel == i ) {
1550         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], selfontcolor );
1551       } else {
1552         rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], tmpfontcolor );
1553       }
1554       dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20;
1555       dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 40 + ( 20 * ( i + 1 ) );
1556       SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest );
1557       SDL_FreeSurface ( rtext );
1558       dest++;
1559
1560     } // for
1561
1562     // update all the rects and send it all to sdl
1563     SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
1564     dest = rects;
1565
1566     // check for input
1567     while ( SDL_WaitEvent ( &event ) ) {
1568
1569       switch ( event.type ) {
1570
1571       case SDL_KEYUP:
1572
1573         if ( event.key.keysym.sym == SDLK_UP ) {
1574           if ( sel ) {
1575             sel--;
1576           }
1577         } else if ( event.key.keysym.sym == SDLK_DOWN ) {
1578           if ( sel < argc - 1 ) {
1579             sel++;
1580           }
1581
1582         } else if ( event.key.keysym.sym == SDLK_RETURN ) {
1583           return ( sel );
1584
1585         } else if ( event.key.keysym.sym == SDLK_q ) {
1586           exit ( 0 );
1587
1588         } else {
1589           return ( -1 ); // nada
1590         }
1591
1592         break;
1593
1594       } // switch
1595
1596       break;
1597     } // while
1598
1599   } // while
1600
1601   return ( -1 );
1602 }
1603
1604 unsigned char ui_determine_row ( mm_appref_t *a ) {
1605   unsigned int row = 0;
1606
1607   mm_appref_t *i = g_categories [ ui_category ].refs;
1608   while ( i != a ) {
1609     i = i -> next;
1610     row++;
1611   } // while
1612   row /= pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 );
1613
1614   return ( row );
1615 }
1616
1617 unsigned char ui_determine_screen_row ( mm_appref_t *a ) {
1618   return ( ui_determine_row ( a ) % pnd_conf_get_as_int_d ( g_conf, "grid.row_max", 5 ) );
1619 }
1620
1621 unsigned char ui_determine_screen_col ( mm_appref_t *a ) {
1622   unsigned int col = 0;
1623
1624   mm_appref_t *i = g_categories [ ui_category ].refs;
1625   while ( i != a ) {
1626     i = i -> next;
1627     col++;
1628   } // while
1629   col %= pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 );
1630
1631   return ( col );
1632 }
1633
1634 unsigned char ui_show_info ( char *pndrun, pnd_disco_t *p ) {
1635   char *viewer, *searchpath;
1636   pnd_conf_handle desktoph;
1637
1638   // viewer
1639   searchpath = pnd_conf_query_searchpath();
1640
1641   desktoph = pnd_conf_fetch_by_id ( pnd_conf_desktop, searchpath );
1642
1643   if ( ! desktoph ) {
1644     return ( 0 );
1645   }
1646
1647   viewer = pnd_conf_get_as_char ( desktoph, "info.viewer" );
1648
1649   if ( ! viewer ) {
1650     return ( 0 ); // no way to view the file
1651   }
1652
1653   // etc
1654   if ( ! p -> unique_id ) {
1655     return ( 0 );
1656   }
1657
1658   if ( ! p -> info_filename ) {
1659     return ( 0 );
1660   }
1661
1662   if ( ! p -> info_name ) {
1663     return ( 0 );
1664   }
1665
1666   if ( ! pndrun ) {
1667     return ( 0 );
1668   }
1669
1670   // exec line
1671   char args [ 1001 ];
1672   char *pargs = args;
1673   if ( pnd_conf_get_as_char ( desktoph, "info.viewer_args" ) ) {
1674     snprintf ( pargs, 1001, "%s %s",
1675                pnd_conf_get_as_char ( desktoph, "info.viewer_args" ), p -> info_filename );
1676   } else {
1677     pargs = NULL;
1678   }
1679
1680   char pndfile [ 1024 ];
1681   if ( p -> object_type == pnd_object_type_directory ) {
1682     // for PXML-app-dir, pnd_run.sh doesn't want the PXML.xml.. it just wants the dir-name
1683     strncpy ( pndfile, p -> object_path, 1000 );
1684   } else if ( p -> object_type == pnd_object_type_pnd ) {
1685     // pnd_run.sh wants the full path and filename for the .pnd file
1686     snprintf ( pndfile, 1020, "%s/%s", p -> object_path, p -> object_filename );
1687   }
1688
1689   if ( ! pnd_apps_exec ( pndrun, pndfile, p -> unique_id, viewer, p -> startdir, pargs,
1690                          p -> clockspeed ? atoi ( p -> clockspeed ) : 0, PND_EXEC_OPTION_NORUN ) )
1691   {
1692     return ( 0 );
1693   }
1694
1695   pnd_log ( pndn_debug, "Info Exec=%s\n", pnd_apps_exec_runline() );
1696
1697   // try running it
1698   int x;
1699   if ( ( x = fork() ) < 0 ) {
1700     pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" );
1701     return ( 0 );
1702   }
1703
1704   if ( x == 0 ) {
1705     execl ( "/bin/sh", "/bin/sh", "-c", pnd_apps_exec_runline(), (char*)NULL );
1706     pnd_log ( pndn_error, "ERROR: Couldn't exec(%s)\n", pnd_apps_exec_runline() );
1707     return ( 0 );
1708   }
1709
1710   return ( 1 );
1711 }