diff --git a/src/apps/serialconnect/SerialWindow.cpp b/src/apps/serialconnect/SerialWindow.cpp
index 277eefb8c8..dcd38aa4db 100644
--- a/src/apps/serialconnect/SerialWindow.cpp
+++ b/src/apps/serialconnect/SerialWindow.cpp
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#include
#include "SerialApp.h"
@@ -41,13 +42,30 @@ SerialWindow::SerialWindow()
B_DOCUMENT_WINDOW, B_QUIT_ON_WINDOW_CLOSE | B_AUTO_UPDATE_SIZE_LIMITS)
, fLogFilePanel(NULL)
{
- SetLayout(new BGroupLayout(B_VERTICAL, 0.0f));
+ BMenuBar* menuBar = new BMenuBar(Bounds(), "menuBar");
+ menuBar->ResizeToPreferred();
- BMenuBar* menuBar = new BMenuBar("menuBar");
- fTermView = new TermView();
+ BRect r = Bounds();
+ r.top = menuBar->Bounds().bottom + 1;
+ r.right -= B_V_SCROLL_BAR_WIDTH;
+ fTermView = new TermView(r);
+ fTermView->ResizeToPreferred();
+
+ r = fTermView->Frame();
+ r.left = r.right + 1;
+ r.right = r.left + B_V_SCROLL_BAR_WIDTH;
+ r.top -= 1;
+ r.bottom -= B_H_SCROLL_BAR_HEIGHT - 1;
+ BScrollBar* scrollBar = new BScrollBar(r, "scrollbar", NULL, 0, 0,
+ B_VERTICAL);
+
+ scrollBar->SetTarget(fTermView);
+
+ ResizeTo(r.right - 1, r.bottom + B_H_SCROLL_BAR_HEIGHT - 1);
AddChild(menuBar);
AddChild(fTermView);
+ AddChild(scrollBar);
fConnectionMenu = new BMenu("Connection");
BMenu* fileMenu = new BMenu("File");
@@ -62,7 +80,7 @@ SerialWindow::SerialWindow()
// TODO edit menu - what's in it ?
//BMenu* editMenu = new BMenu("Edit");
//menuBar->AddItem(editMenu);
-
+
BMenuItem* logFile = new BMenuItem("Log to file" B_UTF8_ELLIPSIS,
new BMessage(kMsgLogfile));
fileMenu->AddItem(logFile);
@@ -78,23 +96,23 @@ SerialWindow::SerialWindow()
// Configuring all this by menus may be a bit unhandy. Make a setting
// window instead ?
- fBaudrateMenu = new BMenu("Baud rate");
+ fBaudrateMenu = new BMenu("Baud rate");
fBaudrateMenu->SetRadioMode(true);
settingsMenu->AddItem(fBaudrateMenu);
- fParityMenu = new BMenu("Parity");
+ fParityMenu = new BMenu("Parity");
fParityMenu->SetRadioMode(true);
settingsMenu->AddItem(fParityMenu);
- fStopbitsMenu = new BMenu("Stop bits");
+ fStopbitsMenu = new BMenu("Stop bits");
fStopbitsMenu->SetRadioMode(true);
settingsMenu->AddItem(fStopbitsMenu);
- fFlowcontrolMenu = new BMenu("Flow control");
+ fFlowcontrolMenu = new BMenu("Flow control");
fFlowcontrolMenu->SetRadioMode(true);
settingsMenu->AddItem(fFlowcontrolMenu);
- fDatabitsMenu = new BMenu("Data bits");
+ fDatabitsMenu = new BMenu("Data bits");
fDatabitsMenu->SetRadioMode(true);
settingsMenu->AddItem(fDatabitsMenu);
@@ -147,7 +165,7 @@ SerialWindow::SerialWindow()
message->AddInt32("baudrate", kBaudrateConstants[i]);
char buffer[7];
- sprintf(buffer,"%d", kBaudrates[i]);
+ sprintf(buffer, "%d", kBaudrates[i]);
BMenuItem* item = new BMenuItem(buffer, message);
fBaudrateMenu->AddItem(item);
@@ -181,7 +199,6 @@ SerialWindow::SerialWindow()
}
-
SerialWindow::~SerialWindow()
{
delete fLogFilePanel;
@@ -198,7 +215,7 @@ void SerialWindow::MenusBeginning()
int deviceCount = serialPort.CountDevices();
bool connected = false;
- for(int i = 0; i < deviceCount; i++)
+ for (int i = 0; i < deviceCount; i++)
{
char buffer[256];
serialPort.GetDeviceName(i, buffer, 256);
@@ -210,7 +227,7 @@ void SerialWindow::MenusBeginning()
const BString& connectedPort = ((SerialApp*)be_app)->GetPort();
- if(connectedPort == buffer) {
+ if (connectedPort == buffer) {
connected = true;
portItem->SetMarked(true);
}
@@ -223,7 +240,7 @@ void SerialWindow::MenusBeginning()
BMenuItem* disconnect = new BMenuItem("Disconnect",
new BMessage(kMsgOpenPort), 'Z', B_OPTION_KEY);
- if(!connected)
+ if (!connected)
disconnect->SetEnabled(false);
fConnectionMenu->AddItem(disconnect);
} else {
@@ -233,6 +250,7 @@ void SerialWindow::MenusBeginning()
}
}
+
void SerialWindow::MessageReceived(BMessage* message)
{
switch(message->what)
@@ -240,7 +258,7 @@ void SerialWindow::MessageReceived(BMessage* message)
case kMsgOpenPort:
{
BMenuItem* disconnectMenu;
- if(message->FindPointer("source", (void**)&disconnectMenu) == B_OK)
+ if (message->FindPointer("source", (void**)&disconnectMenu) == B_OK)
disconnectMenu->SetMarked(false);
be_app->PostMessage(new BMessage(*message));
break;
@@ -257,7 +275,7 @@ void SerialWindow::MessageReceived(BMessage* message)
case kMsgLogfile:
{
// Let's lazy init the file panel
- if(fLogFilePanel == NULL) {
+ if (fLogFilePanel == NULL) {
fLogFilePanel = new BFilePanel(B_SAVE_PANEL, &be_app_messenger,
NULL, B_FILE_NODE, false);
fLogFilePanel->SetMessage(message);
@@ -273,71 +291,63 @@ void SerialWindow::MessageReceived(BMessage* message)
parity_mode parity;
uint32 flowcontrol;
- if(message->FindInt32("databits", (int32*)&dataBits) == B_OK)
- {
- for(int i = 0; i < fDatabitsMenu->CountItems(); i++)
- {
+ 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)
+ if (code == dataBits)
item->SetMarked(true);
}
}
- if(message->FindInt32("stopbits", (int32*)&stopBits) == B_OK)
- {
- for(int i = 0; i < fStopbitsMenu->CountItems(); i++)
- {
+ 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)
+ if (code == stopBits)
item->SetMarked(true);
}
}
- if(message->FindInt32("parity", (int32*)&parity) == B_OK)
+ if (message->FindInt32("parity", (int32*)&parity) == B_OK)
{
- for(int i = 0; i < fParityMenu->CountItems(); i++)
- {
+ for (int i = 0; i < fParityMenu->CountItems(); i++) {
BMenuItem* item = fParityMenu->ItemAt(i);
int32 code;
item->Message()->FindInt32("parity", &code);
- if(code == parity)
+ if (code == parity)
item->SetMarked(true);
}
}
- if(message->FindInt32("flowcontrol", (int32*)&flowcontrol) == B_OK)
- {
- for(int i = 0; i < fFlowcontrolMenu->CountItems(); i++)
- {
+ 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)
+ if (code == (int32)flowcontrol)
item->SetMarked(true);
}
}
- if(message->FindInt32("baudrate", &baudrate) == B_OK)
- {
- for(int i = 0; i < fBaudrateMenu->CountItems(); i++)
- {
+ 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(baudrate == code)
+ if (baudrate == code)
item->SetMarked(true);
}
}
-
+
break;
}
default:
diff --git a/src/apps/serialconnect/SerialWindow.h b/src/apps/serialconnect/SerialWindow.h
index 58fdaa251f..e7e3035b13 100644
--- a/src/apps/serialconnect/SerialWindow.h
+++ b/src/apps/serialconnect/SerialWindow.h
@@ -23,6 +23,7 @@ class SerialWindow: public BWindow
private:
TermView* fTermView;
+
BMenu* fConnectionMenu;
BMenu* fDatabitsMenu;
BMenu* fStopbitsMenu;
diff --git a/src/apps/serialconnect/TermView.cpp b/src/apps/serialconnect/TermView.cpp
index 925494bfe4..f47c4d5572 100644
--- a/src/apps/serialconnect/TermView.cpp
+++ b/src/apps/serialconnect/TermView.cpp
@@ -11,27 +11,30 @@
#include
#include
#include
+#include
#include "SerialApp.h"
+struct ScrollBufferItem {
+ int cols;
+ VTermScreenCell cells[];
+};
+
+
TermView::TermView()
:
BView("TermView", B_WILL_DRAW | B_FRAME_EVENTS)
{
- SetFont(be_fixed_font);
+ _Init();
+}
- font_height height;
- GetFontHeight(&height);
- fFontHeight = height.ascent + height.descent + height.leading + 1;
- fFontWidth = be_fixed_font->StringWidth("X");
- fTerm = vterm_new(kDefaultHeight, kDefaultWidth);
- fTermScreen = vterm_obtain_screen(fTerm);
- vterm_screen_set_callbacks(fTermScreen, &sScreenCallbacks, this);
- vterm_screen_reset(fTermScreen, 1);
-
- vterm_parser_set_utf8(fTerm, 1);
+TermView::TermView(BRect r)
+ :
+ BView(r, "TermView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FRAME_EVENTS)
+{
+ _Init();
}
@@ -41,7 +44,8 @@ TermView::~TermView()
}
-void TermView::AttachedToWindow()
+void
+TermView::AttachedToWindow()
{
MakeFocus();
@@ -49,7 +53,7 @@ void TermView::AttachedToWindow()
VTermPos firstPos;
firstPos.row = 0;
firstPos.col = 0;
- vterm_screen_get_cell(fTermScreen, firstPos, &cell);
+ _GetCell(firstPos, cell);
rgb_color background;
background.red = cell.bg.red;
@@ -61,17 +65,15 @@ void TermView::AttachedToWindow()
}
-void TermView::Draw(BRect updateRect)
+void
+TermView::Draw(BRect updateRect)
{
- VTermRect updatedChars = PixelsToGlyphs(updateRect);
+ VTermRect updatedChars = _PixelsToGlyphs(updateRect);
VTermPos pos;
font_height height;
GetFontHeight(&height);
- int availableRows, availableCols;
- vterm_get_size(fTerm, &availableRows, &availableCols);
-
for (pos.row = updatedChars.start_row; pos.row <= updatedChars.end_row;
pos.row++) {
float x = updatedChars.start_col * fFontWidth + kBorderSpacing;
@@ -82,19 +84,7 @@ void TermView::Draw(BRect updateRect)
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);
+ _GetCell(pos, cell);
rgb_color foreground, background;
foreground.red = cell.fg.red;
@@ -119,7 +109,7 @@ void TermView::Draw(BRect updateRect)
BPoint penLocation = PenLocation();
FillRect(BRect(penLocation.x, penLocation.y - height.ascent,
penLocation.x + cell.width * fFontWidth - 1,
- penLocation.y + height.descent + height.leading - 1),
+ penLocation.y + height.descent + height.leading),
B_SOLID_LOW);
if (cell.chars[0] == 0) {
@@ -138,15 +128,17 @@ void TermView::Draw(BRect updateRect)
}
-void TermView::FrameResized(float width, float height)
+void
+TermView::FrameResized(float width, float height)
{
- VTermRect newSize = PixelsToGlyphs(BRect(0, 0, width - 2 * kBorderSpacing,
+ VTermRect newSize = _PixelsToGlyphs(BRect(0, 0, width - 2 * kBorderSpacing,
height - 2 * kBorderSpacing));
vterm_set_size(fTerm, newSize.end_row, newSize.end_col);
}
-void TermView::GetPreferredSize(float* width, float* height)
+void
+TermView::GetPreferredSize(float* width, float* height)
{
if (width != NULL)
*width = kDefaultWidth * fFontWidth + 2 * kBorderSpacing;
@@ -155,7 +147,8 @@ void TermView::GetPreferredSize(float* width, float* height)
}
-void TermView::KeyDown(const char* bytes, int32 numBytes)
+void
+TermView::KeyDown(const char* bytes, int32 numBytes)
{
BMessage* keyEvent = new BMessage(kMsgDataWrite);
keyEvent->AddData("data", B_RAW_TYPE, bytes, numBytes);
@@ -163,7 +156,8 @@ void TermView::KeyDown(const char* bytes, int32 numBytes)
}
-void TermView::MessageReceived(BMessage* message)
+void
+TermView::MessageReceived(BMessage* message)
{
switch(message->what)
{
@@ -183,7 +177,8 @@ void TermView::MessageReceived(BMessage* message)
}
-void TermView::PushBytes(const char* bytes, size_t length)
+void
+TermView::PushBytes(const char* bytes, size_t length)
{
vterm_push_bytes(fTerm, bytes, length);
}
@@ -192,7 +187,27 @@ void TermView::PushBytes(const char* bytes, size_t length)
// #pragma mark -
-VTermRect TermView::PixelsToGlyphs(BRect pixels) const
+void
+TermView::_Init()
+{
+ SetFont(be_fixed_font);
+
+ font_height height;
+ GetFontHeight(&height);
+ fFontHeight = height.ascent + height.descent + height.leading + 1;
+ fFontWidth = be_fixed_font->StringWidth("X");
+ fTerm = vterm_new(kDefaultHeight, kDefaultWidth);
+
+ fTermScreen = vterm_obtain_screen(fTerm);
+ vterm_screen_set_callbacks(fTermScreen, &sScreenCallbacks, this);
+ vterm_screen_reset(fTermScreen, 1);
+
+ vterm_parser_set_utf8(fTerm, 1);
+}
+
+
+VTermRect
+TermView::_PixelsToGlyphs(BRect pixels) const
{
pixels.OffsetBy(-kBorderSpacing, -kBorderSpacing);
@@ -201,7 +216,7 @@ VTermRect TermView::PixelsToGlyphs(BRect pixels) const
rect.end_col = (int)ceil(pixels.right / fFontWidth);
rect.start_row = (int)floor(pixels.top / fFontHeight);
rect.end_row = (int)ceil(pixels.bottom / fFontHeight);
-/*
+#if 0
printf(
"TOP %d ch < %f px\n"
"BTM %d ch < %f px\n"
@@ -212,12 +227,12 @@ VTermRect TermView::PixelsToGlyphs(BRect pixels) const
rect.start_col, pixels.left,
rect.end_col, pixels.right
);
-*/
+#endif
return rect;
}
-BRect TermView::GlyphsToPixels(const VTermRect& glyphs) const
+BRect TermView::_GlyphsToPixels(const VTermRect& glyphs) const
{
BRect rect;
rect.top = glyphs.start_row * fFontHeight;
@@ -242,40 +257,128 @@ BRect TermView::GlyphsToPixels(const VTermRect& glyphs) const
}
-BRect TermView::GlyphsToPixels(const int width, const int height) const
+BRect
+TermView::_GlyphsToPixels(const int width, const int height) const
{
VTermRect rect;
rect.start_row = 0;
rect.start_col = 0;
rect.end_row = height;
rect.end_col = width;
- return GlyphsToPixels(rect);
+ return _GlyphsToPixels(rect);
}
-void TermView::Damage(VTermRect rect)
+void
+TermView::_GetCell(VTermPos pos, VTermScreenCell& cell)
{
- Invalidate();
-// Invalidate(GlyphsToPixels(rect));
+ int availableRows, availableCols;
+ vterm_get_size(fTerm, &availableRows, &availableCols);
+
+ if (pos.col < 0 || pos.row < -kScrollBackSize || 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.
+ // TODO should they use the attributes of the closest neighbor instead?
+ VTermPos firstPos;
+ firstPos.row = 0;
+ firstPos.col = 0;
+ vterm_screen_get_cell(fTermScreen, firstPos, &cell);
+ cell.chars[0] = 0;
+ cell.width = 1;
+ } else if (pos.row < 0) {
+ // This is a cell from the scroll-back buffer
+ int offset = - pos.row - 1;
+ ScrollBufferItem* line = (ScrollBufferItem*)fScrollBuffer.ItemAt(offset);
+ if (line == NULL || pos.col >= line->cols) {
+ VTermPos firstPos;
+ firstPos.row = 0;
+ firstPos.col = 0;
+ vterm_screen_get_cell(fTermScreen, firstPos, &cell);
+ cell.chars[0] = 0;
+ cell.width = 1;
+ } else
+ cell = line->cells[pos.col];
+ } else
+ vterm_screen_get_cell(fTermScreen, pos, &cell);
}
-/* static */
-int TermView::Damage(VTermRect rect, void* user)
+void
+TermView::_Damage(VTermRect rect)
+{
+// Invalidate();
+ Invalidate(_GlyphsToPixels(rect));
+}
+
+
+void
+TermView::_PushLine(int cols, const VTermScreenCell* cells)
+{
+ ScrollBufferItem* item = (ScrollBufferItem*)malloc(sizeof(int)
+ + cols * sizeof(VTermScreenCell));
+ item->cols = cols;
+ memcpy(item->cells, cells, cols * sizeof(VTermScreenCell));
+
+ fScrollBuffer.AddItem(item, 0);
+
+ free(fScrollBuffer.RemoveItem(kScrollBackSize));
+
+ int availableRows, availableCols;
+ vterm_get_size(fTerm, &availableRows, &availableCols);
+
+ VTermRect dirty;
+ dirty.start_col = 0;
+ dirty.end_col = availableCols;
+ dirty.end_row = 0;
+ dirty.start_row = -fScrollBuffer.CountItems();
+ // FIXME we should rather use CopyRect if possible, and only invalidate the
+ // newly exposed area here.
+ Invalidate(_GlyphsToPixels(dirty));
+
+ BScrollBar* scrollBar = ScrollBar(B_VERTICAL);
+ if (scrollBar != NULL) {
+ // FIXME this is not exactly right, it's off by a few pixels so one step
+ // isn't exactly equal to one line.
+ float range = (fScrollBuffer.CountItems() + availableRows) * fFontHeight;
+ scrollBar->SetRange(availableRows * fFontHeight - range, 0.0f);
+ // TODO we need to adjust this in FrameResized, as availableRows can
+ // change
+ scrollBar->SetProportion(availableRows * fFontHeight / range);
+ scrollBar->SetSteps(fFontHeight, fFontHeight * 3);
+ }
+}
+
+
+/* static */ int
+TermView::_Damage(VTermRect rect, void* user)
{
TermView* view = (TermView*)user;
- view->Damage(rect);
+ view->_Damage(rect);
return 0;
}
-const VTermScreenCallbacks TermView::sScreenCallbacks = {
- &TermView::Damage,
+/* static */ int
+TermView::_PushLine(int cols, const VTermScreenCell* cells, void* user)
+{
+ TermView* view = (TermView*)user;
+ view->_PushLine(cols, cells);
+
+ return 0;
+}
+
+
+const
+VTermScreenCallbacks TermView::sScreenCallbacks = {
+ &TermView::_Damage,
/*.moverect =*/ NULL,
/*.movecursor =*/ NULL,
/*.settermprop =*/ NULL,
/*.setmousefunc =*/ NULL,
/*.bell =*/ NULL,
/*.resize =*/ NULL,
+ &TermView::_PushLine,
+ /*.sb_popline =*/ NULL,
};
diff --git a/src/apps/serialconnect/TermView.h b/src/apps/serialconnect/TermView.h
index a673489b8e..3a2b8a2ad8 100644
--- a/src/apps/serialconnect/TermView.h
+++ b/src/apps/serialconnect/TermView.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2012, Adrien Destugues, pulkomandy@gmail.com
+ * Copyright 2012-2014, Adrien Destugues, pulkomandy@gmail.com
* Distributed under the terms of the MIT licence.
*/
@@ -10,31 +10,45 @@ extern "C" {
#include
}
+
class TermView: public BView
{
public:
- TermView();
- ~TermView();
+ TermView();
+ TermView(BRect bounds);
+ ~TermView();
+
+ void AttachedToWindow();
+ void Draw(BRect updateRect);
+ void FrameResized(float width, float height);
+ void GetPreferredSize(float* width, float* height);
+ void KeyDown(const char* bytes, int32 numBytes);
+ void MessageReceived(BMessage* message);
+
+ void PushBytes(const char* bytes, const size_t length);
- void AttachedToWindow();
- void Draw(BRect updateRect);
- void FrameResized(float width, float height);
- void GetPreferredSize(float* width, float* height);
- void KeyDown(const char* bytes, int32 numBytes);
- void MessageReceived(BMessage* message);
- void PushBytes(const char* bytes, const size_t length);
-
private:
- VTermRect PixelsToGlyphs(BRect pixels) const;
- BRect GlyphsToPixels(const VTermRect& glyphs) const;
- BRect GlyphsToPixels(const int width, const int height) const;
- void Damage(VTermRect rect);
+ void _Init();
- static int Damage(VTermRect rect, void* user);
+ VTermRect _PixelsToGlyphs(BRect pixels) const;
+ BRect _GlyphsToPixels(const VTermRect& glyphs) const;
+ BRect _GlyphsToPixels(const int width, const int height)
+ const;
+ void _GetCell(VTermPos pos, VTermScreenCell& cell);
+
+ void _Damage(VTermRect rect);
+ void _PushLine(int cols, const VTermScreenCell* cells);
+
+ static int _Damage(VTermRect rect, void* user);
+ static int _PushLine(int cols, const VTermScreenCell* cells,
+ void* user);
+ static int _PopLine(int cols, const VTermScreenCell* cells,
+ void* user);
private:
VTerm* fTerm;
VTermScreen* fTermScreen;
+ BList fScrollBuffer;
float fFontWidth;
float fFontHeight;
@@ -43,4 +57,5 @@ class TermView: public BView
static const int kDefaultWidth = 80;
static const int kDefaultHeight = 25;
static const int kBorderSpacing = 3;
+ static const int kScrollBackSize = 1000;
};