mirror of
https://github.com/slendidev/lunar.git
synced 2025-12-08 10:29:52 +02:00
26
.clang-format
Normal file
26
.clang-format
Normal file
@@ -0,0 +1,26 @@
|
||||
UseTab: ForIndentation
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 80
|
||||
|
||||
AlignEscapedNewlines: DontAlign
|
||||
AlignTrailingComments:
|
||||
Kind: Always
|
||||
OverEmptyLines: 0
|
||||
BasedOnStyle: WebKit
|
||||
BraceWrapping:
|
||||
AfterFunction: true
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: true
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentRequiresClause: false
|
||||
InsertNewlineAtEOF: true
|
||||
LineEnding: LF
|
||||
NamespaceIndentation: None
|
||||
PointerAlignment: Right # east pointer
|
||||
QualifierAlignment: Right # east const
|
||||
RemoveSemicolon: true
|
||||
RequiresClausePosition: WithFollowing
|
||||
RequiresExpressionIndentation: OuterScope
|
||||
SpaceAfterTemplateKeyword: false
|
||||
5
.clangd
Normal file
5
.clangd
Normal file
@@ -0,0 +1,5 @@
|
||||
CompileFlags:
|
||||
Add: [
|
||||
"-isystem", "/nix/store/9hy16b0ba5bz0gd309rvhh119mn44f4j-libcxx-21.1.2-dev/include/c++/v1",
|
||||
"-isystem", "/nix/store/jkqdwnd0ifr3fkjif3lz003an2fhbh17-glibc-2.40-66-dev/include"
|
||||
]
|
||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
[Bb]uild*
|
||||
result
|
||||
.cache
|
||||
.direnv
|
||||
59
flake.lock
generated
Normal file
59
flake.lock
generated
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1764517877,
|
||||
"narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=",
|
||||
"rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c",
|
||||
"revCount": 904649,
|
||||
"type": "tarball",
|
||||
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.904649%2Brev-2d293cbfa5a793b4c50d17c05ef9e385b90edf6c/019ad7f2-e8f3-79e9-ad92-dd7a45c069d3/source.tar.gz?rev=2d293cbfa5a793b4c50d17c05ef9e385b90edf6c&revCount=904649"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://flakehub.com/f/NixOS/nixpkgs/0.1"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
72
flake.nix
Normal file
72
flake.nix
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
description = "My flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
nativeBuildInputs = with pkgs; [
|
||||
vulkan-loader
|
||||
vulkan-memory-allocator
|
||||
vulkan-validation-layers
|
||||
vk-bootstrap
|
||||
openxr-loader
|
||||
wayland
|
||||
zlib
|
||||
sdl3
|
||||
];
|
||||
buildInputs = with pkgs; [
|
||||
meson
|
||||
ninja
|
||||
pkg-config
|
||||
];
|
||||
in
|
||||
{
|
||||
devShells.default =
|
||||
pkgs.mkShell.override { stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.llvmPackages_21.stdenv; }
|
||||
{
|
||||
packages =
|
||||
with pkgs;
|
||||
[
|
||||
llvmPackages_21.clang-tools
|
||||
lldb
|
||||
codespell
|
||||
doxygen
|
||||
gtest
|
||||
cppcheck
|
||||
]
|
||||
++ buildInputs
|
||||
++ nativeBuildInputs
|
||||
++ pkgs.lib.optionals pkgs.stdenv.isLinux (
|
||||
with pkgs;
|
||||
[
|
||||
valgrind-light
|
||||
]
|
||||
);
|
||||
shellHook = ''
|
||||
export VK_BOOTSTRAP_LIB=${pkgs.vk-bootstrap}
|
||||
export VK_BOOTSTRAP_DEV=${pkgs.vk-bootstrap.dev}
|
||||
|
||||
cat > .clangd << 'EOF'
|
||||
CompileFlags:
|
||||
Add: [
|
||||
"-isystem", "${pkgs.llvmPackages_21.libcxx.dev}/include/c++/v1",
|
||||
"-isystem", "${pkgs.glibc.dev}/include"
|
||||
]
|
||||
EOF
|
||||
'';
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
50
meson.build
Normal file
50
meson.build
Normal file
@@ -0,0 +1,50 @@
|
||||
project('vr-compositor', 'cpp',
|
||||
version: '0.1',
|
||||
default_options: ['cpp_std=c++23']
|
||||
)
|
||||
|
||||
cc = meson.get_compiler('cpp')
|
||||
|
||||
wayland_dep = dependency('wayland-server')
|
||||
vulkan_dep = dependency('vulkan')
|
||||
openxr_dep = dependency('openxr')
|
||||
zlib_dep = dependency('zlib')
|
||||
sdl3_dep = dependency('sdl3')
|
||||
|
||||
vkbootstrap_dev = get_option('vkbootstrap_dev')
|
||||
vkbootstrap_lib = get_option('vkbootstrap_lib')
|
||||
|
||||
vkbootstrap_inc = include_directories(
|
||||
join_paths(vkbootstrap_dev, 'include')
|
||||
)
|
||||
|
||||
vkbootstrap_dep = cc.find_library(
|
||||
'vk-bootstrap',
|
||||
dirs: join_paths(vkbootstrap_lib, 'lib'),
|
||||
required: true,
|
||||
)
|
||||
|
||||
add_project_arguments(
|
||||
'-Wall',
|
||||
'-Wextra',
|
||||
'-Wpedantic',
|
||||
'-Werror',
|
||||
language : ['c', 'cpp']
|
||||
)
|
||||
|
||||
exe = executable('vr-compositor',
|
||||
[
|
||||
'src/main.cpp',
|
||||
'src/Logger.cpp',
|
||||
'src/Application.cpp',
|
||||
],
|
||||
include_directories: vkbootstrap_inc,
|
||||
dependencies: [
|
||||
wayland_dep,
|
||||
vulkan_dep,
|
||||
openxr_dep,
|
||||
vkbootstrap_dep,
|
||||
zlib_dep,
|
||||
sdl3_dep,
|
||||
]
|
||||
)
|
||||
2
meson_options.txt
Normal file
2
meson_options.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
option('vkbootstrap_dev', type: 'string', description: 'vk-bootstrap dev output path')
|
||||
option('vkbootstrap_lib', type: 'string', description: 'vk-bootstrap lib output path')
|
||||
116
src/Application.cpp
Normal file
116
src/Application.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "Application.h"
|
||||
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <iostream>
|
||||
#include <print>
|
||||
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
#include <VkBootstrap.h>
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
namespace Lunar {
|
||||
|
||||
Application::Application()
|
||||
{
|
||||
vkb::InstanceBuilder instance_builder {};
|
||||
instance_builder.request_validation_layers()
|
||||
.set_app_name("Lunar")
|
||||
.set_engine_name("Lunar")
|
||||
.require_api_version(1, 0, 0)
|
||||
.set_debug_callback_user_data_pointer(this)
|
||||
.set_debug_callback(
|
||||
[](VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT message_type,
|
||||
VkDebugUtilsMessengerCallbackDataEXT const *callback_data,
|
||||
void *user_data) {
|
||||
auto app { reinterpret_cast<Application *>(user_data) };
|
||||
|
||||
auto level = Logger::Level::Debug;
|
||||
if (message_severity
|
||||
& VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
||||
level = Logger::Level::Error;
|
||||
} else if (message_severity
|
||||
& VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
|
||||
level = Logger::Level::Warning;
|
||||
} else if (message_severity
|
||||
& VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
|
||||
level = Logger::Level::Info;
|
||||
}
|
||||
|
||||
app->m_logger.log(level,
|
||||
std::format("[{}] {}",
|
||||
vkb::to_string_message_type(message_type),
|
||||
callback_data->pMessage));
|
||||
|
||||
return VK_FALSE;
|
||||
});
|
||||
auto const instance_builder_ret { instance_builder.build() };
|
||||
if (!instance_builder_ret) {
|
||||
std::println(std::cerr, "Failed to create Vulkan instance. Error: {}",
|
||||
instance_builder_ret.error().message());
|
||||
throw std::runtime_error("App init fail");
|
||||
}
|
||||
|
||||
m_vkb_instance = instance_builder_ret.value();
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
std::println(std::cerr, "Failed to initialize SDL.");
|
||||
throw std::runtime_error("App init fail");
|
||||
}
|
||||
|
||||
m_window = SDL_CreateWindow(
|
||||
"Lunar", 1280, 720, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE);
|
||||
if (!m_window) {
|
||||
m_logger.err("Failed to create SDL window");
|
||||
throw std::runtime_error("App init fail");
|
||||
}
|
||||
|
||||
if (!SDL_Vulkan_CreateSurface(
|
||||
m_window, m_vkb_instance, nullptr, &m_vk_surface)) {
|
||||
m_logger.err("Failed to create vulkan surface");
|
||||
throw std::runtime_error("App init fail");
|
||||
}
|
||||
|
||||
vkb::PhysicalDeviceSelector phys_device_selector { m_vkb_instance };
|
||||
phys_device_selector.set_surface(m_vk_surface)
|
||||
.add_required_extensions({ VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
|
||||
VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
|
||||
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
|
||||
VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
|
||||
VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME });
|
||||
auto physical_device_selector_return { phys_device_selector.select() };
|
||||
if (!physical_device_selector_return) {
|
||||
std::println(std::cerr, "Failed to find Vulkan device. Error: {}",
|
||||
physical_device_selector_return.error().message());
|
||||
throw std::runtime_error("App init fail");
|
||||
}
|
||||
auto phys_device = physical_device_selector_return.value();
|
||||
|
||||
m_logger.info("App init done!");
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
SDL_Vulkan_DestroySurface(m_vkb_instance, m_vk_surface, nullptr);
|
||||
SDL_DestroyWindow(m_window);
|
||||
SDL_Quit();
|
||||
|
||||
vkb::destroy_instance(m_vkb_instance);
|
||||
|
||||
m_logger.info("App destroy done!");
|
||||
}
|
||||
|
||||
auto Application::run() -> void
|
||||
{
|
||||
SDL_Event e;
|
||||
|
||||
while (m_running) {
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (e.type == SDL_EVENT_QUIT)
|
||||
m_running = false;
|
||||
}
|
||||
// nothing else
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Lunar
|
||||
26
src/Application.h
Normal file
26
src/Application.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <VkBootstrap.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include "src/Logger.h"
|
||||
|
||||
namespace Lunar {
|
||||
|
||||
struct Application {
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
auto run() -> void;
|
||||
|
||||
private:
|
||||
vkb::Instance m_vkb_instance;
|
||||
VkSurfaceKHR m_vk_surface { nullptr };
|
||||
SDL_Window *m_window { nullptr };
|
||||
Logger m_logger { "Lunar" };
|
||||
|
||||
bool m_running { true };
|
||||
};
|
||||
|
||||
} // namespace Lunar
|
||||
205
src/Logger.cpp
Normal file
205
src/Logger.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
#include "Logger.h"
|
||||
|
||||
#include "Util.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <shlobj.h> // SHGetKnownFolderPath
|
||||
# include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
# include <pwd.h>
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include <pwd.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
# include <zlib.h>
|
||||
#endif
|
||||
|
||||
#define FG_BLUE "\033[34m"
|
||||
#define FG_RED "\033[31m"
|
||||
#define FG_YELLOW "\033[33m"
|
||||
#define FG_GRAY "\033[90m"
|
||||
#define ANSI_RESET "\033[0m"
|
||||
|
||||
std::filesystem::path get_log_path(std::string_view app_name)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
PWSTR path = nullptr;
|
||||
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path);
|
||||
std::wstring wpath(path);
|
||||
CoTaskMemFree(path);
|
||||
return std::filesystem::path(wpath) / app_name / "logs";
|
||||
#elif defined(__APPLE__)
|
||||
const char *home = getenv("HOME");
|
||||
if (!home)
|
||||
home = getpwuid(getuid())->pw_dir;
|
||||
return std::filesystem::path(home) / "Library" / "Logs" / app_name;
|
||||
#else
|
||||
auto const *home { getenv("HOME") };
|
||||
if (!home)
|
||||
home = getpwuid(getuid())->pw_dir;
|
||||
return std::filesystem::path(home) / ".local" / "share" / app_name / "logs";
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
int compress_file(std::filesystem::path const &input_path,
|
||||
std::filesystem::path const &output_path)
|
||||
{
|
||||
size_t const chunk_size = 4096;
|
||||
|
||||
std::ifstream in { input_path, std::ios::binary };
|
||||
if (!in)
|
||||
return 1;
|
||||
|
||||
gzFile out { gzopen(output_path.string().c_str(), "wb") };
|
||||
if (!out)
|
||||
return 1;
|
||||
defer(gzclose(out));
|
||||
|
||||
std::vector<char> buffer(chunk_size);
|
||||
while (in) {
|
||||
in.read(buffer.data(), buffer.size());
|
||||
std::streamsize bytes = in.gcount();
|
||||
if (bytes > 0)
|
||||
gzwrite(out, buffer.data(), static_cast<unsigned int>(bytes));
|
||||
}
|
||||
|
||||
std::filesystem::remove(input_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
Logger::Logger(std::string_view app_name)
|
||||
{
|
||||
#ifndef __EMSCRIPTEN__
|
||||
auto path { get_log_path(app_name) };
|
||||
auto const exists { std::filesystem::exists(path) };
|
||||
if (exists && !std::filesystem::is_directory(path)) {
|
||||
std::filesystem::remove_all(path);
|
||||
}
|
||||
if (!exists) {
|
||||
std::filesystem::create_directories(path);
|
||||
}
|
||||
|
||||
int max { -1 };
|
||||
std::filesystem::directory_iterator iter(path);
|
||||
for (auto const &file : iter) {
|
||||
if (!file.is_regular_file())
|
||||
continue;
|
||||
|
||||
int v;
|
||||
if (std::sscanf(
|
||||
file.path().filename().stem().string().c_str(), "log_%d", &v)
|
||||
!= 1) {
|
||||
continue;
|
||||
}
|
||||
if (v > max)
|
||||
max = v;
|
||||
|
||||
auto ext = file.path().filename().extension().string();
|
||||
if (ext == ".txt") {
|
||||
auto np = file.path();
|
||||
np.replace_extension(ext + ".gz");
|
||||
compress_file(file.path(), np);
|
||||
}
|
||||
}
|
||||
max++;
|
||||
|
||||
path /= std::format("log_{}.txt", max);
|
||||
m_fout = std::ofstream(path, std::ios::app | std::ios::out);
|
||||
#endif // EMSCRIPTEN
|
||||
}
|
||||
|
||||
auto Logger::debug(std::string_view msg) -> void
|
||||
{
|
||||
log(Logger::Level::Debug, msg);
|
||||
}
|
||||
auto Logger::info(std::string_view msg) -> void
|
||||
{
|
||||
log(Logger::Level::Info, msg);
|
||||
}
|
||||
auto Logger::warn(std::string_view msg) -> void
|
||||
{
|
||||
log(Logger::Level::Warning, msg);
|
||||
}
|
||||
auto Logger::err(std::string_view msg) -> void
|
||||
{
|
||||
log(Logger::Level::Error, msg);
|
||||
}
|
||||
|
||||
std::string get_current_time_string()
|
||||
{
|
||||
auto now { std::chrono::system_clock::now() };
|
||||
auto now_c { std::chrono::system_clock::to_time_t(now) };
|
||||
std::tm tm { *std::gmtime(&now_c) };
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%SZ");
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void Logger::log(Level level, std::string_view msg)
|
||||
{
|
||||
auto time_str = get_current_time_string();
|
||||
std::string level_str;
|
||||
switch (level) {
|
||||
case Logger::Level::Debug:
|
||||
level_str = "DEBUG";
|
||||
break;
|
||||
case Logger::Level::Info:
|
||||
level_str = " INFO";
|
||||
break;
|
||||
case Logger::Level::Warning:
|
||||
level_str = " WARN";
|
||||
break;
|
||||
case Logger::Level::Error:
|
||||
level_str = "ERROR";
|
||||
break;
|
||||
default:
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
auto const msg_file { std::format("{} [{}] {}", time_str, level_str, msg) };
|
||||
#ifdef _WIN32
|
||||
auto const msg_stdout = msg_file;
|
||||
#elif __EMSCRIPTEN__
|
||||
auto const msg_stdout = msg_file;
|
||||
#else
|
||||
char const *color;
|
||||
switch (level) {
|
||||
case Logger::Level::Debug:
|
||||
color = FG_GRAY;
|
||||
break;
|
||||
case Logger::Level::Info:
|
||||
color = FG_BLUE;
|
||||
break;
|
||||
case Logger::Level::Warning:
|
||||
color = FG_YELLOW;
|
||||
break;
|
||||
case Logger::Level::Error:
|
||||
color = FG_RED;
|
||||
break;
|
||||
default:
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
auto const msg_stdout { std::format(
|
||||
"{}{} [{}] {}" ANSI_RESET, color, time_str, level_str, msg) };
|
||||
#endif
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
m_fout << msg_file << std::endl;
|
||||
#endif // EMSCRIPTEN
|
||||
std::println(std::cerr, "{}", msg_stdout);
|
||||
}
|
||||
52
src/Logger.h
Normal file
52
src/Logger.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <string_view>
|
||||
|
||||
struct Logger {
|
||||
enum class Level {
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
};
|
||||
|
||||
Logger(std::string_view app_name);
|
||||
|
||||
auto debug(std::string_view msg) -> void;
|
||||
auto info(std::string_view msg) -> void;
|
||||
auto warn(std::string_view msg) -> void;
|
||||
auto err(std::string_view msg) -> void;
|
||||
|
||||
template<typename... Args>
|
||||
auto debug(std::format_string<Args...> fmt, Args &&...args) -> void
|
||||
{
|
||||
log(Level::Debug, std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
auto info(std::format_string<Args...> fmt, Args &&...args) -> void
|
||||
{
|
||||
log(Level::Info, std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
auto warn(std::format_string<Args...> fmt, Args &&...args) -> void
|
||||
{
|
||||
log(Level::Warning, std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
auto err(std::format_string<Args...> fmt, Args &&...args) -> void
|
||||
{
|
||||
log(Level::Error, std::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
auto log(Level level, std::string_view msg) -> void;
|
||||
|
||||
private:
|
||||
#ifndef __EMSCRIPTEN__
|
||||
std::ofstream m_fout;
|
||||
#endif
|
||||
};
|
||||
26
src/Util.h
Normal file
26
src/Util.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
template<typename F> struct privDefer {
|
||||
F f;
|
||||
privDefer(F f)
|
||||
: f(f)
|
||||
{
|
||||
}
|
||||
~privDefer() { f(); }
|
||||
};
|
||||
|
||||
template<typename F> privDefer<F> defer_func(F f) { return privDefer<F>(f); }
|
||||
|
||||
#define DEFER_1(x, y) x##y
|
||||
#define DEFER_2(x, y) DEFER_1(x, y)
|
||||
#define DEFER_3(x) DEFER_2(x, __COUNTER__)
|
||||
#define defer(code) auto DEFER_3(_defer_) = defer_func([&]() { code; })
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define ALIGN(a) __declspec(align(a))
|
||||
#else
|
||||
# define ALIGN(a) __attribute__((aligned(a)))
|
||||
#endif
|
||||
7
src/main.cpp
Normal file
7
src/main.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "src/Application.h"
|
||||
|
||||
auto main() -> int
|
||||
{
|
||||
Lunar::Application app {};
|
||||
app.run();
|
||||
}
|
||||
Reference in New Issue
Block a user