Blame src/libbluray/decoders/graphics_controller.c

Packit 5e46da
/*
Packit 5e46da
 * This file is part of libbluray
Packit 5e46da
 * Copyright (C) 2010-2013  Petri Hintukainen <phintuka@users.sourceforge.net>
Packit 5e46da
 *
Packit 5e46da
 * This library is free software; you can redistribute it and/or
Packit 5e46da
 * modify it under the terms of the GNU Lesser General Public
Packit 5e46da
 * License as published by the Free Software Foundation; either
Packit 5e46da
 * version 2.1 of the License, or (at your option) any later version.
Packit 5e46da
 *
Packit 5e46da
 * This library is distributed in the hope that it will be useful,
Packit 5e46da
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 5e46da
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 5e46da
 * Lesser General Public License for more details.
Packit 5e46da
 *
Packit 5e46da
 * You should have received a copy of the GNU Lesser General Public
Packit 5e46da
 * License along with this library. If not, see
Packit 5e46da
 * <http://www.gnu.org/licenses/>.
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#if HAVE_CONFIG_H
Packit 5e46da
#include "config.h"
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
#include "graphics_controller.h"
Packit 5e46da
Packit 5e46da
#include "graphics_processor.h"
Packit 5e46da
#include "hdmv_pids.h"
Packit 5e46da
#include "ig.h"
Packit 5e46da
#include "overlay.h"
Packit 5e46da
#include "textst_render.h"
Packit 5e46da
#include "rle.h"
Packit 5e46da
Packit 5e46da
#include "util/macro.h"
Packit 5e46da
#include "util/logging.h"
Packit 5e46da
#include "util/mutex.h"
Packit 5e46da
#include "util/time.h"
Packit 5e46da
Packit 5e46da
#include "bdnav/uo_mask.h"
Packit 5e46da
Packit 5e46da
#include "../register.h"
Packit 5e46da
#include "../keys.h"
Packit 5e46da
Packit 5e46da
#ifdef _WIN32
Packit 5e46da
/* mingw: PRId64 seems to expands to %d without stdio.h ... */
Packit 5e46da
#include <stdio.h>
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
#include <inttypes.h>
Packit 5e46da
#include <string.h>
Packit 5e46da
Packit 5e46da
#define GC_ERROR(...) BD_DEBUG(DBG_GC | DBG_CRIT, __VA_ARGS__)
Packit 5e46da
#define GC_TRACE(...) BD_DEBUG(DBG_GC,            __VA_ARGS__)
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 *
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
typedef struct {
Packit 5e46da
    uint16_t enabled_button;  /* enabled button id */
Packit 5e46da
    uint16_t x, y, w, h;      /* button rect on overlay plane (if drawn) */
Packit 5e46da
    int      visible_object_id; /* id of currently visible object */
Packit 5e46da
    int      animate_indx;    /* currently showing object index of animated button, < 0 for static buttons */
Packit 5e46da
    int      effect_running;  /* single-loop animation not yet complete */
Packit 5e46da
} BOG_DATA;
Packit 5e46da
Packit 5e46da
struct graphics_controller_s {
Packit 5e46da
Packit 5e46da
    BD_REGISTERS   *regs;
Packit 5e46da
Packit 5e46da
    BD_MUTEX        mutex;
Packit 5e46da
Packit 5e46da
    /* overlay output */
Packit 5e46da
    void           *overlay_proc_handle;
Packit 5e46da
    void          (*overlay_proc)(void *, const struct bd_overlay_s * const);
Packit 5e46da
Packit 5e46da
    /* state */
Packit 5e46da
    unsigned        ig_open;
Packit 5e46da
    unsigned        ig_drawn;
Packit 5e46da
    unsigned        ig_dirty;
Packit 5e46da
    unsigned        pg_open;
Packit 5e46da
    unsigned        pg_drawn;
Packit 5e46da
    unsigned        pg_dirty;
Packit 5e46da
    unsigned        popup_visible;
Packit 5e46da
    unsigned        valid_mouse_position;
Packit 5e46da
    unsigned        auto_action_triggered;
Packit 5e46da
    BOG_DATA        bog_data[MAX_NUM_BOGS];
Packit 5e46da
    BOG_DATA       *saved_bog_data;
Packit 5e46da
    BD_UO_MASK      page_uo_mask;
Packit 5e46da
Packit 5e46da
    /* page effects */
Packit 5e46da
    unsigned               effect_idx;
Packit 5e46da
    BD_IG_EFFECT_SEQUENCE *in_effects;
Packit 5e46da
    BD_IG_EFFECT_SEQUENCE *out_effects;
Packit 5e46da
    int64_t                next_effect_time; /* 90 kHz */
Packit 5e46da
Packit 5e46da
    /* timers */
Packit 5e46da
    int64_t                user_timeout;
Packit 5e46da
Packit 5e46da
    /* animated buttons */
Packit 5e46da
    unsigned               frame_interval;
Packit 5e46da
    unsigned               button_effect_running;
Packit 5e46da
    unsigned               button_animation_running;
Packit 5e46da
Packit 5e46da
    /* data */
Packit 5e46da
    PG_DISPLAY_SET *pgs;
Packit 5e46da
    PG_DISPLAY_SET *igs;
Packit 5e46da
    PG_DISPLAY_SET *tgs;  /* TextST */
Packit 5e46da
Packit 5e46da
    /* */
Packit 5e46da
    GRAPHICS_PROCESSOR *pgp;
Packit 5e46da
    GRAPHICS_PROCESSOR *igp;
Packit 5e46da
    GRAPHICS_PROCESSOR *tgp;  /* TextST */
Packit 5e46da
Packit 5e46da
    /* */
Packit 5e46da
    TEXTST_RENDER  *textst_render;
Packit 5e46da
    int             next_dialog_idx;
Packit 5e46da
    int             textst_user_style;
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * object lookup
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static BD_PG_OBJECT *_find_object(PG_DISPLAY_SET *s, unsigned object_id)
Packit 5e46da
{
Packit 5e46da
    unsigned ii;
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < s->num_object; ii++) {
Packit 5e46da
        if (s->object[ii].id == object_id) {
Packit 5e46da
            return &s->object[ii];
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static BD_PG_PALETTE *_find_palette(PG_DISPLAY_SET *s, unsigned palette_id)
Packit 5e46da
{
Packit 5e46da
    unsigned ii;
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < s->num_palette; ii++) {
Packit 5e46da
        if (s->palette[ii].id == palette_id) {
Packit 5e46da
            return &s->palette[ii];
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static BD_IG_BUTTON *_find_button_bog(BD_IG_BOG *bog, unsigned button_id)
Packit 5e46da
{
Packit 5e46da
    unsigned ii;
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < bog->num_buttons; ii++) {
Packit 5e46da
        if (bog->button[ii].id == button_id) {
Packit 5e46da
            return &bog->button[ii];
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static BD_IG_BUTTON *_find_button_page(BD_IG_PAGE *page, unsigned button_id, unsigned *bog_idx)
Packit 5e46da
{
Packit 5e46da
    unsigned ii;
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < page->num_bogs; ii++) {
Packit 5e46da
        BD_IG_BUTTON *button = _find_button_bog(&page->bog[ii], button_id);
Packit 5e46da
        if (button) {
Packit 5e46da
            if (bog_idx) {
Packit 5e46da
                *bog_idx = ii;
Packit 5e46da
            }
Packit 5e46da
            return button;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static BD_IG_PAGE *_find_page(BD_IG_INTERACTIVE_COMPOSITION *c, unsigned page_id)
Packit 5e46da
{
Packit 5e46da
    unsigned ii;
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < c->num_pages; ii++) {
Packit 5e46da
        if (c->page[ii].id == page_id) {
Packit 5e46da
            return &c->page[ii];
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
enum { BTN_NORMAL, BTN_SELECTED, BTN_ACTIVATED };
Packit 5e46da
Packit 5e46da
static BD_PG_OBJECT *_find_object_for_button(PG_DISPLAY_SET *s,
Packit 5e46da
                                             BD_IG_BUTTON *button, int state,
Packit 5e46da
                                             BOG_DATA *bog_data)
Packit 5e46da
{
Packit 5e46da
    BD_PG_OBJECT *object   = NULL;
Packit 5e46da
    unsigned object_id     = 0xffff;
Packit 5e46da
    unsigned object_id_end = 0xffff;
Packit 5e46da
    unsigned repeat        = 0;
Packit 5e46da
Packit 5e46da
    switch (state) {
Packit 5e46da
        case BTN_NORMAL:
Packit 5e46da
            object_id     = button->normal_start_object_id_ref;
Packit 5e46da
            object_id_end = button->normal_end_object_id_ref;
Packit 5e46da
            repeat        = button->normal_repeat_flag;
Packit 5e46da
            break;
Packit 5e46da
        case BTN_SELECTED:
Packit 5e46da
            object_id     = button->selected_start_object_id_ref;
Packit 5e46da
            object_id_end = button->selected_end_object_id_ref;
Packit 5e46da
            repeat        = button->selected_repeat_flag;
Packit 5e46da
            break;
Packit 5e46da
        case BTN_ACTIVATED:
Packit 5e46da
            object_id     = button->activated_start_object_id_ref;
Packit 5e46da
            object_id_end = button->activated_end_object_id_ref;
Packit 5e46da
            break;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (bog_data) {
Packit 5e46da
        bog_data->effect_running = 0;
Packit 5e46da
        if (bog_data->animate_indx >= 0) {
Packit 5e46da
            int range = object_id_end - object_id;
Packit 5e46da
Packit 5e46da
            if (range > 0 && object_id < 0xffff && object_id_end < 0xffff) {
Packit 5e46da
                GC_TRACE("animate button #%d: animate_indx %d, range %d, repeat %d\n",
Packit 5e46da
                         button->id, bog_data->animate_indx, range, repeat);
Packit 5e46da
Packit 5e46da
                object_id += bog_data->animate_indx % (range + 1);
Packit 5e46da
                bog_data->animate_indx++;
Packit 5e46da
                if (!repeat) {
Packit 5e46da
                    if (bog_data->animate_indx > range) {
Packit 5e46da
                        /* terminate animation to the last object */
Packit 5e46da
                        bog_data->animate_indx = -1;
Packit 5e46da
                    } else {
Packit 5e46da
                        bog_data->effect_running = 1;
Packit 5e46da
                    }
Packit 5e46da
                }
Packit 5e46da
            } else {
Packit 5e46da
                /* no animation for this button */
Packit 5e46da
                bog_data->animate_indx = -1;
Packit 5e46da
            }
Packit 5e46da
        } else {
Packit 5e46da
            if (object_id_end < 0xfffe) {
Packit 5e46da
                object_id = object_id_end;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    object = _find_object(s, object_id);
Packit 5e46da
Packit 5e46da
    return object;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static BD_TEXTST_REGION_STYLE *_find_region_style(BD_TEXTST_DIALOG_STYLE *p, unsigned region_style_id)
Packit 5e46da
{
Packit 5e46da
    unsigned ii;
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < p->region_style_count; ii++) {
Packit 5e46da
        if (p->region_style[ii].region_style_id == region_style_id) {
Packit 5e46da
            return &p->region_style[ii];
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return NULL;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * util
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _areas_overlap(BOG_DATA *a, BOG_DATA *b)
Packit 5e46da
{
Packit 5e46da
    return !(a->x + a->w <= b->x        ||
Packit 5e46da
             a->x        >= b->x + b->w ||
Packit 5e46da
             a->y + a->h <= b->y        ||
Packit 5e46da
             a->y        >= b->y + b->h);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _is_button_enabled(GRAPHICS_CONTROLLER *gc, BD_IG_PAGE *page, unsigned button_id)
Packit 5e46da
{
Packit 5e46da
    unsigned ii;
Packit 5e46da
    for (ii = 0; ii < page->num_bogs; ii++) {
Packit 5e46da
        if (gc->bog_data[ii].enabled_button == button_id) {
Packit 5e46da
            return 1;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static uint16_t _find_selected_button_id(GRAPHICS_CONTROLLER *gc)
Packit 5e46da
{
Packit 5e46da
    /* executed when playback condition changes (ex. new page, popup-on, ...) */
Packit 5e46da
    PG_DISPLAY_SET *s         = gc->igs;
Packit 5e46da
    BD_IG_PAGE     *page      = NULL;
Packit 5e46da
    unsigned        page_id   = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
Packit 5e46da
    unsigned        button_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
Packit 5e46da
    unsigned        ii;
Packit 5e46da
Packit 5e46da
    page = _find_page(&s->ics->interactive_composition, page_id);
Packit 5e46da
    if (!page) {
Packit 5e46da
        GC_TRACE("_find_selected_button_id(): unknown page #%d (have %d pages)\n",
Packit 5e46da
              page_id, s->ics->interactive_composition.num_pages);
Packit 5e46da
        return 0xffff;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* run 5.9.8.3 */
Packit 5e46da
Packit 5e46da
    /* 1) always use page->default_selected_button_id_ref if it is valid */
Packit 5e46da
    if (_find_button_page(page, page->default_selected_button_id_ref, NULL) &&
Packit 5e46da
        _is_button_enabled(gc, page, page->default_selected_button_id_ref)) {
Packit 5e46da
Packit 5e46da
        GC_TRACE("_find_selected_button_id() -> default #%d\n", page->default_selected_button_id_ref);
Packit 5e46da
        return page->default_selected_button_id_ref;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* 2) fallback to current PSR10 value if it is valid */
Packit 5e46da
    for (ii = 0; ii < page->num_bogs; ii++) {
Packit 5e46da
        BD_IG_BOG *bog = &page->bog[ii];
Packit 5e46da
        uint16_t   enabled_button = gc->bog_data[ii].enabled_button;
Packit 5e46da
Packit 5e46da
        if (button_id == enabled_button) {
Packit 5e46da
            if (_find_button_bog(bog, enabled_button)) {
Packit 5e46da
                GC_TRACE("_find_selected_button_id() -> PSR10 #%d\n", enabled_button);
Packit 5e46da
                return enabled_button;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* 3) fallback to find first valid_button_id_ref from page */
Packit 5e46da
    for (ii = 0; ii < page->num_bogs; ii++) {
Packit 5e46da
        BD_IG_BOG *bog = &page->bog[ii];
Packit 5e46da
        uint16_t   enabled_button = gc->bog_data[ii].enabled_button;
Packit 5e46da
Packit 5e46da
        if (_find_button_bog(bog, enabled_button)) {
Packit 5e46da
            GC_TRACE("_find_selected_button_id() -> first valid #%d\n", enabled_button);
Packit 5e46da
            return enabled_button;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    GC_TRACE("_find_selected_button_id(): not found -> 0xffff\n");
Packit 5e46da
    return 0xffff;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _reset_user_timeout(GRAPHICS_CONTROLLER *gc)
Packit 5e46da
{
Packit 5e46da
    gc->user_timeout = 0;
Packit 5e46da
Packit 5e46da
    if (gc->igs->ics->interactive_composition.ui_model == IG_UI_MODEL_POPUP ||
Packit 5e46da
        bd_psr_read(gc->regs, PSR_MENU_PAGE_ID) != 0) {
Packit 5e46da
Packit 5e46da
        gc->user_timeout = gc->igs->ics->interactive_composition.user_timeout_duration;
Packit 5e46da
        if (gc->user_timeout) {
Packit 5e46da
            gc->user_timeout += bd_get_scr();
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _save_page_state(GRAPHICS_CONTROLLER *gc)
Packit 5e46da
{
Packit 5e46da
    if (!gc->igs || !gc->igs->ics) {
Packit 5e46da
        GC_TRACE("_save_page_state(): no IG composition\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    PG_DISPLAY_SET *s       = gc->igs;
Packit 5e46da
    BD_IG_PAGE     *page    = NULL;
Packit 5e46da
    unsigned        page_id = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
Packit 5e46da
    unsigned        ii;
Packit 5e46da
Packit 5e46da
    page = _find_page(&s->ics->interactive_composition, page_id);
Packit 5e46da
    if (!page) {
Packit 5e46da
        GC_ERROR("_save_page_state(): unknown page #%d (have %d pages)\n",
Packit 5e46da
              page_id, s->ics->interactive_composition.num_pages);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* copy enabled button state, clear draw state */
Packit 5e46da
Packit 5e46da
    X_FREE(gc->saved_bog_data);
Packit 5e46da
    gc->saved_bog_data = calloc(1, sizeof(gc->bog_data));
Packit 5e46da
    if (!gc->saved_bog_data) {
Packit 5e46da
        GC_ERROR("_save_page_state(): out of memory\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < page->num_bogs; ii++) {
Packit 5e46da
        gc->saved_bog_data[ii].enabled_button = gc->bog_data[ii].enabled_button;
Packit 5e46da
        gc->saved_bog_data[ii].animate_indx   = gc->bog_data[ii].animate_indx >= 0 ? 0 : -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _restore_page_state(GRAPHICS_CONTROLLER *gc)
Packit 5e46da
{
Packit 5e46da
    gc->in_effects  = NULL;
Packit 5e46da
    gc->out_effects = NULL;
Packit 5e46da
Packit 5e46da
    if (gc->saved_bog_data) {
Packit 5e46da
        memcpy(gc->bog_data, gc->saved_bog_data, sizeof(gc->bog_data));
Packit 5e46da
        X_FREE(gc->saved_bog_data);
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _reset_page_state(GRAPHICS_CONTROLLER *gc)
Packit 5e46da
{
Packit 5e46da
    PG_DISPLAY_SET *s       = gc->igs;
Packit 5e46da
    BD_IG_PAGE     *page    = NULL;
Packit 5e46da
    unsigned        page_id = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
Packit 5e46da
    unsigned        ii;
Packit 5e46da
Packit 5e46da
    page = _find_page(&s->ics->interactive_composition, page_id);
Packit 5e46da
    if (!page) {
Packit 5e46da
        GC_ERROR("_reset_page_state(): unknown page #%d (have %d pages)\n",
Packit 5e46da
              page_id, s->ics->interactive_composition.num_pages);
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    memset(gc->bog_data, 0, sizeof(gc->bog_data));
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < page->num_bogs; ii++) {
Packit 5e46da
        gc->bog_data[ii].enabled_button = page->bog[ii].default_valid_button_id_ref;
Packit 5e46da
        gc->bog_data[ii].animate_indx   = 0;
Packit 5e46da
        gc->bog_data[ii].visible_object_id = -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* animation frame rate */
Packit 5e46da
    static const unsigned frame_interval[8] = {
Packit 5e46da
        0,
Packit 5e46da
        90000 / 1001 * 24,
Packit 5e46da
        90000 / 1000 * 24,
Packit 5e46da
        90000 / 1000 * 25,
Packit 5e46da
        90000 / 1001 * 30,
Packit 5e46da
        90000 / 1000 * 50,
Packit 5e46da
        90000 / 1001 * 60,
Packit 5e46da
    };
Packit 5e46da
    gc->frame_interval = frame_interval[s->ics->video_descriptor.frame_rate] * (page->animation_frame_rate_code + 1);
Packit 5e46da
Packit 5e46da
    /* effects */
Packit 5e46da
    gc->effect_idx  = 0;
Packit 5e46da
    gc->in_effects  = NULL;
Packit 5e46da
    gc->out_effects = NULL;
Packit 5e46da
Packit 5e46da
    /* timers */
Packit 5e46da
    _reset_user_timeout(gc);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * overlay operations
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#if defined __GNUC__
Packit 5e46da
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
static void _open_osd(GRAPHICS_CONTROLLER *gc, int plane,
Packit 5e46da
                      unsigned x0, unsigned y0,
Packit 5e46da
                      unsigned width, unsigned height)
Packit 5e46da
{
Packit 5e46da
    if (gc->overlay_proc) {
Packit 5e46da
        BD_OVERLAY ov = {0};
Packit 5e46da
        ov.cmd          = BD_OVERLAY_INIT;
Packit 5e46da
        ov.pts          = -1;
Packit 5e46da
        ov.plane        = plane;
Packit 5e46da
        ov.x            = x0;
Packit 5e46da
        ov.y            = y0;
Packit 5e46da
        ov.w            = width;
Packit 5e46da
        ov.h            = height;
Packit 5e46da
Packit 5e46da
        gc->overlay_proc(gc->overlay_proc_handle, &ov);
Packit 5e46da
Packit 5e46da
        if (plane == BD_OVERLAY_IG) {
Packit 5e46da
            gc->ig_open = 1;
Packit 5e46da
        } else {
Packit 5e46da
            gc->pg_open = 1;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _close_osd(GRAPHICS_CONTROLLER *gc, int plane)
Packit 5e46da
{
Packit 5e46da
    if (gc->overlay_proc) {
Packit 5e46da
        BD_OVERLAY ov = {0};
Packit 5e46da
        ov.cmd     = BD_OVERLAY_CLOSE;
Packit 5e46da
        ov.pts     = -1;
Packit 5e46da
        ov.plane   = plane;
Packit 5e46da
Packit 5e46da
        gc->overlay_proc(gc->overlay_proc_handle, &ov);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (plane == BD_OVERLAY_IG) {
Packit 5e46da
        gc->ig_open = 0;
Packit 5e46da
        gc->ig_drawn = 0;
Packit 5e46da
    } else {
Packit 5e46da
        gc->pg_open = 0;
Packit 5e46da
        gc->pg_drawn = 0;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _flush_osd(GRAPHICS_CONTROLLER *gc, int plane, int64_t pts)
Packit 5e46da
{
Packit 5e46da
    if (gc->overlay_proc) {
Packit 5e46da
        BD_OVERLAY ov = {0};
Packit 5e46da
        ov.cmd     = BD_OVERLAY_FLUSH;
Packit 5e46da
        ov.pts     = pts;
Packit 5e46da
        ov.plane   = plane;
Packit 5e46da
Packit 5e46da
        gc->overlay_proc(gc->overlay_proc_handle, &ov);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _hide_osd(GRAPHICS_CONTROLLER *gc, int plane)
Packit 5e46da
{
Packit 5e46da
    if (gc->overlay_proc) {
Packit 5e46da
        BD_OVERLAY ov = {0};
Packit 5e46da
        ov.cmd     = BD_OVERLAY_HIDE;
Packit 5e46da
        ov.plane   = plane;
Packit 5e46da
Packit 5e46da
        gc->overlay_proc(gc->overlay_proc_handle, &ov);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _clear_osd_area(GRAPHICS_CONTROLLER *gc, int plane, int64_t pts,
Packit 5e46da
                            uint16_t x, uint16_t y, uint16_t w, uint16_t h)
Packit 5e46da
{
Packit 5e46da
    if (gc->overlay_proc) {
Packit 5e46da
        /* wipe area */
Packit 5e46da
        BD_OVERLAY ov = {0};
Packit 5e46da
        ov.cmd     = BD_OVERLAY_WIPE;
Packit 5e46da
        ov.pts     = pts;
Packit 5e46da
        ov.plane   = plane;
Packit 5e46da
        ov.x       = x;
Packit 5e46da
        ov.y       = y;
Packit 5e46da
        ov.w       = w;
Packit 5e46da
        ov.h       = h;
Packit 5e46da
Packit 5e46da
        gc->overlay_proc(gc->overlay_proc_handle, &ov);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _clear_osd(GRAPHICS_CONTROLLER *gc, int plane)
Packit 5e46da
{
Packit 5e46da
    if (gc->overlay_proc) {
Packit 5e46da
        /* clear plane */
Packit 5e46da
        BD_OVERLAY ov = {0};
Packit 5e46da
        ov.cmd     = BD_OVERLAY_CLEAR;
Packit 5e46da
        ov.pts     = -1;
Packit 5e46da
        ov.plane   = plane;
Packit 5e46da
Packit 5e46da
        gc->overlay_proc(gc->overlay_proc_handle, &ov);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (plane == BD_OVERLAY_IG) {
Packit 5e46da
        gc->ig_drawn      = 0;
Packit 5e46da
    } else {
Packit 5e46da
        gc->pg_drawn      = 0;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _clear_bog_area(GRAPHICS_CONTROLLER *gc, BOG_DATA *bog_data)
Packit 5e46da
{
Packit 5e46da
    if (gc->ig_drawn && bog_data->w && bog_data->h) {
Packit 5e46da
Packit 5e46da
        _clear_osd_area(gc, BD_OVERLAY_IG, -1, bog_data->x, bog_data->y, bog_data->w, bog_data->h);
Packit 5e46da
Packit 5e46da
        bog_data->x = bog_data->y = bog_data->w = bog_data->h = 0;
Packit 5e46da
        bog_data->visible_object_id = -1;
Packit 5e46da
Packit 5e46da
        gc->ig_dirty = 1;
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _render_object(GRAPHICS_CONTROLLER *gc,
Packit 5e46da
                           int64_t pts, unsigned plane,
Packit 5e46da
                           uint16_t x, uint16_t y,
Packit 5e46da
                           BD_PG_OBJECT *object,
Packit 5e46da
                           BD_PG_PALETTE *palette)
Packit 5e46da
{
Packit 5e46da
    if (gc->overlay_proc) {
Packit 5e46da
        BD_OVERLAY ov = {0};
Packit 5e46da
        ov.cmd     = BD_OVERLAY_DRAW;
Packit 5e46da
        ov.pts     = pts;
Packit 5e46da
        ov.plane   = plane;
Packit 5e46da
        ov.x       = x;
Packit 5e46da
        ov.y       = y;
Packit 5e46da
        ov.w       = object->width;
Packit 5e46da
        ov.h       = object->height;
Packit 5e46da
        ov.palette = palette->entry;
Packit 5e46da
        ov.img     = object->img;
Packit 5e46da
Packit 5e46da
        gc->overlay_proc(gc->overlay_proc_handle, &ov);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _render_composition_object(GRAPHICS_CONTROLLER *gc,
Packit 5e46da
                                       int64_t pts, unsigned plane,
Packit 5e46da
                                       BD_PG_COMPOSITION_OBJECT *cobj,
Packit 5e46da
                                       BD_PG_OBJECT *object,
Packit 5e46da
                                       BD_PG_PALETTE *palette,
Packit 5e46da
                                       int palette_update_flag)
Packit 5e46da
{
Packit 5e46da
    if (gc->overlay_proc) {
Packit 5e46da
        BD_PG_RLE_ELEM *cropped_img = NULL;
Packit 5e46da
        BD_OVERLAY ov = {0};
Packit 5e46da
        ov.cmd     = BD_OVERLAY_DRAW;
Packit 5e46da
        ov.pts     = pts;
Packit 5e46da
        ov.plane   = plane;
Packit 5e46da
        ov.x       = cobj->x;
Packit 5e46da
        ov.y       = cobj->y;
Packit 5e46da
        ov.w       = object->width;
Packit 5e46da
        ov.h       = object->height;
Packit 5e46da
        ov.palette = palette->entry;
Packit 5e46da
        ov.img     = object->img;
Packit 5e46da
Packit 5e46da
        if (cobj->crop_flag) {
Packit 5e46da
            if (cobj->crop_x || cobj->crop_y || cobj->crop_w != object->width) {
Packit 5e46da
                cropped_img = rle_crop_object(object->img, object->width,
Packit 5e46da
                                              cobj->crop_x, cobj->crop_y, cobj->crop_w, cobj->crop_h);
Packit 5e46da
                if (!cropped_img) {
Packit 5e46da
                    BD_DEBUG(DBG_DECODE | DBG_CRIT, "Error cropping PG object\n");
Packit 5e46da
                    return;
Packit 5e46da
                }
Packit 5e46da
                ov.img = cropped_img;
Packit 5e46da
            }
Packit 5e46da
            ov.w  = cobj->crop_w;
Packit 5e46da
            ov.h  = cobj->crop_h;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        ov.palette_update_flag = palette_update_flag;
Packit 5e46da
Packit 5e46da
        gc->overlay_proc(gc->overlay_proc_handle, &ov);
Packit 5e46da
Packit 5e46da
        bd_refcnt_dec(cropped_img);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _render_rle(GRAPHICS_CONTROLLER *gc,
Packit 5e46da
                        int64_t pts,
Packit 5e46da
                        BD_PG_RLE_ELEM *img,
Packit 5e46da
                        uint16_t x, uint16_t y,
Packit 5e46da
                        uint16_t width, uint16_t height,
Packit 5e46da
                        BD_PG_PALETTE_ENTRY *palette)
Packit 5e46da
{
Packit 5e46da
    if (gc->overlay_proc) {
Packit 5e46da
        BD_OVERLAY ov = {0};
Packit 5e46da
        ov.cmd     = BD_OVERLAY_DRAW;
Packit 5e46da
        ov.pts     = pts;
Packit 5e46da
        ov.plane   = BD_OVERLAY_PG;
Packit 5e46da
        ov.x       = x;
Packit 5e46da
        ov.y       = y;
Packit 5e46da
        ov.w       = width;
Packit 5e46da
        ov.h       = height;
Packit 5e46da
        ov.palette = palette;
Packit 5e46da
        ov.img     = img;
Packit 5e46da
Packit 5e46da
        gc->overlay_proc(gc->overlay_proc_handle, &ov);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * page selection and IG effects
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _select_button(GRAPHICS_CONTROLLER *gc, uint32_t button_id)
Packit 5e46da
{
Packit 5e46da
    BD_IG_PAGE     *page       = NULL;
Packit 5e46da
    unsigned        page_id    = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
Packit 5e46da
    unsigned        bog_idx    = 0;
Packit 5e46da
Packit 5e46da
    /* reset animation */
Packit 5e46da
    page = _find_page(&gc->igs->ics->interactive_composition, page_id);
Packit 5e46da
    if (page && _find_button_page(page, button_id, &bog_idx)) {
Packit 5e46da
        gc->bog_data[bog_idx].animate_indx = 0;
Packit 5e46da
        gc->next_effect_time = bd_get_scr();
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* select page */
Packit 5e46da
    bd_psr_write(gc->regs, PSR_SELECTED_BUTTON_ID, button_id);
Packit 5e46da
    gc->auto_action_triggered = 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _select_page(GRAPHICS_CONTROLLER *gc, uint16_t page_id, int out_effects)
Packit 5e46da
{
Packit 5e46da
    unsigned cur_page_id = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
Packit 5e46da
    BD_IG_PAGE *page = NULL;
Packit 5e46da
Packit 5e46da
    bd_psr_write(gc->regs, PSR_MENU_PAGE_ID, page_id);
Packit 5e46da
Packit 5e46da
    _reset_page_state(gc);
Packit 5e46da
Packit 5e46da
    uint16_t button_id = _find_selected_button_id(gc);
Packit 5e46da
    _select_button(gc, button_id);
Packit 5e46da
Packit 5e46da
    gc->valid_mouse_position = 0;
Packit 5e46da
Packit 5e46da
    if (out_effects) {
Packit 5e46da
        page = _find_page(&gc->igs->ics->interactive_composition, cur_page_id);
Packit 5e46da
        if (page && page->out_effects.num_effects) {
Packit 5e46da
            gc->next_effect_time = bd_get_scr();
Packit 5e46da
            gc->out_effects = &page->out_effects;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    page = _find_page(&gc->igs->ics->interactive_composition, page_id);
Packit 5e46da
    if (page && page->in_effects.num_effects) {
Packit 5e46da
        gc->next_effect_time = bd_get_scr();
Packit 5e46da
        gc->in_effects = &page->in_effects;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (gc->ig_open && !gc->out_effects) {
Packit 5e46da
        _clear_osd(gc, BD_OVERLAY_IG);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _gc_reset(GRAPHICS_CONTROLLER *gc)
Packit 5e46da
{
Packit 5e46da
    if (gc->pg_open) {
Packit 5e46da
        _close_osd(gc, BD_OVERLAY_PG);
Packit 5e46da
    }
Packit 5e46da
    if (gc->ig_open) {
Packit 5e46da
        _close_osd(gc, BD_OVERLAY_IG);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    gc->popup_visible = 0;
Packit 5e46da
    gc->valid_mouse_position = 0;
Packit 5e46da
    gc->page_uo_mask = uo_mask_get_empty();
Packit 5e46da
Packit 5e46da
    graphics_processor_free(&gc->igp);
Packit 5e46da
    graphics_processor_free(&gc->pgp);
Packit 5e46da
    graphics_processor_free(&gc->tgp);
Packit 5e46da
Packit 5e46da
    pg_display_set_free(&gc->pgs);
Packit 5e46da
    pg_display_set_free(&gc->igs);
Packit 5e46da
    pg_display_set_free(&gc->tgs);
Packit 5e46da
Packit 5e46da
    textst_render_free(&gc->textst_render);
Packit 5e46da
    gc->next_dialog_idx = 0;
Packit 5e46da
    gc->textst_user_style = -1;
Packit 5e46da
Packit 5e46da
    memset(gc->bog_data, 0, sizeof(gc->bog_data));
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * register hook
Packit 5e46da
 */
Packit 5e46da
static void _process_psr_event(void *handle, BD_PSR_EVENT *ev)
Packit 5e46da
{
Packit 5e46da
    GRAPHICS_CONTROLLER *gc = (GRAPHICS_CONTROLLER *)handle;
Packit 5e46da
Packit 5e46da
    if (ev->ev_type == BD_PSR_SAVE) {
Packit 5e46da
        BD_DEBUG(DBG_GC, "PSR SAVE event\n");
Packit 5e46da
Packit 5e46da
        /* save menu page state */
Packit 5e46da
        bd_mutex_lock(&gc->mutex);
Packit 5e46da
        _save_page_state(gc);
Packit 5e46da
        bd_mutex_unlock(&gc->mutex);
Packit 5e46da
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (ev->ev_type == BD_PSR_RESTORE) {
Packit 5e46da
        switch (ev->psr_idx) {
Packit 5e46da
Packit 5e46da
            case PSR_SELECTED_BUTTON_ID:
Packit 5e46da
              return;
Packit 5e46da
Packit 5e46da
            case PSR_MENU_PAGE_ID:
Packit 5e46da
                /* restore menus */
Packit 5e46da
                bd_mutex_lock(&gc->mutex);
Packit 5e46da
                _restore_page_state(gc);
Packit 5e46da
                bd_mutex_unlock(&gc->mutex);
Packit 5e46da
                return;
Packit 5e46da
Packit 5e46da
            default:
Packit 5e46da
                /* others: ignore */
Packit 5e46da
                return;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * init / free
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
GRAPHICS_CONTROLLER *gc_init(BD_REGISTERS *regs, void *handle, gc_overlay_proc_f func)
Packit 5e46da
{
Packit 5e46da
    GRAPHICS_CONTROLLER *p = calloc(1, sizeof(*p));
Packit 5e46da
    if (!p) {
Packit 5e46da
        GC_ERROR("gc_init(): out of memory\n");
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    p->regs = regs;
Packit 5e46da
Packit 5e46da
    p->overlay_proc_handle = handle;
Packit 5e46da
    p->overlay_proc        = func;
Packit 5e46da
Packit 5e46da
    bd_mutex_init(&p->mutex);
Packit 5e46da
Packit 5e46da
    bd_psr_register_cb(regs, _process_psr_event, p);
Packit 5e46da
Packit 5e46da
    p->textst_user_style = -1;
Packit 5e46da
Packit 5e46da
    return p;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void gc_free(GRAPHICS_CONTROLLER **p)
Packit 5e46da
{
Packit 5e46da
    if (p && *p) {
Packit 5e46da
Packit 5e46da
        GRAPHICS_CONTROLLER *gc = *p;
Packit 5e46da
Packit 5e46da
        bd_psr_unregister_cb(gc->regs, _process_psr_event, gc);
Packit 5e46da
Packit 5e46da
        _gc_reset(gc);
Packit 5e46da
Packit 5e46da
        if (gc->overlay_proc) {
Packit 5e46da
            gc->overlay_proc(gc->overlay_proc_handle, NULL);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        bd_mutex_destroy(&gc->mutex);
Packit 5e46da
Packit 5e46da
        X_FREE(gc->saved_bog_data);
Packit 5e46da
Packit 5e46da
        X_FREE(*p);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * graphics stream input
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
int gc_decode_ts(GRAPHICS_CONTROLLER *gc, uint16_t pid, uint8_t *block, unsigned num_blocks, int64_t stc)
Packit 5e46da
{
Packit 5e46da
    if (!gc) {
Packit 5e46da
        GC_TRACE("gc_decode_ts(): no graphics controller\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (IS_HDMV_PID_IG(pid)) {
Packit 5e46da
        /* IG stream */
Packit 5e46da
Packit 5e46da
        if (!gc->igp) {
Packit 5e46da
            gc->igp = graphics_processor_init();
Packit 5e46da
            if (!gc->igp) {
Packit 5e46da
                return -1;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        bd_mutex_lock(&gc->mutex);
Packit 5e46da
Packit 5e46da
        if (!graphics_processor_decode_ts(gc->igp, &gc->igs,
Packit 5e46da
                                          pid, block, num_blocks,
Packit 5e46da
                                          stc)) {
Packit 5e46da
            /* no new complete display set */
Packit 5e46da
            bd_mutex_unlock(&gc->mutex);
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (!gc->igs || !gc->igs->complete) {
Packit 5e46da
            bd_mutex_unlock(&gc->mutex);
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* TODO: */
Packit 5e46da
        if (gc->igs->ics) {
Packit 5e46da
            if (gc->igs->ics->interactive_composition.composition_timeout_pts > 0) {
Packit 5e46da
                GC_TRACE("gc_decode_ts(): IG composition_timeout_pts not implemented\n");
Packit 5e46da
            }
Packit 5e46da
            if (gc->igs->ics->interactive_composition.selection_timeout_pts) {
Packit 5e46da
                GC_TRACE("gc_decode_ts(): IG selection_timeout_pts not implemented\n");
Packit 5e46da
            }
Packit 5e46da
            if (gc->igs->ics->interactive_composition.user_timeout_duration) {
Packit 5e46da
                GC_TRACE("gc_decode_ts(): IG user_timeout_duration %d\n",
Packit 5e46da
                         gc->igs->ics->interactive_composition.user_timeout_duration);
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        bd_mutex_unlock(&gc->mutex);
Packit 5e46da
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    else if (IS_HDMV_PID_PG(pid)) {
Packit 5e46da
        /* PG stream */
Packit 5e46da
        if (!gc->pgp) {
Packit 5e46da
            gc->pgp = graphics_processor_init();
Packit 5e46da
            if (!gc->pgp) {
Packit 5e46da
                return -1;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
        graphics_processor_decode_ts(gc->pgp, &gc->pgs,
Packit 5e46da
                                     pid, block, num_blocks,
Packit 5e46da
                                     stc);
Packit 5e46da
Packit 5e46da
        if (!gc->pgs || !gc->pgs->complete) {
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    else if (IS_HDMV_PID_TEXTST(pid)) {
Packit 5e46da
        /* TextST stream */
Packit 5e46da
        if (!gc->tgp) {
Packit 5e46da
            gc->tgp = graphics_processor_init();
Packit 5e46da
            if (!gc->tgp) {
Packit 5e46da
                return -1;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
        graphics_processor_decode_ts(gc->tgp, &gc->tgs,
Packit 5e46da
                                     pid, block, num_blocks,
Packit 5e46da
                                     stc);
Packit 5e46da
Packit 5e46da
        if (!gc->tgs || !gc->tgs->complete) {
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * TextST rendering
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _textst_style_select(GRAPHICS_CONTROLLER *p, int user_style_idx)
Packit 5e46da
{
Packit 5e46da
    p->textst_user_style = user_style_idx;
Packit 5e46da
Packit 5e46da
    GC_ERROR("User style selection not implemented\n");
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int gc_add_font(GRAPHICS_CONTROLLER *p, void *data, size_t size)
Packit 5e46da
{
Packit 5e46da
    if (!p) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!data) {
Packit 5e46da
        textst_render_free(&p->textst_render);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!p->textst_render) {
Packit 5e46da
        p->textst_render = textst_render_init();
Packit 5e46da
        if (!p->textst_render) {
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return textst_render_add_font(p->textst_render, data, size);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _render_textst_region(GRAPHICS_CONTROLLER *p, int64_t pts, BD_TEXTST_REGION_STYLE *style, TEXTST_BITMAP *bmp,
Packit 5e46da
                                 BD_PG_PALETTE_ENTRY *palette)
Packit 5e46da
{
Packit 5e46da
    unsigned bmp_y;
Packit 5e46da
    uint16_t y;
Packit 5e46da
    RLE_ENC  rle;
Packit 5e46da
Packit 5e46da
    if (rle_begin(&rle) < 0) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    for (y = 0, bmp_y = 0; y < style->region_info.region.height; y++) {
Packit 5e46da
        if (y < style->text_box.ypos || y >= style->text_box.ypos + style->text_box.height) {
Packit 5e46da
            if (rle_add_bite(&rle, style->region_info.background_color, style->region_info.region.width) < 0)
Packit 5e46da
                break;
Packit 5e46da
        } else {
Packit 5e46da
            if (rle_add_bite(&rle, style->region_info.background_color, style->text_box.xpos) < 0)
Packit 5e46da
                break;
Packit 5e46da
            if (rle_compress_chunk(&rle, bmp->mem + bmp->stride * bmp_y, bmp->width) < 0)
Packit 5e46da
                break;
Packit 5e46da
            bmp_y++;
Packit 5e46da
            if (rle_add_bite(&rle, style->region_info.background_color,
Packit 5e46da
                             style->region_info.region.width - style->text_box.width - style->text_box.xpos) < 0)
Packit 5e46da
                break;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (rle_add_eol(&rle) < 0)
Packit 5e46da
            break;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_PG_RLE_ELEM *img = rle_get(&rle;;
Packit 5e46da
    if (img) {
Packit 5e46da
        _render_rle(p, pts, img,
Packit 5e46da
                    style->region_info.region.xpos, style->region_info.region.ypos,
Packit 5e46da
                    style->region_info.region.width, style->region_info.region.height,
Packit 5e46da
                    palette);
Packit 5e46da
    } else {
Packit 5e46da
        BD_DEBUG(DBG_DECODE | DBG_CRIT, "Error encoding Text Subtitle region\n");
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    rle_end(&rle;;
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _render_textst(GRAPHICS_CONTROLLER *p, uint32_t stc, GC_NAV_CMDS *cmds)
Packit 5e46da
{
Packit 5e46da
    BD_TEXTST_DIALOG_PRESENTATION *dialog = NULL;
Packit 5e46da
    PG_DISPLAY_SET *s   = p->tgs;
Packit 5e46da
    int64_t         now = ((int64_t)stc) << 1;
Packit 5e46da
    unsigned ii, jj;
Packit 5e46da
Packit 5e46da
    if (!s || !s->dialog || !s->style) {
Packit 5e46da
        GC_ERROR("_render_textst(): no TextST decoded\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
    if (!p->textst_render) {
Packit 5e46da
        GC_ERROR("_render_textst(): no TextST renderer (missing fonts ?)\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    dialog = s->dialog;
Packit 5e46da
Packit 5e46da
    /* loop over all matching dialogs */
Packit 5e46da
    for (ii = p->next_dialog_idx; ii < s->num_dialog; ii++) {
Packit 5e46da
Packit 5e46da
        /* next dialog too far in future ? */
Packit 5e46da
        if (now < 1 || dialog[ii].start_pts >= now + 90000) {
Packit 5e46da
            GC_TRACE("_render_textst(): next event #%d in %"PRId64" seconds (pts %"PRId64")\n",
Packit 5e46da
                     ii, (dialog[ii].start_pts - now)/90000, dialog[ii].start_pts);
Packit 5e46da
            if (cmds) {
Packit 5e46da
                cmds->wakeup_time = (uint32_t)(dialog[ii].start_pts / 2);
Packit 5e46da
            }
Packit 5e46da
            return 1;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        p->next_dialog_idx = ii + 1;
Packit 5e46da
Packit 5e46da
        /* too late ? */
Packit 5e46da
        if (dialog[ii].start_pts < now - 45000) {
Packit 5e46da
            GC_TRACE("_render_textst(): not showing #%d (start time passed)\n",ii);
Packit 5e46da
            continue;
Packit 5e46da
        }
Packit 5e46da
        if (dialog[ii].end_pts < now) {
Packit 5e46da
            GC_TRACE("_render_textst(): not showing #%d (hide time passed)\n",ii);
Packit 5e46da
            continue;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (dialog[ii].palette_update) {
Packit 5e46da
            GC_ERROR("_render_textst(): Palette update not implemented\n");
Packit 5e46da
            continue;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        GC_TRACE("_render_textst(): rendering dialog #%d (pts %"PRId64", diff %"PRId64"\n",
Packit 5e46da
                 ii, dialog[ii].start_pts, dialog[ii].start_pts - now);
Packit 5e46da
Packit 5e46da
Packit 5e46da
        if (!dialog[ii].region_count) {
Packit 5e46da
            continue;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* TODO: */
Packit 5e46da
        if (dialog[ii].region_count > 1) {
Packit 5e46da
            GC_ERROR("_render_textst(): Multiple regions not supported\n");
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* open PG overlay */
Packit 5e46da
        if (!p->pg_open) {
Packit 5e46da
            _open_osd(p, BD_OVERLAY_PG, 0, 0, 1920, 1080);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* render all regions */
Packit 5e46da
        for (jj = 0; jj < dialog[ii].region_count; jj++) {
Packit 5e46da
Packit 5e46da
            BD_TEXTST_DIALOG_REGION *region = &dialog[ii].region[jj];
Packit 5e46da
            BD_TEXTST_REGION_STYLE  *style = NULL;
Packit 5e46da
Packit 5e46da
            // TODO:
Packit 5e46da
            if (region->continous_present_flag) {
Packit 5e46da
                GC_ERROR("_render_textst(): continous_present_flag: not implemented\n");
Packit 5e46da
            }
Packit 5e46da
            if (region->forced_on_flag) {
Packit 5e46da
                GC_ERROR("_render_textst(): forced_on_flag: not implemented\n");
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            style = _find_region_style(s->style, region->region_style_id_ref);
Packit 5e46da
            if (!style) {
Packit 5e46da
                GC_ERROR("_render_textst: region style #%d not found\n", region->region_style_id_ref);
Packit 5e46da
                continue;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            TEXTST_BITMAP bmp = {NULL, style->text_box.width, style->text_box.height, style->text_box.width, 0};
Packit 5e46da
            bmp.mem = malloc((size_t)bmp.width * bmp.height);
Packit 5e46da
            if (bmp.mem) {
Packit 5e46da
                memset(bmp.mem, style->region_info.background_color, (size_t)bmp.width * bmp.height);
Packit 5e46da
Packit 5e46da
                textst_render(p->textst_render, &bmp, style, region);
Packit 5e46da
Packit 5e46da
                _render_textst_region(p, dialog[ii].start_pts, style, &bmp, s->style->palette);
Packit 5e46da
Packit 5e46da
                X_FREE(bmp.mem);
Packit 5e46da
            } else {
Packit 5e46da
                GC_ERROR("_render_textst(): out of memory\n");
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* commit changes */
Packit 5e46da
        _flush_osd(p, BD_OVERLAY_PG, dialog[ii].start_pts);
Packit 5e46da
Packit 5e46da
        /* detect overlapping dialogs (not allowed) */
Packit 5e46da
        if (ii < s->num_dialog - 1) {
Packit 5e46da
            if (dialog[ii + 1].start_pts < dialog[ii].end_pts) {
Packit 5e46da
                GC_ERROR("_render_textst: overlapping dialogs detected\n");
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* push hide events */
Packit 5e46da
        for (jj = 0; jj < dialog[ii].region_count; jj++) {
Packit 5e46da
          BD_TEXTST_DIALOG_REGION *region = &dialog[ii].region[jj];
Packit 5e46da
          BD_TEXTST_REGION_STYLE  *style = NULL;
Packit 5e46da
Packit 5e46da
          style = _find_region_style(s->style, region->region_style_id_ref);
Packit 5e46da
          if (!style) {
Packit 5e46da
            continue;
Packit 5e46da
          }
Packit 5e46da
          _clear_osd_area(p, BD_OVERLAY_PG, dialog[ii].end_pts,
Packit 5e46da
                          style->region_info.region.xpos, style->region_info.region.ypos,
Packit 5e46da
                          style->region_info.region.width, style->region_info.region.height);
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        _hide_osd(p, BD_OVERLAY_PG);
Packit 5e46da
Packit 5e46da
        /* commit changes */
Packit 5e46da
        _flush_osd(p, BD_OVERLAY_PG, dialog[ii].end_pts);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * PG rendering
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _render_pg_composition_object(GRAPHICS_CONTROLLER *gc,
Packit 5e46da
                                         int64_t pts,
Packit 5e46da
                                         BD_PG_COMPOSITION_OBJECT *cobj,
Packit 5e46da
                                         BD_PG_PALETTE *palette)
Packit 5e46da
{
Packit 5e46da
    BD_PG_COMPOSITION *pcs     = gc->pgs->pcs;
Packit 5e46da
    BD_PG_OBJECT      *object  = NULL;
Packit 5e46da
Packit 5e46da
    /* lookup object */
Packit 5e46da
    object  = _find_object(gc->pgs, cobj->object_id_ref);
Packit 5e46da
    if (!object) {
Packit 5e46da
        GC_ERROR("_render_pg_composition_object: object #%d not found\n", cobj->object_id_ref);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* open PG overlay */
Packit 5e46da
    if (!gc->pg_open) {
Packit 5e46da
        _open_osd(gc, BD_OVERLAY_PG, 0, 0, pcs->video_descriptor.video_width, pcs->video_descriptor.video_height);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* render object using composition parameters */
Packit 5e46da
    _render_composition_object(gc, pts, BD_OVERLAY_PG, cobj, object, palette, pcs->palette_update_flag);
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _render_pg(GRAPHICS_CONTROLLER *gc)
Packit 5e46da
{
Packit 5e46da
    PG_DISPLAY_SET    *s       = gc->pgs;
Packit 5e46da
    BD_PG_COMPOSITION *pcs     = NULL;
Packit 5e46da
    BD_PG_PALETTE     *palette = NULL;
Packit 5e46da
    unsigned           display_flag;
Packit 5e46da
    unsigned           ii;
Packit 5e46da
Packit 5e46da
    if (!s || !s->pcs || !s->complete) {
Packit 5e46da
        GC_ERROR("_render_pg(): no composition\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
    pcs = s->pcs;
Packit 5e46da
Packit 5e46da
    /* mark PG display set handled */
Packit 5e46da
    gc->pgs->complete = 0;
Packit 5e46da
Packit 5e46da
    /* lookup palette */
Packit 5e46da
    palette = _find_palette(gc->pgs, pcs->palette_id_ref);
Packit 5e46da
    if (!palette) {
Packit 5e46da
        GC_ERROR("_render_pg(): unknown palette id %d (have %d palettes)\n",
Packit 5e46da
                 pcs->palette_id_ref, s->num_palette);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    display_flag = bd_psr_read(gc->regs, PSR_PG_STREAM) >> 31;
Packit 5e46da
Packit 5e46da
    /* render objects */
Packit 5e46da
    for (ii = 0; ii < pcs->num_composition_objects; ii++) {
Packit 5e46da
        BD_PG_COMPOSITION_OBJECT *cobj = &pcs->composition_object[ii];
Packit 5e46da
        if (cobj->forced_on_flag) {
Packit 5e46da
            GC_ERROR("_render_pg(): forced_on_flag not implemented\n");
Packit 5e46da
        }
Packit 5e46da
        if (cobj->forced_on_flag || display_flag) {
Packit 5e46da
            _render_pg_composition_object(gc, pcs->pts, cobj, palette);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!gc->pg_open) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* commit changes at given pts */
Packit 5e46da
    _flush_osd(gc, BD_OVERLAY_PG, pcs->pts);
Packit 5e46da
Packit 5e46da
    /* clear plane but do not commit changes yet */
Packit 5e46da
    /* -> plane will be cleared and hidden when empty composition arrives */
Packit 5e46da
    /* (-> no need to store object regions for next update / clear event - or use expensive full plane clear) */
Packit 5e46da
    for (ii = 0; ii < pcs->num_composition_objects; ii++) {
Packit 5e46da
        BD_PG_COMPOSITION_OBJECT *cobj   = &pcs->composition_object[ii];
Packit 5e46da
        BD_PG_OBJECT             *object = _find_object(gc->pgs, cobj->object_id_ref);
Packit 5e46da
Packit 5e46da
        if (object) {
Packit 5e46da
            _clear_osd_area(gc, BD_OVERLAY_PG, -1,
Packit 5e46da
                            cobj->x, cobj->y, object->width, object->height);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
    _hide_osd(gc, BD_OVERLAY_PG);
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _reset_pg(GRAPHICS_CONTROLLER *gc)
Packit 5e46da
{
Packit 5e46da
    graphics_processor_free(&gc->pgp);
Packit 5e46da
Packit 5e46da
    pg_display_set_free(&gc->pgs);
Packit 5e46da
Packit 5e46da
    if (gc->pg_open) {
Packit 5e46da
        _close_osd(gc, BD_OVERLAY_PG);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    gc->next_dialog_idx = 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * IG rendering
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _render_button(GRAPHICS_CONTROLLER *gc, BD_IG_BUTTON *button, BD_PG_PALETTE *palette,
Packit 5e46da
                           int state, BOG_DATA *bog_data)
Packit 5e46da
{
Packit 5e46da
    BD_PG_OBJECT *object = _find_object_for_button(gc->igs, button, state, bog_data);
Packit 5e46da
    if (!object) {
Packit 5e46da
        GC_TRACE("_render_button(#%d): object (state %d) not found\n", button->id, state);
Packit 5e46da
Packit 5e46da
        _clear_bog_area(gc, bog_data);
Packit 5e46da
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* object already rendered ? */
Packit 5e46da
    if (bog_data->visible_object_id == object->id &&
Packit 5e46da
        bog_data->x == button->x_pos && bog_data->y == button->y_pos &&
Packit 5e46da
        bog_data->w == object->width && bog_data->h == object->height) {
Packit 5e46da
Packit 5e46da
        GC_TRACE("skipping already rendered button #%d (object #%d at %d,%d %dx%d)\n",
Packit 5e46da
                 button->id, object->id,
Packit 5e46da
                 button->x_pos, button->y_pos, object->width, object->height);
Packit 5e46da
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* new object is smaller than already drawn one, or in different position ? -> need to render background */
Packit 5e46da
    if (bog_data->w > object->width ||
Packit 5e46da
        bog_data->h > object->height ||
Packit 5e46da
        bog_data->x != button->x_pos ||
Packit 5e46da
        bog_data->y != button->y_pos) {
Packit 5e46da
Packit 5e46da
        /* make sure we won't wipe other buttons */
Packit 5e46da
        unsigned ii, skip = 0;
Packit 5e46da
        for (ii = 0; &gc->bog_data[ii] != bog_data; ii++) {
Packit 5e46da
            if (_areas_overlap(bog_data, &gc->bog_data[ii]))
Packit 5e46da
                skip = 1;
Packit 5e46da
            /* FIXME: clean non-overlapping area */
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        GC_TRACE("object size changed, %sclearing background at %d,%d %dx%d\n",
Packit 5e46da
                 skip ? " ** NOT ** " : "",
Packit 5e46da
                 bog_data->x, bog_data->y, bog_data->w, bog_data->h);
Packit 5e46da
Packit 5e46da
        if (!skip) {
Packit 5e46da
            _clear_bog_area(gc, bog_data);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    GC_TRACE("render button #%d using object #%d at %d,%d %dx%d\n",
Packit 5e46da
             button->id, object->id,
Packit 5e46da
             button->x_pos, button->y_pos, object->width, object->height);
Packit 5e46da
Packit 5e46da
    _render_object(gc, -1, BD_OVERLAY_IG,
Packit 5e46da
                   button->x_pos, button->y_pos,
Packit 5e46da
                   object, palette);
Packit 5e46da
Packit 5e46da
    bog_data->x = button->x_pos;
Packit 5e46da
    bog_data->y = button->y_pos;
Packit 5e46da
    bog_data->w = object->width;
Packit 5e46da
    bog_data->h = object->height;
Packit 5e46da
    bog_data->visible_object_id = object->id;
Packit 5e46da
Packit 5e46da
    gc->ig_drawn = 1;
Packit 5e46da
    gc->ig_dirty = 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _render_ig_composition_object(GRAPHICS_CONTROLLER *gc,
Packit 5e46da
                                         int64_t pts,
Packit 5e46da
                                         BD_PG_COMPOSITION_OBJECT *cobj,
Packit 5e46da
                                         BD_PG_PALETTE *palette)
Packit 5e46da
{
Packit 5e46da
    BD_PG_OBJECT   *object  = NULL;
Packit 5e46da
Packit 5e46da
    /* lookup object */
Packit 5e46da
    object  = _find_object(gc->igs, cobj->object_id_ref);
Packit 5e46da
    if (!object) {
Packit 5e46da
        GC_ERROR("_render_ig_composition_object: object #%d not found\n", cobj->object_id_ref);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* render object using composition parameters */
Packit 5e46da
    _render_composition_object(gc, pts, BD_OVERLAY_IG, cobj, object, palette, 0);
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _render_effect(GRAPHICS_CONTROLLER *gc, BD_IG_EFFECT *effect)
Packit 5e46da
{
Packit 5e46da
    BD_PG_PALETTE *palette = NULL;
Packit 5e46da
    unsigned ii;
Packit 5e46da
    int64_t pts = -1;
Packit 5e46da
Packit 5e46da
    if (!gc->ig_open) {
Packit 5e46da
        _open_osd(gc, BD_OVERLAY_IG, 0, 0,
Packit 5e46da
                  gc->igs->ics->video_descriptor.video_width,
Packit 5e46da
                  gc->igs->ics->video_descriptor.video_height);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _clear_osd(gc, BD_OVERLAY_IG);
Packit 5e46da
Packit 5e46da
    palette = _find_palette(gc->igs, effect->palette_id_ref);
Packit 5e46da
    if (!palette) {
Packit 5e46da
        GC_ERROR("_render_effect: palette #%d not found\n", effect->palette_id_ref);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < effect->num_composition_objects; ii++) {
Packit 5e46da
        _render_ig_composition_object(gc, pts, &effect->composition_object[ii], palette);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _flush_osd(gc, BD_OVERLAY_IG, pts);
Packit 5e46da
Packit 5e46da
    _reset_user_timeout(gc);
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _render_page(GRAPHICS_CONTROLLER *gc,
Packit 5e46da
                         unsigned activated_button_id,
Packit 5e46da
                         GC_NAV_CMDS *cmds)
Packit 5e46da
{
Packit 5e46da
    PG_DISPLAY_SET *s       = gc->igs;
Packit 5e46da
    BD_IG_PAGE     *page    = NULL;
Packit 5e46da
    BD_PG_PALETTE  *palette = NULL;
Packit 5e46da
    unsigned        page_id = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
Packit 5e46da
    unsigned        ii;
Packit 5e46da
    unsigned        selected_button_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
Packit 5e46da
    BD_IG_BUTTON   *auto_activate_button = NULL;
Packit 5e46da
Packit 5e46da
    gc->button_effect_running = 0;
Packit 5e46da
    gc->button_animation_running = 0;
Packit 5e46da
Packit 5e46da
    if (s->ics->interactive_composition.ui_model == IG_UI_MODEL_POPUP && !gc->popup_visible) {
Packit 5e46da
Packit 5e46da
        gc->page_uo_mask = uo_mask_get_empty();
Packit 5e46da
Packit 5e46da
        if (gc->ig_open) {
Packit 5e46da
            GC_TRACE("_render_page(): popup menu not visible\n");
Packit 5e46da
            _close_osd(gc, BD_OVERLAY_IG);
Packit 5e46da
            return 1;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* running page effects ? */
Packit 5e46da
    if (gc->out_effects) {
Packit 5e46da
        if (gc->effect_idx < gc->out_effects->num_effects) {
Packit 5e46da
            _render_effect(gc, &gc->out_effects->effect[gc->effect_idx]);
Packit 5e46da
            return 1;
Packit 5e46da
        }
Packit 5e46da
        gc->out_effects = NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    page = _find_page(&s->ics->interactive_composition, page_id);
Packit 5e46da
    if (!page) {
Packit 5e46da
        GC_ERROR("_render_page: unknown page id %d (have %d pages)\n",
Packit 5e46da
              page_id, s->ics->interactive_composition.num_pages);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    gc->page_uo_mask = page->uo_mask_table;
Packit 5e46da
Packit 5e46da
    if (gc->in_effects) {
Packit 5e46da
        if (gc->effect_idx < gc->in_effects->num_effects) {
Packit 5e46da
            _render_effect(gc, &gc->in_effects->effect[gc->effect_idx]);
Packit 5e46da
            return 1;
Packit 5e46da
        }
Packit 5e46da
        gc->in_effects = NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    palette = _find_palette(s, page->palette_id_ref);
Packit 5e46da
    if (!palette) {
Packit 5e46da
        GC_ERROR("_render_page: unknown palette id %d (have %d palettes)\n",
Packit 5e46da
              page->palette_id_ref, s->num_palette);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    GC_TRACE("rendering page #%d using palette #%d. page has %d bogs\n",
Packit 5e46da
          page->id, page->palette_id_ref, page->num_bogs);
Packit 5e46da
Packit 5e46da
    if (!gc->ig_open) {
Packit 5e46da
        _open_osd(gc, BD_OVERLAY_IG, 0, 0,
Packit 5e46da
                  s->ics->video_descriptor.video_width,
Packit 5e46da
                  s->ics->video_descriptor.video_height);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < page->num_bogs; ii++) {
Packit 5e46da
        BD_IG_BOG    *bog      = &page->bog[ii];
Packit 5e46da
        unsigned      valid_id = gc->bog_data[ii].enabled_button;
Packit 5e46da
        BD_IG_BUTTON *button;
Packit 5e46da
Packit 5e46da
        button = _find_button_bog(bog, valid_id);
Packit 5e46da
Packit 5e46da
        if (!button) {
Packit 5e46da
            GC_TRACE("_render_page(): bog %d: button %d not found\n", ii, valid_id);
Packit 5e46da
Packit 5e46da
            // render background
Packit 5e46da
            _clear_bog_area(gc, &gc->bog_data[ii]);
Packit 5e46da
Packit 5e46da
        } else if (button->id == activated_button_id) {
Packit 5e46da
            GC_TRACE("    button #%d activated\n", button->id);
Packit 5e46da
Packit 5e46da
            _render_button(gc, button, palette, BTN_ACTIVATED, &gc->bog_data[ii]);
Packit 5e46da
Packit 5e46da
        } else if (button->id == selected_button_id) {
Packit 5e46da
Packit 5e46da
            if (button->auto_action_flag && !gc->auto_action_triggered) {
Packit 5e46da
                if (cmds) {
Packit 5e46da
                    if (!auto_activate_button) {
Packit 5e46da
                        auto_activate_button = button;
Packit 5e46da
                    }
Packit 5e46da
                } else {
Packit 5e46da
                    GC_ERROR("   auto-activate #%d not triggered (!cmds)\n", button->id);
Packit 5e46da
                }
Packit 5e46da
Packit 5e46da
                _render_button(gc, button, palette, BTN_ACTIVATED, &gc->bog_data[ii]);
Packit 5e46da
Packit 5e46da
            } else {
Packit 5e46da
                _render_button(gc, button, palette, BTN_SELECTED, &gc->bog_data[ii]);
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
        } else {
Packit 5e46da
            _render_button(gc, button, palette, BTN_NORMAL, &gc->bog_data[ii]);
Packit 5e46da
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        gc->button_effect_running    += gc->bog_data[ii].effect_running;
Packit 5e46da
        gc->button_animation_running += (gc->bog_data[ii].animate_indx >= 0);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* process auto-activate */
Packit 5e46da
    if (auto_activate_button) {
Packit 5e46da
        GC_TRACE("   auto-activate #%d\n", auto_activate_button->id);
Packit 5e46da
Packit 5e46da
        /* do not trigger auto action before single-loop animations have been terminated */
Packit 5e46da
        if (gc->button_effect_running) {
Packit 5e46da
            GC_TRACE("   auto-activate #%d not triggered (ANIMATING)\n", auto_activate_button->id);
Packit 5e46da
        } else if (cmds) {
Packit 5e46da
            cmds->num_nav_cmds = auto_activate_button->num_nav_cmds;
Packit 5e46da
            cmds->nav_cmds     = auto_activate_button->nav_cmds;
Packit 5e46da
Packit 5e46da
            gc->auto_action_triggered = 1;
Packit 5e46da
        } else {
Packit 5e46da
            GC_ERROR("_render_page(): auto-activate ignored (missing result buffer)\n");
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (gc->ig_dirty) {
Packit 5e46da
        _flush_osd(gc, BD_OVERLAY_IG, -1);
Packit 5e46da
        gc->ig_dirty = 0;
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * user actions
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#define VK_IS_NUMERIC(vk) (/*vk >= BD_VK_0  &&*/ vk <= BD_VK_9)
Packit 5e46da
#define VK_IS_CURSOR(vk)  (vk >= BD_VK_UP && vk <= BD_VK_RIGHT)
Packit 5e46da
#define VK_TO_NUMBER(vk)  ((vk) - BD_VK_0)
Packit 5e46da
Packit 5e46da
static int _user_input(GRAPHICS_CONTROLLER *gc, uint32_t key, GC_NAV_CMDS *cmds)
Packit 5e46da
{
Packit 5e46da
    PG_DISPLAY_SET *s          = gc->igs;
Packit 5e46da
    BD_IG_PAGE     *page       = NULL;
Packit 5e46da
    unsigned        page_id    = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
Packit 5e46da
    unsigned        cur_btn_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
Packit 5e46da
    unsigned        new_btn_id = cur_btn_id;
Packit 5e46da
    unsigned        ii;
Packit 5e46da
    int             activated_btn_id = -1;
Packit 5e46da
Packit 5e46da
    if (s->ics->interactive_composition.ui_model == IG_UI_MODEL_POPUP && !gc->popup_visible) {
Packit 5e46da
        GC_TRACE("_user_input(): popup menu not visible\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
    if (!gc->ig_open) {
Packit 5e46da
        GC_ERROR("_user_input(): menu not open\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!gc->ig_drawn) {
Packit 5e46da
        GC_ERROR("_user_input(): menu not visible\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _reset_user_timeout(gc);
Packit 5e46da
Packit 5e46da
    if (gc->button_effect_running) {
Packit 5e46da
        GC_ERROR("_user_input(): button_effect_running\n");
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    GC_TRACE("_user_input(%d)\n", key);
Packit 5e46da
Packit 5e46da
    page = _find_page(&s->ics->interactive_composition, page_id);
Packit 5e46da
    if (!page) {
Packit 5e46da
        GC_ERROR("_user_input(): unknown page id %d (have %d pages)\n",
Packit 5e46da
              page_id, s->ics->interactive_composition.num_pages);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (key == BD_VK_MOUSE_ACTIVATE) {
Packit 5e46da
        if (!gc->valid_mouse_position) {
Packit 5e46da
            GC_TRACE("_user_input(): BD_VK_MOUSE_ACTIVATE outside of valid buttons\n");
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
        key = BD_VK_ENTER;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < page->num_bogs; ii++) {
Packit 5e46da
        BD_IG_BOG *bog      = &page->bog[ii];
Packit 5e46da
        unsigned   valid_id = gc->bog_data[ii].enabled_button;
Packit 5e46da
        BD_IG_BUTTON *button = _find_button_bog(bog, valid_id);
Packit 5e46da
        if (!button) {
Packit 5e46da
            continue;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* numeric select */
Packit 5e46da
        if (VK_IS_NUMERIC(key)) {
Packit 5e46da
            if (button->numeric_select_value == VK_TO_NUMBER(key)) {
Packit 5e46da
                new_btn_id = button->id;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* cursor keys */
Packit 5e46da
        else if (VK_IS_CURSOR(key) || key == BD_VK_ENTER) {
Packit 5e46da
            if (button->id == cur_btn_id) {
Packit 5e46da
                switch(key) {
Packit 5e46da
                    case BD_VK_UP:
Packit 5e46da
                        new_btn_id = button->upper_button_id_ref;
Packit 5e46da
                        break;
Packit 5e46da
                    case BD_VK_DOWN:
Packit 5e46da
                        new_btn_id = button->lower_button_id_ref;
Packit 5e46da
                        break;
Packit 5e46da
                    case BD_VK_LEFT:
Packit 5e46da
                        new_btn_id = button->left_button_id_ref;
Packit 5e46da
                        break;
Packit 5e46da
                    case BD_VK_RIGHT:
Packit 5e46da
                        new_btn_id = button->right_button_id_ref;
Packit 5e46da
                        break;
Packit 5e46da
                    case BD_VK_ENTER:
Packit 5e46da
                        activated_btn_id = cur_btn_id;
Packit 5e46da
Packit 5e46da
                        if (cmds) {
Packit 5e46da
                            cmds->num_nav_cmds = button->num_nav_cmds;
Packit 5e46da
                            cmds->nav_cmds     = button->nav_cmds;
Packit 5e46da
                            cmds->sound_id_ref = button->activated_sound_id_ref;
Packit 5e46da
                        } else {
Packit 5e46da
                            GC_ERROR("_user_input(): VD_VK_ENTER action ignored (missing result buffer)\n");
Packit 5e46da
                        }
Packit 5e46da
                        break;
Packit 5e46da
                    default:;
Packit 5e46da
                }
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            if (new_btn_id != cur_btn_id) {
Packit 5e46da
                BD_IG_BUTTON *new_button = _find_button_page(page, new_btn_id, NULL);
Packit 5e46da
                if (new_button && cmds) {
Packit 5e46da
                    cmds->sound_id_ref = new_button->selected_sound_id_ref;
Packit 5e46da
                }
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* render page ? */
Packit 5e46da
    if (new_btn_id != cur_btn_id || activated_btn_id >= 0) {
Packit 5e46da
Packit 5e46da
        _select_button(gc, new_btn_id);
Packit 5e46da
Packit 5e46da
        _render_page(gc, activated_btn_id, cmds);
Packit 5e46da
Packit 5e46da
        /* found one*/
Packit 5e46da
        return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _set_button_page(GRAPHICS_CONTROLLER *gc, uint32_t param)
Packit 5e46da
{
Packit 5e46da
    unsigned page_flag   = param & 0x80000000;
Packit 5e46da
    unsigned effect_flag = param & 0x40000000;
Packit 5e46da
    unsigned button_flag = param & 0x20000000;
Packit 5e46da
    unsigned page_id     = (param >> 16) & 0xff;
Packit 5e46da
    unsigned button_id   = param & 0xffff;
Packit 5e46da
    unsigned bog_idx     = 0;
Packit 5e46da
Packit 5e46da
    PG_DISPLAY_SET *s      = gc->igs;
Packit 5e46da
    BD_IG_PAGE     *page   = NULL;
Packit 5e46da
    BD_IG_BUTTON   *button = NULL;
Packit 5e46da
Packit 5e46da
    GC_TRACE("_set_button_page(0x%08x): page flag %d, id %d, effects %d   button flag %d, id %d\n",
Packit 5e46da
          param, !!page_flag, page_id, !!effect_flag, !!button_flag, button_id);
Packit 5e46da
Packit 5e46da
    /* 10.4.3.4 (D) */
Packit 5e46da
Packit 5e46da
    if (!page_flag && !button_flag) {
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (page_flag) {
Packit 5e46da
Packit 5e46da
        /* current page --> command is ignored */
Packit 5e46da
        if (page_id == bd_psr_read(gc->regs, PSR_MENU_PAGE_ID)) {
Packit 5e46da
            GC_TRACE("  page is current\n");
Packit 5e46da
            return;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        page = _find_page(&s->ics->interactive_composition, page_id);
Packit 5e46da
Packit 5e46da
        /* invalid page --> command is ignored */
Packit 5e46da
        if (!page) {
Packit 5e46da
            GC_TRACE("  page is invalid\n");
Packit 5e46da
            return;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* page changes */
Packit 5e46da
Packit 5e46da
        _select_page(gc, page_id, !effect_flag);
Packit 5e46da
Packit 5e46da
    } else {
Packit 5e46da
        /* page does not change */
Packit 5e46da
        page_id = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
Packit 5e46da
        page    = _find_page(&s->ics->interactive_composition, page_id);
Packit 5e46da
Packit 5e46da
        if (!page) {
Packit 5e46da
            GC_ERROR("_set_button_page(): PSR_MENU_PAGE_ID refers to unknown page %d\n", page_id);
Packit 5e46da
            return;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (button_flag) {
Packit 5e46da
        /* find correct button and overlap group */
Packit 5e46da
        button = _find_button_page(page, button_id, &bog_idx);
Packit 5e46da
Packit 5e46da
        if (!page_flag) {
Packit 5e46da
            if (!button) {
Packit 5e46da
                /* page not given, invalid button --> ignore command */
Packit 5e46da
                GC_TRACE("  button is invalid\n");
Packit 5e46da
                return;
Packit 5e46da
            }
Packit 5e46da
            if (button_id == bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID)) {
Packit 5e46da
                /* page not given, current button --> ignore command */
Packit 5e46da
                GC_TRACE("  button is current\n");
Packit 5e46da
                return;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (button) {
Packit 5e46da
        gc->bog_data[bog_idx].enabled_button = button_id;
Packit 5e46da
        _select_button(gc, button_id);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    _render_page(gc, 0xffff, NULL); /* auto action not triggered yet */
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _enable_button(GRAPHICS_CONTROLLER *gc, uint32_t button_id, unsigned enable)
Packit 5e46da
{
Packit 5e46da
    PG_DISPLAY_SET *s          = gc->igs;
Packit 5e46da
    BD_IG_PAGE     *page       = NULL;
Packit 5e46da
    BD_IG_BUTTON   *button     = NULL;
Packit 5e46da
    unsigned        page_id    = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
Packit 5e46da
    unsigned        cur_btn_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
Packit 5e46da
    unsigned        bog_idx    = 0;
Packit 5e46da
Packit 5e46da
    GC_TRACE("_enable_button(#%d, %s)\n", button_id, enable ? "enable" : "disable");
Packit 5e46da
Packit 5e46da
    page = _find_page(&s->ics->interactive_composition, page_id);
Packit 5e46da
    if (!page) {
Packit 5e46da
        GC_TRACE("_enable_button(): unknown page #%d (have %d pages)\n",
Packit 5e46da
              page_id, s->ics->interactive_composition.num_pages);
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* find correct button overlap group */
Packit 5e46da
    button = _find_button_page(page, button_id, &bog_idx);
Packit 5e46da
    if (!button) {
Packit 5e46da
        GC_TRACE("_enable_button(): unknown button #%d (page #%d)\n", button_id, page_id);
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (enable) {
Packit 5e46da
        if (gc->bog_data[bog_idx].enabled_button == cur_btn_id) {
Packit 5e46da
            /* selected button goes to disabled state */
Packit 5e46da
            bd_psr_write(gc->regs, PSR_SELECTED_BUTTON_ID, 0x10000|button_id);
Packit 5e46da
        }
Packit 5e46da
        gc->bog_data[bog_idx].enabled_button = button_id;
Packit 5e46da
        gc->bog_data[bog_idx].animate_indx = 0;
Packit 5e46da
Packit 5e46da
    } else {
Packit 5e46da
        if (gc->bog_data[bog_idx].enabled_button == button_id) {
Packit 5e46da
            gc->bog_data[bog_idx].enabled_button = 0xffff;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (cur_btn_id == button_id) {
Packit 5e46da
            bd_psr_write(gc->regs, PSR_SELECTED_BUTTON_ID, 0xffff);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _update_selected_button(GRAPHICS_CONTROLLER *gc)
Packit 5e46da
{
Packit 5e46da
    /* executed after IG command sequence terminates */
Packit 5e46da
    unsigned button_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
Packit 5e46da
Packit 5e46da
    GC_TRACE("_update_selected_button(): currently enabled button is #%d\n", button_id);
Packit 5e46da
Packit 5e46da
    /* special case: triggered only after enable button disables selected button */
Packit 5e46da
    if (button_id & 0x10000) {
Packit 5e46da
        button_id &= 0xffff;
Packit 5e46da
        _select_button(gc, button_id);
Packit 5e46da
        GC_TRACE("_update_selected_button() -> #%d [last enabled]\n", button_id);
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (button_id == 0xffff) {
Packit 5e46da
        button_id = _find_selected_button_id(gc);
Packit 5e46da
        _select_button(gc, button_id);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _mouse_move(GRAPHICS_CONTROLLER *gc, uint16_t x, uint16_t y, GC_NAV_CMDS *cmds)
Packit 5e46da
{
Packit 5e46da
    PG_DISPLAY_SET *s          = gc->igs;
Packit 5e46da
    BD_IG_PAGE     *page       = NULL;
Packit 5e46da
    unsigned        page_id    = bd_psr_read(gc->regs, PSR_MENU_PAGE_ID);
Packit 5e46da
    unsigned        cur_btn_id = bd_psr_read(gc->regs, PSR_SELECTED_BUTTON_ID);
Packit 5e46da
    unsigned        new_btn_id = 0xffff;
Packit 5e46da
    unsigned        ii;
Packit 5e46da
Packit 5e46da
    gc->valid_mouse_position = 0;
Packit 5e46da
Packit 5e46da
    if (!gc->ig_drawn) {
Packit 5e46da
        GC_TRACE("_mouse_move(): menu not visible\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (gc->button_effect_running) {
Packit 5e46da
        GC_ERROR("_mouse_move(): button_effect_running\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    page = _find_page(&s->ics->interactive_composition, page_id);
Packit 5e46da
    if (!page) {
Packit 5e46da
        GC_ERROR("_mouse_move(): unknown page #%d (have %d pages)\n",
Packit 5e46da
              page_id, s->ics->interactive_composition.num_pages);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    for (ii = 0; ii < page->num_bogs; ii++) {
Packit 5e46da
        BD_IG_BOG    *bog      = &page->bog[ii];
Packit 5e46da
        unsigned      valid_id = gc->bog_data[ii].enabled_button;
Packit 5e46da
        BD_IG_BUTTON *button   = _find_button_bog(bog, valid_id);
Packit 5e46da
Packit 5e46da
        if (!button)
Packit 5e46da
            continue;
Packit 5e46da
Packit 5e46da
        if (x < button->x_pos || y < button->y_pos)
Packit 5e46da
            continue;
Packit 5e46da
Packit 5e46da
        /* Check for SELECTED state object (button that can be selected) */
Packit 5e46da
        BD_PG_OBJECT *object = _find_object_for_button(s, button, BTN_SELECTED, NULL);
Packit 5e46da
        if (!object)
Packit 5e46da
            continue;
Packit 5e46da
Packit 5e46da
        if (x >= button->x_pos + object->width || y >= button->y_pos + object->height)
Packit 5e46da
            continue;
Packit 5e46da
Packit 5e46da
        /* mouse is over button */
Packit 5e46da
        gc->valid_mouse_position = 1;
Packit 5e46da
Packit 5e46da
        /* is button already selected? */
Packit 5e46da
        if (button->id == cur_btn_id) {
Packit 5e46da
            return 1;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        new_btn_id = button->id;
Packit 5e46da
Packit 5e46da
        if (cmds) {
Packit 5e46da
            cmds->sound_id_ref = button->selected_sound_id_ref;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        break;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (new_btn_id != 0xffff) {
Packit 5e46da
        _select_button(gc, new_btn_id);
Packit 5e46da
Packit 5e46da
        _render_page(gc, -1, cmds);
Packit 5e46da
Packit 5e46da
        _reset_user_timeout(gc);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return gc->valid_mouse_position;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _animate(GRAPHICS_CONTROLLER *gc, GC_NAV_CMDS *cmds)
Packit 5e46da
{
Packit 5e46da
    int result = -1;
Packit 5e46da
Packit 5e46da
    if (gc->ig_open) {
Packit 5e46da
Packit 5e46da
        result = 0;
Packit 5e46da
Packit 5e46da
        if (gc->out_effects) {
Packit 5e46da
            int64_t pts = bd_get_scr();
Packit 5e46da
            int64_t duration = (int64_t)gc->out_effects->effect[gc->effect_idx].duration;
Packit 5e46da
            if (pts >= (gc->next_effect_time + duration)) {
Packit 5e46da
                gc->next_effect_time += duration;
Packit 5e46da
                gc->effect_idx++;
Packit 5e46da
                if (gc->effect_idx >= gc->out_effects->num_effects) {
Packit 5e46da
                    gc->out_effects = NULL;
Packit 5e46da
                    gc->effect_idx = 0;
Packit 5e46da
                    _clear_osd(gc, BD_OVERLAY_IG);
Packit 5e46da
                }
Packit 5e46da
                result = _render_page(gc, 0xffff, cmds);
Packit 5e46da
            }
Packit 5e46da
        } else if (gc->in_effects) {
Packit 5e46da
            int64_t pts = bd_get_scr();
Packit 5e46da
            int64_t duration = (int64_t)gc->in_effects->effect[gc->effect_idx].duration;
Packit 5e46da
            if (pts >= (gc->next_effect_time + duration)) {
Packit 5e46da
                gc->next_effect_time += duration;
Packit 5e46da
                gc->effect_idx++;
Packit 5e46da
                if (gc->effect_idx >= gc->in_effects->num_effects) {
Packit 5e46da
                    gc->in_effects = NULL;
Packit 5e46da
                    gc->effect_idx = 0;
Packit 5e46da
                    _clear_osd(gc, BD_OVERLAY_IG);
Packit 5e46da
                }
Packit 5e46da
                result = _render_page(gc, 0xffff, cmds);
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
        } else if (gc->button_animation_running) {
Packit 5e46da
            int64_t pts = bd_get_scr();
Packit 5e46da
            if (pts >= (gc->next_effect_time + gc->frame_interval)) {
Packit 5e46da
                gc->next_effect_time += gc->frame_interval;
Packit 5e46da
                result = _render_page(gc, 0xffff, cmds);
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _run_timers(GRAPHICS_CONTROLLER *gc, GC_NAV_CMDS *cmds)
Packit 5e46da
{
Packit 5e46da
    int result = -1;
Packit 5e46da
Packit 5e46da
    if (gc->ig_open) {
Packit 5e46da
Packit 5e46da
        result = 0;
Packit 5e46da
Packit 5e46da
        if (gc->user_timeout) {
Packit 5e46da
            int64_t pts = bd_get_scr();
Packit 5e46da
            if (pts > gc->user_timeout) {
Packit 5e46da
Packit 5e46da
                GC_TRACE("user timeout expired\n");
Packit 5e46da
Packit 5e46da
                if (gc->igs->ics->interactive_composition.ui_model != IG_UI_MODEL_POPUP) {
Packit 5e46da
Packit 5e46da
                    if (bd_psr_read(gc->regs, PSR_MENU_PAGE_ID) != 0) {
Packit 5e46da
                        _select_page(gc, 0, 0);
Packit 5e46da
                        result = _render_page(gc, 0xffff, cmds);
Packit 5e46da
                    }
Packit 5e46da
Packit 5e46da
                } else {
Packit 5e46da
                    gc->popup_visible = 0;
Packit 5e46da
                    result = _render_page(gc, 0xffff, cmds);
Packit 5e46da
                }
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int gc_run(GRAPHICS_CONTROLLER *gc, gc_ctrl_e ctrl, uint32_t param, GC_NAV_CMDS *cmds)
Packit 5e46da
{
Packit 5e46da
    int result = -1;
Packit 5e46da
Packit 5e46da
    if (cmds) {
Packit 5e46da
        cmds->num_nav_cmds = 0;
Packit 5e46da
        cmds->nav_cmds     = NULL;
Packit 5e46da
        cmds->sound_id_ref = -1;
Packit 5e46da
        cmds->status       = GC_STATUS_NONE;
Packit 5e46da
        cmds->page_uo_mask = uo_mask_get_empty();
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!gc) {
Packit 5e46da
        GC_TRACE("gc_run(): no graphics controller\n");
Packit 5e46da
        return result;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&gc->mutex);
Packit 5e46da
Packit 5e46da
    /* always accept reset */
Packit 5e46da
    switch (ctrl) {
Packit 5e46da
        case GC_CTRL_RESET:
Packit 5e46da
            _gc_reset(gc);
Packit 5e46da
Packit 5e46da
            bd_mutex_unlock(&gc->mutex);
Packit 5e46da
            return 0;
Packit 5e46da
        case GC_CTRL_PG_UPDATE:
Packit 5e46da
            if (gc->pgs && gc->pgs->pcs) {
Packit 5e46da
                result = _render_pg(gc);
Packit 5e46da
            }
Packit 5e46da
            if (gc->tgs && gc->tgs->dialog) {
Packit 5e46da
                result = _render_textst(gc, param, cmds);
Packit 5e46da
            }
Packit 5e46da
            bd_mutex_unlock(&gc->mutex);
Packit 5e46da
            return result;
Packit 5e46da
Packit 5e46da
        case GC_CTRL_STYLE_SELECT:
Packit 5e46da
            result = _textst_style_select(gc, param);
Packit 5e46da
            bd_mutex_unlock(&gc->mutex);
Packit 5e46da
            return result;
Packit 5e46da
Packit 5e46da
        case GC_CTRL_PG_CHARCODE:
Packit 5e46da
            if (gc->textst_render) {
Packit 5e46da
                textst_render_set_char_code(gc->textst_render, param);
Packit 5e46da
                result = 0;
Packit 5e46da
            }
Packit 5e46da
            bd_mutex_unlock(&gc->mutex);
Packit 5e46da
            return result;
Packit 5e46da
Packit 5e46da
        case GC_CTRL_PG_RESET:
Packit 5e46da
            _reset_pg(gc);
Packit 5e46da
Packit 5e46da
            bd_mutex_unlock(&gc->mutex);
Packit 5e46da
            return 0;
Packit 5e46da
Packit 5e46da
        default:;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* other operations require complete display set */
Packit 5e46da
    if (!gc->igs || !gc->igs->ics || !gc->igs->complete) {
Packit 5e46da
        GC_TRACE("gc_run(): no interactive composition\n");
Packit 5e46da
        bd_mutex_unlock(&gc->mutex);
Packit 5e46da
        return result;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    switch (ctrl) {
Packit 5e46da
Packit 5e46da
        case GC_CTRL_SET_BUTTON_PAGE:
Packit 5e46da
            _set_button_page(gc, param);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case GC_CTRL_VK_KEY:
Packit 5e46da
            if (param != BD_VK_POPUP) {
Packit 5e46da
                result = _user_input(gc, param, cmds);
Packit 5e46da
                break;
Packit 5e46da
            }
Packit 5e46da
            /* BD_VK_POPUP => GC_CTRL_POPUP */
Packit 5e46da
            param = !gc->popup_visible;
Packit 5e46da
            /* fall thru */
Packit 5e46da
Packit 5e46da
        case GC_CTRL_POPUP:
Packit 5e46da
            if (gc->igs->ics->interactive_composition.ui_model != IG_UI_MODEL_POPUP) {
Packit 5e46da
                /* not pop-up menu */
Packit 5e46da
                break;
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            gc->popup_visible = !!param;
Packit 5e46da
Packit 5e46da
            if (gc->popup_visible) {
Packit 5e46da
                _select_page(gc, 0, 0);
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            result = _render_page(gc, 0xffff, cmds);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case GC_CTRL_NOP:
Packit 5e46da
            result = _animate(gc, cmds);
Packit 5e46da
            _run_timers(gc, cmds);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case GC_CTRL_INIT_MENU:
Packit 5e46da
            _select_page(gc, 0, 0);
Packit 5e46da
            _render_page(gc, 0xffff, cmds);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case GC_CTRL_IG_END:
Packit 5e46da
            _update_selected_button(gc);
Packit 5e46da
            _render_page(gc, 0xffff, cmds);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case GC_CTRL_ENABLE_BUTTON:
Packit 5e46da
            _enable_button(gc, param, 1);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case GC_CTRL_DISABLE_BUTTON:
Packit 5e46da
            _enable_button(gc, param, 0);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case GC_CTRL_MOUSE_MOVE:
Packit 5e46da
            result = _mouse_move(gc, param >> 16, param & 0xffff, cmds);
Packit 5e46da
            break;
Packit 5e46da
Packit 5e46da
        case GC_CTRL_RESET:
Packit 5e46da
        case GC_CTRL_PG_RESET:
Packit 5e46da
        case GC_CTRL_PG_UPDATE:
Packit 5e46da
        case GC_CTRL_PG_CHARCODE:
Packit 5e46da
        case GC_CTRL_STYLE_SELECT:
Packit 5e46da
            /* already handled */
Packit 5e46da
            break;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (cmds) {
Packit 5e46da
        if (gc->igs->ics->interactive_composition.ui_model == IG_UI_MODEL_POPUP) {
Packit 5e46da
            cmds->status |= GC_STATUS_POPUP;
Packit 5e46da
        }
Packit 5e46da
        if (gc->ig_drawn) {
Packit 5e46da
            cmds->status |= GC_STATUS_MENU_OPEN;
Packit 5e46da
        }
Packit 5e46da
        if (gc->in_effects || gc->out_effects || gc->button_animation_running || gc->user_timeout) {
Packit 5e46da
            /* do not trigger if unopened pop-up menu has animations */
Packit 5e46da
            if (gc->ig_open) {
Packit 5e46da
                cmds->status |= GC_STATUS_ANIMATE;
Packit 5e46da
                /* user input is still not handled, but user "sees" the menu. */
Packit 5e46da
                cmds->status |= GC_STATUS_MENU_OPEN;
Packit 5e46da
            }
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        if (gc->ig_open) {
Packit 5e46da
            cmds->page_uo_mask = gc->page_uo_mask;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&gc->mutex);
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}