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; };