mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-12 23:07:21 +00:00
[common/client] platform: abstract runtime services
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
#include "core.h"
|
||||
#include "util.h"
|
||||
#include "render_queue.h"
|
||||
#include "evdev.h"
|
||||
#include "platform/input.h"
|
||||
#include "input.h"
|
||||
#include "sw_surface.h"
|
||||
|
||||
@@ -135,10 +135,10 @@ void app_mouseTrace(const char * file, unsigned int line,
|
||||
if (result < 0)
|
||||
return;
|
||||
|
||||
flockfile(stderr);
|
||||
debug_info(file, line, function, "Mouse %06lu: %s", seq,
|
||||
debug_lock();
|
||||
debug_info(file, line, function, "Mouse %06" PRIu64 ": %s", seq,
|
||||
message);
|
||||
funlockfile(stderr);
|
||||
debug_unlock();
|
||||
}
|
||||
|
||||
bool app_isCaptureOnlyMode(void)
|
||||
@@ -174,7 +174,7 @@ bool app_isOverlayMode(void)
|
||||
|
||||
void app_updateCursorPos(double x, double y)
|
||||
{
|
||||
if (evdev_isPointerExclusive())
|
||||
if (inputPlatformIsPointerExclusive())
|
||||
return;
|
||||
|
||||
const bool overlay = app_isOverlayMode();
|
||||
@@ -404,7 +404,7 @@ void app_handleButtonPressInternal(int button)
|
||||
|
||||
void app_handleButtonPress(int button)
|
||||
{
|
||||
if (!evdev_isPointerExclusive())
|
||||
if (!inputPlatformIsPointerExclusive())
|
||||
app_handleButtonPressInternal(button);
|
||||
}
|
||||
|
||||
@@ -433,13 +433,13 @@ void app_handleButtonReleaseInternal(int button)
|
||||
|
||||
void app_handleButtonRelease(int button)
|
||||
{
|
||||
if (!evdev_isPointerExclusive())
|
||||
if (!inputPlatformIsPointerExclusive())
|
||||
app_handleButtonReleaseInternal(button);
|
||||
}
|
||||
|
||||
void app_handleWheelMotion(double motion)
|
||||
{
|
||||
if (evdev_isPointerExclusive())
|
||||
if (inputPlatformIsPointerExclusive())
|
||||
return;
|
||||
|
||||
if (app_isOverlayMode())
|
||||
@@ -576,19 +576,19 @@ void app_handleKeyReleaseInternal(int sc)
|
||||
|
||||
void app_handleKeyPress(int sc)
|
||||
{
|
||||
if (!evdev_isExclusive())
|
||||
if (!inputPlatformIsKeyboardExclusive())
|
||||
app_handleKeyPressInternal(sc);
|
||||
}
|
||||
|
||||
void app_handleKeyRelease(int sc)
|
||||
{
|
||||
if (!evdev_isExclusive())
|
||||
if (!inputPlatformIsKeyboardExclusive())
|
||||
app_handleKeyReleaseInternal(sc);
|
||||
}
|
||||
|
||||
void app_handleKeyboardTyped(const char * typed)
|
||||
{
|
||||
if (evdev_isExclusive())
|
||||
if (inputPlatformIsKeyboardExclusive())
|
||||
return;
|
||||
|
||||
app_registerImGuiText(typed);
|
||||
@@ -606,7 +606,7 @@ void app_handleKeyboardModifiersInternal(
|
||||
|
||||
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super)
|
||||
{
|
||||
if (!evdev_isExclusive())
|
||||
if (!inputPlatformIsKeyboardExclusive())
|
||||
app_handleKeyboardModifiersInternal(ctrl, shift, alt, super);
|
||||
}
|
||||
|
||||
@@ -652,7 +652,7 @@ void app_handleInputResetInternal(bool keyboard, bool pointer)
|
||||
|
||||
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock)
|
||||
{
|
||||
if (evdev_isExclusive())
|
||||
if (inputPlatformIsKeyboardExclusive())
|
||||
return;
|
||||
|
||||
if (!lgInput_keyboardLEDs(numLock, capsLock, scrollLock))
|
||||
@@ -662,7 +662,7 @@ void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock)
|
||||
void app_handleMouseRelative(double normx, double normy,
|
||||
double rawx, double rawy)
|
||||
{
|
||||
if (evdev_isPointerExclusive())
|
||||
if (inputPlatformIsPointerExclusive())
|
||||
return;
|
||||
|
||||
MTRACE("rel delta=%.3f,%.3f raw=%.3f,%.3f inWin=%d inView=%d "
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "kb.h"
|
||||
#include "message.h"
|
||||
#include "input.h"
|
||||
#include "evdev.h"
|
||||
#include "platform/input.h"
|
||||
|
||||
#include "common/time.h"
|
||||
#include "common/debug.h"
|
||||
@@ -147,7 +147,7 @@ bool core_inputEnabled(void)
|
||||
void core_updateKeyboardLEDsSync(void)
|
||||
{
|
||||
lgInput_setKeyboardLEDsSync(
|
||||
inputPolicyEnabled() && !evdev_isExclusive());
|
||||
inputPolicyEnabled() && !inputPlatformIsKeyboardExclusive());
|
||||
}
|
||||
|
||||
void core_updateKeyboardGrab(void)
|
||||
@@ -165,7 +165,7 @@ void core_updateKeyboardGrab(void)
|
||||
const bool active = g_state.focused && !app_isOverlayMode() &&
|
||||
(capture || (inputEnabled && (view || automatic)));
|
||||
|
||||
evdev_setGrab(active, localActive && g_cursor.grab);
|
||||
inputPlatformSetGrab(active, localActive && g_cursor.grab);
|
||||
core_updateKeyboardLEDsSync();
|
||||
if (active)
|
||||
g_state.ds->grabKeyboard();
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include "evdev.h"
|
||||
#include "event_source.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@@ -181,7 +182,9 @@ struct EvdevState
|
||||
|
||||
int epoll;
|
||||
int commandEvent;
|
||||
int outputEvent;
|
||||
LGEventSourceState eventSource;
|
||||
bool eventSourceInitialized;
|
||||
bool started;
|
||||
LGThread * thread;
|
||||
uint64_t observedState;
|
||||
uint64_t nextBarrier;
|
||||
@@ -217,7 +220,6 @@ static struct EvdevState state =
|
||||
{
|
||||
.epoll = -1,
|
||||
.commandEvent = -1,
|
||||
.outputEvent = -1,
|
||||
};
|
||||
|
||||
static struct Option options[] =
|
||||
@@ -521,7 +523,7 @@ static void evdev_availabilityChanged(
|
||||
(void)available;
|
||||
atomic_store_explicit(&state.availabilityDirty, true,
|
||||
memory_order_release);
|
||||
evdev_signal(state.outputEvent, "output");
|
||||
lgEventSourceNotify(&state.eventSource);
|
||||
}
|
||||
|
||||
static void evdev_signal(int fd, const char * name)
|
||||
@@ -593,7 +595,7 @@ static bool evdev_queueOutput(const EvdevOutput * output)
|
||||
last->motion.y = evdev_addLimited(last->motion.y,
|
||||
output->motion.y, EVDEV_MOTION_LIMIT);
|
||||
LG_UNLOCK(state.outputLock);
|
||||
evdev_signal(state.outputEvent, "output");
|
||||
lgEventSourceNotify(&state.eventSource);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -606,7 +608,7 @@ static bool evdev_queueOutput(const EvdevOutput * output)
|
||||
last->wheel.horizontal = evdev_addLimited(last->wheel.horizontal,
|
||||
output->wheel.horizontal, EVDEV_WHEEL_LIMIT);
|
||||
LG_UNLOCK(state.outputLock);
|
||||
evdev_signal(state.outputEvent, "output");
|
||||
lgEventSourceNotify(&state.eventSource);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -654,7 +656,7 @@ static bool evdev_queueOutput(const EvdevOutput * output)
|
||||
DEBUG_WARN("evdev output queue overflowed; input state was reset");
|
||||
}
|
||||
|
||||
evdev_signal(state.outputEvent, "output");
|
||||
lgEventSourceNotify(&state.eventSource);
|
||||
return !overflow;
|
||||
}
|
||||
|
||||
@@ -712,7 +714,7 @@ static void evdev_cancelOutput(void)
|
||||
LG_UNLOCK(state.outputLock);
|
||||
|
||||
evdev_clearForwarded();
|
||||
evdev_signal(state.outputEvent, "output");
|
||||
lgEventSourceNotify(&state.eventSource);
|
||||
}
|
||||
|
||||
static void evdev_updateKey(EvdevDevice * device, unsigned int code,
|
||||
@@ -2001,18 +2003,17 @@ static bool evdev_outputCurrent(
|
||||
output->generation == evdev_generation(desiredState, lane);
|
||||
}
|
||||
|
||||
int evdev_getEventFD(void)
|
||||
LG_DSEventSource evdev_getEventSource(void)
|
||||
{
|
||||
return state.outputEvent;
|
||||
if (!state.started)
|
||||
return (LG_DSEventSource) { 0 };
|
||||
|
||||
return lgEventSourceGet(&state.eventSource);
|
||||
}
|
||||
|
||||
void evdev_dispatch(void * opaque)
|
||||
static void evdev_dispatch(void * opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
if (state.outputEvent < 0)
|
||||
return;
|
||||
|
||||
evdev_drainEvent(state.outputEvent);
|
||||
|
||||
if (!atomic_load_explicit(&state.stop, memory_order_acquire) &&
|
||||
atomic_exchange_explicit(&state.availabilityDirty, false,
|
||||
@@ -2129,6 +2130,9 @@ bool evdev_start(void)
|
||||
|
||||
LG_LOCK_INIT(state.outputLock);
|
||||
state.outputLockInitialized = true;
|
||||
if (!lgEventSourceInit(&state.eventSource, evdev_dispatch, NULL))
|
||||
return false;
|
||||
state.eventSourceInitialized = true;
|
||||
|
||||
state.deviceList = strdup(deviceList);
|
||||
if (!state.deviceList)
|
||||
@@ -2192,13 +2196,6 @@ bool evdev_start(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
state.outputEvent = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
if (state.outputEvent < 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create evdev output event: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
struct epoll_event commandEvent =
|
||||
{
|
||||
.events = EPOLLIN,
|
||||
@@ -2219,9 +2216,10 @@ bool evdev_start(void)
|
||||
}
|
||||
|
||||
lgInput_setKeyboardLEDsListener(evdev_keyboardLEDs, NULL);
|
||||
state.ledsListenerRegistered = true;
|
||||
state.ledsListenerRegistered = true;
|
||||
lgInput_setAvailabilityListener(evdev_availabilityChanged, NULL);
|
||||
state.availabilityListenerRegistered = true;
|
||||
state.started = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2241,6 +2239,8 @@ void evdev_stop(void)
|
||||
state.ledsListenerRegistered = false;
|
||||
}
|
||||
|
||||
state.started = false;
|
||||
|
||||
if (!state.thread)
|
||||
return;
|
||||
|
||||
@@ -2254,10 +2254,10 @@ void evdev_free(void)
|
||||
{
|
||||
evdev_stop();
|
||||
|
||||
if (state.outputEvent >= 0)
|
||||
if (state.eventSourceInitialized)
|
||||
{
|
||||
close(state.outputEvent);
|
||||
state.outputEvent = -1;
|
||||
lgEventSourceFree(&state.eventSource);
|
||||
state.eventSourceInitialized = false;
|
||||
}
|
||||
|
||||
if (state.commandEvent >= 0)
|
||||
|
||||
115
client/src/event_source.c
Normal file
115
client/src/event_source.c
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "event_source.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static bool subscribe(void * opaque, LG_DSEventWake callback,
|
||||
void * callbackOpaque)
|
||||
{
|
||||
LGEventSourceState * state = (LGEventSourceState *)opaque;
|
||||
if (!callback)
|
||||
return false;
|
||||
|
||||
LG_LOCK(state->lock);
|
||||
if (state->callback)
|
||||
{
|
||||
LG_UNLOCK(state->lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
state->callback = callback;
|
||||
state->callbackOpaque = callbackOpaque;
|
||||
if (state->pending && !state->callback(state->callbackOpaque))
|
||||
{
|
||||
state->callback = NULL;
|
||||
state->callbackOpaque = NULL;
|
||||
LG_UNLOCK(state->lock);
|
||||
return false;
|
||||
}
|
||||
LG_UNLOCK(state->lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void unsubscribe(void * opaque)
|
||||
{
|
||||
LGEventSourceState * state = (LGEventSourceState *)opaque;
|
||||
|
||||
LG_LOCK(state->lock);
|
||||
state->callback = NULL;
|
||||
state->callbackOpaque = NULL;
|
||||
LG_UNLOCK(state->lock);
|
||||
}
|
||||
|
||||
static void dispatch(void * opaque)
|
||||
{
|
||||
LGEventSourceState * state = (LGEventSourceState *)opaque;
|
||||
|
||||
LG_LOCK(state->lock);
|
||||
state->pending = false;
|
||||
LG_UNLOCK(state->lock);
|
||||
state->dispatch(state->dispatchOpaque);
|
||||
}
|
||||
|
||||
bool lgEventSourceInit(LGEventSourceState * state,
|
||||
LG_DSEventDispatch dispatchCallback, void * dispatchOpaque)
|
||||
{
|
||||
if (!state || !dispatchCallback)
|
||||
return false;
|
||||
|
||||
memset(state, 0, sizeof(*state));
|
||||
LG_LOCK_INIT(state->lock);
|
||||
state->dispatch = dispatchCallback;
|
||||
state->dispatchOpaque = dispatchOpaque;
|
||||
return true;
|
||||
}
|
||||
|
||||
void lgEventSourceFree(LGEventSourceState * state)
|
||||
{
|
||||
unsubscribe(state);
|
||||
LG_LOCK_FREE(state->lock);
|
||||
}
|
||||
|
||||
LG_DSEventSource lgEventSourceGet(LGEventSourceState * state)
|
||||
{
|
||||
if (!state || !state->dispatch)
|
||||
return (LG_DSEventSource) { 0 };
|
||||
|
||||
return (LG_DSEventSource)
|
||||
{
|
||||
.opaque = state,
|
||||
.subscribe = subscribe,
|
||||
.unsubscribe = unsubscribe,
|
||||
.dispatch = dispatch,
|
||||
};
|
||||
}
|
||||
|
||||
void lgEventSourceNotify(LGEventSourceState * state)
|
||||
{
|
||||
LG_LOCK(state->lock);
|
||||
if (!state->pending)
|
||||
{
|
||||
state->pending = true;
|
||||
if (state->callback && !state->callback(state->callbackOpaque))
|
||||
state->pending = false;
|
||||
}
|
||||
LG_UNLOCK(state->lock);
|
||||
}
|
||||
@@ -19,14 +19,27 @@
|
||||
*/
|
||||
|
||||
#include "font.h"
|
||||
#include "font_platform.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/filesystem.h"
|
||||
#include "common/locking.h"
|
||||
|
||||
#include "cimgui.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
#define STBRP_STATIC
|
||||
#define STB_RECT_PACK_IMPLEMENTATION
|
||||
#include "repos/gui/cimgui/imgui/imstb_rectpack.h"
|
||||
#define STBTT_STATIC
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include "repos/gui/cimgui/imgui/imstb_truetype.h"
|
||||
@@ -34,6 +47,34 @@
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
typedef struct UIFontGlyphSet
|
||||
{
|
||||
uint32_t * data;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
}
|
||||
UIFontGlyphSet;
|
||||
|
||||
typedef struct UIFontFace
|
||||
{
|
||||
char * file;
|
||||
void * data;
|
||||
size_t dataSize;
|
||||
int index;
|
||||
ImVector_ImWchar ranges;
|
||||
}
|
||||
UIFontFace;
|
||||
|
||||
static FontPlatform * UIFontPlatform = NULL;
|
||||
static UIFontGlyphSet UIFontChars = { 0 };
|
||||
static UIFontGlyphSet UIFontRequested = { 0 };
|
||||
static UIFontGlyphSet UIFontResolved = { 0 };
|
||||
static UIFontFace * UIFontFaces = NULL;
|
||||
static size_t UIFontFaceCount = 0;
|
||||
static LG_Lock UIFontLock;
|
||||
static bool UIFontLockInitialized = false;
|
||||
static bool UIFontSelected = false;
|
||||
|
||||
static uint16_t font_readU16(const unsigned char * data)
|
||||
{
|
||||
return (uint16_t)(((uint16_t)data[0] << 8) | data[1]);
|
||||
@@ -54,8 +95,8 @@ static bool font_getSfntOffset(const unsigned char * data, size_t size,
|
||||
if (!data || size < 12 || size > INT_MAX || fontIndex < 0)
|
||||
return false;
|
||||
|
||||
size_t fontOffset = 0;
|
||||
const uint32_t signature = font_readU32(data);
|
||||
size_t fontOffset = 0;
|
||||
const uint32_t signature = font_readU32(data);
|
||||
switch (signature)
|
||||
{
|
||||
case 0x00010000: // TrueType 1.0
|
||||
@@ -68,7 +109,7 @@ static bool font_getSfntOffset(const unsigned char * data, size_t size,
|
||||
|
||||
case 0x74746366: // ttcf
|
||||
{
|
||||
const uint32_t version = font_readU32(data + 4);
|
||||
const uint32_t version = font_readU32(data + 4);
|
||||
const uint32_t fontCount = font_readU32(data + 8);
|
||||
if ((version != 0x00010000 && version != 0x00020000) ||
|
||||
(uint32_t)fontIndex >= fontCount ||
|
||||
@@ -117,3 +158,445 @@ bool font_isStbTruetypeCompatible(
|
||||
stbtt_fontinfo info;
|
||||
return stbtt_InitFont(&info, data, stbOffset);
|
||||
}
|
||||
|
||||
static size_t uiFontGlyphLowerBound(
|
||||
const UIFontGlyphSet * glyphs, uint32_t glyph)
|
||||
{
|
||||
size_t low = 0;
|
||||
size_t high = glyphs->count;
|
||||
|
||||
while (low < high)
|
||||
{
|
||||
const size_t middle = low + (high - low) / 2;
|
||||
if (glyphs->data[middle] < glyph)
|
||||
low = middle + 1;
|
||||
else
|
||||
high = middle;
|
||||
}
|
||||
|
||||
return low;
|
||||
}
|
||||
|
||||
static bool uiFontGlyphReserve(UIFontGlyphSet * glyphs, size_t count)
|
||||
{
|
||||
if (count <= glyphs->capacity)
|
||||
return true;
|
||||
|
||||
if (count > SIZE_MAX / sizeof(*glyphs->data))
|
||||
return false;
|
||||
|
||||
size_t capacity = glyphs->capacity ? glyphs->capacity : 128;
|
||||
while (capacity < count)
|
||||
{
|
||||
if (capacity > SIZE_MAX / 2)
|
||||
{
|
||||
capacity = count;
|
||||
break;
|
||||
}
|
||||
capacity *= 2;
|
||||
}
|
||||
|
||||
uint32_t * data = realloc(glyphs->data, capacity * sizeof(*data));
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
glyphs->data = data;
|
||||
glyphs->capacity = capacity;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool uiFontGlyphAdd(
|
||||
UIFontGlyphSet * glyphs, uint32_t glyph, bool * added)
|
||||
{
|
||||
const size_t position = uiFontGlyphLowerBound(glyphs, glyph);
|
||||
if (position < glyphs->count && glyphs->data[position] == glyph)
|
||||
{
|
||||
*added = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!uiFontGlyphReserve(glyphs, glyphs->count + 1))
|
||||
return false;
|
||||
|
||||
memmove(glyphs->data + position + 1, glyphs->data + position,
|
||||
(glyphs->count - position) * sizeof(*glyphs->data));
|
||||
glyphs->data[position] = glyph;
|
||||
++glyphs->count;
|
||||
*added = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool uiFontGlyphHas(
|
||||
const UIFontGlyphSet * glyphs, uint32_t glyph)
|
||||
{
|
||||
const size_t position = uiFontGlyphLowerBound(glyphs, glyph);
|
||||
return position < glyphs->count && glyphs->data[position] == glyph;
|
||||
}
|
||||
|
||||
static void uiFontGlyphFree(UIFontGlyphSet * glyphs)
|
||||
{
|
||||
free(glyphs->data);
|
||||
glyphs->data = NULL;
|
||||
glyphs->count = 0;
|
||||
glyphs->capacity = 0;
|
||||
}
|
||||
|
||||
static int uiFontGlyphCompare(const void * lhs, const void * rhs)
|
||||
{
|
||||
const uint32_t left = *(const uint32_t *)lhs;
|
||||
const uint32_t right = *(const uint32_t *)rhs;
|
||||
return (left > right) - (left < right);
|
||||
}
|
||||
|
||||
static void uiFontClearFaces(void)
|
||||
{
|
||||
for (size_t i = 0; i < UIFontFaceCount; ++i)
|
||||
{
|
||||
free(UIFontFaces[i].file);
|
||||
free(UIFontFaces[i].data);
|
||||
ImVector_ImWchar_UnInit(&UIFontFaces[i].ranges);
|
||||
}
|
||||
|
||||
free(UIFontFaces);
|
||||
UIFontFaces = NULL;
|
||||
UIFontFaceCount = 0;
|
||||
UIFontResolved.count = 0;
|
||||
}
|
||||
|
||||
static bool uiFontAddRanges(const ImWchar * ranges)
|
||||
{
|
||||
for (; ranges[0]; ranges += 2)
|
||||
for (uint32_t glyph = ranges[0]; glyph <= (uint32_t)ranges[1]; ++glyph)
|
||||
{
|
||||
bool added;
|
||||
if (!uiFontGlyphAdd(&UIFontChars, glyph, &added))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool uiFontBuildRanges(const uint32_t * glyphs, size_t glyphCount,
|
||||
ImVector_ImWchar * ranges)
|
||||
{
|
||||
ImFontGlyphRangesBuilder * builder =
|
||||
ImFontGlyphRangesBuilder_ImFontGlyphRangesBuilder();
|
||||
if (!builder)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < glyphCount; ++i)
|
||||
ImFontGlyphRangesBuilder_AddChar(builder, (ImWchar)glyphs[i]);
|
||||
|
||||
ImVector_ImWchar_Init(ranges);
|
||||
ImFontGlyphRangesBuilder_BuildRanges(builder, ranges);
|
||||
ImFontGlyphRangesBuilder_destroy(builder);
|
||||
if (ranges->Size > 1)
|
||||
return true;
|
||||
|
||||
ImVector_ImWchar_UnInit(ranges);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void * uiFontLoadFile(const char * path, size_t * size)
|
||||
{
|
||||
*size = 0;
|
||||
|
||||
FILE * file = lgOpenFile(path, "rb");
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
if (fseek(file, 0, SEEK_END) != 0)
|
||||
goto fail;
|
||||
|
||||
const long length = ftell(file);
|
||||
if (length <= 0 || length > INT_MAX || fseek(file, 0, SEEK_SET) != 0)
|
||||
goto fail;
|
||||
|
||||
void * data = malloc((size_t)length);
|
||||
if (!data)
|
||||
goto fail;
|
||||
|
||||
if (fread(data, 1, (size_t)length, file) != (size_t)length)
|
||||
{
|
||||
free(data);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
*size = (size_t)length;
|
||||
return data;
|
||||
|
||||
fail:
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static FontPlatformFaceResult uiFontAddFace(void * opaque,
|
||||
const char * file, int index,
|
||||
const uint32_t * glyphs, size_t glyphCount)
|
||||
{
|
||||
(void)opaque;
|
||||
|
||||
size_t dataSize;
|
||||
void * data = uiFontLoadFile(file, &dataSize);
|
||||
if (!data)
|
||||
{
|
||||
DEBUG_WARN("Failed to read UI font face: %s", file);
|
||||
return FONT_PLATFORM_FACE_SKIP;
|
||||
}
|
||||
|
||||
if (!font_isStbTruetypeCompatible(data, dataSize, index))
|
||||
{
|
||||
DEBUG_WARN("Skipping stb-incompatible UI font face: %s (index %d)",
|
||||
file, index);
|
||||
free(data);
|
||||
return FONT_PLATFORM_FACE_SKIP;
|
||||
}
|
||||
|
||||
ImVector_ImWchar ranges;
|
||||
if (!uiFontBuildRanges(glyphs, glyphCount, &ranges))
|
||||
{
|
||||
free(data);
|
||||
return FONT_PLATFORM_FACE_ERROR;
|
||||
}
|
||||
|
||||
char * fileCopy = strdup(file);
|
||||
if (!fileCopy ||
|
||||
!uiFontGlyphReserve(
|
||||
&UIFontResolved, UIFontResolved.count + glyphCount))
|
||||
{
|
||||
free(fileCopy);
|
||||
ImVector_ImWchar_UnInit(&ranges);
|
||||
free(data);
|
||||
return FONT_PLATFORM_FACE_ERROR;
|
||||
}
|
||||
|
||||
UIFontFace * faces = realloc(UIFontFaces,
|
||||
(UIFontFaceCount + 1) * sizeof(*faces));
|
||||
if (!faces)
|
||||
{
|
||||
free(fileCopy);
|
||||
ImVector_ImWchar_UnInit(&ranges);
|
||||
free(data);
|
||||
return FONT_PLATFORM_FACE_ERROR;
|
||||
}
|
||||
UIFontFaces = faces;
|
||||
|
||||
memcpy(UIFontResolved.data + UIFontResolved.count,
|
||||
glyphs, glyphCount * sizeof(*glyphs));
|
||||
UIFontResolved.count += glyphCount;
|
||||
|
||||
UIFontFace * face = &UIFontFaces[UIFontFaceCount++];
|
||||
face->file = fileCopy;
|
||||
face->data = data;
|
||||
face->dataSize = dataSize;
|
||||
face->index = index;
|
||||
face->ranges = ranges;
|
||||
return FONT_PLATFORM_FACE_ACCEPT;
|
||||
}
|
||||
|
||||
static bool uiFontBuildFaces(ImFontAtlas * atlas)
|
||||
{
|
||||
uiFontClearFaces();
|
||||
|
||||
static const ImWchar arrowRanges[] =
|
||||
{
|
||||
0x2190, 0x2193,
|
||||
0,
|
||||
};
|
||||
|
||||
if (!uiFontAddRanges(ImFontAtlas_GetGlyphRangesDefault(atlas)) ||
|
||||
!uiFontAddRanges(arrowRanges))
|
||||
return false;
|
||||
|
||||
size_t unresolved;
|
||||
if (!font_platformResolve(UIFontPlatform,
|
||||
UIFontChars.data, UIFontChars.count, uiFontAddFace, NULL, &unresolved))
|
||||
{
|
||||
uiFontClearFaces();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (UIFontResolved.count)
|
||||
qsort(UIFontResolved.data, UIFontResolved.count,
|
||||
sizeof(*UIFontResolved.data), uiFontGlyphCompare);
|
||||
|
||||
size_t missingRequested = 0;
|
||||
for (size_t i = 0; i < UIFontRequested.count; ++i)
|
||||
if (!uiFontGlyphHas(&UIFontResolved, UIFontRequested.data[i]))
|
||||
++missingRequested;
|
||||
|
||||
if (unresolved && missingRequested)
|
||||
DEBUG_WARN("%u requested UI glyphs have no usable outline font",
|
||||
(unsigned int)missingRequested);
|
||||
|
||||
return UIFontFaceCount > 0;
|
||||
}
|
||||
|
||||
static ImFont * uiFontAddSize(ImFontAtlas * atlas, float size)
|
||||
{
|
||||
ImFont * result = NULL;
|
||||
|
||||
for (size_t i = 0; i < UIFontFaceCount; ++i)
|
||||
{
|
||||
ImFontConfig * config = ImFontConfig_ImFontConfig();
|
||||
if (!config)
|
||||
return NULL;
|
||||
|
||||
config->MergeMode = result != NULL;
|
||||
config->FontDataOwnedByAtlas = false;
|
||||
config->FontNo = UIFontFaces[i].index;
|
||||
|
||||
ImFont * font = ImFontAtlas_AddFontFromMemoryTTF(atlas,
|
||||
UIFontFaces[i].data, (int)UIFontFaces[i].dataSize,
|
||||
size, config, UIFontFaces[i].ranges.Data);
|
||||
ImFontConfig_destroy(config);
|
||||
|
||||
if (!font)
|
||||
{
|
||||
DEBUG_WARN("Failed to add UI font face: %s", UIFontFaces[i].file);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!result)
|
||||
result = font;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool font_init(void)
|
||||
{
|
||||
if (UIFontPlatform)
|
||||
return true;
|
||||
|
||||
FontPlatform * platform;
|
||||
if (!font_platformInit(&platform))
|
||||
return false;
|
||||
|
||||
UIFontPlatform = platform;
|
||||
LG_LOCK_INIT(UIFontLock);
|
||||
UIFontLockInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
char * font_get(const char * fontName)
|
||||
{
|
||||
if (!UIFontPlatform || !fontName)
|
||||
return NULL;
|
||||
|
||||
LG_LOCK(UIFontLock);
|
||||
char * path = font_platformSelect(UIFontPlatform, fontName);
|
||||
if (path)
|
||||
UIFontSelected = true;
|
||||
LG_UNLOCK(UIFontLock);
|
||||
return path;
|
||||
}
|
||||
|
||||
bool font_addText(const char * text)
|
||||
{
|
||||
if (!UIFontPlatform || !text)
|
||||
return false;
|
||||
|
||||
bool changed = false;
|
||||
LG_LOCK(UIFontLock);
|
||||
|
||||
const char * pos = text;
|
||||
while (*pos)
|
||||
{
|
||||
unsigned int glyph;
|
||||
const int length = igImTextCharFromUtf8(&glyph, pos, NULL);
|
||||
if (length <= 0)
|
||||
break;
|
||||
pos += length;
|
||||
|
||||
if (glyph < 0x20 || glyph > IM_UNICODE_CODEPOINT_MAX)
|
||||
continue;
|
||||
|
||||
bool added;
|
||||
if (!uiFontGlyphAdd(&UIFontChars, glyph, &added))
|
||||
{
|
||||
DEBUG_ERROR("Failed to record a UI glyph");
|
||||
break;
|
||||
}
|
||||
changed |= added;
|
||||
|
||||
if (!uiFontGlyphAdd(&UIFontRequested, glyph, &added))
|
||||
{
|
||||
DEBUG_ERROR("Failed to record a requested UI glyph");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LG_UNLOCK(UIFontLock);
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool font_buildAtlas(ImFontAtlas * atlas, float size, ImFont ** large)
|
||||
{
|
||||
if (!UIFontPlatform || !UIFontSelected || !atlas || !large || size <= 0.0f)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
*large = NULL;
|
||||
LG_LOCK(UIFontLock);
|
||||
|
||||
ImFontAtlas_Clear(atlas);
|
||||
if (!uiFontBuildFaces(atlas))
|
||||
{
|
||||
DEBUG_ERROR("Failed to resolve UI fonts");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!uiFontAddSize(atlas, size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to add the primary UI font");
|
||||
goto done;
|
||||
}
|
||||
|
||||
*large = uiFontAddSize(atlas, size * 1.3f);
|
||||
if (!*large)
|
||||
{
|
||||
DEBUG_ERROR("Failed to add the large UI font");
|
||||
goto done;
|
||||
}
|
||||
|
||||
result = true;
|
||||
|
||||
done:
|
||||
if (!result)
|
||||
{
|
||||
ImFontAtlas_Clear(atlas);
|
||||
uiFontClearFaces();
|
||||
}
|
||||
|
||||
LG_UNLOCK(UIFontLock);
|
||||
return result;
|
||||
}
|
||||
|
||||
void font_free(void)
|
||||
{
|
||||
if (!UIFontPlatform)
|
||||
return;
|
||||
|
||||
if (UIFontLockInitialized)
|
||||
LG_LOCK(UIFontLock);
|
||||
|
||||
uiFontClearFaces();
|
||||
uiFontGlyphFree(&UIFontChars);
|
||||
uiFontGlyphFree(&UIFontRequested);
|
||||
uiFontGlyphFree(&UIFontResolved);
|
||||
|
||||
FontPlatform * platform = UIFontPlatform;
|
||||
UIFontPlatform = NULL;
|
||||
UIFontSelected = false;
|
||||
|
||||
if (UIFontLockInitialized)
|
||||
{
|
||||
LG_UNLOCK(UIFontLock);
|
||||
LG_LOCK_FREE(UIFontLock);
|
||||
UIFontLockInitialized = false;
|
||||
}
|
||||
|
||||
font_platformFree(platform);
|
||||
}
|
||||
|
||||
51
client/src/font_platform.h
Normal file
51
client/src/font_platform.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_FONT_PLATFORM_
|
||||
#define _H_LG_FONT_PLATFORM_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct FontPlatform FontPlatform;
|
||||
|
||||
typedef enum FontPlatformFaceResult
|
||||
{
|
||||
FONT_PLATFORM_FACE_ERROR,
|
||||
FONT_PLATFORM_FACE_SKIP,
|
||||
FONT_PLATFORM_FACE_ACCEPT,
|
||||
}
|
||||
FontPlatformFaceResult;
|
||||
|
||||
typedef FontPlatformFaceResult (*FontPlatformAddFaceFn)(
|
||||
void * opaque, const char * file, int index,
|
||||
const uint32_t * glyphs, size_t glyphCount);
|
||||
|
||||
bool font_platformInit(FontPlatform ** platform);
|
||||
void font_platformFree(FontPlatform * platform);
|
||||
|
||||
char * font_platformSelect(FontPlatform * platform, const char * fontName);
|
||||
|
||||
bool font_platformResolve(FontPlatform * platform,
|
||||
const uint32_t * glyphs, size_t glyphCount,
|
||||
FontPlatformAddFaceFn addFace, void * opaque, size_t * unresolved);
|
||||
|
||||
#endif
|
||||
@@ -21,21 +21,13 @@
|
||||
#include "main.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include "common/array.h"
|
||||
@@ -67,9 +59,10 @@
|
||||
#include "overlay_utils.h"
|
||||
#include "util.h"
|
||||
#include "render_queue.h"
|
||||
#include "evdev.h"
|
||||
#include "frame_scheduler.h"
|
||||
#include "input.h"
|
||||
#include "platform/input.h"
|
||||
#include "platform/process.h"
|
||||
#include "sw_surface.h"
|
||||
|
||||
#ifdef ENABLE_TESTS
|
||||
@@ -731,7 +724,7 @@ static void preSwapCallback(void * udata)
|
||||
if (!written)
|
||||
DEBUG_ERROR("Failed to write test capture to: %s", l_testCapture.path);
|
||||
else
|
||||
DEBUG_INFO("Wrote test capture for frame %lu to: %s",
|
||||
DEBUG_INFO("Wrote test capture for frame %" PRIu64 " to: %s",
|
||||
serial, l_testCapture.path);
|
||||
app_setState(APP_STATE_SHUTDOWN);
|
||||
#endif
|
||||
@@ -2734,26 +2727,20 @@ static void fallbackHandleEvents(void)
|
||||
"SPICE fallback services will not function until this is corrected.");
|
||||
}
|
||||
|
||||
void intHandler(int sig)
|
||||
static bool processStopHandler(void * opaque)
|
||||
{
|
||||
switch(sig)
|
||||
(void)opaque;
|
||||
|
||||
if (app_getState() != APP_STATE_SHUTDOWN)
|
||||
{
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
if (app_getState() != APP_STATE_SHUTDOWN)
|
||||
{
|
||||
DEBUG_INFO("Caught signal, shutting down...");
|
||||
app_setState(APP_STATE_SHUTDOWN);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_INFO("Caught second signal, force quitting...");
|
||||
printAllThreadBacktraces();
|
||||
signal(sig, SIG_DFL);
|
||||
raise(sig);
|
||||
}
|
||||
break;
|
||||
DEBUG_INFO("Caught stop request, shutting down...");
|
||||
app_setState(APP_STATE_SHUTDOWN);
|
||||
return true;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Caught second stop request, force quitting...");
|
||||
printAllThreadBacktraces();
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool tryRenderer(const int index, const LG_RendererParams lgrParams,
|
||||
@@ -3625,13 +3612,11 @@ static int lg_run(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (evdev_start())
|
||||
DEBUG_INFO("Using evdev for input capture");
|
||||
if (inputPlatformStart())
|
||||
DEBUG_INFO("Using platform input capture");
|
||||
|
||||
// override the SIGINIT handler so that we can tell the difference between
|
||||
// SIGINT and the user sending a close event, such as ALT+F4
|
||||
signal(SIGINT , intHandler);
|
||||
signal(SIGTERM, intHandler);
|
||||
if (!lgProcessInstallStopHandler(processStopHandler, NULL))
|
||||
DEBUG_WARN("Failed to install the process stop handler");
|
||||
|
||||
if (!lgTransport_create(g_params.transport, &g_state.transport))
|
||||
{
|
||||
@@ -3763,12 +3748,7 @@ static int lg_run(void)
|
||||
.allowNoInput = strcmp(g_params.transport, "test") == 0,
|
||||
.opengl = needsOpenGL,
|
||||
.jitRender = g_params.jitRender,
|
||||
.eventSource =
|
||||
{
|
||||
.fd = evdev_getEventFD(),
|
||||
.callback = evdev_dispatch,
|
||||
.opaque = NULL,
|
||||
},
|
||||
.eventSource = inputPlatformGetEventSource(),
|
||||
};
|
||||
|
||||
g_state.dsInitialized = g_state.ds->init(params);
|
||||
@@ -4183,7 +4163,7 @@ static void lg_shutdown(void)
|
||||
}
|
||||
|
||||
// Stop external input callbacks before tearing down shared client state
|
||||
evdev_stop();
|
||||
inputPlatformStop();
|
||||
if (g_state.ds && g_state.dsInitialized)
|
||||
g_state.ds->shutdown();
|
||||
|
||||
@@ -4229,8 +4209,12 @@ static void lg_shutdown(void)
|
||||
e_startup = NULL;
|
||||
}
|
||||
|
||||
app_releaseAllKeybinds();
|
||||
ll_free(g_state.bindings);
|
||||
if (g_state.bindings)
|
||||
{
|
||||
app_releaseAllKeybinds();
|
||||
ll_free(g_state.bindings);
|
||||
g_state.bindings = NULL;
|
||||
}
|
||||
|
||||
if (g_state.ds && g_state.dsInitialized)
|
||||
{
|
||||
@@ -4242,8 +4226,8 @@ static void lg_shutdown(void)
|
||||
* display server before the filesystem is unmounted. */
|
||||
clipboard_free();
|
||||
|
||||
// Input callbacks have stopped; the evdev state and overlays can go
|
||||
evdev_free();
|
||||
// Input callbacks have stopped; the platform input state and overlays can go
|
||||
inputPlatformFree();
|
||||
|
||||
if (g_state.overlays)
|
||||
{
|
||||
@@ -4276,21 +4260,25 @@ static void lg_shutdown(void)
|
||||
free(g_state.imGuiIni);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
int lgClientMain(int argc, char * argv[])
|
||||
{
|
||||
// initialize for DEBUG_* macros
|
||||
debug_init();
|
||||
|
||||
if (getuid() == 0)
|
||||
{
|
||||
DEBUG_ERROR("Do not run looking glass as root!");
|
||||
return -1;
|
||||
}
|
||||
int result = -1;
|
||||
|
||||
if (getuid() != geteuid())
|
||||
switch (lgProcessGetPrivilege())
|
||||
{
|
||||
DEBUG_ERROR("Do not run looking glass as setuid!");
|
||||
return -1;
|
||||
case LG_PROCESS_PRIVILEGE_ROOT:
|
||||
DEBUG_ERROR("Do not run looking glass as root!");
|
||||
return -1;
|
||||
|
||||
case LG_PROCESS_PRIVILEGE_SETUID:
|
||||
DEBUG_ERROR("Do not run looking glass as setuid!");
|
||||
return -1;
|
||||
|
||||
case LG_PROCESS_PRIVILEGE_NORMAL:
|
||||
break;
|
||||
}
|
||||
|
||||
lgInitProcessTitle(argc, &argv);
|
||||
@@ -4300,8 +4288,11 @@ int main(int argc, char * argv[])
|
||||
DEBUG_INFO("Locking Method: " LG_LOCK_MODE);
|
||||
cpuInfo_log();
|
||||
|
||||
if (!installCrashHandler("/proc/self/exe", argv[0]))
|
||||
char * executable = lgProcessGetExecutablePath(argv[0]);
|
||||
const char * crashExecutable = executable ? executable : argv[0];
|
||||
if (!installCrashHandler(crashExecutable, argv[0]))
|
||||
DEBUG_WARN("Failed to install the crash handler");
|
||||
free(executable);
|
||||
|
||||
lgPathsInit("looking-glass");
|
||||
config_init();
|
||||
@@ -4310,11 +4301,16 @@ int main(int argc, char * argv[])
|
||||
gl_dynProcsInit();
|
||||
|
||||
if (!lgMessage_init())
|
||||
return -1;
|
||||
goto cleanup;
|
||||
|
||||
g_state.bindings = ll_new();
|
||||
|
||||
g_state.overlays = ll_new();
|
||||
if (!g_state.bindings || !g_state.overlays)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the application lists");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
app_registerOverlay(&LGOverlaySplash, NULL);
|
||||
app_registerOverlay(&LGOverlayConfig, NULL);
|
||||
app_registerOverlay(&LGOverlayAlert , NULL);
|
||||
@@ -4335,18 +4331,34 @@ int main(int argc, char * argv[])
|
||||
if (LG_AudioDevs[i]->earlyInit)
|
||||
LG_AudioDevs[i]->earlyInit();
|
||||
|
||||
evdev_earlyInit();
|
||||
inputPlatformEarlyInit();
|
||||
|
||||
if (!config_load(argc, argv))
|
||||
return -1;
|
||||
goto cleanup;
|
||||
|
||||
const int ret = lg_run();
|
||||
result = lg_run();
|
||||
lg_shutdown();
|
||||
|
||||
cleanup:
|
||||
lgProcessRemoveStopHandler();
|
||||
|
||||
if (g_state.overlays)
|
||||
{
|
||||
app_freeOverlays();
|
||||
ll_free(g_state.overlays);
|
||||
g_state.overlays = NULL;
|
||||
}
|
||||
|
||||
if (g_state.bindings)
|
||||
{
|
||||
app_releaseAllKeybinds();
|
||||
ll_free(g_state.bindings);
|
||||
g_state.bindings = NULL;
|
||||
}
|
||||
|
||||
lgMessage_deinit();
|
||||
|
||||
config_free();
|
||||
|
||||
util_freeUIFonts();
|
||||
cleanupCrashHandler();
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -30,253 +30,6 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <fontconfig/fontconfig.h>
|
||||
|
||||
struct UIFontFace
|
||||
{
|
||||
char * file;
|
||||
int index;
|
||||
ImVector_ImWchar ranges;
|
||||
};
|
||||
|
||||
static FcConfig * FontConfig = NULL;
|
||||
static FcPattern * UIFontQuery = NULL;
|
||||
static FcPattern * UIFontPrimary = NULL;
|
||||
static FcCharSet * UIFontChars = NULL;
|
||||
static FcCharSet * UIFontRequested = NULL;
|
||||
static struct UIFontFace * UIFontFaces = NULL;
|
||||
static unsigned int UIFontFaceCount = 0;
|
||||
static LG_Lock UIFontLock;
|
||||
static bool UIFontLockInitialized = false;
|
||||
|
||||
static void uiFontClearFaces(void)
|
||||
{
|
||||
for (unsigned int i = 0; i < UIFontFaceCount; ++i)
|
||||
{
|
||||
free(UIFontFaces[i].file);
|
||||
ImVector_ImWchar_UnInit(&UIFontFaces[i].ranges);
|
||||
}
|
||||
|
||||
free(UIFontFaces);
|
||||
UIFontFaces = NULL;
|
||||
UIFontFaceCount = 0;
|
||||
}
|
||||
|
||||
static void uiFontAddRanges(const ImWchar * ranges)
|
||||
{
|
||||
for (; ranges[0]; ranges += 2)
|
||||
for (FcChar32 c = ranges[0]; c <= (FcChar32)ranges[1]; ++c)
|
||||
FcCharSetAddChar(UIFontChars, c);
|
||||
}
|
||||
|
||||
static bool uiFontCharsetToRanges(
|
||||
const FcCharSet * charset, ImVector_ImWchar * ranges)
|
||||
{
|
||||
ImFontGlyphRangesBuilder * builder =
|
||||
ImFontGlyphRangesBuilder_ImFontGlyphRangesBuilder();
|
||||
if (!builder)
|
||||
return false;
|
||||
|
||||
FcChar32 map[FC_CHARSET_MAP_SIZE];
|
||||
FcChar32 next;
|
||||
for (FcChar32 base = FcCharSetFirstPage(charset, map, &next);
|
||||
base != FC_CHARSET_DONE;
|
||||
base = FcCharSetNextPage(charset, map, &next))
|
||||
{
|
||||
for (unsigned int i = 0; i < FC_CHARSET_MAP_SIZE; ++i)
|
||||
for (unsigned int bit = 0; bit < 32; ++bit)
|
||||
{
|
||||
if (!(map[i] & (UINT32_C(1) << bit)))
|
||||
continue;
|
||||
|
||||
const FcChar32 c = base + i * 32 + bit;
|
||||
if (c >= 0x20 && c <= IM_UNICODE_CODEPOINT_MAX)
|
||||
ImFontGlyphRangesBuilder_AddChar(builder, (ImWchar)c);
|
||||
}
|
||||
}
|
||||
|
||||
ImVector_ImWchar_Init(ranges);
|
||||
ImFontGlyphRangesBuilder_BuildRanges(builder, ranges);
|
||||
ImFontGlyphRangesBuilder_destroy(builder);
|
||||
return ranges->Size > 1;
|
||||
}
|
||||
|
||||
static bool uiFontAddFace(const FcPattern * pattern, FcCharSet ** remainingPtr)
|
||||
{
|
||||
FcCharSet * remaining = *remainingPtr;
|
||||
FcChar8 * file;
|
||||
FcCharSet * charset;
|
||||
FcBool outline = FcTrue;
|
||||
int index = 0;
|
||||
|
||||
if (FcPatternGetString(pattern, FC_FILE, 0, &file) != FcResultMatch ||
|
||||
FcPatternGetCharSet(pattern, FC_CHARSET, 0, &charset) != FcResultMatch)
|
||||
return true;
|
||||
|
||||
if (FcPatternGetBool(pattern, FC_OUTLINE, 0, &outline) == FcResultMatch &&
|
||||
!outline)
|
||||
return true;
|
||||
|
||||
if (FcPatternGetInteger(pattern, FC_INDEX, 0, &index) == FcResultMatch)
|
||||
index &= 0xFFFF;
|
||||
|
||||
FcCharSet * covered = FcCharSetIntersect(remaining, charset);
|
||||
if (!covered)
|
||||
return false;
|
||||
|
||||
if (FcCharSetCount(covered) == 0)
|
||||
{
|
||||
FcCharSetDestroy(covered);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t fontDataSize;
|
||||
void * fontData = igImFileLoadToMemory(
|
||||
(const char *)file, "rb", &fontDataSize, 0);
|
||||
if (!fontData)
|
||||
{
|
||||
DEBUG_WARN("Failed to read UI font face: %s", file);
|
||||
FcCharSetDestroy(covered);
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool compatible = font_isStbTruetypeCompatible(
|
||||
fontData, fontDataSize, index);
|
||||
igMemFree(fontData);
|
||||
if (!compatible)
|
||||
{
|
||||
DEBUG_WARN("Skipping stb-incompatible UI font face: %s (index %d)",
|
||||
file, index);
|
||||
FcCharSetDestroy(covered);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct UIFontFace * faces = realloc(UIFontFaces,
|
||||
sizeof(*UIFontFaces) * (UIFontFaceCount + 1));
|
||||
if (!faces)
|
||||
{
|
||||
FcCharSetDestroy(covered);
|
||||
return false;
|
||||
}
|
||||
UIFontFaces = faces;
|
||||
|
||||
struct UIFontFace * face = &UIFontFaces[UIFontFaceCount];
|
||||
memset(face, 0, sizeof(*face));
|
||||
face->file = strdup((const char *)file);
|
||||
if (!face->file || !uiFontCharsetToRanges(covered, &face->ranges))
|
||||
{
|
||||
free(face->file);
|
||||
face->file = NULL;
|
||||
FcCharSetDestroy(covered);
|
||||
return false;
|
||||
}
|
||||
|
||||
face->index = index;
|
||||
|
||||
++UIFontFaceCount;
|
||||
|
||||
FcCharSet * next = FcCharSetSubtract(remaining, covered);
|
||||
FcCharSetDestroy(covered);
|
||||
if (!next)
|
||||
return false;
|
||||
|
||||
FcCharSetDestroy(remaining);
|
||||
*remainingPtr = next;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool uiFontBuildFaces(ImFontAtlas * atlas)
|
||||
{
|
||||
uiFontClearFaces();
|
||||
|
||||
uiFontAddRanges(ImFontAtlas_GetGlyphRangesDefault(atlas));
|
||||
uiFontAddRanges((ImWchar[]) {
|
||||
0x2190, 0x2193, // four directional arrows
|
||||
0,
|
||||
});
|
||||
|
||||
FcCharSet * remaining = FcCharSetCopy(UIFontChars);
|
||||
if (!remaining)
|
||||
return false;
|
||||
|
||||
if (!uiFontAddFace(UIFontPrimary, &remaining))
|
||||
goto fail;
|
||||
|
||||
if (FcCharSetCount(remaining) > 0)
|
||||
{
|
||||
FcPattern * query = FcPatternDuplicate(UIFontQuery);
|
||||
if (!query)
|
||||
goto fail;
|
||||
|
||||
FcPatternDel(query, FC_CHARSET);
|
||||
FcPatternAddCharSet(query, FC_CHARSET, remaining);
|
||||
|
||||
FcResult result;
|
||||
FcFontSet * fonts = FcFontSort(FontConfig, query, FcTrue, NULL, &result);
|
||||
FcPatternDestroy(query);
|
||||
if (!fonts)
|
||||
goto fail;
|
||||
|
||||
for (int i = 0; i < fonts->nfont && FcCharSetCount(remaining) > 0; ++i)
|
||||
if (!uiFontAddFace(fonts->fonts[i], &remaining))
|
||||
{
|
||||
FcFontSetSortDestroy(fonts);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
FcFontSetSortDestroy(fonts);
|
||||
}
|
||||
|
||||
if (FcCharSetCount(remaining) > 0)
|
||||
{
|
||||
FcCharSet * missing = FcCharSetIntersect(remaining, UIFontRequested);
|
||||
if (missing)
|
||||
{
|
||||
if (FcCharSetCount(missing) > 0)
|
||||
DEBUG_WARN("%u requested UI glyphs have no usable outline font",
|
||||
FcCharSetCount(missing));
|
||||
FcCharSetDestroy(missing);
|
||||
}
|
||||
}
|
||||
|
||||
FcCharSetDestroy(remaining);
|
||||
return UIFontFaceCount > 0;
|
||||
|
||||
fail:
|
||||
FcCharSetDestroy(remaining);
|
||||
uiFontClearFaces();
|
||||
return false;
|
||||
}
|
||||
|
||||
static ImFont * uiFontAddSize(ImFontAtlas * atlas, float size)
|
||||
{
|
||||
ImFont * result = NULL;
|
||||
|
||||
for (unsigned int i = 0; i < UIFontFaceCount; ++i)
|
||||
{
|
||||
ImFontConfig * config = ImFontConfig_ImFontConfig();
|
||||
if (!config)
|
||||
return NULL;
|
||||
|
||||
config->MergeMode = result != NULL;
|
||||
config->FontNo = UIFontFaces[i].index;
|
||||
|
||||
ImFont * font = ImFontAtlas_AddFontFromFileTTF(atlas,
|
||||
UIFontFaces[i].file, size, config, UIFontFaces[i].ranges.Data);
|
||||
ImFontConfig_destroy(config);
|
||||
|
||||
if (!font)
|
||||
{
|
||||
DEBUG_WARN("Failed to add UI font face: %s", UIFontFaces[i].file);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!result)
|
||||
result = font;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool util_fileGetContents(const char * filename, char ** buffer, size_t * length)
|
||||
{
|
||||
@@ -473,177 +226,26 @@ bool util_hasGLExt(const char * exts, const char * ext)
|
||||
|
||||
bool util_initUIFonts(void)
|
||||
{
|
||||
if (FontConfig)
|
||||
return true;
|
||||
|
||||
FontConfig = FcInitLoadConfigAndFonts();
|
||||
|
||||
if (!FontConfig)
|
||||
{
|
||||
DEBUG_ERROR("FcInitLoadConfigAndFonts Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
UIFontChars = FcCharSetCreate();
|
||||
UIFontRequested = FcCharSetCreate();
|
||||
if (!UIFontChars || !UIFontRequested)
|
||||
{
|
||||
DEBUG_ERROR("FcCharSetCreate Failed");
|
||||
if (UIFontChars)
|
||||
FcCharSetDestroy(UIFontChars);
|
||||
if (UIFontRequested)
|
||||
FcCharSetDestroy(UIFontRequested);
|
||||
UIFontChars = NULL;
|
||||
UIFontRequested = NULL;
|
||||
FcConfigDestroy(FontConfig);
|
||||
FontConfig = NULL;
|
||||
FcFini();
|
||||
return false;
|
||||
}
|
||||
|
||||
LG_LOCK_INIT(UIFontLock);
|
||||
UIFontLockInitialized = true;
|
||||
return true;
|
||||
return font_init();
|
||||
}
|
||||
|
||||
char * util_getUIFont(const char * fontName)
|
||||
{
|
||||
char * ttf = NULL;
|
||||
|
||||
FcPattern * pat = FcNameParse((const FcChar8*) fontName);
|
||||
if (!pat)
|
||||
{
|
||||
DEBUG_ERROR("FCNameParse failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FcConfigSubstitute(FontConfig, pat, FcMatchPattern);
|
||||
FcDefaultSubstitute(pat);
|
||||
FcResult result;
|
||||
FcChar8 * file = NULL;
|
||||
FcPattern * match = FcFontMatch(FontConfig, pat, &result);
|
||||
|
||||
if (!match)
|
||||
{
|
||||
DEBUG_ERROR("FcFontMatch Failed");
|
||||
goto fail_parse;
|
||||
}
|
||||
|
||||
FcPatternDestroy(UIFontQuery);
|
||||
FcPatternDestroy(UIFontPrimary);
|
||||
UIFontQuery = FcPatternDuplicate(pat);
|
||||
UIFontPrimary = FcPatternDuplicate(match);
|
||||
if (!UIFontQuery || !UIFontPrimary)
|
||||
{
|
||||
DEBUG_ERROR("Failed to save the UI font pattern");
|
||||
goto fail_match;
|
||||
}
|
||||
|
||||
if (FcPatternGetString(match, FC_FILE, 0, &file) == FcResultMatch)
|
||||
ttf = strdup((char *) file);
|
||||
else
|
||||
DEBUG_ERROR("Failed to locate the requested font: %s", fontName);
|
||||
|
||||
fail_match:
|
||||
FcPatternDestroy(match);
|
||||
|
||||
fail_parse:
|
||||
FcPatternDestroy(pat);
|
||||
return ttf;
|
||||
return font_get(fontName);
|
||||
}
|
||||
|
||||
bool util_uiFontAddText(const char * text)
|
||||
{
|
||||
if (!UIFontChars || !text)
|
||||
return false;
|
||||
|
||||
bool changed = false;
|
||||
LG_LOCK(UIFontLock);
|
||||
|
||||
const char * pos = text;
|
||||
while (*pos)
|
||||
{
|
||||
unsigned int c;
|
||||
const int length = igImTextCharFromUtf8(&c, pos, NULL);
|
||||
if (length <= 0)
|
||||
break;
|
||||
pos += length;
|
||||
|
||||
if (c < 0x20 || c > IM_UNICODE_CODEPOINT_MAX)
|
||||
continue;
|
||||
|
||||
FcCharSetAddChar(UIFontRequested, c);
|
||||
if (!FcCharSetHasChar(UIFontChars, c) &&
|
||||
FcCharSetAddChar(UIFontChars, c))
|
||||
changed = true;
|
||||
}
|
||||
|
||||
LG_UNLOCK(UIFontLock);
|
||||
return changed;
|
||||
return font_addText(text);
|
||||
}
|
||||
|
||||
bool util_buildUIFontAtlas(
|
||||
ImFontAtlas * atlas, float size, ImFont ** large)
|
||||
{
|
||||
if (!atlas || !UIFontQuery || !UIFontPrimary || !UIFontChars)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
LG_LOCK(UIFontLock);
|
||||
|
||||
ImFontAtlas_Clear(atlas);
|
||||
if (!uiFontBuildFaces(atlas))
|
||||
{
|
||||
DEBUG_ERROR("Failed to resolve UI fonts");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!uiFontAddSize(atlas, size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to add the primary UI font");
|
||||
goto done;
|
||||
}
|
||||
|
||||
*large = uiFontAddSize(atlas, size * 1.3f);
|
||||
if (!*large)
|
||||
{
|
||||
DEBUG_ERROR("Failed to add the large UI font");
|
||||
goto done;
|
||||
}
|
||||
|
||||
result = true;
|
||||
|
||||
done:
|
||||
LG_UNLOCK(UIFontLock);
|
||||
return result;
|
||||
return font_buildAtlas(atlas, size, large);
|
||||
}
|
||||
|
||||
void util_freeUIFonts(void)
|
||||
{
|
||||
if (!FontConfig)
|
||||
return;
|
||||
|
||||
if (UIFontLockInitialized)
|
||||
LG_LOCK(UIFontLock);
|
||||
|
||||
uiFontClearFaces();
|
||||
FcCharSetDestroy(UIFontChars);
|
||||
FcCharSetDestroy(UIFontRequested);
|
||||
FcPatternDestroy(UIFontQuery);
|
||||
FcPatternDestroy(UIFontPrimary);
|
||||
UIFontChars = NULL;
|
||||
UIFontRequested = NULL;
|
||||
UIFontQuery = NULL;
|
||||
UIFontPrimary = NULL;
|
||||
|
||||
if (UIFontLockInitialized)
|
||||
{
|
||||
LG_UNLOCK(UIFontLock);
|
||||
LG_LOCK_FREE(UIFontLock);
|
||||
UIFontLockInitialized = false;
|
||||
}
|
||||
|
||||
FcConfigDestroy(FontConfig);
|
||||
FontConfig = NULL;
|
||||
FcFini();
|
||||
font_free();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user