MythTV  0.26-pre
graphics_controller.c
Go to the documentation of this file.
00001 /*
00002  * This file is part of libbluray
00003  * Copyright (C) 2010  hpi1
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Lesser General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2.1 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Lesser General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public
00016  * License along with this library. If not, see
00017  * <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include "graphics_controller.h"
00021 
00022 #include "graphics_processor.h"
00023 #include "ig.h"
00024 #include "overlay.h"
00025 
00026 #include "util/macro.h"
00027 #include "util/logging.h"
00028 #include "util/mutex.h"
00029 
00030 #include "../register.h"
00031 #include "../keys.h"
00032 
00033 #include <inttypes.h>
00034 #include <string.h>
00035 
00036 #define GC_ERROR(...) BD_DEBUG(DBG_GC | DBG_CRIT, __VA_ARGS__)
00037 #define GC_TRACE(...) BD_DEBUG(DBG_GC,            __VA_ARGS__)
00038 
00039 /*
00040  *
00041  */
00042 
00043 typedef struct {
00044     uint16_t enabled_button;  /* enabled button id */
00045     uint16_t x, y, w, h;      /* button rect on overlay plane (if drawn) */
00046     int      animate_indx;    /* currently showing object index of animated button, < 0 for static buttons */
00047 } BOG_DATA;
00048 
00049 struct graphics_controller_s {
00050 
00051     BD_REGISTERS   *regs;
00052 
00053     BD_MUTEX        mutex;
00054 
00055     /* overlay output */
00056     void           *overlay_proc_handle;
00057     void          (*overlay_proc)(void *, const struct bd_overlay_s * const);
00058 
00059     /* state */
00060     unsigned        ig_drawn;
00061     unsigned        pg_drawn;
00062     unsigned        popup_visible;
00063     unsigned        valid_mouse_position;
00064     BOG_DATA       *bog_data;
00065     BOG_DATA       *saved_bog_data;
00066 
00067     /* data */
00068     PG_DISPLAY_SET *pgs;
00069     PG_DISPLAY_SET *igs;
00070 
00071     /* */
00072     GRAPHICS_PROCESSOR *pgp;
00073     GRAPHICS_PROCESSOR *igp;
00074 };
00075 
00076 /*
00077  * object lookup
00078  */
00079 
00080 static BD_PG_OBJECT *_find_object(PG_DISPLAY_SET *s, unsigned object_id)
00081 {
00082     unsigned ii;
00083 
00084     for (ii = 0; ii < s->num_object; ii++) {
00085         if (s->object[ii].id == object_id) {
00086             return &s->object[ii];
00087         }
00088     }
00089 
00090     return NULL;
00091 }
00092 
00093 static BD_PG_PALETTE *_find_palette(PG_DISPLAY_SET *s, unsigned palette_id)
00094 {
00095     unsigned ii;
00096 
00097     for (ii = 0; ii < s->num_palette; ii++) {
00098         if (s->palette[ii].id == palette_id) {
00099             return &s->palette[ii];
00100         }
00101     }
00102 
00103     return NULL;
00104 }
00105 
00106 static BD_IG_BUTTON *_find_button_bog(BD_IG_BOG *bog, unsigned button_id)
00107 {
00108     unsigned ii;
00109 
00110     for (ii = 0; ii < bog->num_buttons; ii++) {
00111         if (bog->button[ii].id == button_id) {
00112             return &bog->button[ii];
00113         }
00114     }
00115 
00116     return NULL;
00117 }
00118 
00119 static BD_IG_BUTTON *_find_button_page(BD_IG_PAGE *page, unsigned button_id, unsigned *bog_idx)
00120 {
00121     unsigned ii;
00122 
00123     for (ii = 0; ii < page->num_bogs; ii++) {
00124         BD_IG_BUTTON *button = _find_button_bog(&page->bog[ii], button_id);
00125         if (button) {
00126             if (bog_idx) {
00127                 *bog_idx = ii;
00128             }
00129             return button;
00130         }
00131     }
00132 
00133     return NULL;
00134 }
00135 
00136 static BD_IG_PAGE *_find_page(BD_IG_INTERACTIVE_COMPOSITION *c, unsigned page_id)
00137 {
00138     unsigned ii;
00139 
00140     for (ii = 0; ii < c->num_pages; ii++) {
00141         if (c->page[ii].id == page_id) {
00142             return &c->page[ii];
00143         }
00144     }
00145 
00146     return NULL;
00147 }
00148 
00149 enum { BTN_NORMAL, BTN_SELECTED, BTN_ACTIVATED };
00150 
00151 static BD_PG_OBJECT *_find_object_for_button(PG_DISPLAY_SET *s,
00152                                              BD_IG_BUTTON *button, int state,
00153                                              BOG_DATA *bog_data)
00154 {
00155     BD_PG_OBJECT *object   = NULL;
00156     unsigned object_id     = 0xffff;
00157     unsigned object_id_end = 0xffff;
00158     unsigned repeat        = 0;
00159 
00160     switch (state) {
00161         case BTN_NORMAL:
00162             object_id     = button->normal_start_object_id_ref;
00163             object_id_end = button->normal_end_object_id_ref;
00164             repeat        = button->normal_repeat_flag;
00165             break;
00166         case BTN_SELECTED:
00167             object_id     = button->selected_start_object_id_ref;
00168             object_id_end = button->selected_end_object_id_ref;
00169             repeat        = button->selected_repeat_flag;
00170             break;
00171         case BTN_ACTIVATED:
00172             object_id     = button->activated_start_object_id_ref;
00173             object_id_end = button->activated_end_object_id_ref;
00174             break;
00175     }
00176 
00177     if (bog_data) {
00178         if (bog_data->animate_indx >= 0) {
00179             int range = object_id_end - object_id;
00180 
00181             if (range > 0 && object_id < 0xffff && object_id_end < 0xffff) {
00182                 GC_TRACE("animate button #%d: animate_indx %d, range %d, repeat %d\n",
00183                          button->id, bog_data->animate_indx, range, repeat);
00184 
00185                 object_id += bog_data->animate_indx % (range + 1);
00186                 bog_data->animate_indx++;
00187                 if (!repeat && bog_data->animate_indx > range) {
00188                 /* terminate animation to the last object */
00189                     bog_data->animate_indx = -1;
00190                 }
00191 
00192             } else {
00193                 /* no animation for this button */
00194                 bog_data->animate_indx = -1;
00195             }
00196         }
00197     }
00198 
00199     object = _find_object(s, object_id);
00200 
00201     return object;
00202 }
00203 
00204 /*
00205  * util
00206  */
00207 
00208 static int _is_button_enabled(GRAPHICS_CONTROLLER *gc, BD_IG_PAGE *page, unsigned button_id)
00209 {
00210     unsigned ii;
00211     for (ii = 0; ii < page->num_bogs; ii++) {
00212         if (gc->bog_data[ii].enabled_button == button_id) {
00213             return 1;
00214         }
00215     }
00216     return 0;
00217 }
00218 
00219 static uint16_t _find_selected_button_id(GRAPHICS_CONTROLLER *gc)
00220 {
00221     /* executed when playback condition changes (ex. new page, popup-on, ...) */
00222     PG_DISPLAY_SET *s         = gc->igs;
00223     BD_IG_PAGE     *page      = NULL;
00224     unsigned        page_id   = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
00225     unsigned        button_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
00226     unsigned        ii;
00227 
00228     page = _find_page(&s->ics->interactive_composition, page_id);
00229     if (!page) {
00230         GC_TRACE("_find_selected_button_id(): unknown page #%d (have %d pages)\n",
00231               page_id, s->ics->interactive_composition.num_pages);
00232         return 0xffff;
00233     }
00234 
00235     /* run 5.9.8.3 */
00236 
00237     /* 1) always use page->default_selected_button_id_ref if it is valid */
00238     if (_find_button_page(page, page->default_selected_button_id_ref, NULL) &&
00239         _is_button_enabled(gc, page, page->default_selected_button_id_ref)) {
00240 
00241         GC_TRACE("_find_selected_button_id() -> default #%d\n", page->default_selected_button_id_ref);
00242         return page->default_selected_button_id_ref;
00243     }
00244 
00245     /* 2) fallback to current PSR10 value if it is valid */
00246     for (ii = 0; ii < page->num_bogs; ii++) {
00247         BD_IG_BOG *bog = &page->bog[ii];
00248         uint16_t   enabled_button = gc->bog_data[ii].enabled_button;
00249 
00250         if (button_id == enabled_button) {
00251             if (_find_button_bog(bog, enabled_button)) {
00252                 GC_TRACE("_find_selected_button_id() -> PSR10 #%d\n", enabled_button);
00253                 return enabled_button;
00254             }
00255         }
00256     }
00257 
00258     /* 3) fallback to find first valid_button_id_ref from page */
00259     for (ii = 0; ii < page->num_bogs; ii++) {
00260         BD_IG_BOG *bog = &page->bog[ii];
00261         uint16_t   enabled_button = gc->bog_data[ii].enabled_button;
00262 
00263         if (_find_button_bog(bog, enabled_button)) {
00264             GC_TRACE("_find_selected_button_id() -> first valid #%d\n", enabled_button);
00265             return enabled_button;
00266         }
00267     }
00268 
00269     GC_TRACE("_find_selected_button_id(): not found -> 0xffff\n");
00270     return 0xffff;
00271 }
00272 
00273 static int _save_page_state(GRAPHICS_CONTROLLER *gc)
00274 {
00275     if (!gc->bog_data) {
00276         GC_ERROR("_save_page_state(): no bog data !\n");
00277         return -1;
00278     }
00279 
00280     PG_DISPLAY_SET *s       = gc->igs;
00281     BD_IG_PAGE     *page    = NULL;
00282     unsigned        page_id = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
00283     unsigned        ii;
00284 
00285     page = _find_page(&s->ics->interactive_composition, page_id);
00286     if (!page) {
00287         GC_ERROR("_save_page_state(): unknown page #%d (have %d pages)\n",
00288               page_id, s->ics->interactive_composition.num_pages);
00289         return -1;
00290     }
00291 
00292     /* copy enabled button state, clear draw state */
00293 
00294     X_FREE(gc->saved_bog_data);
00295     gc->saved_bog_data = calloc(page->num_bogs, sizeof(*gc->saved_bog_data));
00296 
00297     for (ii = 0; ii < page->num_bogs; ii++) {
00298         gc->saved_bog_data[ii].enabled_button = gc->bog_data[ii].enabled_button;
00299         gc->saved_bog_data[ii].animate_indx   = gc->bog_data[ii].animate_indx >= 0 ? 0 : -1;
00300     }
00301 
00302     return 1;
00303 }
00304 
00305 static int _restore_page_state(GRAPHICS_CONTROLLER *gc)
00306 {
00307     if (gc->saved_bog_data) {
00308         if (gc->bog_data) {
00309             GC_ERROR("_restore_page_state(): bog data already exists !\n");
00310             X_FREE(gc->bog_data);
00311         }
00312         gc->bog_data       = gc->saved_bog_data;
00313         gc->saved_bog_data = NULL;
00314 
00315         return 1;
00316     }
00317     return -1;
00318 }
00319 
00320 static void _reset_page_state(GRAPHICS_CONTROLLER *gc)
00321 {
00322     PG_DISPLAY_SET *s       = gc->igs;
00323     BD_IG_PAGE     *page    = NULL;
00324     unsigned        page_id = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
00325     unsigned        ii;
00326 
00327     page = _find_page(&s->ics->interactive_composition, page_id);
00328     if (!page) {
00329         GC_ERROR("_reset_page_state(): unknown page #%d (have %d pages)\n",
00330               page_id, s->ics->interactive_composition.num_pages);
00331         return;
00332     }
00333 
00334     size_t size = page->num_bogs * sizeof(*gc->bog_data);
00335     gc->bog_data = realloc(gc->bog_data, size);
00336 
00337     memset(gc->bog_data, 0, size);
00338 
00339     for (ii = 0; ii < page->num_bogs; ii++) {
00340         gc->bog_data[ii].enabled_button = page->bog[ii].default_valid_button_id_ref;
00341         gc->bog_data[ii].animate_indx   = 0;
00342     }
00343 }
00344 
00345 static void _clear_osd_area(GRAPHICS_CONTROLLER *gc, int plane,
00346                             uint16_t x, uint16_t y, uint16_t w, uint16_t h)
00347 {
00348     if (gc->overlay_proc) {
00349         /* clear plane */
00350         const BD_OVERLAY ov = {
00351             .pts     = -1,
00352             .plane   = plane,
00353             .x       = x,
00354             .y       = y,
00355             .w       = w,
00356             .h       = h,
00357             .palette = NULL,
00358             .img     = NULL,
00359         };
00360 
00361         gc->overlay_proc(gc->overlay_proc_handle, &ov);
00362     }
00363 }
00364 
00365 static void _clear_osd(GRAPHICS_CONTROLLER *gc, int plane)
00366 {
00367     _clear_osd_area(gc, plane, 0, 0, 1920, 1080);
00368 
00369     if (plane == BD_OVERLAY_IG) {
00370         gc->ig_drawn      = 0;
00371     } else {
00372         gc->pg_drawn      = 0;
00373     }
00374 }
00375 
00376 static void _clear_bog_area(GRAPHICS_CONTROLLER *gc, BOG_DATA *bog_data)
00377 {
00378     if (gc->ig_drawn && bog_data->w && bog_data->h) {
00379 
00380         _clear_osd_area(gc, BD_OVERLAY_IG, bog_data->x, bog_data->y, bog_data->w, bog_data->h);
00381 
00382         bog_data->x = bog_data->y = bog_data->w = bog_data->h = 0;
00383     }
00384 }
00385 
00386 static void _select_button(GRAPHICS_CONTROLLER *gc, uint32_t button_id)
00387 {
00388     bd_psr_write(gc->regs, PSR_SELECTED_BUTTON_ID, button_id);
00389 }
00390 
00391 static void _select_page(GRAPHICS_CONTROLLER *gc, uint16_t page_id)
00392 {
00393     bd_psr_write(gc->regs, PSR_MENU_PAGE_ID, page_id);
00394     _clear_osd(gc, BD_OVERLAY_IG);
00395     _reset_page_state(gc);
00396 
00397     uint16_t button_id = _find_selected_button_id(gc);
00398     _select_button(gc, button_id);
00399 }
00400 
00401 static void _gc_reset(GRAPHICS_CONTROLLER *gc)
00402 {
00403     _clear_osd(gc, BD_OVERLAY_PG);
00404     _clear_osd(gc, BD_OVERLAY_IG);
00405 
00406     gc->popup_visible = 0;
00407 
00408     graphics_processor_free(&gc->igp);
00409     graphics_processor_free(&gc->pgp);
00410 
00411     pg_display_set_free(&gc->pgs);
00412     pg_display_set_free(&gc->igs);
00413 
00414     X_FREE(gc->bog_data);
00415 }
00416 
00417 /*
00418  * register hook
00419  */
00420 static void _process_psr_event(void *handle, BD_PSR_EVENT *ev)
00421 {
00422     GRAPHICS_CONTROLLER *gc = (GRAPHICS_CONTROLLER *)handle;
00423 
00424     if (ev->ev_type == BD_PSR_SAVE) {
00425         BD_DEBUG(DBG_GC, "PSR SAVE event\n");
00426 
00427         /* save menu page state */
00428         bd_mutex_lock(&gc->mutex);
00429         _save_page_state(gc);
00430         bd_mutex_unlock(&gc->mutex);
00431 
00432         return;
00433     }
00434 
00435     if (ev->ev_type == BD_PSR_RESTORE) {
00436         switch (ev->psr_idx) {
00437 
00438             case PSR_SELECTED_BUTTON_ID:
00439               return;
00440 
00441             case PSR_MENU_PAGE_ID:
00442                 /* restore menus */
00443                 bd_mutex_lock(&gc->mutex);
00444                 _restore_page_state(gc);
00445                 bd_mutex_unlock(&gc->mutex);
00446                 return;
00447 
00448             default:
00449                 /* others: ignore */
00450                 return;
00451         }
00452     }
00453 }
00454 
00455 /*
00456  * init / free
00457  */
00458 
00459 GRAPHICS_CONTROLLER *gc_init(BD_REGISTERS *regs, void *handle, gc_overlay_proc_f func)
00460 {
00461     GRAPHICS_CONTROLLER *p = calloc(1, sizeof(*p));
00462 
00463     p->regs = regs;
00464 
00465     p->overlay_proc_handle = handle;
00466     p->overlay_proc        = func;
00467 
00468     bd_mutex_init(&p->mutex);
00469 
00470     bd_psr_register_cb(regs, _process_psr_event, p);
00471 
00472     return p;
00473 }
00474 
00475 void gc_free(GRAPHICS_CONTROLLER **p)
00476 {
00477     if (p && *p) {
00478 
00479         GRAPHICS_CONTROLLER *gc = *p;
00480 
00481         bd_psr_unregister_cb(gc->regs, _process_psr_event, gc);
00482 
00483         _gc_reset(gc);
00484 
00485         if (gc->overlay_proc) {
00486             gc->overlay_proc(gc->overlay_proc_handle, NULL);
00487         }
00488 
00489         bd_mutex_destroy(&gc->mutex);
00490 
00491         X_FREE(*p);
00492     }
00493 }
00494 
00495 /*
00496  * graphics stream input
00497  */
00498 
00499 int gc_decode_ts(GRAPHICS_CONTROLLER *gc, uint16_t pid, uint8_t *block, unsigned num_blocks, int64_t stc)
00500 {
00501     if (!gc) {
00502         GC_TRACE("gc_decode_ts(): no graphics controller\n");
00503         return -1;
00504     }
00505 
00506     if (pid >= 0x1400 && pid < 0x1500) {
00507         /* IG stream */
00508 
00509         if (!gc->igp) {
00510             gc->igp = graphics_processor_init();
00511         }
00512 
00513         bd_mutex_lock(&gc->mutex);
00514 
00515         if (!graphics_processor_decode_ts(gc->igp, &gc->igs,
00516                                           pid, block, num_blocks,
00517                                           stc)) {
00518             /* no new complete display set */
00519             bd_mutex_unlock(&gc->mutex);
00520             return 0;
00521         }
00522 
00523         if (!gc->igs || !gc->igs->complete) {
00524             bd_mutex_unlock(&gc->mutex);
00525             return 0;
00526         }
00527 
00528         gc->popup_visible = 0;
00529 
00530         _select_page(gc, 0);
00531 
00532         bd_mutex_unlock(&gc->mutex);
00533 
00534         return 1;
00535     }
00536 
00537     else if (pid >= 0x1200 && pid < 0x1300) {
00538         /* PG stream */
00539         if (!gc->pgp) {
00540             gc->pgp = graphics_processor_init();
00541         }
00542         graphics_processor_decode_ts(gc->pgp, &gc->pgs,
00543                                      pid, block, num_blocks,
00544                                      stc);
00545 
00546         if (!gc->pgs || !gc->pgs->complete) {
00547             return 0;
00548         }
00549 
00550         return 1;
00551     }
00552 
00553     return -1;
00554 }
00555 
00556 /*
00557  * IG rendering
00558  */
00559 
00560 static void _render_button(GRAPHICS_CONTROLLER *gc, BD_IG_BUTTON *button, BD_PG_PALETTE *palette,
00561                            int state, BOG_DATA *bog_data)
00562 {
00563     BD_PG_OBJECT *object    = NULL;
00564     BD_OVERLAY    ov;
00565 
00566     object = _find_object_for_button(gc->igs, button, state, bog_data);
00567     if (!object) {
00568         GC_TRACE("_render_button(#%d): object (state %d) not found\n", button->id, state);
00569 
00570         _clear_bog_area(gc, bog_data);
00571 
00572         return;
00573     }
00574 
00575     ov.pts   = -1;
00576     ov.plane = BD_OVERLAY_IG;
00577 
00578     ov.x = bog_data->x = button->x_pos;
00579     ov.y = bog_data->y = button->y_pos;
00580     ov.w = bog_data->w = object->width;
00581     ov.h = bog_data->h = object->height;
00582 
00583     ov.img     = object->img;
00584     ov.palette = palette->entry;
00585 
00586     if (gc->overlay_proc) {
00587         gc->overlay_proc(gc->overlay_proc_handle, &ov);
00588         gc->ig_drawn = 1;
00589     }
00590 }
00591 
00592 static void _render_page(GRAPHICS_CONTROLLER *gc,
00593                          unsigned activated_button_id,
00594                          GC_NAV_CMDS *cmds)
00595 {
00596     PG_DISPLAY_SET *s       = gc->igs;
00597     BD_IG_PAGE     *page    = NULL;
00598     BD_PG_PALETTE  *palette = NULL;
00599     unsigned        page_id = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
00600     unsigned        ii;
00601     unsigned        selected_button_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
00602 
00603     if (s->ics->interactive_composition.ui_model == IG_UI_MODEL_POPUP && !gc->popup_visible) {
00604         GC_TRACE("_render_page(): popup menu not visible\n");
00605 
00606         _clear_osd(gc, BD_OVERLAY_IG);
00607 
00608         return;
00609     }
00610 
00611     page = _find_page(&s->ics->interactive_composition, page_id);
00612     if (!page) {
00613         GC_ERROR("_render_page: unknown page id %d (have %d pages)\n",
00614               page_id, s->ics->interactive_composition.num_pages);
00615         return;
00616     }
00617 
00618     palette = _find_palette(s, page->palette_id_ref);
00619     if (!palette) {
00620         GC_ERROR("_render_page: unknown palette id %d (have %d palettes)\n",
00621               page->palette_id_ref, s->num_palette);
00622         return;
00623     }
00624 
00625     GC_TRACE("rendering page #%d using palette #%d. page has %d bogs\n",
00626           page->id, page->palette_id_ref, page->num_bogs);
00627 
00628     for (ii = 0; ii < page->num_bogs; ii++) {
00629         BD_IG_BOG    *bog      = &page->bog[ii];
00630         unsigned      valid_id = gc->bog_data[ii].enabled_button;
00631         BD_IG_BUTTON *button;
00632 
00633         button = _find_button_bog(bog, valid_id);
00634 
00635         if (!button) {
00636             GC_TRACE("_render_page(): bog %d: button %d not found\n", ii, valid_id);
00637 
00638         } else if (button->id == activated_button_id) {
00639             _render_button(gc, button, palette, BTN_ACTIVATED, &gc->bog_data[ii]);
00640 
00641         } else if (button->id == selected_button_id) {
00642 
00643             _render_button(gc, button, palette, BTN_SELECTED, &gc->bog_data[ii]);
00644 
00645             if (button->auto_action_flag && cmds) {
00646                 cmds->num_nav_cmds = button->num_nav_cmds;
00647                 cmds->nav_cmds     = button->nav_cmds;
00648             }
00649 
00650         } else {
00651             _render_button(gc, button, palette, BTN_NORMAL, &gc->bog_data[ii]);
00652 
00653         }
00654     }
00655 }
00656 
00657 /*
00658  * user actions
00659  */
00660 
00661 #define VK_IS_NUMERIC(vk) (/*vk >= BD_VK_0  &&*/ vk <= BD_VK_9)
00662 #define VK_IS_CURSOR(vk)  (vk >= BD_VK_UP && vk <= BD_VK_RIGHT)
00663 #define VK_TO_NUMBER(vk)  ((vk) - BD_VK_0)
00664 
00665 static int _user_input(GRAPHICS_CONTROLLER *gc, bd_vk_key_e key, GC_NAV_CMDS *cmds)
00666 {
00667     PG_DISPLAY_SET *s          = gc->igs;
00668     BD_IG_PAGE     *page       = NULL;
00669     unsigned        page_id    = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
00670     unsigned        cur_btn_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
00671     unsigned        new_btn_id = cur_btn_id;
00672     unsigned        ii;
00673     int             activated_btn_id = -1;
00674 
00675     if (s->ics->interactive_composition.ui_model == IG_UI_MODEL_POPUP && !gc->popup_visible) {
00676         GC_TRACE("_user_input(): popup menu not visible\n");
00677         return -1;
00678     }
00679     if (!gc->ig_drawn) {
00680         GC_ERROR("_user_input(): menu not visible\n");
00681         return -1;
00682     }
00683 
00684     GC_TRACE("_user_input(%d)\n", key);
00685 
00686     page = _find_page(&s->ics->interactive_composition, page_id);
00687     if (!page) {
00688         GC_ERROR("_user_input(): unknown page id %d (have %d pages)\n",
00689               page_id, s->ics->interactive_composition.num_pages);
00690         return -1;
00691     }
00692 
00693     if (key == BD_VK_MOUSE_ACTIVATE) {
00694         if (!gc->valid_mouse_position) {
00695             GC_TRACE("_user_input(): BD_VK_MOUSE_ACTIVATE outside of valid buttons\n");
00696             return -1;
00697         }
00698         key = BD_VK_ENTER;
00699     }
00700 
00701     for (ii = 0; ii < page->num_bogs; ii++) {
00702         BD_IG_BOG *bog      = &page->bog[ii];
00703         unsigned   valid_id = gc->bog_data[ii].enabled_button;
00704         BD_IG_BUTTON *button = _find_button_bog(bog, valid_id);
00705         if (!button) {
00706             continue;
00707         }
00708 
00709         /* numeric select */
00710         if (VK_IS_NUMERIC(key)) {
00711             if (button->numeric_select_value == VK_TO_NUMBER(key)) {
00712                 new_btn_id = button->id;
00713             }
00714         }
00715 
00716         /* cursor keys */
00717         else if (VK_IS_CURSOR(key) || key == BD_VK_ENTER) {
00718             if (button->id == cur_btn_id) {
00719                 switch(key) {
00720                     case BD_VK_UP:
00721                         new_btn_id = button->upper_button_id_ref;
00722                         break;
00723                     case BD_VK_DOWN:
00724                         new_btn_id = button->lower_button_id_ref;
00725                         break;
00726                     case BD_VK_LEFT:
00727                         new_btn_id = button->left_button_id_ref;
00728                         break;
00729                     case BD_VK_RIGHT:
00730                         new_btn_id = button->right_button_id_ref;
00731                         break;
00732                     case BD_VK_ENTER:
00733                         activated_btn_id = cur_btn_id;
00734 
00735                         cmds->num_nav_cmds = button->num_nav_cmds;
00736                         cmds->nav_cmds     = button->nav_cmds;
00737                         cmds->sound_id_ref = button->activated_sound_id_ref;
00738                         break;
00739                     default:;
00740                 }
00741             }
00742 
00743             if (new_btn_id != cur_btn_id) {
00744                 BD_IG_BUTTON *button = _find_button_page(page, new_btn_id, NULL);
00745                 if (button) {
00746                     cmds->sound_id_ref = button->selected_sound_id_ref;
00747                 }
00748             }
00749         }
00750     }
00751 
00752     /* render page ? */
00753     if (new_btn_id != cur_btn_id || activated_btn_id >= 0) {
00754 
00755         _select_button(gc, new_btn_id);
00756 
00757         _render_page(gc, activated_btn_id, cmds);
00758 
00759         /* found one*/
00760         return 1;
00761     }
00762 
00763     return 0;
00764 }
00765 
00766 static void _set_button_page(GRAPHICS_CONTROLLER *gc, uint32_t param)
00767 {
00768     unsigned page_flag   = param & 0x80000000;
00769     unsigned effect_flag = param & 0x40000000;
00770     unsigned button_flag = param & 0x20000000;
00771     unsigned page_id     = (param >> 16) & 0xff;
00772     unsigned button_id   = param & 0xffff;
00773     unsigned bog_idx     = 0;
00774 
00775     PG_DISPLAY_SET *s      = gc->igs;
00776     BD_IG_PAGE     *page   = NULL;
00777     BD_IG_BUTTON   *button = NULL;
00778 
00779     GC_TRACE("_set_button_page(0x%08x): page flag %d, id %d, effects %d   button flag %d, id %d\n",
00780           param, !!page_flag, page_id, !!effect_flag, !!button_flag, button_id);
00781 
00782     /* 10.4.3.4 (D) */
00783 
00784     if (!page_flag && !button_flag) {
00785         return;
00786     }
00787 
00788     if (page_flag) {
00789 
00790         /* current page --> command is ignored */
00791         if (page_id == bd_psr_read(gc->regs, PSR_MENU_PAGE_ID)) {
00792             GC_TRACE("  page is current\n");
00793             return;
00794         }
00795 
00796         page = _find_page(&s->ics->interactive_composition, page_id);
00797 
00798         /* invalid page --> command is ignored */
00799         if (!page) {
00800             GC_TRACE("  page is invalid\n");
00801             return;
00802         }
00803 
00804         /* page changes */
00805 
00806         _select_page(gc, page_id);
00807 
00808     } else {
00809         /* page does not change */
00810         page_id = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
00811         page    = _find_page(&s->ics->interactive_composition, page_id);
00812 
00813         if (!page) {
00814             GC_ERROR("_set_button_page(): PSR_MENU_PAGE_ID refers to unknown page %d\n", page_id);
00815             return;
00816         }
00817     }
00818 
00819     if (button_flag) {
00820         /* find correct button and overlap group */
00821         button = _find_button_page(page, button_id, &bog_idx);
00822 
00823         if (!page_flag) {
00824             if (!button) {
00825                 /* page not given, invalid button --> ignore command */
00826                 GC_TRACE("  button is invalid\n");
00827                 return;
00828             }
00829             if (button_id == bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID)) {
00830                 /* page not given, current button --> ignore command */
00831                 GC_TRACE("  button is current\n");
00832                 return;
00833             }
00834         }
00835     }
00836 
00837     if (button) {
00838         gc->bog_data[bog_idx].enabled_button = button_id;
00839         _select_button(gc, button_id);
00840     }
00841 
00842     _render_page(gc, 0xffff, NULL);
00843 }
00844 
00845 static void _enable_button(GRAPHICS_CONTROLLER *gc, uint32_t button_id, unsigned enable)
00846 {
00847     PG_DISPLAY_SET *s          = gc->igs;
00848     BD_IG_PAGE     *page       = NULL;
00849     BD_IG_BUTTON   *button     = NULL;
00850     unsigned        page_id    = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
00851     unsigned        cur_btn_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
00852     unsigned        bog_idx    = 0;
00853 
00854     GC_TRACE("_enable_button(#%d, %s)\n", button_id, enable ? "enable" : "disable");
00855 
00856     page = _find_page(&s->ics->interactive_composition, page_id);
00857     if (!page) {
00858         GC_TRACE("_enable_button(): unknown page #%d (have %d pages)\n",
00859               page_id, s->ics->interactive_composition.num_pages);
00860         return;
00861     }
00862 
00863     /* find correct button overlap group */
00864     button = _find_button_page(page, button_id, &bog_idx);
00865     if (!button) {
00866         GC_TRACE("_enable_button(): unknown button #%d (page #%d)\n", button_id, page_id);
00867         return;
00868     }
00869 
00870     if (enable) {
00871         if (gc->bog_data[bog_idx].enabled_button == cur_btn_id) {
00872             /* selected button goes to disabled state */
00873             bd_psr_write(gc->regs, PSR_SELECTED_BUTTON_ID, 0x10000|button_id);
00874         }
00875         gc->bog_data[bog_idx].enabled_button = button_id;
00876 
00877     } else {
00878         if (gc->bog_data[bog_idx].enabled_button == button_id) {
00879             gc->bog_data[bog_idx].enabled_button = 0xffff;
00880         }
00881 
00882         if (cur_btn_id == button_id) {
00883             bd_psr_write(gc->regs, PSR_SELECTED_BUTTON_ID, 0xffff);
00884         }
00885     }
00886 }
00887 
00888 static void _update_selected_button(GRAPHICS_CONTROLLER *gc)
00889 {
00890     /* executed after IG command sequence terminates */
00891     unsigned button_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
00892 
00893     GC_TRACE("_update_selected_button(): currently enabled button is #%d\n", button_id);
00894 
00895     /* special case: triggered only after enable button disables selected button */
00896     if (button_id & 0x10000) {
00897         button_id &= 0xffff;
00898         _select_button(gc, button_id);
00899         GC_TRACE("_update_selected_button() -> #%d [last enabled]\n", button_id);
00900         return;
00901     }
00902 
00903     if (button_id == 0xffff) {
00904         button_id = _find_selected_button_id(gc);
00905         _select_button(gc, button_id);
00906     }
00907 }
00908 
00909 static int _mouse_move(GRAPHICS_CONTROLLER *gc, unsigned x, unsigned y, GC_NAV_CMDS *cmds)
00910 {
00911     PG_DISPLAY_SET *s          = gc->igs;
00912     BD_IG_PAGE     *page       = NULL;
00913     unsigned        page_id    = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
00914     unsigned        cur_btn_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
00915     unsigned        new_btn_id = 0xffff;
00916     unsigned        ii;
00917 
00918     gc->valid_mouse_position = 0;
00919 
00920     if (!gc->ig_drawn) {
00921         GC_TRACE("_mouse_move(): menu not visible\n");
00922         return -1;
00923     }
00924 
00925     page = _find_page(&s->ics->interactive_composition, page_id);
00926     if (!page) {
00927         GC_ERROR("_mouse_move(): unknown page #%d (have %d pages)\n",
00928               page_id, s->ics->interactive_composition.num_pages);
00929         return -1;
00930     }
00931 
00932     for (ii = 0; ii < page->num_bogs; ii++) {
00933         BD_IG_BOG    *bog      = &page->bog[ii];
00934         unsigned      valid_id = gc->bog_data[ii].enabled_button;
00935         BD_IG_BUTTON *button   = _find_button_bog(bog, valid_id);
00936 
00937         if (!button)
00938             continue;
00939 
00940         if (x < button->x_pos || y < button->y_pos)
00941             continue;
00942 
00943         /* Check for SELECTED state object (button that can be selected) */
00944         BD_PG_OBJECT *object = _find_object_for_button(s, button, BTN_SELECTED, NULL);
00945         if (!object)
00946             continue;
00947 
00948         if (x >= button->x_pos + object->width || y >= button->y_pos + object->height)
00949             continue;
00950 
00951         /* mouse is over button */
00952         gc->valid_mouse_position = 1;
00953 
00954         /* is button already selected? */
00955         if (button->id == cur_btn_id) {
00956             return 1;
00957         }
00958 
00959         new_btn_id = button->id;
00960         break;
00961     }
00962 
00963     if (new_btn_id != 0xffff) {
00964         _select_button(gc, new_btn_id);
00965 
00966         _render_page(gc, -1, cmds);
00967     }
00968 
00969     return gc->valid_mouse_position;
00970 }
00971 
00972 int gc_run(GRAPHICS_CONTROLLER *gc, gc_ctrl_e ctrl, uint32_t param, GC_NAV_CMDS *cmds)
00973 {
00974     int result = -1;
00975 
00976     if (cmds) {
00977         cmds->num_nav_cmds = 0;
00978         cmds->nav_cmds     = NULL;
00979         cmds->sound_id_ref = -1;
00980     }
00981 
00982     if (!gc) {
00983         GC_TRACE("gc_run(): no graphics controller\n");
00984         return result;
00985     }
00986 
00987     bd_mutex_lock(&gc->mutex);
00988 
00989     /* always accept reset */
00990     switch (ctrl) {
00991         case GC_CTRL_RESET:
00992             _gc_reset(gc);
00993 
00994             bd_mutex_unlock(&gc->mutex);
00995             return 0;
00996 
00997         default:;
00998     }
00999 
01000     /* other operations require complete display set */
01001     if (!gc->igs || !gc->igs->ics || !gc->igs->complete) {
01002         GC_TRACE("gc_run(): no interactive composition\n");
01003         bd_mutex_unlock(&gc->mutex);
01004         return result;
01005     }
01006 
01007     switch (ctrl) {
01008 
01009         case GC_CTRL_SET_BUTTON_PAGE:
01010             _set_button_page(gc, param);
01011             break;
01012 
01013         case GC_CTRL_VK_KEY:
01014             if (param != BD_VK_POPUP) {
01015                 result = _user_input(gc, param, cmds);
01016                 break;
01017             }
01018             param = !gc->popup_visible;
01019             /* fall thru (BD_VK_POPUP) */
01020 
01021         case GC_CTRL_POPUP:
01022             if (gc->igs->ics->interactive_composition.ui_model != IG_UI_MODEL_POPUP) {
01023                 /* not pop-up menu */
01024                 break;
01025             }
01026 
01027             gc->popup_visible = !!param;
01028 
01029             if (gc->popup_visible) {
01030                 _select_page(gc, 0);
01031             }
01032 
01033             /* fall thru */
01034 
01035         case GC_CTRL_NOP:
01036             _render_page(gc, 0xffff, cmds);
01037             break;
01038 
01039         case GC_CTRL_IG_END:
01040             _update_selected_button(gc);
01041             _render_page(gc, 0xffff, cmds);
01042             break;
01043 
01044         case GC_CTRL_ENABLE_BUTTON:
01045             _enable_button(gc, param, 1);
01046             break;
01047 
01048         case GC_CTRL_DISABLE_BUTTON:
01049             _enable_button(gc, param, 0);
01050             break;
01051 
01052         case GC_CTRL_MOUSE_MOVE:
01053             result = _mouse_move(gc, param >> 16, param & 0xffff, cmds);
01054             break;
01055 
01056         case GC_CTRL_RESET:
01057             /* already handled */
01058             break;
01059     }
01060 
01061     bd_mutex_unlock(&gc->mutex);
01062 
01063     return result;
01064 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends