#include "stdafx.h" #include "Editor.h" #include "sdl2.h" #include #include "icon.h" #ifdef __linux__ # include # include # include # include #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; }