aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--.gitmodules3
-rw-r--r--ProcessUtils.cpp155
-rw-r--r--ProcessUtils.h15
-rw-r--r--RadTerminal.cpp690
-rw-r--r--RadTerminal.sln37
-rw-r--r--RadTerminal.vcxproj112
-rw-r--r--WinUtils.h36
m---------libtsm0
-rw-r--r--tsm.vcxproj170
-rw-r--r--tsm.vcxproj.filters44
11 files changed, 1267 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a15991d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+Bin/
+Int/
+.vs/
+*.VC.db
+*.vcxproj.user
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..f4fc4c1
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "libtsm"]
+ path = libtsm
+ url = https://github.com/RadAd/libtsm.git
diff --git a/ProcessUtils.cpp b/ProcessUtils.cpp
new file mode 100644
index 0000000..8b8aa45
--- /dev/null
+++ b/ProcessUtils.cpp
@@ -0,0 +1,155 @@
+#include "ProcessUtils.h"
+#include <Windows.h>
+#include <tchar.h>
+#include <vector>
+
+// TODO
+// Support .lnk files
+
+namespace {
+ void Clear(SubProcessData* spd)
+ {
+ spd->hOutput = NULL;
+ spd->hInput = NULL;
+ spd->pi.hThread = NULL;
+ spd->pi.hProcess = NULL;
+ spd->hPC = NULL;
+ }
+
+ SubProcessData CreateSubProcess(LPCTSTR cmd, COORD size, HANDLE hInput, HANDLE hOutput, bool bUseConPty)
+ {
+ SubProcessData spd = {};
+
+ DWORD dwCreationFlags = EXTENDED_STARTUPINFO_PRESENT;
+ STARTUPINFOEX si = {};
+ std::vector<BYTE> attrList;
+ si.StartupInfo.cb = sizeof(si);
+
+ if (bUseConPty)
+ {
+ spd.hr = CreatePseudoConsole(
+ size,
+ hInput,
+ hOutput,
+ 0,
+ &spd.hPC);
+ if (spd.hr != S_OK)
+ {
+ CleanupSubProcess(&spd);
+ Clear(&spd);
+ return spd;
+ }
+
+ SIZE_T size = 0;
+ if (InitializeProcThreadAttributeList(NULL, 1, 0, &size))
+ {
+ spd.hr = HRESULT_FROM_WIN32(GetLastError());
+ CleanupSubProcess(&spd);
+ Clear(&spd);
+ return spd;
+ }
+
+ attrList.resize(size);
+ si.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(attrList.data());
+ if (!InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size))
+ {
+ spd.hr = HRESULT_FROM_WIN32(GetLastError());
+ CleanupSubProcess(&spd);
+ Clear(&spd);
+ return spd;
+ }
+ if (!UpdateProcThreadAttribute(
+ si.lpAttributeList,
+ 0,
+ PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
+ spd.hPC, sizeof(HPCON),
+ NULL,
+ NULL))
+ {
+ spd.hr = HRESULT_FROM_WIN32(GetLastError());
+ CleanupSubProcess(&spd);
+ Clear(&spd);
+ return spd;
+ }
+ }
+ else
+ {
+ si.StartupInfo.dwXSize = size.X;
+ si.StartupInfo.dwYSize = size.Y;
+ si.StartupInfo.dwFlags = STARTF_USESIZE | STARTF_USESTDHANDLES;
+ si.StartupInfo.hStdInput = hInput;
+ si.StartupInfo.hStdOutput = hOutput;
+ si.StartupInfo.hStdError = si.StartupInfo.hStdOutput;
+ dwCreationFlags |= CREATE_NO_WINDOW;
+ }
+
+ TCHAR localcmd[MAX_PATH];
+ wcscpy_s(localcmd, cmd);
+ if (!CreateProcess(nullptr, localcmd, nullptr, nullptr, FALSE, dwCreationFlags, nullptr, nullptr, &si.StartupInfo, &spd.pi))
+ {
+ spd.hr = HRESULT_FROM_WIN32(GetLastError());
+ CleanupSubProcess(&spd);
+ Clear(&spd);
+ return spd;
+ }
+
+ spd.hr = S_OK;
+ return spd;
+ }
+}
+
+SubProcessData CreateSubProcess(LPCTSTR cmd, COORD size, bool bUseConPty)
+{
+ SubProcessData spd = {};
+
+ HANDLE hReadPipeInput = NULL;
+ HANDLE hWritePipeInput = NULL;
+ if (spd.hr == S_OK && !CreatePipe(&hReadPipeInput, &hWritePipeInput, nullptr, 0))
+ spd.hr = HRESULT_FROM_WIN32(GetLastError());
+
+ HANDLE hWritePipeOutput = NULL;
+ HANDLE hReadPipeOutput = NULL;
+ if (spd.hr == S_OK && !CreatePipe(&hReadPipeOutput, &hWritePipeOutput, nullptr, 0))
+ spd.hr = HRESULT_FROM_WIN32(GetLastError());
+
+ if (spd.hr == S_OK)
+ spd = CreateSubProcess(cmd, size, hReadPipeInput, hWritePipeOutput, true);
+
+ if (spd.hr == S_OK)
+ {
+ spd.hInput = hWritePipeInput;
+ spd.hOutput = hReadPipeOutput;
+ }
+ else
+ {
+ CloseHandle(hWritePipeInput);
+ CloseHandle(hReadPipeOutput);
+ }
+
+ CloseHandle(hReadPipeInput);
+ CloseHandle(hWritePipeOutput);
+
+ return spd;
+}
+
+void CleanupSubProcess(const SubProcessData* spd)
+{
+ CloseHandle(spd->hOutput);
+ CloseHandle(spd->hInput);
+
+ TerminateProcess(spd->pi.hProcess, 0xFF);
+
+ CloseHandle(spd->pi.hThread);
+ CloseHandle(spd->pi.hProcess);
+ ClosePseudoConsole(spd->hPC);
+}
+
+UINT GetIcon(const SubProcessData* spd, HICON *phIconLarge, HICON *phIconSmall)
+{
+ TCHAR exe[MAX_PATH];
+ DWORD exelen = ARRAYSIZE(exe);
+ QueryFullProcessImageName(spd->pi.hProcess, 0, exe, &exelen);
+
+ WORD iIcon = 0;
+ return ExtractIconEx(exe, 0, phIconLarge, phIconSmall, 1);
+}
diff --git a/ProcessUtils.h b/ProcessUtils.h
new file mode 100644
index 0000000..0b6f304
--- /dev/null
+++ b/ProcessUtils.h
@@ -0,0 +1,15 @@
+#pragma once
+#include <Windows.h>
+
+struct SubProcessData
+{
+ HRESULT hr;
+ HPCON hPC;
+ PROCESS_INFORMATION pi;
+ HANDLE hInput;
+ HANDLE hOutput;
+};
+
+SubProcessData CreateSubProcess(LPCTSTR cmd, COORD zsCon, bool bUseConPty);
+void CleanupSubProcess(const SubProcessData* spd);
+UINT GetIcon(const SubProcessData* spd, HICON *phIconLarge, HICON *phIconSmall);
diff --git a/RadTerminal.cpp b/RadTerminal.cpp
new file mode 100644
index 0000000..ff0d61b
--- /dev/null
+++ b/RadTerminal.cpp
@@ -0,0 +1,690 @@
+#include <windows.h>
+#include <windowsx.h>
+#include <tchar.h>
+#include <string>
+#include "ProcessUtils.h"
+#include "WinUtils.h"
+#include "libtsm\src\tsm\libtsm.h"
+#include "libtsm\external\xkbcommon\xkbcommon-keysyms.h"
+
+// TODO
+// https://stackoverflow.com/questions/5966903/how-to-get-mousemove-and-mouseclick-in-bash
+// scrollback
+// remove polling
+// unicode/emoji
+// selection
+// cut/paste
+// drop files
+// status bar
+// flash window on updates
+
+#ifdef _UNICODE
+#define tstring wstring
+#else
+#define tstring string
+#endif
+
+#define PROJ_NAME TEXT("RadTerminal")
+
+void ShowError(HWND hWnd, LPCTSTR msg, HRESULT hr)
+{
+ TCHAR fullmsg[1024];
+ _stprintf_s(fullmsg, _T("%s: 0x%x"), msg, hr);
+ MessageBox(hWnd, fullmsg, PROJ_NAME, MB_ICONERROR);
+}
+
+#define CHECK(x, r) \
+ if (!(x)) \
+ { \
+ ShowError(hWnd, _T(#x), HRESULT_FROM_WIN32(GetLastError())); \
+ return (r); \
+ }
+
+LRESULT CALLBACK RadTerminalWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+struct RadTerminalCreate
+{
+ int iFontHeight;
+ LPTSTR strFontFace;
+ LPTSTR strScheme;
+ COORD szCon;
+ std::tstring strCmd;
+};
+
+int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, PTSTR pCmdLine, int nCmdShow)
+{
+ HWND hWnd = NULL;
+
+ WNDCLASS wc = {};
+
+ wc.lpfnWndProc = RadTerminalWindowProc;
+ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ //wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
+ wc.hInstance = hInstance;
+ wc.lpszClassName = TEXT("RadTerminal");
+
+ ATOM atom = NULL;
+ CHECK(atom = RegisterClass(&wc), EXIT_FAILURE);
+
+ RadTerminalCreate rtc = {};
+ rtc.iFontHeight = 16;
+ rtc.strFontFace = _T("Consolas");
+ //rtc.strScheme = _T("solarized");
+ rtc.szCon = { 80, 25 };
+
+ bool command = false;
+ for (int i = 1; i < __argc; ++i)
+ {
+ LPCTSTR arg = __targv[i];
+ if (command)
+ {
+ rtc.strCmd += ' ';
+ rtc.strCmd += arg;
+ }
+ else if (_tcsicmp(arg, _T("-w")) == 0)
+ rtc.szCon.X = _tstoi(__targv[++i]);
+ else if (_tcsicmp(arg, _T("-h")) == 0)
+ rtc.szCon.Y = _tstoi(__targv[++i]);
+ else if (_tcsicmp(arg, _T("-scheme")) == 0)
+ rtc.strScheme = __targv[++i];
+ else if (_tcsicmp(arg, _T("-font_face")) == 0)
+ rtc.strFontFace = __targv[++i];
+ else if (_tcsicmp(arg, _T("-font_size")) == 0)
+ rtc.iFontHeight = _tstoi(__targv[++i]);
+ else
+ {
+ rtc.strCmd = arg;
+ command = true;
+ }
+ }
+
+ if (rtc.strCmd.empty())
+ //rtc.strCmd = _T("%COMSPEC%");
+ rtc.strCmd = _T("cmd");
+
+ HWND hChildWnd = CreateWindowEx(
+ 0,
+ MAKEINTATOM(atom),
+ PROJ_NAME,
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, // Parent window
+ NULL, // Menu
+ hInstance,
+ &rtc
+ );
+ CHECK(hChildWnd, EXIT_FAILURE);
+
+ ShowWindow(hChildWnd, nCmdShow);
+
+ MSG msg = {};
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+void tsm_log(void *data,
+ const char *file,
+ int line,
+ const char *func,
+ const char *subs,
+ unsigned int sev,
+ const char *format,
+ va_list args)
+{
+ char buf[1024];
+ sprintf_s(buf, "tsm_log: %s:%d %s %s %d\n", strrchr(file, '\\'), line, func, subs, sev);
+ OutputDebugStringA(buf);
+ OutputDebugStringA("tsm_log: ");
+ vsprintf_s(buf, format, args);
+ OutputDebugStringA(buf);
+ OutputDebugStringA("\n");
+}
+
+struct tsm_screen_draw_info
+{
+ HFONT hFontNormal;
+ HFONT hFontBold;
+ TEXTMETRIC tm;
+};
+
+struct tsm_screen_draw_data
+{
+ HDC hdc;
+ const tsm_screen_draw_info* info;
+
+ POINT p;
+ HFONT hFont;
+ std::tstring drawbuf;
+ COLORREF bg;
+ COLORREF fg;
+};
+
+SIZE GetCellSize(const tsm_screen_draw_info* di)
+{
+ return { di->tm.tmAveCharWidth, di->tm.tmHeight };
+}
+
+POINT GetScreenPos(const tsm_screen_draw_info* di, COORD pos)
+{
+ SIZE sz = GetCellSize(di);
+ return { pos.X * sz.cx, pos.Y * sz.cy };
+}
+
+void Flush(tsm_screen_draw_data* const draw)
+{
+ if (!draw->drawbuf.empty())
+ {
+ HFONT hFontOrig = SelectFont(draw->hdc, draw->hFont);
+ SetBkColor(draw->hdc, draw->bg);
+ SetTextColor(draw->hdc, draw->fg);
+ TextOut(draw->hdc, draw->p.x, draw->p.y, draw->drawbuf.c_str(), (int) draw->drawbuf.length());
+ draw->drawbuf.clear();
+ SelectFont(draw->hdc, hFontOrig);
+ }
+}
+
+int tsm_screen_draw(struct tsm_screen *con,
+ uint64_t id,
+ const uint32_t *ch,
+ size_t len,
+ unsigned int width,
+ unsigned int posx,
+ unsigned int posy,
+ const struct tsm_screen_attr *attr,
+ tsm_age_t age,
+ void *data)
+{
+ tsm_screen_draw_data* const draw = (tsm_screen_draw_data*) data;
+ // TODO Underline, Inverse, Protect, Blink
+ const COORD pos = { (SHORT) posx, (SHORT) posy };
+ const POINT p = GetScreenPos(draw->info, pos);
+ const HFONT hFont = attr->bold ? draw->info->hFontBold : draw->info->hFontNormal;
+ const COLORREF bg = RGB(attr->br, attr->bg, attr->bb);
+ const COLORREF fg = RGB(attr->fr, attr->fg, attr->fb);
+ if (hFont != draw->hFont || bg != draw->bg || fg != draw->fg || p.y != draw->p.y)
+ {
+ Flush(draw);
+ draw->hFont = hFont;
+ draw->bg = bg;
+ draw->fg = fg;
+ draw->p = p;
+ }
+ if (len > 0)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ uint32_t chr = ch[i];
+
+ char buf[4];
+ size_t buflen = tsm_ucs4_to_utf8(chr, buf);
+
+ wchar_t bufw[4];
+ int bufwlen = MultiByteToWideChar(CP_UTF8, 0, buf, (int) buflen, bufw, ARRAYSIZE(bufw));
+
+ draw->drawbuf.append(bufw, bufwlen);
+ }
+ }
+ else
+ draw->drawbuf += ' ';
+ return 0;
+}
+
+void tsm_vte_write(struct tsm_vte *vte,
+ const char *u8,
+ size_t len,
+ void *data)
+{
+ const HANDLE hInput = (HANDLE) data;
+
+ while (len > 0)
+ {
+ DWORD written = 0;
+ WriteFile(hInput, u8, (DWORD) len, &written, nullptr);
+ len -= written;
+ }
+ //FlushFileBuffers(hInput);
+}
+
+bool tsm_vte_read(struct tsm_vte *vte, HANDLE hOutput)
+{
+ DWORD avail = 0;
+ if (PeekNamedPipe(hOutput, nullptr, 0, nullptr, &avail, nullptr) && avail > 0)
+ {
+ while (avail > 0)
+ {
+ char buf[1024];
+ const DWORD toread = (avail> ARRAYSIZE(buf)) ? ARRAYSIZE(buf) : avail;
+ DWORD read = 0;
+ ReadFile(hOutput, buf, toread, &read, nullptr);
+ tsm_vte_input(vte, buf, read);
+ avail -= read;
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+void tsm_vte_osc(struct tsm_vte *vte,
+ const char *u8,
+ size_t len,
+ void *data)
+{
+ if (strncmp(u8, "0;", 2) == 0 || strncmp(u8, "2;", 2) == 0)
+ {
+ HWND hWnd = (HWND) data;
+ SetWindowTextA(hWnd, u8 + 2);
+ }
+}
+
+struct RadTerminalData
+{
+ struct tsm_screen *screen;
+ struct tsm_vte *vte;
+ tsm_screen_draw_info draw_info;
+ SubProcessData spd;
+};
+
+void DrawCursor(HDC hdc, const RadTerminalData* const data)
+{
+ unsigned int flags = tsm_screen_get_flags(data->screen);
+ if (!(flags & TSM_SCREEN_HIDE_CURSOR))
+ {
+ const COORD pos = { (SHORT) tsm_screen_get_cursor_x(data->screen), (SHORT) tsm_screen_get_cursor_y(data->screen) };
+ RECT rc = Rect(GetScreenPos(&data->draw_info, pos), GetCellSize(&data->draw_info));
+ // TODO Different cursor styles
+ rc.top += (rc.bottom - rc.top) * 8 / 10;
+ InvertRect(hdc, &rc);
+ }
+}
+
+BOOL RadTerminalWindowOnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct)
+{
+ const RadTerminalCreate* const rtc = (RadTerminalCreate*) lpCreateStruct->lpCreateParams;
+
+ RadTerminalData* const data = new RadTerminalData;
+ ZeroMemory(data, sizeof(RadTerminalData));
+ SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data);
+
+ data->spd = CreateSubProcess(rtc->strCmd.c_str(), rtc->szCon, true);
+ if (data->spd.hr != S_OK)
+ {
+ ShowError(hWnd, _T("CreateSubProcess"), data->spd.hr);
+ return FALSE;
+ }
+
+ // TODO Report error
+ int e = 0;
+ e = tsm_screen_new(&data->screen, tsm_log, nullptr);
+ e = tsm_screen_resize(data->screen, rtc->szCon.X, rtc->szCon.Y);
+ e = tsm_vte_new(&data->vte, data->screen, tsm_vte_write, data->spd.hInput, tsm_log, nullptr);
+ tsm_vte_set_osc_cb(data->vte, tsm_vte_osc, hWnd);
+ if (rtc->strScheme != nullptr)
+ {
+ char scheme[1024];
+ WideCharToMultiByte(CP_UTF8, 0, rtc->strScheme, -1, scheme, ARRAYSIZE(scheme), nullptr, nullptr);
+ e = tsm_vte_set_palette(data->vte, scheme);
+ }
+
+ CHECK(data->draw_info.hFontNormal = CreateFont(rtc->strFontFace, rtc->iFontHeight, FW_NORMAL), FALSE);
+ CHECK(data->draw_info.hFontBold = CreateFont(rtc->strFontFace, rtc->iFontHeight, FW_BOLD), FALSE);
+
+ HDC hdc = GetDC(hWnd);
+ SelectFont(hdc, data->draw_info.hFontNormal);
+ GetTextMetrics(hdc, &data->draw_info.tm);
+ ReleaseDC(hWnd, hdc);
+
+ RECT r = Rect({ 0, 0 }, GetScreenPos(&data->draw_info, rtc->szCon));
+ const DWORD style = GetWindowStyle(hWnd);
+ CHECK(AdjustWindowRect(&r, style, FALSE), FALSE);
+ CHECK(SetWindowPos(hWnd, 0, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOMOVE | SWP_NOZORDER), FALSE);
+
+ HICON hIconLarge = NULL, hIconSmall = NULL;
+ UINT count = GetIcon(&data->spd, &hIconLarge, &hIconSmall);
+ if (count > 0)
+ {
+ SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIconLarge);
+ SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIconSmall);
+ }
+
+ CHECK(SetTimer(hWnd, 1, 10, nullptr), FALSE);
+ CHECK(SetTimer(hWnd, 2, 500, nullptr), FALSE);
+
+ return TRUE;
+}
+
+void RadTerminalWindowOnDestroy(HWND hWnd)
+{
+ const RadTerminalData* const data = (RadTerminalData*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
+
+ CleanupSubProcess(&data->spd);
+
+ DeleteFont(data->draw_info.hFontNormal);
+ DeleteFont(data->draw_info.hFontBold);
+
+ tsm_vte_unref(data->vte);
+ tsm_screen_unref(data->screen);
+
+ delete data;
+ SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) nullptr);
+ PostQuitMessage(0);
+}
+
+void RadTerminalWindowOnPaint(HWND hWnd)
+{
+ const RadTerminalData* const data = (RadTerminalData*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(hWnd, &ps);
+
+ HBRUSH hBrush = (HBRUSH) GetClassLongPtr(hWnd, GCLP_HBRBACKGROUND);
+ if (hBrush != NULL)
+ FillRect(hdc, &ps.rcPaint, hBrush);
+
+ tsm_screen_draw_data draw = {};
+ draw.hdc = hdc;
+ draw.info = &data->draw_info;
+ draw.p.y = -1;
+ tsm_age_t age = tsm_screen_draw(data->screen, tsm_screen_draw, (void*) &draw);
+ Flush(&draw);
+
+ HWND hActive = GetActiveWindow();
+ if (hActive == hWnd)
+ DrawCursor(hdc, data);
+
+ EndPaint(hWnd, &ps);
+}
+
+void RadTerminalWindowOnKeyDown(HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)
+{
+ const RadTerminalData* const data = (RadTerminalData*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
+ //uint32_t ascii = MapVirtualKeyA(vk, MAPVK_VK_TO_CHAR);
+ BYTE KeyState[256];
+ GetKeyboardState(KeyState);
+
+ uint32_t keysym = XKB_KEY_NoSymbol;
+ uint32_t ascii = 0;
+ uint32_t unicode = ascii;
+
+ unsigned int mods = 0;
+ if (KeyState[VK_SHIFT] != 0)
+ mods |= TSM_SHIFT_MASK;
+ if (KeyState[VK_SCROLL] != 0)
+ mods |= TSM_LOCK_MASK;
+ if (KeyState[VK_CONTROL] != 0)
+ mods |= TSM_CONTROL_MASK;
+ if (KeyState[VK_MENU] != 0)
+ mods |= TSM_ALT_MASK;
+ if (KeyState[VK_LWIN] != 0)
+ mods |= TSM_LOGO_MASK;
+
+ UINT scan = (cRepeat >> 8);
+ WORD charsAscii[2] = {};
+ if (ToAscii(vk, scan, KeyState, charsAscii, 0) > 0)
+ {
+ ascii = charsAscii[0];
+ keysym = ascii;
+ }
+ WCHAR charsUnicode[4] = {};
+ if (ToUnicode(vk, scan, KeyState, charsUnicode, ARRAYSIZE(charsUnicode), 0) > 0)
+ {
+ unicode = charsUnicode[0];
+ keysym = ascii;
+ }
+
+ switch (vk)
+ {
+ case VK_BACK: keysym = XKB_KEY_BackSpace; break;
+ case VK_TAB: keysym = XKB_KEY_Tab; break;
+ //case VK_: keysym = XKB_KEY_Linefeed; break;
+ case VK_CLEAR: keysym = XKB_KEY_Clear; break;
+ case VK_RETURN: keysym = XKB_KEY_Return; break;
+ case VK_PAUSE: keysym = XKB_KEY_Pause; break;
+ case VK_SCROLL: keysym = XKB_KEY_Scroll_Lock; break;
+ //case VK_: keysym = XKB_KEY_Sys_Req; break;
+ case VK_ESCAPE: keysym = XKB_KEY_Escape; break;
+ case VK_DELETE: keysym = XKB_KEY_Delete; break;
+
+ case VK_HOME: keysym = XKB_KEY_Home; break;
+ case VK_LEFT: keysym = XKB_KEY_Left; break;
+ case VK_UP: keysym = XKB_KEY_Up; break;
+ case VK_RIGHT: keysym = XKB_KEY_Right; break;
+ case VK_DOWN: keysym = XKB_KEY_Down; break;
+ //case VK_PRIOR: keysym = XKB_KEY_Prior; break;
+ case VK_PRIOR: keysym = XKB_KEY_Page_Up; break;
+ //case VK_NEXT: keysym = XKB_KEY_Next; break;
+ case VK_NEXT: keysym = XKB_KEY_Page_Down; break;
+ case VK_END: keysym = XKB_KEY_End; break;
+ //case VK_: keysym = XKB_KEY_Begin; break;
+
+ case VK_SELECT: keysym = XKB_KEY_Select; break;
+ case VK_PRINT: keysym = XKB_KEY_Print; break;
+ case VK_EXECUTE: keysym = XKB_KEY_Execute; break;
+ case VK_INSERT: keysym = XKB_KEY_Insert; break;
+ //case VK_: keysym = XKB_KEY_Undo; break;
+ //case VK_: keysym = XKB_KEY_Redo; break;
+ //case VK_: keysym = XKB_KEY_Menu; break;
+ //case VK_: keysym = XKB_KEY_Find; break;
+ case VK_CANCEL: keysym = XKB_KEY_Cancel; break;
+ case VK_HELP: keysym = XKB_KEY_Help; break;
+ //case VK_: keysym = XKB_KEY_Break; break;
+ //case VK_: keysym = XKB_KEY_Mode_switch; break;
+ //case VK_: keysym = XKB_KEY_script_switch; break;
+ case VK_NUMLOCK: keysym = XKB_KEY_Num_Lock; break;
+
+ //case VK_: keysym = XKB_KEY_KP_Space; break;
+ //case VK_: keysym = XKB_KEY_KP_Tab; break;
+ //case VK_: keysym = XKB_KEY_KP_Enter; break;
+ //case VK_: keysym = XKB_KEY_KP_F1; break;
+ //case VK_: keysym = XKB_KEY_KP_F2; break;
+ //case VK_: keysym = XKB_KEY_KP_F3; break;
+ //case VK_: keysym = XKB_KEY_KP_F4; break;
+ //case VK_: keysym = XKB_KEY_KP_Home; break;
+ //case VK_: keysym = XKB_KEY_KP_Left; break;
+ //case VK_: keysym = XKB_KEY_KP_Up; break;
+ //case VK_: keysym = XKB_KEY_KP_Right; break;
+ //case VK_: keysym = XKB_KEY_KP_Down; break;
+ //case VK_: keysym = XKB_KEY_KP_Prior; break;
+ //case VK_: keysym = XKB_KEY_KP_Page_Up; break;
+ //case VK_: keysym = XKB_KEY_KP_Next; break;
+ //case VK_: keysym = XKB_KEY_KP_Page_Down; break;
+ //case VK_: keysym = XKB_KEY_KP_End 0xff9c
+ //case VK_: keysym = XKB_KEY_KP_Begin 0xff9d
+ //case VK_: keysym = XKB_KEY_KP_Insert 0xff9e
+ //case VK_: keysym = XKB_KEY_KP_Delete 0xff9f
+ //case VK_: keysym = XKB_KEY_KP_Equal 0xffbd /* Equals */
+ //case VK_: keysym = XKB_KEY_KP_Multiply 0xffaa
+ //case VK_: keysym = XKB_KEY_KP_Add 0xffab
+ //case VK_: keysym = XKB_KEY_KP_Separator 0xffac /* Separator, often comma */
+ //case VK_: keysym = XKB_KEY_KP_Subtract 0xffad
+ //case VK_: keysym = XKB_KEY_KP_Decimal 0xffae
+ //case VK_: keysym = XKB_KEY_KP_Divide 0xffaf
+
+ case VK_NUMPAD0: keysym = XKB_KEY_KP_0; break;
+ case VK_NUMPAD1: keysym = XKB_KEY_KP_1; break;
+ case VK_NUMPAD2: keysym = XKB_KEY_KP_2; break;
+ case VK_NUMPAD3: keysym = XKB_KEY_KP_3; break;
+ case VK_NUMPAD4: keysym = XKB_KEY_KP_4; break;
+ case VK_NUMPAD5: keysym = XKB_KEY_KP_5; break;
+ case VK_NUMPAD6: keysym = XKB_KEY_KP_6; break;
+ case VK_NUMPAD7: keysym = XKB_KEY_KP_7; break;
+ case VK_NUMPAD8: keysym = XKB_KEY_KP_8; break;
+ case VK_NUMPAD9: keysym = XKB_KEY_KP_9; break;
+
+ case VK_F1: keysym = XKB_KEY_F1; break;
+ case VK_F2: keysym = XKB_KEY_F2; break;
+ case VK_F3: keysym = XKB_KEY_F3; break;
+ case VK_F4: keysym = XKB_KEY_F4; break;
+ case VK_F5: keysym = XKB_KEY_F5; break;
+ case VK_F6: keysym = XKB_KEY_F6; break;
+ case VK_F7: keysym = XKB_KEY_F7; break;
+ case VK_F8: keysym = XKB_KEY_F8; break;
+ case VK_F9: keysym = XKB_KEY_F9; break;
+ case VK_F10: keysym = XKB_KEY_F10; break;
+ case VK_F11: keysym = XKB_KEY_F11; break;
+ //case VK_: keysym = XKB_KEY_L1; break;
+ case VK_F12: keysym = XKB_KEY_F12; break;
+ //case VK_: keysym = XKB_KEY_L2; break;
+ case VK_F13: keysym = XKB_KEY_F13; break;
+ //case VK_: keysym = XKB_KEY_L3; break;
+ case VK_F14: keysym = XKB_KEY_F14; break;
+ //case VK_: keysym = XKB_KEY_L4; break;
+ case VK_F15: keysym = XKB_KEY_F15; break;
+ //case VK_: keysym = XKB_KEY_L5; break;
+ case VK_F16: keysym = XKB_KEY_F16; break;
+ //case VK_: keysym = XKB_KEY_L6; break;
+ case VK_F17: keysym = XKB_KEY_F17; break;
+ //case VK_: keysym = XKB_KEY_L7; break;
+ case VK_F18: keysym = XKB_KEY_F18; break;
+ //case VK_: keysym = XKB_KEY_L8; break;
+ case VK_F19: keysym = XKB_KEY_F19; break;
+ //case VK_: keysym = XKB_KEY_L9; break;
+ case VK_F20: keysym = XKB_KEY_F20; break;
+ //case VK_: keysym = XKB_KEY_L10; break;
+ case VK_F21: keysym = XKB_KEY_F21; break;
+ //case VK_: keysym = XKB_KEY_R1; break;
+ case VK_F22: keysym = XKB_KEY_F22; break;
+ //case VK_: keysym = XKB_KEY_R2; break;
+ case VK_F23: keysym = XKB_KEY_F23; break;
+ //case VK_: keysym = XKB_KEY_R3; break;
+ case VK_F24: keysym = XKB_KEY_F24; break;
+ //case VK_: keysym = XKB_KEY_R4; break;
+ //case VK_: keysym = XKB_KEY_F25; break;
+ //case VK_: keysym = XKB_KEY_R5; break;
+ //case VK_: keysym = XKB_KEY_F26; break;
+ //case VK_: keysym = XKB_KEY_R6; break;
+ //case VK_: keysym = XKB_KEY_F27; break;
+ //case VK_: keysym = XKB_KEY_R7; break;
+ //case VK_: keysym = XKB_KEY_F28; break;
+ //case VK_: keysym = XKB_KEY_R8; break;
+ //case VK_: keysym = XKB_KEY_F29; break;
+ //case VK_: keysym = XKB_KEY_R9; break;
+ //case VK_: keysym = XKB_KEY_F30; break;
+ //case VK_: keysym = XKB_KEY_R10; break;
+ //case VK_: keysym = XKB_KEY_F31; break;
+ //case VK_: keysym = XKB_KEY_R11; break;
+ //case VK_: keysym = XKB_KEY_F32; break;
+ //case VK_: keysym = XKB_KEY_R12; break;
+ //case VK_: keysym = XKB_KEY_F33; break;
+ //case VK_: keysym = XKB_KEY_R13; break;
+ //case VK_: keysym = XKB_KEY_F34; break;
+ //case VK_: keysym = XKB_KEY_R14; break;
+ //case VK_: keysym = XKB_KEY_F35; break;
+ //case VK_: keysym = XKB_KEY_R15; break;
+ }
+
+ bool b = tsm_vte_handle_keyboard(data->vte, keysym, ascii, mods, unicode);
+ InvalidateRect(hWnd, nullptr, TRUE);
+
+ FORWARD_WM_KEYDOWN(hWnd, vk, cRepeat, flags, DefWindowProc);
+}
+
+void RadTerminalWindowOnTimer(HWND hWnd, UINT id)
+{
+ const RadTerminalData* const data = (RadTerminalData*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
+ switch (id)
+ {
+ case 1:
+ {
+ if (tsm_vte_read(data->vte, data->spd.hOutput))
+ InvalidateRect(hWnd, nullptr, TRUE);
+
+ DWORD exitcode = 0;
+ if (GetExitCodeProcess(data->spd.pi.hProcess, &exitcode) && exitcode != STILL_ACTIVE)
+ {
+ DestroyWindow(hWnd);
+ }
+ }
+ break;
+
+ case 2:
+ {
+ HWND hActive = GetActiveWindow();
+ if (hActive == hWnd)
+ {
+ HDC hdc = GetDC(hWnd);
+ DrawCursor(hdc, data);
+ ReleaseDC(hWnd, hdc);
+ }
+ }
+ break;
+ }
+}
+
+void RadTerminalWindowOnSize(HWND hWnd, UINT state, int cx, int cy)
+{
+ if (state == SIZE_RESTORED || state == SIZE_MAXIMIZED)
+ {
+ const RadTerminalData* const data = (RadTerminalData*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
+ SIZE sz = GetCellSize(&data->draw_info);
+ COORD size = { (SHORT) (cx / sz.cx), (SHORT) (cy / sz.cy) };
+ int e = tsm_screen_resize(data->screen, size.X, size.Y);
+ ResizePseudoConsole(data->spd.hPC, size);
+ }
+}
+
+void RadTerminalWindowOnSizing(HWND hWnd, UINT edge, LPRECT prRect)
+{
+ const RadTerminalData* const data = (RadTerminalData*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
+ const DWORD style = GetWindowStyle(hWnd);
+ UnadjustWindowRect(prRect, style, FALSE);
+ SIZE sz = GetCellSize(&data->draw_info);
+ COORD size = { (SHORT) ((prRect->right - prRect->left) / sz.cx), (SHORT) ((prRect->bottom - prRect->top) / sz.cy) };
+
+ switch (edge)
+ {
+ case WMSZ_LEFT: case WMSZ_TOPLEFT: case WMSZ_BOTTOMLEFT:
+ prRect->left = prRect->right - size.X * sz.cx;
+ break;
+
+ case WMSZ_RIGHT: case WMSZ_TOPRIGHT: case WMSZ_BOTTOMRIGHT:
+ prRect->right = prRect->left + size.X * sz.cx;
+ break;
+ }
+
+ switch (edge)
+ {
+ case WMSZ_TOP: case WMSZ_TOPLEFT: case WMSZ_TOPRIGHT:
+ prRect->top = prRect->bottom - size.Y * sz.cy;
+ break;
+
+ case WMSZ_BOTTOM: case WMSZ_BOTTOMLEFT: case WMSZ_BOTTOMRIGHT:
+ prRect->bottom = prRect->top + size.Y * sz.cy;
+ break;
+ }
+
+ AdjustWindowRect(prRect, style, FALSE);
+}
+
+void RadTerminalWindowOnActivate(HWND hWnd, UINT state, HWND hwndActDeact, BOOL fMinimized)
+{
+ if (state == WA_INACTIVE)
+ InvalidateRect(hWnd, nullptr, TRUE);
+}
+
+/* void Cls_OnSizing(HWND hwnd, UINT edge, LPRECT prRect) */
+#define HANDLE_WM_SIZING(hwnd, wParam, lParam, fn) \
+ ((fn)((hwnd), (UINT)(wParam), (LPRECT)lParam), TRUE)
+
+LRESULT CALLBACK RadTerminalWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ HANDLE_MSG(hWnd, WM_CREATE, RadTerminalWindowOnCreate);
+ HANDLE_MSG(hWnd, WM_DESTROY, RadTerminalWindowOnDestroy);
+ HANDLE_MSG(hWnd, WM_PAINT, RadTerminalWindowOnPaint);
+ HANDLE_MSG(hWnd, WM_KEYDOWN, RadTerminalWindowOnKeyDown);
+ HANDLE_MSG(hWnd, WM_TIMER, RadTerminalWindowOnTimer);
+ HANDLE_MSG(hWnd, WM_SIZE, RadTerminalWindowOnSize);
+ HANDLE_MSG(hWnd, WM_SIZING, RadTerminalWindowOnSizing);
+ HANDLE_MSG(hWnd, WM_ACTIVATE, RadTerminalWindowOnActivate);
+ //case (WM_KEYDOWN): HANDLE_WM_KEYDOWN((hWnd), (wParam), (lParam), (RadTerminalWindowOnKeyDown)); return 1;
+ //HANDLE_MSG(hWnd, WM_CHAR, RadTerminalWindowOnChar);
+ default: return DefWindowProc(hWnd, uMsg, wParam, lParam);
+ }
+}
diff --git a/RadTerminal.sln b/RadTerminal.sln
new file mode 100644
index 0000000..045b096
--- /dev/null
+++ b/RadTerminal.sln
@@ -0,0 +1,37 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RadTerminal", "RadTerminal.vcxproj", "{5660720B-5A4B-4F82-85C3-AE789F685218}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tsm", "tsm.vcxproj", "{F7508F50-F42C-4091-A87E-227FFE89C038}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5660720B-5A4B-4F82-85C3-AE789F685218}.Debug|x64.ActiveCfg = Debug|x64
+ {5660720B-5A4B-4F82-85C3-AE789F685218}.Debug|x64.Build.0 = Debug|x64
+ {5660720B-5A4B-4F82-85C3-AE789F685218}.Debug|x86.ActiveCfg = Debug|Win32
+ {5660720B-5A4B-4F82-85C3-AE789F685218}.Debug|x86.Build.0 = Debug|Win32
+ {5660720B-5A4B-4F82-85C3-AE789F685218}.Release|x64.ActiveCfg = Release|x64
+ {5660720B-5A4B-4F82-85C3-AE789F685218}.Release|x64.Build.0 = Release|x64
+ {5660720B-5A4B-4F82-85C3-AE789F685218}.Release|x86.ActiveCfg = Release|Win32
+ {5660720B-5A4B-4F82-85C3-AE789F685218}.Release|x86.Build.0 = Release|Win32
+ {F7508F50-F42C-4091-A87E-227FFE89C038}.Debug|x64.ActiveCfg = Debug|x64
+ {F7508F50-F42C-4091-A87E-227FFE89C038}.Debug|x64.Build.0 = Debug|x64
+ {F7508F50-F42C-4091-A87E-227FFE89C038}.Debug|x86.ActiveCfg = Debug|Win32
+ {F7508F50-F42C-4091-A87E-227FFE89C038}.Debug|x86.Build.0 = Debug|Win32
+ {F7508F50-F42C-4091-A87E-227FFE89C038}.Release|x64.ActiveCfg = Release|x64
+ {F7508F50-F42C-4091-A87E-227FFE89C038}.Release|x64.Build.0 = Release|x64
+ {F7508F50-F42C-4091-A87E-227FFE89C038}.Release|x86.ActiveCfg = Release|Win32
+ {F7508F50-F42C-4091-A87E-227FFE89C038}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/RadTerminal.vcxproj b/RadTerminal.vcxproj
new file mode 100644
index 0000000..45ec2d8
--- /dev/null
+++ b/RadTerminal.vcxproj
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{5660720B-5A4B-4F82-85C3-AE789F685218}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>RadTerminal</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <OutDir>$(SolutionDir)Bin\$(Platform)$(Configuration)\</OutDir>
+ <IntDir>Int\$(Platform)$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)'=='Debug'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)'=='Release'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="ProcessUtils.cpp" />
+ <ClCompile Include="RadTerminal.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="tsm.vcxproj">
+ <Project>{f7508f50-f42c-4091-a87e-227ffe89c038}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="ProcessUtils.h" />
+ <ClInclude Include="WinUtils.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/WinUtils.h b/WinUtils.h
new file mode 100644
index 0000000..a2b630b
--- /dev/null
+++ b/WinUtils.h
@@ -0,0 +1,36 @@
+#pragma once
+#include <windows.h>
+
+inline BOOL UnadjustWindowRect(
+ LPRECT prc,
+ DWORD dwStyle,
+ BOOL fMenu)
+{
+ RECT rc;
+ SetRectEmpty(&rc);
+ BOOL fRc = AdjustWindowRect(&rc, dwStyle, fMenu);
+ if (fRc) {
+ prc->left -= rc.left;
+ prc->top -= rc.top;
+ prc->right -= rc.right;
+ prc->bottom -= rc.bottom;
+ }
+ return fRc;
+}
+
+inline RECT Rect(POINT p1, POINT p2)
+{
+ return { p1.x, p1.y, p2.x, p2.y };
+}
+
+inline RECT Rect(POINT p1, SIZE s2)
+{
+ return { p1.x, p1.y, p1.x + s2.cx, p1.y + s2.cy };
+}
+
+inline HFONT CreateFont(LPTSTR pFontFace, int iFontHeight, int cWeight)
+{
+ return CreateFont(iFontHeight, 0, 0, 0, cWeight, FALSE, FALSE, FALSE, ANSI_CHARSET,
+ OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+ DEFAULT_PITCH | FF_DONTCARE, pFontFace);
+}
diff --git a/libtsm b/libtsm
new file mode 160000
+Subproject 9e581863c0cdb7c4055d58eae88a36ce8496d00
diff --git a/tsm.vcxproj b/tsm.vcxproj
new file mode 100644
index 0000000..a9008d6
--- /dev/null
+++ b/tsm.vcxproj
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="libtsm\external\wcwidth\wcwidth.h" />
+ <ClInclude Include="libtsm\external\xkbcommon\xkbcommon-keysyms.h" />
+ <ClInclude Include="libtsm\src\shared\shl-array.h" />
+ <ClInclude Include="libtsm\src\shared\shl-htable.h" />
+ <ClInclude Include="libtsm\src\shared\shl-llog.h" />
+ <ClInclude Include="libtsm\src\tsm\libtsm-int.h" />
+ <ClInclude Include="libtsm\src\tsm\libtsm.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="libtsm\external\wcwidth\wcwidth.c" />
+ <ClCompile Include="libtsm\src\shared\shl-htable.c" />
+ <ClCompile Include="libtsm\src\tsm\tsm-render.c" />
+ <ClCompile Include="libtsm\src\tsm\tsm-screen.c" />
+ <ClCompile Include="libtsm\src\tsm\tsm-selection.c" />
+ <ClCompile Include="libtsm\src\tsm\tsm-unicode.c" />
+ <ClCompile Include="libtsm\src\tsm\tsm-vte-charsets.c" />
+ <ClCompile Include="libtsm\src\tsm\tsm-vte.c" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{F7508F50-F42C-4091-A87E-227FFE89C038}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>tsm</RootNamespace>
+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(SolutionDir)Bin\$(Platform)$(Configuration)\</OutDir>
+ <IntDir>Int\$(Platform)$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(SolutionDir)Bin\$(Platform)$(Configuration)\</OutDir>
+ <IntDir>Int\$(Platform)$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(SolutionDir)Bin\$(Platform)$(Configuration)\</OutDir>
+ <IntDir>Int\$(Platform)$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(SolutionDir)Bin\$(Platform)$(Configuration)\</OutDir>
+ <IntDir>Int\$(Platform)$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>BUILD_ENABLE_DEBUG;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ProjectDir)\libtsm\src\shared;$(ProjectDir)\libtsm\external;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>BUILD_ENABLE_DEBUG;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ProjectDir)\libtsm\src\shared;$(ProjectDir)\libtsm\external;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ProjectDir)\libtsm\src\shared;$(ProjectDir)\libtsm\external;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>$(ProjectDir)\libtsm\src\shared;$(ProjectDir)\libtsm\external;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/tsm.vcxproj.filters b/tsm.vcxproj.filters
new file mode 100644
index 0000000..30ff511
--- /dev/null
+++ b/tsm.vcxproj.filters
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClInclude Include="libtsm\src\tsm\libtsm.h" />
+ <ClInclude Include="libtsm\src\tsm\libtsm-int.h" />
+ <ClInclude Include="libtsm\external\wcwidth\wcwidth.h">
+ <Filter>external</Filter>
+ </ClInclude>
+ <ClInclude Include="libtsm\external\xkbcommon\xkbcommon-keysyms.h">
+ <Filter>external</Filter>
+ </ClInclude>
+ <ClInclude Include="libtsm\src\shared\shl-array.h">
+ <Filter>shared</Filter>
+ </ClInclude>
+ <ClInclude Include="libtsm\src\shared\shl-htable.h">
+ <Filter>shared</Filter>
+ </ClInclude>
+ <ClInclude Include="libtsm\src\shared\shl-llog.h">
+ <Filter>shared</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="libtsm\src\tsm\tsm-render.c" />
+ <ClCompile Include="libtsm\src\tsm\tsm-screen.c" />
+ <ClCompile Include="libtsm\src\tsm\tsm-selection.c" />
+ <ClCompile Include="libtsm\src\tsm\tsm-unicode.c" />
+ <ClCompile Include="libtsm\src\tsm\tsm-vte.c" />
+ <ClCompile Include="libtsm\src\tsm\tsm-vte-charsets.c" />
+ <ClCompile Include="libtsm\external\wcwidth\wcwidth.c">
+ <Filter>external</Filter>
+ </ClCompile>
+ <ClCompile Include="libtsm\src\shared\shl-htable.c">
+ <Filter>shared</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="external">
+ <UniqueIdentifier>{99e2687b-9168-4201-87f1-ff62c9b0680e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="shared">
+ <UniqueIdentifier>{a67ec51c-945f-4b13-acf3-4187ec50ebff}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project> \ No newline at end of file