From ccc6b9be5b6527d14bb35b6ce57dcb422c0422f0 Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Wed, 20 Nov 2013 22:34:16 +0100 Subject: [PATCH] Make SerialConnect more complete * Mark the current connected device, or disable "Disconnect" menu when there is no connection. * Save and restore serial port settings * Improve drawing code: make sure the border around the termview is repainted, and do not leave a 1px space between lines unpainted. --- src/apps/serialconnect/SerialApp.cpp | 64 +++++++- src/apps/serialconnect/SerialApp.h | 9 ++ src/apps/serialconnect/SerialWindow.cpp | 185 +++++++++++++++++------- src/apps/serialconnect/SerialWindow.h | 6 + src/apps/serialconnect/TermView.cpp | 86 ++++++----- 5 files changed, 256 insertions(+), 94 deletions(-) diff --git a/src/apps/serialconnect/SerialApp.cpp b/src/apps/serialconnect/SerialApp.cpp index fccbc6b44c..8b8c167d64 100644 --- a/src/apps/serialconnect/SerialApp.cpp +++ b/src/apps/serialconnect/SerialApp.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "SerialWindow.h" @@ -37,6 +39,7 @@ SerialApp::~SerialApp() void SerialApp::ReadyToRun() { + LoadSettings(); fWindow->Show(); } @@ -47,10 +50,9 @@ void SerialApp::MessageReceived(BMessage* message) { case kMsgOpenPort: { - const char* portName; - if(message->FindString("port name", &portName) == B_OK) + if(message->FindString("port name", &fPortPath) == B_OK) { - fSerialPort.Open(portName); + fSerialPort.Open(fPortPath); release_sem(fSerialLock); } else { fSerialPort.Close(); @@ -207,6 +209,62 @@ void SerialApp::MessageReceived(BMessage* message) } +bool SerialApp::QuitRequested() +{ + if(BApplication::QuitRequested()) { + SaveSettings(); + return true; + } + return false; +} + + +const BString& SerialApp::GetPort() +{ + return fPortPath; +} + + +void SerialApp::LoadSettings() +{ + BPath path; + find_directory(B_USER_SETTINGS_DIRECTORY, &path); + path.Append("SerialConnect"); + + BFile file(path.Path(), B_READ_ONLY); + BMessage message(kMsgSettings); + if(message.Unflatten(&file) != B_OK) + { + message.AddInt32("parity", fSerialPort.ParityMode()); + message.AddInt32("databits", fSerialPort.DataBits()); + message.AddInt32("stopbits", fSerialPort.StopBits()); + message.AddInt32("baudrate", fSerialPort.DataRate()); + message.AddInt32("flowcontrol", fSerialPort.FlowControl()); + } + + be_app->PostMessage(&message); + fWindow->PostMessage(&message); +} + + +void SerialApp::SaveSettings() +{ + BMessage message(kMsgSettings); + message.AddInt32("parity", fSerialPort.ParityMode()); + message.AddInt32("databits", fSerialPort.DataBits()); + message.AddInt32("stopbits", fSerialPort.StopBits()); + message.AddInt32("baudrate", fSerialPort.DataRate()); + message.AddInt32("flowcontrol", fSerialPort.FlowControl()); + + BPath path; + find_directory(B_USER_SETTINGS_DIRECTORY, &path); + path.Append("SerialConnect"); + + BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE); + message.Flatten(&file); +} + + /* static */ status_t SerialApp::PollSerial(void*) { diff --git a/src/apps/serialconnect/SerialApp.h b/src/apps/serialconnect/SerialApp.h index 5aa79670cc..33bee0b44a 100644 --- a/src/apps/serialconnect/SerialApp.h +++ b/src/apps/serialconnect/SerialApp.h @@ -10,6 +10,7 @@ #include #include +#include class BFile; @@ -23,12 +24,20 @@ class SerialApp: public BApplication ~SerialApp(); void ReadyToRun(); void MessageReceived(BMessage* message); + bool QuitRequested(); + const BString& GetPort(); + + private: + void LoadSettings(); + void SaveSettings(); + private: BSerialPort fSerialPort; sem_id fSerialLock; SerialWindow* fWindow; BFile* fLogFile; + BString fPortPath; static status_t PollSerial(void*); diff --git a/src/apps/serialconnect/SerialWindow.cpp b/src/apps/serialconnect/SerialWindow.cpp index ccc099c5e4..cbd7b67e4a 100644 --- a/src/apps/serialconnect/SerialWindow.cpp +++ b/src/apps/serialconnect/SerialWindow.cpp @@ -19,6 +19,14 @@ #include "TermView.h" +const int SerialWindow::kBaudrates[] = { 50, 75, 110, 134, 150, 200, 300, 600, + 1200, 1800, 2400, 4800, 9600, 19200, 31250, 38400, 57600, 115200, 230400 +}; + + +const char* SerialWindow::kWindowTitle = "SerialConnect"; + + SerialWindow::SerialWindow() : BWindow(BRect(100, 100, 400, 400), SerialWindow::kWindowTitle, B_DOCUMENT_WINDOW, B_QUIT_ON_WINDOW_CLOSE | B_AUTO_UPDATE_SIZE_LIMITS) @@ -49,34 +57,37 @@ SerialWindow::SerialWindow() BMenuItem* logFile = new BMenuItem("Log to file" B_UTF8_ELLIPSIS, new BMessage(kMsgLogfile)); fileMenu->AddItem(logFile); +#if 0 + // TODO implement these BMenuItem* xmodemSend = new BMenuItem("X/Y/ZModem send" B_UTF8_ELLIPSIS, NULL); fileMenu->AddItem(xmodemSend); BMenuItem* xmodemReceive = new BMenuItem( "X/Y/Zmodem receive" B_UTF8_ELLIPSIS, NULL); fileMenu->AddItem(xmodemReceive); +#endif // Configuring all this by menus may be a bit unhandy. Make a setting // window instead ? - BMenu* baudRate = new BMenu("Baud rate"); - baudRate->SetRadioMode(true); - settingsMenu->AddItem(baudRate); + fBaudrateMenu = new BMenu("Baud rate"); + fBaudrateMenu->SetRadioMode(true); + settingsMenu->AddItem(fBaudrateMenu); - BMenu* parity = new BMenu("Parity"); - parity->SetRadioMode(true); - settingsMenu->AddItem(parity); + fParityMenu = new BMenu("Parity"); + fParityMenu->SetRadioMode(true); + settingsMenu->AddItem(fParityMenu); - BMenu* stopBits = new BMenu("Stop bits"); - stopBits->SetRadioMode(true); - settingsMenu->AddItem(stopBits); + fStopbitsMenu = new BMenu("Stop bits"); + fStopbitsMenu->SetRadioMode(true); + settingsMenu->AddItem(fStopbitsMenu); - BMenu* flowControl = new BMenu("Flow control"); - flowControl->SetRadioMode(true); - settingsMenu->AddItem(flowControl); + fFlowcontrolMenu = new BMenu("Flow control"); + fFlowcontrolMenu->SetRadioMode(true); + settingsMenu->AddItem(fFlowcontrolMenu); - BMenu* dataBits = new BMenu("Data bits"); - dataBits->SetRadioMode(true); - settingsMenu->AddItem(dataBits); + fDatabitsMenu = new BMenu("Data bits"); + fDatabitsMenu->SetRadioMode(true); + settingsMenu->AddItem(fDatabitsMenu); BMessage* message = new BMessage(kMsgSettings); @@ -90,12 +101,11 @@ SerialWindow::SerialWindow() message = new BMessage(kMsgSettings); message->AddInt32("parity", B_EVEN_PARITY); BMenuItem* parityEven = new BMenuItem("Even", message); - parityNone->SetMarked(true); - parity->AddItem(parityNone); - parity->AddItem(parityOdd); - parity->AddItem(parityEven); - parity->SetTargetForItems(be_app); + fParityMenu->AddItem(parityNone); + fParityMenu->AddItem(parityOdd); + fParityMenu->AddItem(parityEven); + fParityMenu->SetTargetForItems(be_app); message = new BMessage(kMsgSettings); message->AddInt32("databits", B_DATA_BITS_7); @@ -104,52 +114,41 @@ SerialWindow::SerialWindow() message = new BMessage(kMsgSettings); message->AddInt32("databits", B_DATA_BITS_8); BMenuItem* data8 = new BMenuItem("8", message); - data8->SetMarked(true); - dataBits->AddItem(data7); - dataBits->AddItem(data8); - dataBits->SetTargetForItems(be_app); + fDatabitsMenu->AddItem(data7); + fDatabitsMenu->AddItem(data8); + fDatabitsMenu->SetTargetForItems(be_app); message = new BMessage(kMsgSettings); message->AddInt32("stopbits", B_STOP_BITS_1); - BMenuItem* stop1 = new BMenuItem("1", NULL); + BMenuItem* stop1 = new BMenuItem("1", message); message = new BMessage(kMsgSettings); message->AddInt32("stopbits", B_STOP_BITS_2); - BMenuItem* stop2 = new BMenuItem("2", NULL); - stop1->SetMarked(true); + BMenuItem* stop2 = new BMenuItem("2", message); - stopBits->AddItem(stop1); - stopBits->AddItem(stop2); - stopBits->SetTargetForItems(be_app); - - static const int baudrates[] = { 50, 75, 110, 134, 150, 200, 300, 600, - 1200, 1800, 2400, 4800, 9600, 19200, 31250, 38400, 57600, 115200, - 230400 - }; + fStopbitsMenu->AddItem(stop1); + fStopbitsMenu->AddItem(stop2); + fStopbitsMenu->SetTargetForItems(be_app); // Loop backwards to add fastest rates at top of menu - for (int i = sizeof(baudrates) / sizeof(char*); --i >= 0;) + for (int i = sizeof(kBaudrates) / sizeof(char*); --i >= 0;) { message = new BMessage(kMsgSettings); - message->AddInt32("baudrate", baudrates[i]); + message->AddInt32("baudrate", kBaudrates[i]); char buffer[7]; - sprintf(buffer,"%d", baudrates[i]); + sprintf(buffer,"%d", kBaudrates[i]); BMenuItem* item = new BMenuItem(buffer, message); - if (baudrates[i] == 19200) - item->SetMarked(true); - - baudRate->AddItem(item); + fBaudrateMenu->AddItem(item); } - baudRate->SetTargetForItems(be_app); + fBaudrateMenu->SetTargetForItems(be_app); message = new BMessage(kMsgSettings); message->AddInt32("flowcontrol", B_HARDWARE_CONTROL); BMenuItem* hardware = new BMenuItem("Hardware", message); - hardware->SetMarked(true); message = new BMessage(kMsgSettings); message->AddInt32("flowcontrol", B_SOFTWARE_CONTROL); @@ -163,11 +162,11 @@ SerialWindow::SerialWindow() message->AddInt32("flowcontrol", 0); BMenuItem* noFlow = new BMenuItem("None", message); - flowControl->AddItem(hardware); - flowControl->AddItem(software); - flowControl->AddItem(both); - flowControl->AddItem(noFlow); - flowControl->SetTargetForItems(be_app); + fFlowcontrolMenu->AddItem(hardware); + fFlowcontrolMenu->AddItem(software); + fFlowcontrolMenu->AddItem(both); + fFlowcontrolMenu->AddItem(noFlow); + fFlowcontrolMenu->SetTargetForItems(be_app); CenterOnScreen(); } @@ -188,6 +187,7 @@ void SerialWindow::MenusBeginning() // fill it with the (updated) serial port list BSerialPort serialPort; int deviceCount = serialPort.CountDevices(); + bool connected = false; for(int i = 0; i < deviceCount; i++) { @@ -199,6 +199,13 @@ void SerialWindow::MenusBeginning() BMenuItem* portItem = new BMenuItem(buffer, message); portItem->SetTarget(be_app); + const BString& connectedPort = ((SerialApp*)be_app)->GetPort(); + + if(connectedPort == buffer) { + connected = true; + portItem->SetMarked(true); + } + fConnectionMenu->AddItem(portItem); } @@ -207,6 +214,8 @@ void SerialWindow::MenusBeginning() BMenuItem* disconnect = new BMenuItem("Disconnect", new BMessage(kMsgOpenPort), 'Z', B_OPTION_KEY); + if(!connected) + disconnect->SetEnabled(false); fConnectionMenu->AddItem(disconnect); } else { BMenuItem* noDevices = new BMenuItem("", NULL); @@ -246,10 +255,82 @@ void SerialWindow::MessageReceived(BMessage* message) fLogFilePanel->Show(); break; } + case kMsgSettings: + { + int32 baudrate; + stop_bits stopBits; + data_bits dataBits; + parity_mode parity; + uint32 flowcontrol; + + if(message->FindInt32("databits", (int32*)&dataBits) == B_OK) + { + for(int i = 0; i < fDatabitsMenu->CountItems(); i++) + { + BMenuItem* item = fDatabitsMenu->ItemAt(i); + int32 code; + item->Message()->FindInt32("databits", &code); + + if(code == dataBits) + item->SetMarked(true); + } + } + + if(message->FindInt32("stopbits", (int32*)&stopBits) == B_OK) + { + for(int i = 0; i < fStopbitsMenu->CountItems(); i++) + { + BMenuItem* item = fStopbitsMenu->ItemAt(i); + int32 code; + item->Message()->FindInt32("stopbits", &code); + + if(code == stopBits) + item->SetMarked(true); + } + } + + if(message->FindInt32("parity", (int32*)&parity) == B_OK) + { + for(int i = 0; i < fParityMenu->CountItems(); i++) + { + BMenuItem* item = fParityMenu->ItemAt(i); + int32 code; + item->Message()->FindInt32("parity", &code); + + if(code == parity) + item->SetMarked(true); + } + } + + if(message->FindInt32("flowcontrol", (int32*)&flowcontrol) == B_OK) + { + for(int i = 0; i < fFlowcontrolMenu->CountItems(); i++) + { + BMenuItem* item = fFlowcontrolMenu->ItemAt(i); + int32 code; + item->Message()->FindInt32("flowcontrol", &code); + + if(code == (int32)flowcontrol) + item->SetMarked(true); + } + } + + if(message->FindInt32("baudrate", &baudrate) == B_OK) + { + for(int i = 0; i < fBaudrateMenu->CountItems(); i++) + { + BMenuItem* item = fBaudrateMenu->ItemAt(i); + int32 code; + item->Message()->FindInt32("baudrate", &code); + + if(code == kBaudrates[baudrate]) + item->SetMarked(true); + } + } + + break; + } default: BWindow::MessageReceived(message); } } - - -const char* SerialWindow::kWindowTitle = "SerialConnect"; diff --git a/src/apps/serialconnect/SerialWindow.h b/src/apps/serialconnect/SerialWindow.h index 10591f6cbe..df35443fe2 100644 --- a/src/apps/serialconnect/SerialWindow.h +++ b/src/apps/serialconnect/SerialWindow.h @@ -23,7 +23,13 @@ class SerialWindow: public BWindow private: TermView* fTermView; BMenu* fConnectionMenu; + BMenu* fDatabitsMenu; + BMenu* fStopbitsMenu; + BMenu* fParityMenu; + BMenu* fFlowcontrolMenu; + BMenu* fBaudrateMenu; BFilePanel* fLogFilePanel; + static const int kBaudrates[]; static const char* kWindowTitle; }; diff --git a/src/apps/serialconnect/TermView.cpp b/src/apps/serialconnect/TermView.cpp index 60acc832c0..4979a791ce 100644 --- a/src/apps/serialconnect/TermView.cpp +++ b/src/apps/serialconnect/TermView.cpp @@ -19,19 +19,19 @@ TermView::TermView() : BView("TermView", B_WILL_DRAW | B_FRAME_EVENTS) { + SetFont(be_fixed_font); + font_height height; GetFontHeight(&height); fFontHeight = height.ascent + height.descent + height.leading; fFontWidth = be_fixed_font->StringWidth("X"); fTerm = vterm_new(kDefaultHeight, kDefaultWidth); - vterm_parser_set_utf8(fTerm, 1); - fTermScreen = vterm_obtain_screen(fTerm); vterm_screen_set_callbacks(fTermScreen, &sScreenCallbacks, this); vterm_screen_reset(fTermScreen, 1); - SetFont(be_fixed_font); + vterm_parser_set_utf8(fTerm, 1); } @@ -61,52 +61,60 @@ void TermView::Draw(BRect updateRect) for (pos.row = updatedChars.start_row; pos.row <= updatedChars.end_row; pos.row++) { float x = updatedChars.start_col * fFontWidth + kBorderSpacing; - float y = pos.row * fFontHeight + height.ascent + kBorderSpacing; + float y = pos.row * fFontHeight + height.ascent + kBorderSpacing + 1; MovePenTo(x, y); for (pos.col = updatedChars.start_col; pos.col <= updatedChars.end_col;) { + VTermScreenCell cell; + if (pos.col < 0 || pos.row < 0 || pos.col >= availableCols || pos.row >= availableRows) { + + // All cells outside the used terminal area are drawn with the + // same background color as the top-left one. + VTermPos firstPos; + firstPos.row = 0; + firstPos.col = 0; + vterm_screen_get_cell(fTermScreen, firstPos, &cell); + cell.chars[0] = 0; + cell.width = 1; + } else + vterm_screen_get_cell(fTermScreen, pos, &cell); + + rgb_color foreground, background; + foreground.red = cell.fg.red; + foreground.green = cell.fg.green; + foreground.blue = cell.fg.blue; + background.red = cell.bg.red; + background.green = cell.bg.green; + background.blue = cell.bg.blue; + + if(cell.attrs.reverse) { + SetLowColor(foreground); + SetViewColor(foreground); + SetHighColor(background); + } else { + SetLowColor(background); + SetViewColor(background); + SetHighColor(foreground); + } + + BPoint penLocation = PenLocation(); + FillRect(BRect(penLocation.x, penLocation.y - height.ascent, + penLocation.x + cell.width * fFontWidth, penLocation.y + 1), + B_SOLID_LOW); + + if (cell.chars[0] == 0) { DrawString(" "); pos.col ++; } else { - VTermScreenCell cell; - vterm_screen_get_cell(fTermScreen, pos, &cell); - - rgb_color foreground, background; - foreground.red = cell.fg.red; - foreground.green = cell.fg.green; - foreground.blue = cell.fg.blue; - background.red = cell.bg.red; - background.green = cell.bg.green; - background.blue = cell.bg.blue; - - if(cell.attrs.reverse) { - SetLowColor(foreground); - SetViewColor(foreground); - SetHighColor(background); - } else { - SetLowColor(background); - SetViewColor(background); - SetHighColor(foreground); - } - - BPoint penLocation = PenLocation(); - FillRect(BRect(penLocation.x, penLocation.y - height.ascent, - penLocation.x + cell.width * fFontWidth, penLocation.y), B_SOLID_LOW); - - if (cell.chars[0] == 0) { - DrawString(" "); - pos.col ++; - } else { - char buffer[VTERM_MAX_CHARS_PER_CELL]; - wcstombs(buffer, (wchar_t*)cell.chars, + char buffer[VTERM_MAX_CHARS_PER_CELL]; + wcstombs(buffer, (wchar_t*)cell.chars, VTERM_MAX_CHARS_PER_CELL); - DrawString(buffer); - pos.col += cell.width; - } + DrawString(buffer); + pos.col += cell.width; } } } @@ -116,7 +124,7 @@ void TermView::Draw(BRect updateRect) void TermView::FrameResized(float width, float height) { VTermRect newSize = PixelsToGlyphs(BRect(0, 0, width - 2 * kBorderSpacing, - height - 2 * kBorderSpacing)); + height - 2 * kBorderSpacing)); vterm_set_size(fTerm, newSize.end_row, newSize.end_col); }