1/*
2 * Copyright 2015-2016, Rene Gollent, rene@gollent.com.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "ReportUserInterface.h"
8
9#include <stdio.h>
10
11#include <Entry.h>
12#include <FindDirectory.h>
13#include <Path.h>
14
15#include <AutoLocker.h>
16
17#include "MessageCodes.h"
18#include "UiUtils.h"
19
20
21ReportUserInterface::ReportUserInterface(thread_id targetThread,
22	const char* reportPath)
23	:
24	fTeam(NULL),
25	fListener(NULL),
26	fTargetThread(targetThread),
27	fReportPath(reportPath),
28	fShowSemaphore(-1),
29	fReportSemaphore(-1),
30	fShown(false),
31	fTerminating(false)
32{
33}
34
35
36ReportUserInterface::~ReportUserInterface()
37{
38	if (fShowSemaphore >= 0)
39		delete_sem(fShowSemaphore);
40
41	if (fTeam != NULL)
42		fTeam->RemoveListener(this);
43}
44
45
46const char*
47ReportUserInterface::ID() const
48{
49	return "ReportUserInterface";
50}
51
52
53status_t
54ReportUserInterface::Init(Team* team, UserInterfaceListener* listener)
55{
56	fShowSemaphore = create_sem(0, "show report");
57	if (fShowSemaphore < 0)
58		return fShowSemaphore;
59
60	fReportSemaphore = create_sem(0, "report generator wait");
61	if (fReportSemaphore < 0)
62		return fReportSemaphore;
63
64	fTeam = team;
65	fListener = listener;
66
67	fTeam->AddListener(this);
68
69	return B_OK;
70}
71
72
73void
74ReportUserInterface::Show()
75{
76	fShown = true;
77	release_sem(fShowSemaphore);
78}
79
80
81void
82ReportUserInterface::Terminate()
83{
84	fTerminating = true;
85}
86
87
88UserInterface*
89ReportUserInterface::Clone() const
90{
91	// the report interface does not support cloning, since
92	// it won't ever be asked to interactively restart.
93	return NULL;
94}
95
96
97bool
98ReportUserInterface::IsInteractive() const
99{
100	return false;
101}
102
103
104status_t
105ReportUserInterface::LoadSettings(const TeamUiSettings* settings)
106{
107	return B_OK;
108}
109
110
111status_t
112ReportUserInterface::SaveSettings(TeamUiSettings*& settings) const
113{
114	return B_OK;
115}
116
117
118void
119ReportUserInterface::NotifyUser(const char* title, const char* message,
120	user_notification_type type)
121{
122}
123
124
125void
126ReportUserInterface::NotifyBackgroundWorkStatus(const char* message)
127{
128}
129
130
131int32
132ReportUserInterface::SynchronouslyAskUser(const char* title,
133	const char* message, const char* choice1, const char* choice2,
134	const char* choice3)
135{
136	return -1;
137}
138
139
140status_t
141ReportUserInterface::SynchronouslyAskUserForFile(entry_ref* _ref)
142{
143	return B_UNSUPPORTED;
144}
145
146
147void
148ReportUserInterface::Run()
149{
150	// Wait for the Show() semaphore to be released.
151	status_t error;
152	do {
153		error = acquire_sem(fShowSemaphore);
154	} while (error == B_INTERRUPTED);
155
156	if (error != B_OK)
157		return;
158
159	bool waitNeeded = false;
160	if (fTargetThread > 0) {
161		AutoLocker< ::Team> teamLocker(fTeam);
162		::Thread* thread = fTeam->ThreadByID(fTargetThread);
163		if (thread == NULL)
164			waitNeeded = true;
165		else if (thread->State() != THREAD_STATE_STOPPED) {
166			waitNeeded = true;
167			fListener->ThreadActionRequested(fTargetThread, MSG_THREAD_STOP);
168		}
169	}
170
171	if (waitNeeded) {
172		do {
173			error = acquire_sem(fShowSemaphore);
174		} while (error == B_INTERRUPTED);
175
176		if (error != B_OK)
177			return;
178	}
179
180	entry_ref ref;
181	if (fReportPath != NULL && fReportPath[0] == '/') {
182		error = get_ref_for_path(fReportPath, &ref);
183	} else {
184		char filename[B_FILE_NAME_LENGTH];
185		if (fReportPath != NULL)
186			strlcpy(filename, fReportPath, sizeof(filename));
187		else
188			UiUtils::ReportNameForTeam(fTeam, filename, sizeof(filename));
189
190		BPath path;
191		error = find_directory(B_DESKTOP_DIRECTORY, &path);
192		if (error == B_OK)
193			error = path.Append(filename);
194		if (error == B_OK)
195			error = get_ref_for_path(path.Path(), &ref);
196	}
197
198	if (error != B_OK)
199		printf("Unable to get ref for report path %s\n", strerror(error));
200	else {
201		fListener->DebugReportRequested(&ref);
202
203		do {
204			error = acquire_sem(fReportSemaphore);
205		} while (error == B_INTERRUPTED);
206	}
207
208	fListener->UserInterfaceQuitRequested(
209		UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM);
210}
211
212
213void
214ReportUserInterface::ThreadAdded(const Team::ThreadEvent& event)
215{
216	::Thread* thread = event.GetThread();
217	if (thread->ID() != fTargetThread)
218		return;
219
220	if (thread->State() != THREAD_STATE_STOPPED)
221		fListener->ThreadActionRequested(thread->ID(), MSG_THREAD_STOP);
222	else
223		release_sem(fShowSemaphore);
224}
225
226
227void
228ReportUserInterface::ThreadStateChanged(const Team::ThreadEvent& event)
229{
230	::Thread* thread = event.GetThread();
231	if (thread->ID() != fTargetThread)
232		return;
233	else if (thread->State() == THREAD_STATE_STOPPED)
234		release_sem(fShowSemaphore);
235}
236
237
238void
239ReportUserInterface::DebugReportChanged(const Team::DebugReportEvent& event)
240{
241	if (event.GetFinalStatus() == B_OK)
242		printf("Debug report saved to %s\n", event.GetReportPath());
243	else {
244		fprintf(stderr, "Failed to write debug report: %s\n", strerror(
245				event.GetFinalStatus()));
246	}
247	release_sem(fReportSemaphore);
248}
249