1 /* 2 * Copyright 2012-2014, 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 120 BMessage* message = new BMessage(kMsgSettings); 121 message->AddInt32("parity", B_NO_PARITY); 122 BMenuItem* parityNone = new BMenuItem("None", message); 123 124 message = new BMessage(kMsgSettings); 125 message->AddInt32("parity", B_ODD_PARITY); 126 BMenuItem* parityOdd = new BMenuItem("Odd", message); 127 128 message = new BMessage(kMsgSettings); 129 message->AddInt32("parity", B_EVEN_PARITY); 130 BMenuItem* parityEven = new BMenuItem("Even", message); 131 132 fParityMenu->AddItem(parityNone); 133 fParityMenu->AddItem(parityOdd); 134 fParityMenu->AddItem(parityEven); 135 fParityMenu->SetTargetForItems(be_app); 136 137 message = new BMessage(kMsgSettings); 138 message->AddInt32("databits", B_DATA_BITS_7); 139 BMenuItem* data7 = new BMenuItem("7", message); 140 141 message = new BMessage(kMsgSettings); 142 message->AddInt32("databits", B_DATA_BITS_8); 143 BMenuItem* data8 = new BMenuItem("8", message); 144 145 fDatabitsMenu->AddItem(data7); 146 fDatabitsMenu->AddItem(data8); 147 fDatabitsMenu->SetTargetForItems(be_app); 148 149 message = new BMessage(kMsgSettings); 150 message->AddInt32("stopbits", B_STOP_BITS_1); 151 BMenuItem* stop1 = new BMenuItem("1", message); 152 153 message = new BMessage(kMsgSettings); 154 message->AddInt32("stopbits", B_STOP_BITS_2); 155 BMenuItem* stop2 = new BMenuItem("2", message); 156 157 fStopbitsMenu->AddItem(stop1); 158 fStopbitsMenu->AddItem(stop2); 159 fStopbitsMenu->SetTargetForItems(be_app); 160 161 // Loop backwards to add fastest rates at top of menu 162 for (int i = sizeof(kBaudrates) / sizeof(char*); --i >= 0;) 163 { 164 message = new BMessage(kMsgSettings); 165 message->AddInt32("baudrate", kBaudrateConstants[i]); 166 167 char buffer[7]; 168 sprintf(buffer, "%d", kBaudrates[i]); 169 BMenuItem* item = new BMenuItem(buffer, message); 170 171 fBaudrateMenu->AddItem(item); 172 } 173 174 fBaudrateMenu->SetTargetForItems(be_app); 175 176 message = new BMessage(kMsgSettings); 177 message->AddInt32("flowcontrol", B_HARDWARE_CONTROL); 178 BMenuItem* hardware = new BMenuItem("Hardware", message); 179 180 message = new BMessage(kMsgSettings); 181 message->AddInt32("flowcontrol", B_SOFTWARE_CONTROL); 182 BMenuItem* software = new BMenuItem("Software", message); 183 184 message = new BMessage(kMsgSettings); 185 message->AddInt32("flowcontrol", B_HARDWARE_CONTROL | B_SOFTWARE_CONTROL); 186 BMenuItem* both = new BMenuItem("Both", message); 187 188 message = new BMessage(kMsgSettings); 189 message->AddInt32("flowcontrol", 0); 190 BMenuItem* noFlow = new BMenuItem("None", message); 191 192 fFlowcontrolMenu->AddItem(hardware); 193 fFlowcontrolMenu->AddItem(software); 194 fFlowcontrolMenu->AddItem(both); 195 fFlowcontrolMenu->AddItem(noFlow); 196 fFlowcontrolMenu->SetTargetForItems(be_app); 197 198 CenterOnScreen(); 199 } 200 201 202 SerialWindow::~SerialWindow() 203 { 204 delete fLogFilePanel; 205 } 206 207 208 void SerialWindow::MenusBeginning() 209 { 210 // remove all items from the menu 211 fConnectionMenu->RemoveItems(0, fConnectionMenu->CountItems(), true); 212 213 // fill it with the (updated) serial port list 214 BSerialPort serialPort; 215 int deviceCount = serialPort.CountDevices(); 216 bool connected = false; 217 218 for (int i = 0; i < deviceCount; i++) 219 { 220 char buffer[256]; 221 serialPort.GetDeviceName(i, buffer, 256); 222 223 BMessage* message = new BMessage(kMsgOpenPort); 224 message->AddString("port name", buffer); 225 BMenuItem* portItem = new BMenuItem(buffer, message); 226 portItem->SetTarget(be_app); 227 228 const BString& connectedPort = ((SerialApp*)be_app)->GetPort(); 229 230 if (connectedPort == buffer) { 231 connected = true; 232 portItem->SetMarked(true); 233 } 234 235 fConnectionMenu->AddItem(portItem); 236 } 237 238 if (deviceCount > 0) { 239 fConnectionMenu->AddSeparatorItem(); 240 241 BMenuItem* disconnect = new BMenuItem("Disconnect", 242 new BMessage(kMsgOpenPort), 'Z', B_OPTION_KEY); 243 if (!connected) 244 disconnect->SetEnabled(false); 245 disconnect->SetTarget(be_app); 246 fConnectionMenu->AddItem(disconnect); 247 } else { 248 BMenuItem* noDevices = new BMenuItem("<no serial port available>", NULL); 249 noDevices->SetEnabled(false); 250 fConnectionMenu->AddItem(noDevices); 251 } 252 } 253 254 255 void SerialWindow::MessageReceived(BMessage* message) 256 { 257 switch (message->what) 258 { 259 case kMsgDataRead: 260 { 261 const char* bytes; 262 ssize_t length; 263 if (message->FindData("data", B_RAW_TYPE, (const void**)&bytes, 264 &length) == B_OK) 265 fTermView->PushBytes(bytes, length); 266 return; 267 } 268 case kMsgLogfile: 269 { 270 // Let's lazy init the file panel 271 if (fLogFilePanel == NULL) { 272 fLogFilePanel = new BFilePanel(B_SAVE_PANEL, &be_app_messenger, 273 NULL, B_FILE_NODE, false); 274 fLogFilePanel->SetMessage(message); 275 } 276 fLogFilePanel->Show(); 277 return; 278 } 279 case kMsgSettings: 280 { 281 int32 baudrate; 282 stop_bits stopBits; 283 data_bits dataBits; 284 parity_mode parity; 285 uint32 flowcontrol; 286 287 if (message->FindInt32("databits", (int32*)&dataBits) == B_OK) { 288 for (int i = 0; i < fDatabitsMenu->CountItems(); i++) { 289 BMenuItem* item = fDatabitsMenu->ItemAt(i); 290 int32 code; 291 item->Message()->FindInt32("databits", &code); 292 293 if (code == dataBits) 294 item->SetMarked(true); 295 } 296 } 297 298 if (message->FindInt32("stopbits", (int32*)&stopBits) == B_OK) { 299 for (int i = 0; i < fStopbitsMenu->CountItems(); i++) { 300 BMenuItem* item = fStopbitsMenu->ItemAt(i); 301 int32 code; 302 item->Message()->FindInt32("stopbits", &code); 303 304 if (code == stopBits) 305 item->SetMarked(true); 306 } 307 } 308 309 if (message->FindInt32("parity", (int32*)&parity) == B_OK) 310 { 311 for (int i = 0; i < fParityMenu->CountItems(); i++) { 312 BMenuItem* item = fParityMenu->ItemAt(i); 313 int32 code; 314 item->Message()->FindInt32("parity", &code); 315 316 if (code == parity) 317 item->SetMarked(true); 318 } 319 } 320 321 if (message->FindInt32("flowcontrol", (int32*)&flowcontrol) 322 == B_OK) { 323 for (int i = 0; i < fFlowcontrolMenu->CountItems(); i++) { 324 BMenuItem* item = fFlowcontrolMenu->ItemAt(i); 325 int32 code; 326 item->Message()->FindInt32("flowcontrol", &code); 327 328 if (code == (int32)flowcontrol) 329 item->SetMarked(true); 330 } 331 } 332 333 if (message->FindInt32("baudrate", &baudrate) == B_OK) { 334 for (int i = 0; i < fBaudrateMenu->CountItems(); i++) { 335 BMenuItem* item = fBaudrateMenu->ItemAt(i); 336 int32 code; 337 item->Message()->FindInt32("baudrate", &code); 338 339 if (baudrate == code) 340 item->SetMarked(true); 341 } 342 } 343 344 return; 345 } 346 } 347 348 BWindow::MessageReceived(message); 349 } 350