xref: /haiku/src/apps/debugger/user_interface/gui/team_window/ConsoleOutputView.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
1 /*
2  * Copyright 2013-2014, Rene Gollent, rene@gollent.com.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ConsoleOutputView.h"
8 
9 #include <new>
10 
11 #include <Button.h>
12 #include <CheckBox.h>
13 #include <LayoutBuilder.h>
14 #include <ScrollView.h>
15 #include <String.h>
16 #include <TextView.h>
17 
18 #include <AutoDeleter.h>
19 
20 
21 enum {
22 	MSG_CLEAR_OUTPUT	= 'clou',
23 	MSG_POST_OUTPUT		= 'poou'
24 };
25 
26 
27 static const bigtime_t kOutputWaitInterval = 10000;
28 
29 
30 // #pragma mark - ConsoleOutputView::OutputInfo
31 
32 
33 struct ConsoleOutputView::OutputInfo {
34 	int32 	fd;
35 	BString	text;
36 
37 	OutputInfo(int32 fd, const BString& text)
38 		:
39 		fd(fd),
40 		text(text)
41 	{
42 	}
43 };
44 
45 
46 // #pragma mark - ConsoleOutputView
47 
48 
49 ConsoleOutputView::ConsoleOutputView()
50 	:
51 	BGroupView(B_VERTICAL, 0.0f),
52 	fStdoutEnabled(NULL),
53 	fStderrEnabled(NULL),
54 	fConsoleOutput(NULL),
55 	fClearButton(NULL),
56 	fPendingOutput(NULL),
57 	fWorkToDoSem(-1),
58 	fOutputWorker(-1)
59 {
60 	SetName("ConsoleOutput");
61 }
62 
63 
64 ConsoleOutputView::~ConsoleOutputView()
65 {
66 	if (fWorkToDoSem > 0)
67 		delete_sem(fWorkToDoSem);
68 
69 	if (fOutputWorker > 0)
70 		wait_for_thread(fOutputWorker, NULL);
71 
72 	delete fPendingOutput;
73 }
74 
75 
76 /*static*/ ConsoleOutputView*
77 ConsoleOutputView::Create()
78 {
79 	ConsoleOutputView* self = new ConsoleOutputView();
80 
81 	try {
82 		self->_Init();
83 	} catch (...) {
84 		delete self;
85 		throw;
86 	}
87 
88 	return self;
89 }
90 
91 
92 void
93 ConsoleOutputView::ConsoleOutputReceived(int32 fd, const BString& output)
94 {
95 	if (fd == 1 && fStdoutEnabled->Value() != B_CONTROL_ON)
96 		return;
97 	else if (fd == 2 && fStderrEnabled->Value() != B_CONTROL_ON)
98 		return;
99 
100 	OutputInfo* info = new(std::nothrow) OutputInfo(fd, output);
101 	if (info == NULL)
102 		return;
103 
104 	ObjectDeleter<OutputInfo> infoDeleter(info);
105 	if (fPendingOutput->AddItem(info)) {
106 		infoDeleter.Detach();
107 		release_sem(fWorkToDoSem);
108 	}
109 }
110 
111 
112 void
113 ConsoleOutputView::MessageReceived(BMessage* message)
114 {
115 	switch (message->what) {
116 		case MSG_CLEAR_OUTPUT:
117 		{
118 			fConsoleOutput->SetText("");
119 			fPendingOutput->MakeEmpty();
120 			break;
121 		}
122 		case MSG_POST_OUTPUT:
123 		{
124 			OutputInfo* info = fPendingOutput->RemoveItemAt(0);
125 			if (info == NULL)
126 				break;
127 
128 			ObjectDeleter<OutputInfo> infoDeleter(info);
129 			_HandleConsoleOutput(info);
130 		}
131 		default:
132 			BGroupView::MessageReceived(message);
133 			break;
134 	}
135 }
136 
137 
138 void
139 ConsoleOutputView::AttachedToWindow()
140 {
141 	BGroupView::AttachedToWindow();
142 
143 	fStdoutEnabled->SetValue(B_CONTROL_ON);
144 	fStderrEnabled->SetValue(B_CONTROL_ON);
145 	fClearButton->SetTarget(this);
146 }
147 
148 
149 void
150 ConsoleOutputView::LoadSettings(const BMessage& settings)
151 {
152 	fStdoutEnabled->SetValue(settings.GetBool("showStdout", true)
153 			? B_CONTROL_ON : B_CONTROL_OFF);
154 	fStderrEnabled->SetValue(settings.GetBool("showStderr", true)
155 			? B_CONTROL_ON : B_CONTROL_OFF);
156 }
157 
158 
159 status_t
160 ConsoleOutputView::SaveSettings(BMessage& settings)
161 {
162 	bool value = fStdoutEnabled->Value() == B_CONTROL_ON;
163 	if (settings.AddBool("showStdout", value) != B_OK)
164 		return B_NO_MEMORY;
165 
166 	value = fStderrEnabled->Value() == B_CONTROL_ON;
167 	if (settings.AddBool("showStderr", value) != B_OK)
168 		return B_NO_MEMORY;
169 
170 	return B_OK;
171 }
172 
173 
174 void
175 ConsoleOutputView::_Init()
176 {
177 	fPendingOutput = new OutputInfoList(10, true);
178 
179 	fWorkToDoSem = create_sem(0, "output_work_available");
180 	if (fWorkToDoSem < 0)
181 		throw std::bad_alloc();
182 
183 	fOutputWorker = spawn_thread(_OutputWorker, "output worker", B_LOW_PRIORITY, this);
184 	if (fOutputWorker < 0)
185 		throw std::bad_alloc();
186 
187 	resume_thread(fOutputWorker);
188 
189 	BScrollView* consoleScrollView;
190 
191 	BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0.0f)
192 		.Add(consoleScrollView = new BScrollView("console scroll", NULL, 0,
193 			true, true), 3.0f)
194 		.AddGroup(B_VERTICAL, 0.0f)
195 			.SetInsets(B_USE_SMALL_SPACING)
196 			.Add(fStdoutEnabled = new BCheckBox("Stdout"))
197 			.Add(fStderrEnabled = new BCheckBox("Stderr"))
198 			.Add(fClearButton = new BButton("Clear"))
199 			.AddGlue()
200 		.End()
201 	.End();
202 
203 	consoleScrollView->SetTarget(fConsoleOutput = new BTextView("Console"));
204 
205 	fClearButton->SetMessage(new BMessage(MSG_CLEAR_OUTPUT));
206 	fConsoleOutput->MakeEditable(false);
207 	fConsoleOutput->SetStylable(true);
208 	fConsoleOutput->SetDoesUndo(false);
209 }
210 
211 
212 int32
213 ConsoleOutputView::_OutputWorker(void* arg)
214 {
215 	ConsoleOutputView* view = (ConsoleOutputView*)arg;
216 
217 	for (;;) {
218 		status_t error = acquire_sem(view->fWorkToDoSem);
219 		if (error == B_INTERRUPTED)
220 			continue;
221 		else if (error != B_OK)
222 			break;
223 
224 		BMessenger(view).SendMessage(MSG_POST_OUTPUT);
225 		snooze(kOutputWaitInterval);
226 	}
227 
228 	return B_OK;
229 }
230 
231 
232 void
233 ConsoleOutputView::_HandleConsoleOutput(OutputInfo* info)
234 {
235 	if (info->fd == 1 && fStdoutEnabled->Value() != B_CONTROL_ON)
236 		return;
237 	else if (info->fd == 2 && fStderrEnabled->Value() != B_CONTROL_ON)
238 		return;
239 
240 	text_run_array run;
241 	run.count = 1;
242 	run.runs[0].font = be_fixed_font;
243 	run.runs[0].offset = 0;
244 	run.runs[0].color.red = info->fd == 1 ? 0 : 192;
245 	run.runs[0].color.green = 0;
246 	run.runs[0].color.blue = 0;
247 	run.runs[0].color.alpha = 255;
248 
249 	bool autoScroll = false;
250 	BScrollBar* scroller = fConsoleOutput->ScrollBar(B_VERTICAL);
251 	float min, max;
252 	scroller->GetRange(&min, &max);
253 	if (min == max || scroller->Value() == max)
254 		autoScroll = true;
255 
256 	fConsoleOutput->Insert(fConsoleOutput->TextLength(), info->text,
257 		info->text.Length(), &run);
258 	if (autoScroll) {
259 		scroller->GetRange(&min, &max);
260 		fConsoleOutput->ScrollTo(0.0, max);
261 	}
262 }
263