1 | // Aseprite |
2 | // Copyright (C) 2019-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2016 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/app.h" |
13 | #include "app/cli/app_options.h" |
14 | #include "app/console.h" |
15 | #include "app/resource_finder.h" |
16 | #include "app/send_crash.h" |
17 | #include "base/exception.h" |
18 | #include "base/memory.h" |
19 | #include "base/system_console.h" |
20 | #include "doc/palette.h" |
21 | #include "os/error.h" |
22 | #include "os/system.h" |
23 | #include "ver/info.h" |
24 | |
25 | #if ENABLE_SENTRY |
26 | #include "app/sentry_wrapper.h" |
27 | #if LAF_WINDOWS |
28 | #define USE_SENTRY_BREADCRUMB_FOR_WINTAB 1 |
29 | #include "os/win/wintab.h" |
30 | #endif |
31 | #else |
32 | #include "base/memory_dump.h" |
33 | #endif |
34 | |
35 | #include <clocale> |
36 | #include <cstdlib> |
37 | #include <ctime> |
38 | #include <iostream> |
39 | |
40 | #if LAF_WINDOWS |
41 | #include <windows.h> |
42 | #endif |
43 | |
44 | namespace { |
45 | |
46 | // Memory leak detector wrapper |
47 | class MemLeak { |
48 | public: |
49 | #ifdef LAF_MEMLEAK |
50 | MemLeak() { base_memleak_init(); } |
51 | ~MemLeak() { base_memleak_exit(); } |
52 | #else |
53 | MemLeak() { } |
54 | #endif |
55 | }; |
56 | |
57 | #if LAF_WINDOWS |
58 | // Successful calls to CoInitialize() (S_OK or S_FALSE) must match |
59 | // the calls to CoUninitialize(). |
60 | // From: https://docs.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-couninitialize#remarks |
61 | struct CoInit { |
62 | HRESULT hr; |
63 | CoInit() { |
64 | hr = CoInitialize(nullptr); |
65 | } |
66 | ~CoInit() { |
67 | if (hr == S_OK || hr == S_FALSE) |
68 | CoUninitialize(); |
69 | } |
70 | }; |
71 | #endif // LAF_WINDOWS |
72 | |
73 | #if USE_SENTRY_BREADCRUMB_FOR_WINTAB |
74 | // Delegate to write Wintab information as a Sentry breadcrumb (to |
75 | // know if there is a specific Wintab driver giving problems) |
76 | class WintabApiDelegate : public os::WintabAPI::Delegate { |
77 | bool m_done = false; |
78 | public: |
79 | WintabApiDelegate() { |
80 | os::instance()->setWintabDelegate(this); |
81 | } |
82 | ~WintabApiDelegate() { |
83 | os::instance()->setWintabDelegate(nullptr); |
84 | } |
85 | void onWintabID(const std::string& id) override { |
86 | if (!m_done) { |
87 | m_done = true; |
88 | app::Sentry::addBreadcrumb("Wintab ID=" + id); |
89 | } |
90 | } |
91 | void onWintabFields(const std::map<std::string, std::string>& fields) override { |
92 | app::Sentry::addBreadcrumb("Wintab DLL" , fields); |
93 | } |
94 | }; |
95 | #endif // USE_SENTRY_BREADCRUMB_FOR_WINTAB |
96 | |
97 | } |
98 | |
99 | // Aseprite entry point. (Called from "os" library.) |
100 | int app_main(int argc, char* argv[]) |
101 | { |
102 | // Initialize the locale. Aseprite isn't ready to handle numeric |
103 | // fields with other locales (e.g. we expect strings like "10.32" be |
104 | // used in std::strtod(), not something like "10,32"). |
105 | std::setlocale(LC_ALL, "en-US" ); |
106 | ASSERT(std::strtod("10.32" , nullptr) == 10.32); |
107 | |
108 | // Initialize the random seed. |
109 | std::srand(static_cast<unsigned int>(std::time(nullptr))); |
110 | |
111 | #if LAF_WINDOWS |
112 | CoInit com; // To create COM objects |
113 | #endif |
114 | |
115 | try { |
116 | #if ENABLE_SENTRY |
117 | app::Sentry sentry; |
118 | #else |
119 | base::MemoryDump memoryDump; |
120 | #endif |
121 | MemLeak memleak; |
122 | base::SystemConsole systemConsole; |
123 | app::AppOptions options(argc, const_cast<const char**>(argv)); |
124 | os::SystemRef system(os::make_system()); |
125 | doc::Palette::initBestfit(); |
126 | app::App app; |
127 | |
128 | #if ENABLE_SENTRY |
129 | sentry.init(); |
130 | #if USE_SENTRY_BREADCRUMB_FOR_WINTAB |
131 | WintabApiDelegate wintabDelegate; |
132 | #endif |
133 | #else |
134 | // Change the memory dump filename to save on disk (.dmp |
135 | // file). Note: Only useful on Windows. |
136 | { |
137 | const std::string fn = app::SendCrash::DefaultMemoryDumpFilename(); |
138 | if (!fn.empty()) |
139 | memoryDump.setFileName(fn); |
140 | } |
141 | #endif |
142 | |
143 | const int code = app.initialize(options); |
144 | |
145 | if (options.startShell()) |
146 | systemConsole.prepareShell(); |
147 | |
148 | app.run(); |
149 | |
150 | // After starting the GUI, we'll always return 0, but in batch |
151 | // mode we can return the error code. |
152 | return (app.isGui() ? 0: code); |
153 | } |
154 | catch (std::exception& e) { |
155 | std::cerr << e.what() << '\n'; |
156 | os::error_message(e.what()); |
157 | return 1; |
158 | } |
159 | } |
160 | |