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