commit e51cff183d15b08cf4032ec41f4d2a9490a62d47
parent 8c1cbd07d94e009f1f00b1fc5e7b6671fa737bd5
Author: rob <rob@tarina.org>
Date: Tue, 14 Nov 2023 16:48:21 +0200
gui makefiles
Diffstat:
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, ¶m) == 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, ¶m);
+ 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, ¶m);
+ 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,
+ ¶m);
+ 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);
+ }
+}