Starting work on Android port

This commit is contained in:
Yousef Khadadeh 2026-02-24 17:21:41 +03:00
parent 5d1b7678f0
commit ab13ccd595
183 changed files with 6790 additions and 906 deletions

View file

@ -6,6 +6,13 @@
#ifdef __EMSCRIPTEN__
#include <include/ports/SkFontMgr_directory.h>
#elif __ANDROID__
#include <include/ports/SkFontScanner_FreeType.h>
#include <include/ports/SkFontMgr_data.h>
#include <include/ports/SkFontMgr_android_ndk.h>
#include <SDL3/SDL_filesystem.h>
#include <Helpers/StringHelpers.hpp>
#include <Helpers/Logger.hpp>
#elif __APPLE__
#include <include/ports/SkFontMgr_mac_ct.h>
#include <ApplicationServices/ApplicationServices.h>
@ -27,6 +34,29 @@ FontData::FontData()
#ifdef __EMSCRIPTEN__
defaultFontMgr = SkFontMgr_New_Custom_Directory("data/fonts");
#elif __ANDROID__
{
std::vector<sk_sp<SkData>> fontFiles;
int globCount;
std::filesystem::path fontFolderPath("data/fonts");
char** filesInPath = SDL_GlobDirectory(fontFolderPath.c_str(), nullptr, 0, &globCount);
if(filesInPath) {
Logger::get().cross_platform_println("Yay!");
for(int i = 0; i < globCount; i++) {
std::filesystem::path filePath = fontFolderPath / std::filesystem::path(filesInPath[i]);
SDL_PathInfo fileInfo;
Logger::get().cross_platform_println("Font: " + filePath.string());
if(SDL_GetPathInfo(filePath.c_str(), &fileInfo) && fileInfo.type == SDL_PATHTYPE_FILE) {
std::string strData = read_file_to_string(filePath);
fontFiles.emplace_back(SkData::MakeWithCopy(strData.data(), strData.size()));
Logger::get().cross_platform_println("loaded!");
}
}
SDL_free(filesInPath);
defaultFontMgr = SkFontMgr_New_Custom_Data(SkSpan(fontFiles.data(), fontFiles.size()));
}
}
localFontMgr = SkFontMgr_New_AndroidNDK(true, SkFontScanner_Make_FreeType());
#elif __APPLE__
for(const auto& dirEntry : std::filesystem::recursive_directory_iterator("data/fonts")) {
if(dirEntry.is_regular_file()) {
@ -53,7 +83,6 @@ FontData::FontData()
localFontMgr = SkFontMgr_New_FontConfig(nullptr, SkFontScanner_Make_FreeType());
defaultFontMgr = SkFontMgr_New_Custom_Directory("data/fonts");
#endif
map["Roboto"] = defaultFontMgr->makeFromFile("data/fonts/Roboto-variable.ttf");
collection = sk_make_sp<skia::textlayout::FontCollection>();

View file

@ -14,6 +14,7 @@
#include "VersionConstants.hpp"
#include "World.hpp"
#include <SDL3/SDL_dialog.h>
#include <SDL3/SDL_filesystem.h>
#include <SDL3/SDL_render.h>
#include <algorithm>
#include <filesystem>
@ -52,28 +53,39 @@ Toolbar::Toolbar(MainProgram& initMain):
// NOTE: On windows, when the native file picker is open, any call to MakeFromFile fails
// So, it's better to load the icons at the beginning of the program so that the icon loading doesn't fail later
load_icons_at("data/icons");
#ifdef __ANDROID__
load_icons_at("data/icons/RemixIcon"); // SDL_GlobDirectory doesn't work recursively on Android
#endif
load_default_palette();
load_default_theme();
}
void Toolbar::load_icons_at(const std::filesystem::path& pathToLoad) {
for(auto& iconDirEntry : std::filesystem::recursive_directory_iterator(pathToLoad)) {
if(iconDirEntry.is_regular_file()) {
std::string iconRelativePath = iconDirEntry.path().relative_path().string();
std::replace(iconRelativePath.begin(), iconRelativePath.end(), '\\', '/');
auto stream = SkStream::MakeFromFile(iconRelativePath.c_str());
if(!stream)
throw std::runtime_error("[Toolbar::Toolbar] Could not open file " + iconRelativePath);
auto svgDom = SkSVGDOM::Builder().make(*stream);
if(!svgDom)
throw std::runtime_error("[Toolbar::Toolbar] Could not parse SVG " + iconRelativePath);
else {
if(svgDom->containerSize().width() == 0 || svgDom->containerSize().height() == 0)
svgDom->setContainerSize({1000, 1000});
io->svgData[iconRelativePath] = svgDom;
int globCount;
char** filesInPath = SDL_GlobDirectory(pathToLoad.c_str(), nullptr, 0, &globCount);
if(filesInPath) {
for(int i = 0; i < globCount; i++) {
std::filesystem::path filePath = pathToLoad / std::filesystem::path(filesInPath[i]);
SDL_PathInfo fileInfo;
if(SDL_GetPathInfo(filePath.c_str(), &fileInfo) && fileInfo.type == SDL_PATHTYPE_FILE) {
Logger::get().log("INFO", filePath.string());
std::string iconRelativePath = filePath.relative_path().string();
std::replace(iconRelativePath.begin(), iconRelativePath.end(), '\\', '/');
std::string iconData = read_file_to_string(iconRelativePath);
auto stream = SkMemoryStream(iconData.c_str(), iconData.size(), false);
auto svgDom = SkSVGDOM::Builder().make(stream);
if(!svgDom)
throw std::runtime_error("[Toolbar::Toolbar] Could not parse SVG " + iconRelativePath);
else {
if(svgDom->containerSize().width() == 0 || svgDom->containerSize().height() == 0)
svgDom->setContainerSize({1000, 1000});
io->svgData[iconRelativePath] = svgDom;
}
}
}
SDL_free(filesInPath);
}
}
@ -216,11 +228,22 @@ void Toolbar::color_selector_right(Vector4f* color) {
}
void Toolbar::load_licenses() {
std::filesystem::path third_party_license_path("data/third_party_licenses");
for(const auto& entry : std::filesystem::directory_iterator(third_party_license_path)) {
if(entry.is_regular_file())
thirdPartyLicenses.emplace_back(entry.path().filename().string(), read_file_to_string(entry.path()));
{
int globCount;
std::filesystem::path third_party_license_path("data/third_party_licenses");
char** filesInPath = SDL_GlobDirectory(third_party_license_path.c_str(), nullptr, 0, &globCount);
if(filesInPath) {
for(int i = 0; i < globCount; i++) {
std::filesystem::path filePath = third_party_license_path / std::filesystem::path(filesInPath[i]);
SDL_PathInfo fileInfo;
if(SDL_GetPathInfo(filePath.c_str(), &fileInfo) && fileInfo.type == SDL_PATHTYPE_FILE) {
thirdPartyLicenses.emplace_back(filePath.filename().string(), read_file_to_string(filePath));
}
}
SDL_free(filesInPath);
}
}
std::sort(thirdPartyLicenses.begin(), thirdPartyLicenses.end(), [](const auto& a1, const auto& a2) {
return std::lexicographical_compare(a1.first.begin(), a1.first.end(), a2.first.begin(), a2.first.end());
});

View file

@ -2,7 +2,7 @@
#include "Helpers/StringHelpers.hpp"
#include "Helpers/FileDownloader.hpp"
#include "include/gpu/GpuTypes.h"
#include "sago/platform_folders.h"
#include <SDL3/SDL_filesystem.h>
#include <SDL3/SDL_hints.h>
#include <SDL3/SDL_oldnames.h>
#include <chrono>
@ -326,7 +326,9 @@ const char* emscripten_before_unload(int eventType, const void *reserved, void *
#endif
void init_logs(MainStruct& mS) {
mS.homePath = std::filesystem::current_path();
char* homePathSDL = SDL_GetCurrentDirectory();
mS.homePath = std::filesystem::path(homePathSDL);
SDL_free(homePathSDL);
#ifdef __EMSCRIPTEN__
EM_ASM(
FS.mkdir('/infinipaint');
@ -348,15 +350,10 @@ void init_logs(MainStruct& mS) {
std::filesystem::create_directory(CONFIG_FOLDER_NAME);
mS.configPath = mS.homePath / CONFIG_FOLDER_NAME;
#else
std::string CONFIG_FOLDER_NAME = "infinipaint";
std::filesystem::path configHome(sago::getConfigHome());
std::filesystem::create_directory(configHome);
mS.configPath = configHome / CONFIG_FOLDER_NAME;
char* configPathSDL = SDL_GetPrefPath("ErrorAtLine0", "infinipaint");
mS.configPath = std::filesystem::path(configPathSDL);
SDL_free(configPathSDL);
#endif
if(!std::filesystem::create_directory(mS.configPath))
std::cout << "Failed to create config directory." << std::endl;
mS.logFile = std::ofstream(mS.configPath / "log.txt");
Logger::get().add_log("FATAL", [&, mS = &mS](const std::string& text) {
mS->logFile << "[FATAL] " << text << std::endl;
@ -365,7 +362,7 @@ void init_logs(MainStruct& mS) {
});
Logger::get().add_log("INFO", [&, mS = &mS](const std::string& text) {
mS->logFile << "[INFO] " << text << std::endl;
std::cout << "[INFO] " << text << std::endl;
Logger::get().cross_platform_println("[INFO] " + text);
});
Logger::get().log("INFO", "Home Path: " + mS.homePath.string());
@ -425,13 +422,16 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
mS.m->configPath = mS.configPath;
mS.m->homePath = mS.homePath;
#ifndef __EMSCRIPTEN__
mS.m->documentsPath = sago::getDocumentsFolder();
if(!std::filesystem::exists(mS.m->documentsPath) || !std::filesystem::is_directory(mS.m->documentsPath)) {
mS.m->documentsPath = sago::getDesktopFolder();
if(!std::filesystem::exists(mS.m->documentsPath) || !std::filesystem::is_directory(mS.m->documentsPath)) {
const char* documentsPathSDL = SDL_GetUserFolder(SDL_FOLDER_DOCUMENTS);
if(!documentsPathSDL) {
documentsPathSDL = SDL_GetUserFolder(SDL_FOLDER_DESKTOP);
if(!documentsPathSDL)
mS.m->documentsPath = mS.m->configPath;
}
else
mS.m->documentsPath = std::filesystem::path(documentsPathSDL);
}
else
mS.m->documentsPath = std::filesystem::path(documentsPathSDL);
#endif
mS.m->window.sdlWindow = mS.window;
mS.m->update_scale_and_density();