[haiku-commits] haiku: hrev44388 - in src/apps/serialconnect/libvterm: src src/apps/serialconnect include

  • From: pulkomandy@xxxxxxxxxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Mon, 23 Jul 2012 21:40:44 +0200 (CEST)

hrev44388 adds 12 changesets to branch 'master'
old head: 5cdd07a8148b04cd1b7e29778ec0661df7dbe46d
new head: dae0a4e0abda9ce3dff8e31007a8f66bc14421c8

----------------------------------------------------------------------------

558e33f: Add MikMod to libs available at build time.
  Going to use it for a media decoder, and there is no way to do that outside 
of Haiku
  sourcetree so far...

4c08828: Merge branch 'master' of ssh://git.haiku-os.org/haiku

7d39550: Merge branch 'master' of ssh://git.haiku-os.org/haiku

e914c54: Merge branch 'master' of ssh://git.haiku-os.org/haiku

ab3e5e1: Merge branch 'master' of ssh://git.haiku-os.org/haiku

5c48a6b: Merge branch 'master' of ssh://git.haiku-os.org/haiku

816e3cb: Merge branch 'master' of ssh://git.haiku-os.org/haiku

c9f321e: Merge branch 'master' of ssh://git.haiku-os.org/haiku

8c115d4: Merge branch 'master' of ssh://git.haiku-os.org/haiku

f01f7fe: Merge branch 'master' of ssh://git.haiku-os.org/haiku

fc3f407: Merge branch 'master' of ssh://git.haiku-os.org/haiku

dae0a4e: WIP version of SerialConnect. Not working, but added to the tree 
anyway so :
   * You can code review it
   * You can help developping
  Uses libvterm as the backend for parsing ANSI escape sequences. The lib was
  changed slightly to build with GCC2. It could be used by Terminal as well as
  it seems cleaner and more reliable than our current parser.

                [ Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx> ]

----------------------------------------------------------------------------

26 files changed, 4933 insertions(+)
build/jam/OptionalBuildFeatures                    |   32 +
src/apps/Jamfile                                   |    1 +
src/apps/serialconnect/Jamfile                     |   21 +
src/apps/serialconnect/SerialApp.cpp               |   87 +
src/apps/serialconnect/SerialApp.h                 |   35 +
src/apps/serialconnect/SerialWindow.cpp            |  118 ++
src/apps/serialconnect/SerialWindow.h              |   17 +
src/apps/serialconnect/TermView.cpp                |  120 ++
src/apps/serialconnect/TermView.h                  |   37 +
src/apps/serialconnect/libvterm/include/vterm.h    |  245 +++
.../serialconnect/libvterm/include/vterm_input.h   |   39 +
src/apps/serialconnect/libvterm/src/encoding.c     |  221 +++
.../libvterm/src/encoding/DECdrawing.inc           |   36 +
.../libvterm/src/encoding/DECdrawing.tbl           |   31 +
.../serialconnect/libvterm/src/encoding/uk.inc     |    6 +
.../serialconnect/libvterm/src/encoding/uk.tbl     |    1 +
src/apps/serialconnect/libvterm/src/input.c        |  171 ++
src/apps/serialconnect/libvterm/src/parser.c       |  344 ++++
src/apps/serialconnect/libvterm/src/pen.c          |  373 ++++
src/apps/serialconnect/libvterm/src/rect.h         |   56 +
src/apps/serialconnect/libvterm/src/screen.c       |  664 ++++++++
src/apps/serialconnect/libvterm/src/state.c        | 1416 ++++++++++++++++
src/apps/serialconnect/libvterm/src/unicode.c      |  332 ++++
src/apps/serialconnect/libvterm/src/utf8.h         |   41 +
src/apps/serialconnect/libvterm/src/vterm.c        |  322 ++++
.../serialconnect/libvterm/src/vterm_internal.h    |  167 ++

############################################################################

Commit:      558e33f79f7c4c9334b2d28069162c721a709d4b
URL:         http://cgit.haiku-os.org/haiku/commit/?id=558e33f
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Sat Jun  9 11:49:22 2012 UTC

Add MikMod to libs available at build time.
Going to use it for a media decoder, and there is no way to do that outside of 
Haiku
sourcetree so far...

----------------------------------------------------------------------------

diff --git a/build/jam/OptionalBuildFeatures b/build/jam/OptionalBuildFeatures
index 350a781..9e461e0 100644
--- a/build/jam/OptionalBuildFeatures
+++ b/build/jam/OptionalBuildFeatures
@@ -333,6 +333,38 @@ if $(TARGET_ARCH) = x86 {
 }
 
 
+# MikMod
+local mikmodBaseURL = http://haiku-files.org/files/optional-packages/lib ;
+if $(TARGET_ARCH) = x86 {
+       if $(HAIKU_GCC_VERSION[1]) >= 4 {
+               HAIKU_MIKMOD_FILE = 
libmikmod-3.1.11-r1a3-x86-gcc4-2011-05-26.zip ;
+       } else {
+               HAIKU_MIKMOD_FILE = 
libmikmod-3.1.11-r1a3-x86-gcc2-2011-05-19.zip ;
+       }
+
+       local mikmodZipFile = [ DownloadFile $(HAIKU_MIKMOD_FILE)
+               : $(mikmodBaseURL)/$(HAIKU_MIKMOD_FILE) ] ;
+
+       HAIKU_MIKMOD_DIR = [ FDirName $(HAIKU_OPTIONAL_BUILD_PACKAGES_DIR)
+               $(HAIKU_MIKMOD_FILE:B) ] ;
+
+       HAIKU_MIKMOD_HEADERS_DEPENDENCY = [ ExtractArchive $(HAIKU_MIKMOD_DIR)
+               : common/include/ : $(mikmodZipFile) : extracted-mikmod ] ;
+
+       HAIKU_MIKMOD_LIBS = [ ExtractArchive $(HAIKU_MIKMOD_DIR)
+               :
+               common/lib/libmikmod.a
+               : $(mikmodZipFile)
+               : extracted-ffmpeg ] ;
+       Depends $(HAIKU_MIKMOD_LIBS) : $(HAIKU_MIKMOD_HEADERS_DEPENDENCY) ;
+
+       HAIKU_MIKMOD_HEADERS = [ FDirName $(HAIKU_MIKMOD_DIR) common include ] ;
+
+} else {
+       Echo "MikMod support not available on $(TARGET_ARCH)" ;
+}
+
+
 # Freetype
 local freetypeBaseURL = http://haiku-files.org/files/optional-packages/lib ;
 if $(TARGET_ARCH) = ppc || $(TARGET_ARCH) = x86 {

############################################################################

Commit:      4c08828e0b3349b765e9d92ea9427547ac28386b
URL:         http://cgit.haiku-os.org/haiku/commit/?id=4c08828
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Sat Jun  9 11:50:34 2012 UTC

Merge branch 'master' of ssh://git.haiku-os.org/haiku

----------------------------------------------------------------------------

############################################################################

Commit:      7d395500efc9c5c7ceea161ee1fe0b9c75c60768
URL:         http://cgit.haiku-os.org/haiku/commit/?id=7d39550
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Mon Jun 18 18:47:41 2012 UTC

Merge branch 'master' of ssh://git.haiku-os.org/haiku

----------------------------------------------------------------------------

############################################################################

Commit:      e914c544a77e36317afb4a9b5f812d02e597fa3d
URL:         http://cgit.haiku-os.org/haiku/commit/?id=e914c54
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Sun Jul  8 13:07:25 2012 UTC

Merge branch 'master' of ssh://git.haiku-os.org/haiku

----------------------------------------------------------------------------

############################################################################

Commit:      ab3e5e110a377b38571227f7bc644b0cf7cfde92
URL:         http://cgit.haiku-os.org/haiku/commit/?id=ab3e5e1
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Fri Jul 13 18:54:01 2012 UTC

Merge branch 'master' of ssh://git.haiku-os.org/haiku

----------------------------------------------------------------------------

############################################################################

Commit:      5c48a6b93d757df40161de28aa6f8bcb5c103a8f
URL:         http://cgit.haiku-os.org/haiku/commit/?id=5c48a6b
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Sun Jul 15 15:30:48 2012 UTC

Merge branch 'master' of ssh://git.haiku-os.org/haiku

----------------------------------------------------------------------------

############################################################################

Commit:      816e3cb6491608d1b6aa4202040030c99feb747a
URL:         http://cgit.haiku-os.org/haiku/commit/?id=816e3cb
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Mon Jul 16 17:57:30 2012 UTC

Merge branch 'master' of ssh://git.haiku-os.org/haiku

----------------------------------------------------------------------------

############################################################################

Commit:      c9f321ebc74eca9d2f8df359f202837a132e9374
URL:         http://cgit.haiku-os.org/haiku/commit/?id=c9f321e
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Tue Jul 17 16:17:08 2012 UTC

Merge branch 'master' of ssh://git.haiku-os.org/haiku

----------------------------------------------------------------------------

############################################################################

Commit:      8c115d4c5f0d25143900965bdcf9cc4cf58010bb
URL:         http://cgit.haiku-os.org/haiku/commit/?id=8c115d4
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Wed Jul 18 16:12:41 2012 UTC

Merge branch 'master' of ssh://git.haiku-os.org/haiku

----------------------------------------------------------------------------

############################################################################

Commit:      f01f7fec8f157b4f1eb9255cc764d003589ce20f
URL:         http://cgit.haiku-os.org/haiku/commit/?id=f01f7fe
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Thu Jul 19 19:01:31 2012 UTC

Merge branch 'master' of ssh://git.haiku-os.org/haiku

----------------------------------------------------------------------------

############################################################################

Commit:      fc3f40765a4d5bcbb2f554be085823dedd83ffe0
URL:         http://cgit.haiku-os.org/haiku/commit/?id=fc3f407
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Mon Jul 23 19:32:24 2012 UTC

Merge branch 'master' of ssh://git.haiku-os.org/haiku

----------------------------------------------------------------------------

############################################################################

Revision:    hrev44388
Commit:      dae0a4e0abda9ce3dff8e31007a8f66bc14421c8
URL:         http://cgit.haiku-os.org/haiku/commit/?id=dae0a4e
Author:      Adrien Destugues - PulkoMandy <pulkomandy@xxxxxxxxxxxxx>
Date:        Mon Jul 23 19:36:40 2012 UTC

WIP version of SerialConnect. Not working, but added to the tree anyway so :
 * You can code review it
 * You can help developping
Uses libvterm as the backend for parsing ANSI escape sequences. The lib was
changed slightly to build with GCC2. It could be used by Terminal as well as
it seems cleaner and more reliable than our current parser.

----------------------------------------------------------------------------

diff --git a/src/apps/Jamfile b/src/apps/Jamfile
index cd66b3e..0f90365 100644
--- a/src/apps/Jamfile
+++ b/src/apps/Jamfile
@@ -50,6 +50,7 @@ HaikuSubInclude readonlybootprompt ;
 HaikuSubInclude remotedesktop ;
 HaikuSubInclude resedit ;
 HaikuSubInclude screenshot ;
+HaikuSubInclude serialconnect ;
 HaikuSubInclude showimage ;
 HaikuSubInclude soundrecorder ;
 HaikuSubInclude stylededit ;
diff --git a/src/apps/serialconnect/Jamfile b/src/apps/serialconnect/Jamfile
new file mode 100644
index 0000000..20043b1
--- /dev/null
+++ b/src/apps/serialconnect/Jamfile
@@ -0,0 +1,21 @@
+SubDir HAIKU_TOP src apps serialconnect ;
+
+SubDirSysHdrs [ FDirName $(HAIKU_TOP) src apps serialconnect libvterm include 
] ;
+
+SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src apps serialconnect libvterm src ] 
;
+
+Application SerialConnect :
+       SerialApp.cpp
+       SerialWindow.cpp
+       TermView.cpp
+       encoding.c
+       input.c
+       parser.c
+       pen.c
+       screen.c
+       state.c
+       unicode.c
+       vterm.c
+       : be device $(HAIKU_LOCALE_LIBS) $(TARGET_LIBSUPC++)
+;
+
diff --git a/src/apps/serialconnect/SerialApp.cpp 
b/src/apps/serialconnect/SerialApp.cpp
new file mode 100644
index 0000000..7697168
--- /dev/null
+++ b/src/apps/serialconnect/SerialApp.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012, Adrien Destugues, pulkomandy@xxxxxxxxx
+ * Distributed under the terms of the MIT licence.
+ */
+
+
+#include "SerialApp.h"
+
+#include "SerialWindow.h"
+
+
+SerialApp::SerialApp()
+       :
+       BApplication(SerialApp::kApplicationSignature)
+{
+       fWindow = new SerialWindow();
+
+       serialLock = create_sem(0, "Serial port lock");
+       thread_id id = spawn_thread(PollSerial, "Serial port poller",
+               B_LOW_PRIORITY, this);
+       resume_thread(id);
+}
+
+
+void SerialApp::ReadyToRun()
+{
+       fWindow->Show();
+}
+
+
+void SerialApp::MessageReceived(BMessage* message)
+{
+       switch(message->what)
+       {
+               case kMsgOpenPort:
+               {
+                       const char* portName;
+                       message->FindString("port name", &portName);
+                       serialPort.Open(portName);
+                       release_sem(serialLock);
+                       break;
+               }
+               case kMsgDataRead:
+               {
+                       // TODO forward to the window   
+                       break;
+               }
+               default:
+                       BApplication::MessageReceived(message);
+       }
+}
+
+
+/* static */
+status_t SerialApp::PollSerial(void*)
+{
+       SerialApp* application = (SerialApp*)be_app;
+       char buffer[256];
+
+       for(;;)
+       {
+               ssize_t bytesRead;
+
+               bytesRead = application->serialPort.Read(buffer, 256);
+               if (bytesRead == B_FILE_ERROR)
+               {
+                       // Port is not open - wait for it and start over
+                       acquire_sem(application->serialLock);
+               } else {
+                       // We read something, forward it to the app for handling
+                       BMessage* serialData = new BMessage(kMsgDataRead);
+                       // TODO bytesRead is not nul terminated. Use generic 
data rather
+                       serialData->AddString("data", buffer);
+                       be_app_messenger.SendMessage(serialData);
+               }
+       }
+}
+
+const char* SerialApp::kApplicationSignature
+       = "application/x-vnd.haiku.SerialConnect";
+
+
+int main(int argc, char** argv)
+{
+       SerialApp app;
+       app.Run();
+}
diff --git a/src/apps/serialconnect/SerialApp.h 
b/src/apps/serialconnect/SerialApp.h
new file mode 100644
index 0000000..a210feb
--- /dev/null
+++ b/src/apps/serialconnect/SerialApp.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012, Adrien Destugues, pulkomandy@xxxxxxxxx
+ * Distributed under the terms of the MIT licence.
+ */
+
+
+#include <Application.h>
+#include <SerialPort.h>
+
+
+class SerialWindow;
+
+
+class SerialApp: public BApplication
+{
+       public:
+               SerialApp();
+               void ReadyToRun();
+               void MessageReceived(BMessage* message);
+
+       private:
+               BSerialPort serialPort;
+               static status_t PollSerial(void*);
+
+               sem_id serialLock;
+               SerialWindow* fWindow;
+
+               static const char* kApplicationSignature;
+};
+
+enum messageConstants {
+       kMsgOpenPort = 'open',
+       kMsgDataRead = 'dare',
+};
+
diff --git a/src/apps/serialconnect/SerialWindow.cpp 
b/src/apps/serialconnect/SerialWindow.cpp
new file mode 100644
index 0000000..bb30691
--- /dev/null
+++ b/src/apps/serialconnect/SerialWindow.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012, Adrien Destugues, pulkomandy@xxxxxxxxx
+ * Distributed under the terms of the MIT licence.
+ */
+
+
+#include "SerialWindow.h"
+
+#include <GroupLayout.h>
+#include <Menu.h>
+#include <MenuBar.h>
+#include <MenuItem.h>
+#include <SerialPort.h>
+
+#include "TermView.h"
+
+
+SerialWindow::SerialWindow()
+       :
+       BWindow(BRect(100, 100, 400, 400), SerialWindow::kWindowTitle,
+               B_DOCUMENT_WINDOW, B_QUIT_ON_WINDOW_CLOSE)
+{
+       SetLayout(new BGroupLayout(B_VERTICAL, 0.0f));
+
+       BMenuBar* menuBar = new BMenuBar("menuBar");
+       TermView* termView = new TermView();
+
+       AddChild(menuBar);
+       AddChild(termView);
+
+       BMenu* connectionMenu = new BMenu("Connections");
+       BMenu* editMenu = new BMenu("Edit");
+       BMenu* settingsMenu = new BMenu("Settings");
+
+       menuBar->AddItem(connectionMenu);
+       menuBar->AddItem(editMenu);
+       menuBar->AddItem(settingsMenu);
+
+       // TODO messages
+       BMenu* connect = new BMenu("Connect");
+       connectionMenu->AddItem(connect);
+
+       BSerialPort serialPort;
+       int deviceCount = serialPort.CountDevices();
+
+       for(int i = 0; i < deviceCount; i++)
+       {
+               char buffer[256];
+               serialPort.GetDeviceName(i, buffer, 256);
+
+               BMenuItem* portItem = new BMenuItem(buffer, NULL);
+
+               connect->AddItem(portItem);
+       }
+
+#if SUPPORTS_MODEM
+       BMenuItem* connectModem = new BMenuItem(
+               "Connect via modem" B_UTF8_ELLIPSIS, NULL, 'M', 0);
+       connectionMenu->AddItem(connectModem);
+#endif
+       BMenuItem* Disconnect = new BMenuItem("Disconnect", NULL,
+               'Z', B_OPTION_KEY);
+       connectionMenu->AddItem(Disconnect);
+
+       // TODO edit menu - what's in it ?
+       
+       // Configuring all this by menus may be a bit unhandy. Make a setting
+       // window instead ?
+       BMenu* parity = new BMenu("Parity"); 
+       settingsMenu->AddItem(parity);
+       BMenu* dataBits = new BMenu("Data bits"); 
+       settingsMenu->AddItem(dataBits);
+       BMenu* stopBits = new BMenu("Stop bits"); 
+       settingsMenu->AddItem(stopBits);
+       BMenu* baudRate = new BMenu("Baud rate"); 
+       settingsMenu->AddItem(baudRate);
+       BMenu* flowControl = new BMenu("Flow control"); 
+       settingsMenu->AddItem(flowControl);
+
+       BMenuItem* parityNone = new BMenuItem("None", NULL);
+       parity->AddItem(parityNone);
+       BMenuItem* parityOdd = new BMenuItem("Odd", NULL);
+       parity->AddItem(parityOdd);
+       BMenuItem* parityEven = new BMenuItem("Even", NULL);
+       parity->AddItem(parityEven);
+
+       BMenuItem* data7 = new BMenuItem("7", NULL);
+       dataBits->AddItem(data7);
+       BMenuItem* data8 = new BMenuItem("8", NULL);
+       dataBits->AddItem(data8);
+
+       BMenuItem* stop1 = new BMenuItem("1", NULL);
+       stopBits->AddItem(stop1);
+       BMenuItem* stop2 = new BMenuItem("2", NULL);
+       stopBits->AddItem(stop2);
+
+       static const char* baudrates[] = { "50", "75", "110", "134", "150", 
"200",
+               "300", "600", "1200", "1800", "2400", "4800", "9600", "19200", 
"31250",
+               "38400", "57600", "115200", "230400"
+       };
+
+       // Loop backwards to add fastest rates at top of menu
+       for (int i = sizeof(baudrates) / sizeof(char*); --i >= 0;)
+       {
+               BMenuItem* item = new BMenuItem(baudrates[i], NULL);
+               baudRate->AddItem(item);
+       }
+
+       BMenuItem* rtsCts = new BMenuItem("RTS/CTS", NULL);
+       flowControl->AddItem(rtsCts);
+       BMenuItem* noFlow = new BMenuItem("None", NULL);
+       flowControl->AddItem(noFlow);
+
+       CenterOnScreen();
+}
+
+
+const char* SerialWindow::kWindowTitle = "SerialConnect";
diff --git a/src/apps/serialconnect/SerialWindow.h 
b/src/apps/serialconnect/SerialWindow.h
new file mode 100644
index 0000000..c541290
--- /dev/null
+++ b/src/apps/serialconnect/SerialWindow.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2012, Adrien Destugues, pulkomandy@xxxxxxxxx
+ * Distributed under the terms of the MIT licence.
+ */
+
+
+#include <Window.h>
+
+
+class SerialWindow: public BWindow
+{
+       public:
+               SerialWindow::SerialWindow();
+
+       private:
+               static const char* kWindowTitle;
+};
diff --git a/src/apps/serialconnect/TermView.cpp 
b/src/apps/serialconnect/TermView.cpp
new file mode 100644
index 0000000..7899bf0
--- /dev/null
+++ b/src/apps/serialconnect/TermView.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2012, Adrien Destugues, pulkomandy@xxxxxxxxx
+ * Distributed under the terms of the MIT licence.
+ */
+
+
+#include "TermView.h"
+
+#include <stdio.h>
+
+#include <Layout.h>
+
+
+TermView::TermView()
+       :
+       BView("TermView", B_WILL_DRAW)
+{
+       fTerm = vterm_new(kDefaultWidth, kDefaultHeight);
+       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);
+
+       font_height height;
+       GetFontHeight(&height);
+       fFontHeight = height.ascent + height.descent + height.leading;
+       fFontWidth = be_fixed_font->StringWidth("X");
+
+       // TEST
+       //vterm_push_bytes(fTerm,"Hello World!",11);
+}
+
+
+TermView::~TermView()
+{
+       vterm_free(fTerm);
+}
+
+
+void TermView::Draw(BRect updateRect)
+{
+       VTermRect updatedChars = PixelsToGlyphs(updateRect);
+       
+       VTermPos pos;
+       font_height height;
+       GetFontHeight(&height);
+       MovePenTo(kBorderSpacing, height.ascent + kBorderSpacing);
+       for (pos.row = updatedChars.start_row; pos.row < updatedChars.end_row;
+                       pos.row++)
+       {
+               for (pos.col = updatedChars.start_col;
+                               pos.col < updatedChars.end_col; pos.col++)
+               {
+                       VTermScreenCell cell;
+                       vterm_screen_get_cell(fTermScreen, pos, &cell);
+
+                       char buffer[6];
+                       wcstombs(buffer, (wchar_t*)cell.chars, 6);
+
+                       DrawString(buffer);
+               }
+       }
+}
+
+
+VTermRect TermView::PixelsToGlyphs(BRect pixels) const
+{
+       pixels.OffsetBy(-kBorderSpacing, -kBorderSpacing);
+
+       VTermRect rect;
+       rect.start_col = (int)floor(pixels.left / fFontWidth);
+       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("pixels:\t%d\t%d\t%d\t%d\n"
+               "glyps:\t%d\t%d\t%d\t%d\n",
+               (int)pixels.top, (int)pixels.bottom, (int)pixels.left, 
(int)pixels.right,
+               rect.start_row, rect.end_row, rect.start_col, rect.end_col);
+#endif
+       return rect;
+}
+
+
+BRect TermView::GlyphsToPixels(const VTermRect& glyphs) const
+{
+       BRect rect;
+       rect.top = glyphs.start_row * fFontHeight;
+       rect.bottom = glyphs.end_row * fFontHeight;
+       rect.left = glyphs.start_col * fFontWidth;
+       rect.right = glyphs.end_col * fFontWidth;
+
+       return rect;
+}
+
+
+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);
+}
+
+
+const VTermScreenCallbacks TermView::sScreenCallbacks = {
+       /*.damage =*/ NULL,
+       /*.moverect =*/ NULL,
+       /*.movecursor =*/ NULL,
+       /*.settermprop =*/ NULL,
+       /*.setmousefunc =*/ NULL,
+       /*.bell =*/ NULL,
+       /*.resize =*/ NULL,
+};
diff --git a/src/apps/serialconnect/TermView.h 
b/src/apps/serialconnect/TermView.h
new file mode 100644
index 0000000..4e485c7
--- /dev/null
+++ b/src/apps/serialconnect/TermView.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012, Adrien Destugues, pulkomandy@xxxxxxxxx
+ * Distributed under the terms of the MIT licence.
+ */
+
+
+#include <View.h>
+
+extern "C" {
+       #include <vterm.h>
+}
+
+class TermView: public BView
+{
+       public:
+               TermView();
+               ~TermView();
+
+               void Draw(BRect updateRect);
+               
+       private:
+               VTermRect PixelsToGlyphs(BRect pixels) const;
+               BRect GlyphsToPixels(const VTermRect& glyphs) const;
+               BRect GlyphsToPixels(const int width, const int height) const;
+
+       private:
+               VTerm* fTerm;
+               VTermScreen* fTermScreen;
+               float fFontWidth;
+               float fFontHeight;
+
+               static const VTermScreenCallbacks sScreenCallbacks;
+
+               static const int kDefaultWidth = 80;
+               static const int kDefaultHeight = 25;
+               static const int kBorderSpacing = 3;
+};
diff --git a/src/apps/serialconnect/libvterm/include/vterm.h 
b/src/apps/serialconnect/libvterm/include/vterm.h
new file mode 100644
index 0000000..4728651
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/include/vterm.h
@@ -0,0 +1,245 @@
+#ifndef __VTERM_H__
+#define __VTERM_H__
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "vterm_input.h"
+
+typedef struct VTerm VTerm;
+typedef struct VTermState VTermState;
+typedef struct VTermScreen VTermScreen;
+
+typedef struct {
+  int row;
+  int col;
+} VTermPos;
+
+/* some small utility functions; we can just keep these static here */
+
+/* order points by on-screen flow order */
+static inline int vterm_pos_cmp(VTermPos a, VTermPos b)
+{
+  return (a.row == b.row) ? a.col - b.col : a.row - b.row;
+}
+
+typedef struct {
+  int start_row;
+  int end_row;
+  int start_col;
+  int end_col;
+} VTermRect;
+
+/* true if the rect contains the point */
+static inline int vterm_rect_contains(VTermRect r, VTermPos p)
+{
+  return p.row >= r.start_row && p.row < r.end_row &&
+         p.col >= r.start_col && p.col < r.end_col;
+}
+
+/* move a rect */
+static inline void vterm_rect_move(VTermRect *rect, int row_delta, int 
col_delta)
+{
+  rect->start_row += row_delta; rect->end_row += row_delta;
+  rect->start_col += col_delta; rect->end_col += col_delta;
+}
+
+/* Flag to indicate non-final subparameters in a single CSI parameter.
+ * Consider
+ *   CSI 1;2:3:4;5a
+ * 1 4 and 5 are final.
+ * 2 and 3 are non-final and will have this bit set
+ *
+ * Don't confuse this with the final byte of the CSI escape; 'a' in this case.
+ */
+#define CSI_ARG_FLAG_MORE (1<<31)
+#define CSI_ARG_MASK      (~(1<<31))
+
+#define CSI_ARG_HAS_MORE(a) ((a) & CSI_ARG_FLAG_MORE)
+#define CSI_ARG(a)          ((a) & CSI_ARG_MASK)
+
+/* Can't use -1 to indicate a missing argument; use this instead */
+#define CSI_ARG_MISSING ((1UL<<31)-1)
+
+#define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
+#define CSI_ARG_OR(a,def)     (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : 
CSI_ARG(a))
+#define CSI_ARG_COUNT(a)      (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 
0 ? 1 : CSI_ARG(a))
+
+typedef struct {
+  int (*text)(const char *bytes, size_t len, void *user);
+  int (*control)(unsigned char control, void *user);
+  int (*escape)(const char *bytes, size_t len, void *user);
+  int (*csi)(const char *leader, const long args[], int argcount, const char 
*intermed, char command, void *user);
+  int (*osc)(const char *command, size_t cmdlen, void *user);
+  int (*dcs)(const char *command, size_t cmdlen, void *user);
+  int (*resize)(int rows, int cols, void *user);
+} VTermParserCallbacks;
+
+typedef struct {
+  uint8_t red, green, blue;
+} VTermColor;
+
+typedef enum {
+  /* VTERM_VALUETYPE_NONE = 0 */
+  VTERM_VALUETYPE_BOOL = 1,
+  VTERM_VALUETYPE_INT,
+  VTERM_VALUETYPE_STRING,
+  VTERM_VALUETYPE_COLOR,
+} VTermValueType;
+
+typedef union {
+  int boolean;
+  int number;
+  char *string;
+  VTermColor color;
+} VTermValue;
+
+typedef enum {
+  /* VTERM_ATTR_NONE = 0 */
+  VTERM_ATTR_BOLD = 1,   // bool:   1, 22
+  VTERM_ATTR_UNDERLINE,  // number: 4, 21, 24
+  VTERM_ATTR_ITALIC,     // bool:   3, 23
+  VTERM_ATTR_BLINK,      // bool:   5, 25
+  VTERM_ATTR_REVERSE,    // bool:   7, 27
+  VTERM_ATTR_STRIKE,     // bool:   9, 29
+  VTERM_ATTR_FONT,       // number: 10-19
+  VTERM_ATTR_FOREGROUND, // color:  30-39 90-97
+  VTERM_ATTR_BACKGROUND, // color:  40-49 100-107
+} VTermAttr;
+
+typedef enum {
+  /* VTERM_PROP_NONE = 0 */
+  VTERM_PROP_CURSORVISIBLE = 1, // bool
+  VTERM_PROP_CURSORBLINK,       // bool
+  VTERM_PROP_ALTSCREEN,         // bool
+  VTERM_PROP_TITLE,             // string
+  VTERM_PROP_ICONNAME,          // string
+  VTERM_PROP_REVERSE,           // bool
+  VTERM_PROP_CURSORSHAPE,       // number
+} VTermProp;
+
+enum {
+  VTERM_PROP_CURSORSHAPE_BLOCK = 1,
+  VTERM_PROP_CURSORSHAPE_UNDERLINE,
+};
+
+typedef void (*VTermMouseFunc)(int x, int y, int button, int pressed, int 
modifiers, void *data);
+
+typedef struct {
+  int (*putglyph)(const uint32_t chars[], int width, VTermPos pos, void *user);
+  int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+  int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
+  int (*moverect)(VTermRect dest, VTermRect src, void *user);
+  int (*erase)(VTermRect rect, void *user);
+  int (*initpen)(void *user);
+  int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
+  int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+  int (*setmousefunc)(VTermMouseFunc func, void *data, void *user);
+  int (*bell)(void *user);
+  int (*resize)(int rows, int cols, void *user);
+} VTermStateCallbacks;
+
+typedef struct {
+  int (*damage)(VTermRect rect, void *user);
+  int (*moverect)(VTermRect dest, VTermRect src, void *user);
+  int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+  int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+  int (*setmousefunc)(VTermMouseFunc func, void *data, void *user);
+  int (*bell)(void *user);
+  int (*resize)(int rows, int cols, void *user);
+} VTermScreenCallbacks;
+
+typedef struct {
+  /* libvterm relies on this memory to be zeroed out before it is returned
+   * by the allocator. */
+  void *(*malloc)(size_t size, void *allocdata);
+  void  (*free)(void *ptr, void *allocdata);
+} VTermAllocatorFunctions;
+
+VTerm *vterm_new(int rows, int cols);
+VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions 
*funcs, void *allocdata);
+void   vterm_free(VTerm* vt);
+
+void vterm_get_size(VTerm *vt, int *rowsp, int *colsp);
+void vterm_set_size(VTerm *vt, int rows, int cols);
+
+void vterm_set_parser_callbacks(VTerm *vt, const VTermParserCallbacks 
*callbacks, void *user);
+
+VTermState *vterm_obtain_state(VTerm *vt);
+
+void vterm_state_reset(VTermState *state, int hard);
+void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks 
*callbacks, void *user);
+void vterm_state_get_cursorpos(VTermState *state, VTermPos *cursorpos);
+void vterm_state_set_default_colors(VTermState *state, VTermColor *default_fg, 
VTermColor *default_bg);
+void vterm_state_set_bold_highbright(VTermState *state, int 
bold_is_highbright);
+int  vterm_state_get_penattr(VTermState *state, VTermAttr attr, VTermValue 
*val);
+
+VTermValueType vterm_get_attr_type(VTermAttr attr);
+VTermValueType vterm_get_prop_type(VTermProp prop);
+
+VTermScreen *vterm_obtain_screen(VTerm *vt);
+
+void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen);
+void vterm_screen_set_callbacks(VTermScreen *screen, const 
VTermScreenCallbacks *callbacks, void *user);
+
+typedef enum {
+  VTERM_DAMAGE_CELL,    /* every cell */
+  VTERM_DAMAGE_ROW,     /* entire rows */
+  VTERM_DAMAGE_SCREEN,  /* entire screen */
+  VTERM_DAMAGE_SCROLL,  /* entire screen + scrollrect */
+} VTermDamageSize;
+
+void vterm_screen_flush_damage(VTermScreen *screen);
+void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size);
+
+void   vterm_screen_reset(VTermScreen *screen, int hard);
+size_t vterm_screen_get_chars(VTermScreen *screen, uint32_t *chars, size_t 
len, const VTermRect rect);
+size_t vterm_screen_get_text(VTermScreen *screen, char *str, size_t len, const 
VTermRect rect);
+
+typedef struct {
+#define VTERM_MAX_CHARS_PER_CELL 6
+  uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
+  char     width;
+  struct {
+    unsigned int bold      : 1;
+    unsigned int underline : 2;
+    unsigned int italic    : 1;
+    unsigned int blink     : 1;
+    unsigned int reverse   : 1;
+    unsigned int strike    : 1;
+    unsigned int font      : 4; /* 0 to 9 */
+  } attrs;
+  VTermColor fg, bg;
+} VTermScreenCell;
+
+void vterm_screen_get_cell(VTermScreen *screen, VTermPos pos, VTermScreenCell 
*cell);
+
+int vterm_screen_is_eol(VTermScreen *screen, VTermPos pos);
+
+void vterm_input_push_char(VTerm *vt, VTermModifier state, uint32_t c);
+void vterm_input_push_key(VTerm *vt, VTermModifier state, VTermKey key);
+
+void vterm_parser_set_utf8(VTerm *vt, int is_utf8);
+void vterm_push_bytes(VTerm *vt, const char *bytes, size_t len);
+
+size_t vterm_output_bufferlen(VTerm *vt); /* deprecated */
+
+size_t vterm_output_get_buffer_size(VTerm *vt);
+size_t vterm_output_get_buffer_current(VTerm *vt);
+size_t vterm_output_get_buffer_remaining(VTerm *vt);
+
+size_t vterm_output_bufferread(VTerm *vt, char *buffer, size_t len);
+
+void vterm_scroll_rect(VTermRect rect,
+                       int downward,
+                       int rightward,
+                       int (*moverect)(VTermRect src, VTermRect dest, void 
*user),
+                       int (*eraserect)(VTermRect rect, void *user),
+                       void *user);
+
+void vterm_copy_cells(VTermRect dest,
+                      VTermRect src,
+                      void (*copycell)(VTermPos dest, VTermPos src, void 
*user),
+                      void *user);
+
+#endif
diff --git a/src/apps/serialconnect/libvterm/include/vterm_input.h 
b/src/apps/serialconnect/libvterm/include/vterm_input.h
new file mode 100644
index 0000000..69cc6cb
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/include/vterm_input.h
@@ -0,0 +1,39 @@
+#ifndef __VTERM_INPUT_H__
+#define __VTERM_INPUT_H__
+
+typedef enum {
+  VTERM_MOD_NONE  = 0x00,
+  VTERM_MOD_SHIFT = 0x01,
+  VTERM_MOD_ALT   = 0x02,
+  VTERM_MOD_CTRL  = 0x04,
+} VTermModifier;
+
+typedef enum {
+  VTERM_KEY_NONE,
+
+  VTERM_KEY_ENTER,
+  VTERM_KEY_TAB,
+  VTERM_KEY_BACKSPACE,
+  VTERM_KEY_ESCAPE,
+
+  VTERM_KEY_UP,
+  VTERM_KEY_DOWN,
+  VTERM_KEY_LEFT,
+  VTERM_KEY_RIGHT,
+
+  VTERM_KEY_INS,
+  VTERM_KEY_DEL,
+  VTERM_KEY_HOME,
+  VTERM_KEY_END,
+  VTERM_KEY_PAGEUP,
+  VTERM_KEY_PAGEDOWN,
+
+  VTERM_KEY_FUNCTION_0,
+  VTERM_KEY_FUNCTION_MAX = VTERM_KEY_FUNCTION_0 + 255,
+
+  VTERM_KEY_MAX, // Must be last
+} VTermKey;
+
+#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0+(n))
+
+#endif
diff --git a/src/apps/serialconnect/libvterm/src/encoding.c 
b/src/apps/serialconnect/libvterm/src/encoding.c
new file mode 100644
index 0000000..373c9bc
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/src/encoding.c
@@ -0,0 +1,221 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+
+#define UNICODE_INVALID 0xFFFD
+
+#ifdef DEBUG
+# define DEBUG_PRINT_UTF8
+#endif
+
+struct UTF8DecoderData {
+  // number of bytes remaining in this codepoint
+  int bytes_remaining;
+
+  // number of bytes total in this codepoint once it's finished
+  // (for detecting overlongs)
+  int bytes_total;
+
+  int this_cp;
+};
+
+static void init_utf8(VTermEncoding *enc, void *data_)
+{
+  struct UTF8DecoderData *data = data_;
+
+  data->bytes_remaining = 0;
+  data->bytes_total     = 0;
+}
+
+static void decode_utf8(VTermEncoding *enc, void *data_,
+                        uint32_t cp[], int *cpi, int cplen,
+                        const char bytes[], size_t *pos, size_t bytelen)
+{
+  struct UTF8DecoderData *data = data_;
+
+#ifdef DEBUG_PRINT_UTF8
+  printf("BEGIN UTF-8\n");
+#endif
+
+  for( ; *pos < bytelen; (*pos)++) {
+    unsigned char c = bytes[*pos];
+
+#ifdef DEBUG_PRINT_UTF8
+    printf(" pos=%zd c=%02x rem=%d\n", *pos, c, data->bytes_remaining);
+#endif
+
+    if(c < 0x20)
+      return;
+
+    else if(c >= 0x20 && c < 0x80) {
+      if(data->bytes_remaining)
+        cp[(*cpi)++] = UNICODE_INVALID;
+
+      cp[(*cpi)++] = c;
+#ifdef DEBUG_PRINT_UTF8
+      printf(" UTF-8 char: U+%04x\n", c);
+#endif
+      data->bytes_remaining = 0;
+    }
+
+    else if(c >= 0x80 && c < 0xc0) {
+      if(!data->bytes_remaining) {
+        cp[(*cpi)++] = UNICODE_INVALID;
+        continue;
+      }
+
+      data->this_cp <<= 6;
+      data->this_cp |= c & 0x3f;
+      data->bytes_remaining--;
+
+      if(!data->bytes_remaining) {
+#ifdef DEBUG_PRINT_UTF8
+        printf(" UTF-8 raw char U+%04x bytelen=%d ", data->this_cp, 
data->bytes_total);
+#endif
+        // Check for overlong sequences
+        switch(data->bytes_total) {
+        case 2:
+          if(data->this_cp <  0x0080) data->this_cp = UNICODE_INVALID; break;
+        case 3:
+          if(data->this_cp <  0x0800) data->this_cp = UNICODE_INVALID; break;
+        case 4:
+          if(data->this_cp < 0x10000) data->this_cp = UNICODE_INVALID; break;
+        case 5:
+          if(data->this_cp < 0x200000) data->this_cp = UNICODE_INVALID; break;
+        case 6:
+          if(data->this_cp < 0x4000000) data->this_cp = UNICODE_INVALID; break;
+        }
+        // Now look for plain invalid ones
+        if((data->this_cp >= 0xD800 && data->this_cp <= 0xDFFF) ||
+           data->this_cp == 0xFFFE ||
+           data->this_cp == 0xFFFF)
+          data->this_cp = UNICODE_INVALID;
+#ifdef DEBUG_PRINT_UTF8
+        printf(" char: U+%04x\n", data->this_cp);
+#endif
+        cp[(*cpi)++] = data->this_cp;
+      }
+    }
+
+    else if(c >= 0xc0 && c < 0xe0) {
+      if(data->bytes_remaining)
+        cp[(*cpi)++] = UNICODE_INVALID;
+
+      data->this_cp = c & 0x1f;
+      data->bytes_total = 2;
+      data->bytes_remaining = 1;
+    }
+
+    else if(c >= 0xe0 && c < 0xf0) {
+      if(data->bytes_remaining)
+        cp[(*cpi)++] = UNICODE_INVALID;
+
+      data->this_cp = c & 0x0f;
+      data->bytes_total = 3;
+      data->bytes_remaining = 2;
+    }
+
+    else if(c >= 0xf0 && c < 0xf8) {
+      if(data->bytes_remaining)
+        cp[(*cpi)++] = UNICODE_INVALID;
+
+      data->this_cp = c & 0x07;
+      data->bytes_total = 4;
+      data->bytes_remaining = 3;
+    }
+
+    else if(c >= 0xf8 && c < 0xfc) {
+      if(data->bytes_remaining)
+        cp[(*cpi)++] = UNICODE_INVALID;
+
+      data->this_cp = c & 0x03;
+      data->bytes_total = 5;
+      data->bytes_remaining = 4;
+    }
+
+    else if(c >= 0xfc && c < 0xfe) {
+      if(data->bytes_remaining)
+        cp[(*cpi)++] = UNICODE_INVALID;
+
+      data->this_cp = c & 0x01;
+      data->bytes_total = 6;
+      data->bytes_remaining = 5;
+    }
+
+    else {
+      cp[(*cpi)++] = UNICODE_INVALID;
+    }
+  }
+}
+
+static VTermEncoding encoding_utf8 = {
+  .init   = &init_utf8,
+  .decode = &decode_utf8,
+};
+
+static void decode_usascii(VTermEncoding *enc, void *data,
+                           uint32_t cp[], int *cpi, int cplen,
+                           const char bytes[], size_t *pos, size_t bytelen)
+{
+  for(; *pos < bytelen; (*pos)++) {
+    unsigned char c = bytes[*pos];
+
+    if(c < 0x20 || c >= 0x80)
+      return;
+
+    cp[(*cpi)++] = c;
+  }
+}
+
+static VTermEncoding encoding_usascii = {
+  .decode = &decode_usascii,
+};
+
+struct StaticTableEncoding {
+  const VTermEncoding enc;
+  const uint32_t chars[128];
+};
+
+static void decode_table(VTermEncoding *enc, void *data,
+                         uint32_t cp[], int *cpi, int cplen,
+                         const char bytes[], size_t *pos, size_t bytelen)
+{
+  struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
+
+  for(; *pos < bytelen; (*pos)++) {
+    unsigned char c = (bytes[*pos]) & 0x7f;
+
+    if(c < 0x20)
+      return;
+
+    if(table->chars[c])
+      cp[(*cpi)++] = table->chars[c];
+    else
+      cp[(*cpi)++] = c;
+  }
+}
+
+#include "encoding/DECdrawing.inc"
+#include "encoding/uk.inc"
+
+static struct {
+  VTermEncodingType type;
+  char designation;
+  VTermEncoding *enc;
+}
+encodings[] = {
+  { ENC_UTF8,      'u', &encoding_utf8 },
+  { ENC_SINGLE_94, '0', (VTermEncoding*)&encoding_DECdrawing },
+  { ENC_SINGLE_94, 'A', (VTermEncoding*)&encoding_uk },
+  { ENC_SINGLE_94, 'B', &encoding_usascii },
+  { 0, 0 },
+};
+
+VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
+{
+  int i;
+  for(i = 0; encodings[i].designation; i++)
+    if(encodings[i].type == type && encodings[i].designation == designation)
+      return encodings[i].enc;
+  return NULL;
+}
diff --git a/src/apps/serialconnect/libvterm/src/encoding/DECdrawing.inc 
b/src/apps/serialconnect/libvterm/src/encoding/DECdrawing.inc
new file mode 100644
index 0000000..47093ed
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/src/encoding/DECdrawing.inc
@@ -0,0 +1,36 @@
+static const struct StaticTableEncoding encoding_DECdrawing = {
+  { .decode = &decode_table },
+  {
+    [0x60] = 0x25C6,
+    [0x61] = 0x2592,
+    [0x62] = 0x2409,
+    [0x63] = 0x240C,
+    [0x64] = 0x240D,
+    [0x65] = 0x240A,
+    [0x66] = 0x00B0,
+    [0x67] = 0x00B1,
+    [0x68] = 0x2424,
+    [0x69] = 0x240B,
+    [0x6a] = 0x2518,
+    [0x6b] = 0x2510,
+    [0x6c] = 0x250C,
+    [0x6d] = 0x2514,
+    [0x6e] = 0x253C,
+    [0x6f] = 0x23BA,
+    [0x70] = 0x23BB,
+    [0x71] = 0x2500,
+    [0x72] = 0x23BC,
+    [0x73] = 0x23BD,
+    [0x74] = 0x251C,
+    [0x75] = 0x2524,
+    [0x76] = 0x2534,
+    [0x77] = 0x252C,
+    [0x78] = 0x2502,
+    [0x79] = 0x2A7D,
+    [0x7a] = 0x2A7E,
+    [0x7b] = 0x03C0,
+    [0x7c] = 0x2260,
+    [0x7d] = 0x00A3,
+    [0x7e] = 0x00B7,
+  }
+};
diff --git a/src/apps/serialconnect/libvterm/src/encoding/DECdrawing.tbl 
b/src/apps/serialconnect/libvterm/src/encoding/DECdrawing.tbl
new file mode 100644
index 0000000..6e19c50
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/src/encoding/DECdrawing.tbl
@@ -0,0 +1,31 @@
+6/0 = U+25C6 # BLACK DIAMOND
+6/1 = U+2592 # MEDIUM SHADE (checkerboard)
+6/2 = U+2409 # SYMBOL FOR HORIZONTAL TAB
+6/3 = U+240C # SYMBOL FOR FORM FEED
+6/4 = U+240D # SYMBOL FOR CARRIAGE RETURN
+6/5 = U+240A # SYMBOL FOR LINE FEED
+6/6 = U+00B0 # DEGREE SIGN
+6/7 = U+00B1 # PLUS-MINUS SIGN (plus or minus)
+6/8 = U+2424 # SYMBOL FOR NEW LINE
+6/9 = U+240B # SYMBOL FOR VERTICAL TAB
+6/10 = U+2518 # BOX DRAWINGS LIGHT UP AND LEFT (bottom-right corner)
+6/11 = U+2510 # BOX DRAWINGS LIGHT DOWN AND LEFT (top-right corner)
+6/12 = U+250C # BOX DRAWINGS LIGHT DOWN AND RIGHT (top-left corner)
+6/13 = U+2514 # BOX DRAWINGS LIGHT UP AND RIGHT (bottom-left corner)
+6/14 = U+253C # BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL (crossing lines)
+6/15 = U+23BA # HORIZONTAL SCAN LINE-1
+7/0 = U+23BB # HORIZONTAL SCAN LINE-3
+7/1 = U+2500 # BOX DRAWINGS LIGHT HORIZONTAL
+7/2 = U+23BC # HORIZONTAL SCAN LINE-7
+7/3 = U+23BD # HORIZONTAL SCAN LINE-9
+7/4 = U+251C # BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+7/5 = U+2524 # BOX DRAWINGS LIGHT VERTICAL AND LEFT
+7/6 = U+2534 # BOX DRAWINGS LIGHT UP AND HORIZONTAL
+7/7 = U+252C # BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+7/8 = U+2502 # BOX DRAWINGS LIGHT VERTICAL
+7/9 = U+2A7D # LESS-THAN OR SLANTED EQUAL-TO
+7/10 = U+2A7E # GREATER-THAN OR SLANTED EQUAL-TO
+7/11 = U+03C0 # GREEK SMALL LETTER PI
+7/12 = U+2260 # NOT EQUAL TO
+7/13 = U+00A3 # POUND SIGN
+7/14 = U+00B7 # MIDDLE DOT
diff --git a/src/apps/serialconnect/libvterm/src/encoding/uk.inc 
b/src/apps/serialconnect/libvterm/src/encoding/uk.inc
new file mode 100644
index 0000000..da1445d
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/src/encoding/uk.inc
@@ -0,0 +1,6 @@
+static const struct StaticTableEncoding encoding_uk = {
+  { .decode = &decode_table },
+  {
+    [0x23] = 0x00a3,
+  }
+};
diff --git a/src/apps/serialconnect/libvterm/src/encoding/uk.tbl 
b/src/apps/serialconnect/libvterm/src/encoding/uk.tbl
new file mode 100644
index 0000000..b27b1a2
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/src/encoding/uk.tbl
@@ -0,0 +1 @@
+2/3 = "£"
diff --git a/src/apps/serialconnect/libvterm/src/input.c 
b/src/apps/serialconnect/libvterm/src/input.c
new file mode 100644
index 0000000..f851162
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/src/input.c
@@ -0,0 +1,171 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+
+#include "utf8.h"
+
+void vterm_input_push_char(VTerm *vt, VTermModifier mod, uint32_t c)
+{
+  int needs_CSIu;
+  /* The shift modifier is never important for Unicode characters
+   * apart from Space
+   */
+  if(c != ' ')
+    mod &= ~VTERM_MOD_SHIFT;
+  /* However, since Shift-Space is too easy to mistype accidentally, remove
+   * shift if it's the only modifier
+   */
+  else if(mod == VTERM_MOD_SHIFT)
+    mod = 0;
+
+  if(mod == 0) {
+    // Normal text - ignore just shift
+    char str[6];
+    int seqlen = fill_utf8(c, str);
+    vterm_push_output_bytes(vt, str, seqlen);
+    return;
+  }
+
+  switch(c) {
+    /* Special Ctrl- letters that can't be represented elsewise */
+    case 'h': case 'i': case 'j': case 'm': case '[':
+      needs_CSIu = 1;
+      break;
+    /* Ctrl-\ ] ^ _ don't need CSUu */
+    case '\\': case ']': case '^': case '_':
+      needs_CSIu = 0;
+      break;
+    /* All other characters needs CSIu except for letters a-z */
+    default:
+      needs_CSIu = (c < 'a' || c > 'z');
+  }
+
+  /* ALT we can just prefix with ESC; anything else requires CSI u */
+  if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) {
+    vterm_push_output_sprintf(vt, "\e[%d;%du", c, mod+1);
+    return;
+  }
+
+  if(mod & VTERM_MOD_CTRL)
+    c &= 0x1f;
+
+  vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? "\e" : "", c);
+}
+
+typedef struct {
+  enum {
+    KEYCODE_NONE,
+    KEYCODE_LITERAL,
+    KEYCODE_TAB,
+    KEYCODE_ENTER,
+    KEYCODE_CSI,
+    KEYCODE_CSI_CURSOR,
+    KEYCODE_CSINUM,
+  } type;
+  char literal;
+  int csinum;
+} keycodes_s;
+
+keycodes_s keycodes[] = {
+  { KEYCODE_NONE }, // NONE
+
+  { KEYCODE_ENTER,   '\r'   }, // ENTER
+  { KEYCODE_TAB,     '\t'   }, // TAB
+  { KEYCODE_LITERAL, '\x7f' }, // BACKSPACE == ASCII DEL
+  { KEYCODE_LITERAL, '\e'   }, // ESCAPE
+
+  { KEYCODE_CSI_CURSOR, 'A' }, // UP
+  { KEYCODE_CSI_CURSOR, 'B' }, // DOWN
+  { KEYCODE_CSI_CURSOR, 'D' }, // LEFT
+  { KEYCODE_CSI_CURSOR, 'C' }, // RIGHT
+
+  { KEYCODE_CSINUM, '~', 2 },  // INS
+  { KEYCODE_CSINUM, '~', 3 },  // DEL
+  { KEYCODE_CSI_CURSOR, 'H' }, // HOME
+  { KEYCODE_CSI_CURSOR, 'F' }, // END
+  { KEYCODE_CSINUM, '~', 5 },  // PAGEUP
+  { KEYCODE_CSINUM, '~', 6 },  // PAGEDOWN
+
+  { KEYCODE_NONE },            // F0 - shouldn't happen
+  { KEYCODE_CSI_CURSOR, 'P' }, // F1
+  { KEYCODE_CSI_CURSOR, 'Q' }, // F2
+  { KEYCODE_CSI_CURSOR, 'R' }, // F3
+  { KEYCODE_CSI_CURSOR, 'S' }, // F4
+  { KEYCODE_CSINUM, '~', 15 }, // F5
+  { KEYCODE_CSINUM, '~', 17 }, // F6
+  { KEYCODE_CSINUM, '~', 18 }, // F7
+  { KEYCODE_CSINUM, '~', 19 }, // F8
+  { KEYCODE_CSINUM, '~', 20 }, // F9
+  { KEYCODE_CSINUM, '~', 21 }, // F10
+  { KEYCODE_CSINUM, '~', 23 }, // F11
+  { KEYCODE_CSINUM, '~', 24 }, // F12
+};
+
+void vterm_input_push_key(VTerm *vt, VTermModifier mod, VTermKey key)
+{
+  keycodes_s k;
+  /* Since Shift-Enter and Shift-Backspace are too easy to mistype
+   * accidentally, remove shift if it's the only modifier
+   */
+  if((key == VTERM_KEY_ENTER || key == VTERM_KEY_BACKSPACE) && mod == 
VTERM_MOD_SHIFT)
+    mod = 0;
+
+  if(key == VTERM_KEY_NONE || key >= VTERM_KEY_MAX)
+    return;
+
+  if(key >= sizeof(keycodes)/sizeof(keycodes[0]))
+    return;
+
+  k = keycodes[key];
+
+  switch(k.type) {
+  case KEYCODE_NONE:
+    break;
+
+  case KEYCODE_TAB:
+    /* Shift-Tab is CSI Z but plain Tab is 0x09 */
+    if(mod == VTERM_MOD_SHIFT)
+      vterm_push_output_sprintf(vt, "\e[Z");
+    else if(mod & VTERM_MOD_SHIFT)
+      vterm_push_output_sprintf(vt, "\e[1;%dZ", mod+1);
+    else
+      goto literal;
+    break;
+
+  case KEYCODE_ENTER:
+    /* Enter is CRLF in newline mode, but just LF in linefeed */
+    if(vt->state->mode.newline)
+      vterm_push_output_sprintf(vt, "\r\n");
+    else
+      goto literal;
+    break;
+
+literal:
+  case KEYCODE_LITERAL:
+    if(mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL))
+      vterm_push_output_sprintf(vt, "\e[%d;%du", k.literal, mod+1);
+    else
+      vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? "\e%c" : "%c", 
k.literal);
+    break;
+
+  case KEYCODE_CSI_CURSOR:
+    if(vt->state->mode.cursor && mod == 0) {
+      vterm_push_output_sprintf(vt, "\eO%c", k.literal);
+      break;
+    }
+    /* else FALLTHROUGH */
+  case KEYCODE_CSI:
+    if(mod == 0)
+      vterm_push_output_sprintf(vt, "\e[%c", k.literal);
+    else
+      vterm_push_output_sprintf(vt, "\e[1;%d%c", mod + 1, k.literal);
+    break;
+
+  case KEYCODE_CSINUM:
+    if(mod == 0)
+      vterm_push_output_sprintf(vt, "\e[%d%c", k.csinum, k.literal);
+    else
+      vterm_push_output_sprintf(vt, "\e[%d;%d%c", k.csinum, mod + 1, 
k.literal);
+    break;
+  }
+}
diff --git a/src/apps/serialconnect/libvterm/src/parser.c 
b/src/apps/serialconnect/libvterm/src/parser.c
new file mode 100644
index 0000000..cfd7e2c
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/src/parser.c
@@ -0,0 +1,344 @@
+#include "vterm_internal.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#define CSI_ARGS_MAX 16
+#define CSI_LEADER_MAX 16
+#define CSI_INTERMED_MAX 16
+
+static void do_control(VTerm *vt, unsigned char control)
+{
+  if(vt->parser_callbacks && vt->parser_callbacks->control)
+    if((*vt->parser_callbacks->control)(control, vt->cbdata))
+      return;
+
+  fprintf(stderr, "libvterm: Unhandled control 0x%02x\n", control);
+}
+
+static void do_string_csi(VTerm *vt, const char *args, size_t arglen, char 
command)
+{
+  size_t i = 0;
+
+  int leaderlen = 0;
+  char leader[CSI_LEADER_MAX];
+  int argcount = 1; // Always at least 1 arg
+  long csi_args[CSI_ARGS_MAX];
+  int argi;
+  int intermedlen = 0;
+  char intermed[CSI_INTERMED_MAX];
+
+  // Extract leader bytes 0x3c to 0x3f
+  for( ; i < arglen; i++) {
+    if(args[i] < 0x3c || args[i] > 0x3f)
+      break;
+    if(leaderlen < CSI_LEADER_MAX-1)
+      leader[leaderlen++] = args[i];
+  }
+
+  leader[leaderlen] = 0;
+
+  for( ; i < arglen; i++)
+    if(args[i] == 0x3b || args[i] == 0x3a) // ; or :
+      argcount++;
+
+  /* TODO: Consider if these buffers should live in the VTerm struct itself */
+  if(argcount > CSI_ARGS_MAX)
+    argcount = CSI_ARGS_MAX;
+
+  for(argi = 0; argi < argcount; argi++)
+    csi_args[argi] = CSI_ARG_MISSING;
+
+  argi = 0;
+  for(i = leaderlen; i < arglen && argi < argcount; i++) {
+    switch(args[i]) {
+    case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+    case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
+      if(csi_args[argi] == CSI_ARG_MISSING)
+        csi_args[argi] = 0;
+      csi_args[argi] *= 10;
+      csi_args[argi] += args[i] - '0';
+      break;
+    case 0x3a:
+      csi_args[argi] |= CSI_ARG_FLAG_MORE;
+      /* FALLTHROUGH */
+    case 0x3b:
+      argi++;
+      break;
+    default:
+      goto done_leader;
+    }
+  }
+done_leader: ;
+
+
+  for( ; i < arglen; i++) {
+    if((args[i] & 0xf0) != 0x20)
+      break;
+
+    if(intermedlen < CSI_INTERMED_MAX-1)
+      intermed[intermedlen++] = args[i];
+  }
+
+  intermed[intermedlen] = 0;
+
+  if(i < arglen) {
+    fprintf(stderr, "libvterm: TODO unhandled CSI bytes \"%.*s\"\n", 
(int)(arglen - i), args + i);
+  }
+
+  //printf("Parsed CSI args %.*s as:\n", arglen, args);
+  //printf(" leader: %s\n", leader);
+  //for(argi = 0; argi < argcount; argi++) {
+  //  printf(" %lu", CSI_ARG(csi_args[argi]));
+  //  if(!CSI_ARG_HAS_MORE(csi_args[argi]))
+  //    printf("\n");
+  //printf(" intermed: %s\n", intermed);
+  //}
+
+  if(vt->parser_callbacks && vt->parser_callbacks->csi)
+    if((*vt->parser_callbacks->csi)(leaderlen ? leader : NULL, csi_args, 
argcount, intermedlen ? intermed : NULL, command, vt->cbdata))
+      return;
+
+  fprintf(stderr, "libvterm: Unhandled CSI %.*s %c\n", (int)arglen, args, 
command);
+}
+
+static void append_strbuffer(VTerm *vt, const char *str, size_t len)
+{
+  if(len > vt->strbuffer_len - vt->strbuffer_cur) {
+    len = vt->strbuffer_len - vt->strbuffer_cur;
+    fprintf(stderr, "Truncating strbuffer preserve to %zd bytes\n", len);
+  }
+
+  if(len > 0) {
+    strncpy(vt->strbuffer + vt->strbuffer_cur, str, len);
+    vt->strbuffer_cur += len;
+  }
+}
+
+static size_t do_string(VTerm *vt, const char *str_frag, size_t len)
+{
+  size_t eaten;
+
+  if(vt->strbuffer_cur) {
+    if(str_frag)
+      append_strbuffer(vt, str_frag, len);
+
+    str_frag = vt->strbuffer;
+    len = vt->strbuffer_cur;
+  }
+  else if(!str_frag) {
+    fprintf(stderr, "parser.c: TODO: No strbuffer _and_ no final 
fragment???\n");
+    len = 0;
+  }
+
+  vt->strbuffer_cur = 0;
+
+  switch(vt->parser_state) {
+  case NORMAL:
+    if(vt->parser_callbacks && vt->parser_callbacks->text)
+      if((eaten = (*vt->parser_callbacks->text)(str_frag, len, vt->cbdata)))
+        return eaten;
+
+    fprintf(stderr, "libvterm: Unhandled text (%zu chars)\n", len);
+    return 0;
+
+  case ESC:
+    if(len == 1 && str_frag[0] >= 0x40 && str_frag[0] < 0x60) {
+      // C1 emulations using 7bit clean
+      // ESC 0x40 == 0x80
+      do_control(vt, str_frag[0] + 0x40);
+      return 0;
+    }
+
+    if(vt->parser_callbacks && vt->parser_callbacks->escape)
+      if((*vt->parser_callbacks->escape)(str_frag, len, vt->cbdata))
+        return 0;
+
+    fprintf(stderr, "libvterm: Unhandled escape ESC 0x%02x\n", 
str_frag[len-1]);
+    return 0;
+
+  case CSI:
+    do_string_csi(vt, str_frag, len - 1, str_frag[len - 1]);
+    return 0;
+
+  case OSC:
+    if(vt->parser_callbacks && vt->parser_callbacks->osc)
+      if((*vt->parser_callbacks->osc)(str_frag, len, vt->cbdata))
+        return 0;
+
+    fprintf(stderr, "libvterm: Unhandled OSC %.*s\n", (int)len, str_frag);
+    return 0;
+
+  case DCS:
+    if(vt->parser_callbacks && vt->parser_callbacks->dcs)
+      if((*vt->parser_callbacks->dcs)(str_frag, len, vt->cbdata))
+        return 0;
+
+    fprintf(stderr, "libvterm: Unhandled DCS %.*s\n", (int)len, str_frag);
+    return 0;
+
+  case ESC_IN_OSC:
+  case ESC_IN_DCS:
+    fprintf(stderr, "libvterm: ARGH! Should never do_string() in 
ESC_IN_{OSC,DCS}\n");
+    return 0;
+  }
+
+  return 0;
+}
+
+void vterm_push_bytes(VTerm *vt, const char *bytes, size_t len)
+{
+  size_t pos = 0;
+  const char *string_start = NULL;
+
+  switch(vt->parser_state) {
+  case NORMAL:
+    string_start = NULL;
+    break;
+  case ESC:
+  case ESC_IN_OSC:
+  case ESC_IN_DCS:
+  case CSI:
+  case OSC:
+  case DCS:
+    string_start = bytes;
+    break;
+  }
+
+#define ENTER_STRING_STATE(st) do { vt->parser_state = st; string_start = 
bytes + pos + 1; } while(0)
+#define ENTER_NORMAL_STATE()   do { vt->parser_state = NORMAL; string_start = 
NULL; } while(0)
+
+  for( ; pos < len; pos++) {
+    unsigned char c = bytes[pos];
+
+    if(c == 0x00 || c == 0x7f) { // NUL, DEL
+      if(vt->parser_state != NORMAL) {
+        append_strbuffer(vt, string_start, bytes + pos - string_start);
+        string_start = bytes + pos + 1;
+      }
+      continue;
+    }
+    if(c == 0x18 || c == 0x1a) { // CAN, SUB
+      ENTER_NORMAL_STATE();
+      continue;
+    }
+    else if(c == 0x1b) { // ESC
+      if(vt->parser_state == OSC)
+        vt->parser_state = ESC_IN_OSC;
+      else if(vt->parser_state == DCS)
+        vt->parser_state = ESC_IN_DCS;
+      else
+        ENTER_STRING_STATE(ESC);
+      continue;
+    }
+    else if(c == 0x07 &&  // BEL, can stand for ST in OSC or DCS state
+            (vt->parser_state == OSC || vt->parser_state == DCS)) {
+      // fallthrough
+    }
+    else if(c < 0x20) { // other C0
+      if(vt->parser_state != NORMAL)
+        append_strbuffer(vt, string_start, bytes + pos - string_start);
+      do_control(vt, c);
+      if(vt->parser_state != NORMAL)
+        string_start = bytes + pos + 1;
+      continue;
+    }
+    // else fallthrough
+
+    switch(vt->parser_state) {
+    case ESC_IN_OSC:
+    case ESC_IN_DCS:
+      if(c == 0x5c) { // ST
+        switch(vt->parser_state) {
+          case ESC_IN_OSC: vt->parser_state = OSC; break;
+          case ESC_IN_DCS: vt->parser_state = DCS; break;
+          default: break;
+        }
+        do_string(vt, string_start, bytes + pos - string_start - 1);
+        ENTER_NORMAL_STATE();
+        break;
+      }
+      vt->parser_state = ESC;
+      string_start = bytes + pos;
+      // else fallthrough
+
+    case ESC:
+      switch(c) {
+      case 0x50: // DCS
+        ENTER_STRING_STATE(DCS);
+        break;
+      case 0x5b: // CSI
+        ENTER_STRING_STATE(CSI);
+        break;
+      case 0x5d: // OSC
+        ENTER_STRING_STATE(OSC);
+        break;
+      default:
+        if(c >= 0x30 && c < 0x7f) {
+          /* +1 to pos because we want to include this command byte as well */
+          do_string(vt, string_start, bytes + pos - string_start + 1);
+          ENTER_NORMAL_STATE();
+        }
+        else if(c >= 0x20 && c < 0x30) {
+          /* intermediate byte */
+        }
+        else {
+          fprintf(stderr, "TODO: Unhandled byte %02x in Escape\n", c);
+        }
+      }
+      break;
+
+    case CSI:
+      if(c >= 0x40 && c <= 0x7f) {
+        /* +1 to pos because we want to include this command byte as well */
+        do_string(vt, string_start, bytes + pos - string_start + 1);
+        ENTER_NORMAL_STATE();
+      }
+      break;
+
+    case OSC:
+    case DCS:
+      if(c == 0x07 || (c == 0x9c && !vt->is_utf8)) {
+        do_string(vt, string_start, bytes + pos - string_start);
+        ENTER_NORMAL_STATE();
+      }
+      break;
+
+    case NORMAL:
+      if(c >= 0x80 && c < 0xa0 && !vt->is_utf8) {
+        switch(c) {
+        case 0x90: // DCS
+          ENTER_STRING_STATE(DCS);
+          break;
+        case 0x9b: // CSI
+          ENTER_STRING_STATE(CSI);
+          break;
+        case 0x9d: // OSC
+          ENTER_STRING_STATE(OSC);
+          break;
+        default:
+          do_control(vt, c);
+          break;
+        }
+      }
+      else {
+        size_t text_eaten = do_string(vt, bytes + pos, len - pos);
+
+        if(text_eaten == 0) {
+          string_start = bytes + pos;
+          goto pause;
+        }
+
+        pos += (text_eaten - 1); // we'll ++ it again in a moment
+      }
+      break;
+    }
+  }
+
+pause:
+  if(string_start && string_start < len + bytes) {
+    size_t remaining = len - (string_start - bytes);
+    append_strbuffer(vt, string_start, remaining);
+  }
+}
diff --git a/src/apps/serialconnect/libvterm/src/pen.c 
b/src/apps/serialconnect/libvterm/src/pen.c
new file mode 100644
index 0000000..3f42b60
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/src/pen.c
@@ -0,0 +1,373 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+
+static const VTermColor ansi_colors[] = {
+  /* R    G    B */
+  {   0,   0,   0 }, // black
+  { 224,   0,   0 }, // red
+  {   0, 224,   0 }, // green
+  { 224, 224,   0 }, // yellow
+  {   0,   0, 224 }, // blue
+  { 224,   0, 224 }, // magenta
+  {   0, 224, 224 }, // cyan
+  { 224, 224, 224 }, // white == light grey
+
+  // high intensity
+  { 128, 128, 128 }, // black
+  { 255,  64,  64 }, // red
+  {  64, 255,  64 }, // green
+  { 255, 255,  64 }, // yellow
+  {  64,  64, 255 }, // blue
+  { 255,  64, 255 }, // magenta
+  {  64, 255, 255 }, // cyan
+  { 255, 255, 255 }, // white for real
+};
+
+static int ramp6[] = {
+  0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF,
+};
+
+static int ramp24[] = {
+  0x00, 0x0B, 0x16, 0x21, 0x2C, 0x37, 0x42, 0x4D, 0x58, 0x63, 0x6E, 0x79,
+  0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF,
+};
+
+static void lookup_colour_ansi(long index, char is_bg, VTermColor *col)
+{
+  if(index >= 0 && index < 16) {
+    *col = ansi_colors[index];
+  }
+}
+
+static int lookup_colour(int palette, const long args[], int argcount, char 
is_bg, VTermColor *col)
+{
+  long index;
+
+  switch(palette) {
+  case 2: // RGB mode - 3 args contain colour values directly
+    if(argcount < 3)
+      return argcount;
+
+    col->red   = CSI_ARG(args[0]);
+    col->green = CSI_ARG(args[1]);
+    col->blue  = CSI_ARG(args[2]);
+
+    return 3;
+
+  case 5: // XTerm 256-colour mode
+    index = argcount ? CSI_ARG_OR(args[0], -1) : -1;
+
+    if(index >= 0 && index < 16) {
+      // Normal 8 colours or high intensity - parse as palette 0
+      lookup_colour_ansi(index, is_bg, col);
+    }
+    else if(index >= 16 && index < 232) {
+      // 216-colour cube
+      index -= 16;
+
+      col->blue  = ramp6[index     % 6];
+      col->green = ramp6[index/6   % 6];
+      col->red   = ramp6[index/6/6 % 6];
+    }
+    else if(index >= 232 && index < 256) {
+      // 24 greyscales
+      index -= 232;
+
+      col->red   = ramp24[index];
+      col->green = ramp24[index];
+      col->blue  = ramp24[index];
+    }
+
+    return argcount ? 1 : 0;
+
+  default:
+    fprintf(stderr, "Unrecognised colour palette %d\n", palette);
+    return 0;
+  }
+}
+
+// Some conveniences
+
+static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type, 
VTermValue *val)
+{
+#ifdef DEBUG
+  if(type != vterm_get_attr_type(attr)) {
+    fprintf(stderr, "Cannot set attr %d as it has type %d, not type %d\n",
+        attr, vterm_get_attr_type(attr), type);
+    return;
+  }
+#endif
+  if(state->callbacks && state->callbacks->setpenattr)
+    (*state->callbacks->setpenattr)(attr, val, state->cbdata);
+}
+
+static void setpenattr_bool(VTermState *state, VTermAttr attr, int boolean)
+{
+  VTermValue val = { .boolean = boolean };
+  setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
+}
+
+static void setpenattr_int(VTermState *state, VTermAttr attr, int number)
+{
+  VTermValue val = { .number = number };
+  setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
+}
+
+static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color)
+{
+  VTermValue val = { .color = color };
+  setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
+}
+
+static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
+{
+  VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : 
&state->pen.fg;
+
+  lookup_colour_ansi(col, attr == VTERM_ATTR_BACKGROUND, colp);
+
+  setpenattr_col(state, attr, *colp);
+}
+
+void vterm_state_resetpen(VTermState *state)
+{
+  state->pen.bold = 0;      setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
+  state->pen.underline = 0; setpenattr_int( state, VTERM_ATTR_UNDERLINE, 0);
+  state->pen.italic = 0;    setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
+  state->pen.blink = 0;     setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
+  state->pen.reverse = 0;   setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
+  state->pen.strike = 0;    setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
+  state->pen.font = 0;      setpenattr_int( state, VTERM_ATTR_FONT, 0);
+
+  state->fg_ansi = -1;
+  state->pen.fg = state->default_fg;  setpenattr_col(state, 
VTERM_ATTR_FOREGROUND, state->default_fg);
+  state->pen.bg = state->default_bg;  setpenattr_col(state, 
VTERM_ATTR_BACKGROUND, state->default_bg);
+}
+
+void vterm_state_savepen(VTermState *state, int save)
+{
+  if(save) {
+    state->saved.pen = state->pen;
+  }
+  else {
+    state->pen = state->saved.pen;
+
+    setpenattr_bool(state, VTERM_ATTR_BOLD,       state->pen.bold);
+    setpenattr_int( state, VTERM_ATTR_UNDERLINE,  state->pen.underline);
+    setpenattr_bool(state, VTERM_ATTR_ITALIC,     state->pen.italic);
+    setpenattr_bool(state, VTERM_ATTR_BLINK,      state->pen.blink);
+    setpenattr_bool(state, VTERM_ATTR_REVERSE,    state->pen.reverse);
+    setpenattr_bool(state, VTERM_ATTR_STRIKE,     state->pen.strike);
+    setpenattr_int( state, VTERM_ATTR_FONT,       state->pen.font);
+    setpenattr_col( state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+    setpenattr_col( state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+  }
+}
+
+void vterm_state_set_default_colors(VTermState *state, VTermColor *default_fg, 
VTermColor *default_bg)
+{
+  state->default_fg = *default_fg;
+  state->default_bg = *default_bg;
+}
+
+void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
+{
+  state->bold_is_highbright = bold_is_highbright;
+}
+
+void vterm_state_setpen(VTermState *state, const long args[], int argcount)
+{
+  // SGR - ECMA-48 8.3.117
+
+  int argi = 0;
+  int value;
+
+  while(argi < argcount) {
+    // This logic is easier to do 'done' backwards; set it true, and make it
+    // false again in the 'default' case
+    int done = 1;
+
+    long arg;
+    switch(arg = CSI_ARG(args[argi])) {
+    case CSI_ARG_MISSING:
+    case 0: // Reset
+      vterm_state_resetpen(state);
+      break;
+
+    case 1: // Bold on
+      state->pen.bold = 1;
+      setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
+      if(state->fg_ansi > -1 && state->bold_is_highbright)
+        set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, state->fg_ansi + 
(state->pen.bold ? 8 : 0));
+      break;
+
+    case 3: // Italic on
+      state->pen.italic = 1;
+      setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
+      break;
+
+    case 4: // Underline single
+      state->pen.underline = 1;
+      setpenattr_int(state, VTERM_ATTR_UNDERLINE, 1);
+      break;
+
+    case 5: // Blink
+      state->pen.blink = 1;
+      setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
+      break;
+
+    case 7: // Reverse on
+      state->pen.reverse = 1;
+      setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
+      break;
+
+    case 9: // Strikethrough on
+      state->pen.strike = 1;
+      setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
+      break;
+
+    case 10: case 11: case 12: case 13: case 14:
+    case 15: case 16: case 17: case 18: case 19: // Select font
+      state->pen.font = CSI_ARG(args[argi]) - 10;
+      setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
+      break;
+
+    case 21: // Underline double
+      state->pen.underline = 2;
+      setpenattr_int(state, VTERM_ATTR_UNDERLINE, 2);
+      break;
+
+    case 22: // Bold off
+      state->pen.bold = 0;
+      setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
+      break;
+
+    case 23: // Italic and Gothic (currently unsupported) off
+      state->pen.italic = 0;
+      setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
+      break;
+
+    case 24: // Underline off
+      state->pen.underline = 0;
+      setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
+      break;
+
+    case 25: // Blink off
+      state->pen.blink = 0;
+      setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
+      break;
+
+    case 27: // Reverse off
+      state->pen.reverse = 0;
+      setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
+      break;
+
+    case 29: // Strikethrough off
+      state->pen.strike = 0;
+      setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
+      break;
+
+    case 30: case 31: case 32: case 33:
+    case 34: case 35: case 36: case 37: // Foreground colour palette
+      value = CSI_ARG(args[argi]) - 30;
+      state->fg_ansi = value;
+      if(state->pen.bold && state->bold_is_highbright)
+        value += 8;
+      set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
+      break;
+
+    case 38: // Foreground colour alternative palette
+      state->fg_ansi = -1;
+      if(argcount - argi < 1)
+        return;
+      argi += 1 + lookup_colour(CSI_ARG(args[argi+1]), args+argi+2, 
argcount-argi-2, 0, &state->pen.fg);
+      setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+      break;
+
+    case 39: // Foreground colour default
+      state->fg_ansi = -1;
+      state->pen.fg = state->default_fg;
+      setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+      break;
+
+    case 40: case 41: case 42: case 43:
+    case 44: case 45: case 46: case 47: // Background colour palette
+      set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, CSI_ARG(args[argi]) - 40);
+      break;
+
+    case 48: // Background colour alternative palette
+      if(argcount - argi < 1)
+        return;
+      argi += 1 + lookup_colour(CSI_ARG(args[argi+1]), args+argi+2, 
argcount-argi-2, 1, &state->pen.bg);
+      setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+      break;
+
+    case 49: // Default background
+      state->pen.bg = state->default_bg;
+      setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+      break;
+
+    case 90: case 91: case 92: case 93:
+    case 94: case 95: case 96: case 97: // Foreground colour high-intensity 
palette
+      set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, CSI_ARG(args[argi]) - 90 
+ 8);
+      break;
+
+    case 100: case 101: case 102: case 103:
+    case 104: case 105: case 106: case 107: // Background colour 
high-intensity palette
+      set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, CSI_ARG(args[argi]) - 100 
+ 8);
+      break;
+
+    default:
+      done = 0;
+      break;
+    }
+
+    if(!done)
+      fprintf(stderr, "libvterm: Unhandled CSI SGR %lu\n", arg);
+
+    while(CSI_ARG_HAS_MORE(args[argi++]));
+  }
+}
+
+int vterm_state_get_penattr(VTermState *state, VTermAttr attr, VTermValue *val)
+{
+  switch(attr) {
+  case VTERM_ATTR_BOLD:
+    val->boolean = state->pen.bold;
+    return 1;
+
+  case VTERM_ATTR_UNDERLINE:
+    val->number = state->pen.underline;
+    return 1;
+
+  case VTERM_ATTR_ITALIC:
+    val->boolean = state->pen.italic;
+    return 1;
+
+  case VTERM_ATTR_BLINK:
+    val->boolean = state->pen.blink;
+    return 1;
+
+  case VTERM_ATTR_REVERSE:
+    val->boolean = state->pen.reverse;
+    return 1;
+
+  case VTERM_ATTR_STRIKE:
+    val->boolean = state->pen.strike;
+    return 1;
+
+  case VTERM_ATTR_FONT:
+    val->number = state->pen.font;
+    return 1;
+
+  case VTERM_ATTR_FOREGROUND:
+    val->color = state->pen.fg;
+    return 1;
+
+  case VTERM_ATTR_BACKGROUND:
+    val->color = state->pen.bg;
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/src/apps/serialconnect/libvterm/src/rect.h 
b/src/apps/serialconnect/libvterm/src/rect.h
new file mode 100644
index 0000000..2114f24
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/src/rect.h
@@ -0,0 +1,56 @@
+/*
+ * Some utility functions on VTermRect structures
+ */
+
+#define STRFrect "(%d,%d-%d,%d)"
+#define ARGSrect(r) (r).start_row, (r).start_col, (r).end_row, (r).end_col
+
+/* Expand dst to contain src as well */
+static void rect_expand(VTermRect *dst, VTermRect *src)
+{
+  if(dst->start_row > src->start_row) dst->start_row = src->start_row;
+  if(dst->start_col > src->start_col) dst->start_col = src->start_col;
+  if(dst->end_row   < src->end_row)   dst->end_row   = src->end_row;
+  if(dst->end_col   < src->end_col)   dst->end_col   = src->end_col;
+}
+
+/* Clip the dst to ensure it does not step outside of bounds */
+static void rect_clip(VTermRect *dst, VTermRect *bounds)
+{
+  if(dst->start_row < bounds->start_row) dst->start_row = bounds->start_row;
+  if(dst->start_col < bounds->start_col) dst->start_col = bounds->start_col;
+  if(dst->end_row   > bounds->end_row)   dst->end_row   = bounds->end_row;
+  if(dst->end_col   > bounds->end_col)   dst->end_col   = bounds->end_col;
+  /* Ensure it doesn't end up negatively-sized */
+  if(dst->end_row < dst->start_row) dst->end_row = dst->start_row;
+  if(dst->end_col < dst->start_col) dst->end_col = dst->start_col;
+}
+
+/* True if the two rectangles are equal */
+static int rect_equal(VTermRect *a, VTermRect *b)
+{
+  return (a->start_row == b->start_row) &&
+         (a->start_col == b->start_col) &&
+         (a->end_row   == b->end_row)   &&
+         (a->end_col   == b->end_col);
+}
+
+/* True if small is contained entirely within big */
+static int rect_contains(VTermRect *big, VTermRect *small)
+{
+  if(small->start_row < big->start_row) return 0;
+  if(small->start_col < big->start_col) return 0;
+  if(small->end_row   > big->end_row)   return 0;
+  if(small->end_col   > big->end_col)   return 0;
+  return 1;
+}
+
+/* True if the rectangles overlap at all */
+static int rect_intersects(VTermRect *a, VTermRect *b)
+{
+  if(a->start_row > b->end_row || b->start_row > a->end_row)
+    return 0;
+  if(a->start_col > b->end_col || b->start_col > a->end_col)
+    return 0;
+  return 1;
+}
diff --git a/src/apps/serialconnect/libvterm/src/screen.c 
b/src/apps/serialconnect/libvterm/src/screen.c
new file mode 100644
index 0000000..439d586
--- /dev/null
+++ b/src/apps/serialconnect/libvterm/src/screen.c
@@ -0,0 +1,664 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+
+#include "rect.h"
+#include "utf8.h"
+
+#define UNICODE_SPACE 0x20
+#define UNICODE_LINEFEED 0x0a
+
+/* State of the pen at some moment in time, also used in a cell */
+typedef struct
+{
+  /* After the bitfield */
+  VTermColor   fg, bg;
+
+  unsigned int bold      : 1;
+  unsigned int underline : 2;
+  unsigned int italic    : 1;
+  unsigned int blink     : 1;
+  unsigned int reverse   : 1;
+  unsigned int strike    : 1;
+  unsigned int font      : 4; /* 0 to 9 */
+} ScreenPen;
+
+/* Internal representation of a screen cell */
+typedef struct
+{
+  uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
+  ScreenPen pen;
+} ScreenCell;
+
+struct VTermScreen
+{
+  VTerm *vt;
+  VTermState *state;
+
+  const VTermScreenCallbacks *callbacks;
+  void *cbdata;
+
+  VTermDamageSize damage_merge;
+  /* start_row == -1 => no damage */
+  VTermRect damaged;
+  VTermRect pending_scrollrect;
+  int pending_scroll_downward, pending_scroll_rightward;
+
+  int rows;
+  int cols;
+  int global_reverse;
+
+  /* Primary and Altscreen. buffers[1] is lazily allocated as needed */
+  ScreenCell *buffers[2];
+
+  /* buffer will == buffers[0] or buffers[1], depending on altscreen */
+  ScreenCell *buffer;
+
+  ScreenPen pen;
+};
+
+static inline ScreenCell *getcell(VTermScreen *screen, int row, int col)
+{
+  /* TODO: Bounds checking */
+  return screen->buffer + (screen->cols * row) + col;
+}
+
+static ScreenCell *realloc_buffer(VTermScreen *screen, ScreenCell *buffer, int 
new_rows, int new_cols)
+{
+  ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, 
sizeof(ScreenCell) * new_rows * new_cols);
+  int row, col;
+
+  for(row = 0; row < new_rows; row++) {
+    for(col = 0; col < new_cols; col++) {
+      ScreenCell *new_cell = new_buffer + row*new_cols + col;
+
+      if(buffer && row < screen->rows && col < screen->cols)
+        *new_cell = buffer[row * screen->cols + col];
+      else {
+        new_cell->chars[0] = 0;
+        new_cell->pen = screen->pen;
+      }
+    }
+  }
+
+  if(buffer)
+    vterm_allocator_free(screen->vt, buffer);
+
+  return new_buffer;
+}
+
+static void damagerect(VTermScreen *screen, VTermRect rect)
+{
+  VTermRect emit;
+
+  switch(screen->damage_merge) {
+  case VTERM_DAMAGE_CELL:
+    /* Always emit damage event */
+    emit = rect;
+    break;
+
+  case VTERM_DAMAGE_ROW:
+    /* Emit damage longer than one row. Try to merge with existing damage in
+     * the same row */
+    if(rect.end_row > rect.start_row + 1) {
+      // Bigger than 1 line - flush existing, emit this
+      vterm_screen_flush_damage(screen);
+      emit = rect;
+    }
+    else if(screen->damaged.start_row == -1) {
+      // None stored yet
+      screen->damaged = rect;
+      return;
+    }
+    else if(rect.start_row == screen->damaged.start_row) {
+      // Merge with the stored line
+      if(screen->damaged.start_col > rect.start_col)
+        screen->damaged.start_col = rect.start_col;
+      if(screen->damaged.end_col < rect.end_col)
+        screen->damaged.end_col = rect.end_col;
+      return;
+    }
+    else {
+      // Emit the currently stored line, store a new one
+      emit = screen->damaged;
+      screen->damaged = rect;
+    }
+    break;
+
+  case VTERM_DAMAGE_SCREEN:
+  case VTERM_DAMAGE_SCROLL:
+    /* Never emit damage event */
+    if(screen->damaged.start_row == -1)
+      screen->damaged = rect;
+    else {
+      rect_expand(&screen->damaged, &rect);
+    }
+    return;
+
+  default:
+    fprintf(stderr, "TODO: Maybe merge damage for level %d\n", 
screen->damage_merge);
+    return;
+  }
+
+  if(screen->callbacks && screen->callbacks->damage)
+    (*screen->callbacks->damage)(emit, screen->cbdata);
+}
+
+static void damagescreen(VTermScreen *screen)
+{
+  VTermRect rect = {
+    .start_row = 0,
+    .end_row   = screen->rows,
+    .start_col = 0,
+    .end_col   = screen->cols,
+  };
+
+  damagerect(screen, rect);
+}
+
+static int putglyph(const uint32_t chars[], int width, VTermPos pos, void 
*user)
+{
+  VTermScreen *screen = user;
+  ScreenCell *cell = getcell(screen, pos.row, pos.col);
+  int i;
+  int col;
+
+  VTermRect rect = {
+    .start_row = pos.row,
+    .end_row   = pos.row+1,
+    .start_col = pos.col,
+    .end_col   = pos.col+width,
+  };
+
+  for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && chars[i]; i++) {
+    cell->chars[i] = chars[i];
+    cell->pen = screen->pen;
+  }
+  if(i < VTERM_MAX_CHARS_PER_CELL)
+    cell->chars[i] = 0;
+
+  for(col = 1; col < width; col++)
+    getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1;
+
+  damagerect(screen, rect);
+
+  return 1;
+}
+
+static void copycell(VTermPos dest, VTermPos src, void *user)
+{
+  VTermScreen *screen = user;
+  ScreenCell *destcell = getcell(screen, dest.row, dest.col);
+  ScreenCell *srccell = getcell(screen, src.row, src.col);
+
+  *destcell = *srccell;
+}
+
+static int moverect_internal(VTermRect dest, VTermRect src, void *user)
+{
+  VTermScreen *screen = user;
+
+  vterm_copy_cells(dest, src, &copycell, screen);
+
+  return 1;
+}
+
+static int moverect_user(VTermRect dest, VTermRect src, void *user)
+{
+  VTermScreen *screen = user;
+
+  if(screen->callbacks && screen->callbacks->moverect) {
+    if(screen->damage_merge != VTERM_DAMAGE_SCROLL)
+      // Avoid an infinite loop
+      vterm_screen_flush_damage(screen);
+
+    if((*screen->callbacks->moverect)(dest, src, screen->cbdata))
+      return 1;
+  }
+
+  damagerect(screen, dest);
+
+  return 1;
+}
+
+static int erase_internal(VTermRect rect, void *user)
+{
+  VTermScreen *screen = user;
+  int row, col;
+
+  for(row = rect.start_row; row < rect.end_row; row++)
+    for(col = rect.start_col; col < rect.end_col; col++) {
+      ScreenCell *cell = getcell(screen, row, col);
+      cell->chars[0] = 0;
+      cell->pen = screen->pen;
+    }
+
+  return 1;
+}
+
+static int erase_user(VTermRect rect, void *user)
+{
+  VTermScreen *screen = user;
+
+  damagerect(screen, rect);
+
+  return 1;
+}
+
+static int erase(VTermRect rect, void *user)
+{
+  erase_internal(rect, user);
+  return erase_user(rect, user);
+}
+
+static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
+{
+  VTermScreen *screen = user;
+
+  vterm_scroll_rect(rect, downward, rightward,
+      moverect_internal, erase_internal, screen);
+
+  if(screen->damage_merge == VTERM_DAMAGE_SCROLL) {
+    if(screen->damaged.start_row != -1 &&
+       !rect_intersects(&rect, &screen->damaged)) {
+      vterm_screen_flush_damage(screen);
+    }
+
+    if(screen->pending_scrollrect.start_row == -1) {
+      screen->pending_scrollrect = rect;
+      screen->pending_scroll_downward  = downward;
+      screen->pending_scroll_rightward = rightward;
+    }
+    else if(rect_equal(&screen->pending_scrollrect, &rect) &&
+       ((screen->pending_scroll_downward  == 0 && downward  == 0) ||
+        (screen->pending_scroll_rightward == 0 && rightward == 0))) {
+      screen->pending_scroll_downward  += downward;
+      screen->pending_scroll_rightward += rightward;
+    }
+    else {
+      vterm_screen_flush_damage(screen);
+
+      screen->pending_scrollrect = rect;
+      screen->pending_scroll_downward  = downward;
+      screen->pending_scroll_rightward = rightward;
+    }
+
+    if(screen->damaged.start_row != -1) {
+      if(rect_contains(&rect, &screen->damaged)) {
+        vterm_rect_move(&screen->damaged, -downward, -rightward);
+        rect_clip(&screen->damaged, &rect);
+      }
+      else {
+        fprintf(stderr, "TODO: scrollrect split damage\n");
+      }
+    }
+
+    return 1;
+  }
+
+  vterm_screen_flush_damage(screen);
+
+  vterm_scroll_rect(rect, downward, rightward,
+      moverect_user, erase_user, screen);
+
+  return 1;
+}
+
+static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
+{
+  VTermScreen *screen = user;
+
+  if(screen->callbacks && screen->callbacks->movecursor)
+    return (*screen->callbacks->movecursor)(pos, oldpos, visible, 
screen->cbdata);
+
+  return 0;
+}
+
+static int setpenattr(VTermAttr attr, VTermValue *val, void *user)
+{
+  VTermScreen *screen = user;
+
+  switch(attr) {
+  case VTERM_ATTR_BOLD:
+    screen->pen.bold = val->boolean;
+    return 1;
+  case VTERM_ATTR_UNDERLINE:
+    screen->pen.underline = val->number;
+    return 1;
+  case VTERM_ATTR_ITALIC:
+    screen->pen.italic = val->boolean;
+    return 1;
+  case VTERM_ATTR_BLINK:
+    screen->pen.blink = val->boolean;
+    return 1;
+  case VTERM_ATTR_REVERSE:
+    screen->pen.reverse = val->boolean;
+    return 1;
+  case VTERM_ATTR_STRIKE:
+    screen->pen.strike = val->boolean;
+    return 1;
+  case VTERM_ATTR_FONT:
+    screen->pen.font = val->number;
+    return 1;
+  case VTERM_ATTR_FOREGROUND:
+    screen->pen.fg = val->color;
+    return 1;
+  case VTERM_ATTR_BACKGROUND:
+    screen->pen.bg = val->color;
+    return 1;
+  }
+
+  return 0;
+}
+
+static int settermprop(VTermProp prop, VTermValue *val, void *user)
+{
+  VTermScreen *screen = user;
+
+  switch(prop) {
+  case VTERM_PROP_ALTSCREEN:
+    if(val->boolean && !screen->buffers[1])
+      return 0;
+
+    screen->buffer = val->boolean ? screen->buffers[1] : screen->buffers[0];
+    /* only send a damage event on disable; because during enable there's an
+     * erase that sends a damage anyway
+     */
+    if(!val->boolean)
+      damagescreen(screen);
+    break;
+  case VTERM_PROP_REVERSE:
+    screen->global_reverse = val->boolean;
+    damagescreen(screen);
+    break;

[ *** diff truncated: 2599 lines dropped *** ]



Other related posts: