Core LibraryalphaHome

Choose documentation 3

latest

Current version

latest
English

Early release libtmux is released, and its APIs may still change between releases. This site's structure and URLs may change too.

Edit this page on GitHub

tmux client library for C++

Control tmux, connect to and create servers, navigate sessions, windows, and panes, send keys, watch for changes via control mode, capture pane contents.

Install

include(FetchContent)
FetchContent_Declare(libtmux-cxx
GIT_REPOSITORY https://github.com/libtmux/libtmux-cxx
GIT_TAG v0.1.0-alpha.13)
FetchContent_MakeAvailable(libtmux-cxx)

At a glance

Server → Session → Window → Pane. Each level of the tmux hierarchy has its own entry in the C++ API, with the tmux object it wraps.

TypeWraps
libtmux::Server A tmux server
libtmux::Session A session, ID $n
libtmux::Window A window in a session, ID @n
libtmux::Pane A pane in a window, ID %n

What it does

Start with C++

Example details
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <stdexcept>
#include <libtmux/libtmux.hpp>
int main() {
char directory[] = "/tmp/ltx-cxx-home-XXXXXX";
if (!mkdtemp(directory)) {
std::perror("create temporary directory");
return 1;
}
const auto socket = std::filesystem::path(directory) / "tmux.sock";
const auto server = libtmux::Server::startable_at_socket_path(
socket,
"/dev/null",
{},
{.timeout = std::chrono::seconds{5}}).value();
const auto work = [&] {
const auto session = server.new_session({
.name = "work",
.shell_command = "/bin/sh",
}).value();
const auto editor = session.new_window({.name = "editor"}).value();
const auto logs = editor.split({
.horizontal = true,
.shell_command = "/bin/sh",
.percentage = 30,
}).value();
logs.send_text("printf '\\nhello from %s\\n' libtmux").value();
logs.send_key("Enter").value();
const auto ready = logs.wait_for_text(
"hello from libtmux",
{.timeout = std::chrono::seconds{5}}).value();
if (!ready.matched) throw std::runtime_error("output did not arrive");
const auto visible = logs.capture().value();
for (const auto line : libtmux::capture_lines(visible)) {
if (line == "hello from libtmux") {
std::puts("hello from libtmux");
return;
}
}
throw std::runtime_error("capture is missing the output line");
};
work();
}
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <stdexcept>
#include <utility>
#include <libtmux/libtmux.hpp>
int main() {
char directory[] = "/tmp/ltx-cxx-home-XXXXXX";
if (!mkdtemp(directory)) {
std::perror("create temporary directory");
return 1;
}
const auto checked = [](auto result) {
if (!result) throw std::runtime_error(result.error().diagnostic);
return std::move(result).value();
};
const auto socket = std::filesystem::path(directory) / "tmux.sock";
const auto server = checked(libtmux::Server::startable_at_socket_path(
socket,
"/dev/null",
{},
{.timeout = std::chrono::seconds{5}}));
const auto work = [&] {
const auto session = checked(server.new_session({
.name = "work",
.shell_command = "/bin/sh",
}));
const auto editor = checked(session.new_window({.name = "editor"}));
const auto logs = checked(editor.split({
.horizontal = true,
.shell_command = "/bin/sh",
.percentage = 30,
}));
checked(logs.send_text("printf '\\nhello from %s\\n' libtmux"));
checked(logs.send_key("Enter"));
const auto ready = checked(logs.wait_for_text(
"hello from libtmux",
{.timeout = std::chrono::seconds{5}}));
if (!ready.matched) throw std::runtime_error("output did not arrive");
const auto visible = checked(logs.capture());
for (const auto line : libtmux::capture_lines(visible)) {
if (line == "hello from libtmux") {
std::puts("hello from libtmux");
return;
}
}
throw std::runtime_error("capture is missing the output line");
};
int status = 0;
try {
work();
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
status = 1;
}
return status;
}
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <stdexcept>
#include <utility>
#include <libtmux/libtmux.hpp>
int main() {
char directory[] = "/tmp/ltx-cxx-home-XXXXXX";
if (!mkdtemp(directory)) {
std::perror("create temporary directory");
return 1;
}
const auto socket = std::filesystem::path(directory) / "tmux.sock";
auto created = libtmux::Server::startable_at_socket_path(
socket,
"/dev/null",
{},
{.timeout = std::chrono::seconds{5}});
if (!created) {
std::fprintf(stderr, "%s\n", created.error().diagnostic.c_str());
std::filesystem::remove_all(directory);
return 1;
}
const auto server = std::move(created).value();
const auto work = [&] {
const auto session = server.new_session({
.name = "work",
.shell_command = "/bin/sh",
}).value();
const auto editor = session.new_window({.name = "editor"}).value();
const auto logs = editor.split({
.horizontal = true,
.shell_command = "/bin/sh",
.percentage = 30,
}).value();
logs.send_text("printf '\\nhello from %s\\n' libtmux").value();
logs.send_key("Enter").value();
const auto ready = logs.wait_for_text(
"hello from libtmux",
{.timeout = std::chrono::seconds{5}}).value();
if (!ready.matched) throw std::runtime_error("output did not arrive");
const auto visible = logs.capture().value();
for (const auto line : libtmux::capture_lines(visible)) {
if (line == "hello from libtmux") {
std::puts("hello from libtmux");
return;
}
}
throw std::runtime_error("capture is missing the output line");
};
int status = 0;
try {
work();
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
status = 1;
}
if (std::filesystem::exists(socket)) {
server.kill().value();
}
std::filesystem::remove_all(directory);
return status;
}
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <stdexcept>
#include <utility>
#include <libtmux/libtmux.hpp>
int main() {
char directory[] = "/tmp/ltx-cxx-home-XXXXXX";
if (!mkdtemp(directory)) {
std::perror("create temporary directory");
return 1;
}
const auto checked = [](auto result) {
if (!result) throw std::runtime_error(result.error().diagnostic);
return std::move(result).value();
};
const auto socket = std::filesystem::path(directory) / "tmux.sock";
auto created = libtmux::Server::startable_at_socket_path(
socket,
"/dev/null",
{},
{.timeout = std::chrono::seconds{5}});
if (!created) {
std::fprintf(stderr, "%s\n", created.error().diagnostic.c_str());
std::filesystem::remove_all(directory);
return 1;
}
const auto server = std::move(created).value();
const auto work = [&] {
const auto session = checked(server.new_session({
.name = "work",
.shell_command = "/bin/sh",
}));
const auto editor = checked(session.new_window({.name = "editor"}));
const auto logs = checked(editor.split({
.horizontal = true,
.shell_command = "/bin/sh",
.percentage = 30,
}));
checked(logs.send_text("printf '\\nhello from %s\\n' libtmux"));
checked(logs.send_key("Enter"));
const auto ready = checked(logs.wait_for_text(
"hello from libtmux",
{.timeout = std::chrono::seconds{5}}));
if (!ready.matched) throw std::runtime_error("output did not arrive");
const auto visible = checked(logs.capture());
for (const auto line : libtmux::capture_lines(visible)) {
if (line == "hello from libtmux") {
std::puts("hello from libtmux");
return;
}
}
throw std::runtime_error("capture is missing the output line");
};
int status = 0;
try {
work();
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
status = 1;
}
if (std::filesystem::exists(socket)) {
server.kill().value();
}
std::filesystem::remove_all(directory);
return status;
}
Run this example

CMake 3.25+, Clang 18 with matching libc++ development files, C++23, tmux on Linux/macOS

Save the program above as main.cpp in a new directory, along with the files below.

Cleanup is omitted; the private tmux server and its temporary files may remain. Select Cleanup to remove them when the program finishes. The program stops its private tmux server and removes any temporary files it created when it finishes. Error handling adds diagnostics; every version checks required results and deadlines.

CMakeLists.txt
cmake_minimum_required(VERSION 3.25)
project(libtmux_api_example LANGUAGES CXX)
find_package(libtmux REQUIRED)
add_executable(api_example main.cpp)
target_link_libraries(api_example PRIVATE libtmux::libtmux)

Setup and run

# Save the files above at their displayed relative paths.
git clone https://github.com/libtmux/libtmux-cxx.git libtmux-source
git -C libtmux-source checkout --detach 3f93b021cb51e8349bfb9cecc6fd4091f6e79a0f
cmake -S libtmux-source -B library \
-DCMAKE_TOOLCHAIN_FILE="$PWD/libtmux-source/cmake/toolchains/clang-libcxx.cmake" \
-DCMAKE_BUILD_TYPE=Release -DLIBTMUX_BUILD_TESTS=OFF -DLIBTMUX_BUILD_EXAMPLES=OFF \
-DLIBTMUX_BUILD_TESTING_LIBRARY=OFF -DLIBTMUX_CXX_STANDARD=23 \
-DCMAKE_CXX_COMPILER=clang++
cmake --build library -j 2
cmake --install library --prefix "$PWD/prefix"
cmake -S . -B build -DCMAKE_PREFIX_PATH="$PWD/prefix" -DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_CXX_STANDARD=23
cmake --build build -j 2
build/api_example

Expected output

hello from libtmux

Library revision: 3f93b021cb51.

Typed API

What you get over the tmux CLI

Testing

Run tests against an isolated tmux server: libtmux::test::ScopedTmuxServer.

Esc

Type to search.

Reference previewOpen reference