终端与子进程桥接:ConPTY(Windows)/ PTY(POSIX)/ SSH 远程连接模式。
Terminal 控件本身只负责渲染和输入,需与进程/远程连接桥接:
• ConPTY(Windows 10+):CreatePseudoConsole API 创建伪终端
• PTY(Linux/macOS):openpty / forkpty 创建伪终端对
• SSH:libssh2 等库建立远程 Shell 通道
数据流向:进程 stdout → TerminalWrite;用户输入 → InputCallback → 进程 stdin
| 函数 | 说明 |
|---|---|
int xuiTerminalResize(pW, iCols, iRows) | 同步终端尺寸 |
int xuiTerminalGetCursor(pW, pCol, pRow) | 获取光标位置 |
int xuiTerminalSetInputCallback(pW, onData, pUser) | 用户输入回调 |
int xuiTerminalWrite(pW, pData, iSize) | 进程输出→终端 |
int xuiTerminalFlush(pW) | 刷新显示 |
gcc -O2 -I. -o ch179.exe ch179_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch179.exe
// 输入回调:用户键入 → 写入 ConPTY
static void OnTermInput(xui_widget pW,
const void* pData, int iSize, void* pUser)
{
HANDLE hIn = (HANDLE)pUser;
DWORD written;
WriteFile(hIn, pData, iSize, &written, NULL);
}
// 创建 ConPTY 后设置回调
xuiTerminalSetInputCallback(pTerm, OnTermInput, hConPTYIn);
// 读取线程:ConPTY 输出 → Terminal
void ReadThread(void* arg) {
char buf[4096];
DWORD nRead;
while (ReadFile(hConPTYOut, buf, 4096, &nRead, NULL)) {
xuiTerminalWrite(pTerm, buf, nRead);
xuiTerminalFlush(pTerm);
}
}
// 窗口大小变化时同步
xuiTerminalResize(pTerm, iNewCols, iNewRows);
// 同时调用 ResizePseudoConsole(hConPTY, coord)将以下代码保存为 ch179_main.c,编译后运行即可看到终端与 ConPTY 桥接效果(启动 cmd.exe):
#include "xge.h"
#include "xui.h"
#include <windows.h>
static xui_context g_pCtx;
static xui_widget g_pTerm;
static HANDLE g_hPTYIn;
static HANDLE g_hPTYOut;
static void onTermInput(const char* pData, int iSize, void* pUser) {
(void)pUser;
DWORD written;
WriteFile(g_hPTYIn, pData, iSize, &written, NULL);
}
static DWORD WINAPI readThread(LPVOID arg) {
(void)arg;
char buf[4096];
DWORD nRead;
while (ReadFile(g_hPTYOut, buf, 4096, &nRead, NULL)) {
xuiTerminalWrite(g_pTerm, buf, nRead);
xuiTerminalFlush(g_pTerm);
}
return 0;
}
static int onFrame(void* pUser) {
(void)pUser;
xgeClear(0x1E1E1EFF);
xui_vec2_t pos;
xgeMouseGet(&pos.x, &pos.y);
xuiInputPointerMove(g_pCtx, pos.x, pos.y);
if (xgeMouseDown(XGE_MOUSE_LEFT))
xuiInputPointerDown(g_pCtx, pos.x, pos.y, 0);
else
xuiInputPointerUp(g_pCtx, pos.x, pos.y, 0);
xuiUpdate(g_pCtx, xgeGetDelta());
xuiLayout(g_pCtx);
xuiRender(g_pCtx, NULL, NULL, 0);
return XGE_OK;
}
int main(void) {
xge_desc_t d = { 0 };
d.iWidth = 800; d.iHeight = 600;
d.sTitle = "ch179 - ConPTY";
d.iFlags = XGE_INIT_VSYNC;
xgeInit(&d);
xuiCreate(&g_pCtx);
extern const xui_proxy_t tXgeProxy;
xui_proxy_t tXgeProxy = xuiProxyXge();
xuiSetProxy(g_pCtx, &tXgeProxy);
xuiSetViewportSize(g_pCtx, 800, 600);
// 创建终端
xui_terminal_desc_t tdesc = { 0 };
xuiTerminalCreate(g_pCtx, &g_pTerm, &tdesc);
xuiTerminalResize(g_pTerm, 80, 24);
xuiTerminalSetInputCallback(g_pTerm, onTermInput, NULL);
// 创建管道模拟 ConPTY(简化示例)
SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, TRUE };
CreatePipe(&g_hPTYOut, &g_hPTYIn, &sa, 0);
// 启动读取线程
CreateThread(NULL, 0, readThread, NULL, 0, NULL);
// 写入欢迎信息
xuiTerminalWriteText(g_pTerm, "ConPTY Bridge Demo\r\n");
xuiTerminalFlush(g_pTerm);
xui_widget pRoot = xuiGetRootWidget(g_pCtx);
xuiWidgetAddChild(pRoot, g_pTerm);
xuiWidgetSetRect(g_pTerm, (xui_rect_t){0, 0, 800, 600});
xgeRun(onFrame, NULL);
xuiDestroy(g_pCtx);
xgeUnit();
return 0;
}