File: crash-handler.cpp

package info (click to toggle)
obs-advanced-scene-switcher 1.32.8-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 43,492 kB
  • sloc: xml: 297,593; cpp: 147,875; python: 387; sh: 280; ansic: 170; makefile: 33
file content (153 lines) | stat: -rw-r--r-- 3,097 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "crash-handler.hpp"
#include "log-helper.hpp"
#include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "ui-helpers.hpp"

#include <obs-frontend-api.h>
#include <obs-module.h>

#include <QDir>
#include <QFile>
#include <QMainWindow>
#include <QTimer>

#include <thread>

namespace advss {

static constexpr std::string_view sentinel = ".running";

#ifndef NDEBUG
static constexpr bool handleUncleanShutdown = false;
#else
static constexpr bool handleUncleanShutdown = true;
#endif

static bool wasCleanShutdown = false;

static void setup();
static bool setupDone = []() {
	AddPluginInitStep(setup);
	return true;
}();

static void handleShutdown(enum obs_frontend_event event, void *)
{
	if (event != OBS_FRONTEND_EVENT_EXIT) {
		return;
	}

	char *sentinelFile = obs_module_config_path(sentinel.data());
	if (!sentinelFile) {
		return;
	}

	QFile file(sentinelFile);
	if (!file.exists()) {
		bfree(sentinelFile);
		return;
	}

	if (!file.remove()) {
		blog(LOG_WARNING, "failed to remove sentinel file");
	}

	bfree(sentinelFile);
}

static void setup()
{
	char *sentinelFile = obs_module_config_path(sentinel.data());
	if (!sentinelFile) {
		return;
	}

	QString dirPath = QFileInfo(sentinelFile).absolutePath();
	QDir dir(dirPath);
	if (!dir.exists()) {
		if (!dir.mkpath(dirPath)) {
			blog(LOG_WARNING,
			     "failed to create directory for sentinel file");
			bfree(sentinelFile);
			return;
		}
	}

	QFile file(sentinelFile);

	wasCleanShutdown = file.exists();

	if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
		blog(LOG_WARNING, "failed to create sentinel file");
		bfree(sentinelFile);
		return;
	}

	file.write("running");
	file.close();
	bfree(sentinelFile);

	obs_frontend_add_event_callback(handleShutdown, nullptr);
	return;
}

static bool wasUncleanShutdown()
{
	static bool alreadyHandled = false;

	if (!handleUncleanShutdown || !wasCleanShutdown || alreadyHandled) {
		alreadyHandled = true;
		return false;
	}

	alreadyHandled = true;
	blog(LOG_WARNING, "unclean shutdown detected");
	return true;
}

static void askForStartupSkip()
{
	bool skipStart = DisplayMessage(
		obs_module_text("AdvSceneSwitcher.crashDetected"), true, false);
	if (!skipStart) {
		StartPlugin();
	}
}

bool ShouldSkipPluginStartOnUncleanShutdown()
{
	if (!wasUncleanShutdown()) {
		return false;
	}

	// This function is called while the plugin settings are being loaded.
	// Blocking at this stage can cause issues such as OBS failing to start
	// or crashing.
	// Therefore, we ask the user whether they want to start the plugin
	// asynchronously.
	//
	// On macOS, an additional QTimer::singleShot wrapper is required for
	// this to work correctly.
	static const auto showDialogWrapper = [](void *) {
#ifdef __APPLE__
		QTimer::singleShot(0,
				   static_cast<QMainWindow *>(
					   obs_frontend_get_main_window()),
				   []() {
#endif
					   askForStartupSkip();
#ifdef __APPLE__
				   });
#endif
	};

	std::thread t([]() {
		obs_queue_task(OBS_TASK_UI, showDialogWrapper, nullptr, false);
	});
	t.detach();

	return true;
}

} // namespace advss