Files
WickedEngine/Editor/main_SDL2.cpp
T

277 lines
8.1 KiB
C++

#include "stdafx.h"
#include "Editor.h"
#include "sdl2.h"
#include <fstream>
#include "icon.h"
#ifdef __linux__
# include <execinfo.h>
# include <csignal>
# include <cstdio>
# include <unistd.h>
#endif
using namespace std;
class EditorWithDevInfo : public Editor
{
public:
const char* GetAdapterName() const
{
return graphicsDevice == nullptr ? "(no device)" : graphicsDevice->GetAdapterName().c_str();
}
const char* GetDriverDescription() const
{
return graphicsDevice == nullptr ? "(no device)" : graphicsDevice->GetDriverDescription().c_str();
}
} editor;
int sdl_loop()
{
while (editor.KeepRunning())
{
editor.Run();
SDL_Event event;
while(SDL_PollEvent(&event)){
bool textinput_action_delete = false;
wi::input::sdlinput::ProcessEvent(event);
switch(event.type){
case SDL_QUIT:
editor.Exit();
break;
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_RESIZED:
// Tells the engine to reload window configuration (size and dpi)
editor.SetWindow(editor.window);
editor.SaveWindowSize();
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
editor.is_window_active = false;
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
editor.is_window_active = true;
editor.HotReload();
break;
default:
break;
}
case SDL_KEYDOWN:
if(event.key.keysym.scancode == SDL_SCANCODE_BACKSPACE
|| event.key.keysym.scancode == SDL_SCANCODE_KP_BACKSPACE) {
wi::gui::TextInputField::DeleteFromInput();
textinput_action_delete = true;
} else if(wi::input::Down(wi::input::KEYBOARD_BUTTON_LCONTROL) || wi::input::Down(wi::input::KEYBOARD_BUTTON_RCONTROL)) {
// HACK: AddInput will check if Ctrl is pressed and
// handle Ctrl-C/Ctrl-V/Ctrl-X for us, but
// wi::input::Down will only be accurate after
// wi::input::Update was run, which will happen next
// frame, which is too late for us. So we just call it
// now and then call AddInput
wi::input::Update(editor.window, editor.canvas);
wi::gui::TextInputField::AddInput('?'); // AddInput actually ignores the argument when Ctrl is pressed
}
break;
case SDL_TEXTINPUT:
if(!textinput_action_delete){
if(event.text.text[0] >= 21){
wi::gui::TextInputField::AddInput(event.text.text[0]);
}
}
break;
case SDL_DROPFILE:
editor.renderComponent.Open(event.drop.file);
editor.is_window_active = true;
break;
default:
break;
}
}
}
return 0;
}
void set_window_icon(SDL_Window *window) {
// these masks are needed to tell SDL_CreateRGBSurface(From)
// to assume the data it gets is byte-wise RGB(A) data
Uint32 rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
int shift = (embedded_image.bytes_per_pixel == 3) ? 8 : 0;
rmask = 0xff000000 >> shift;
gmask = 0x00ff0000 >> shift;
bmask = 0x0000ff00 >> shift;
amask = 0x000000ff >> shift;
#else // little endian, like x86
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = (embedded_image.bytes_per_pixel == 3) ? 0 : 0xff000000;
#endif
SDL_Surface* icon = SDL_CreateRGBSurfaceFrom((void*)embedded_image.pixel_data, embedded_image.width,
embedded_image.height, embedded_image.bytes_per_pixel*8, embedded_image.bytes_per_pixel* embedded_image.width,
rmask, gmask, bmask, amask);
SDL_SetWindowIcon(window, icon);
SDL_FreeSurface(icon);
}
#ifdef __linux__
void crash_handler(int sig)
{
static bool already_handled = false;
void* btbuf[100];
char outbuf[512];
if (already_handled) return;
already_handled = true;
// we can only use functions that are async-signal-safe, see
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04
// we'll use fork() and let the child process do the heavy lifting of writing stuff.
// this is technically not 100% safe, but works well enough in practise.
pid_t child = fork();
if (child < 0) {
const char* msg = "Sorry, a problem occured and crash_handler couldn't fork() to tell you more\n";
write(STDERR_FILENO, msg, strlen(msg));
_exit(128 + sig);
}
if (child > 0) {
_exit(128 + sig);
}
// not we're in a child process and can use printf etc.
size_t size = backtrace(btbuf, 100);
snprintf(
outbuf, sizeof(outbuf),
"Signal: %i (%s)\n"
"Version: %s\n"
"Adapter: %s\n"
"Driver: %s\n"
"Stacktrace:\n",
sig, sigdescr_np(sig),
wi::version::GetVersionString(),
editor.GetAdapterName(),
editor.GetDriverDescription()
);
fprintf(
stderr,
"\e[31m" // red
"The editor just crashed, sorry about that! If you make a bug report, please include the following information:\n\n%s",
outbuf
);
backtrace_symbols_fd(btbuf, size, STDERR_FILENO); // backtrace_symbols does a malloc which could crash, backtrace_symbols_fd does not.
fprintf(stderr, "\e[m"); // back to normal
// finally, we also try to write the crash data to a file
// this might fail because we're in a weird state right now, but there's no harm done if it doesn't work
// Use C interface because some stuff in the c++ stdlib could be calling malloc
const char* filename = "wicked-editor-crash-log.txt";
FILE* logfile = fopen(filename, "w");
if (logfile != nullptr)
{
fputs(outbuf, logfile);
fflush(logfile);
backtrace_symbols_fd(btbuf, size, fileno(logfile));
fputs("\nBacklog:\n", logfile);
wi::backlog::_forEachLogEntry_unsafe([&logfile] (auto&& entry) {
fputs(entry.text.c_str(), logfile);
fflush(logfile);
});
fclose(logfile);
char cwdbuf[200];
fprintf(stderr, "\e[1mcrash log written to %s/%s\e[m\n", getcwd(cwdbuf, sizeof(cwdbuf)), filename);
}
_exit(0);
}
#endif
int main(int argc, char *argv[])
{
#ifdef __linux__
// dummy backtrace() call to force libgcc to be loaded ahead of time.
// Otherwise it might lead to malloc calls in the crash_handler, which we want to avoid
void* dummy[1];
backtrace(dummy, 1);
for (int sig : std::array{SIGABRT, SIGBUS, SIGILL, SIGFPE, SIGSEGV})
{
signal(sig, crash_handler);
}
#endif
wi::arguments::Parse(argc, argv);
sdl2::sdlsystem_ptr_t system = sdl2::make_sdlsystem(SDL_INIT_EVERYTHING | SDL_INIT_EVENTS);
if (*system) {
wilog_error("Error creating SDL2 system");
}
int width = 1920;
int height = 1080;
bool fullscreen = false;
wi::Timer timer;
if (editor.config.Open("config.ini"))
{
if (editor.config.Has("width"))
{
width = editor.config.GetInt("width");
height = editor.config.GetInt("height");
}
fullscreen = editor.config.GetBool("fullscreen");
editor.allow_hdr = editor.config.GetBool("allow_hdr");
wilog("config.ini loaded in %.2f milliseconds\n", (float)timer.elapsed_milliseconds());
}
width = std::max(100, width);
height = std::max(100, height);
sdl2::window_ptr_t window = sdl2::make_window(
wi::helper::StringRemoveTrailingWhitespaces(exe_customization.name_padded).c_str(),
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
width, height,
SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
if (!window) {
wilog_error("Error creating window");
}
set_window_icon(window.get());
if (fullscreen)
{
//SDL_SetWindowFullscreen(window.get(), SDL_TRUE);
//SDL_SetWindowFullscreen(window.get(), SDL_WINDOW_FULLSCREEN);
SDL_SetWindowFullscreen(window.get(), SDL_WINDOW_FULLSCREEN_DESKTOP);
}
editor.SetWindow(window.get());
int ret = sdl_loop();
wi::jobsystem::ShutDown();
return ret;
}