Rewrite the crash reporter code
This commit is contained in:
parent
c08a47c920
commit
096b92f1b4
22 changed files with 1305 additions and 2475 deletions
|
|
@ -33,6 +33,9 @@ set(ZWIDGET_SOURCES
|
|||
src/widgets/listview/listview.cpp
|
||||
src/widgets/tabwidget/tabwidget.cpp
|
||||
src/window/window.cpp
|
||||
src/systemdialogs/folder_browse_dialog.cpp
|
||||
src/systemdialogs/open_file_dialog.cpp
|
||||
src/systemdialogs/save_file_dialog.cpp
|
||||
)
|
||||
|
||||
set(ZWIDGET_INCLUDES
|
||||
|
|
@ -63,6 +66,9 @@ set(ZWIDGET_INCLUDES
|
|||
include/zwidget/widgets/listview/listview.h
|
||||
include/zwidget/widgets/tabwidget/tabwidget.h
|
||||
include/zwidget/window/window.h
|
||||
include/zwidget/systemdialogs/folder_browse_dialog.h
|
||||
include/zwidget/systemdialogs/open_file_dialog.h
|
||||
include/zwidget/systemdialogs/save_file_dialog.h
|
||||
)
|
||||
|
||||
set(ZWIDGET_WIN32_SOURCES
|
||||
|
|
@ -97,6 +103,7 @@ source_group("src\\widgets\\checkboxlabel" REGULAR_EXPRESSION "${CMAKE_CURRENT_S
|
|||
source_group("src\\widgets\\listview" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/widgets/listview/.+")
|
||||
source_group("src\\widgets\\tabwidget" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/widgets/tabwidget/.+")
|
||||
source_group("src\\window" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/window/.+")
|
||||
source_group("src\\systemdialogs" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/systemdialogs/.+")
|
||||
source_group("include" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/.+")
|
||||
source_group("include\\core" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/core/.+")
|
||||
source_group("include\\widgets" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/widgets/.+")
|
||||
|
|
@ -116,6 +123,7 @@ source_group("include\\widgets\\tabwidget" REGULAR_EXPRESSION "${CMAKE_CURRENT_S
|
|||
source_group("include\\window" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/window/.+")
|
||||
source_group("include\\window\\win32" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/window/win32/.+")
|
||||
source_group("include\\window\\sdl2" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/window/sdl2/.+")
|
||||
source_group("include\\systemdialogs" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/systemdialogs/.+")
|
||||
|
||||
include_directories(include include/zwidget src)
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,8 @@ public:
|
|||
|
||||
static Size GetScreenSize();
|
||||
|
||||
void* GetNativeHandle();
|
||||
|
||||
protected:
|
||||
virtual void OnPaintFrame(Canvas* canvas);
|
||||
virtual void OnPaint(Canvas* canvas) { }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class Widget;
|
||||
|
||||
/// \brief Displays the system folder browsing dialog
|
||||
class BrowseFolderDialog
|
||||
{
|
||||
public:
|
||||
/// \brief Constructs an browse folder dialog.
|
||||
static std::unique_ptr<BrowseFolderDialog> Create(Widget*owner);
|
||||
|
||||
virtual ~BrowseFolderDialog() = default;
|
||||
|
||||
/// \brief Get the full path of the directory selected.
|
||||
virtual std::string SelectedPath() const = 0;
|
||||
|
||||
/// \brief Sets the initial directory that is displayed.
|
||||
virtual void SetInitialDirectory(const std::string& path) = 0;
|
||||
|
||||
/// \brief Sets the text that appears in the title bar.
|
||||
virtual void SetTitle(const std::string& title) = 0;
|
||||
|
||||
/// \brief Shows the file dialog.
|
||||
/// \return true if the user clicks the OK button of the dialog that is displayed, false otherwise.
|
||||
virtual bool Show() = 0;
|
||||
};
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Widget;
|
||||
|
||||
/// \brief Displays the system open file dialog.
|
||||
class OpenFileDialog
|
||||
{
|
||||
public:
|
||||
/// \brief Constructs an open file dialog.
|
||||
static std::unique_ptr<OpenFileDialog> Create(Widget* owner);
|
||||
|
||||
virtual ~OpenFileDialog() = default;
|
||||
|
||||
/// \brief Get the full path of the file selected.
|
||||
///
|
||||
/// If multiple files are selected, this returns the first file.
|
||||
virtual std::string Filename() const = 0;
|
||||
|
||||
/// \brief Gets an array that contains one file name for each selected file.
|
||||
virtual std::vector<std::string> Filenames() const = 0;
|
||||
|
||||
/// \brief Sets if multiple files can be selected or not.
|
||||
/// \param multiselect = When true, multiple items can be selected.
|
||||
virtual void SetMultiSelect(bool multiselect) = 0;
|
||||
|
||||
/// \brief Sets a string containing the full path of the file selected.
|
||||
virtual void SetFilename(const std::string &filename) = 0;
|
||||
|
||||
/// \brief Sets the default extension to use.
|
||||
virtual void SetDefaultExtension(const std::string& extension) = 0;
|
||||
|
||||
/// \brief Add a filter that determines what types of files are displayed.
|
||||
virtual void AddFilter(const std::string &filter_description, const std::string &filter_extension) = 0;
|
||||
|
||||
/// \brief Clears all filters.
|
||||
virtual void ClearFilters() = 0;
|
||||
|
||||
/// \brief Sets a default filter, on a 0-based index.
|
||||
virtual void SetFilterIndex(int filter_index) = 0;
|
||||
|
||||
/// \brief Sets the initial directory that is displayed.
|
||||
virtual void SetInitialDirectory(const std::string &path) = 0;
|
||||
|
||||
/// \brief Sets the text that appears in the title bar.
|
||||
virtual void SetTitle(const std::string &title) = 0;
|
||||
|
||||
/// \brief Shows the file dialog.
|
||||
/// \return true if the user clicks the OK button of the dialog that is displayed, false otherwise.
|
||||
virtual bool Show() = 0;
|
||||
};
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Widget;
|
||||
|
||||
/// \brief Displays the system save file dialog.
|
||||
class SaveFileDialog
|
||||
{
|
||||
public:
|
||||
/// \brief Constructs a save file dialog.
|
||||
static std::unique_ptr<SaveFileDialog> Create(Widget *owner);
|
||||
|
||||
virtual ~SaveFileDialog() = default;
|
||||
|
||||
/// \brief Get the full path of the file selected.
|
||||
virtual std::string Filename() const = 0;
|
||||
|
||||
/// \brief Sets a string containing the full path of the file selected.
|
||||
virtual void SetFilename(const std::string &filename) = 0;
|
||||
|
||||
/// \brief Sets the default extension to use.
|
||||
virtual void SetDefaultExtension(const std::string& extension) = 0;
|
||||
|
||||
/// \brief Add a filter that determines what types of files are displayed.
|
||||
virtual void AddFilter(const std::string &filter_description, const std::string &filter_extension) = 0;
|
||||
|
||||
/// \brief Clears all filters.
|
||||
virtual void ClearFilters() = 0;
|
||||
|
||||
/// \brief Sets a default filter, on a 0-based index.
|
||||
virtual void SetFilterIndex(int filter_index) = 0;
|
||||
|
||||
/// \brief Sets the initial directory that is displayed.
|
||||
virtual void SetInitialDirectory(const std::string &path) = 0;
|
||||
|
||||
/// \brief Sets the text that appears in the title bar.
|
||||
virtual void SetTitle(const std::string &title) = 0;
|
||||
|
||||
/// \brief Shows the file dialog.
|
||||
/// \return true if the user clicks the OK button of the dialog that is displayed, false otherwise.
|
||||
virtual bool Show() = 0;
|
||||
};
|
||||
|
|
@ -165,4 +165,6 @@ public:
|
|||
|
||||
virtual std::string GetClipboardText() = 0;
|
||||
virtual void SetClipboardText(const std::string& text) = 0;
|
||||
|
||||
virtual void* GetNativeHandle() = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -733,6 +733,12 @@ Size Widget::GetScreenSize()
|
|||
return DisplayWindow::GetScreenSize();
|
||||
}
|
||||
|
||||
void* Widget::GetNativeHandle()
|
||||
{
|
||||
Widget* w = Window();
|
||||
return w ? w->DispWindow->GetNativeHandle() : nullptr;
|
||||
}
|
||||
|
||||
void Widget::SetStyleClass(const std::string& themeClass)
|
||||
{
|
||||
if (StyleClass != themeClass)
|
||||
|
|
|
|||
188
libraries/ZWidget/src/systemdialogs/folder_browse_dialog.cpp
Normal file
188
libraries/ZWidget/src/systemdialogs/folder_browse_dialog.cpp
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
|
||||
#include "systemdialogs/folder_browse_dialog.h"
|
||||
#include "core/widget.h"
|
||||
#include "window/window.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <Shlobj.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
static std::string from_utf16(const std::wstring& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0, nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("WideCharToMultiByte failed");
|
||||
std::string result;
|
||||
result.resize(needed);
|
||||
needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size(), nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("WideCharToMultiByte failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::wstring to_utf16(const std::string& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
std::wstring result;
|
||||
result.resize(needed);
|
||||
needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size());
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class ComPtr
|
||||
{
|
||||
public:
|
||||
ComPtr() { Ptr = nullptr; }
|
||||
ComPtr(const ComPtr& other) { Ptr = other.Ptr; if (Ptr) Ptr->AddRef(); }
|
||||
ComPtr(ComPtr&& move) { Ptr = move.Ptr; move.Ptr = nullptr; }
|
||||
~ComPtr() { reset(); }
|
||||
ComPtr& operator=(const ComPtr& other) { if (this != &other) { if (Ptr) Ptr->Release(); Ptr = other.Ptr; if (Ptr) Ptr->AddRef(); } return *this; }
|
||||
void reset() { if (Ptr) Ptr->Release(); Ptr = nullptr; }
|
||||
T* get() { return Ptr; }
|
||||
static IID GetIID() { return __uuidof(T); }
|
||||
void** InitPtr() { return (void**)TypedInitPtr(); }
|
||||
T** TypedInitPtr() { reset(); return &Ptr; }
|
||||
operator T* () const { return Ptr; }
|
||||
T* operator ->() const { return Ptr; }
|
||||
T* Ptr;
|
||||
};
|
||||
}
|
||||
|
||||
class BrowseFolderDialogImpl : public BrowseFolderDialog
|
||||
{
|
||||
public:
|
||||
BrowseFolderDialogImpl(Widget *owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
Widget *owner = nullptr;
|
||||
|
||||
std::string selected_path;
|
||||
std::string initial_directory;
|
||||
std::string title;
|
||||
|
||||
bool Show() override
|
||||
{
|
||||
std::wstring title16 = to_utf16(title);
|
||||
std::wstring initial_directory16 = to_utf16(initial_directory);
|
||||
|
||||
HRESULT result;
|
||||
ComPtr<IFileOpenDialog> open_dialog;
|
||||
|
||||
result = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, open_dialog.GetIID(), open_dialog.InitPtr());
|
||||
throw_if_failed(result, "CoCreateInstance(FileOpenDialog) failed");
|
||||
|
||||
result = open_dialog->SetTitle(title16.c_str());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetTitle failed");
|
||||
|
||||
result = open_dialog->SetOptions(FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetOptions((FOS_PICKFOLDERS) failed");
|
||||
|
||||
if (initial_directory16.length() > 0)
|
||||
{
|
||||
LPITEMIDLIST item_id_list = nullptr;
|
||||
SFGAOF flags = 0;
|
||||
result = SHParseDisplayName(initial_directory16.c_str(), nullptr, &item_id_list, SFGAO_FILESYSTEM, &flags);
|
||||
throw_if_failed(result, "SHParseDisplayName failed");
|
||||
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateShellItem(nullptr, nullptr, item_id_list, folder_item.TypedInitPtr());
|
||||
ILFree(item_id_list);
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
|
||||
/* This code requires Windows Vista or newer:
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateItemFromParsingName(initial_directory16.c_str(), nullptr, folder_item.GetIID(), folder_item.InitPtr());
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
*/
|
||||
|
||||
if (folder_item)
|
||||
{
|
||||
result = open_dialog->SetFolder(folder_item);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFolder failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (owner && owner->Window())
|
||||
result = open_dialog->Show((HWND)owner->Window()->GetNativeHandle());
|
||||
else
|
||||
result = open_dialog->Show(0);
|
||||
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
ComPtr<IShellItem> chosen_folder;
|
||||
result = open_dialog->GetResult(chosen_folder.TypedInitPtr());
|
||||
throw_if_failed(result, "IFileOpenDialog.GetResult failed");
|
||||
|
||||
WCHAR *buffer = nullptr;
|
||||
result = chosen_folder->GetDisplayName(SIGDN_FILESYSPATH, &buffer);
|
||||
throw_if_failed(result, "IShellItem.GetDisplayName failed");
|
||||
|
||||
std::wstring output_directory16;
|
||||
if (buffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
output_directory16 = buffer;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CoTaskMemFree(buffer);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
CoTaskMemFree(buffer);
|
||||
selected_path = from_utf16(output_directory16);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string BrowseFolderDialog::SelectedPath() const override
|
||||
{
|
||||
return selected_path;
|
||||
}
|
||||
|
||||
void BrowseFolderDialog::SetInitialDirectory(const std::string& path) override
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void BrowseFolderDialog::SetTitle(const std::string& newtitle) override
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void throw_if_failed(HRESULT result, const std::string &error)
|
||||
{
|
||||
if (FAILED(result))
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<BrowseFolderDialog> BrowseFolderDialog::Create(Widget* owner)
|
||||
{
|
||||
return std::make_unique<BrowseFolderDialogImpl>(owner);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::unique_ptr<BrowseFolderDialog> BrowseFolderDialog::Create(Widget* owner)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
286
libraries/ZWidget/src/systemdialogs/open_file_dialog.cpp
Normal file
286
libraries/ZWidget/src/systemdialogs/open_file_dialog.cpp
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
|
||||
#include "systemdialogs/open_file_dialog.h"
|
||||
#include "core/widget.h"
|
||||
#include "window/window.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <Shlobj.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
static std::string from_utf16(const std::wstring& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0, nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("WideCharToMultiByte failed");
|
||||
std::string result;
|
||||
result.resize(needed);
|
||||
needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size(), nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("WideCharToMultiByte failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::wstring to_utf16(const std::string& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
std::wstring result;
|
||||
result.resize(needed);
|
||||
needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size());
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class ComPtr
|
||||
{
|
||||
public:
|
||||
ComPtr() { Ptr = nullptr; }
|
||||
ComPtr(const ComPtr& other) { Ptr = other.Ptr; if (Ptr) Ptr->AddRef(); }
|
||||
ComPtr(ComPtr&& move) { Ptr = move.Ptr; move.Ptr = nullptr; }
|
||||
~ComPtr() { reset(); }
|
||||
ComPtr& operator=(const ComPtr& other) { if (this != &other) { if (Ptr) Ptr->Release(); Ptr = other.Ptr; if (Ptr) Ptr->AddRef(); } return *this; }
|
||||
void reset() { if (Ptr) Ptr->Release(); Ptr = nullptr; }
|
||||
T* get() { return Ptr; }
|
||||
static IID GetIID() { return __uuidof(T); }
|
||||
void** InitPtr() { return (void**)TypedInitPtr(); }
|
||||
T** TypedInitPtr() { reset(); return &Ptr; }
|
||||
operator T* () const { return Ptr; }
|
||||
T* operator ->() const { return Ptr; }
|
||||
T* Ptr;
|
||||
};
|
||||
}
|
||||
|
||||
class OpenFileDialogImpl : public OpenFileDialog
|
||||
{
|
||||
public:
|
||||
OpenFileDialogImpl(Widget* owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
Widget* owner = nullptr;
|
||||
|
||||
std::string initial_directory;
|
||||
std::string initial_filename;
|
||||
std::string title;
|
||||
std::vector<std::string> filenames;
|
||||
bool multi_select = false;
|
||||
|
||||
struct Filter
|
||||
{
|
||||
std::string description;
|
||||
std::string extension;
|
||||
};
|
||||
std::vector<Filter> filters;
|
||||
int filterindex = 0;
|
||||
std::string defaultext;
|
||||
|
||||
bool Show() override
|
||||
{
|
||||
std::wstring title16 = to_utf16(title);
|
||||
std::wstring initial_directory16 = to_utf16(initial_directory);
|
||||
|
||||
HRESULT result;
|
||||
ComPtr<IFileOpenDialog> open_dialog;
|
||||
|
||||
result = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, open_dialog.GetIID(), open_dialog.InitPtr());
|
||||
throw_if_failed(result, "CoCreateInstance(FileOpenDialog) failed");
|
||||
|
||||
result = open_dialog->SetTitle(title16.c_str());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetTitle failed");
|
||||
|
||||
if (!initial_filename.empty())
|
||||
{
|
||||
result = open_dialog->SetFileName(to_utf16(initial_filename).c_str());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFileName failed");
|
||||
}
|
||||
|
||||
FILEOPENDIALOGOPTIONS options = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST;
|
||||
if (multi_select)
|
||||
options |= FOS_ALLOWMULTISELECT;
|
||||
result = open_dialog->SetOptions(options);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetOptions() failed");
|
||||
|
||||
if (!filters.empty())
|
||||
{
|
||||
std::vector<COMDLG_FILTERSPEC> filterspecs(filters.size());
|
||||
std::vector<std::wstring> descriptions(filters.size());
|
||||
std::vector<std::wstring> extensions(filters.size());
|
||||
for (size_t i = 0; i < filters.size(); i++)
|
||||
{
|
||||
descriptions[i] = to_utf16(filters[i].description);
|
||||
extensions[i] = to_utf16(filters[i].extension);
|
||||
COMDLG_FILTERSPEC& spec = filterspecs[i];
|
||||
spec.pszName = descriptions[i].c_str();
|
||||
spec.pszSpec = extensions[i].c_str();
|
||||
}
|
||||
result = open_dialog->SetFileTypes((UINT)filterspecs.size(), filterspecs.data());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFileTypes() failed");
|
||||
|
||||
if ((size_t)filterindex < filters.size())
|
||||
{
|
||||
result = open_dialog->SetFileTypeIndex(filterindex);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFileTypeIndex() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (!defaultext.empty())
|
||||
{
|
||||
result = open_dialog->SetDefaultExtension(to_utf16(defaultext).c_str());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetDefaultExtension() failed");
|
||||
}
|
||||
|
||||
if (initial_directory16.length() > 0)
|
||||
{
|
||||
LPITEMIDLIST item_id_list = nullptr;
|
||||
SFGAOF flags = 0;
|
||||
result = SHParseDisplayName(initial_directory16.c_str(), nullptr, &item_id_list, SFGAO_FILESYSTEM, &flags);
|
||||
throw_if_failed(result, "SHParseDisplayName failed");
|
||||
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateShellItem(nullptr, nullptr, item_id_list, folder_item.TypedInitPtr());
|
||||
ILFree(item_id_list);
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
|
||||
/* This code requires Windows Vista or newer:
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateItemFromParsingName(initial_directory16.c_str(), nullptr, folder_item.GetIID(), folder_item.InitPtr());
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
*/
|
||||
|
||||
if (folder_item)
|
||||
{
|
||||
result = open_dialog->SetFolder(folder_item);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFolder failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (owner && owner->Window())
|
||||
result = open_dialog->Show((HWND)owner->Window()->GetNativeHandle());
|
||||
else
|
||||
result = open_dialog->Show(0);
|
||||
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
ComPtr<IShellItemArray> items;
|
||||
result = open_dialog->GetSelectedItems(items.TypedInitPtr());
|
||||
throw_if_failed(result, "IFileOpenDialog.GetSelectedItems failed");
|
||||
|
||||
DWORD num_items = 0;
|
||||
result = items->GetCount(&num_items);
|
||||
throw_if_failed(result, "IShellItemArray.GetCount failed");
|
||||
|
||||
for (DWORD i = 0; i < num_items; i++)
|
||||
{
|
||||
ComPtr<IShellItem> item;
|
||||
result = items->GetItemAt(i, item.TypedInitPtr());
|
||||
throw_if_failed(result, "IShellItemArray.GetItemAt failed");
|
||||
|
||||
WCHAR* buffer = nullptr;
|
||||
result = item->GetDisplayName(SIGDN_FILESYSPATH, &buffer);
|
||||
throw_if_failed(result, "IShellItem.GetDisplayName failed");
|
||||
|
||||
std::wstring output16;
|
||||
if (buffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
output16 = buffer;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CoTaskMemFree(buffer);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
CoTaskMemFree(buffer);
|
||||
filenames.push_back(from_utf16(output16));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Filename() const override
|
||||
{
|
||||
return !filenames.empty() ? filenames.front() : std::string();
|
||||
}
|
||||
|
||||
std::vector<std::string> Filenames() const override
|
||||
{
|
||||
return filenames;
|
||||
}
|
||||
|
||||
void SetMultiSelect(bool new_multi_select) override
|
||||
{
|
||||
multi_select = new_multi_select;
|
||||
}
|
||||
|
||||
void SetFilename(const std::string& filename) override
|
||||
{
|
||||
initial_filename = filename;
|
||||
}
|
||||
|
||||
void AddFilter(const std::string& filter_description, const std::string& filter_extension) override
|
||||
{
|
||||
Filter f;
|
||||
f.description = filter_description;
|
||||
f.extension = filter_extension;
|
||||
filters.push_back(std::move(f));
|
||||
}
|
||||
|
||||
void ClearFilters() override
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
void SetFilterIndex(int filter_index) override
|
||||
{
|
||||
filterindex = filter_index;
|
||||
}
|
||||
|
||||
void SetInitialDirectory(const std::string& path) override
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void SetTitle(const std::string& newtitle) override
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void SetDefaultExtension(const std::string& extension) override
|
||||
{
|
||||
defaultext = extension;
|
||||
}
|
||||
|
||||
void throw_if_failed(HRESULT result, const std::string& error)
|
||||
{
|
||||
if (FAILED(result))
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<OpenFileDialog> OpenFileDialog::Create(Widget* owner)
|
||||
{
|
||||
return std::make_unique<OpenFileDialogImpl>(owner);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::unique_ptr<OpenFileDialog> OpenFileDialog::Create(Widget* owner)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
263
libraries/ZWidget/src/systemdialogs/save_file_dialog.cpp
Normal file
263
libraries/ZWidget/src/systemdialogs/save_file_dialog.cpp
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
|
||||
#include "systemdialogs/save_file_dialog.h"
|
||||
#include "core/widget.h"
|
||||
#include "window/window.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <Shlobj.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
static std::string from_utf16(const std::wstring& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0, nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("WideCharToMultiByte failed");
|
||||
std::string result;
|
||||
result.resize(needed);
|
||||
needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size(), nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("WideCharToMultiByte failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::wstring to_utf16(const std::string& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
std::wstring result;
|
||||
result.resize(needed);
|
||||
needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size());
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class ComPtr
|
||||
{
|
||||
public:
|
||||
ComPtr() { Ptr = nullptr; }
|
||||
ComPtr(const ComPtr& other) { Ptr = other.Ptr; if (Ptr) Ptr->AddRef(); }
|
||||
ComPtr(ComPtr&& move) { Ptr = move.Ptr; move.Ptr = nullptr; }
|
||||
~ComPtr() { reset(); }
|
||||
ComPtr& operator=(const ComPtr& other) { if (this != &other) { if (Ptr) Ptr->Release(); Ptr = other.Ptr; if (Ptr) Ptr->AddRef(); } return *this; }
|
||||
void reset() { if (Ptr) Ptr->Release(); Ptr = nullptr; }
|
||||
T* get() { return Ptr; }
|
||||
static IID GetIID() { return __uuidof(T); }
|
||||
void** InitPtr() { return (void**)TypedInitPtr(); }
|
||||
T** TypedInitPtr() { reset(); return &Ptr; }
|
||||
operator T* () const { return Ptr; }
|
||||
T* operator ->() const { return Ptr; }
|
||||
T* Ptr;
|
||||
};
|
||||
}
|
||||
|
||||
class SaveFileDialogImpl : public SaveFileDialog
|
||||
{
|
||||
public:
|
||||
SaveFileDialogImpl(Widget* owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
Widget* owner = nullptr;
|
||||
|
||||
std::string filename;
|
||||
std::string initial_directory;
|
||||
std::string initial_filename;
|
||||
std::string title;
|
||||
std::vector<std::string> filenames;
|
||||
|
||||
struct Filter
|
||||
{
|
||||
std::string description;
|
||||
std::string extension;
|
||||
};
|
||||
std::vector<Filter> filters;
|
||||
int filterindex = 0;
|
||||
std::string defaultext;
|
||||
|
||||
bool Show() override
|
||||
{
|
||||
std::wstring title16 = to_utf16(title);
|
||||
std::wstring initial_directory16 = to_utf16(initial_directory);
|
||||
|
||||
HRESULT result;
|
||||
ComPtr<IFileSaveDialog> save_dialog;
|
||||
|
||||
result = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_ALL, save_dialog.GetIID(), save_dialog.InitPtr());
|
||||
throw_if_failed(result, "CoCreateInstance(FileSaveDialog) failed");
|
||||
|
||||
result = save_dialog->SetTitle(title16.c_str());
|
||||
throw_if_failed(result, "IFileSaveDialog.SetTitle failed");
|
||||
|
||||
if (!initial_filename.empty())
|
||||
{
|
||||
result = save_dialog->SetFileName(to_utf16(initial_filename).c_str());
|
||||
throw_if_failed(result, "IFileSaveDialog.SetFileName failed");
|
||||
}
|
||||
|
||||
FILEOPENDIALOGOPTIONS options = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST;
|
||||
result = save_dialog->SetOptions(options);
|
||||
throw_if_failed(result, "IFileSaveDialog.SetOptions() failed");
|
||||
|
||||
if (!filters.empty())
|
||||
{
|
||||
std::vector<COMDLG_FILTERSPEC> filterspecs(filters.size());
|
||||
std::vector<std::wstring> descriptions(filters.size());
|
||||
std::vector<std::wstring> extensions(filters.size());
|
||||
for (size_t i = 0; i < filters.size(); i++)
|
||||
{
|
||||
descriptions[i] = to_utf16(filters[i].description);
|
||||
extensions[i] = to_utf16(filters[i].extension);
|
||||
COMDLG_FILTERSPEC& spec = filterspecs[i];
|
||||
spec.pszName = descriptions[i].c_str();
|
||||
spec.pszSpec = extensions[i].c_str();
|
||||
}
|
||||
result = save_dialog->SetFileTypes((UINT)filterspecs.size(), filterspecs.data());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFileTypes() failed");
|
||||
|
||||
if ((size_t)filterindex < filters.size())
|
||||
{
|
||||
result = save_dialog->SetFileTypeIndex(filterindex);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFileTypeIndex() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (!defaultext.empty())
|
||||
{
|
||||
result = save_dialog->SetDefaultExtension(to_utf16(defaultext).c_str());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetDefaultExtension() failed");
|
||||
}
|
||||
|
||||
if (initial_directory16.length() > 0)
|
||||
{
|
||||
LPITEMIDLIST item_id_list = nullptr;
|
||||
SFGAOF flags = 0;
|
||||
result = SHParseDisplayName(initial_directory16.c_str(), nullptr, &item_id_list, SFGAO_FILESYSTEM, &flags);
|
||||
throw_if_failed(result, "SHParseDisplayName failed");
|
||||
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateShellItem(nullptr, nullptr, item_id_list, folder_item.TypedInitPtr());
|
||||
ILFree(item_id_list);
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
|
||||
/* This code requires Windows Vista or newer:
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateItemFromParsingName(initial_directory16.c_str(), nullptr, folder_item.GetIID(), folder_item.InitPtr());
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
*/
|
||||
|
||||
if (folder_item)
|
||||
{
|
||||
result = save_dialog->SetFolder(folder_item);
|
||||
throw_if_failed(result, "IFileSaveDialog.SetFolder failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (owner && owner->Window())
|
||||
result = save_dialog->Show((HWND)owner->Window()->GetNativeHandle());
|
||||
else
|
||||
result = save_dialog->Show(0);
|
||||
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
ComPtr<IShellItem> chosen_folder;
|
||||
result = save_dialog->GetResult(chosen_folder.TypedInitPtr());
|
||||
throw_if_failed(result, "IFileSaveDialog.GetResult failed");
|
||||
|
||||
WCHAR* buffer = nullptr;
|
||||
result = chosen_folder->GetDisplayName(SIGDN_FILESYSPATH, &buffer);
|
||||
throw_if_failed(result, "IShellItem.GetDisplayName failed");
|
||||
|
||||
std::wstring output16;
|
||||
if (buffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
output16 = buffer;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CoTaskMemFree(buffer);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
CoTaskMemFree(buffer);
|
||||
filename = from_utf16(output16);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Filename() const override
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
|
||||
void SetFilename(const std::string& filename) override
|
||||
{
|
||||
initial_filename = filename;
|
||||
}
|
||||
|
||||
void AddFilter(const std::string& filter_description, const std::string& filter_extension) override
|
||||
{
|
||||
Filter f;
|
||||
f.description = filter_description;
|
||||
f.extension = filter_extension;
|
||||
filters.push_back(std::move(f));
|
||||
}
|
||||
|
||||
void ClearFilters() override
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
void SetFilterIndex(int filter_index) override
|
||||
{
|
||||
filterindex = filter_index;
|
||||
}
|
||||
|
||||
void SetInitialDirectory(const std::string& path) override
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void SetTitle(const std::string& newtitle) override
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void SetDefaultExtension(const std::string& extension) override
|
||||
{
|
||||
defaultext = extension;
|
||||
}
|
||||
|
||||
void throw_if_failed(HRESULT result, const std::string& error)
|
||||
{
|
||||
if (FAILED(result))
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<SaveFileDialog> SaveFileDialog::Create(Widget* owner)
|
||||
{
|
||||
return std::make_unique<SaveFileDialogImpl>(owner);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::unique_ptr<SaveFileDialog> SaveFileDialog::Create(Widget* owner)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include "widgets/lineedit/lineedit.h"
|
||||
#include "core/utf8reader.h"
|
||||
#include "core/colorf.h"
|
||||
|
|
|
|||
|
|
@ -286,11 +286,10 @@ void SDL2DisplayWindow::RunLoop()
|
|||
|
||||
while (!ExitRunLoop)
|
||||
{
|
||||
SDL_Event event;
|
||||
SDL_Event event = {};
|
||||
int result = SDL_WaitEvent(&event);
|
||||
if (result == 0)
|
||||
throw std::runtime_error(std::string("SDL_WaitEvent failed:") + SDL_GetError());
|
||||
DispatchEvent(event);
|
||||
if (result == 1)
|
||||
DispatchEvent(event); // Silently ignore if it fails and pray it doesn't busy loop, because SDL and Linux utterly sucks!
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ public:
|
|||
std::string GetClipboardText() override;
|
||||
void SetClipboardText(const std::string& text) override;
|
||||
|
||||
void* GetNativeHandle() override { return WindowHandle; }
|
||||
|
||||
static void DispatchEvent(const SDL_Event& event);
|
||||
static SDL2DisplayWindow* FindEventWindow(const SDL_Event& event);
|
||||
|
||||
|
|
|
|||
|
|
@ -128,8 +128,6 @@ void Win32DisplayWindow::SetWindowFrame(const Rect& box)
|
|||
|
||||
void Win32DisplayWindow::SetClientFrame(const Rect& box)
|
||||
{
|
||||
// This function is currently unused but needs to be disabled because it contains Windows API calls that were only added in Windows 10.
|
||||
#if 0
|
||||
double dpiscale = GetDpiScale();
|
||||
|
||||
RECT rect = {};
|
||||
|
|
@ -143,7 +141,6 @@ void Win32DisplayWindow::SetClientFrame(const Rect& box)
|
|||
AdjustWindowRectExForDpi(&rect, style, FALSE, exstyle, GetDpiForWindow(WindowHandle));
|
||||
|
||||
SetWindowPos(WindowHandle, nullptr, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOZORDER);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::Show()
|
||||
|
|
@ -271,22 +268,9 @@ int Win32DisplayWindow::GetPixelHeight() const
|
|||
return box.bottom;
|
||||
}
|
||||
|
||||
typedef UINT(WINAPI* GetDpiForWindow_t)(HWND);
|
||||
double Win32DisplayWindow::GetDpiScale() const
|
||||
{
|
||||
static GetDpiForWindow_t pGetDpiForWindow = nullptr;
|
||||
static bool done = false;
|
||||
if (!done)
|
||||
{
|
||||
HMODULE hMod = GetModuleHandleA("User32.dll");
|
||||
if (hMod != nullptr) pGetDpiForWindow = reinterpret_cast<GetDpiForWindow_t>(GetProcAddress(hMod, "GetDpiForWindow"));
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (pGetDpiForWindow)
|
||||
return pGetDpiForWindow(WindowHandle) / 96.0;
|
||||
else
|
||||
return 1.0;
|
||||
return GetDpiForWindow(WindowHandle) / 96.0;
|
||||
}
|
||||
|
||||
std::string Win32DisplayWindow::GetClipboardText()
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ public:
|
|||
|
||||
Point GetLParamPos(LPARAM lparam) const;
|
||||
|
||||
void* GetNativeHandle() override { return reinterpret_cast<void*>(WindowHandle); }
|
||||
|
||||
static void ProcessEvents();
|
||||
static void RunLoop();
|
||||
static void ExitLoop();
|
||||
|
|
|
|||
|
|
@ -408,19 +408,14 @@ set( PLAT_WIN32_SOURCES
|
|||
common/platform/win32/st_start.cpp
|
||||
common/platform/win32/base_sysfb.cpp
|
||||
common/platform/win32/win32basevideo.cpp
|
||||
common/platform/win32/i_crash.cpp
|
||||
)
|
||||
|
||||
if (HAVE_VULKAN)
|
||||
list (APPEND PLAT_WIN32_SOURCES common/platform/win32/win32vulkanvideo.cpp )
|
||||
endif()
|
||||
|
||||
# todo: implement an actual crash catcher for ARM
|
||||
# for now this is purely experimental
|
||||
if (NOT ${TARGET_ARCHITECTURE} MATCHES "arm" )
|
||||
set (PLAT_WIN32_SOURCES ${PLAT_WIN32_SOURCES} common/platform/win32/i_crash.cpp )
|
||||
endif()
|
||||
if (MSVC AND ${TARGET_ARCHITECTURE} MATCHES "arm")
|
||||
set (PLAT_WIN32_SOURCES ${PLAT_WIN32_SOURCES} common/platform/win32/i_crash_arm.cpp )
|
||||
add_definitions( -DNO_SSE -D__ARM__ -DRAPIDJSON_ENDIAN=RAPIDJSON_LITTLEENDIAN)
|
||||
endif()
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +0,0 @@
|
|||
// Licensed to the Public Domain by Rachael Alexandersion (c) 2021
|
||||
// This file is nothing more than a stub. Please replace with a proper copyright notice if this function is implemented for ARM. Or just use the regular i_crash.cpp
|
||||
|
||||
void DisplayCrashLog()
|
||||
{
|
||||
// stub
|
||||
}
|
||||
|
|
@ -32,14 +32,13 @@
|
|||
**
|
||||
*/
|
||||
|
||||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <objbase.h>
|
||||
#include <commctrl.h>
|
||||
#include <richedit.h>
|
||||
#include <string>
|
||||
#include <ShlObj.h>
|
||||
|
||||
#include <processenv.h>
|
||||
#include <shellapi.h>
|
||||
|
|
@ -80,8 +79,6 @@
|
|||
|
||||
#include "i_mainwindow.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
// The main window's title.
|
||||
#ifdef _M_X64
|
||||
#define X64 " 64-bit"
|
||||
|
|
@ -91,53 +88,25 @@
|
|||
#define X64 ""
|
||||
#endif
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
void InitCrashReporter(const std::wstring& reports_directory, const std::wstring& uploader_executable);
|
||||
FString GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create);
|
||||
|
||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||
|
||||
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
|
||||
void CreateCrashLog (const char *custominfo, DWORD customsize);
|
||||
void DisplayCrashLog ();
|
||||
void DestroyCustomCursor();
|
||||
int GameMain();
|
||||
|
||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||
|
||||
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||||
|
||||
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||||
|
||||
extern EXCEPTION_POINTERS CrashPointers;
|
||||
extern UINT TimerPeriod;
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
// The command line arguments.
|
||||
FArgs *Args;
|
||||
|
||||
HINSTANCE g_hInst;
|
||||
HANDLE MainThread;
|
||||
DWORD MainThreadID;
|
||||
HANDLE StdOut;
|
||||
bool FancyStdOut, AttachedStdOut;
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// I_SetIWADInfo
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void I_SetIWADInfo()
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DoMain
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int DoMain (HINSTANCE hInstance)
|
||||
|
|
@ -167,7 +136,7 @@ int DoMain (HINSTANCE hInstance)
|
|||
// handle instead of creating a console window.
|
||||
|
||||
StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (StdOut != NULL)
|
||||
if (StdOut != nullptr)
|
||||
{
|
||||
// It seems that running from a shell always creates a std output
|
||||
// for us, even if it doesn't go anywhere. (Running from Explorer
|
||||
|
|
@ -177,7 +146,7 @@ int DoMain (HINSTANCE hInstance)
|
|||
BY_HANDLE_FILE_INFORMATION info;
|
||||
if (!GetFileInformationByHandle(StdOut, &info))
|
||||
{
|
||||
StdOut = NULL;
|
||||
StdOut = nullptr;
|
||||
}
|
||||
}
|
||||
if (StdOut == nullptr)
|
||||
|
|
@ -185,7 +154,7 @@ int DoMain (HINSTANCE hInstance)
|
|||
if (AttachConsole(ATTACH_PARENT_PROCESS))
|
||||
{
|
||||
StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD foo; WriteFile(StdOut, "\n", 1, &foo, NULL);
|
||||
DWORD foo; WriteFile(StdOut, "\n", 1, &foo, nullptr);
|
||||
AttachedStdOut = true;
|
||||
}
|
||||
if (StdOut == nullptr && AllocConsole())
|
||||
|
|
@ -217,7 +186,7 @@ int DoMain (HINSTANCE hInstance)
|
|||
|
||||
// Figure out what directory the program resides in.
|
||||
WCHAR progbuff[1024];
|
||||
if (GetModuleFileNameW(nullptr, progbuff, sizeof progbuff) == 0)
|
||||
if (GetModuleFileNameW(nullptr, progbuff, 1024) == 0)
|
||||
{
|
||||
MessageBoxA(nullptr, "Fatal", "Could not determine program location.", MB_ICONEXCLAMATION|MB_OK);
|
||||
exit(-1);
|
||||
|
|
@ -242,7 +211,7 @@ int DoMain (HINSTANCE hInstance)
|
|||
// element. DEVMODE is not one of those structures.
|
||||
memset (&displaysettings, 0, sizeof(displaysettings));
|
||||
displaysettings.dmSize = sizeof(displaysettings);
|
||||
EnumDisplaySettings (NULL, ENUM_CURRENT_SETTINGS, &displaysettings);
|
||||
EnumDisplaySettings (nullptr, ENUM_CURRENT_SETTINGS, &displaysettings);
|
||||
x = (displaysettings.dmPelsWidth - width) / 2;
|
||||
y = (displaysettings.dmPelsHeight - height) / 2;
|
||||
|
||||
|
|
@ -260,17 +229,17 @@ int DoMain (HINSTANCE hInstance)
|
|||
WinWidth = cRect.right;
|
||||
WinHeight = cRect.bottom;
|
||||
|
||||
CoInitialize (NULL);
|
||||
atexit ([](){ CoUninitialize(); }); // beware of calling convention.
|
||||
if (SUCCEEDED(CoInitialize(nullptr)))
|
||||
atexit ([](){ CoUninitialize(); }); // beware of calling convention.
|
||||
|
||||
int ret = GameMain ();
|
||||
|
||||
if (mainwindow.CheckForRestart())
|
||||
{
|
||||
HMODULE hModule = GetModuleHandleW(NULL);
|
||||
HMODULE hModule = GetModuleHandleW(nullptr);
|
||||
WCHAR path[MAX_PATH];
|
||||
GetModuleFileNameW(hModule, path, MAX_PATH);
|
||||
ShellExecuteW(NULL, L"open", path, GetCommandLineW(), NULL, SW_SHOWNORMAL);
|
||||
ShellExecuteW(nullptr, L"open", path, GetCommandLineW(), nullptr, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
DestroyCustomCursor();
|
||||
|
|
@ -287,7 +256,7 @@ int DoMain (HINSTANCE hInstance)
|
|||
if (StdOut != nullptr) WriteFile(StdOut, "Press any key to exit...", 24, &bytes, nullptr);
|
||||
FlushConsoleInputBuffer(stdinput);
|
||||
SetConsoleMode(stdinput, 0);
|
||||
ReadConsole(stdinput, &bytes, 1, &bytes, NULL);
|
||||
ReadConsole(stdinput, &bytes, 1, &bytes, nullptr);
|
||||
}
|
||||
else if (StdOut == nullptr)
|
||||
{
|
||||
|
|
@ -319,231 +288,13 @@ void I_ShowFatalError(const char *msg)
|
|||
}
|
||||
}
|
||||
|
||||
// Here is how the error logging system works.
|
||||
//
|
||||
// To catch exceptions that occur in secondary threads, CatchAllExceptions is
|
||||
// set as the UnhandledExceptionFilter for this process. It records the state
|
||||
// of the thread at the time of the crash using CreateCrashLog and then queues
|
||||
// an APC on the primary thread. When the APC executes, it raises a software
|
||||
// exception that gets caught by the __try/__except block in WinMain.
|
||||
// I_GetEvent calls SleepEx to put the primary thread in a waitable state
|
||||
// periodically so that the APC has a chance to execute.
|
||||
//
|
||||
// Exceptions on the primary thread are caught by the __try/__except block in
|
||||
// WinMain. Not only does it record the crash information, it also shuts
|
||||
// everything down and displays a dialog with the information present. If a
|
||||
// console log is being produced, the information will also be appended to it.
|
||||
//
|
||||
// If a debugger is running, CatchAllExceptions never executes, so secondary
|
||||
// thread exceptions will always be caught by the debugger. For the primary
|
||||
// thread, IsDebuggerPresent is called to determine if a debugger is present.
|
||||
// Note that this function is not present on Windows 95, so we cannot
|
||||
// statically link to it.
|
||||
//
|
||||
// To make this work with MinGW, you will need to use inline assembly
|
||||
// because GCC offers no native support for Windows' SEH.
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// SleepForever
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void SleepForever ()
|
||||
{
|
||||
Sleep (INFINITE);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ExitMessedUp
|
||||
//
|
||||
// An exception occurred while exiting, so don't do any standard processing.
|
||||
// Just die.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
LONG WINAPI ExitMessedUp (LPEXCEPTION_POINTERS foo)
|
||||
{
|
||||
ExitProcess (1000);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ExitFatally
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void CALLBACK ExitFatally (ULONG_PTR dummy)
|
||||
{
|
||||
SetUnhandledExceptionFilter (ExitMessedUp);
|
||||
I_ShutdownGraphics ();
|
||||
mainwindow.RestoreConView ();
|
||||
DisplayCrashLog ();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#ifndef _M_ARM64
|
||||
//==========================================================================
|
||||
//
|
||||
// CatchAllExceptions
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
namespace
|
||||
{
|
||||
CONTEXT MainThreadContext;
|
||||
}
|
||||
|
||||
LONG WINAPI CatchAllExceptions (LPEXCEPTION_POINTERS info)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (info->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
|
||||
{
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool caughtsomething = false;
|
||||
|
||||
if (caughtsomething) return EXCEPTION_EXECUTE_HANDLER;
|
||||
caughtsomething = true;
|
||||
|
||||
char *custominfo = (char *)HeapAlloc (GetProcessHeap(), 0, 16384);
|
||||
|
||||
CrashPointers = *info;
|
||||
if (sysCallbacks.CrashInfo && custominfo) sysCallbacks.CrashInfo(custominfo, 16384, "\r\n");
|
||||
CreateCrashLog (custominfo, (DWORD)strlen(custominfo));
|
||||
|
||||
// If the main thread crashed, then make it clean up after itself.
|
||||
// Otherwise, put the crashing thread to sleep and signal the main thread to clean up.
|
||||
if (GetCurrentThreadId() == MainThreadID)
|
||||
{
|
||||
#ifdef _M_X64
|
||||
*info->ContextRecord = MainThreadContext;
|
||||
#else
|
||||
info->ContextRecord->Eip = (DWORD_PTR)ExitFatally;
|
||||
#endif // _M_X64
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef _M_X64
|
||||
info->ContextRecord->Eip = (DWORD_PTR)SleepForever;
|
||||
#else
|
||||
info->ContextRecord->Rip = (DWORD_PTR)SleepForever;
|
||||
#endif
|
||||
QueueUserAPC (ExitFatally, MainThread, 0);
|
||||
}
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
#else // !_M_ARM64
|
||||
// stub this function for ARM64
|
||||
LONG WINAPI CatchAllExceptions (LPEXCEPTION_POINTERS info)
|
||||
{
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
#endif // !_M_ARM64
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// infiniterecursion
|
||||
//
|
||||
// Debugging routine for testing the crash logger.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
#ifdef _DEBUG
|
||||
static void infiniterecursion(int foo)
|
||||
{
|
||||
if (foo)
|
||||
{
|
||||
infiniterecursion(foo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Setting this to 'true' allows getting the standard notification for a crash
|
||||
// which offers the very important feature to open a debugger and see the crash in context right away.
|
||||
CUSTOM_CVAR(Bool, disablecrashlog, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
SetUnhandledExceptionFilter(!*self ? CatchAllExceptions : nullptr);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// WinMain
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE nothing, LPWSTR cmdline, int nCmdShow)
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE nothing, LPWSTR cmdline, int nCmdShow)
|
||||
{
|
||||
g_hInst = hInstance;
|
||||
|
||||
InitCommonControls (); // Load some needed controls and be pretty under XP
|
||||
|
||||
// We need to load riched20.dll so that we can create the control.
|
||||
if (NULL == LoadLibraryA ("riched20.dll"))
|
||||
{
|
||||
// This should only happen on basic Windows 95 installations, but since we
|
||||
// don't support Windows 95, we have no obligation to provide assistance in
|
||||
// getting it installed.
|
||||
MessageBoxA(NULL, "Could not load riched20.dll", GAMENAME " Error", MB_OK | MB_ICONSTOP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !defined(__GNUC__) && defined(_DEBUG)
|
||||
if (__argc == 2 && __wargv != nullptr && wcscmp (__wargv[1], L"TestCrash") == 0)
|
||||
{
|
||||
__try
|
||||
{
|
||||
*(int *)0 = 0;
|
||||
}
|
||||
__except(CrashPointers = *GetExceptionInformation(),
|
||||
CreateCrashLog ("TestCrash", 9), EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
}
|
||||
DisplayCrashLog ();
|
||||
return 0;
|
||||
}
|
||||
if (__argc == 2 && __wargv != nullptr && wcscmp (__wargv[1], L"TestStackCrash") == 0)
|
||||
{
|
||||
__try
|
||||
{
|
||||
infiniterecursion(1);
|
||||
}
|
||||
__except(CrashPointers = *GetExceptionInformation(),
|
||||
CreateCrashLog ("TestStackCrash", 14), EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
}
|
||||
DisplayCrashLog ();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
MainThread = INVALID_HANDLE_VALUE;
|
||||
DuplicateHandle (GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &MainThread,
|
||||
0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
MainThreadID = GetCurrentThreadId();
|
||||
|
||||
#ifndef _DEBUG
|
||||
if (MainThread != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
#ifndef _M_ARM64
|
||||
SetUnhandledExceptionFilter (CatchAllExceptions);
|
||||
#endif
|
||||
|
||||
#ifdef _M_X64
|
||||
static bool setJumpResult = false;
|
||||
RtlCaptureContext(&MainThreadContext);
|
||||
if (setJumpResult)
|
||||
{
|
||||
ExitFatally(0);
|
||||
return 0;
|
||||
}
|
||||
setJumpResult = true;
|
||||
#endif // _M_X64
|
||||
}
|
||||
#endif
|
||||
InitCommonControls();
|
||||
|
||||
#if defined(_DEBUG) && defined(_MSC_VER)
|
||||
// Uncomment this line to make the Visual C++ CRT check the heap before
|
||||
|
|
@ -558,14 +309,24 @@ int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE nothing, LPWSTR cmdline, int
|
|||
//_crtBreakAlloc = 227524;
|
||||
#endif
|
||||
|
||||
int ret = DoMain (hInstance);
|
||||
// Setup crash reporting, unless it is the crash reporter launching us in response to a crash
|
||||
if (wcsstr(cmdline, L"-showcrashreport") == nullptr)
|
||||
{
|
||||
WCHAR exeFilename[1024] = {};
|
||||
if (GetModuleFileName(0, exeFilename, 1023) != 0)
|
||||
{
|
||||
FString reportsDirectory = GetKnownFolder(CSIDL_LOCAL_APPDATA, FOLDERID_LocalAppData, true);
|
||||
reportsDirectory += "/" GAMENAMELOWERCASE;
|
||||
reportsDirectory += "/crashreports";
|
||||
CreatePath(reportsDirectory.GetChars());
|
||||
|
||||
CloseHandle (MainThread);
|
||||
MainThread = INVALID_HANDLE_VALUE;
|
||||
return ret;
|
||||
InitCrashReporter(reportsDirectory.WideString(), exeFilename);
|
||||
}
|
||||
}
|
||||
|
||||
return DoMain(hInstance);
|
||||
}
|
||||
|
||||
// each platform has its own specific version of this function.
|
||||
void I_SetWindowTitle(const char* caption)
|
||||
{
|
||||
mainwindow.SetWindowTitle(caption);
|
||||
|
|
|
|||
|
|
@ -6,14 +6,16 @@
|
|||
#include <zwidget/core/image.h>
|
||||
#include <zwidget/widgets/pushbutton/pushbutton.h>
|
||||
#include <zwidget/widgets/scrollbar/scrollbar.h>
|
||||
#include <zwidget/systemdialogs/save_file_dialog.h>
|
||||
#include <miniz.h>
|
||||
|
||||
bool ErrorWindow::ExecModal(const std::string& text, const std::string& log)
|
||||
bool ErrorWindow::ExecModal(const std::string& text, const std::string& log, std::vector<uint8_t> minidump)
|
||||
{
|
||||
Size screenSize = GetScreenSize();
|
||||
double windowWidth = 1200.0;
|
||||
double windowHeight = 700.0;
|
||||
|
||||
auto window = std::make_unique<ErrorWindow>();
|
||||
auto window = std::make_unique<ErrorWindow>(std::move(minidump));
|
||||
window->SetText(text, log);
|
||||
window->SetFrameGeometry((screenSize.width - windowWidth) * 0.5, (screenSize.height - windowHeight) * 0.5, windowWidth, windowHeight);
|
||||
window->Show();
|
||||
|
|
@ -23,7 +25,7 @@ bool ErrorWindow::ExecModal(const std::string& text, const std::string& log)
|
|||
return window->Restart;
|
||||
}
|
||||
|
||||
ErrorWindow::ErrorWindow() : Widget(nullptr, WidgetType::Window)
|
||||
ErrorWindow::ErrorWindow(std::vector<uint8_t> initminidump) : Widget(nullptr, WidgetType::Window), minidump(std::move(initminidump))
|
||||
{
|
||||
FStringf caption("Fatal Error - " GAMENAME " %s (%s)", GetVersionString(), GetGitTime());
|
||||
SetWindowTitle(caption.GetChars());
|
||||
|
|
@ -34,13 +36,21 @@ ErrorWindow::ErrorWindow() : Widget(nullptr, WidgetType::Window)
|
|||
|
||||
LogView = new LogViewer(this);
|
||||
ClipboardButton = new PushButton(this);
|
||||
RestartButton = new PushButton(this);
|
||||
|
||||
ClipboardButton->OnClick = [=]() { OnClipboardButtonClicked(); };
|
||||
RestartButton->OnClick = [=]() { OnRestartButtonClicked(); };
|
||||
|
||||
ClipboardButton->SetText("Copy to clipboard");
|
||||
RestartButton->SetText("Restart");
|
||||
|
||||
if (minidump.empty())
|
||||
{
|
||||
RestartButton = new PushButton(this);
|
||||
RestartButton->OnClick = [=]() { OnRestartButtonClicked(); };
|
||||
RestartButton->SetText("Restart");
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveReportButton = new PushButton(this);
|
||||
SaveReportButton->OnClick = [=]() { OnSaveReportButtonClicked(); };
|
||||
SaveReportButton->SetText("Save Report");
|
||||
}
|
||||
|
||||
LogView->SetFocus();
|
||||
}
|
||||
|
|
@ -83,6 +93,37 @@ void ErrorWindow::OnRestartButtonClicked()
|
|||
DisplayWindow::ExitLoop();
|
||||
}
|
||||
|
||||
void ErrorWindow::OnSaveReportButtonClicked()
|
||||
{
|
||||
auto dialog = SaveFileDialog::Create(this);
|
||||
dialog->AddFilter("Crash Report Zip Files", "*.zip");
|
||||
dialog->AddFilter("All Files", "*.*");
|
||||
dialog->SetFilename("CrashReport.zip");
|
||||
dialog->SetDefaultExtension("zip");
|
||||
if (dialog->Show())
|
||||
{
|
||||
std::string filename = dialog->Filename();
|
||||
|
||||
mz_zip_archive zip = {};
|
||||
if (mz_zip_writer_init_heap(&zip, 0, 16 * 1024 * 1024))
|
||||
{
|
||||
mz_zip_writer_add_mem(&zip, "minidump.dmp", minidump.data(), minidump.size(), MZ_DEFAULT_COMPRESSION);
|
||||
mz_zip_writer_add_mem(&zip, "log.txt", clipboardtext.data(), clipboardtext.size(), MZ_DEFAULT_COMPRESSION);
|
||||
}
|
||||
void* buffer = nullptr;
|
||||
size_t buffersize = 0;
|
||||
mz_zip_writer_finalize_heap_archive(&zip, &buffer, &buffersize);
|
||||
mz_zip_writer_end(&zip);
|
||||
|
||||
std::unique_ptr<FileWriter> f(FileWriter::Open(filename.c_str()));
|
||||
if (f)
|
||||
{
|
||||
f->Write(buffer, buffersize);
|
||||
f->Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ErrorWindow::OnClose()
|
||||
{
|
||||
Restart = false;
|
||||
|
|
@ -96,7 +137,10 @@ void ErrorWindow::OnGeometryChanged()
|
|||
|
||||
double y = GetHeight() - 15.0 - ClipboardButton->GetPreferredHeight();
|
||||
ClipboardButton->SetFrameGeometry(20.0, y, 170.0, ClipboardButton->GetPreferredHeight());
|
||||
RestartButton->SetFrameGeometry(GetWidth() - 20.0 - 100.0, y, 100.0, RestartButton->GetPreferredHeight());
|
||||
if (RestartButton)
|
||||
RestartButton->SetFrameGeometry(GetWidth() - 20.0 - 100.0, y, 100.0, RestartButton->GetPreferredHeight());
|
||||
else if (SaveReportButton)
|
||||
SaveReportButton->SetFrameGeometry(GetWidth() - 20.0 - 100.0, y, 100.0, SaveReportButton->GetPreferredHeight());
|
||||
y -= 20.0;
|
||||
|
||||
LogView->SetFrameGeometry(Rect::xywh(0.0, 0.0, w, y));
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ class Scrollbar;
|
|||
class ErrorWindow : public Widget
|
||||
{
|
||||
public:
|
||||
static bool ExecModal(const std::string& text, const std::string& log);
|
||||
static bool ExecModal(const std::string& text, const std::string& log, std::vector<uint8_t> minidump = {});
|
||||
|
||||
ErrorWindow();
|
||||
ErrorWindow(std::vector<uint8_t> minidump);
|
||||
|
||||
bool Restart = false;
|
||||
|
||||
|
|
@ -25,11 +25,14 @@ private:
|
|||
|
||||
void OnClipboardButtonClicked();
|
||||
void OnRestartButtonClicked();
|
||||
void OnSaveReportButtonClicked();
|
||||
|
||||
LogViewer* LogView = nullptr;
|
||||
PushButton* ClipboardButton = nullptr;
|
||||
PushButton* RestartButton = nullptr;
|
||||
PushButton* SaveReportButton = nullptr;
|
||||
|
||||
std::vector<uint8_t> minidump;
|
||||
std::string clipboardtext;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#define WIN32_MEAN_AND_LEAN
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
|
|
@ -117,6 +119,7 @@
|
|||
#include "screenjob.h"
|
||||
#include "startscreen.h"
|
||||
#include "shiftstate.h"
|
||||
#include "common/widgets/errorwindow.h"
|
||||
|
||||
#ifdef __unix__
|
||||
#include "i_system.h" // for SHARE_DIR
|
||||
|
|
@ -3717,6 +3720,49 @@ static int D_DoomMain_Internal (void)
|
|||
LoadHexFont(wad); // load hex font early so we have it during startup.
|
||||
InitWidgetResources(wad);
|
||||
|
||||
if (Args->CheckParm("-showcrashreport"))
|
||||
{
|
||||
FString minidumpFilename = Args->GetArg(2);
|
||||
FString logFilename = Args->GetArg(3);
|
||||
|
||||
FString logText;
|
||||
{
|
||||
FileReader fr;
|
||||
if (fr.OpenFile(logFilename.GetChars()))
|
||||
{
|
||||
std::vector<char> data(fr.GetLength() + 1);
|
||||
if (fr.Read(data.data(), data.size() - 1) == (FileReader::Size)data.size() - 1)
|
||||
{
|
||||
logText = data.data();
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> minidump;
|
||||
{
|
||||
FileReader fr;
|
||||
if (fr.OpenFile(minidumpFilename.GetChars()))
|
||||
{
|
||||
minidump.resize(fr.GetLength());
|
||||
if (fr.Read(minidump.data(), minidump.size()) != (FileReader::Size)minidump.size())
|
||||
{
|
||||
minidump.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FString text;
|
||||
text.Format("%s fatally crashed!", GAMENAME);
|
||||
ErrorWindow::ExecModal(text.GetChars(), logText.GetChars(), std::move(minidump));
|
||||
|
||||
// Crash reporter only uses -showcrashreport on Windows at the moment and there seems to be no abstraction available
|
||||
#ifdef WIN32
|
||||
DeleteFile(logFilename.WideString().c_str());
|
||||
DeleteFile(minidumpFilename.WideString().c_str());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
C_InitConsole(80*8, 25*8, false);
|
||||
I_DetectOS();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue