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.
This commit is contained in:
Adrien Destugues
2013-11-20 22:39:16 +01:00
parent 5c4dd6047b
commit ccc6b9be5b
5 changed files with 256 additions and 94 deletions
+61 -3
View File
@@ -12,6 +12,8 @@
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <FindDirectory.h>
#include <Path.h>
#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*)
{
+9
View File
@@ -10,6 +10,7 @@
#include <Application.h>
#include <SerialPort.h>
#include <String.h>
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*);
+133 -52
View File
@@ -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("<no serial port available>", 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";
+6
View File
@@ -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;
};
+47 -39
View File
@@ -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);
}