Пример #1
0
/**
 * @brief Reset and free the UI data hunk
 * @note Even called in case of an error when CL_Shutdown was called - maybe even
 * before CL_InitLocal (and thus UI_Init) was called
 * @sa CL_Shutdown
 * @sa UI_Init
 */
void UI_Shutdown (void)
{
	/* MN is not yet initialized */
	if (ui_global.adata == nullptr)
		return;

	const uiBehaviour_t* confunc = UI_GetNodeBehaviour("confunc");

	/* remove all confunc commands */
	for (int i = 0; i < ui_global.numWindows; i++) {
		uiNode_t* node = ui_global.windows[i];
		while (node) {
			/* remove the command */
			if (node->behaviour == confunc) {
				/* many nodes to one command is allowed */
				if (Cmd_Exists(node->name))
					Cmd_RemoveCommand(node->name);
			}

			/* recursive next */
			if (node->firstChild != nullptr) {
				node = node->firstChild;
				continue;
			}
			while (node) {
				if (node->next != nullptr) {
					node = node->next;
					break;
				}
				node = node->parent;
			}
		}
	}

	UI_FontShutdown();
	UI_ResetInput();
	UI_ResetTimers();

#ifdef DEBUG
	Cmd_RemoveCommand("debug_uimemory");
#endif
	Cmd_RemoveCommand("ui_restart");

	Mem_Free(ui_global.adata);
	OBJZERO(ui_global);

	/* release pools */
	Mem_FreePool(ui_sysPool);
	Mem_FreePool(ui_dynStringPool);
	Mem_FreePool(ui_dynPool);
	ui_sysPool = nullptr;
	ui_dynStringPool = nullptr;
	ui_dynPool = nullptr;
}
Пример #2
0
/**
 * @brief test behaviour name
 */
static void testKnownBehaviours (void)
{
	uiBehaviour_t* behaviour;

	behaviour = UI_GetNodeBehaviour("button");
	CU_ASSERT_FATAL(behaviour != nullptr);
	CU_ASSERT_STRING_EQUAL(behaviour->name, "button");

	/* first one */
	behaviour = UI_GetNodeBehaviour("");
	CU_ASSERT(behaviour != nullptr);

	/* last one */
	behaviour = UI_GetNodeBehaviour("zone");
	CU_ASSERT_FATAL(behaviour != nullptr);
	CU_ASSERT_STRING_EQUAL(behaviour->name, "zone");

	/* unknown */
	behaviour = UI_GetNodeBehaviour("dahu");
	CU_ASSERT(behaviour == nullptr);
}
Пример #3
0
/**
 * @brief Reset and free the UI data hunk
 * @note Even called in case of an error when CL_Shutdown was called - maybe even
 * before CL_InitLocal (and thus UI_Init) was called
 * @sa CL_Shutdown
 * @sa UI_Init
 */
void UI_Shutdown (void)
{
	int i;
	const uiBehaviour_t *confunc;

	/* MN is not yet initialized */
	if (ui_global.adata == NULL)
		return;

	confunc = UI_GetNodeBehaviour("confunc");

	/* remove all confunc commands */
	for (i = 0; i < ui_global.numWindows; i++) {
		uiNode_t *node = ui_global.windows[i];
		while (node) {

			/* remove the command */
			if (node->behaviour == confunc) {
				/* many nodes to one command is allowed */
				if (Cmd_Exists(node->name))
					Cmd_RemoveCommand(node->name);
			}

			/* recursive next */
			if (node->firstChild != NULL)
				node = node->firstChild;
			else {
				while (node) {
					if (node->next != NULL) {
						node = node->next;
						break;
					}
					node = node->parent;
				}
			}
		}
	}

	if (ui_global.adataize)
		Mem_Free(ui_global.adata);
	ui_global.adata = NULL;
	ui_global.adataize = 0;

	/* release pools */
	Mem_FreePool(ui_sysPool);
	Mem_FreePool(ui_dynStringPool);
	Mem_FreePool(ui_dynPool);
	ui_sysPool = NULL;
	ui_dynStringPool = NULL;
	ui_dynPool = NULL;
}
Пример #4
0
/**
 * @brief Allocate a node into the UI memory (do notr call behaviour->new)
 * @note It's not a dynamic memory allocation. Please only use it at the loading time
 * @todo Assert out when we are not in parsing/loading stage
 * @param[in] name Name of the new node, else NULL if we dont want to edit it.
 * @param[in] type Name of the node behavior
 * @param[in] isDynamic Allocate a node in static or dynamic memory
 */
static uiNode_t* UI_AllocNodeWithoutNew (const char* name, const char* type, qboolean isDynamic)
{
	uiNode_t* node;
	uiBehaviour_t *behaviour;
	int nodeSize;

	behaviour = UI_GetNodeBehaviour(type);
	if (behaviour == NULL)
		Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: Node behaviour '%s' doesn't exist", type);

	nodeSize = sizeof(*node) + behaviour->extraDataSize;

	if (!isDynamic) {
		if (ui_global.curadata + nodeSize > ui_global.adata + ui_global.adataize)
			Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: No more memory to allocate a new node");
		/** @todo fix this hard coded '8' value */
		ui_global.curadata = ALIGN_PTR(ui_global.curadata, 8);
		node = (uiNode_t*) ui_global.curadata;
		ui_global.curadata += nodeSize;
		ui_global.numNodes++;
		memset(node, 0, nodeSize);
	} else {
		node = (uiNode_t*)Mem_PoolAlloc(nodeSize, ui_dynPool, 0);
		memset(node, 0, nodeSize);
		node->dynamic = qtrue;
	}

	node->behaviour = behaviour;
#ifdef DEBUG
	node->behaviour->count++;
#endif
	if (node->behaviour->isAbstract)
		Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: Node behavior '%s' is abstract. We can't instantiate it.", type);

	if (name != NULL) {
		Q_strncpyz(node->name, name, sizeof(node->name));
		if (strlen(node->name) != strlen(name))
			Com_Printf("UI_AllocNodeWithoutNew: Node name \"%s\" truncated. New name is \"%s\"\n", name, node->name);
	}

	/* initialize default properties */
	if (node->behaviour->loading)
		node->behaviour->loading(node);

	return node;
}
Пример #5
0
/**
 * @brief Allocate a node into the UI memory (do not call behaviour->new)
 * @note It's not a dynamic memory allocation. Please only use it at the loading time
 * @todo Assert out when we are not in parsing/loading stage
 * @param[in] name Name of the new node, else nullptr if we don't want to edit it.
 * @param[in] type Name of the node behavior
 * @param[in] isDynamic Allocate a node in static or dynamic memory
 */
static uiNode_t* UI_AllocNodeWithoutNew (const char* name, const char* type, bool isDynamic)
{
	uiNode_t* node;
	uiBehaviour_t *behaviour;
	int nodeSize;

	behaviour = UI_GetNodeBehaviour(type);
	if (behaviour == nullptr)
		Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: Node behaviour '%s' doesn't exist", type);

	nodeSize = sizeof(*node) + behaviour->extraDataSize;

	if (!isDynamic) {
		void *memory = UI_AllocHunkMemory(nodeSize, STRUCT_MEMORY_ALIGN, true);
		if (memory == nullptr)
			Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: No more memory to allocate a new node - increase the cvar ui_hunksize");
		node = static_cast<uiNode_t*>(memory);
		ui_global.numNodes++;
	} else {
		node = static_cast<uiNode_t*>(Mem_PoolAlloc(nodeSize, ui_dynPool, 0));
		node->dynamic = true;
	}

	node->behaviour = behaviour;
#ifdef DEBUG
	UI_Node_DebugCountWidget(node, 1);
#endif
	if (UI_Node_IsAbstract(node))
		Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: Node behavior '%s' is abstract. We can't instantiate it.", type);

	if (name != nullptr) {
		Q_strncpyz(node->name, name, sizeof(node->name));
		if (strlen(node->name) != strlen(name))
			Com_Printf("UI_AllocNodeWithoutNew: Node name \"%s\" truncated. New name is \"%s\"\n", name, node->name);
	}

	/* initialize default properties */
	UI_Node_Loading(node);

	return node;
}
Пример #6
0
/**
 * @brief after execution of a unittest window, it analyse color of
 * normalized indicator, and create asserts. Both Fail/Pass asserts is
 * useful to count number.
 */
static void UFO_AnalyseTestWindow (const char* windowName)
{
	const uiBehaviour_t* stringBehaviour = UI_GetNodeBehaviour("string");
	const uiNode_t* node;
	const uiNode_t* window = UI_GetWindow(windowName);
	CU_ASSERT_FATAL(window != nullptr);
	CU_ASSERT_FATAL(stringBehaviour != nullptr);

	for (node = window->firstChild; node != nullptr; node = node->next) {
		bool isGreen;
		bool isRed;
		/* skip non "string" nodes */
		if (node->behaviour != stringBehaviour)
			continue;
		if (node->invis)
			continue;
		/* skip nodes without "test" prefix */
		if (!Q_strstart(node->name, "test"))
			continue;

		isGreen = node->color[0] < 0.1 && node->color[1] > 0.9 && node->color[2] < 0.1;
		isRed = node->color[0] > 0.9 && node->color[1] < 0.1 && node->color[2] < 0.1;

		if (isGreen) {
			/** @note Useful to count number of tests */
			CU_ASSERT_TRUE(true);
		} else if (isRed) {
			const char* message = va("%s.%s failed.", windowName, node->name);
			/*Com_Printf("Error: %s\n", message);*/
			/*CU_FAIL(message);*/
			CU_assertImplementation(CU_FALSE, 0, va("CU_FAIL(%s)", message), va("base/ufos/uitest/%s.ufo", windowName), "", CU_FALSE);
		} else {
			const char* message = va("%s.%s have an unknown status.", windowName, node->name);
			/*Com_Printf("Warning: %s\n", message);*/
			/*CU_FAIL(message);*/
			CU_assertImplementation(CU_FALSE, 0, va("CU_FAIL(%s)", message), va("base/ufos/uitest/%s.ufo", windowName), "", CU_FALSE);
		}
	}
}
Пример #7
0
/**
 * @brief Parse a component
 * @sa CL_ParseClientData
 * @code
 * component panel componentName {
 * }
 * @endcode
 */
bool UI_ParseComponent (const char* type, const char* name, const char** text)
{
	const char* errhead = "UI_ParseComponent: unexpected end of file (component";
	const char* token;

	if (!Q_streq(type, "component")) {
		Com_Error(ERR_FATAL, "UI_ParseComponent: \"component\" expected but \"%s\" found.\n", type);
		return false;	/* never reached */
	}

	/* check the name */
	if (!UI_TokenIsName(name, false)) {
		Com_Printf("UI_ParseNode: \"%s\" is not a well formed node name ([a-zA-Z_][a-zA-Z0-9_]*)\n", name);
		return false;
	}
	if (UI_TokenIsReserved(name)) {
		Com_Printf("UI_ParseNode: \"%s\" is a reserved token, we can't call a node with it\n", name);
		return false;
	}

	token = Com_EParse(text, errhead, "");
	if (text == nullptr)
		return false;

	/* get keyword */
	if (!Q_streq(token, "extends")) {
		Com_Printf("UI_ParseComponent: \"extends\" expected but \"%s\" found (component %s)\n", token, name);
		return false;
	}
	token = Com_EParse(text, errhead, "");
	if (text == nullptr)
		return false;

	/* initialize component */
	uiNode_t* component = nullptr;
	const uiBehaviour_t* behaviour = UI_GetNodeBehaviour(token);
	if (behaviour) {
		/* initialize a new node from behaviour */
		component = UI_AllocNode(name, behaviour->name, false);
	} else {
		const uiNode_t* inheritedComponent = UI_GetComponent(token);
		if (inheritedComponent) {
			/* initialize from a component */
			component = UI_CloneNode(inheritedComponent, nullptr, true, name, false);
		} else {
			Com_Printf("UI_ParseComponent: node behaviour/component '%s' doesn't exists (component %s)\n", token, name);
			return false;
		}
	}

	/* get body */
	token = Com_EParse(text, errhead, "");
	if (!*text)
		return false;
	bool result = UI_ParseNodeBody(component, text, &token, errhead);
	if (!result)
		return false;

	/* validate properties */
	UI_Node_Loaded(component);

	UI_InsertComponent(component);
	return true;
}
Пример #8
0
/**
 * @brief parse a node
 * @sa UI_ParseNodeProperties
 * @todo we can think about merging UI_ParseNodeProperties here
 * @note first token already read
 * @note dont read more than the need token (last right token is '}' of end of node)
 */
static uiNode_t* UI_ParseNode (uiNode_t* parent, const char** text, const char** token, const char* errhead)
{
	uiNode_t* node = nullptr;
	uiBehaviour_t* behaviour;
	uiNode_t* component = nullptr;

	/* get the behaviour */
	behaviour = UI_GetNodeBehaviour(*token);
	if (!behaviour) {
		component = UI_GetComponent(*token);
	}
	if (behaviour == nullptr && component == nullptr) {
		Com_Printf("UI_ParseNode: node behaviour/component '%s' doesn't exist (%s)\n", *token, UI_GetPath(parent));
		return nullptr;
	}

	/* get the name */
	*token = Com_EParse(text, errhead, "");
	if (!*text)
		return nullptr;
	if (!UI_TokenIsName(*token, Com_GetType(text) == TT_QUOTED_WORD)) {
		Com_Printf("UI_ParseNode: \"%s\" is not a well formed node name ([a-zA-Z_][a-zA-Z0-9_]*)\n", *token);
		return nullptr;
	}
	if (UI_TokenIsReserved(*token)) {
		Com_Printf("UI_ParseNode: \"%s\" is a reserved token, we can't call a node with it\n", *token);
		return nullptr;
	}

	/* test if node already exists */
	/* Already existing node should only come from inherited node, we should not have 2 definitions of the same node into the same window. */
	if (parent)
		node = UI_GetNode(parent, *token);

	/* reuse a node */
	if (node) {
		const uiBehaviour_t* test = (behaviour != nullptr) ? behaviour : (component != nullptr) ? component->behaviour : nullptr;
		if (node->behaviour != test) {
			Com_Printf("UI_ParseNode: we can't change node type (node \"%s\")\n", UI_GetPath(node));
			return nullptr;
		}
		Com_DPrintf(DEBUG_CLIENT, "... over-riding node %s\n", UI_GetPath(node));

	/* else initialize a component */
	} else if (component) {
		node = UI_CloneNode(component, nullptr, true, *token, false);
		if (parent) {
			if (parent->root)
				UI_UpdateRoot(node, parent->root);
			UI_AppendNode(parent, node);
		}

	/* else initialize a new node */
	} else {
		node = UI_AllocNode(*token, behaviour->name, false);
		node->parent = parent;
		if (parent)
			node->root = parent->root;
		/** @todo move it into caller */
		if (parent)
			UI_AppendNode(parent, node);
	}

	/* get body */
	const bool result = UI_ParseNodeBody(node, text, token, errhead);
	if (!result)
		return nullptr;

	/* validate properties */
	UI_Node_Loaded(node);

	return node;
}
Пример #9
0
/**
 * Initializes the inheritance (every node extends the abstract node)
 * @param behaviour The behaviour to initialize
 */
static void UI_InitializeNodeBehaviour (uiBehaviour_t* behaviour)
{
	if (behaviour->isInitialized)
		return;

	/** @todo check (when its possible) properties are ordered by name */
	/* check and update properties data */
	if (behaviour->properties) {
		int num = 0;
		const value_t* current = behaviour->properties;
		while (current->string != NULL) {
			num++;
			current++;
		}
		behaviour->propertyCount = num;
	}

	/* everything inherits 'abstractnode' */
	if (behaviour->extends == NULL && !Q_streq(behaviour->name, "abstractnode")) {
		behaviour->extends = "abstractnode";
	}

	if (behaviour->extends) {
		int i = 0;
		behaviour->super = UI_GetNodeBehaviour(behaviour->extends);
		UI_InitializeNodeBehaviour(behaviour->super);

		while (qtrue) {
			const size_t pos = virtualFunctions[i];
			uintptr_t superFunc;
			uintptr_t func;
			if (pos == -1)
				break;

			/* cache super function if we don't overwrite it */
			superFunc = *(uintptr_t*)((byte*)behaviour->super + pos);
			func = *(uintptr_t*)((byte*)behaviour + pos);
			if (func == 0 && superFunc != 0)
				*(uintptr_t*)((byte*)behaviour + pos) = superFunc;

			i++;
		}
	}

	/* property must not overwrite another property */
	if (behaviour->super && behaviour->properties) {
		const value_t* property = behaviour->properties;
		while (property->string != NULL) {
			const value_t *p = UI_GetPropertyFromBehaviour(behaviour->super, property->string);
#if 0	/**< @todo not possible at the moment, not sure its the right way */
			const uiBehaviour_t *b = UI_GetNodeBehaviour(current->string);
#endif
			if (p != NULL)
				Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' overwrite another property", property->string, behaviour->name);
#if 0	/**< @todo not possible at the moment, not sure its the right way */
			if (b != NULL)
				Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' use the name of an existing node behaviour", property->string, behaviour->name);
#endif
			property++;
		}
	}

	/* Sanity: A property must not be outside the node memory */
	if (behaviour->properties) {
		const int size = sizeof(uiNode_t) + behaviour->extraDataSize;
		const value_t* property = behaviour->properties;
		while (property->string != NULL) {
			if (property->type != V_UI_NODEMETHOD && property->ofs + property->size > size)
				Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' is outside the node memory. The C code need a fix.", property->string, behaviour->name);
			property++;
		}
	}

	behaviour->isInitialized = qtrue;
}
Пример #10
0
/**
 * @brief Initialize a node behaviour memory, after registration, and before unsing it.
 * @param behaviour Behaviour to initialize
 */
void UI_InitializeNodeBehaviour (uiBehaviour_t* behaviour)
{
	if (behaviour->isInitialized)
		return;

	/* everything inherits 'abstractnode' */
	if (behaviour->extends == NULL && !Q_streq(behaviour->name, "abstractnode")) {
		behaviour->extends = "abstractnode";
	}

	if (behaviour->extends) {
		int i = 0;

		/** TODO Find a way to remove that, if possible */
		behaviour->super = UI_GetNodeBehaviour(behaviour->extends);
		UI_InitializeNodeBehaviour(behaviour->super);

		while (qtrue) {
			const size_t pos = virtualFunctions[i];
			uintptr_t superFunc;
			uintptr_t func;
			if (pos == -1)
				break;

			/* cache super function if we don't overwrite it */
			superFunc = *(uintptr_t*)((byte*)behaviour->super + pos);
			func = *(uintptr_t*)((byte*)behaviour + pos);
			if (func == 0 && superFunc != 0)
				*(uintptr_t*)((byte*)behaviour + pos) = superFunc;

			i++;
		}
	}

	/* sort properties by alphabet */
	if (behaviour->localProperties) {
		int i = 0;
		const value_t* previous;
		const value_t** oldmemory = behaviour->localProperties;
		behaviour->localProperties = UI_AllocHunkMemory(sizeof(value_t*) * (behaviour->propertyCount+1), STRUCT_MEMORY_ALIGN, qfalse);
		if (behaviour->localProperties == NULL) {
			Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: UI memory hunk exceeded - increase the size");
		}

		previous = NULL;
		for (i = 0; i < behaviour->propertyCount; i++) {
			const value_t* better = NULL;
			const value_t** current;
			/* search the next element after previous */
			for (current = oldmemory; *current != NULL; current++) {
				if (previous != NULL && Q_strcasecmp(previous->string, (*current)->string) >= 0) {
					continue;
				}
				if (better == NULL || Q_strcasecmp(better->string, (*current)->string) >= 0) {
					better = *current;
				}
			}
			previous = better;
			behaviour->localProperties[i] = better;
		}
		behaviour->localProperties[behaviour->propertyCount] = NULL;
		Mem_Free(oldmemory);
	}

	/* property must not overwrite another property */
	if (behaviour->super && behaviour->localProperties) {
		const value_t** property = behaviour->localProperties;
		while (*property) {
			const value_t *p = UI_GetPropertyFromBehaviour(behaviour->super, (*property)->string);
#if 0	/**< @todo not possible at the moment, not sure its the right way */
			const uiBehaviour_t *b = UI_GetNodeBehaviour(current->string);
#endif
			if (p != NULL)
				Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' overwrite another property", (*property)->string, behaviour->name);
#if 0	/**< @todo not possible at the moment, not sure its the right way */
			if (b != NULL)
				Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' use the name of an existing node behaviour", (*property)->string, behaviour->name);
#endif
			property++;
		}
	}

	/* Sanity: A property must not be outside the node memory */
	if (behaviour->localProperties) {
		const int size = sizeof(uiNode_t) + behaviour->extraDataSize;
		const value_t** property = behaviour->localProperties;
		while (*property) {
			if ((*property)->type != V_UI_NODEMETHOD && (*property)->ofs + (*property)->size > size)
				Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' is outside the node memory. The C code need a fix.", (*property)->string, behaviour->name);
			property++;
		}
	}

	behaviour->isInitialized = qtrue;
}