view nuklear_ui/font_win.c @ 1971:80920c21bb52

Add an event log soft flush and call it twice per frame in between hard flushes to netplay latency when there are insufficient hardware updates to flush packets in the middle of a frame
author Michael Pavone <pavone@retrodev.com>
date Fri, 08 May 2020 11:40:30 -0700
parents 137dbd05ceab
children
line wrap: on
line source

#include <windows.h>
#include <shlobj.h>
#include <string.h>
#include "../paths.h"
#include "../util.h"
#include "sfnt.h"

uint8_t *default_font(uint32_t *size_out)
{
	static const char *thin[] = {"Thin", NULL};
	static const char *extra_light[] = {"ExtraLight", "UltraLight", NULL};
	static const char *light[] = {"Light", NULL};
	static const char *regular[] = {"Regular", "Normal", "Book", NULL};
	static const char *medium[] = {"Medium", NULL};
	static const char *semi_bold[] = {"SemiBold", "DemiBold", NULL};
	static const char *bold[] = {"Bold", NULL};
	static const char *extra_bold[] = {"ExtraBold", "UltraBold", NULL};
	static const char *heavy[] = {"Heavy", "Black", NULL};
	static const char **weight_to_subfamilies[] = {
		NULL,
		thin,
		extra_light,
		light,
		regular,
		medium,
		semi_bold,
		bold,
		extra_bold,
		heavy
	};

	NONCLIENTMETRICSA metrics = {
		.cbSize = sizeof(metrics)
	};
	char *pref_name = NULL, *pref_prefix = NULL;
	const char **pref_sub_families;
	if (SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(metrics), &metrics, 0)) {
		pref_name = metrics.lfMenuFont.lfFaceName;
		int32_t weight = metrics.lfMenuFont.lfWeight / 100;
		if (weight < 1 || weight > 9) {
			weight = 4;
		}
		pref_sub_families = weight_to_subfamilies[weight];
	}
	if (pref_name) {
		uint32_t prefix_len = 0;
		while (pref_name[prefix_len] && pref_name[prefix_len] != ' ')
		{
			prefix_len++;
		}
		pref_prefix = malloc(prefix_len + 1);
		memcpy(pref_prefix, pref_name, prefix_len);
		pref_prefix[prefix_len] = 0;
	}
	sfnt_table *selected = NULL;
	char windows[MAX_PATH];
	SHGetFolderPathA(NULL, CSIDL_WINDOWS, NULL, 0, windows);
	char *fonts = path_append(windows, "Fonts");
	size_t num_entries;
	char *tahoma = NULL, *arial = NULL;
	dir_entry *entries = get_dir_list(fonts, &num_entries);
	char *path = NULL;
	for (size_t i = 0; i < num_entries; i++)
	{
		if (entries[i].is_dir) {
			continue;
		}
		char *ext = path_extension(entries[i].name);
		if (!ext || (strcasecmp(ext, "ttf") && strcasecmp(ext, "ttc") && strcasecmp(ext, "dfont"))) {
			//not a truetype font, ignore
			free(ext);
			continue;
		}
		free(ext);
		char *base = basename_no_extension(entries[i].name);
		if (pref_prefix && !strncasecmp(base, pref_prefix, 6)) {
			path = path_append(fonts, entries[i].name);
			FILE *f = fopen(path, "rb");
			if (f)
			{
				long font_size = file_size(f);
				uint8_t *blob = malloc(font_size);
				if (font_size == fread(blob, 1, font_size, f))
				{
					sfnt_container *sfnt = load_sfnt(blob, font_size);
					if (sfnt) {
						selected = sfnt_subfamily_by_names(sfnt, pref_sub_families);
						if (!selected) {
							sfnt_free(sfnt);
						}
					} else {
						free(blob);
					}
				} else {
					free(blob);
				}
				fclose(f);
			}
			free(path);
			free(base);
			if (selected) {
				printf("Found preferred font in %s\n", entries[i].name);
				break;
			}
		} else if (!strcasecmp(base, "tahoma")) {
			tahoma = entries[i].name;
		} else if (!strcasecmp(base, "arial")) {
			arial = entries[i].name;
		}
		free(base);
	}
	if (!selected) {
		path = NULL;
		if (tahoma) {
			path = path_append(fonts, tahoma);
		} else if (arial) {
			path = path_append(fonts, arial);
		}
		if (path) {
			FILE *f = fopen(path, "rb");
			if (f)
			{
				long font_size = file_size(f);
				uint8_t *blob = malloc(font_size);
				if (font_size == fread(blob, 1, font_size, f))
				{
					sfnt_container *sfnt = load_sfnt(blob, font_size);
					if (sfnt) {
						selected = sfnt->tables;
					} else {
						free(blob);
					}
				}
				fclose(f);
			}
			free(path);
		}
	}
	free(pref_prefix);
	free(fonts);
	free_dir_list(entries, num_entries);
	if (selected) {
		return sfnt_flatten(selected, size_out);
	}
	return NULL;
}