xref: /haiku/src/apps/serialconnect/SerialWindow.cpp (revision f8da8f3477d3c18142e59d17d05a545982faa5a8)
1 /*
2  * Copyright 2012, Adrien Destugues, pulkomandy@gmail.com
3  * Distributed under the terms of the MIT licence.
4  */
5 
6 
7 #include "SerialWindow.h"
8 
9 #include <stdio.h>
10 
11 #include <FilePanel.h>
12 #include <GroupLayout.h>
13 #include <Menu.h>
14 #include <MenuBar.h>
15 #include <MenuItem.h>
16 #include <SerialPort.h>
17 
18 #include "SerialApp.h"
19 #include "TermView.h"
20 
21 
22 const int SerialWindow::kBaudrates[] = { 50, 75, 110, 134, 150, 200, 300, 600,
23 	1200, 1800, 2400, 4800, 9600, 19200, 31250, 38400, 57600, 115200, 230400
24 };
25 
26 
27 // The values for these constants are not in the expected order, so we have to
28 // rely on this lookup table if we want to keep the menu items sorted.
29 const int SerialWindow::kBaudrateConstants[] = { B_50_BPS, B_75_BPS, B_110_BPS,
30 	B_134_BPS, B_150_BPS, B_200_BPS, B_300_BPS, B_600_BPS, B_1200_BPS,
31 	B_1800_BPS, B_2400_BPS, B_4800_BPS, B_9600_BPS, B_19200_BPS, B_31250_BPS,
32 	B_38400_BPS, B_57600_BPS, B_115200_BPS, B_230400_BPS
33 };
34 
35 
36 const char* SerialWindow::kWindowTitle = "SerialConnect";
37 
38 
39 SerialWindow::SerialWindow()
40 	: BWindow(BRect(100, 100, 400, 400), SerialWindow::kWindowTitle,
41 		B_DOCUMENT_WINDOW, B_QUIT_ON_WINDOW_CLOSE | B_AUTO_UPDATE_SIZE_LIMITS)
42 	, fLogFilePanel(NULL)
43 {
44 	SetLayout(new BGroupLayout(B_VERTICAL, 0.0f));
45 
46 	BMenuBar* menuBar = new BMenuBar("menuBar");
47 	fTermView = new TermView();
48 
49 	AddChild(menuBar);
50 	AddChild(fTermView);
51 
52 	fConnectionMenu = new BMenu("Connection");
53 	BMenu* fileMenu = new BMenu("File");
54 	BMenu* settingsMenu = new BMenu("Settings");
55 
56 	fConnectionMenu->SetRadioMode(true);
57 
58 	menuBar->AddItem(fConnectionMenu);
59 	menuBar->AddItem(fileMenu);
60 	menuBar->AddItem(settingsMenu);
61 
62 	// TODO edit menu - what's in it ?
63 	//BMenu* editMenu = new BMenu("Edit");
64 	//menuBar->AddItem(editMenu);
65 
66 	BMenuItem* logFile = new BMenuItem("Log to file" B_UTF8_ELLIPSIS,
67 		new BMessage(kMsgLogfile));
68 	fileMenu->AddItem(logFile);
69 #if 0
70 	// TODO implement these
71 	BMenuItem* xmodemSend = new BMenuItem("X/Y/ZModem send" B_UTF8_ELLIPSIS,
72 		NULL);
73 	fileMenu->AddItem(xmodemSend);
74 	BMenuItem* xmodemReceive = new BMenuItem(
75 		"X/Y/Zmodem receive" B_UTF8_ELLIPSIS, NULL);
76 	fileMenu->AddItem(xmodemReceive);
77 #endif
78 
79 	// Configuring all this by menus may be a bit unhandy. Make a setting
80 	// window instead ?
81 	fBaudrateMenu = new BMenu("Baud rate");
82 	fBaudrateMenu->SetRadioMode(true);
83 	settingsMenu->AddItem(fBaudrateMenu);
84 
85 	fParityMenu = new BMenu("Parity");
86 	fParityMenu->SetRadioMode(true);
87 	settingsMenu->AddItem(fParityMenu);
88 
89 	fStopbitsMenu = new BMenu("Stop bits");
90 	fStopbitsMenu->SetRadioMode(true);
91 	settingsMenu->AddItem(fStopbitsMenu);
92 
93 	fFlowcontrolMenu = new BMenu("Flow control");
94 	fFlowcontrolMenu->SetRadioMode(true);
95 	settingsMenu->AddItem(fFlowcontrolMenu);
96 
97 	fDatabitsMenu = new BMenu("Data bits");
98 	fDatabitsMenu->SetRadioMode(true);
99 	settingsMenu->AddItem(fDatabitsMenu);
100 
101 
102 	BMessage* message = new BMessage(kMsgSettings);
103 	message->AddInt32("parity", B_NO_PARITY);
104 	BMenuItem* parityNone = new BMenuItem("None", message);
105 
106 	message = new BMessage(kMsgSettings);
107 	message->AddInt32("parity", B_ODD_PARITY);
108 	BMenuItem* parityOdd = new BMenuItem("Odd", message);
109 
110 	message = new BMessage(kMsgSettings);
111 	message->AddInt32("parity", B_EVEN_PARITY);
112 	BMenuItem* parityEven = new BMenuItem("Even", message);
113 
114 	fParityMenu->AddItem(parityNone);
115 	fParityMenu->AddItem(parityOdd);
116 	fParityMenu->AddItem(parityEven);
117 	fParityMenu->SetTargetForItems(be_app);
118 
119 	message = new BMessage(kMsgSettings);
120 	message->AddInt32("databits", B_DATA_BITS_7);
121 	BMenuItem* data7 = new BMenuItem("7", message);
122 
123 	message = new BMessage(kMsgSettings);
124 	message->AddInt32("databits", B_DATA_BITS_8);
125 	BMenuItem* data8 = new BMenuItem("8", message);
126 
127 	fDatabitsMenu->AddItem(data7);
128 	fDatabitsMenu->AddItem(data8);
129 	fDatabitsMenu->SetTargetForItems(be_app);
130 
131 	message = new BMessage(kMsgSettings);
132 	message->AddInt32("stopbits", B_STOP_BITS_1);
133 	BMenuItem* stop1 = new BMenuItem("1", message);
134 
135 	message = new BMessage(kMsgSettings);
136 	message->AddInt32("stopbits", B_STOP_BITS_2);
137 	BMenuItem* stop2 = new BMenuItem("2", message);
138 
139 	fStopbitsMenu->AddItem(stop1);
140 	fStopbitsMenu->AddItem(stop2);
141 	fStopbitsMenu->SetTargetForItems(be_app);
142 
143 	// Loop backwards to add fastest rates at top of menu
144 	for (int i = sizeof(kBaudrates) / sizeof(char*); --i >= 0;)
145 	{
146 		message = new BMessage(kMsgSettings);
147 		message->AddInt32("baudrate", kBaudrateConstants[i]);
148 
149 		char buffer[7];
150 		sprintf(buffer,"%d", kBaudrates[i]);
151 		BMenuItem* item = new BMenuItem(buffer, message);
152 
153 		fBaudrateMenu->AddItem(item);
154 	}
155 
156 	fBaudrateMenu->SetTargetForItems(be_app);
157 
158 	message = new BMessage(kMsgSettings);
159 	message->AddInt32("flowcontrol", B_HARDWARE_CONTROL);
160 	BMenuItem* hardware = new BMenuItem("Hardware", message);
161 
162 	message = new BMessage(kMsgSettings);
163 	message->AddInt32("flowcontrol", B_SOFTWARE_CONTROL);
164 	BMenuItem* software = new BMenuItem("Software", message);
165 
166 	message = new BMessage(kMsgSettings);
167 	message->AddInt32("flowcontrol", B_HARDWARE_CONTROL | B_SOFTWARE_CONTROL);
168 	BMenuItem* both = new BMenuItem("Both", message);
169 
170 	message = new BMessage(kMsgSettings);
171 	message->AddInt32("flowcontrol", 0);
172 	BMenuItem* noFlow = new BMenuItem("None", message);
173 
174 	fFlowcontrolMenu->AddItem(hardware);
175 	fFlowcontrolMenu->AddItem(software);
176 	fFlowcontrolMenu->AddItem(both);
177 	fFlowcontrolMenu->AddItem(noFlow);
178 	fFlowcontrolMenu->SetTargetForItems(be_app);
179 
180 	CenterOnScreen();
181 }
182 
183 
184 
185 SerialWindow::~SerialWindow()
186 {
187 	delete fLogFilePanel;
188 }
189 
190 
191 void SerialWindow::MenusBeginning()
192 {
193 	// remove all items from the menu
194 	fConnectionMenu->RemoveItems(0, fConnectionMenu->CountItems(), true);
195 
196 	// fill it with the (updated) serial port list
197 	BSerialPort serialPort;
198 	int deviceCount = serialPort.CountDevices();
199 	bool connected = false;
200 
201 	for(int i = 0; i < deviceCount; i++)
202 	{
203 		char buffer[256];
204 		serialPort.GetDeviceName(i, buffer, 256);
205 
206 		BMessage* message = new BMessage(kMsgOpenPort);
207 		message->AddString("port name", buffer);
208 		BMenuItem* portItem = new BMenuItem(buffer, message);
209 		portItem->SetTarget(be_app);
210 
211 		const BString& connectedPort = ((SerialApp*)be_app)->GetPort();
212 
213 		if(connectedPort == buffer) {
214 			connected = true;
215 			portItem->SetMarked(true);
216 		}
217 
218 		fConnectionMenu->AddItem(portItem);
219 	}
220 
221 	if (deviceCount > 0) {
222 		fConnectionMenu->AddSeparatorItem();
223 
224 		BMenuItem* disconnect = new BMenuItem("Disconnect",
225 			new BMessage(kMsgOpenPort), 'Z', B_OPTION_KEY);
226 		if(!connected)
227 			disconnect->SetEnabled(false);
228 		fConnectionMenu->AddItem(disconnect);
229 	} else {
230 		BMenuItem* noDevices = new BMenuItem("<no serial port available>", NULL);
231 		noDevices->SetEnabled(false);
232 		fConnectionMenu->AddItem(noDevices);
233 	}
234 }
235 
236 void SerialWindow::MessageReceived(BMessage* message)
237 {
238 	switch(message->what)
239 	{
240 		case kMsgOpenPort:
241 		{
242 			BMenuItem* disconnectMenu;
243 			if(message->FindPointer("source", (void**)&disconnectMenu) == B_OK)
244 				disconnectMenu->SetMarked(false);
245 			be_app->PostMessage(new BMessage(*message));
246 			break;
247 		}
248 		case kMsgDataRead:
249 		{
250 			const char* bytes;
251 			ssize_t length;
252 			message->FindData("data", B_RAW_TYPE, (const void**)&bytes, &length);
253 			fTermView->PushBytes(bytes, length);
254 			break;
255 		}
256 		case kMsgLogfile:
257 		{
258 			// Let's lazy init the file panel
259 			if(fLogFilePanel == NULL) {
260 				fLogFilePanel = new BFilePanel(B_SAVE_PANEL, &be_app_messenger,
261 					NULL, B_FILE_NODE, false);
262 				fLogFilePanel->SetMessage(message);
263 			}
264 			fLogFilePanel->Show();
265 			break;
266 		}
267 		case kMsgSettings:
268 		{
269 			int32 baudrate;
270 			stop_bits stopBits;
271 			data_bits dataBits;
272 			parity_mode parity;
273 			uint32 flowcontrol;
274 
275 			if(message->FindInt32("databits", (int32*)&dataBits) == B_OK)
276 			{
277 				for(int i = 0; i < fDatabitsMenu->CountItems(); i++)
278 				{
279 					BMenuItem* item = fDatabitsMenu->ItemAt(i);
280 					int32 code;
281 					item->Message()->FindInt32("databits", &code);
282 
283 					if(code == dataBits)
284 						item->SetMarked(true);
285 				}
286 			}
287 
288 			if(message->FindInt32("stopbits", (int32*)&stopBits) == B_OK)
289 			{
290 				for(int i = 0; i < fStopbitsMenu->CountItems(); i++)
291 				{
292 					BMenuItem* item = fStopbitsMenu->ItemAt(i);
293 					int32 code;
294 					item->Message()->FindInt32("stopbits", &code);
295 
296 					if(code == stopBits)
297 						item->SetMarked(true);
298 				}
299 			}
300 
301 			if(message->FindInt32("parity", (int32*)&parity) == B_OK)
302 			{
303 				for(int i = 0; i < fParityMenu->CountItems(); i++)
304 				{
305 					BMenuItem* item = fParityMenu->ItemAt(i);
306 					int32 code;
307 					item->Message()->FindInt32("parity", &code);
308 
309 					if(code == parity)
310 						item->SetMarked(true);
311 				}
312 			}
313 
314 			if(message->FindInt32("flowcontrol", (int32*)&flowcontrol) == B_OK)
315 			{
316 				for(int i = 0; i < fFlowcontrolMenu->CountItems(); i++)
317 				{
318 					BMenuItem* item = fFlowcontrolMenu->ItemAt(i);
319 					int32 code;
320 					item->Message()->FindInt32("flowcontrol", &code);
321 
322 					if(code == (int32)flowcontrol)
323 						item->SetMarked(true);
324 				}
325 			}
326 
327 			if(message->FindInt32("baudrate", &baudrate) == B_OK)
328 			{
329 				for(int i = 0; i < fBaudrateMenu->CountItems(); i++)
330 				{
331 					BMenuItem* item = fBaudrateMenu->ItemAt(i);
332 					int32 code;
333 					item->Message()->FindInt32("baudrate", &code);
334 
335 					if(baudrate == code)
336 						item->SetMarked(true);
337 				}
338 			}
339 
340 			break;
341 		}
342 		default:
343 			BWindow::MessageReceived(message);
344 	}
345 }
346