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