vfs: check userland buffers before reading them.
[haiku.git] / src / apps / serialconnect / SerialWindow.cpp
blob6890c6907079a1ad9a61150ede601383f0484235
1 /*
2 * Copyright 2012-2017, Adrien Destugues, pulkomandy@pulkomandy.tk
3 * Distributed under the terms of the MIT licence.
4 */
7 #include "SerialWindow.h"
9 #include <stdio.h>
11 #include <FilePanel.h>
12 #include <GroupLayout.h>
13 #include <Menu.h>
14 #include <MenuBar.h>
15 #include <MenuItem.h>
16 #include <ScrollView.h>
17 #include <SerialPort.h>
18 #include <StatusBar.h>
20 #include "SerialApp.h"
21 #include "TermView.h"
24 const int SerialWindow::kBaudrates[] = { 50, 75, 110, 134, 150, 200, 300, 600,
25 1200, 1800, 2400, 4800, 9600, 19200, 31250, 38400, 57600, 115200, 230400
29 // The values for these constants are not in the expected order, so we have to
30 // rely on this lookup table if we want to keep the menu items sorted.
31 const int SerialWindow::kBaudrateConstants[] = { B_50_BPS, B_75_BPS, B_110_BPS,
32 B_134_BPS, B_150_BPS, B_200_BPS, B_300_BPS, B_600_BPS, B_1200_BPS,
33 B_1800_BPS, B_2400_BPS, B_4800_BPS, B_9600_BPS, B_19200_BPS, B_31250_BPS,
34 B_38400_BPS, B_57600_BPS, B_115200_BPS, B_230400_BPS
38 const char* SerialWindow::kWindowTitle = "SerialConnect";
41 SerialWindow::SerialWindow()
42 : BWindow(BRect(100, 100, 400, 400), SerialWindow::kWindowTitle,
43 B_DOCUMENT_WINDOW, B_QUIT_ON_WINDOW_CLOSE | B_AUTO_UPDATE_SIZE_LIMITS)
44 , fLogFilePanel(NULL)
45 , fSendFilePanel(NULL)
47 BMenuBar* menuBar = new BMenuBar(Bounds(), "menuBar");
48 menuBar->ResizeToPreferred();
50 BRect r = Bounds();
51 r.top = menuBar->Bounds().bottom + 1;
52 r.right -= B_V_SCROLL_BAR_WIDTH;
53 fTermView = new TermView(r);
54 fTermView->ResizeToPreferred();
56 r = fTermView->Frame();
57 r.left = r.right + 1;
58 r.right = r.left + B_V_SCROLL_BAR_WIDTH;
59 r.top -= 1;
60 r.bottom -= B_H_SCROLL_BAR_HEIGHT - 1;
61 BScrollBar* scrollBar = new BScrollBar(r, "scrollbar", NULL, 0, 0,
62 B_VERTICAL);
64 scrollBar->SetTarget(fTermView);
66 ResizeTo(r.right - 1, r.bottom + B_H_SCROLL_BAR_HEIGHT - 1);
68 r = fTermView->Frame();
69 r.top = r.bottom - 37;
71 fStatusBar = new BStatusBar(r, "file transfer progress", NULL, NULL);
72 fStatusBar->SetResizingMode(B_FOLLOW_BOTTOM | B_FOLLOW_LEFT_RIGHT);
73 fStatusBar->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
74 fStatusBar->Hide();
76 AddChild(menuBar);
77 AddChild(fTermView);
78 AddChild(scrollBar);
79 AddChild(fStatusBar);
81 fConnectionMenu = new BMenu("Connection");
82 fFileMenu = new BMenu("File");
83 BMenu* settingsMenu = new BMenu("Settings");
85 fConnectionMenu->SetRadioMode(true);
87 menuBar->AddItem(fConnectionMenu);
88 menuBar->AddItem(fFileMenu);
89 menuBar->AddItem(settingsMenu);
91 // TODO edit menu - what's in it ? Usual copy/paste would be nice but we
92 // need a way to select text in the window.
93 //BMenu* editMenu = new BMenu("Edit");
94 //menuBar->AddItem(editMenu);
96 BMenuItem* logFile = new BMenuItem("Log to file" B_UTF8_ELLIPSIS,
97 new BMessage(kMsgLogfile));
98 fFileMenu->AddItem(logFile);
100 // The "send" items are disabled initially. They are enabled only once we
101 // are connected to a serial port.
102 BMessage* sendMsg = new BMessage(kMsgSendFile);
103 sendMsg->AddString("protocol", "xmodem");
104 BMenuItem* xmodemSend = new BMenuItem("XModem send" B_UTF8_ELLIPSIS,
105 sendMsg);
106 fFileMenu->AddItem(xmodemSend);
107 xmodemSend->SetEnabled(false);
109 BMenuItem* rawSend = new BMenuItem("Raw send" B_UTF8_ELLIPSIS,
110 new BMessage(kMsgSendFile));
111 fFileMenu->AddItem(rawSend);
112 rawSend->SetEnabled(false);
114 #if 0
115 // TODO implement this
116 BMenuItem* xmodemReceive = new BMenuItem(
117 "X/Y/Zmodem receive" B_UTF8_ELLIPSIS, NULL);
118 fFileMenu->AddItem(xmodemReceive);
119 xmodemReceive->SetEnabled(false);
120 #endif
122 // Configuring all this by menus may be a bit unhandy. Make a setting
123 // window instead ?
124 fBaudrateMenu = new BMenu("Baud rate");
125 fBaudrateMenu->SetRadioMode(true);
126 settingsMenu->AddItem(fBaudrateMenu);
128 fParityMenu = new BMenu("Parity");
129 fParityMenu->SetRadioMode(true);
130 settingsMenu->AddItem(fParityMenu);
132 fStopbitsMenu = new BMenu("Stop bits");
133 fStopbitsMenu->SetRadioMode(true);
134 settingsMenu->AddItem(fStopbitsMenu);
136 fFlowcontrolMenu = new BMenu("Flow control");
137 fFlowcontrolMenu->SetRadioMode(true);
138 settingsMenu->AddItem(fFlowcontrolMenu);
140 fDatabitsMenu = new BMenu("Data bits");
141 fDatabitsMenu->SetRadioMode(true);
142 settingsMenu->AddItem(fDatabitsMenu);
144 fLineTerminatorMenu = new BMenu("Line terminator");
145 fLineTerminatorMenu->SetRadioMode(true);
146 settingsMenu->AddItem(fLineTerminatorMenu);
148 BMessage* message = new BMessage(kMsgSettings);
149 message->AddInt32("parity", B_NO_PARITY);
150 BMenuItem* parityNone = new BMenuItem("None", message);
152 message = new BMessage(kMsgSettings);
153 message->AddInt32("parity", B_ODD_PARITY);
154 BMenuItem* parityOdd = new BMenuItem("Odd", message);
156 message = new BMessage(kMsgSettings);
157 message->AddInt32("parity", B_EVEN_PARITY);
158 BMenuItem* parityEven = new BMenuItem("Even", message);
160 fParityMenu->AddItem(parityNone);
161 fParityMenu->AddItem(parityOdd);
162 fParityMenu->AddItem(parityEven);
163 fParityMenu->SetTargetForItems(be_app);
165 message = new BMessage(kMsgSettings);
166 message->AddInt32("databits", B_DATA_BITS_7);
167 BMenuItem* data7 = new BMenuItem("7", message);
169 message = new BMessage(kMsgSettings);
170 message->AddInt32("databits", B_DATA_BITS_8);
171 BMenuItem* data8 = new BMenuItem("8", message);
173 fDatabitsMenu->AddItem(data7);
174 fDatabitsMenu->AddItem(data8);
175 fDatabitsMenu->SetTargetForItems(be_app);
177 message = new BMessage(kMsgSettings);
178 message->AddInt32("stopbits", B_STOP_BITS_1);
179 BMenuItem* stop1 = new BMenuItem("1", message);
181 message = new BMessage(kMsgSettings);
182 message->AddInt32("stopbits", B_STOP_BITS_2);
183 BMenuItem* stop2 = new BMenuItem("2", message);
185 fStopbitsMenu->AddItem(stop1);
186 fStopbitsMenu->AddItem(stop2);
187 fStopbitsMenu->SetTargetForItems(be_app);
189 // Loop backwards to add fastest rates at top of menu
190 for (int i = sizeof(kBaudrates) / sizeof(kBaudrates[0]); --i >= 0;)
192 message = new BMessage(kMsgSettings);
193 message->AddInt32("baudrate", kBaudrateConstants[i]);
195 char buffer[7];
196 sprintf(buffer, "%d", kBaudrates[i]);
197 BMenuItem* item = new BMenuItem(buffer, message);
199 fBaudrateMenu->AddItem(item);
202 message = new BMessage(kMsgCustomBaudrate);
203 BMenuItem* custom = new BMenuItem("custom" B_UTF8_ELLIPSIS, message);
204 fBaudrateMenu->AddItem(custom);
206 fBaudrateMenu->SetTargetForItems(be_app);
208 message = new BMessage(kMsgSettings);
209 message->AddInt32("flowcontrol", B_HARDWARE_CONTROL);
210 BMenuItem* hardware = new BMenuItem("Hardware", message);
212 message = new BMessage(kMsgSettings);
213 message->AddInt32("flowcontrol", B_SOFTWARE_CONTROL);
214 BMenuItem* software = new BMenuItem("Software", message);
216 message = new BMessage(kMsgSettings);
217 message->AddInt32("flowcontrol", B_HARDWARE_CONTROL | B_SOFTWARE_CONTROL);
218 BMenuItem* both = new BMenuItem("Both", message);
220 message = new BMessage(kMsgSettings);
221 message->AddInt32("flowcontrol", 0);
222 BMenuItem* noFlow = new BMenuItem("None", message);
224 fFlowcontrolMenu->AddItem(hardware);
225 fFlowcontrolMenu->AddItem(software);
226 fFlowcontrolMenu->AddItem(both);
227 fFlowcontrolMenu->AddItem(noFlow);
228 fFlowcontrolMenu->SetTargetForItems(be_app);
230 message = new BMessage(kMsgSettings);
231 message->AddString("terminator", "\n");
232 BMenuItem* lf = new BMenuItem("LF (\\n)", message);
234 message = new BMessage(kMsgSettings);
235 message->AddString("terminator", "\r");
236 BMenuItem* cr = new BMenuItem("CR (\\r)", message);
238 message = new BMessage(kMsgSettings);
239 message->AddString("terminator", "\r\n");
240 BMenuItem* crlf = new BMenuItem("CR/LF (\\r\\n)", message);
242 fLineTerminatorMenu->AddItem(lf);
243 fLineTerminatorMenu->AddItem(cr);
244 fLineTerminatorMenu->AddItem(crlf);
246 CenterOnScreen();
250 SerialWindow::~SerialWindow()
252 delete fLogFilePanel;
253 delete fSendFilePanel;
257 void SerialWindow::MenusBeginning()
259 // remove all items from the menu
260 fConnectionMenu->RemoveItems(0, fConnectionMenu->CountItems(), true);
262 // fill it with the (updated) serial port list
263 BSerialPort serialPort;
264 int deviceCount = serialPort.CountDevices();
265 bool connected = false;
267 for (int i = 0; i < deviceCount; i++)
269 char buffer[256];
270 serialPort.GetDeviceName(i, buffer, 256);
272 BMessage* message = new BMessage(kMsgOpenPort);
273 message->AddString("port name", buffer);
274 BMenuItem* portItem = new BMenuItem(buffer, message);
275 portItem->SetTarget(be_app);
277 const BString& connectedPort = ((SerialApp*)be_app)->GetPort();
279 if (connectedPort == buffer) {
280 connected = true;
281 portItem->SetMarked(true);
284 fConnectionMenu->AddItem(portItem);
287 if (deviceCount > 0) {
288 fConnectionMenu->AddSeparatorItem();
290 BMenuItem* disconnect = new BMenuItem("Disconnect",
291 new BMessage(kMsgOpenPort), 'Z', B_OPTION_KEY);
292 if (!connected)
293 disconnect->SetEnabled(false);
294 disconnect->SetTarget(be_app);
295 fConnectionMenu->AddItem(disconnect);
296 } else {
297 BMenuItem* noDevices = new BMenuItem("<no serial port available>", NULL);
298 noDevices->SetEnabled(false);
299 fConnectionMenu->AddItem(noDevices);
304 void SerialWindow::MessageReceived(BMessage* message)
306 switch (message->what)
308 case kMsgOpenPort:
310 BString path;
311 bool open = (message->FindString("port name", &path) == B_OK);
312 int i = 1; // Skip "log to file", which woeks even when offline.
313 BMenuItem* item;
314 while((item = fFileMenu->ItemAt(i++)))
316 item->SetEnabled(open);
318 return;
320 case kMsgDataRead:
322 const char* bytes;
323 ssize_t length;
324 if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
325 &length) == B_OK)
326 fTermView->PushBytes(bytes, length);
327 return;
329 case kMsgLogfile:
331 // Let's lazy init the file panel
332 if (fLogFilePanel == NULL) {
333 fLogFilePanel = new BFilePanel(B_SAVE_PANEL,
334 &be_app_messenger, NULL, B_FILE_NODE, false);
335 fLogFilePanel->SetMessage(message);
337 fLogFilePanel->Show();
338 return;
340 case kMsgSendFile:
342 // Let's lazy init the file panel
343 if (fSendFilePanel == NULL) {
344 fSendFilePanel = new BFilePanel(B_OPEN_PANEL,
345 &be_app_messenger, NULL, B_FILE_NODE, false);
347 fSendFilePanel->SetMessage(message);
348 fSendFilePanel->Show();
349 return;
351 case kMsgSettings:
353 int32 baudrate;
354 stop_bits stopBits;
355 data_bits dataBits;
356 parity_mode parity;
357 uint32 flowcontrol;
358 BString terminator;
360 if (message->FindInt32("databits", (int32*)&dataBits) == B_OK) {
361 for (int i = 0; i < fDatabitsMenu->CountItems(); i++) {
362 BMenuItem* item = fDatabitsMenu->ItemAt(i);
363 int32 code;
364 item->Message()->FindInt32("databits", &code);
366 if (code == dataBits)
367 item->SetMarked(true);
371 if (message->FindInt32("stopbits", (int32*)&stopBits) == B_OK) {
372 for (int i = 0; i < fStopbitsMenu->CountItems(); i++) {
373 BMenuItem* item = fStopbitsMenu->ItemAt(i);
374 int32 code;
375 item->Message()->FindInt32("stopbits", &code);
377 if (code == stopBits)
378 item->SetMarked(true);
382 if (message->FindInt32("parity", (int32*)&parity) == B_OK)
384 for (int i = 0; i < fParityMenu->CountItems(); i++) {
385 BMenuItem* item = fParityMenu->ItemAt(i);
386 int32 code;
387 item->Message()->FindInt32("parity", &code);
389 if (code == parity)
390 item->SetMarked(true);
394 if (message->FindInt32("flowcontrol", (int32*)&flowcontrol)
395 == B_OK) {
396 for (int i = 0; i < fFlowcontrolMenu->CountItems(); i++) {
397 BMenuItem* item = fFlowcontrolMenu->ItemAt(i);
398 int32 code;
399 item->Message()->FindInt32("flowcontrol", &code);
401 if (code == (int32)flowcontrol)
402 item->SetMarked(true);
406 if (message->FindInt32("baudrate", &baudrate) == B_OK) {
407 int i;
408 BMenuItem* item = NULL;
409 for (i = 0; i < fBaudrateMenu->CountItems(); i++) {
410 item = fBaudrateMenu->ItemAt(i);
411 int32 code = 0;
412 item->Message()->FindInt32("baudrate", &code);
414 if (baudrate == code) {
415 item->SetMarked(true);
416 break;
420 if (i == fBaudrateMenu->CountItems() && item != NULL) {
421 // Rate was not found, mark it as "custom".
422 // Since that is the last item in the menu, we still point
423 // to it.
424 item->SetMarked(true);
425 item->Message()->SetInt32("baudrate", baudrate);
429 if (message->FindString("terminator", &terminator) == B_OK) {
430 fTermView->SetLineTerminator(terminator);
431 for (int i = 0; i < fLineTerminatorMenu->CountItems(); i++) {
432 BMenuItem* item = fLineTerminatorMenu->ItemAt(i);
433 BString code;
434 item->Message()->FindString("terminator", &code);
436 if (terminator == code)
437 item->SetMarked(true);
441 return;
443 case kMsgProgress:
445 // File transfer progress
446 int32 pos = message->FindInt32("pos");
447 int32 size = message->FindInt32("size");
448 BString label = message->FindString("info");
450 if (pos >= size) {
451 if (!fStatusBar->IsHidden()) {
452 fStatusBar->Hide();
453 fTermView->ResizeBy(0, fStatusBar->Bounds().Height() - 1);
455 } else {
456 BString text;
457 text.SetToFormat("%" B_PRId32 "/%" B_PRId32, pos, size);
458 fStatusBar->SetMaxValue(size);
459 fStatusBar->SetTo(pos, label, text);
460 if (fStatusBar->IsHidden()) {
461 fStatusBar->Show();
462 fTermView->ResizeBy(0, -(fStatusBar->Bounds().Height() - 1));
465 return;
469 BWindow::MessageReceived(message);