tarinaretake

git clone https://git.tarina.org/tarinaretake
Log | Files | Refs | README | LICENSE

commit e51cff183d15b08cf4032ec41f4d2a9490a62d47
parent 8c1cbd07d94e009f1f00b1fc5e7b6671fa737bd5
Author: rob <rob@tarina.org>
Date:   Tue, 14 Nov 2023 16:48:21 +0200

gui makefiles

Diffstat:
Agui/Makefile.include | 28++++++++++++++++++++++++++++
Agui/libs/ilclient/Makefile | 5+++++
Agui/libs/ilclient/ilclient.c | 1836+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agui/libs/ilclient/ilclient.h | 1039+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agui/libs/ilclient/ilcore.c | 308+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agui/libs/vgfont/Makefile | 7+++++++
Agui/libs/vgfont/font.c | 355+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agui/libs/vgfont/graphics.c | 1608+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agui/libs/vgfont/graphics_x_private.h | 366+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agui/libs/vgfont/libvgfont.a | 0
Agui/libs/vgfont/vgfont.h | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agui/libs/vgfont/vgft.c | 416+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agui/libs/vgfont/vgft.h | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agui/src/Makefile | 7+++++++
Agui/src/VeraMono.ttf | 0
Agui/src/fixedsys.ttf | 0
Agui/src/main.c | 306+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agui/src/main_new.c | 42++++++++++++++++++++++++++++++++++++++++++
18 files changed, 6529 insertions(+), 0 deletions(-)

diff --git a/gui/Makefile.include b/gui/Makefile.include @@ -0,0 +1,28 @@ + +CFLAGS+=-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -U_FORTIFY_SOURCE -Wall -g -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -Wno-psabi + +LDFLAGS+=-L$(SDKSTAGE)/opt/vc/lib/ -lbrcmGLESv2 -lbrcmEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm -L$(SDKSTAGE)/home/pi/tarina/gui/libs/ilclient -L$(SDKSTAGE)/home/pi/tarina/gui/libs/vgfont + +INCLUDES+=-I$(SDKSTAGE)/opt/vc/include/ -I$(SDKSTAGE)/opt/vc/include/interface/vcos/pthreads -I$(SDKSTAGE)/opt/vc/include/interface/vmcs_host/linux -I./ -I$(SDKSTAGE)/home/pi/tarina/gui/libs/ilclient -I$(SDKSTAGE)/home/pi/tarina/gui/libs/vgfont + +all: $(BIN) $(LIB) + +%.o: %.c + @rm -f $@ + $(CC) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations + +%.o: %.cpp + @rm -f $@ + $(CXX) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations + +%.bin: $(OBJS) + $(CC) -o $@ -Wl,--whole-archive $(OBJS) $(LDFLAGS) -Wl,--no-whole-archive -rdynamic + +%.a: $(OBJS) + $(AR) r $@ $^ + +clean: + for i in $(OBJS); do (if test -e "$$i"; then ( rm $$i ); fi ); done + @rm -f $(BIN) $(LIB) + + diff --git a/gui/libs/ilclient/Makefile b/gui/libs/ilclient/Makefile @@ -0,0 +1,5 @@ +OBJS=ilclient.o ilcore.o +LIB=libilclient.a + +include ../../Makefile.include + diff --git a/gui/libs/ilclient/ilclient.c b/gui/libs/ilclient/ilclient.c @@ -0,0 +1,1836 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file + * + * \brief This API defines helper functions for writing IL clients. + * + * This file defines an IL client side library. This is useful when + * writing IL clients, since there tends to be much repeated and + * common code across both single and multiple clients. This library + * seeks to remove that common code and abstract some of the + * interactions with components. There is a wrapper around a + * component and tunnel, and some operations can be done on lists of + * these. The callbacks from components are handled, and specific + * events can be checked or waited for. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include "interface/vcos/vcos.h" +#include "interface/vcos/vcos_logging.h" +#include "interface/vmcs_host/vchost.h" + +#include "IL/OMX_Broadcom.h" +#include "ilclient.h" + +#define VCOS_LOG_CATEGORY (&ilclient_log_category) + +#ifndef ILCLIENT_THREAD_DEFAULT_STACK_SIZE +#define ILCLIENT_THREAD_DEFAULT_STACK_SIZE (6<<10) +#endif + +static VCOS_LOG_CAT_T ilclient_log_category; + +/****************************************************************************** +Static data and types used only in this file. +******************************************************************************/ + +struct _ILEVENT_T { + OMX_EVENTTYPE eEvent; + OMX_U32 nData1; + OMX_U32 nData2; + OMX_PTR pEventData; + struct _ILEVENT_T *next; +}; + +#define NUM_EVENTS 100 +struct _ILCLIENT_T { + ILEVENT_T *event_list; + VCOS_SEMAPHORE_T event_sema; + ILEVENT_T event_rep[NUM_EVENTS]; + + ILCLIENT_CALLBACK_T port_settings_callback; + void *port_settings_callback_data; + ILCLIENT_CALLBACK_T eos_callback; + void *eos_callback_data; + ILCLIENT_CALLBACK_T error_callback; + void *error_callback_data; + ILCLIENT_BUFFER_CALLBACK_T fill_buffer_done_callback; + void *fill_buffer_done_callback_data; + ILCLIENT_BUFFER_CALLBACK_T empty_buffer_done_callback; + void *empty_buffer_done_callback_data; + ILCLIENT_CALLBACK_T configchanged_callback; + void *configchanged_callback_data; +}; + +struct _COMPONENT_T { + OMX_HANDLETYPE comp; + ILCLIENT_CREATE_FLAGS_T flags; + VCOS_SEMAPHORE_T sema; + VCOS_EVENT_FLAGS_T event; + struct _COMPONENT_T *related; + OMX_BUFFERHEADERTYPE *out_list; + OMX_BUFFERHEADERTYPE *in_list; + char name[32]; + char bufname[32]; + unsigned int error_mask; + unsigned int private; + ILEVENT_T *list; + ILCLIENT_T *client; +}; + +#define random_wait() +static char *states[] = {"Invalid", "Loaded", "Idle", "Executing", "Pause", "WaitingForResources"}; + +typedef enum { + ILCLIENT_ERROR_UNPOPULATED = 0x1, + ILCLIENT_ERROR_SAMESTATE = 0x2, + ILCLIENT_ERROR_BADPARAMETER = 0x4 +} ILERROR_MASK_T; + +/****************************************************************************** +Static functions. +******************************************************************************/ + +static OMX_ERRORTYPE ilclient_empty_buffer_done(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_empty_buffer_done_error(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_fill_buffer_done(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_fill_buffer_done_error(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); +static OMX_ERRORTYPE ilclient_event_handler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, + OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData); +static void ilclient_lock_events(ILCLIENT_T *st); +static void ilclient_unlock_events(ILCLIENT_T *st); + +/****************************************************************************** +Global functions +******************************************************************************/ + +/*********************************************************** + * Name: ilclient_init + * + * Description: Creates ilclient pointer + * + * Returns: pointer to client structure + ***********************************************************/ +ILCLIENT_T *ilclient_init() +{ + ILCLIENT_T *st = vcos_malloc(sizeof(ILCLIENT_T), "ilclient"); + int i; + + if (!st) + return NULL; + + vcos_log_set_level(VCOS_LOG_CATEGORY, VCOS_LOG_WARN); + vcos_log_register("ilclient", VCOS_LOG_CATEGORY); + + memset(st, 0, sizeof(ILCLIENT_T)); + + i = vcos_semaphore_create(&st->event_sema, "il:event", 1); + vc_assert(i == VCOS_SUCCESS); + + ilclient_lock_events(st); + st->event_list = NULL; + for (i=0; i<NUM_EVENTS; i++) + { + st->event_rep[i].eEvent = -1; // mark as unused + st->event_rep[i].next = st->event_list; + st->event_list = st->event_rep+i; + } + ilclient_unlock_events(st); + return st; +} + +/*********************************************************** + * Name: ilclient_destroy + * + * Description: frees client state + * + * Returns: void + ***********************************************************/ +void ilclient_destroy(ILCLIENT_T *st) +{ + vcos_semaphore_delete(&st->event_sema); + vcos_free(st); + vcos_log_unregister(VCOS_LOG_CATEGORY); +} + +/*********************************************************** + * Name: ilclient_set_port_settings_callback + * + * Description: sets the callback used when receiving port settings + * changed messages. The data field in the callback function will be + * the port index reporting the message. + * + * Returns: void + ***********************************************************/ +void ilclient_set_port_settings_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->port_settings_callback = func; + st->port_settings_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_eos_callback + * + * Description: sets the callback used when receiving eos flags. The + * data parameter in the callback function will be the port index + * reporting an eos flag. + * + * Returns: void + ***********************************************************/ +void ilclient_set_eos_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->eos_callback = func; + st->eos_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_error_callback + * + * Description: sets the callback used when receiving error events. + * The data parameter in the callback function will be the error code + * being reported. + * + * Returns: void + ***********************************************************/ +void ilclient_set_error_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->error_callback = func; + st->error_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_fill_buffer_done_callback + * + * Description: sets the callback used when receiving + * fill_buffer_done event + * + * Returns: void + ***********************************************************/ +void ilclient_set_fill_buffer_done_callback(ILCLIENT_T *st, ILCLIENT_BUFFER_CALLBACK_T func, void *userdata) +{ + st->fill_buffer_done_callback = func; + st->fill_buffer_done_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_empty_buffer_done_callback + * + * Description: sets the callback used when receiving + * empty_buffer_done event + * + * Returns: void + ***********************************************************/ +void ilclient_set_empty_buffer_done_callback(ILCLIENT_T *st, ILCLIENT_BUFFER_CALLBACK_T func, void *userdata) +{ + st->empty_buffer_done_callback = func; + st->empty_buffer_done_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_set_configchanged_callback + * + * Description: sets the callback used when a config changed + * event is received + * + * Returns: void + ***********************************************************/ +void ilclient_set_configchanged_callback(ILCLIENT_T *st, ILCLIENT_CALLBACK_T func, void *userdata) +{ + st->configchanged_callback = func; + st->configchanged_callback_data = userdata; +} + +/*********************************************************** + * Name: ilclient_create_component + * + * Description: initialises a component state structure and creates + * the IL component. + * + * Returns: 0 on success, -1 on failure + ***********************************************************/ +int ilclient_create_component(ILCLIENT_T *client, COMPONENT_T **comp, char *name, + ILCLIENT_CREATE_FLAGS_T flags) +{ + OMX_CALLBACKTYPE callbacks; + OMX_ERRORTYPE error; + char component_name[128]; + int32_t status; + + *comp = vcos_malloc(sizeof(COMPONENT_T), "il:comp"); + if(!*comp) + return -1; + + memset(*comp, 0, sizeof(COMPONENT_T)); + +#define COMP_PREFIX "OMX.broadcom." + + status = vcos_event_flags_create(&(*comp)->event,"il:comp"); + vc_assert(status == VCOS_SUCCESS); + status = vcos_semaphore_create(&(*comp)->sema, "il:comp", 1); + vc_assert(status == VCOS_SUCCESS); + (*comp)->client = client; + + vcos_snprintf((*comp)->name, sizeof((*comp)->name), "cl:%s", name); + vcos_snprintf((*comp)->bufname, sizeof((*comp)->bufname), "cl:%s buffer", name); + vcos_snprintf(component_name, sizeof(component_name), "%s%s", COMP_PREFIX, name); + + (*comp)->flags = flags; + + callbacks.EventHandler = ilclient_event_handler; + callbacks.EmptyBufferDone = flags & ILCLIENT_ENABLE_INPUT_BUFFERS ? ilclient_empty_buffer_done : ilclient_empty_buffer_done_error; + callbacks.FillBufferDone = flags & ILCLIENT_ENABLE_OUTPUT_BUFFERS ? ilclient_fill_buffer_done : ilclient_fill_buffer_done_error; + + error = OMX_GetHandle(&(*comp)->comp, component_name, *comp, &callbacks); + + if (error == OMX_ErrorNone) + { + OMX_UUIDTYPE uid; + char name[128]; + OMX_VERSIONTYPE compVersion, specVersion; + + if(OMX_GetComponentVersion((*comp)->comp, name, &compVersion, &specVersion, &uid) == OMX_ErrorNone) + { + char *p = (char *) uid + strlen(COMP_PREFIX); + + vcos_snprintf((*comp)->name, sizeof((*comp)->name), "cl:%s", p); + (*comp)->name[sizeof((*comp)->name)-1] = 0; + vcos_snprintf((*comp)->bufname, sizeof((*comp)->bufname), "cl:%s buffer", p); + (*comp)->bufname[sizeof((*comp)->bufname)-1] = 0; + } + + if(flags & (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_OUTPUT_ZERO_BUFFERS)) + { + OMX_PORT_PARAM_TYPE ports; + OMX_INDEXTYPE types[] = {OMX_IndexParamAudioInit, OMX_IndexParamVideoInit, OMX_IndexParamImageInit, OMX_IndexParamOtherInit}; + int i; + + ports.nSize = sizeof(OMX_PORT_PARAM_TYPE); + ports.nVersion.nVersion = OMX_VERSION; + + for(i=0; i<4; i++) + { + OMX_ERRORTYPE error = OMX_GetParameter((*comp)->comp, types[i], &ports); + if(error == OMX_ErrorNone) + { + uint32_t j; + for(j=0; j<ports.nPorts; j++) + { + if(flags & ILCLIENT_DISABLE_ALL_PORTS) + ilclient_disable_port(*comp, ports.nStartPortNumber+j); + + if(flags & ILCLIENT_OUTPUT_ZERO_BUFFERS) + { + OMX_PARAM_PORTDEFINITIONTYPE portdef; + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = ports.nStartPortNumber+j; + if(OMX_GetParameter((*comp)->comp, OMX_IndexParamPortDefinition, &portdef) == OMX_ErrorNone) + { + if(portdef.eDir == OMX_DirOutput && portdef.nBufferCountActual > 0) + { + portdef.nBufferCountActual = 0; + OMX_SetParameter((*comp)->comp, OMX_IndexParamPortDefinition, &portdef); + } + } + } + } + } + } + } + return 0; + } + else + { + vcos_event_flags_delete(&(*comp)->event); + vcos_semaphore_delete(&(*comp)->sema); + vcos_free(*comp); + *comp = NULL; + return -1; + } +} + +/*********************************************************** + * Name: ilclient_remove_event + * + * Description: Removes an event from a component event list. ignore1 + * and ignore2 are flags indicating whether to not match on nData1 and + * nData2 respectively. + * + * Returns: 0 if the event was removed. -1 if no matching event was + * found. + ***********************************************************/ +int ilclient_remove_event(COMPONENT_T *st, OMX_EVENTTYPE eEvent, + OMX_U32 nData1, int ignore1, OMX_IN OMX_U32 nData2, int ignore2) +{ + ILEVENT_T *cur, *prev; + uint32_t set; + ilclient_lock_events(st->client); + + cur = st->list; + prev = NULL; + + while (cur && !(cur->eEvent == eEvent && (ignore1 || cur->nData1 == nData1) && (ignore2 || cur->nData2 == nData2))) + { + prev = cur; + cur = cur->next; + } + + if (cur == NULL) + { + ilclient_unlock_events(st->client); + return -1; + } + + if (prev == NULL) + st->list = cur->next; + else + prev->next = cur->next; + + // add back into spare list + cur->next = st->client->event_list; + st->client->event_list = cur; + cur->eEvent = -1; // mark as unused + + // if we're removing an OMX_EventError or OMX_EventParamOrConfigChanged event, then clear the error bit from the eventgroup, + // since the user might have been notified through the error callback, and then + // can't clear the event bit - this will then cause problems the next time they + // wait for an error. + if(eEvent == OMX_EventError) + vcos_event_flags_get(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + else if(eEvent == OMX_EventParamOrConfigChanged) + vcos_event_flags_get(&st->event, ILCLIENT_CONFIG_CHANGED, VCOS_OR_CONSUME, 0, &set); + + ilclient_unlock_events(st->client); + return 0; +} + +/*********************************************************** + * Name: ilclient_state_transition + * + * Description: Transitions a null terminated list of IL components to + * a given state. All components are told to transition in a random + * order before any are checked for transition completion. + * + * Returns: void + ***********************************************************/ +void ilclient_state_transition(COMPONENT_T *list[], OMX_STATETYPE state) +{ + OMX_ERRORTYPE error; + int i, num; + uint32_t set; + + num=0; + while (list[num]) + num++; + + // if we transition the supplier port first, it will call freebuffer on the non + // supplier, which will correctly signal a port unpopulated error. We want to + // ignore these errors. + if (state == OMX_StateLoaded) + for (i=0; i<num; i++) + list[i]->error_mask |= ILCLIENT_ERROR_UNPOPULATED; + for (i=0; i<num; i++) + list[i]->private = ((rand() >> 13) & 0xff)+1; + + for (i=0; i<num; i++) + { + // transition the components in a random order + int j, min = -1; + for (j=0; j<num; j++) + if (list[j]->private && (min == -1 || list[min]->private > list[j]->private)) + min = j; + + list[min]->private = 0; + + random_wait(); + //Clear error event for this component + vcos_event_flags_get(&list[min]->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + + error = OMX_SendCommand(list[min]->comp, OMX_CommandStateSet, state, NULL); + vc_assert(error == OMX_ErrorNone); + } + + random_wait(); + + for (i=0; i<num; i++) + if(ilclient_wait_for_command_complete(list[i], OMX_CommandStateSet, state) < 0) + vc_assert(0); + + if (state == OMX_StateLoaded) + for (i=0; i<num; i++) + list[i]->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED; +} + +/*********************************************************** + * Name: ilclient_teardown_tunnels + * + * Description: tears down a null terminated list of tunnels. + * + * Returns: void + ***********************************************************/ +void ilclient_teardown_tunnels(TUNNEL_T *tunnel) +{ + int i; + OMX_ERRORTYPE error; + + i=0;; + while (tunnel[i].source) + { + error = OMX_SetupTunnel(tunnel[i].source->comp, tunnel[i].source_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SetupTunnel(tunnel[i].sink->comp, tunnel[i].sink_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + i++; + } +} + +/*********************************************************** + * Name: ilclient_disable_tunnel + * + * Description: disables a tunnel by disabling the ports. Allows + * ports to signal same state error if they were already disabled. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_tunnel(TUNNEL_T *tunnel) +{ + OMX_ERRORTYPE error; + + if(tunnel->source == 0 || tunnel->sink == 0) + return; + + tunnel->source->error_mask |= ILCLIENT_ERROR_UNPOPULATED; + tunnel->sink->error_mask |= ILCLIENT_ERROR_UNPOPULATED; + + error = OMX_SendCommand(tunnel->source->comp, OMX_CommandPortDisable, tunnel->source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel->sink->comp, OMX_CommandPortDisable, tunnel->sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + if(ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortDisable, tunnel->source_port) < 0) + vc_assert(0); + + if(ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortDisable, tunnel->sink_port) < 0) + vc_assert(0); + + tunnel->source->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED; + tunnel->sink->error_mask &= ~ILCLIENT_ERROR_UNPOPULATED; +} + +/*********************************************************** + * Name: ilclient_enable_tunnel + * + * Description: enables a tunnel by enabling the ports + * + * Returns: 0 on success, -1 on failure + ***********************************************************/ +int ilclient_enable_tunnel(TUNNEL_T *tunnel) +{ + OMX_STATETYPE state; + OMX_ERRORTYPE error; + + ilclient_debug_output("ilclient: enable tunnel from %x/%d to %x/%d", + tunnel->source, tunnel->source_port, + tunnel->sink, tunnel->sink_port); + + error = OMX_SendCommand(tunnel->source->comp, OMX_CommandPortEnable, tunnel->source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel->sink->comp, OMX_CommandPortEnable, tunnel->sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + // to complete, the sink component can't be in loaded state + error = OMX_GetState(tunnel->sink->comp, &state); + vc_assert(error == OMX_ErrorNone); + if (state == OMX_StateLoaded) + { + int ret = 0; + + if(ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortEnable, tunnel->sink_port) != 0 || + OMX_SendCommand(tunnel->sink->comp, OMX_CommandStateSet, OMX_StateIdle, NULL) != OMX_ErrorNone || + (ret = ilclient_wait_for_command_complete_dual(tunnel->sink, OMX_CommandStateSet, OMX_StateIdle, tunnel->source)) < 0) + { + if(ret == -2) + { + // the error was reported fom the source component: clear this error and disable the sink component + ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortEnable, tunnel->source_port); + ilclient_disable_port(tunnel->sink, tunnel->sink_port); + } + + ilclient_debug_output("ilclient: could not change component state to IDLE"); + ilclient_disable_port(tunnel->source, tunnel->source_port); + return -1; + } + } + else + { + if (ilclient_wait_for_command_complete(tunnel->sink, OMX_CommandPortEnable, tunnel->sink_port) != 0) + { + ilclient_debug_output("ilclient: could not change sink port %d to enabled", tunnel->sink_port); + + //Oops failed to enable the sink port + ilclient_disable_port(tunnel->source, tunnel->source_port); + //Clean up the port enable event from the source port. + ilclient_wait_for_event(tunnel->source, OMX_EventCmdComplete, + OMX_CommandPortEnable, 0, tunnel->source_port, 0, + ILCLIENT_PORT_ENABLED | ILCLIENT_EVENT_ERROR, VCOS_EVENT_FLAGS_SUSPEND); + return -1; + } + } + + if(ilclient_wait_for_command_complete(tunnel->source, OMX_CommandPortEnable, tunnel->source_port) != 0) + { + ilclient_debug_output("ilclient: could not change source port %d to enabled", tunnel->source_port); + + //Failed to enable the source port + ilclient_disable_port(tunnel->sink, tunnel->sink_port); + return -1; + } + + return 0; +} + + +/*********************************************************** + * Name: ilclient_flush_tunnels + * + * Description: flushes all ports used in a null terminated list of + * tunnels. max specifies the maximum number of tunnels to flush from + * the list, where max=0 means all tunnels. + * + * Returns: void + ***********************************************************/ +void ilclient_flush_tunnels(TUNNEL_T *tunnel, int max) +{ + OMX_ERRORTYPE error; + int i; + + i=0; + while (tunnel[i].source && (max == 0 || i < max)) + { + error = OMX_SendCommand(tunnel[i].source->comp, OMX_CommandFlush, tunnel[i].source_port, NULL); + vc_assert(error == OMX_ErrorNone); + + error = OMX_SendCommand(tunnel[i].sink->comp, OMX_CommandFlush, tunnel[i].sink_port, NULL); + vc_assert(error == OMX_ErrorNone); + + ilclient_wait_for_event(tunnel[i].source, OMX_EventCmdComplete, + OMX_CommandFlush, 0, tunnel[i].source_port, 0, + ILCLIENT_PORT_FLUSH, VCOS_EVENT_FLAGS_SUSPEND); + ilclient_wait_for_event(tunnel[i].sink, OMX_EventCmdComplete, + OMX_CommandFlush, 0, tunnel[i].sink_port, 0, + ILCLIENT_PORT_FLUSH, VCOS_EVENT_FLAGS_SUSPEND); + i++; + } +} + + +/*********************************************************** + * Name: ilclient_return_events + * + * Description: Returns all events from a component event list to the + * list of unused event structures. + * + * Returns: void + ***********************************************************/ +void ilclient_return_events(COMPONENT_T *comp) +{ + ilclient_lock_events(comp->client); + while (comp->list) + { + ILEVENT_T *next = comp->list->next; + comp->list->next = comp->client->event_list; + comp->client->event_list = comp->list; + comp->list = next; + } + ilclient_unlock_events(comp->client); +} + +/*********************************************************** + * Name: ilclient_cleanup_components + * + * Description: frees all components from a null terminated list and + * deletes resources used in component state structure. + * + * Returns: void + ***********************************************************/ +void ilclient_cleanup_components(COMPONENT_T *list[]) +{ + int i; + OMX_ERRORTYPE error; + + i=0; + while (list[i]) + { + ilclient_return_events(list[i]); + if (list[i]->comp) + { + error = OMX_FreeHandle(list[i]->comp); + + vc_assert(error == OMX_ErrorNone); + } + i++; + } + + i=0; + while (list[i]) + { + vcos_event_flags_delete(&list[i]->event); + vcos_semaphore_delete(&list[i]->sema); + vcos_free(list[i]); + list[i] = NULL; + i++; + } +} + +/*********************************************************** + * Name: ilclient_change_component_state + * + * Description: changes the state of a single component. Note: this + * may not be suitable if the component is tunnelled and requires + * connected components to also change state. + * + * Returns: 0 on success, -1 on failure (note - trying to change to + * the same state which causes a OMX_ErrorSameState is treated as + * success) + ***********************************************************/ +int ilclient_change_component_state(COMPONENT_T *comp, OMX_STATETYPE state) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandStateSet, state, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandStateSet, state) < 0) + { + ilclient_debug_output("ilclient: could not change component state to %d", state); + ilclient_remove_event(comp, OMX_EventError, 0, 1, 0, 1); + return -1; + } + return 0; +} + +/*********************************************************** + * Name: ilclient_disable_port + * + * Description: disables a port on a given component. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_port(COMPONENT_T *comp, int portIndex) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandPortDisable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortDisable, portIndex) < 0) + vc_assert(0); +} + +/*********************************************************** + * Name: ilclient_enabled_port + * + * Description: enables a port on a given component. + * + * Returns: void + ***********************************************************/ +void ilclient_enable_port(COMPONENT_T *comp, int portIndex) +{ + OMX_ERRORTYPE error; + error = OMX_SendCommand(comp->comp, OMX_CommandPortEnable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortEnable, portIndex) < 0) + vc_assert(0); +} + + +/*********************************************************** + * Name: ilclient_enable_port_buffers + * + * Description: enables a port on a given component which requires + * buffers to be supplied by the client. + * + * Returns: void + ***********************************************************/ +int ilclient_enable_port_buffers(COMPONENT_T *comp, int portIndex, + ILCLIENT_MALLOC_T ilclient_malloc, + ILCLIENT_FREE_T ilclient_free, + void *private) +{ + OMX_ERRORTYPE error; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + OMX_BUFFERHEADERTYPE *list = NULL, **end = &list; + OMX_STATETYPE state; + int i; + + memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = portIndex; + + // work out buffer requirements, check port is in the right state + error = OMX_GetParameter(comp->comp, OMX_IndexParamPortDefinition, &portdef); + if(error != OMX_ErrorNone || portdef.bEnabled != OMX_FALSE || portdef.nBufferCountActual == 0 || portdef.nBufferSize == 0) + return -1; + + // check component is in the right state to accept buffers + error = OMX_GetState(comp->comp, &state); + if (error != OMX_ErrorNone || !(state == OMX_StateIdle || state == OMX_StateExecuting || state == OMX_StatePause)) + return -1; + + // send the command + error = OMX_SendCommand(comp->comp, OMX_CommandPortEnable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + + for (i=0; i != portdef.nBufferCountActual; i++) + { + unsigned char *buf; + if(ilclient_malloc) + buf = ilclient_malloc(private, portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname); + else + buf = vcos_malloc_aligned(portdef.nBufferSize, portdef.nBufferAlignment, comp->bufname); + + if(!buf) + break; + + error = OMX_UseBuffer(comp->comp, end, portIndex, NULL, portdef.nBufferSize, buf); + if(error != OMX_ErrorNone) + { + if(ilclient_free) + ilclient_free(private, buf); + else + vcos_free(buf); + + break; + } + end = (OMX_BUFFERHEADERTYPE **) &((*end)->pAppPrivate); + } + + // queue these buffers + vcos_semaphore_wait(&comp->sema); + + if(portdef.eDir == OMX_DirInput) + { + *end = comp->in_list; + comp->in_list = list; + } + else + { + *end = comp->out_list; + comp->out_list = list; + } + + vcos_semaphore_post(&comp->sema); + + if(i != portdef.nBufferCountActual || + ilclient_wait_for_command_complete(comp, OMX_CommandPortEnable, portIndex) < 0) + { + ilclient_disable_port_buffers(comp, portIndex, NULL, ilclient_free, private); + + // at this point the first command might have terminated with an error, which means that + // the port is disabled before the disable_port_buffers function is called, so we're left + // with the error bit set and an error event in the queue. Clear these now if they exist. + ilclient_remove_event(comp, OMX_EventError, 0, 1, 1, 0); + + return -1; + } + + // success + return 0; +} + + +/*********************************************************** + * Name: ilclient_disable_port_buffers + * + * Description: disables a port on a given component which has + * buffers supplied by the client. + * + * Returns: void + ***********************************************************/ +void ilclient_disable_port_buffers(COMPONENT_T *comp, int portIndex, + OMX_BUFFERHEADERTYPE *bufferList, + ILCLIENT_FREE_T ilclient_free, + void *private) +{ + OMX_ERRORTYPE error; + OMX_BUFFERHEADERTYPE *list = bufferList; + OMX_BUFFERHEADERTYPE **head, *clist, *prev; + OMX_PARAM_PORTDEFINITIONTYPE portdef; + int num; + + // get the buffers off the relevant queue + memset(&portdef, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); + portdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = portIndex; + + // work out buffer requirements, check port is in the right state + error = OMX_GetParameter(comp->comp, OMX_IndexParamPortDefinition, &portdef); + if(error != OMX_ErrorNone || portdef.bEnabled != OMX_TRUE || portdef.nBufferCountActual == 0 || portdef.nBufferSize == 0) + return; + + num = portdef.nBufferCountActual; + + error = OMX_SendCommand(comp->comp, OMX_CommandPortDisable, portIndex, NULL); + vc_assert(error == OMX_ErrorNone); + + while(num > 0) + { + VCOS_UNSIGNED set; + + if(list == NULL) + { + vcos_semaphore_wait(&comp->sema); + + // take buffers for this port off the relevant queue + head = portdef.eDir == OMX_DirInput ? &comp->in_list : &comp->out_list; + clist = *head; + prev = NULL; + + while(clist) + { + if((portdef.eDir == OMX_DirInput ? clist->nInputPortIndex : clist->nOutputPortIndex) == portIndex) + { + OMX_BUFFERHEADERTYPE *pBuffer = clist; + + if(!prev) + clist = *head = (OMX_BUFFERHEADERTYPE *) pBuffer->pAppPrivate; + else + clist = prev->pAppPrivate = (OMX_BUFFERHEADERTYPE *) pBuffer->pAppPrivate; + + pBuffer->pAppPrivate = list; + list = pBuffer; + } + else + { + prev = clist; + clist = (OMX_BUFFERHEADERTYPE *) &(clist->pAppPrivate); + } + } + + vcos_semaphore_post(&comp->sema); + } + + while(list) + { + void *buf = list->pBuffer; + OMX_BUFFERHEADERTYPE *next = list->pAppPrivate; + + error = OMX_FreeBuffer(comp->comp, portIndex, list); + vc_assert(error == OMX_ErrorNone); + + if(ilclient_free) + ilclient_free(private, buf); + else + vcos_free(buf); + + num--; + list = next; + } + + if(num) + { + OMX_U32 mask = ILCLIENT_PORT_DISABLED | ILCLIENT_EVENT_ERROR; + mask |= (portdef.eDir == OMX_DirInput ? ILCLIENT_EMPTY_BUFFER_DONE : ILCLIENT_FILL_BUFFER_DONE); + + // also wait for command complete/error in case we didn't have all the buffers allocated + vcos_event_flags_get(&comp->event, mask, VCOS_OR_CONSUME, -1, &set); + + if((set & ILCLIENT_EVENT_ERROR) && ilclient_remove_event(comp, OMX_EventError, 0, 1, 1, 0) >= 0) + return; + + if((set & ILCLIENT_PORT_DISABLED) && ilclient_remove_event(comp, OMX_EventCmdComplete, OMX_CommandPortDisable, 0, portIndex, 0) >= 0) + return; + } + } + + if(ilclient_wait_for_command_complete(comp, OMX_CommandPortDisable, portIndex) < 0) + vc_assert(0); +} + + +/*********************************************************** + * Name: ilclient_setup_tunnel + * + * Description: creates a tunnel between components that require that + * ports be inititially disabled, then enabled after tunnel setup. If + * timeout is non-zero, it will initially wait until a port settings + * changes message has been received by the output port. If port + * streams are supported by the output port, the requested port stream + * will be selected. + * + * Returns: 0 indicates success, negative indicates failure. + * -1: a timeout waiting for the parameter changed + * -2: an error was returned instead of parameter changed + * -3: no streams are available from this port + * -4: requested stream is not available from this port + * -5: the data format was not acceptable to the sink + ***********************************************************/ +int ilclient_setup_tunnel(TUNNEL_T *tunnel, unsigned int portStream, int timeout) +{ + OMX_ERRORTYPE error; + OMX_PARAM_U32TYPE param; + OMX_STATETYPE state; + int32_t status; + int enable_error; + + // source component must at least be idle, not loaded + error = OMX_GetState(tunnel->source->comp, &state); + vc_assert(error == OMX_ErrorNone); + if (state == OMX_StateLoaded && ilclient_change_component_state(tunnel->source, OMX_StateIdle) < 0) + return -2; + + // wait for the port parameter changed from the source port + if(timeout) + { + status = ilclient_wait_for_event(tunnel->source, OMX_EventPortSettingsChanged, + tunnel->source_port, 0, -1, 1, + ILCLIENT_PARAMETER_CHANGED | ILCLIENT_EVENT_ERROR, timeout); + + if (status < 0) + { + ilclient_debug_output( + "ilclient: timed out waiting for port settings changed on port %d", tunnel->source_port); + return status; + } + } + + // disable ports + ilclient_disable_tunnel(tunnel); + + // if this source port uses port streams, we need to select one of them before proceeding + // if getparameter causes an error that's fine, nothing needs selecting + param.nSize = sizeof(OMX_PARAM_U32TYPE); + param.nVersion.nVersion = OMX_VERSION; + param.nPortIndex = tunnel->source_port; + if (OMX_GetParameter(tunnel->source->comp, OMX_IndexParamNumAvailableStreams, &param) == OMX_ErrorNone) + { + if (param.nU32 == 0) + { + // no streams available + // leave the source port disabled, and return a failure + return -3; + } + if (param.nU32 <= portStream) + { + // requested stream not available + // no streams available + // leave the source port disabled, and return a failure + return -4; + } + + param.nU32 = portStream; + error = OMX_SetParameter(tunnel->source->comp, OMX_IndexParamActiveStream, &param); + vc_assert(error == OMX_ErrorNone); + } + + // now create the tunnel + error = OMX_SetupTunnel(tunnel->source->comp, tunnel->source_port, tunnel->sink->comp, tunnel->sink_port); + + enable_error = 0; + + if (error != OMX_ErrorNone || (enable_error=ilclient_enable_tunnel(tunnel)) < 0) + { + // probably format not compatible + error = OMX_SetupTunnel(tunnel->source->comp, tunnel->source_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + error = OMX_SetupTunnel(tunnel->sink->comp, tunnel->sink_port, NULL, 0); + vc_assert(error == OMX_ErrorNone); + + if(enable_error) + { + //Clean up the errors. This does risk removing an error that was nothing to do with this tunnel :-/ + ilclient_remove_event(tunnel->sink, OMX_EventError, 0, 1, 0, 1); + ilclient_remove_event(tunnel->source, OMX_EventError, 0, 1, 0, 1); + } + + ilclient_debug_output("ilclient: could not setup/enable tunnel (setup=0x%x,enable=%d)", + error, enable_error); + return -5; + } + + return 0; +} + +/*********************************************************** + * Name: ilclient_wait_for_event + * + * Description: waits for a given event to appear on a component event + * list. If not immediately present, will wait on that components + * event group for the given event flag. + * + * Returns: 0 indicates success, negative indicates failure. + * -1: a timeout was received. + * -2: an error event was received. + * -3: a config change event was received. + ***********************************************************/ +int ilclient_wait_for_event(COMPONENT_T *comp, OMX_EVENTTYPE event, + OMX_U32 nData1, int ignore1, OMX_IN OMX_U32 nData2, int ignore2, + int event_flag, int suspend) +{ + int32_t status; + uint32_t set; + + while (ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) < 0) + { + // if we want to be notified of errors, check the list for an error now + // before blocking, the event flag may have been cleared already. + if(event_flag & ILCLIENT_EVENT_ERROR) + { + ILEVENT_T *cur; + ilclient_lock_events(comp->client); + cur = comp->list; + while(cur && cur->eEvent != OMX_EventError) + cur = cur->next; + + if(cur) + { + // clear error flag + vcos_event_flags_get(&comp->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + ilclient_unlock_events(comp->client); + return -2; + } + + ilclient_unlock_events(comp->client); + } + // check for config change event if we are asked to be notified of that + if(event_flag & ILCLIENT_CONFIG_CHANGED) + { + ILEVENT_T *cur; + ilclient_lock_events(comp->client); + cur = comp->list; + while(cur && cur->eEvent != OMX_EventParamOrConfigChanged) + cur = cur->next; + + ilclient_unlock_events(comp->client); + + if(cur) + return ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) == 0 ? 0 : -3; + } + + status = vcos_event_flags_get(&comp->event, event_flag, VCOS_OR_CONSUME, + suspend, &set); + if (status != 0) + return -1; + if (set & ILCLIENT_EVENT_ERROR) + return -2; + if (set & ILCLIENT_CONFIG_CHANGED) + return ilclient_remove_event(comp, event, nData1, ignore1, nData2, ignore2) == 0 ? 0 : -3; + } + + return 0; +} + + + +/*********************************************************** + * Name: ilclient_wait_for_command_complete_dual + * + * Description: Waits for an event signalling command completion. In + * this version we may also return failure if there is an error event + * that has terminated a command on a second component. + * + * Returns: 0 on success, -1 on failure of comp, -2 on failure of other + ***********************************************************/ +int ilclient_wait_for_command_complete_dual(COMPONENT_T *comp, OMX_COMMANDTYPE command, OMX_U32 nData2, COMPONENT_T *other) +{ + OMX_U32 mask = ILCLIENT_EVENT_ERROR; + int ret = 0; + + switch(command) { + case OMX_CommandStateSet: mask |= ILCLIENT_STATE_CHANGED; break; + case OMX_CommandPortDisable: mask |= ILCLIENT_PORT_DISABLED; break; + case OMX_CommandPortEnable: mask |= ILCLIENT_PORT_ENABLED; break; + default: return -1; + } + + if(other) + other->related = comp; + + while(1) + { + ILEVENT_T *cur, *prev = NULL; + VCOS_UNSIGNED set; + + ilclient_lock_events(comp->client); + + cur = comp->list; + while(cur && + !(cur->eEvent == OMX_EventCmdComplete && cur->nData1 == command && cur->nData2 == nData2) && + !(cur->eEvent == OMX_EventError && cur->nData2 == 1)) + { + prev = cur; + cur = cur->next; + } + + if(cur) + { + if(prev == NULL) + comp->list = cur->next; + else + prev->next = cur->next; + + // work out whether this was a success or a fail event + ret = cur->eEvent == OMX_EventCmdComplete || cur->nData1 == OMX_ErrorSameState ? 0 : -1; + + if(cur->eEvent == OMX_EventError) + vcos_event_flags_get(&comp->event, ILCLIENT_EVENT_ERROR, VCOS_OR_CONSUME, 0, &set); + + // add back into spare list + cur->next = comp->client->event_list; + comp->client->event_list = cur; + cur->eEvent = -1; // mark as unused + + ilclient_unlock_events(comp->client); + break; + } + else if(other != NULL) + { + // check the other component for an error event that terminates a command + cur = other->list; + while(cur && !(cur->eEvent == OMX_EventError && cur->nData2 == 1)) + cur = cur->next; + + if(cur) + { + // we don't remove the event in this case, since the user + // can confirm that this event errored by calling wait_for_command on the + // other component + + ret = -2; + ilclient_unlock_events(comp->client); + break; + } + } + + ilclient_unlock_events(comp->client); + + vcos_event_flags_get(&comp->event, mask, VCOS_OR_CONSUME, VCOS_SUSPEND, &set); + } + + if(other) + other->related = NULL; + + return ret; +} + + +/*********************************************************** + * Name: ilclient_wait_for_command_complete + * + * Description: Waits for an event signalling command completion. + * + * Returns: 0 on success, -1 on failure. + ***********************************************************/ +int ilclient_wait_for_command_complete(COMPONENT_T *comp, OMX_COMMANDTYPE command, OMX_U32 nData2) +{ + return ilclient_wait_for_command_complete_dual(comp, command, nData2, NULL); +} + +/*********************************************************** + * Name: ilclient_get_output_buffer + * + * Description: Returns an output buffer returned from a component + * using the OMX_FillBufferDone callback from the output list for the + * given component and port index. + * + * Returns: pointer to buffer if available, otherwise NULL + ***********************************************************/ +OMX_BUFFERHEADERTYPE *ilclient_get_output_buffer(COMPONENT_T *comp, int portIndex, int block) +{ + OMX_BUFFERHEADERTYPE *ret = NULL, *prev = NULL; + VCOS_UNSIGNED set; + + do { + vcos_semaphore_wait(&comp->sema); + ret = comp->out_list; + while(ret != NULL && ret->nOutputPortIndex != portIndex) + { + prev = ret; + ret = ret->pAppPrivate; + } + + if(ret) + { + if(prev == NULL) + comp->out_list = ret->pAppPrivate; + else + prev->pAppPrivate = ret->pAppPrivate; + + ret->pAppPrivate = NULL; + } + vcos_semaphore_post(&comp->sema); + + if(block && !ret) + vcos_event_flags_get(&comp->event, ILCLIENT_FILL_BUFFER_DONE, VCOS_OR_CONSUME, -1, &set); + + } while(block && !ret); + + return ret; +} + +/*********************************************************** + * Name: ilclient_get_input_buffer + * + * Description: Returns an input buffer return from a component using + * the OMX_EmptyBufferDone callback from the output list for the given + * component and port index. + * + * Returns: pointer to buffer if available, otherwise NULL + ***********************************************************/ +OMX_BUFFERHEADERTYPE *ilclient_get_input_buffer(COMPONENT_T *comp, int portIndex, int block) +{ + OMX_BUFFERHEADERTYPE *ret = NULL, *prev = NULL; + + do { + VCOS_UNSIGNED set; + + vcos_semaphore_wait(&comp->sema); + ret = comp->in_list; + while(ret != NULL && ret->nInputPortIndex != portIndex) + { + prev = ret; + ret = ret->pAppPrivate; + } + + if(ret) + { + if(prev == NULL) + comp->in_list = ret->pAppPrivate; + else + prev->pAppPrivate = ret->pAppPrivate; + + ret->pAppPrivate = NULL; + } + vcos_semaphore_post(&comp->sema); + + if(block && !ret) + vcos_event_flags_get(&comp->event, ILCLIENT_EMPTY_BUFFER_DONE, VCOS_OR_CONSUME, -1, &set); + + } while(block && !ret); + + return ret; +} + +/*********************************************************** + * Name: ilclient_debug_output + * + * Description: prints a varg message to the log or the debug screen + * under win32 + * + * Returns: void + ***********************************************************/ +void ilclient_debug_output(char *format, ...) +{ + va_list args; + + va_start(args, format); + vcos_vlog_info(format, args); + va_end(args); +} + +/****************************************************************************** +Static functions +******************************************************************************/ + +/*********************************************************** + * Name: ilclient_lock_events + * + * Description: locks the client event structure + * + * Returns: void + ***********************************************************/ +static void ilclient_lock_events(ILCLIENT_T *st) +{ + vcos_semaphore_wait(&st->event_sema); +} + +/*********************************************************** + * Name: ilclient_unlock_events + * + * Description: unlocks the client event structure + * + * Returns: void + ***********************************************************/ +static void ilclient_unlock_events(ILCLIENT_T *st) +{ + vcos_semaphore_post(&st->event_sema); +} + +/*********************************************************** + * Name: ilclient_event_handler + * + * Description: event handler passed to core to use as component + * callback + * + * Returns: success + ***********************************************************/ +static OMX_ERRORTYPE ilclient_event_handler(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_EVENTTYPE eEvent, + OMX_IN OMX_U32 nData1, + OMX_IN OMX_U32 nData2, + OMX_IN OMX_PTR pEventData) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + ILEVENT_T *event; + OMX_ERRORTYPE error = OMX_ErrorNone; + + ilclient_lock_events(st->client); + + // go through the events on this component and remove any duplicates in the + // existing list, since the client probably doesn't need them. it's better + // than asserting when we run out. + event = st->list; + while(event != NULL) + { + ILEVENT_T **list = &(event->next); + while(*list != NULL) + { + if((*list)->eEvent == event->eEvent && + (*list)->nData1 == event->nData1 && + (*list)->nData2 == event->nData2) + { + // remove this duplicate + ILEVENT_T *rem = *list; + ilclient_debug_output("%s: removing %d/%d/%d", st->name, event->eEvent, event->nData1, event->nData2); + *list = rem->next; + rem->eEvent = -1; + rem->next = st->client->event_list; + st->client->event_list = rem; + } + else + list = &((*list)->next); + } + + event = event->next; + } + + vc_assert(st->client->event_list); + event = st->client->event_list; + + switch (eEvent) { + case OMX_EventCmdComplete: + switch (nData1) { + case OMX_CommandStateSet: + ilclient_debug_output("%s: callback state changed (%s)", st->name, states[nData2]); + vcos_event_flags_set(&st->event, ILCLIENT_STATE_CHANGED, VCOS_OR); + break; + case OMX_CommandPortDisable: + ilclient_debug_output("%s: callback port disable %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_DISABLED, VCOS_OR); + break; + case OMX_CommandPortEnable: + ilclient_debug_output("%s: callback port enable %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_ENABLED, VCOS_OR); + break; + case OMX_CommandFlush: + ilclient_debug_output("%s: callback port flush %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_PORT_FLUSH, VCOS_OR); + break; + case OMX_CommandMarkBuffer: + ilclient_debug_output("%s: callback mark buffer %d", st->name, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_MARKED_BUFFER, VCOS_OR); + break; + default: + vc_assert(0); + } + break; + case OMX_EventError: + { + // check if this component failed a command, and we have to notify another command + // of this failure + if(nData2 == 1 && st->related != NULL) + vcos_event_flags_set(&st->related->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + + error = nData1; + switch (error) { + case OMX_ErrorPortUnpopulated: + if (st->error_mask & ILCLIENT_ERROR_UNPOPULATED) + { + ilclient_debug_output("%s: ignore error: port unpopulated (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: port unpopulated %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorSameState: + if (st->error_mask & ILCLIENT_ERROR_SAMESTATE) + { + ilclient_debug_output("%s: ignore error: same state (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: same state %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorBadParameter: + if (st->error_mask & ILCLIENT_ERROR_BADPARAMETER) + { + ilclient_debug_output("%s: ignore error: bad parameter (%d)", st->name, nData2); + event = NULL; + break; + } + ilclient_debug_output("%s: bad parameter %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorIncorrectStateTransition: + ilclient_debug_output("%s: incorrect state transition %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorBadPortIndex: + ilclient_debug_output("%s: bad port index %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorStreamCorrupt: + ilclient_debug_output("%s: stream corrupt %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorInsufficientResources: + ilclient_debug_output("%s: insufficient resources %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorUnsupportedSetting: + ilclient_debug_output("%s: unsupported setting %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorOverflow: + ilclient_debug_output("%s: overflow %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDiskFull: + ilclient_debug_output("%s: disk full %x (%d)", st->name, error, nData2); + //we do not set the error + break; + case OMX_ErrorMaxFileSize: + ilclient_debug_output("%s: max file size %x (%d)", st->name, error, nData2); + //we do not set the error + break; + case OMX_ErrorDrmUnauthorised: + ilclient_debug_output("%s: drm file is unauthorised %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDrmExpired: + ilclient_debug_output("%s: drm file has expired %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + case OMX_ErrorDrmGeneral: + ilclient_debug_output("%s: drm library error %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + default: + vc_assert(0); + ilclient_debug_output("%s: unexpected error %x (%d)", st->name, error, nData2); + vcos_event_flags_set(&st->event, ILCLIENT_EVENT_ERROR, VCOS_OR); + break; + } + } + break; + case OMX_EventBufferFlag: + ilclient_debug_output("%s: buffer flag %d/%x", st->name, nData1, nData2); + if (nData2 & OMX_BUFFERFLAG_EOS) + { + vcos_event_flags_set(&st->event, ILCLIENT_BUFFER_FLAG_EOS, VCOS_OR); + nData2 = OMX_BUFFERFLAG_EOS; + } + else + vc_assert(0); + break; + case OMX_EventPortSettingsChanged: + ilclient_debug_output("%s: port settings changed %d", st->name, nData1); + vcos_event_flags_set(&st->event, ILCLIENT_PARAMETER_CHANGED, VCOS_OR); + break; + case OMX_EventMark: + ilclient_debug_output("%s: buffer mark %p", st->name, pEventData); + vcos_event_flags_set(&st->event, ILCLIENT_BUFFER_MARK, VCOS_OR); + break; + case OMX_EventParamOrConfigChanged: + ilclient_debug_output("%s: param/config 0x%X on port %d changed", st->name, nData2, nData1); + vcos_event_flags_set(&st->event, ILCLIENT_CONFIG_CHANGED, VCOS_OR); + break; + default: + vc_assert(0); + break; + } + + if (event) + { + // fill in details + event->eEvent = eEvent; + event->nData1 = nData1; + event->nData2 = nData2; + event->pEventData = pEventData; + + // remove from top of spare list + st->client->event_list = st->client->event_list->next; + + // put at head of component event queue + event->next = st->list; + st->list = event; + } + ilclient_unlock_events(st->client); + + // now call any callbacks without the event lock so the client can + // remove the event in context + switch(eEvent) { + case OMX_EventError: + if(event && st->client->error_callback) + st->client->error_callback(st->client->error_callback_data, st, error); + break; + case OMX_EventBufferFlag: + if ((nData2 & OMX_BUFFERFLAG_EOS) && st->client->eos_callback) + st->client->eos_callback(st->client->eos_callback_data, st, nData1); + break; + case OMX_EventPortSettingsChanged: + if (st->client->port_settings_callback) + st->client->port_settings_callback(st->client->port_settings_callback_data, st, nData1); + break; + case OMX_EventParamOrConfigChanged: + if (st->client->configchanged_callback) + st->client->configchanged_callback(st->client->configchanged_callback_data, st, nData2); + break; + default: + // ignore + break; + } + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_empty_buffer_done + * + * Description: passed to core to use as component callback, puts + * buffer on list + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_empty_buffer_done(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + OMX_BUFFERHEADERTYPE *list; + + ilclient_debug_output("%s: empty buffer done %p", st->name, pBuffer); + + vcos_semaphore_wait(&st->sema); + // insert at end of the list, so we process buffers in + // the same order + list = st->in_list; + while(list && list->pAppPrivate) + list = list->pAppPrivate; + + if(!list) + st->in_list = pBuffer; + else + list->pAppPrivate = pBuffer; + + pBuffer->pAppPrivate = NULL; + vcos_semaphore_post(&st->sema); + + vcos_event_flags_set(&st->event, ILCLIENT_EMPTY_BUFFER_DONE, VCOS_OR); + + if (st->client->empty_buffer_done_callback) + st->client->empty_buffer_done_callback(st->client->empty_buffer_done_callback_data, st); + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_empty_buffer_done_error + * + * Description: passed to core to use as component callback, asserts + * on use as client not expecting component to use this callback. + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_empty_buffer_done_error(OMX_IN OMX_HANDLETYPE hComponent, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) +{ + vc_assert(0); + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_fill_buffer_done + * + * Description: passed to core to use as component callback, puts + * buffer on list + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_fill_buffer_done(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) +{ + COMPONENT_T *st = (COMPONENT_T *) pAppData; + OMX_BUFFERHEADERTYPE *list; + + ilclient_debug_output("%s: fill buffer done %p", st->name, pBuffer); + + vcos_semaphore_wait(&st->sema); + // insert at end of the list, so we process buffers in + // the correct order + list = st->out_list; + while(list && list->pAppPrivate) + list = list->pAppPrivate; + + if(!list) + st->out_list = pBuffer; + else + list->pAppPrivate = pBuffer; + + pBuffer->pAppPrivate = NULL; + vcos_semaphore_post(&st->sema); + + vcos_event_flags_set(&st->event, ILCLIENT_FILL_BUFFER_DONE, VCOS_OR); + + if (st->client->fill_buffer_done_callback) + st->client->fill_buffer_done_callback(st->client->fill_buffer_done_callback_data, st); + + return OMX_ErrorNone; +} + +/*********************************************************** + * Name: ilclient_fill_buffer_done_error + * + * Description: passed to core to use as component callback, asserts + * on use as client not expecting component to use this callback. + * + * Returns: + ***********************************************************/ +static OMX_ERRORTYPE ilclient_fill_buffer_done_error(OMX_OUT OMX_HANDLETYPE hComponent, + OMX_OUT OMX_PTR pAppData, + OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) +{ + vc_assert(0); + return OMX_ErrorNone; +} + + + +OMX_HANDLETYPE ilclient_get_handle(COMPONENT_T *comp) +{ + vcos_assert(comp); + return comp->comp; +} + + +static struct { + OMX_PORTDOMAINTYPE dom; + int param; +} port_types[] = { + { OMX_PortDomainVideo, OMX_IndexParamVideoInit }, + { OMX_PortDomainAudio, OMX_IndexParamAudioInit }, + { OMX_PortDomainImage, OMX_IndexParamImageInit }, + { OMX_PortDomainOther, OMX_IndexParamOtherInit }, +}; + +int ilclient_get_port_index(COMPONENT_T *comp, OMX_DIRTYPE dir, OMX_PORTDOMAINTYPE type, int index) +{ + uint32_t i; + // for each possible port type... + for (i=0; i<sizeof(port_types)/sizeof(port_types[0]); i++) + { + if ((port_types[i].dom == type) || (type == (OMX_PORTDOMAINTYPE) -1)) + { + OMX_PORT_PARAM_TYPE param; + OMX_ERRORTYPE error; + uint32_t j; + + param.nSize = sizeof(param); + param.nVersion.nVersion = OMX_VERSION; + error = OMX_GetParameter(ILC_GET_HANDLE(comp), port_types[i].param, &param); + assert(error == OMX_ErrorNone); + + // for each port of this type... + for (j=0; j<param.nPorts; j++) + { + int port = param.nStartPortNumber+j; + + OMX_PARAM_PORTDEFINITIONTYPE portdef; + portdef.nSize = sizeof(portdef); + portdef.nVersion.nVersion = OMX_VERSION; + portdef.nPortIndex = port; + + error = OMX_GetParameter(ILC_GET_HANDLE(comp), OMX_IndexParamPortDefinition, &portdef); + assert(error == OMX_ErrorNone); + + if (portdef.eDir == dir) + { + if (index-- == 0) + return port; + } + } + } + } + return -1; +} + +int ilclient_suggest_bufsize(COMPONENT_T *comp, OMX_U32 nBufSizeHint) +{ + OMX_PARAM_BRCMOUTPUTBUFFERSIZETYPE param; + OMX_ERRORTYPE error; + + param.nSize = sizeof(param); + param.nVersion.nVersion = OMX_VERSION; + param.nBufferSize = nBufSizeHint; + error = OMX_SetParameter(ILC_GET_HANDLE(comp), OMX_IndexParamBrcmOutputBufferSize, + &param); + assert(error == OMX_ErrorNone); + + return (error == OMX_ErrorNone) ? 0 : -1; +} + +unsigned int ilclient_stack_size(void) +{ + return ILCLIENT_THREAD_DEFAULT_STACK_SIZE; +} + diff --git a/gui/libs/ilclient/ilclient.h b/gui/libs/ilclient/ilclient.h @@ -0,0 +1,1039 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file + * + * \brief This API defines helper functions for writing IL clients. + * + * This file defines an IL client side library. This is useful when + * writing IL clients, since there tends to be much repeated and + * common code across both single and multiple clients. This library + * seeks to remove that common code and abstract some of the + * interactions with components. There is a wrapper around a + * component and tunnel, and some operations can be done on lists of + * these. The callbacks from components are handled, and specific + * events can be checked or waited for. + */ + +#ifndef _IL_CLIENT_H +#define _IL_CLIENT_H + +#include "IL/OMX_Broadcom.h" +#include "interface/vcos/vcos.h" + +/** + * The <DFN>ILCLIENT_T</DFN> structure encapsulates the state needed for the IL + * Client API. It contains a set of callback functions used to + * communicate with the user. It also includes a linked list of free + * event structures. + ***********************************************************/ +typedef struct _ILCLIENT_T ILCLIENT_T; + + +/** + * Each <DFN>ILEVENT_T</DFN> structure stores the result of an <DFN>EventHandler</DFN> + * callback from a component, storing the event message type and any + * parameters returned. + ***********************************************************/ +typedef struct _ILEVENT_T ILEVENT_T; + + + +struct _COMPONENT_T; + +/** + * The <DFN>COMPONENT_T</DFN> structure represents an IL component, + * together with the necessary extra information required by the IL + * Client API. This structure stores the handle to the OMX component, + * as well as the event list containing all events sent by this + * component. The component state structure also holds a pair of + * buffer queues, for input and output buffers returned to the client + * by the <DFN>FillBufferDone</DFN> and <DFN>EmptyBufferDone</DFN> + * callbacks. As some operations result in error callbacks that can + * be ignored, an error mask is maintained to allow some errors to be + * ignored. A pointer to the client state structure is also added. + ***********************************************************/ +typedef struct _COMPONENT_T COMPONENT_T; + + +/** + * The generic callback function is used for communicating events from + * a particular component to the user. + * + * @param userdata The data returned from when the callback was registered. + * + * @param comp The component structure representing the component that + * originated this event. + * + * @param data The relevant data field from the event. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_CALLBACK_T)(void *userdata, COMPONENT_T *comp, OMX_U32 data); + + +/** + * The buffer callback function is used for indicating that a + * component has returned a buffer on a port using client buffer + * communication. + * + * @param data The data returned from when the callback was registered. + * + * @param comp The component from which the buffer originated. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_BUFFER_CALLBACK_T)(void *data, COMPONENT_T *comp); + + +/** + * The malloc function is passed into + * <DFN>ilclient_enable_port_buffers()</DFN> and used for allocating the + * buffer payload. + * + * @param userdata Private pointer passed into + * <DFN>ilclient_enable_port_buffers()</DFN> call. + * + * @param size Size in bytes of the requested memory region. + * + * @param align Alignment requirement in bytes for the base memory address. + * + * @param description Text description of the memory being allocated. + * + * @return The memory address on success, <DFN>NULL</DFN> on failure. + ***********************************************************/ +typedef void *(*ILCLIENT_MALLOC_T)(void *userdata, VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *description); + + +/** + * The free function is passed into + * <DFN>ilclient_enable_port_buffers()</DFN> and + * <DFN>ilclient_disable_port_buffers()</DFN> and used for freeing the + * buffer payload. + * + * @param userdata Private pointer passed into + * <DFN>ilclient_enable_port_buffers()</DFN> and + * <DFN>ilclient_disable_port_buffers()</DFN>. + * + * @param pointer Memory address to free, that was previously returned + * from <DFN>ILCLIENT_MALLOC_T</DFN> function. + * + * @return Void. + ***********************************************************/ +typedef void (*ILCLIENT_FREE_T)(void *userdata, void *pointer); + + +/** + * The event mask enumeration describes the possible events that the + * user can ask to wait for when waiting for a particular event. + ***********************************************************/ +typedef enum { + ILCLIENT_EMPTY_BUFFER_DONE = 0x1, /**< Set when a buffer is + returned from an input + port */ + + ILCLIENT_FILL_BUFFER_DONE = 0x2, /**< Set when a buffer is + returned from an output + port */ + + ILCLIENT_PORT_DISABLED = 0x4, /**< Set when a port indicates + it has completed a disable + command. */ + + ILCLIENT_PORT_ENABLED = 0x8, /**< Set when a port indicates + is has completed an enable + command. */ + + ILCLIENT_STATE_CHANGED = 0x10, /**< Set when a component + indicates it has completed + a state change command. */ + + ILCLIENT_BUFFER_FLAG_EOS = 0x20, /**< Set when a port signals + an EOS event. */ + + ILCLIENT_PARAMETER_CHANGED = 0x40, /**< Set when a port signals a + port settings changed + event. */ + + ILCLIENT_EVENT_ERROR = 0x80, /**< Set when a component + indicates an error. */ + + ILCLIENT_PORT_FLUSH = 0x100, /**< Set when a port indicates + is has completed a flush + command. */ + + ILCLIENT_MARKED_BUFFER = 0x200, /**< Set when a port indicates + it has marked a buffer. */ + + ILCLIENT_BUFFER_MARK = 0x400, /**< Set when a port indicates + it has received a buffer + mark. */ + + ILCLIENT_CONFIG_CHANGED = 0x800 /**< Set when a config parameter + changed. */ +} ILEVENT_MASK_T; + + +/** + * On component creation the user can set flags to control the + * creation of that component. + ***********************************************************/ +typedef enum { + ILCLIENT_FLAGS_NONE = 0x0, /**< Used if no flags are + set. */ + + ILCLIENT_ENABLE_INPUT_BUFFERS = 0x1, /**< If set we allow the + client to communicate with + input ports via buffer + communication, rather than + tunneling with another + component. */ + + ILCLIENT_ENABLE_OUTPUT_BUFFERS = 0x2, /**< If set we allow the + client to communicate with + output ports via buffer + communication, rather than + tunneling with another + component. */ + + ILCLIENT_DISABLE_ALL_PORTS = 0x4, /**< If set we disable all + ports on creation. */ + + ILCLIENT_HOST_COMPONENT = 0x8, /**< Create a host component. + The default host ilcore + only can create host components + by being locally hosted + so should only be used for testing + purposes. */ + + ILCLIENT_OUTPUT_ZERO_BUFFERS = 0x10 /**< All output ports will have + nBufferCountActual set to zero, + if supported by the component. */ +} ILCLIENT_CREATE_FLAGS_T; + + +/** + * \brief This structure represents a tunnel in the OpenMAX IL API. + * + * Some operations in this API act on a tunnel, so the tunnel state + * structure (<DFN>TUNNEL_T</DFN>) is a convenient store of the source and sink + * of the tunnel. For each, a pointer to the relevant component state + * structure and the port index is stored. + ***********************************************************/ +typedef struct { + COMPONENT_T *source; /**< The source component */ + int source_port; /**< The output port index on the source component */ + COMPONENT_T *sink; /**< The sink component */ + int sink_port; /**< The input port index on the sink component */ +} TUNNEL_T; + + +/** + * The <DFN>set_tunnel</DFN> macro is a useful function that initialises a + * <DFN>TUNNEL_T</DFN> structure. + ***********************************************************/ +#define set_tunnel(t,a,b,c,d) do {TUNNEL_T *_ilct = (t); \ + _ilct->source = (a); _ilct->source_port = (b); \ + _ilct->sink = (c); _ilct->sink_port = (d);} while(0) + +/** + * For calling OpenMAX IL methods directory, we need to access the + * <DFN>OMX_HANDLETYPE</DFN> corresponding to the <DFN>COMPONENT_T</DFN> structure. This + * macro enables this while keeping the <DFN>COMPONENT_T</DFN> structure opaque. + * The parameter <DFN>x</DFN> should be of the type <DFN>*COMPONENT_T</DFN>. + ***********************************************************/ +#define ILC_GET_HANDLE(x) ilclient_get_handle(x) + +/** + * An IL Client structure is created by the <DFN>ilclient_init()</DFN> + * method. This structure is used when creating components, but + * otherwise is not needed in other API functions as a pointer to this + * structure is maintained in the <DFN>COMPONENT_T</DFN> structure. + * + * @return pointer to client structure + ***********************************************************/ +VCHPRE_ ILCLIENT_T VCHPOST_ *ilclient_init(void); + +/** + * When all components have been deleted, the IL Client structure can + * be destroyed by calling the <DFN>ilclient_destroy()</DFN> function. + * + * @param handle The client handle. After calling this function, this + * handle should not be used. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_destroy(ILCLIENT_T *handle); + +/** + * The <DFN>ilclient_set_port_settings_callback()</DFN> function registers a + * callback to be used when the <DFN>OMX_EventPortSettingsChanged</DFN> event is + * received. When the event is received, a pointer to the component + * structure and port index is returned by the callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a <DFN>NULL</DFN> function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_port_settings_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The <DFN>ilclient_set_eos_callback()</DFN> function registers a callback to be + * used when the <DFN>OMX_EventBufferFlag</DFN> is received with the + * <DFN>OMX_BUFFERFLAG_EOS</DFN> flag set. When the event is received, a pointer + * to the component structure and port index is returned by the + * callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a <DFN>NULL</DFN> function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_eos_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The <DFN>ilclient_set_error_callback()</DFN> function registers a callback to be + * used when the <DFN>OMX_EventError</DFN> is received from a component. When + * the event is received, a pointer to the component structure and the + * error code are reported by the callback. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a <DFN>NULL</DFN> function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_error_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + +/** + * The <DFN>ilclient_set_configchanged_callback()</DFN> function + * registers a callback to be used when an + * <DFN>OMX_EventParamOrConfigChanged</DFN> event occurs. When the + * event is received, a pointer to the component structure and the + * index value that has changed are reported by the callback. The + * user may then use an <DFN>OMX_GetConfig</DFN> call with the index + * as specified to retrieve the updated information. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a <DFN>NULL</DFN> function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_configchanged_callback(ILCLIENT_T *handle, + ILCLIENT_CALLBACK_T func, + void *userdata); + + +/** + * The <DFN>ilclient_set_fill_buffer_done_callback()</DFN> function registers a + * callback to be used when a buffer passed to an output port using the + * <DFN>OMX_FillBuffer</DFN> call is returned with the <DFN>OMX_FillBufferDone</DFN> + * callback. When the event is received, a pointer to the component + * structure is returned by the callback. The user may then use the + * <DFN>ilclient_get_output_buffer()</DFN> function to retrieve the buffer. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a <DFN>NULL</DFN> function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_fill_buffer_done_callback(ILCLIENT_T *handle, + ILCLIENT_BUFFER_CALLBACK_T func, + void *userdata); + +/** + * The <DFN>ilclient_set_empty_buffer_done_callback()</DFN> function registers a + * callback to be used when a buffer passed to an input port using the + * <DFN>OMX_EmptyBuffer</DFN> call is returned with the <DFN>OMX_EmptyBufferDone</DFN> + * callback. When the event is received, a pointer to the component + * structure is returned by the callback. The user may then use the + * <DFN>ilclient_get_input_buffer()</DFN> function to retrieve the buffer. + * + * @param handle The client handle + * + * @param func The callback function to use. Calling this function + * with a <DFN>NULL</DFN> function pointer will deregister any existing + * registered callback. + * + * @param userdata Data to be passed back when calling the callback + * function. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_set_empty_buffer_done_callback(ILCLIENT_T *handle, + ILCLIENT_BUFFER_CALLBACK_T func, + void *userdata); + + +/** + * Components are created using the <DFN>ilclient_create_component()</DFN> + * function. + * + * @param handle The client handle + * + * @param comp On successful creation, the component structure pointer + * will be written back into <DFN>comp</DFN>. + * + * @param name The name of the component to be created. Component + * names as provided are automatically prefixed with + * <DFN>"OMX.broadcom."</DFN> before passing to the IL core. The name + * provided will also be used in debugging messages added about this + * component. + * + * @param flags The client can specify some creation behaviour by using + * the <DFN>flags</DFN> field. The meaning of each flag is defined + * by the <DFN>ILCLIENT_CREATE_FLAGS_T</DFN> type. + * + * @return 0 on success, -1 on failure + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_create_component(ILCLIENT_T *handle, + COMPONENT_T **comp, + char *name, + ILCLIENT_CREATE_FLAGS_T flags); + +/** + * The <DFN>ilclient_cleanup_components()</DFN> function deallocates all + * state associated with components and frees the OpenMAX component + * handles. All tunnels connecting components should have been torn + * down explicitly, and all components must be in loaded state. + * + * @param list A null-terminated list of component pointers to be + * deallocated. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_cleanup_components(COMPONENT_T *list[]); + + +/** + * The <DFN>ilclient_change_component_state()</DFN> function changes the + * state of an individual component. This will trigger the state + * change, and also wait for that state change to be completed. It + * should not be called if this state change has dependencies on other + * components also changing states. Trying to change a component to + * its current state is treated as success. + * + * @param comp The component to change. + * + * @param state The new state to transition to. + * + * @return 0 on success, -1 on failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_change_component_state(COMPONENT_T *comp, + OMX_STATETYPE state); + + +/** + * The <DFN>ilclient_state_transition()</DFN> function transitions a set of + * components that need to perform a simultaneous state transition; + * for example, when two components are tunnelled and the buffer + * supplier port needs to allocate and pass buffers to a non-supplier + * port. All components are sent a command to change state, then the + * function will wait for all components to signal that they have + * changed state. + * + * @param list A null-terminated list of component pointers. + * + * @param state The new state to which to transition all components. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_state_transition(COMPONENT_T *list[], + OMX_STATETYPE state); + + +/** + * The <DFN>ilclient_disable_port()</DFN> function disables a port on a + * given component. This function sends the disable port message to + * the component and waits for the component to signal that this has + * taken place. If the port is already disabled, this is treated as a + * success. + * + * @param comp The component containing the port to disable. + * + * @param portIndex The port index of the port to disable. This must + * be a named port index, rather than a <DFN>OMX_ALL</DFN> value. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_disable_port(COMPONENT_T *comp, + int portIndex); + + +/** + * The <DFN>ilclient_enable_port()</DFN> function enables a port on a + * given component. This function sends the enable port message to + * the component and waits for the component to signal that this has + * taken place. If the port is already disabled, this is treated as a + * success. + * + * @param comp The component containing the port to enable. + * + * @param portIndex The port index of the port to enable. This must + * be a named port index, rather than a <DFN>OMX_ALL</DFN> value. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_enable_port(COMPONENT_T *comp, + int portIndex); + + + +/** + * The <DFN>ilclient_enable_port_buffers()</DFN> function enables a port + * in base profile mode on a given component. The port is not + * tunneled, so requires buffers to be allocated. + * + * @param comp The component containing the port to enable. + * + * @param portIndex The port index of the port to enable. This must + * be a named port index, rather than a <DFN>OMX_ALL</DFN> value. + * + * @param ilclient_malloc This function will be used to allocate + * buffer payloads. If <DFN>NULL</DFN> then + * <DFN>vcos_malloc_aligned</DFN> will be used. + * + * @param ilclient_free If an error occurs, this function is used to + * free previously allocated payloads. If <DFN>NULL</DFN> then + * <DFN>vcos_free</DFN> will be used. + * + * @param userdata The first argument to calls to + * <DFN>ilclient_malloc</DFN> and <DFN>ilclient_free</DFN>, if these + * arguments are not <DFN>NULL</DFN>. + * + * @return 0 indicates success. -1 indicates failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_enable_port_buffers(COMPONENT_T *comp, + int portIndex, + ILCLIENT_MALLOC_T ilclient_malloc, + ILCLIENT_FREE_T ilclient_free, + void *userdata); + + +/** + * The <DFN>ilclient_disable_port_buffers()</DFN> function disables a + * port in base profile mode on a given component. The port is not + * tunneled, and has been supplied with buffers by the client. + * + * @param comp The component containing the port to disable. + * + * @param portIndex The port index of the port to disable. This must + * be a named port index, rather than a <DFN>OMX_ALL</DFN> value. + * + * @param bufferList A list of buffers, using <DFN>pAppPrivate</DFN> + * as the next pointer that were allocated on this port, and currently + * held by the application. If buffers on this port are held by IL + * client or the component then these are automatically freed. + * + * @param ilclient_free This function is used to free the buffer payloads. + * If <DFN>NULL</DFN> then <DFN>vcos_free</DFN> will be used. + * + * @param userdata The first argument to calls to + * <DFN>ilclient_free</DFN>. + * + * @return void + */ +VCHPRE_ void VCHPOST_ ilclient_disable_port_buffers(COMPONENT_T *comp, + int portIndex, + OMX_BUFFERHEADERTYPE *bufferList, + ILCLIENT_FREE_T ilclient_free, + void *userdata); + + +/** + * With a populated tunnel structure, the + * <DFN>ilclient_setup_tunnel()</DFN> function connects the tunnel. It + * first transitions the source component to idle if currently in + * loaded state, and then optionally checks the source event list for + * a port settings changed event from the source port. If this event + * is not in the event queue then this function optionally waits for + * it to arrive. To disable this check for the port settings changed + * event, set <DFN>timeout</DFN> to zero. + * + * Both ports are then disabled, and the source port is inspected for + * a port streams parameter. If this is supported, then the + * <DFN>portStream</DFN> argument is used to select which port stream + * to use. The two ports are then tunnelled using the + * <DFN>OMX_SetupTunnel</DFN> function. If this is successful, then + * both ports are enabled. Note that for disabling and enabling the + * tunnelled ports, the functions <DFN>ilclient_disable_tunnel()</DFN> + * and <DFN>ilclient_enable_tunnel()</DFN> are used, so the relevant + * documentation for those functions applies here. + * + * @param tunnel The tunnel structure representing the tunnel to + * set up. + * + * @param portStream If port streams are supported on the output port + * of the tunnel, then this parameter indicates the port stream to + * select on this port. + * + * @param timeout The time duration in milliseconds to wait for the + * output port to signal a port settings changed event before + * returning a timeout failure. If this is 0, then we do not check + * for a port settings changed before setting up the tunnel. + * + * @return 0 indicates success, negative indicates failure: + * - -1: a timeout waiting for the parameter changed + * - -2: an error was returned instead of parameter changed + * - -3: no streams are available from this port + * - -4: requested stream is not available from this port + * - -5: the data format was not acceptable to the sink + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_setup_tunnel(TUNNEL_T *tunnel, + unsigned int portStream, + int timeout); + + +/** + * The <DFN>ilclient_disable_tunnel()</DFN> function disables both ports listed in + * the tunnel structure. It will send a port disable command to each + * port, then waits for both to indicate they have completed the + * transition. The errors <DFN>OMX_ErrorPortUnpopulated</DFN> and + * <DFN>OMX_ErrorSameState</DFN> are both ignored by this function; the former + * since the first port to disable may deallocate buffers before the + * second port has been disabled, leading to the second port reporting + * the unpopulated error. + * + * @param tunnel The tunnel to disable. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_disable_tunnel(TUNNEL_T *tunnel); + + +/** + * The <DFN>ilclient_enable_tunnel()</DFN> function enables both ports listed in + * the tunnel structure. It will first send a port enable command to + * each port. It then checks whether the sink component is not in + * loaded state - if so, the function waits for both ports to complete + * the requested port enable. If the sink component was in loaded + * state, then this component is transitioned to idle to allow the + * ports to exchange buffers and enable the ports. This is since + * typically this function is used when creating a tunnel between two + * components, where the source component is processing data to enable + * it to report the port settings changed event, and the sink port has + * yet to be used. Before transitioning the sink component to idle, + * this function waits for the sink port to be enabled - since the + * component is in loaded state, this will happen quickly. If the + * transition to idle fails, the sink component is transitioned back + * to loaded and the source port disabled. If the transition + * succeeds, the function then waits for the source port to complete + * the requested port enable. + * + * @param tunnel The tunnel to enable. + * + * @return 0 on success, -1 on failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_enable_tunnel(TUNNEL_T *tunnel); + + +/** + * The <DFN>ilclient_flush_tunnels()</DFN> function will flush a number of tunnels + * from the list of tunnels presented. For each tunnel that is to be + * flushed, both source and sink ports are sent a flush command. The + * function then waits for both ports to report they have completed + * the flush operation. + * + * @param tunnel List of tunnels. The list must be terminated with a + * tunnel structure with <DFN>NULL</DFN> component entries. + * + * @param max The maximum number of tunnels to flush from the list. + * A value of 0 indicates that all tunnels in the list are flushed. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_flush_tunnels(TUNNEL_T *tunnel, + int max); + + +/** + * The <DFN>ilclient_teardown_tunnels()</DFN> function tears down all tunnels in + * the list of tunnels presented. For each tunnel in the list, the + * <DFN>OMX_SetupTunnel</DFN> is called on the source port and on the sink port, + * where for both calls the destination component is <DFN>NULL</DFN> and the + * destination port is zero. The VMCSX IL implementation requires + * that all tunnels are torn down in this manner before components are + * freed. + * + * @param tunnels List of tunnels to teardown. The list must be + * terminated with a tunnel structure with <DFN>NULL</DFN> component entries. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_teardown_tunnels(TUNNEL_T *tunnels); + + +/** + * The <DFN>ilclient_get_output_buffer()</DFN> function returns a buffer + * that was sent to an output port and that has been returned from a + * component using the <DFN>OMX_FillBufferDone</DFN> callback. + * + * @param comp The component that returned the buffer. + * + * @param portIndex The port index on the component that the buffer + * was returned from. + * + * @param block If non-zero, the function will block until a buffer is available. + * + * @return Pointer to buffer if available, otherwise <DFN>NULL</DFN>. + ***********************************************************/ +VCHPRE_ OMX_BUFFERHEADERTYPE* VCHPOST_ ilclient_get_output_buffer(COMPONENT_T *comp, + int portIndex, + int block); + + +/** + * The <DFN>ilclient_get_input_buffer()</DFN> function returns a buffer + * that was sent to an input port and that has been returned from a + * component using the <DFN>OMX_EmptyBufferDone</DFN> callback. + * + * @param comp The component that returned the buffer. + * + * @param portIndex The port index on the component from which the buffer + * was returned. + * + * @param block If non-zero, the function will block until a buffer is available. + * + * @return pointer to buffer if available, otherwise <DFN>NULL</DFN> + ***********************************************************/ +VCHPRE_ OMX_BUFFERHEADERTYPE* VCHPOST_ ilclient_get_input_buffer(COMPONENT_T *comp, + int portIndex, + int block); + + +/** + * The <DFN>ilclient_remove_event()</DFN> function queries the event list for the + * given component, matching against the given criteria. If a matching + * event is found, it is removed and added to the free event list. + * + * @param comp The component that returned the matching event. + * + * @param event The event type of the matching event. + * + * @param nData1 The <DFN>nData1</DFN> field of the matching event. + * + * @param ignore1 Whether to ignore the <DFN>nData1</DFN> field when finding a + * matching event. A value of 0 indicates that <DFN>nData1</DFN> must match, a + * value of 1 indicates that <DFN>nData1</DFN> does not have to match. + * + * @param nData2 The <DFN>nData2</DFN> field of the matching event. + * + * @param ignore2 Whether to ignore the <DFN>nData2</DFN> field when finding a + * matching event. A value of 0 indicates that <DFN>nData2</DFN> must match, a + * value of 1 indicates that <DFN>nData2</DFN> does not have to match. + * + * @return 0 if the event was removed. -1 if no matching event was + * found. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_remove_event(COMPONENT_T *comp, + OMX_EVENTTYPE event, + OMX_U32 nData1, + int ignore1, + OMX_U32 nData2, + int ignore2); + + +/** + * The <DFN>ilclient_return_events()</DFN> function removes all events + * from a component event list and adds them to the IL client free + * event list. This function is automatically called when components + * are freed. + * + * @param comp The component from which all events should be moved to + * the free list. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_return_events(COMPONENT_T *comp); + + +/** + * The <DFN>ilclient_wait_for_event()</DFN> function is similar to + * <DFN>ilclient_remove_event()</DFN>, but allows the caller to block until that + * event arrives. + * + * @param comp The component that returned the matching event. + * + * @param event The event type of the matching event. + * + * @param nData1 The <DFN>nData1</DFN> field of the matching event. + * + * @param ignore1 Whether to ignore the <DFN>nData1</DFN> field when finding a + * matching event. A value of 0 indicates that <DFN>nData1</DFN> must match, a + * value of 1 indicates that <DFN>nData1</DFN> does not have to match. + * + * @param nData2 The <DFN>nData2</DFN> field of the matching event. + * + * @param ignore2 Whether to ignore the <DFN>nData2</DFN> field when finding a + * matching event. A value of 0 indicates that <DFN>nData2</DFN> must match, a + * value of 1 indicates that <DFN>nData2</DFN> does not have to match. + * + * @param event_flag Specifies a bitfield of IL client events to wait + * for, given in <DFN>ILEVENT_MASK_T</DFN>. If any of these events + * are signalled by the component, the event list is then re-checked + * for a matching event. If the <DFN>ILCLIENT_EVENT_ERROR</DFN> bit + * is included, and an error is signalled by the component, then the + * function will return an error code. If the + * <DFN>ILCLIENT_CONFIG_CHANGED</DFN> bit is included, and this bit is + * signalled by the component, then the function will return an error + * code. + * + * @param timeout Specifies how long to block for in milliseconds + * before returning a failure. + * + * @return 0 indicates success, a matching event has been removed from + * the component's event queue. A negative return indicates failure, + * in this case no events have been removed from the component's event + * queue. + * - -1: a timeout was received. + * - -2: an error event was received. + * - -3: a config changed event was received. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_event(COMPONENT_T *comp, + OMX_EVENTTYPE event, + OMX_U32 nData1, + int ignore1, + OMX_U32 nData2, + int ignore2, + int event_flag, + int timeout); + + +/** + * The <DFN>ilclient_wait_for_command_complete()</DFN> function waits + * for a message from a component that indicates that the command + * has completed. This is either a command success message, or an + * error message that signals the completion of an event. + * + * @param comp The component currently processing a command. + * + * @param command The command being processed. This must be either + * <DFN>OMX_CommandStateSet</DFN>, <DFN>OMX_CommandPortDisable</DFN> + * or <DFN>OMX_CommandPortEnable</DFN>. + * + * @param nData2 The expected value of <DFN>nData2</DFN> in the + * command complete message. + * + * @return 0 indicates success, either the command successfully completed + * or the <DFN>OMX_ErrorSameState</DFN> was returned. -1 indicates + * that the command terminated with a different error message. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_command_complete(COMPONENT_T *comp, + OMX_COMMANDTYPE command, + OMX_U32 nData2); + + +/** + * The <DFN>ilclient_wait_for_command_complete_dual()</DFN> function + * is similar to <DFN>ilclient_wait_for_command_complete()</DFN>. The + * difference is that while waiting for the component to complete the + * event or raise an error, we can also report if another reports an + * error that terminates a command. This is useful if the two + * components are tunneled, and we need to wait for one component to + * enable a port, or change state to <DFN>OMX_StateIdle</DFN>. If the + * other component is the buffer supplier and reports an error, then + * it will not allocate buffers, so our first component may stall. + * + * @param comp The component currently processing a command. + * + * @param command The command being processed. This must be either + * <DFN>OMX_CommandStateSet</DFN>, <DFN>OMX_CommandPortDisable</DFN> + * or <DFN>OMX_CommandPortEnable</DFN>. + * + * @param nData2 The expected value of <DFN>nData2</DFN> in the + * command complete message. + * + * @param related Another component, where we will return if this + * component raises an error that terminates a command. + * + * @return 0 indicates success, either the command successfully + * completed or the <DFN>OMX_ErrorSameState</DFN> was returned. -1 + * indicates that the command terminated with a different error + * message. -2 indicates that the related component raised an error. + * In this case, the error is not cleared from the related + * component's event list. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_wait_for_command_complete_dual(COMPONENT_T *comp, + OMX_COMMANDTYPE command, + OMX_U32 nData2, + COMPONENT_T *related); + + +/** + * The <DFN>ilclient_debug_output()</DFN> function adds a message to a + * host-specific debug display. For a local VideoCore host the message is + * added to the internal message log. For a Win32 host the message is + * printed to the debug display. This function should be customised + * when IL client is ported to another platform. + * + * @param format A message to add, together with the variable + * argument list similar to <DFN>printf</DFN> and other standard C functions. + * + * @return void + ***********************************************************/ +VCHPRE_ void VCHPOST_ ilclient_debug_output(char *format, ...); + +/** + * The <DFN>ilclient_get_handle()</DFN> function returns the + * underlying OMX component held by an IL component handle. This is + * needed when calling methods such as <DFN>OMX_SetParameter</DFN> on + * a component created using the IL client library. + * + * @param comp IL component handle + * + * @return The <DFN>OMX_HANDLETYPE</DFN> value for the component. + ***********************************************************/ +VCHPRE_ OMX_HANDLETYPE VCHPOST_ ilclient_get_handle(COMPONENT_T *comp); + + +#ifndef OMX_SKIP64BIT + +/** + * Macro to return <DFN>OMX_TICKS</DFN> from a signed 64 bit value. + * This is the version where <DFN>OMX_TICKS</DFN> is a signed 64 bit + * value, an alternative definition is used when <DFN>OMX_TICKS</DFN> + * is a structure. + */ +#define ilclient_ticks_from_s64(s) (s) + +/** + * Macro to return signed 64 bit value from <DFN>OMX_TICKS</DFN>. + * This is the version where <DFN>OMX_TICKS</DFN> is a signed 64 bit + * value, an alternative definition is used when <DFN>OMX_TICKS</DFN> + * is a structure. + */ +#define ilclient_ticks_to_s64(t) (t) + +#else + +/** + * Inline function to return <DFN>OMX_TICKS</DFN> from a signed 64 bit + * value. This is the version where <DFN>OMX_TICKS</DFN> is a + * structure, an alternative definition is used when + * <DFN>OMX_TICKS</DFN> is a signed 64 bit value. + */ +static inline OMX_TICKS ilclient_ticks_from_s64(int64_t s) { + OMX_TICKS ret; + ret.nLowPart = s; + ret.nHighPart = s>>32; + return ret; +} + +/** + * Inline function to return signed 64 bit value from + * <DFN>OMX_TICKS</DFN>. This is the version where + * <DFN>OMX_TICKS</DFN> is a structure, an alternative definition is + * used when <DFN>OMX_TICKS</DFN> is a signed 64 bit value. + */ +static inline int64_t ilclient_ticks_to_s64(OMX_TICKS t) { + uint64_t u = t.nLowPart | ((uint64_t)t.nHighPart << 32); + return u; +} + + +#endif /* OMX_SKIP64BIT */ + +/** + * The <DFN>ilclient_get_port_index()</DFN> function returns the n'th + * port index of the specified type and direction for the given + * component. + * + * @param comp The component of interest + * @param dir The direction + * @param type The type, or -1 for any type. + * @param index Which port (counting from 0). + * + * @return The port index, or -1 if not found. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_get_port_index(COMPONENT_T *comp, + OMX_DIRTYPE dir, + OMX_PORTDOMAINTYPE type, + int index); + + +/** + * The <DFN>ilclient_suggest_bufsize()</DFN> function gives a + * component a hint about the size of buffer it should use. This size + * is set on the component by setting the + * <DFN>OMX_IndexParamBrcmOutputBufferSize</DFN> index on the given + * component. + * + * @param comp IL component handle + * @param nBufSizeHint Size of buffer in bytes + * + * @return 0 indicates success, -1 indicates failure. + ***********************************************************/ +VCHPRE_ int VCHPOST_ ilclient_suggest_bufsize(COMPONENT_T *comp, + OMX_U32 nBufSizeHint); + + +/** + * The <DFN>ilclient_stack_size()</DFN> function suggests a minimum + * stack size that tasks calling into with API will require. + * + * @return Suggested stack size in bytes. + ***********************************************************/ +VCHPRE_ unsigned int VCHPOST_ ilclient_stack_size(void); + +#endif /* ILCLIENT_H */ diff --git a/gui/libs/ilclient/ilcore.c b/gui/libs/ilclient/ilcore.c @@ -0,0 +1,308 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * \file + * + * \brief Host core implementation. + */ + +#include <stdio.h> +#include <stdarg.h> + +//includes +#include <memory.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "IL/OMX_Component.h" +#include "interface/vcos/vcos.h" + +#include "interface/vmcs_host/vcilcs.h" +#include "interface/vmcs_host/vchost.h" +#include "interface/vmcs_host/vcilcs_common.h" + +static int coreInit = 0; +static int nActiveHandles = 0; +static ILCS_SERVICE_T *ilcs_service = NULL; +static VCOS_MUTEX_T lock; +static VCOS_ONCE_T once = VCOS_ONCE_INIT; + +/* Atomic creation of lock protecting shared state */ +static void initOnce(void) +{ + VCOS_STATUS_T status; + status = vcos_mutex_create(&lock, VCOS_FUNCTION); + vcos_demand(status == VCOS_SUCCESS); +} + +/* OMX_Init */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void) +{ + VCOS_STATUS_T status; + OMX_ERRORTYPE err = OMX_ErrorNone; + + status = vcos_once(&once, initOnce); + vcos_demand(status == VCOS_SUCCESS); + + vcos_mutex_lock(&lock); + + if(coreInit == 0) + { + // we need to connect via an ILCS connection to VideoCore + VCHI_INSTANCE_T initialise_instance; + VCHI_CONNECTION_T *connection; + ILCS_CONFIG_T config; + + vc_host_get_vchi_state(&initialise_instance, &connection); + + vcilcs_config(&config); + + ilcs_service = ilcs_init((VCHIQ_INSTANCE_T) initialise_instance, (void **) &connection, &config, 0); + + if(ilcs_service == NULL) + { + err = OMX_ErrorHardware; + goto end; + } + + coreInit = 1; + } + else + coreInit++; + +end: + vcos_mutex_unlock(&lock); + return err; +} + +/* OMX_Deinit */ +OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void) +{ + if(coreInit == 0) // || (coreInit == 1 && nActiveHandles > 0)) + return OMX_ErrorNotReady; + + vcos_mutex_lock(&lock); + + coreInit--; + + if(coreInit == 0) + { + // we need to teardown the ILCS connection to VideoCore + ilcs_deinit(ilcs_service); + ilcs_service = NULL; + } + + vcos_mutex_unlock(&lock); + + return OMX_ErrorNone; +} + + +/* OMX_ComponentNameEnum */ +OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum( + OMX_OUT OMX_STRING cComponentName, + OMX_IN OMX_U32 nNameLength, + OMX_IN OMX_U32 nIndex) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_component_name_enum(ilcs_get_common(ilcs_service), cComponentName, nNameLength, nIndex); +} + + +/* OMX_GetHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle( + OMX_OUT OMX_HANDLETYPE* pHandle, + OMX_IN OMX_STRING cComponentName, + OMX_IN OMX_PTR pAppData, + OMX_IN OMX_CALLBACKTYPE* pCallBacks) +{ + OMX_ERRORTYPE eError; + OMX_COMPONENTTYPE *pComp; + OMX_HANDLETYPE hHandle = 0; + + if (pHandle == NULL || cComponentName == NULL || pCallBacks == NULL || ilcs_service == NULL) + { + if(pHandle) + *pHandle = NULL; + return OMX_ErrorBadParameter; + } + + { + pComp = (OMX_COMPONENTTYPE *)malloc(sizeof(OMX_COMPONENTTYPE)); + if (!pComp) + { + vcos_assert(0); + return OMX_ErrorInsufficientResources; + } + memset(pComp, 0, sizeof(OMX_COMPONENTTYPE)); + hHandle = (OMX_HANDLETYPE)pComp; + pComp->nSize = sizeof(OMX_COMPONENTTYPE); + pComp->nVersion.nVersion = OMX_VERSION; + eError = vcil_out_create_component(ilcs_get_common(ilcs_service), hHandle, cComponentName); + + if (eError == OMX_ErrorNone) { + // Check that all function pointers have been filled in. + // All fields should be non-zero. + int i; + uint32_t *p = (uint32_t *) pComp; + for(i=0; i<sizeof(OMX_COMPONENTTYPE)>>2; i++) + if(*p++ == 0) + eError = OMX_ErrorInvalidComponent; + + if(eError != OMX_ErrorNone && pComp->ComponentDeInit) + pComp->ComponentDeInit(hHandle); + } + + if (eError == OMX_ErrorNone) { + eError = pComp->SetCallbacks(hHandle,pCallBacks,pAppData); + if (eError != OMX_ErrorNone) + pComp->ComponentDeInit(hHandle); + } + if (eError == OMX_ErrorNone) { + *pHandle = hHandle; + } + else { + *pHandle = NULL; + free(pComp); + } + } + + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + nActiveHandles++; + vcos_mutex_unlock(&lock); + } + + return eError; +} + +/* OMX_FreeHandle */ +OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle( + OMX_IN OMX_HANDLETYPE hComponent) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pComp; + + if (hComponent == NULL || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + pComp = (OMX_COMPONENTTYPE*)hComponent; + + if (ilcs_service == NULL) + return OMX_ErrorBadParameter; + + eError = (pComp->ComponentDeInit)(hComponent); + if (eError == OMX_ErrorNone) { + vcos_mutex_lock(&lock); + --nActiveHandles; + vcos_mutex_unlock(&lock); + free(pComp); + } + + vcos_assert(nActiveHandles >= 0); + + return eError; +} + +/* OMX_SetupTunnel */ +OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel( + OMX_IN OMX_HANDLETYPE hOutput, + OMX_IN OMX_U32 nPortOutput, + OMX_IN OMX_HANDLETYPE hInput, + OMX_IN OMX_U32 nPortInput) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + OMX_COMPONENTTYPE *pCompIn, *pCompOut; + OMX_TUNNELSETUPTYPE oTunnelSetup; + + if ((hOutput == NULL && hInput == NULL) || ilcs_service == NULL) + return OMX_ErrorBadParameter; + + oTunnelSetup.nTunnelFlags = 0; + oTunnelSetup.eSupplier = OMX_BufferSupplyUnspecified; + + pCompOut = (OMX_COMPONENTTYPE*)hOutput; + + if (hOutput){ + eError = pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, hInput, nPortInput, &oTunnelSetup); + } + + if (eError == OMX_ErrorNone && hInput) { + pCompIn = (OMX_COMPONENTTYPE*)hInput; + eError = pCompIn->ComponentTunnelRequest(hInput, nPortInput, hOutput, nPortOutput, &oTunnelSetup); + + if (eError != OMX_ErrorNone && hOutput) { + /* cancel tunnel request on output port since input port failed */ + pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, NULL, 0, NULL); + } + } + return eError; +} + +/* OMX_GetComponentsOfRole */ +OMX_ERRORTYPE OMX_GetComponentsOfRole ( + OMX_IN OMX_STRING role, + OMX_INOUT OMX_U32 *pNumComps, + OMX_INOUT OMX_U8 **compNames) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumComps = 0; + return eError; +} + +/* OMX_GetRolesOfComponent */ +OMX_ERRORTYPE OMX_GetRolesOfComponent ( + OMX_IN OMX_STRING compName, + OMX_INOUT OMX_U32 *pNumRoles, + OMX_OUT OMX_U8 **roles) +{ + OMX_ERRORTYPE eError = OMX_ErrorNone; + + *pNumRoles = 0; + return eError; +} + +/* OMX_GetDebugInformation */ +OMX_ERRORTYPE OMX_GetDebugInformation ( + OMX_OUT OMX_STRING debugInfo, + OMX_INOUT OMX_S32 *pLen) +{ + if(ilcs_service == NULL) + return OMX_ErrorBadParameter; + + return vcil_out_get_debug_information(ilcs_get_common(ilcs_service), debugInfo, pLen); +} + + + +/* File EOF */ + diff --git a/gui/libs/vgfont/Makefile b/gui/libs/vgfont/Makefile @@ -0,0 +1,7 @@ +OBJS=font.o vgft.o graphics.o +LIB=libvgfont.a + +INCLUDES+=-I$(SDKSTAGE)/usr/include/freetype2 -I$(SDKSTAGE)/usr/include -I$(SDKSTAGE)/usr/include/arm-linux-gnueabi + +include ../../Makefile.include + diff --git a/gui/libs/vgfont/font.c b/gui/libs/vgfont/font.c @@ -0,0 +1,355 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +/** @file font.c + * + * Fairly primitive font handling, just enough to emulate the old API. + * + * Hinting and Font Size + * + * The old API does not create fonts explicitly, it just renders them + * as needed. That works fine for unhinted fonts, but for hinted fonts we + * care about font size. + * + * Since we now *can* do hinted fonts, we should do. Regenerating the + * fonts each time becomes quite slow, so we maintain a cache of fonts. + * + * For the typical applications which use graphics_x this is fine, but + * won't work well if lots of fonts sizes are used. + * + * Unicode + * + * This API doesn't support unicode at all at present, nor UTF-8. + */ + +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> + +#include "graphics_x_private.h" +#include "vgft.h" + +#define VMCS_INSTALL_PREFIX "" + +/** The one and only (default) font we support for now. + */ +static struct +{ + const char *file; + void *mem; + size_t len; +} default_font = { "VeraMono.ttf" }; + +/** An entry in our list of fonts + */ +typedef struct gx_font_cache_entry_t +{ + struct gx_font_cache_entry_t *next; + VGFT_FONT_T font; + uint32_t ptsize; /** size in points, 26.6 */ +} gx_font_cache_entry_t; + +static char fname[128]; +static int inited; +static gx_font_cache_entry_t *fonts; + +static VGFT_FONT_T *find_font(const char *text, uint32_t text_size); + +VCOS_STATUS_T gx_priv_font_init(const char *font_dir) +{ + VCOS_STATUS_T ret; + size_t len; + int rc; + if (vgft_init()) + { + ret = VCOS_ENOMEM; + goto fail_init; + } + + int fd = -1; + // search for the font + sprintf(fname, "%s/%s", font_dir, default_font.file); + fd = open(fname, O_RDONLY); + + if (fd < 0) + { + GX_ERROR("Could not open font file '%s'", default_font.file); + ret = VCOS_ENOENT; + goto fail_open; + } + + len = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + + default_font.mem = vcos_malloc(len, default_font.file); + if (!default_font.mem) + { + GX_ERROR("No memory for font %s", fname); + ret = VCOS_ENOMEM; + goto fail_mem; + } + + rc = read(fd, default_font.mem, len); + if (rc != len) + { + GX_ERROR("Could not read font %s", fname); + ret = VCOS_EINVAL; + goto fail_rd; + } + default_font.len = len; + close(fd); + + GX_TRACE("Opened font file '%s'", fname); + + inited = 1; + return VCOS_SUCCESS; + +fail_rd: + vcos_free(default_font.mem); +fail_mem: + if (fd >= 0) close(fd); +fail_open: + vgft_term(); +fail_init: + return ret; +} + +void gx_priv_font_term(void) +{ + gx_font_cache_flush(); + vgft_term(); + vcos_free(default_font.mem); +} + +/** Render text. + * + * FIXME: Not at all optimal - re-renders each time. + * FIXME: Not UTF-8 aware + * FIXME: better caching + */ +VCOS_STATUS_T gx_priv_render_text( GX_DISPLAY_T *disp, + GRAPHICS_RESOURCE_HANDLE res, + int32_t x, + int32_t y, + uint32_t width, + uint32_t height, + uint32_t fg_colour, + uint32_t bg_colour, + const char *text, + uint32_t text_length, + uint32_t text_size ) +{ + VGfloat vg_colour[4]; + VGFT_FONT_T *font; + VGPaint fg; + GX_CLIENT_STATE_T save; + VCOS_STATUS_T status = VCOS_SUCCESS; + int clip = 1; + + vcos_demand(inited); // has gx_font_init() been called? + + gx_priv_save(&save, res); + + if (width == GRAPHICS_RESOURCE_WIDTH && + height == GRAPHICS_RESOURCE_HEIGHT) + { + clip = 0; + } + + width = (width == GRAPHICS_RESOURCE_WIDTH) ? res->width : width; + height = (height == GRAPHICS_RESOURCE_HEIGHT) ? res->height : height; + font = find_font(text, text_size); + if (!font) + { + status = VCOS_ENOMEM; + goto finish; + } + + // setup the clipping rectangle + if (clip) + { + VGint coords[] = {x,y,width,height}; + vgSeti(VG_SCISSORING, VG_TRUE); + vgSetiv(VG_SCISSOR_RECTS, 4, coords); + } + + // setup the background colour if needed + if (bg_colour != GRAPHICS_TRANSPARENT_COLOUR) + { + int err; + VGfloat rendered_w, rendered_h; + VGfloat vg_bg_colour[4]; + + // setup the background colour... + gx_priv_colour_to_paint(bg_colour, vg_bg_colour); + vgSetfv(VG_CLEAR_COLOR, 4, vg_bg_colour); + + // fill in a rectangle... + vgft_get_text_extents(font, text, text_length, (VGfloat)x, (VGfloat)y, &rendered_w, &rendered_h); + + if ( ( 0 < (VGint)rendered_w ) && ( 0 < (VGint)rendered_h ) ) + { + // Have to compensate for the messed up y position of multiline text. + VGfloat offset = vgft_first_line_y_offset(font); + int32_t bottom = y + offset - rendered_h; + + vgClear(x, bottom, (VGint)rendered_w, (VGint)rendered_h); + err = vgGetError(); + if (err) + { + GX_LOG("Error %d clearing bg text %d %d %g %g", + err, x, y, rendered_w, rendered_h); + vcos_assert(0); + } // if + } // if + } // if + // setup the foreground colour + fg = vgCreatePaint(); + if (!fg) + { + status = VCOS_ENOMEM; + goto finish; + } + + // draw the foreground text + vgSetParameteri(fg, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + gx_priv_colour_to_paint(fg_colour, vg_colour); + vgSetParameterfv(fg, VG_PAINT_COLOR, 4, vg_colour); + vgSetPaint(fg, VG_FILL_PATH); + + vgft_font_draw(font, (VGfloat)x, (VGfloat)y, text, text_length, VG_FILL_PATH); + + vgDestroyPaint(fg); + + vcos_assert(vgGetError() == 0); + vgSeti(VG_SCISSORING, VG_FALSE); + +finish: + gx_priv_restore(&save); + + return status; +} + + +/** Find a font in our cache, or create a new entry in the cache. + * + * Very primitive at present. + */ +static VGFT_FONT_T *find_font(const char *text, uint32_t text_size) +{ + int ptsize, dpi_x = 0, dpi_y = 0; + VCOS_STATUS_T status; + gx_font_cache_entry_t *font; + + ptsize = text_size << 6; // freetype takes size in points, in 26.6 format. + + for (font = fonts; font; font = font->next) + { + if (font->ptsize == ptsize) + return &font->font; + } + + font = vcos_malloc(sizeof(*font), "font"); + if (!font) + return NULL; + + font->ptsize = ptsize; + + status = vgft_font_init(&font->font); + if (status != VCOS_SUCCESS) + { + vcos_free(font); + return NULL; + } + + // load the font + status = vgft_font_load_mem(&font->font, default_font.mem, default_font.len); + if (status != VCOS_SUCCESS) + { + GX_LOG("Could not load font from memory: %d", status); + vgft_font_term(&font->font); + vcos_free(font); + return NULL; + } + + status = vgft_font_convert_glyphs(&font->font, ptsize, dpi_x, dpi_y); + if (status != VCOS_SUCCESS) + { + GX_LOG("Could not convert font '%s' at size %d", fname, ptsize); + vgft_font_term(&font->font); + vcos_free(font); + return NULL; + } + + font->next = fonts; + fonts = font; + + return &font->font; +} + +void gx_font_cache_flush(void) +{ + while (fonts != NULL) + { + struct gx_font_cache_entry_t *next = fonts->next; + vgft_font_term(&fonts->font); + vcos_free(fonts); + fonts = next; + } +} + +int32_t graphics_resource_text_dimensions_ext(GRAPHICS_RESOURCE_HANDLE res, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height, + const uint32_t text_size ) +{ + GX_CLIENT_STATE_T save; + VGfloat w, h; + int ret = -1; + + gx_priv_save(&save, res); + + VGFT_FONT_T *font = find_font(text, text_size); + if (!font) + goto finish; + + + vgft_get_text_extents(font, text, text_length, 0.0, 0.0, &w, &h); + *width = w; + *height = h; + ret = 0; + +finish: + gx_priv_restore(&save); + return ret; +} + diff --git a/gui/libs/vgfont/graphics.c b/gui/libs/vgfont/graphics.c @@ -0,0 +1,1608 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Graphics library for VG + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "vgfont.h" +#include "graphics_x_private.h" + +/****************************************************************************** +Defines. +******************************************************************************/ +#define ATEXT_FONT_SIZE 12 /*< Default font size (font size can be set with *_ext functions). */ + +/****************************************************************************** +Local data +******************************************************************************/ +static GX_DISPLAY_T display; /*< Our one and only EGL display. */ + +/** + * We create one eglContext for each of the possible graphics_x resource types + * that are supported. + ***********************************************************/ +static EGLContext gx_contexts[GRAPHICS_RESOURCE_HANDLE_TYPE_MAX]; + +/** Note: we have to share all our contexts, because otherwise it seems + * to be not valid to blit from one image to another if the images + * have different contexts. + * + * That means we have to use a single global lock to serialise all accesses + * to any contexts. + ***********************************************************/ +static VCOS_MUTEX_T lock; + +static EGLConfig gx_configs[GRAPHICS_RESOURCE_HANDLE_TYPE_MAX]; + +static int inited; + +/****************************************************************************** +Local Functions +******************************************************************************/ + +/** Convert graphics_x colour formats into EGL format. */ +static int gx_egl_attrib_colours(EGLint *attribs, GRAPHICS_RESOURCE_TYPE_T res_type) +{ + int i, n; + static EGLint rgba[] = {EGL_RED_SIZE, EGL_GREEN_SIZE, EGL_BLUE_SIZE, EGL_ALPHA_SIZE}; + static uint8_t rgb565[] = {5,6,5,0}; + static uint8_t rgb888[] = {8,8,8,0}; + static uint8_t rgb32a[] = {8,8,8,8}; + + uint8_t *sizes = NULL; + + switch (res_type) + { + case GRAPHICS_RESOURCE_RGB565: + sizes = rgb565; + break; + case GRAPHICS_RESOURCE_RGB888: + sizes = rgb888; + break; + case GRAPHICS_RESOURCE_RGBA32: + sizes = rgb32a; + break; + default: + vcos_assert(0); + return -1; + } + for (n=0, i=0; i<countof(rgba); i++) + { + attribs[n++] = rgba[i]; + attribs[n++] = sizes[i]; + } + return n; +} + +/* Create an EGLContext for a given GRAPHICS_RESOURCE_TYPE */ +static VCOS_STATUS_T create_context(EGLDisplay disp, + GRAPHICS_RESOURCE_TYPE_T image_type, + EGLContext *shared_with) +{ + int n; + EGLConfig configs[1]; + EGLint nconfigs, attribs[32]; + n = gx_egl_attrib_colours(attribs, image_type); + + // we want to be able to do OpenVG on this surface... + attribs[n++] = EGL_RENDERABLE_TYPE; attribs[n++] = EGL_OPENVG_BIT; + attribs[n++] = EGL_SURFACE_TYPE; attribs[n++] = EGL_WINDOW_BIT; + + attribs[n] = EGL_NONE; + + EGLBoolean egl_ret = eglChooseConfig(disp, + attribs, configs, + countof(configs), &nconfigs); + + if (!egl_ret || !nconfigs) + { + GX_LOG("%s: no suitable configurations for res type %d", + __FUNCTION__, image_type); + return VCOS_EINVAL; + } + + EGLContext cxt = eglCreateContext(disp, configs[0], *shared_with, 0); + if (!cxt) + { + GX_LOG("Could not create context for image type %d: 0x%x", + image_type, eglGetError()); + return VCOS_ENOSPC; + } + + gx_contexts[image_type] = cxt; + gx_configs[image_type] = configs[0]; + *shared_with = cxt; + + return VCOS_SUCCESS; +} + +/****************************************************************************** +Functions private to code inside GraphicsX +******************************************************************************/ + +static VCOS_STATUS_T gx_priv_initialise( void ) +{ + int i; + EGLDisplay disp; + EGLint egl_maj, egl_min; + int32_t ret = VCOS_EINVAL; + EGLBoolean result; + + vcos_demand(inited == 0); + + vcos_log_set_level(&gx_log_cat, VCOS_LOG_WARN); + vcos_log_register("graphics", &gx_log_cat); + + memset(&display,0,sizeof(display)); + + gx_priv_init(); + + disp = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + if (disp == EGL_NO_DISPLAY) + { + GX_LOG("Could not open display: 0x%x", eglGetError()); + vcos_assert(0); + goto fail_disp; + } + + result = eglInitialize(disp, &egl_maj, &egl_min); + if (!result) + { + GX_LOG("Could not init display :0x%x", eglGetError()); + vcos_assert(0); // really can't continue + goto fail_egl_init; + } + + result = eglBindAPI(EGL_OPENVG_API); + vcos_assert(result); // really should succeed + + display.disp = disp; + + GX_TRACE("Supported client APIS: %s", eglQueryString(disp, EGL_CLIENT_APIS)); + + // create the available contexts + EGLContext shared_context = EGL_NO_CONTEXT; + ret = create_context(disp,GRAPHICS_RESOURCE_RGB565, &shared_context); + ret |= create_context(disp,GRAPHICS_RESOURCE_RGB888, &shared_context); + ret |= create_context(disp,GRAPHICS_RESOURCE_RGBA32, &shared_context); + + if (ret != VCOS_SUCCESS) + goto fail_cxt; + + eglSwapInterval(disp, 1); + + inited = 1; + + return ret; + +fail_cxt: + for (i=0; i<GRAPHICS_RESOURCE_HANDLE_TYPE_MAX; i++) + { + if (gx_contexts[i]) + { + eglDestroyContext(display.disp,gx_contexts[i]); + vcos_mutex_delete(&lock); + } + } + eglTerminate(display.disp); +fail_egl_init: +fail_disp: + return ret; +} + +/*****************************************************************************/ +void gx_priv_save(GX_CLIENT_STATE_T *state, GRAPHICS_RESOURCE_HANDLE res) +{ + EGLBoolean egl_result; + vcos_assert(res == NULL || (res->magic == RES_MAGIC)); + vcos_assert(res == NULL || !res->context_bound); + + state->context = eglGetCurrentContext(); + state->api = eglQueryAPI(); + state->read_surface = eglGetCurrentSurface(EGL_READ); + state->draw_surface = eglGetCurrentSurface(EGL_DRAW); + state->res = res; + + vcos_assert(state->api); // should never be anything other than VG or GL + + vcos_mutex_lock(&lock); + + egl_result = eglBindAPI(EGL_OPENVG_API); + vcos_assert(egl_result); + + if (res) + { + GX_TRACE("gx_priv_save: eglMakeCurrent: %s, res %x surface %x, cxt %x", vcos_thread_get_name(vcos_thread_current()), + (uint32_t)res, (uint32_t)res->surface, (uint32_t)res->context); + + egl_result = eglMakeCurrent(display.disp, res->surface, + res->surface, res->context); + vcos_assert(egl_result); + + res->context_bound = 1; + } +} + +/*****************************************************************************/ +void gx_priv_restore(GX_CLIENT_STATE_T *state) +{ + EGLBoolean egl_result; + + GX_TRACE("gx_priv_restore: eglMakeCurrent: %s, res %x draw_surface %x, surface %x, cxt %x", vcos_thread_get_name(vcos_thread_current()), + (uint32_t)state->res, (uint32_t)state->draw_surface, (uint32_t)state->read_surface, (uint32_t)state->context); + + // disconnect our thread from this context, so we other threads can use it via + // this API + egl_result = eglMakeCurrent(display.disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + vcos_assert(egl_result); + + // now return to the client's API binding + egl_result = eglBindAPI(state->api); + vcos_assert(egl_result); + + egl_result = eglMakeCurrent(display.disp, state->draw_surface, state->read_surface, state->context); + vcos_assert(egl_result); + + if (state->res) state->res->context_bound = 0; + + vcos_mutex_unlock(&lock); +} + +/****************************************************************************** +Functions and data exported as part of the public GraphicsX API +******************************************************************************/ + +VCOS_LOG_CAT_T gx_log_cat; /*< Logging category for GraphicsX. */ + +int32_t graphics_initialise( void ) +{ + // dummy initialisation function. This is typically called + // early in the day before VLLs are available, and so cannot + // do anything useful. + return 0; +} + +/*****************************************************************************/ +int32_t graphics_uninitialise( void ) +{ + int i; + vcos_assert(inited); + + gx_priv_font_term(); + + for (i=0; i<GRAPHICS_RESOURCE_HANDLE_TYPE_MAX; i++) + if (gx_contexts[i]) + eglDestroyContext(display.disp,gx_contexts[i]); + + eglTerminate(display.disp); + gx_priv_destroy(); + vcos_log_unregister(&gx_log_cat); + inited = 0; + return 0; +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_create_window( uint32_t screen_id, + uint32_t width, + uint32_t height, + GRAPHICS_RESOURCE_TYPE_T image_type, + GRAPHICS_RESOURCE_HANDLE *resource_handle ) +{ + int rc; + VCOS_STATUS_T status = VCOS_SUCCESS; + GRAPHICS_RESOURCE_HANDLE h; + EGLBoolean egl_result; + void *cookie; + GX_CLIENT_STATE_T save; + + if (!gx_contexts[image_type]) + { + GX_LOG("Invalid image type %d", image_type); + return VCOS_EINVAL; + } + + h = vcos_calloc(1,sizeof(*h), "graphics_x_resource"); + if (!h) + { + GX_LOG("%s: no memory for resource", __FUNCTION__); + return VCOS_ENOMEM; + } + + // now need to get the native window + rc = gx_priv_create_native_window(screen_id, + width, height, image_type, + &h->u.native_window, + &cookie); + if (rc < 0) + { + GX_LOG("%s: could not create native window", __FUNCTION__); + status = VCOS_ENOMEM; + goto fail_create_native_win; + } + + h->magic = RES_MAGIC; + h->type = GX_WINDOW; + h->alpha = 1.0; + + h->surface = eglCreateWindowSurface(display.disp, gx_configs[image_type], &h->u.native_window.egl_win, NULL); + if (!h->surface) + { + GX_LOG("Could not create window surface: 0x%x", eglGetError()); + status = VCOS_ENOMEM; + goto fail_win; + } + + egl_result = eglSurfaceAttrib(display.disp, h->surface, + EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); + vcos_assert(egl_result); + + h->context = gx_contexts[image_type]; + h->screen_id = screen_id; + h->width = width; + h->height = height; + h->restype = image_type; + + gx_priv_save(&save, h); + + // fill it with black + status = gx_priv_resource_fill(h, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0xff)); + vcos_assert(status == VCOS_SUCCESS); + + gx_priv_finish_native_window(h, cookie); + gx_priv_flush(h); + + *resource_handle = h; + gx_priv_restore(&save); + return status; + +fail_win: + gx_priv_destroy_native_window(h); +fail_create_native_win: + vcos_free(h); + return status; +} + +/*****************************************************************************/ +int32_t graphics_delete_resource( GRAPHICS_RESOURCE_HANDLE res ) +{ + EGLBoolean result; + + if (!res) + { + // let it slide - mimics old behaviour + return 0; + } + GX_TRACE("delete resource @%p", res); + + vcos_assert(res->magic == RES_MAGIC); + + if (res->type == GX_PBUFFER) + { + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + vgDestroyImage(res->u.pixmap); + vcos_assert(vgGetError() == 0); + gx_priv_restore(&save); + } + + GX_TRACE("graphics_delete_resource: calling eglDestroySurface..."); + result = eglDestroySurface(display.disp, res->surface); + vcos_assert(result); + + GX_TRACE("graphics_delete_resource: calling eglWaitClient..."); + eglWaitClient(); // wait for EGL to finish sorting out its surfaces + + if (res->type == GX_WINDOW) + { + GX_TRACE("graphics_delete_resource: calling gx_priv_destroy_native_window..."); + gx_priv_destroy_native_window(res); + } + + res->magic = ~RES_MAGIC; + vcos_free(res); + GX_TRACE("graphics_delete_resource: done"); + + return 0; +} + +/*****************************************************************************/ +int32_t graphics_update_displayed_resource(GRAPHICS_RESOURCE_HANDLE res, + const uint32_t x_offset, + const uint32_t y_offset, + const uint32_t width, + const uint32_t height ) +{ + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + gx_priv_flush(res); + + gx_priv_restore(&save); + + return 0; +} + +/*****************************************************************************/ +int32_t graphics_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ) +{ + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + VCOS_STATUS_T st = gx_priv_resource_fill( + res, + x, res->height-y-height, + width, height, + fill_colour); + + gx_priv_restore(&save); + + return st == VCOS_SUCCESS ? 0 : -1; +} + +/*****************************************************************************/ +int32_t graphics_resource_render_text_ext( GRAPHICS_RESOURCE_HANDLE res, + const int32_t x, + const int32_t y, + const uint32_t width, + const uint32_t height, + const uint32_t fg_colour, + const uint32_t bg_colour, + const char *text, + const uint32_t text_length, + const uint32_t text_size ) +{ + + /* + * FIXME: Not at all optimal - re-renders each time. + * FIXME: Not UTF-8 safe + * FIXME: much better caching (or any caching) + */ + VCOS_STATUS_T rc = gx_priv_render_text( + &display, res, + x, res->height-y-text_size, width, height, fg_colour, bg_colour, + text, text_length, text_size); + + return (rc == VCOS_SUCCESS) ? 0 : -1; +} + +/*****************************************************************************/ +int32_t graphics_resource_render_text( GRAPHICS_RESOURCE_HANDLE res, + const int32_t x, + const int32_t y, + const uint32_t width, /* this can be GRAPHICS_RESOURCE_WIDTH for no clipping */ + const uint32_t height, /* this can be GRAPHICS_RESOURCE_HEIGHT for no clipping */ + const uint32_t fg_colour, + const uint32_t bg_colour, + const char *text, + const uint32_t text_length) +{ + return graphics_resource_render_text_ext(res, x, y, width, height, + fg_colour, bg_colour, + text, text_length, + ATEXT_FONT_SIZE); +} + +/*****************************************************************************/ +int32_t graphics_get_resource_size( + const GRAPHICS_RESOURCE_HANDLE res, + uint32_t *w, + uint32_t *h) +{ + if (w) *w = res->width; + if (h) *h = res->height; + return 0; +} + +/*****************************************************************************/ +int32_t graphics_get_resource_type(const GRAPHICS_RESOURCE_HANDLE res, GRAPHICS_RESOURCE_TYPE_T *type) +{ + if (type) *type = res->restype; + return 0; +} + +/*****************************************************************************/ +int32_t graphics_bitblt( const GRAPHICS_RESOURCE_HANDLE src, + const uint32_t x, // offset within source + const uint32_t y, // offset within source + const uint32_t width, + const uint32_t height, + GRAPHICS_RESOURCE_HANDLE dest, + const uint32_t x_pos, + const uint32_t y_pos ) +{ + int rc = -1; + VGfloat old[9]; + uint32_t w, h; + VGPaint paint = VG_INVALID_HANDLE; + GX_CLIENT_STATE_T save; + int is_child = 0; + VGImage img = VG_INVALID_HANDLE; + + gx_priv_save(&save, dest); + + if (src->type != GX_PBUFFER) + { + vcos_assert(0); + goto finish; + } + + // create a child image that contains just the part wanted + w = width == GRAPHICS_RESOURCE_WIDTH ? src->width : width; + h = height == GRAPHICS_RESOURCE_HEIGHT ? src->height : height; + + if (x==0 && y==0 && + w == src->width && + h == src->height) + { + img = src->u.pixmap; + } + else + { + is_child = 1; + img = vgChildImage(src->u.pixmap, x, y, w, h); + if (img == VG_INVALID_HANDLE) + { + vcos_assert(0); + goto finish; + } + } + + vcos_assert(vgGetError()==0); + + vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); + vgGetMatrix(old); + vgLoadIdentity(); + vgTranslate((VGfloat)x_pos, (VGfloat)(dest->height-y_pos)); + vgScale(1.0, -1.0); + + // Do we have a translucency going on? + if (src->alpha != 1.0) + { + VGfloat colour[4] = {1.0,1.0,1.0,src->alpha}; + paint = vgCreatePaint(); + + vgSetParameterfv(paint, VG_PAINT_COLOR, 4, colour); + vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_MULTIPLY); + vgSetPaint(paint, VG_STROKE_PATH | VG_FILL_PATH); + } + vcos_assert(vgGetError()==0); + + vgDrawImage(img); + vcos_assert(vgGetError()==0); + vgLoadMatrix(old); + + int err = vgGetError(); + + if (err) + { + GX_LOG("vg error %x blitting area", err); + vcos_assert(0); + rc = -1; + } + else + { + rc = 0; + } +finish: + if (paint != VG_INVALID_HANDLE) + vgDestroyPaint(paint); + + if (is_child) + vgDestroyImage(img); + + gx_priv_restore(&save); + return rc; +} + +void gx_priv_flush(GRAPHICS_RESOURCE_HANDLE res) +{ + EGLBoolean result; + result = eglSwapBuffers(display.disp, res->surface); + vcos_assert(result); +} + + +/** Map a colour, which the client will have supplied in RGB888. + */ + +void gx_priv_colour_to_paint(uint32_t col, VGfloat *rgba) +{ + // with OpenVG we use RGB order. + rgba[0] = ((VGfloat)((col & R_888_MASK) >> 16 )) / 0xff; + rgba[1] = ((VGfloat)((col & G_888_MASK) >> 8 )) / 0xff; + rgba[2] = ((VGfloat)((col & B_888_MASK) >> 0 )) / 0xff; + rgba[3] = ((VGfloat)((col & ALPHA_888_MASK) >> 24)) / 0xff; +} + +/** Fill an area of a surface with a fixed colour. + */ +VCOS_STATUS_T gx_priv_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ) +{ + VGfloat vg_clear_colour[4]; + + gx_priv_colour_to_paint(fill_colour, vg_clear_colour); + vgSeti(VG_SCISSORING, VG_FALSE); + + vgSetfv(VG_CLEAR_COLOR, 4, vg_clear_colour); + vgClear(x, y, width, height); + + int err = vgGetError(); + if (err) + { + GX_LOG("vg error %x filling area", err); + vcos_assert(0); + } + + return VCOS_SUCCESS; +} + +VCOS_STATUS_T gx_priv_get_pixels(const GRAPHICS_RESOURCE_HANDLE res, void **p_pixels, GX_RASTER_ORDER_T raster_order) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + void *pixels, *dest; + uint32_t width, height; + int data_size, pitch; + VGImageFormat image_format; + + if (!p_pixels) + { + status = VCOS_EINVAL; + goto finish; + } + + GX_TRACE("%s: res %p", __FUNCTION__, res); + + graphics_get_resource_size(res, &width, &height); + + /* FIXME: implement e.g. gx_get_pitch */ + switch (res->restype) + { + case GRAPHICS_RESOURCE_RGB565: + pitch = ((width + 31)&(~31)) << 1; + break; + case GRAPHICS_RESOURCE_RGB888: + case GRAPHICS_RESOURCE_RGBA32: + pitch = ((width + 31)&(~31)) << 2; + break; + default: + { + GX_LOG("Unsupported pixel format"); + status = VCOS_EINVAL; + goto finish; + } + } + + data_size = pitch * height; + + /* NB: vgReadPixels requires that the data pointer is aligned, but does not + require the stride to be aligned. Most implementations probably will + require that as well though... */ + pixels = vcos_malloc(data_size, "gx_get_pixels data"); + if (!pixels) + { + GX_LOG("Could not allocate %d bytes for vgReadPixels", data_size); + status = VCOS_ENOMEM; + goto finish; + } + /* FIXME: introduce e.g. GX_COLOR_FORMAT and mapping to VGImageFormat... */ + + /* Hand out image data formatted to match OpenGL RGBA format. + */ + switch (res->restype) + { + case GRAPHICS_RESOURCE_RGB565: + image_format = VG_sBGR_565; + break; + case GRAPHICS_RESOURCE_RGB888: + image_format = VG_sXBGR_8888; + break; + case GRAPHICS_RESOURCE_RGBA32: + image_format = VG_sABGR_8888; + break; + default: + { + GX_LOG("Unsupported pixel format"); + status = VCOS_EINVAL; + goto finish; + } + } + + /* VG raster order is bottom-to-top */ + if (raster_order == GX_TOP_BOTTOM) + { + dest = ((uint8_t*)pixels)+(pitch*(height-1)); + pitch = -pitch; + } + else + { + dest = pixels; + } + + vgReadPixels(dest, pitch, image_format, 0, 0, width, height); + + vcos_assert(vgGetError() == 0); + + *p_pixels = pixels; + +finish: + return status; +} + +static VCOS_STATUS_T convert_image_type(GRAPHICS_RESOURCE_TYPE_T image_type, + VGImageFormat *vg_image_type, + int *pbytes_per_pixel) +{ + int bytes_per_pixel; + + switch (image_type) + { + case GRAPHICS_RESOURCE_RGB565: + *vg_image_type = VG_sRGB_565; + bytes_per_pixel = 2; + break; + case GRAPHICS_RESOURCE_RGB888: + *vg_image_type = VG_sRGBX_8888; + bytes_per_pixel = 3; // 24 bpp + break; + case GRAPHICS_RESOURCE_RGBA32: + *vg_image_type = VG_sARGB_8888; + bytes_per_pixel = 4; + break; + default: + vcos_assert(0); + *vg_image_type = 0; + return VCOS_EINVAL; + } + if (pbytes_per_pixel) + *pbytes_per_pixel = bytes_per_pixel; + + return VCOS_SUCCESS; +} + + +/*****************************************************************************/ +VCOS_STATUS_T gx_create_pbuffer( uint32_t width, + uint32_t height, + GRAPHICS_RESOURCE_TYPE_T image_type, + GRAPHICS_RESOURCE_HANDLE *resource_handle ) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + GRAPHICS_RESOURCE_HANDLE h; + VGImage image; + VGImageFormat vg_image_type; + GX_CLIENT_STATE_T save; + + h = vcos_calloc(1,sizeof(*h), "graphics_x_resource"); + if (!h) + { + GX_LOG("%s: no memory for resource", __FUNCTION__); + return VCOS_ENOMEM; + } + + status = convert_image_type(image_type, &vg_image_type, NULL); + if (status != VCOS_SUCCESS) + { + vcos_free(h); + return status; + } + + h->magic = RES_MAGIC; + h->context = gx_contexts[image_type]; + h->config = gx_configs[image_type]; + h->alpha = 1.0; + h->type = GX_PBUFFER; + h->width = width; + h->height = height; + h->restype = image_type; + + GX_TRACE("Creating pbuffer surface"); + + EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE}; + h->surface = eglCreatePbufferSurface(display.disp, h->config, + attribs); + if (!h->surface) + { + GX_LOG("Could not create EGL pbuffer surface: 0x%x", eglGetError()); + vcos_free(h); + return VCOS_EINVAL; + } + + gx_priv_save(&save, h); + + image = vgCreateImage(vg_image_type, width, height, VG_IMAGE_QUALITY_BETTER); + if (image == VG_INVALID_HANDLE) + { + GX_LOG("Could not create vg image type %d: vg error 0x%x", + vg_image_type, vgGetError()); + eglDestroySurface(display.disp, h->surface); + vcos_free(h); + status = VCOS_ENOMEM; + goto finish; + } + + h->u.pixmap = image; + + // fill it with black + status = gx_priv_resource_fill(h, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0xff)); + vcos_assert(status == VCOS_SUCCESS); + + *resource_handle = h; +finish: + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +GX_PAINT_T *gx_create_gradient(GRAPHICS_RESOURCE_HANDLE res, + uint32_t start_colour, + uint32_t end_colour) +{ + // holds the two colour stops (offset,r,g,b,a). + VGfloat fill_stops[10]; + GX_CLIENT_STATE_T save; + VGPaint paint = VG_INVALID_HANDLE; + + gx_priv_save(&save, res); + + paint = vgCreatePaint(); + if (!paint) + { + gx_priv_restore(&save); + vcos_log("Could not create paint: vg %d\n", vgGetError()); + vcos_assert(0); + goto finish; + } + + fill_stops[0] = 0.0; + gx_priv_colour_to_paint(start_colour, fill_stops+1); + + fill_stops[5] = 1.0; + gx_priv_colour_to_paint(end_colour, fill_stops+6); + + vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT); + vgSetParameterfv(paint, VG_PAINT_COLOR_RAMP_STOPS, 5*2, fill_stops); + +finish: + gx_priv_restore(&save); + return (GX_PAINT_T*)paint; +} + +/*****************************************************************************/ +void gx_destroy_paint(GRAPHICS_RESOURCE_HANDLE res, GX_PAINT_T *p) +{ + GX_CLIENT_STATE_T save; + VGPaint paint = (VGPaint)p; + gx_priv_save(&save, res); + vgDestroyPaint(paint); + gx_priv_restore(&save); +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_fill_gradient(GRAPHICS_RESOURCE_HANDLE dest, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height, + uint32_t radius, + GX_PAINT_T *p) +{ + /* Define start and end points of gradient, see OpenVG specification, + section 9.3.3. */ + VGfloat gradient[4] = {0.0, 0.0, 0.0, 0.0}; + VGPaint paint = (VGPaint)p; + VGPath path; + GX_CLIENT_STATE_T save; + VCOS_STATUS_T status = VCOS_SUCCESS; + + if (!paint) + return VCOS_EINVAL; + + gx_priv_save(&save, dest); + + if (width == GRAPHICS_RESOURCE_WIDTH) + width = dest->width; + + if (height == GRAPHICS_RESOURCE_HEIGHT) + height = dest->height; + + gradient[2] = width; + + vgSetParameterfv(paint, VG_PAINT_LINEAR_GRADIENT, 4, gradient); + vgSetPaint(paint, VG_FILL_PATH); + + path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_S_32, + 1.0, 0.0, 8, 8, VG_PATH_CAPABILITY_ALL); + if (!path) + { + status = VCOS_ENOMEM; + goto finish; + } + + vguRoundRect(path, (VGfloat)x, (VGfloat)y, (VGfloat)width, (VGfloat)height, + (VGfloat)radius, (VGfloat)radius); + vgDrawPath(path, VG_FILL_PATH); + vgDestroyPath(path); + + vcos_assert(vgGetError() == 0); + +finish: + gx_priv_restore(&save); + + return status; +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_graphics_init(const char *font_dir) +{ + GX_CLIENT_STATE_T save; + VCOS_STATUS_T rc; + + gx_priv_save(&save, NULL); + + rc = gx_priv_initialise(); + if (rc == VCOS_SUCCESS) + rc = gx_priv_font_init(font_dir); + + gx_priv_restore(&save); + + return rc; +} + +/*****************************************************************************/ +int gx_is_double_buffered(void) +{ + return 1; +} + +/*****************************************************************************/ +int32_t graphics_userblt(GRAPHICS_RESOURCE_TYPE_T src_type, + const void *src_data, + const uint32_t src_x, + const uint32_t src_y, + const uint32_t width, + const uint32_t height, + const uint32_t pitch, + GRAPHICS_RESOURCE_HANDLE dest, + const uint32_t x_pos, + const uint32_t y_pos ) +{ + VCOS_STATUS_T status; + VGImageFormat vg_src_type; + int bytes_per_pixel; + GX_CLIENT_STATE_T save; + + status = convert_image_type(src_type, &vg_src_type, &bytes_per_pixel); + if (status != VCOS_SUCCESS) + return status; + + gx_priv_save(&save, dest); + + if (dest->type == GX_PBUFFER) + { + vgImageSubData(dest->u.pixmap, + src_data, + pitch, + vg_src_type, + x_pos, y_pos, width, height); + } + else if (dest->type == GX_WINDOW) + { + // need to invert this as VG thinks zero is at the bottom + // while graphics_x thinks it is at the top. + vgWritePixels((uint8_t*)src_data + pitch*(height-1), + -pitch, + vg_src_type, + x_pos, dest->height-y_pos-height, width, height); + } + else + { + vcos_assert(0); + } + + if (vgGetError() == 0) + status = VCOS_SUCCESS; + else + { + vcos_assert(0); + status = VCOS_EINVAL; + } + + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +int32_t graphics_resource_text_dimensions( GRAPHICS_RESOURCE_HANDLE resource_handle, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height ) +{ + return graphics_resource_text_dimensions_ext(resource_handle, text, text_length, width, height, ATEXT_FONT_SIZE); +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_render_arrowhead(GRAPHICS_RESOURCE_HANDLE res, + uint32_t tip_x, uint32_t tip_y, + int32_t w, int32_t h, + GX_PAINT_T *p) +{ + VGfloat gradient[4]; + VGPaint paint = (VGPaint)p; + VGPath path; + VCOS_STATUS_T status = VCOS_SUCCESS; + + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + if (!paint) + { + vcos_assert(0); + status = VCOS_EINVAL; + goto finish; + } + + gradient[0] = 0.0; gradient[1] = 0.0; + gradient[2] = w; gradient[2] = 0.0; + + vgSetParameterfv(paint, VG_PAINT_LINEAR_GRADIENT, 4, gradient); + vgSetPaint(paint, VG_FILL_PATH); + + path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_S_32, + 1.0, 0.0, 8, 8, VG_PATH_CAPABILITY_ALL); + if (!path) + { + status = VCOS_ENOMEM; + goto finish; + } + VGfloat points[] = { + (VGfloat)tip_x, (VGfloat)tip_y, + (VGfloat)tip_x + w, (VGfloat)tip_y + h/2, + (VGfloat)tip_x + w, (VGfloat)tip_y - h/2, + }; + + vguPolygon(path, points, 3, 1); + + vgDrawPath(path, VG_FILL_PATH); + vgDestroyPath(path); + + vcos_assert(vgGetError()==0); + +finish: + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +int32_t gx_apply_alpha( GRAPHICS_RESOURCE_HANDLE resource_handle, + const uint8_t alpha ) +{ + vcos_assert(resource_handle); + if (resource_handle->type != GX_PBUFFER) + { + vcos_assert(0); + return -1; + } + resource_handle->alpha = 1.0*alpha/255; + return 0; +} + +/*****************************************************************************/ +int32_t graphics_resource_set_alpha_per_colour( GRAPHICS_RESOURCE_HANDLE res, + const uint32_t colour, + const uint8_t alpha ) +{ + GX_ERROR("Not implemented yet!"); + return 0; +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_get_pixels(const GRAPHICS_RESOURCE_HANDLE res, void **pixels) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + /* Default to top-top-bottom raster scan order */ + status = gx_priv_get_pixels(res, pixels, GX_TOP_BOTTOM); + + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +VCOS_STATUS_T gx_get_pixels_in_raster_order(const GRAPHICS_RESOURCE_HANDLE res, + void **pixels, + GX_RASTER_ORDER_T raster_order) +{ + VCOS_STATUS_T status = VCOS_SUCCESS; + GX_CLIENT_STATE_T save; + gx_priv_save(&save, res); + + status = gx_priv_get_pixels(res, pixels, raster_order); + + gx_priv_restore(&save); + return status; +} + +/*****************************************************************************/ +void gx_free_pixels(const GRAPHICS_RESOURCE_HANDLE res, void *pixels) +{ + vcos_free(pixels); +} + +VCOS_STATUS_T gx_bind_vg( GX_CLIENT_STATE_T *save, GRAPHICS_RESOURCE_HANDLE res ) +{ + gx_priv_save(save, res); + vcos_assert(vgGetError()==0); + return VCOS_SUCCESS; +} + +/** Unbind VG */ +void gx_unbind_vg(GX_CLIENT_STATE_T *restore) +{ + gx_priv_restore(restore); +} + + +GX_CLIENT_STATE_T *gx_alloc_context(void) +{ + GX_CLIENT_STATE_T *ret = vcos_calloc(1,sizeof(*ret), "gx_client_state"); + return ret; +} + +void gx_free_context(GX_CLIENT_STATE_T *state) +{ + vcos_free(state); +} + +void gx_convert_colour(uint32_t colour, float *dest) +{ + gx_priv_colour_to_paint(colour, dest); +} + + +#define MAX_DISPLAY_HANDLES 4 + +#define CHANGE_LAYER (1<<0) +#define CHANGE_OPACITY (1<<1) +#define CHANGE_DEST (1<<2) +#define CHANGE_SRC (1<<3) +#define CHANGE_MASK (1<<4) +#define CHANGE_XFORM (1<<5) + +typedef struct +{ + /** Keep a display handle going for each connected screen (LCD, HDMI). */ + DISPMANX_DISPLAY_HANDLE_T screens[MAX_DISPLAY_HANDLES]; + int refcounts[MAX_DISPLAY_HANDLES]; + + //a flag to count the number of dispman starts that have been invoked + + uint32_t dispman_start_count; + // maintain the single global handle to the update in progress + DISPMANX_UPDATE_HANDLE_T current_update; + + VCOS_MUTEX_T lock; +} gx_priv_state_t; + +static gx_priv_state_t gx; + +void gx_priv_init(void) +{ + vcos_mutex_create(&gx.lock,NULL); +} + +void gx_priv_destroy(void) +{ + vcos_mutex_delete(&gx.lock); +} + + +static +int32_t gx_priv_open_screen(uint32_t index, DISPMANX_DISPLAY_HANDLE_T *pscreen) +{ + int ret = -1; + vcos_mutex_lock(&gx.lock); + + if (gx.refcounts[index] != 0) + { + *pscreen = gx.screens[index]; + gx.refcounts[index]++; + ret = 0; + } + else + { + DISPMANX_DISPLAY_HANDLE_T h = vc_dispmanx_display_open(index); + if (h == DISPMANX_NO_HANDLE) + { + GX_LOG("Could not open dispmanx display %d", index); + ret = -1; + goto finish; + } + gx.screens[index] = h; + gx.refcounts[index] = 1; + *pscreen = h; + ret = 0; + } +finish: + vcos_mutex_unlock(&gx.lock); + return ret; +} + +static +int32_t gx_priv_release_screen(uint32_t index) +{ + vcos_mutex_lock(&gx.lock); + gx.refcounts[index]--; + if (gx.refcounts[index] == 0) + { + vc_dispmanx_display_close(gx.screens[index]); + gx.screens[index] = DISPMANX_NO_HANDLE; + } + vcos_mutex_unlock(&gx.lock); + return 0; +} + + + + +int gx_priv_create_native_window(uint32_t screen_id, + uint32_t w, uint32_t h, + GRAPHICS_RESOURCE_TYPE_T type, + GX_NATIVE_WINDOW_T *win, + void **cookie) +{ + int rc; + DISPMANX_DISPLAY_HANDLE_T dispmanx_display; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + DISPMANX_UPDATE_HANDLE_T current_update; + *cookie = NULL; + + rc = gx_priv_open_screen(screen_id, &dispmanx_display); + if (rc < 0) + { + GX_LOG("Could not open display %d", screen_id); + goto fail_screen; + } + + current_update = vc_dispmanx_update_start(0); + if (!current_update) + { + GX_LOG("Could not start update on screen %d", screen_id); + goto fail_update; + } + + src_rect.x = src_rect.y = 0; + src_rect.width = w << 16; + src_rect.height = h << 16; + + dst_rect.x = dst_rect.y = 0; + dst_rect.width = dst_rect.height = 1; + + win->egl_win.width = w; + win->egl_win.height = h; + VC_DISPMANX_ALPHA_T alpha; + memset(&alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T)); + alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE; + + DISPMANX_CLAMP_T clamp; + memset(&clamp, 0x0, sizeof(DISPMANX_CLAMP_T)); + + win->egl_win.element = vc_dispmanx_element_add(current_update, dispmanx_display, + 0 /* layer */, &dst_rect, + 0 /* src */, &src_rect, + DISPMANX_PROTECTION_NONE, + &alpha /* alpha */, + &clamp /* clamp */, + 0 /* transform */); + + if ( !win->egl_win.element ) + { + GX_LOG("Could not add element %dx%d",w,h); + vc_dispmanx_update_submit_sync(current_update); + rc = -1; + } + + // have to pass back the update so it can be completed *After* the + // window has been initialised (filled with background colour). + *cookie = (void*)current_update; + + return 0; + +fail_update: + gx_priv_release_screen(screen_id); +fail_screen: + return rc; +} + +void gx_priv_finish_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res, + void *current_update) +{ + vc_dispmanx_update_submit_sync((DISPMANX_UPDATE_HANDLE_T)current_update); +} + +void +gx_priv_destroy_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res) +{ + DISPMANX_UPDATE_HANDLE_T current_update; + + if((current_update = vc_dispmanx_update_start(0)) != 0) + { + int ret = vc_dispmanx_element_remove(current_update, res->u.native_window.egl_win.element); + vcos_assert(ret == 0); + ret = vc_dispmanx_update_submit_sync(current_update); + vcos_assert(ret == 0); + } + + gx_priv_release_screen(res->screen_id); +} + + +/*********************************************************** + * Name: graphics_get_display_size + * + * Arguments: + * void + * + * Description: Return size of display + * + * Returns: int32_t: + * >=0 if it succeeded + * + ***********************************************************/ +int32_t graphics_get_display_size( const uint16_t display_number, + uint32_t *width, + uint32_t *height) +{ + DISPMANX_MODEINFO_T mode_info; + int32_t success = -1; + DISPMANX_DISPLAY_HANDLE_T disp; + vcos_assert(width && height); + *width = *height = 0; + + if(vcos_verify(display_number < MAX_DISPLAY_HANDLES)) + { + // TODO Shouldn't this close the display if it wasn't previously open? + if (gx_priv_open_screen(display_number, &disp) < 0) + { + vcos_assert(0); + return -1; + } + success = vc_dispmanx_display_get_info(disp, &mode_info); + + if( success >= 0 ) + { + *width = mode_info.width; + *height = mode_info.height; + vcos_assert(*height > 64); + } + else + { + vcos_assert(0); + } + } + + return success; +} + +static inline uint16_t auto_size(uint16_t arg, uint16_t actual_size) +{ + return arg == GRAPHICS_RESOURCE_WIDTH ? actual_size : arg; +} + +int32_t graphics_display_resource( GRAPHICS_RESOURCE_HANDLE res, + const uint16_t screen_number, + const int16_t z_order, + const uint16_t offset_x, + const uint16_t offset_y, + const uint16_t dest_width, + const uint16_t dest_height, + const VC_DISPMAN_TRANSFORM_T transform, + const uint8_t display ) +{ + DISPMANX_UPDATE_HANDLE_T update; + int32_t rc; + int xform_changed; + + if (!res) + { + // mimics old behaviour. + (void)vcos_verify(0); + return 0; + } + vcos_assert(res->magic == RES_MAGIC); + + xform_changed = transform != res->transform; + res->transform = transform; + + rc = graphics_update_start(); + update = gx.current_update; + vcos_assert(rc == 0); + + if (display) + { + VC_RECT_T src_rect, dest_rect; + + int32_t src_width = res->width; + int32_t src_height = res->height; + + uint32_t change_flags = CHANGE_LAYER; + + // has the destination position changed? + uint32_t w = auto_size(dest_width, res->width); + uint32_t h = auto_size(dest_height, res->height); + + vcos_assert(screen_number == res->screen_id); + + if (gx.screens[screen_number] == 0) + { + vcos_assert(0); + DISPMANX_DISPLAY_HANDLE_T display_handle; + gx_priv_open_screen(screen_number, &display_handle); + } + + if ((offset_x != res->dest.x) || + (offset_y != res->dest.y) || + (h != res->dest.height) || + (w != res->dest.width)) + { + change_flags |= CHANGE_DEST; + res->dest.x = offset_x; + res->dest.y = offset_y; + res->dest.height = h; + res->dest.width = w; + } + + if (xform_changed) + change_flags |= CHANGE_XFORM; + + vc_dispmanx_rect_set( &src_rect, 0, 0, ((uint32_t)src_width)<<16, ((uint32_t)src_height)<<16 ); + vc_dispmanx_rect_set( &dest_rect, offset_x, offset_y, w, h); + + rc = vc_dispmanx_element_change_attributes(update, + res->u.native_window.egl_win.element, + change_flags, + z_order, /* layer */ + 0xff, /* opacity */ + &dest_rect, + &src_rect, + 0, transform); + + vcos_assert(rc==0); + gx_priv_flush(res); + + } + else + { + vgFinish(); + eglWaitClient(); + rc = vc_dispmanx_element_change_source(update, res->u.native_window.egl_win.element, 0); + vcos_assert(rc==0); + } + + rc = graphics_update_end(); + vcos_assert(rc==0); + + return rc; +} + +/*********************************************************** + * Name: graphics_update_start + * + * Arguments: + * void + * + * Description: Starts an update UNLESS and update is already in progress + * + * Returns: int32_t: + * >=0 if it succeeded + * + ***********************************************************/ +int32_t graphics_update_start(void) +{ + int32_t success = 0; + + //check we are not already in an update + if ( 0 == gx.dispman_start_count ) + { + gx.current_update = vc_dispmanx_update_start( 10 ); + if( gx.current_update == DISPMANX_NO_HANDLE ) + { + //error + success = -1; + vc_assert( 0 ); + } + } + + if( success == 0 ) + { + //inc the counter + gx.dispman_start_count++; + } + + return success; +} + + +/*********************************************************** + * Name: graphics_update_end + * + * Arguments: + * void + * + * Description: Ends an update UNLESS more than one update is in progress + * + * Returns: int32_t: + * >=0 if it succeeded + * + ***********************************************************/ +int32_t graphics_update_end( void ) +{ + int32_t success = -1; + + // make sure you are checking the return value of graphics_update_start + if(vcos_verify(gx.current_update != DISPMANX_NO_HANDLE)) + { + //check we are in an update + if(vcos_verify(gx.dispman_start_count > 0)) + { + //dec the counter + gx.dispman_start_count--; + + success = 0; + + //is the counter now 0? + if( 0 == gx.dispman_start_count ) + { + eglWaitClient(); + if( vc_dispmanx_update_submit_sync( gx.current_update ) != 0 ) + { + //error + success = -1; + vc_assert( 0 ); + } + } + } + } + + return success; +} + diff --git a/gui/libs/vgfont/graphics_x_private.h b/gui/libs/vgfont/graphics_x_private.h @@ -0,0 +1,366 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Graphics library for VG + +#ifndef GRAPHICS_X_PRIVATE_H +#define GRAPHICS_X_PRIVATE_H + +#define VCOS_LOG_CATEGORY (&gx_log_cat) + +#include "EGL/egl.h" +#include "EGL/eglext.h" +#include "VG/openvg.h" +#include "VG/vgu.h" + +#include "vgfont.h" +#include "bcm_host.h" + +extern VCOS_LOG_CAT_T gx_log_cat; + +#define LOG_ERR( fmt, arg... ) vcos_log_error( "%s:%d " fmt, __func__, __LINE__, ##arg) + +#define GX_ERROR(format, arg...) if (1) {} else printf( format "\n", ##arg) +#define GX_LOG(format, arg...) if (1) {} else printf( format "\n", ##arg) +#define GX_TRACE(format, arg...) if (1) {} else printf( format "\n", ##arg) + +typedef struct +{ + EGL_DISPMANX_WINDOW_T egl_win; +} GX_NATIVE_WINDOW_T; + +typedef enum +{ + GX_TOP_BOTTOM, + GX_BOTTOM_TOP, +} GX_RASTER_ORDER_T; + +typedef struct {} GX_PAINT_T; + +typedef struct GX_CLIENT_STATE_T GX_CLIENT_STATE_T; +typedef struct { + EGLDisplay disp; +} GX_DISPLAY_T; + +struct GX_DISPLAY_T +{ + EGLDisplay disp; +}; + +typedef enum +{ + GX_WINDOW, GX_PIXMAP, GX_PBUFFER +} GX_RES_TYPE; + +#define RES_MAGIC ('G'<<24|'X'<<16|'R'<<8|'S'<<0) +#define GX_PRIV_FLAG_FLIP (1<<0) + +/** + * Structure encapsulating the per-surface state. + ***********************************************************/ +typedef struct GRAPHICS_RESOURCE_HANDLE_TABLE_T +{ + union + { + GX_NATIVE_WINDOW_T native_window; + VGImage pixmap; + } u; + GX_RES_TYPE type; + + uint32_t magic; /** To work around broken create interface */ + int context_bound; + const char *last_caller; + EGLSurface surface; + EGLContext context; + EGLConfig config; + uint32_t screen_id; /** 0-LCD, etc */ + uint16_t width; + uint16_t height; + GRAPHICS_RESOURCE_TYPE_T restype; + VC_DISPMAN_TRANSFORM_T transform; + + VC_RECT_T dest; /** destination rectangle in use, for book-keeping */ + + VGfloat alpha; +} GRAPHICS_RESOURCE_HANDLE_TABLE_T; + +/** + * Structure used to store an EGL client state. + ***********************************************************/ +struct GX_CLIENT_STATE_T +{ + EGLSurface read_surface; + EGLSurface draw_surface; + EGLContext context; + EGLenum api; + GRAPHICS_RESOURCE_HANDLE res; +}; + +/** + * \fixme add documentation + * + ***********************************************************/ +void gx_priv_init(void); + +/** + * \fixme add documentation + * + ***********************************************************/ +void gx_priv_destroy(void); + +/** + * \fixme add documentation + * + * @param col colour + * + * @param rgba OpenVG paint colour + * + ***********************************************************/ +void gx_priv_colour_to_paint(uint32_t col, VGfloat *rgba); + +/** + * Save current EGL client state. + * + * @param state upon return, holds the saved EGL client state. + * + * @param res handle to the surface the EGL client state belongs to (may be <code>NULL</code>). + * + */ +void gx_priv_save(GX_CLIENT_STATE_T *state, GRAPHICS_RESOURCE_HANDLE res); + +/** + * Restore current EGL client state. + * + * @param state the EGL client state to restore. + * + */ +void gx_priv_restore(GX_CLIENT_STATE_T *state); + +/** + * Create a native window for a surface. + * + * @param screen_id \fixme + * + * @param w width of the window + * + * @param h height of the window + * + * @param type color/raster format of the resource + * + * @param win upon successful return, holds a handle to the native window + * + * @param cookie \fixme + * + * @return VCOS_SUCCESS on success, or error code. + */ +int gx_priv_create_native_window(uint32_t screen_id, + uint32_t w, uint32_t h, + GRAPHICS_RESOURCE_TYPE_T type, + GX_NATIVE_WINDOW_T *win, + void **cookie); + +/** + * Destroy native window bound to surface. + * + * @param res Handle to surface. + * + */ +void gx_priv_destroy_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res); + +/** + * Initialise font from the given directory. + * + * @param font_dir path to font + * + * \fixme only supports Vera.tff at the moment? + * + * @return VCOS_SUCCESS on success, or error code. + */ +VCOS_STATUS_T gx_priv_font_init(const char *font_dir); + +/** + * \fixme add documentation + * + ***********************************************************/ +void gx_priv_font_term(void); + +/** + * Fill an area of a surface with a single colour. + * + * @param res Handle to surface. + * + * @param x x-offset of area to fill + * + * @param y y-offset of area to fill + * + * @param width width of area to fill + * + * @param height height of area to fill + * + * @param fill_colour fill colour + * + ***********************************************************/ +VCOS_STATUS_T gx_priv_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ); + +/** + * Render text into a surface + * + * @param disp Handle to display. + * + * @param res Handle to surface. + * + * @param x x-offset + * + * @param y y-offset + * + * @param width bounding rectangle width + * + * @param height bounding rectangle height + * + * @param fg_colour foreground color + * + * @param bg_colour background color + * + * @param text text to render + * + * @param text_length length of text + * + * @param text_size size of text + * + ***********************************************************/ +VCOS_STATUS_T gx_priv_render_text( GX_DISPLAY_T *disp, + GRAPHICS_RESOURCE_HANDLE res, + int32_t x, + int32_t y, + uint32_t width, + uint32_t height, + uint32_t fg_colour, + uint32_t bg_colour, + const char *text, + uint32_t text_length, + uint32_t text_size ); + +/** + * Flush a surface. + * + * @param res Handle to surface. + * + ***********************************************************/ +void gx_priv_flush(GRAPHICS_RESOURCE_HANDLE res); + +/** + * Called after the EGL/VG initialisation of a window has completed + * following its creation. + * + * @param res ??? + * + * @param cookie ??? + * + ***********************************************************/ +void gx_priv_finish_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res, + void *cookie); + +/** + * Flush font cache. + * + ***********************************************************/ +void gx_font_cache_flush(void); + +/** + * Read a bitmap (.BMP) image from the given file. + * + * @param filename filename (must not be <code>NULL</code>). + * + * @param width holds the width of the image upon return. + * + * @param height holds the height of the image upon return. + * + * @param pitch_bytes holds the pitch of the image data (in bytes) upon return. + * + * @param restype holds the type of the image upon return. + * + * @param vg_format holds the OpenVG image format upon return. + * + * @param flags holds flags describing image properties upon return. + * + * @param image_data_size holds size of the image data upon return. + * + * @param pimage_data holds the image data buffer upon return (must not be <code>NULL</code>), + * the caller is responsible for releasing the buffer afterwards. + * + * @return 0 if success, non-zero otherwise (in which case the output parameters + * may be invalid). + * + ***********************************************************/ +int gx_priv_read_bmp(const char *file_name, + uint32_t *width, uint32_t *height, uint32_t *pitch_bytes, + GRAPHICS_RESOURCE_TYPE_T *restype, + VGImageFormat *vg_format, + uint32_t *flags, + uint32_t *image_data_size, + void **pimage_data); + +/** + * Read a Targa (.TGA) image from the given file. + * + * @param filename filename (must not be <code>NULL</code>). + * + * @param width holds the width of the image upon return. + * + * @param height holds the height of the image upon return. + * + * @param pitch_bytes holds the pitch of the image data (in bytes) upon return. + * + * @param restype holds the type of the image upon return. + * + * @param vg_format holds the OpenVG image format upon return. + * + * @param flags holds flags describing image properties upon return. + * + * @param image_data_size holds size of the image data upon return. + * + * @param pimage_data holds the image data buffer upon return (must not be <code>NULL</code>), + * the caller is responsible for releasing the memory afterwards. + * + * @return 0 if success, non-zero otherwise (in which case the output parameters. + * may be invalid). + * + ***********************************************************/ +int gx_priv_read_tga(const char *file_name, + uint32_t *width, uint32_t *height, uint32_t *pitch_bytes, + GRAPHICS_RESOURCE_TYPE_T *restype, + VGImageFormat *vg_format, + uint32_t *flags, + uint32_t *image_data_size, + void **pimage_data); + +#endif diff --git a/gui/libs/vgfont/libvgfont.a b/gui/libs/vgfont/libvgfont.a Binary files differ. diff --git a/gui/libs/vgfont/vgfont.h b/gui/libs/vgfont/vgfont.h @@ -0,0 +1,136 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +#ifndef VCFTLIB_H +#define VCFTLIB_H + +#include <stdint.h> +#include "interface/vmcs_host/vc_dispservice_x_defs.h" +#include "interface/vctypes/vc_image_types.h" +#include "interface/vcos/vcos.h" + +//Definitions which in certain functions can be used to mean the actual width and height of a resource, without +//having to know the data implicitly. +#define GRAPHICS_RESOURCE_WIDTH 0xFFFF +#define GRAPHICS_RESOURCE_HEIGHT 0xFFFF + +#define R_888_MASK (0x00FF0000) +#define G_888_MASK (0x0000FF00) +#define B_888_MASK (0x000000FF) +#define ALPHA_888_MASK (0xFF000000) +#define GRAPHICS_RGBA32( r, g, b, a ) GRAPHICS_RGBA888( r, g, b, a ) +#define GRAPHICS_RGBA888( r, g, b, a ) ( (((a) << (8+8+8)) & ALPHA_888_MASK) | (((b) << (8+8)) & R_888_MASK) | (((g) << 8) & G_888_MASK) | ((r) & B_888_MASK) ) +#define GRAPHICS_TRANSPARENT_COLOUR 0x00000001UL + +//resource defs + +typedef enum +{ + GRAPHICS_RESOURCE_HANDLE_TYPE_MIN, + + GRAPHICS_RESOURCE_RGB565, + GRAPHICS_RESOURCE_RGB888, /* 888 format is ONLY used when loading bitmaps + - you can't create or delete bitmaps with this format */ + GRAPHICS_RESOURCE_RGBA32, + GRAPHICS_RESOURCE_TF_RGB32A, + GRAPHICS_RESOURCE_TF_RGB565, + GRAPHICS_RESOURCE_YUV420, + + GRAPHICS_RESOURCE_HANDLE_TYPE_MAX + +} GRAPHICS_RESOURCE_TYPE_T; + + +typedef struct GRAPHICS_RESOURCE_HANDLE_TABLE_T *GRAPHICS_RESOURCE_HANDLE; + +VCOS_STATUS_T gx_graphics_init(const char *font_dir); +int32_t graphics_delete_resource( GRAPHICS_RESOURCE_HANDLE res ); +VCOS_STATUS_T gx_create_window( uint32_t screen_id, + uint32_t width, + uint32_t height, + GRAPHICS_RESOURCE_TYPE_T image_type, + GRAPHICS_RESOURCE_HANDLE *resource_handle ); + +int32_t graphics_display_resource( GRAPHICS_RESOURCE_HANDLE res, + const uint16_t screen_number, + const int16_t z_order, + const uint16_t offset_x, + const uint16_t offset_y, + const uint16_t dest_width, + const uint16_t dest_height, + const VC_DISPMAN_TRANSFORM_T transform, + const uint8_t display ); + +int32_t graphics_resource_fill(GRAPHICS_RESOURCE_HANDLE res, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t fill_colour ); + +int32_t graphics_update_displayed_resource(GRAPHICS_RESOURCE_HANDLE res, + const uint32_t x_offset, + const uint32_t y_offset, + const uint32_t width, + const uint32_t height ); + +int32_t graphics_resource_render_text_ext( GRAPHICS_RESOURCE_HANDLE res, + const int32_t x, + const int32_t y, + const uint32_t width, + const uint32_t height, + const uint32_t fg_colour, + const uint32_t bg_colour, + const char *text, + const uint32_t text_length, + const uint32_t text_size ); + +int32_t graphics_resource_text_dimensions_ext(GRAPHICS_RESOURCE_HANDLE res, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height, + const uint32_t text_size ); + +int32_t graphics_get_resource_size( + const GRAPHICS_RESOURCE_HANDLE res, + uint32_t *w, + uint32_t *h); + +int32_t graphics_update_start(void); + +int32_t graphics_update_end( void ); + +int32_t graphics_resource_text_dimensions( GRAPHICS_RESOURCE_HANDLE resource_handle, + const char *text, + const uint32_t text_length, + uint32_t *width, + uint32_t *height ); + +#endif diff --git a/gui/libs/vgfont/vgft.c b/gui/libs/vgfont/vgft.c @@ -0,0 +1,416 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +#include <assert.h> +#include <stdlib.h> + +#include "graphics_x_private.h" +#include "vgft.h" + +static FT_Library lib; + +int vgft_init(void) +{ + if (FT_Init_FreeType(&lib) == 0) + return 0; + else + { + return -1; + } +} + +void vgft_term(void) +{ + FT_Done_FreeType(lib); +} + +#define SEGMENTS_COUNT_MAX 256 +#define COORDS_COUNT_MAX 1024 + +static VGuint segments_count; +static VGubyte segments[SEGMENTS_COUNT_MAX]; +static VGuint coords_count; +static VGfloat coords[COORDS_COUNT_MAX]; + +static VGfloat float_from_26_6(FT_Pos x) +{ + return (VGfloat)x / 64.0f; +} + +static void convert_contour(const FT_Vector *points, const char *tags, short points_count) +{ + int first_coords = coords_count; + + int first = 1; + char last_tag = 0; + int c = 0; + + for (; points_count != 0; ++points, ++tags, --points_count) { + ++c; + + char tag = *tags; + if (first) { + assert(tag & 0x1); + assert(c==1); c=0; + segments[segments_count++] = VG_MOVE_TO; + first = 0; + } else if (tag & 0x1) { + /* on curve */ + + if (last_tag & 0x1) { + /* last point was also on -- line */ + assert(c==1); c=0; + segments[segments_count++] = VG_LINE_TO; + } else { + /* last point was off -- quad or cubic */ + if (last_tag & 0x2) { + /* cubic */ + assert(c==3); c=0; + segments[segments_count++] = VG_CUBIC_TO; + } else { + /* quad */ + assert(c==2); c=0; + segments[segments_count++] = VG_QUAD_TO; + } + } + } else { + /* off curve */ + + if (tag & 0x2) { + /* cubic */ + + assert((last_tag & 0x1) || (last_tag & 0x2)); /* last either on or off and cubic */ + } else { + /* quad */ + + if (!(last_tag & 0x1)) { + /* last was also off curve */ + + assert(!(last_tag & 0x2)); /* must be quad */ + + /* add on point half-way between */ + assert(c==2); c=1; + segments[segments_count++] = VG_QUAD_TO; + VGfloat x = (coords[coords_count - 2] + float_from_26_6(points->x)) * 0.5f; + VGfloat y = (coords[coords_count - 1] + float_from_26_6(points->y)) * 0.5f; + coords[coords_count++] = x; + coords[coords_count++] = y; + } + } + } + last_tag = tag; + + coords[coords_count++] = float_from_26_6(points->x); + coords[coords_count++] = float_from_26_6(points->y); + } + + if (last_tag & 0x1) { + /* last point was also on -- line (implicit with close path) */ + assert(c==0); + } else { + ++c; + + /* last point was off -- quad or cubic */ + if (last_tag & 0x2) { + /* cubic */ + assert(c==3); c=0; + segments[segments_count++] = VG_CUBIC_TO; + } else { + /* quad */ + assert(c==2); c=0; + segments[segments_count++] = VG_QUAD_TO; + } + + coords[coords_count++] = coords[first_coords + 0]; + coords[coords_count++] = coords[first_coords + 1]; + } + + segments[segments_count++] = VG_CLOSE_PATH; +} + +static void convert_outline(const FT_Vector *points, const char *tags, const short *contours, short contours_count, short points_count) +{ + segments_count = 0; + coords_count = 0; + + short last_contour = 0; + for (; contours_count != 0; ++contours, --contours_count) { + short contour = *contours + 1; + convert_contour(points + last_contour, tags + last_contour, contour - last_contour); + last_contour = contour; + } + assert(last_contour == points_count); + + assert(segments_count <= SEGMENTS_COUNT_MAX); /* oops... we overwrote some memory */ + assert(coords_count <= COORDS_COUNT_MAX); +} + +VCOS_STATUS_T vgft_font_init(VGFT_FONT_T *font) +{ + font->ft_face = NULL; + font->vg_font = vgCreateFont(0); + if (font->vg_font == VG_INVALID_HANDLE) + { + return VCOS_ENOMEM; + } + return VCOS_SUCCESS; +} + +VCOS_STATUS_T vgft_font_load_mem(VGFT_FONT_T *font, void *mem, size_t len) +{ + if (FT_New_Memory_Face(lib, mem, len, 0, &font->ft_face)) + { + return VCOS_EINVAL; + } + return VCOS_SUCCESS; +} + +VCOS_STATUS_T vgft_font_load_file(VGFT_FONT_T *font, const char *file) +{ + if (FT_New_Face(lib, file, 0, &font->ft_face)) { + return VCOS_EINVAL; + } + return VCOS_SUCCESS; +} + +VCOS_STATUS_T vgft_font_convert_glyphs(VGFT_FONT_T *font, unsigned int char_height, unsigned int dpi_x, unsigned int dpi_y) +{ + FT_UInt glyph_index; + FT_ULong ch; + + if (FT_Set_Char_Size(font->ft_face, 0, char_height, dpi_x, dpi_y)) + { + FT_Done_Face(font->ft_face); + vgDestroyFont(font->vg_font); + return VCOS_EINVAL; + } + + ch = FT_Get_First_Char(font->ft_face, &glyph_index); + + while (ch != 0) + { + if (FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT)) { + FT_Done_Face(font->ft_face); + vgDestroyFont(font->vg_font); + return VCOS_ENOMEM; + } + + VGPath vg_path; + FT_Outline *outline = &font->ft_face->glyph->outline; + if (outline->n_contours != 0) { + vg_path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL); + assert(vg_path != VG_INVALID_HANDLE); + + convert_outline(outline->points, outline->tags, outline->contours, outline->n_contours, outline->n_points); + vgAppendPathData(vg_path, segments_count, segments, coords); + } else { + vg_path = VG_INVALID_HANDLE; + } + + VGfloat origin[] = { 0.0f, 0.0f }; + VGfloat escapement[] = { float_from_26_6(font->ft_face->glyph->advance.x), float_from_26_6(font->ft_face->glyph->advance.y) }; + vgSetGlyphToPath(font->vg_font, glyph_index, vg_path, VG_FALSE, origin, escapement); + + if (vg_path != VG_INVALID_HANDLE) { + vgDestroyPath(vg_path); + } + ch = FT_Get_Next_Char(font->ft_face, ch, &glyph_index); + } + + return VCOS_SUCCESS; +} + +void vgft_font_term(VGFT_FONT_T *font) +{ + if (font->ft_face) + FT_Done_Face(font->ft_face); + if (font->vg_font) + vgDestroyFont(font->vg_font); + memset(font, 0, sizeof(*font)); +} + + +#define CHAR_COUNT_MAX 200 +static VGuint glyph_indices[CHAR_COUNT_MAX]; +static VGfloat adjustments_x[CHAR_COUNT_MAX]; +static VGfloat adjustments_y[CHAR_COUNT_MAX]; + +// Draws the first char_count characters from text, with adjustments, starting +// from the current origin. The peek argument indicates whether to peek ahead +// and get a final adjustment based on the next character past char_count, or +// else just assume that this is the end of the text and add no final +// adjustment. + +static void draw_chars(VGFT_FONT_T *font, const char *text, int char_count, VGbitfield paint_modes, int peek) { + // Put in first character + glyph_indices[0] = FT_Get_Char_Index(font->ft_face, text[0]); + int prev_glyph_index = glyph_indices[0]; + + // Calculate glyph_indices and adjustments + int i; + FT_Vector kern; + for (i = 1; i != char_count; ++i) { + int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); + if (!glyph_index) { return; } + glyph_indices[i] = glyph_index; + + if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0); + adjustments_x[i - 1] = float_from_26_6(kern.x); + adjustments_y[i - 1] = float_from_26_6(kern.y); + + prev_glyph_index = glyph_index; + } + + // Get the last adjustment? + if (peek) { + int peek_glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); + if (!peek_glyph_index) { return; } + if (FT_Get_Kerning(font->ft_face, prev_glyph_index, peek_glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0); + adjustments_x[char_count - 1] = float_from_26_6(kern.x); + adjustments_y[char_count - 1] = float_from_26_6(kern.y); + } else { + adjustments_x[char_count - 1] = 0.0f; + adjustments_y[char_count - 1] = 0.0f; + } + + vgDrawGlyphs(font->vg_font, char_count, glyph_indices, adjustments_x, adjustments_y, paint_modes, VG_FALSE); +} + +// Goes to the x,y position and draws arbitrary number of characters, draws +// iteratively if the char_count exceeds the max buffer size given above. + +static void draw_line(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, int char_count, VGbitfield paint_modes) { + if (char_count == 0) return; + + // Set origin to requested x,y + VGfloat glor[] = { x, y }; + vgSetfv(VG_GLYPH_ORIGIN, 2, glor); + + // Draw the characters in blocks to reuse buffer memory + const char *curr_text = text; + int chars_left = char_count; + while (chars_left > CHAR_COUNT_MAX) { + draw_chars(font, curr_text, CHAR_COUNT_MAX, paint_modes, 1); + chars_left -= CHAR_COUNT_MAX; + curr_text += CHAR_COUNT_MAX; + } + + // Draw the last block + draw_chars(font, curr_text, chars_left, paint_modes, 0); +} + +void vgft_font_draw(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, unsigned text_length, VGbitfield paint_modes) +{ + VGfloat descent = float_from_26_6(font->ft_face->size->metrics.descender); + int last_draw = 0; + int i = 0; + y -= descent; + for (;;) { + int last = !text[i] || (text_length && i==text_length); + + if ((text[i] == '\n') || last) + { + draw_line(font, x, y, text + last_draw, i - last_draw, paint_modes); + last_draw = i+1; + y -= float_from_26_6(font->ft_face->size->metrics.height); + } + if (last) + { + break; + } + ++i; + } +} + +// Get text extents for a single line + +static void line_extents(VGFT_FONT_T *font, VGfloat *x, VGfloat *y, const char *text, int chars_count) +{ + int i; + int prev_glyph_index = 0; + if (chars_count == 0) return; + + for (i=0; i < chars_count; i++) + { + int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]); + if (!glyph_index) return; + + if (i != 0) + { + FT_Vector kern; + if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index, + FT_KERNING_DEFAULT, &kern)) + { + assert(0); + } + *x += float_from_26_6(kern.x); + *y += float_from_26_6(kern.y); + } + FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT); + *x += float_from_26_6(font->ft_face->glyph->advance.x); + + prev_glyph_index = glyph_index; + } +} + +// Text extents for some ASCII text. +// +// Use text_length if non-zero, otherwise look for trailing '\0'. + +void vgft_get_text_extents(VGFT_FONT_T *font, + const char *text, + unsigned text_length, + VGfloat unused0, VGfloat unused1, + VGfloat *w, VGfloat *h) { + int last_draw = 0; + VGfloat max_x = 0; + VGfloat y = 0; + + int i, last; + for (i = 0, last = 0; !last; ++i) { + last = !text[i] || (text_length && i==text_length); + if ((text[i] == '\n') || last) { + VGfloat x = 0; + line_extents(font, &x, &y, text + last_draw, i - last_draw); + last_draw = i + 1; + y -= float_from_26_6(font->ft_face->size->metrics.height); + if (x > max_x) max_x = x; + } + } + *w = max_x; + *h = -y; +} + +// Get y offset for first line; mitigates issue of start y being middle of block +// for multiline renders by vgft_font_draw. Currently simple, may be worth +// adding y kerning? + +VGfloat vgft_first_line_y_offset(VGFT_FONT_T *font) { + return float_from_26_6(font->ft_face->size->metrics.height); +} diff --git a/gui/libs/vgfont/vgft.h b/gui/libs/vgfont/vgft.h @@ -0,0 +1,70 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Font handling for graphicsx + +#ifndef VGFT_H +#define VGFT_H + +#include "interface/vcos/vcos.h" +#include <VG/openvg.h> +#include <ft2build.h> + +typedef int VGFT_BOOL; +#define VGFT_FALSE 0 +#define VGFT_TRUE (!VGFT_FALSE) + +#include FT_FREETYPE_H + +/* Returns 0 on success */ +extern int vgft_init(void); +extern void vgft_term(void); + +typedef struct { + VGFont vg_font; + FT_Face ft_face; +} VGFT_FONT_T; + +/** Initialise a FT->VG font */ +VCOS_STATUS_T vgft_font_init(VGFT_FONT_T *font); + +/** Load a font file from memory */ +VCOS_STATUS_T vgft_font_load_mem(VGFT_FONT_T *font, void *mem, size_t len); + +/** Convert a font into VG glyphs */ +VCOS_STATUS_T vgft_font_convert_glyphs(VGFT_FONT_T *font, unsigned int char_height, unsigned int dpi_x, unsigned int dpi_y); + +/** Release a font. */ +void vgft_font_term(VGFT_FONT_T *font); + +void vgft_font_draw(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, unsigned text_length, VGbitfield paint_modes); + +void vgft_get_text_extents(VGFT_FONT_T *font, const char *text, unsigned text_length, VGfloat start_x, VGfloat start_y, VGfloat *w, VGfloat *h); + +VGfloat vgft_first_line_y_offset(VGFT_FONT_T *font); + +#endif diff --git a/gui/src/Makefile b/gui/src/Makefile @@ -0,0 +1,7 @@ +OBJS=main.o +BIN=../tarinagui.bin + +LDFLAGS+=-lvgfont -lfreetype -lz + +include ../Makefile.include + diff --git a/gui/src/VeraMono.ttf b/gui/src/VeraMono.ttf Binary files differ. diff --git a/gui/src/fixedsys.ttf b/gui/src/fixedsys.ttf Binary files differ. diff --git a/gui/src/main.c b/gui/src/main.c @@ -0,0 +1,306 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Test app for VG font library. + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <unistd.h> + +#include "bcm_host.h" +#include "vgfont.h" + +int32_t render_subtitle(GRAPHICS_RESOURCE_HANDLE img, const char *text, const uint32_t text_size, const uint32_t text_size_selected, const uint32_t x_offset, const uint32_t y_offset, uint32_t fontcolor) +{ + uint32_t height=0; + uint32_t img_w, img_h; + + graphics_get_resource_size(img, &img_w, &img_h); + + // split now points to last line of text. split-text = length of initial text. text_length-(split-text) is length of last line + if (fontcolor == 7) { + graphics_resource_render_text_ext(img, x_offset, y_offset-height, + GRAPHICS_RESOURCE_WIDTH, + GRAPHICS_RESOURCE_HEIGHT, + GRAPHICS_RGBA32(255,255,255,100), /* fg */ + GRAPHICS_RGBA32(20,20,20,200), /* bg */ + text, 90, text_size); + } + if (fontcolor == 6) { + graphics_resource_render_text_ext(img, x_offset, y_offset-height, + GRAPHICS_RESOURCE_WIDTH, + GRAPHICS_RESOURCE_HEIGHT, + GRAPHICS_RGBA32(225,255,255,0), /* fg */ + GRAPHICS_RGBA32(0,0,0,0), /* bg */ + text, 90, text_size); + } + if (fontcolor == 5) { + graphics_resource_render_text_ext(img, x_offset, y_offset-height, + GRAPHICS_RESOURCE_WIDTH, + GRAPHICS_RESOURCE_HEIGHT, + GRAPHICS_RGBA32(255,255,255,0xff), /* fg */ + GRAPHICS_RGBA32(0,0,0,150), /* bg */ + text, 90, text_size); + } + if (fontcolor == 4) { + graphics_resource_render_text_ext(img, x_offset, y_offset-height, + GRAPHICS_RESOURCE_WIDTH, + GRAPHICS_RESOURCE_HEIGHT, + GRAPHICS_RGBA32(30,255,255,0xff), /* fg */ + GRAPHICS_RGBA32(0,0,0,0), /* bg */ + text, 90, text_size); + } + if (fontcolor == 3) { + graphics_resource_render_text_ext(img, x_offset, y_offset-height, + GRAPHICS_RESOURCE_WIDTH, + GRAPHICS_RESOURCE_HEIGHT, + GRAPHICS_RGBA32(30,30,255,0xff), /* fg */ + GRAPHICS_RGBA32(0,0,0,0), /* bg */ + text, 90, text_size); + } + if (fontcolor == 2) { + graphics_resource_render_text_ext(img, x_offset, y_offset-height, + GRAPHICS_RESOURCE_WIDTH, + GRAPHICS_RESOURCE_HEIGHT, + GRAPHICS_RGBA32(30,255,30,0xff), /* fg */ + GRAPHICS_RGBA32(0,0,0,0), /* bg */ + text, 90, text_size); + } + if (fontcolor == 1) { + graphics_resource_render_text_ext(img, x_offset, y_offset-height, + GRAPHICS_RESOURCE_WIDTH, + GRAPHICS_RESOURCE_HEIGHT, + GRAPHICS_RGBA32(0,0,0,0xff), /* fg */ + GRAPHICS_RGBA32(30,255,255,0xff), /* bg */ + text, 90, text_size_selected); + } + return 0; + } + +int main(void) +{ + GRAPHICS_RESOURCE_HANDLE img; + uint32_t width, height; + int LAYER=10; + bcm_host_init(); + int s; + + s = gx_graphics_init("."); + assert(s == 0); + + s = graphics_get_display_size(0, &width, &height); + assert(s == 0); + + s = gx_create_window(0, width, height, GRAPHICS_RESOURCE_RGBA32, &img); + assert(s == 0); + + // transparent before display to avoid screen flash + graphics_resource_fill(img, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0x00)); + + graphics_display_resource(img, 0, LAYER, 0, 0, GRAPHICS_RESOURCE_WIDTH, GRAPHICS_RESOURCE_HEIGHT, VC_DISPMAN_ROT0, 1); + + uint32_t text_size = 15; + uint32_t text_size_selected = 16; + FILE * fp; + FILE * fp2; + FILE * fp3; + char * line = 0; + //char * b = NULL; + size_t len = 0; + ssize_t read = 0; + int linenr = 0; + int selected; + int showmenu; + int header; + int menuadd = 1; + char newread[500]; + char oldread[500]; + char vumeter[130]; + uint32_t y_offset2 = 0; + uint32_t y_offset3 = 21; + uint32_t y_offset4 = 42; + uint32_t y_offset5 = 421; + uint32_t y_offset6 = 442; + uint32_t y_offset = 466; + int space = 10; + int morespace = 12; + int rectime = 700; + int color = 3; + int row1 = 0; + int row2 = 0; + int row3 = 0; + int row4 = 0; + int row5 = 0; + if (width == 1920){ + y_offset2 = 5; + y_offset3 = 45; + y_offset4 = 85; + y_offset5 = 960; + y_offset6 = 1000; + y_offset = 1040; + rectime = 1600; + text_size = 30; + text_size_selected = 32; + space = 23; + morespace = 27; + } + else { + space = 10; + morespace = 12; + } + while (1) { + // char ch; + linenr = 0; + fp = fopen("/dev/shm/interface","r"); + if (fp != NULL){ + fread(newread, sizeof(char), 500, fp); + fclose(fp); + } + fp3 = fopen("/dev/shm/vumeter","r"); + while(fgets(vumeter, 130, fp3) != NULL); + fclose(fp3); + // check if something has changed + if (strcmp(newread, oldread) != 0) { + strcpy(oldread, newread); + //const char *text = "Never give up on your dreams"; + color = 3; + row1 = 0; + row2 = 0; + row3 = 0; + row4 = 0; + row5 = 0; + graphics_resource_fill(img, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0x00)); + // blue, at the top (y=40) + // selected 0 1 2 3 4 5 6 7 8 + + // draw the text if updated + fp2 = fopen("/dev/shm/interface", "r"); + if (fp2 != NULL){ + while ((read = getline(&line, &len, fp2)) != -1) { + //line = b; + read = read - 1; //don't count the selected line + line[read] = '\0'; //remove return char + //strcat(line, " "); + //printf("%s",line); + if (linenr == 0) + selected = atoi(line); + if (linenr == 1) + showmenu = atoi(line); + if (linenr == selected + 2 + menuadd) + color = 1; //selected color + else { + if (showmenu == 1) + color = 5; //unselected; + if (showmenu == 2) + color = 7; + if (showmenu == 0) + color = 6; + }; + if ((linenr == 2) && (read == 0)) + header = 0; + if ((linenr == 2) && (read > 0)) + header = 1; + if (selected == 420){ + if (linenr == 1) + render_subtitle(img, line, text_size, text_size_selected, 0, y_offset2, 5); + if (linenr > 1) { + render_subtitle(img, line, text_size, text_size_selected, row1, y_offset3, 5); + row1 += read * space + morespace; + } + } + if (header == 0){ //check if normal menu or header menu + if (selected < 420){ + if ((linenr == 6+menuadd) && (read > 0)){ //show recording time if there is any + render_subtitle(img, line, text_size, text_size_selected, rectime, y_offset2, 3); + } + if (linenr >= 2+menuadd && linenr <= 5+menuadd){ + if (color == 6) + color = 5; + render_subtitle(img, line, text_size, text_size_selected, row1, y_offset2, color); + row1 += read * space + morespace; + } + if (linenr >= 7+menuadd && linenr <= 12+menuadd){ + render_subtitle(img, line, text_size, text_size_selected, row2, y_offset3, color); + row2 += read * space + morespace; + } + if (linenr >= 13+menuadd && linenr <= 20+menuadd){ + render_subtitle(img, line, text_size, text_size_selected, row3, y_offset4, color); + row3 += read * space + morespace; + } + if (linenr >= 21+menuadd && linenr <= 27+menuadd){ + render_subtitle(img, line, text_size, text_size_selected, row4, y_offset5, color); + row4 += read * space + morespace; + } + if (linenr >= 28+menuadd && linenr <= 40+menuadd){ + render_subtitle(img, line, text_size, text_size_selected, row5, y_offset6, color); + row5 += read * space + morespace; + } + } + } + else { // header menu + if (linenr == 1+menuadd) + render_subtitle(img, line, text_size, text_size_selected, 0, y_offset2, 5); + if (linenr > 1+menuadd){ + render_subtitle(img, line, text_size, text_size_selected, row1, y_offset3, color); + row1 += read * space + morespace; + } + } + linenr += 1; + free(line); + line = NULL; + } + free(line); + line = NULL; + fclose(fp2); + } + //graphics_update_displayed_resource(img, 0, 0, 0, 0); + } + char s_vol1 = vumeter[85]; + char s_vol2 = vumeter[86]; + char s_vol[1]; + s_vol[0] = s_vol1; + s_vol[1] = s_vol2; + int vol = atoi(s_vol); + int vucolor = 6; + //printf("%s", s_vol); + if (vol >= 0 && vol < 35) + vucolor = 4; + if (vol >= 35 && vol < 99) + vucolor = 2; + if (vol >= 99) + vucolor = 3; + render_subtitle(img, vumeter, text_size, text_size_selected , 0, y_offset, vucolor); + graphics_update_displayed_resource(img, 0, 0, 0, 0); + usleep(20000); + } + graphics_display_resource(img, 0, LAYER, 0, 0, GRAPHICS_RESOURCE_WIDTH, GRAPHICS_RESOURCE_HEIGHT, VC_DISPMAN_ROT0, 0); + graphics_delete_resource(img); + return 0; +} + diff --git a/gui/src/main_new.c b/gui/src/main_new.c @@ -0,0 +1,42 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int main(void) +{ + FILE * fp; + FILE * fp2; + char * line = NULL; + size_t len = 0; + ssize_t read; + int linenr = 0; + int selected; + char newread[500]; + char oldread[500]; + + while (1) { + linenr = 0; + fp = fopen("interface", "r"); + fread(newread, sizeof(char), 500, fp); + fclose(fp); + if (strcmp(newread, oldread) != 0) { + strcpy(oldread, newread); + fp2 = fopen("interface", "r"); + while ((read = getline(&line, &len, fp2)) != -1) { + if (linenr == 0) + selected = atoi(line); + if (linenr == selected) + printf("selected"); + printf("%zu",read); + printf(line); + linenr += 1; + } + fclose(fp2); + //if (line) + // free(line); + } + usleep(20000); + } +}