libtmux for Erlang
Erlang-based tmux client
tmux client library for Erlang. Control tmux, connect to and create servers, navigate sessions, windows, and panes, send keys, capture pane contents
-module(readme_demo).-moduledoc "Create a workspace, query its graph and preserve captured values.".-export([run/1]).-import_record(tmux_model, [pane]).
-spec run(binary()) -> map().run(Executable) -> {ok, Server, Ownership} = tmux_server:start_owned(#{executable => Executable}), try {ok, Session} = tmux_session:create(Server, #{name => <<"workspace">>, window_name => <<"editor">>, width => 120, height => 30, launch => #{argv => [<<"/bin/cat">>]}}), {ok, Worker} = tmux_window:create(Session, #{name => <<"workers">>, launch => #{argv => [<<"/bin/cat">>]}}), {ok, Right} = tmux_pane:split(Worker, #{direction => horizontal, launch => #{argv => [<<"/bin/cat">>]}}), {ok, _} = tmux_window:select_layout(Worker, even_horizontal), {ok, _} = tmux_pane:resize(Right, #{width => 40}), {ok, Snapshot} = tmux_server:snapshot(Server), Windows = tmux_select:windows(Snapshot), Workers = #{name => <<"workers">>, panes => #{count => 2, every => #{current_command => <<"cat">>}}}, {ok, Captured} = tmux_select:one(Workers, Windows), {ok, Children} = tmux_snapshot:related(panes, Captured, Snapshot), Ids = [Id || #pane{id = Id} <:- Children], 2 = length(Ids), Criteria = #{width => #{gte => 20}, window => #{is => #{name => <<"workers">>}}}, Query = tmux_query:where(Criteria, tmux_query:panes(Server)), {ok, Fetched} = tmux_query:fetch(Query), 2 = tmux_select:size(Fetched), {ok, _} = tmux_window:rename(Worker, <<"finished">>), <<"workers">> = tmux_model:get(name, Captured), {ok, Fresh} = tmux_query:fetch(tmux_query:where( #{name => <<"finished">>}, tmux_query:windows(Server))), {ok, Renamed} = tmux_select:one(Fresh), <<"finished">> = tmux_model:get(name, Renamed), 1 = length(tmux_snapshot:sessions(Snapshot)), 2 = length(tmux_snapshot:windows(Snapshot)), 3 = length(tmux_snapshot:panes(Snapshot)), #{sessions => 1, windows => 2, panes => 3, matched_panes => 2, captured_name => <<"workers">>, immutable => true} after ok = tmux_server:stop_owned(Ownership) end.-module(readme_demo).-moduledoc "Create a workspace, query its graph and preserve captured values.".-export([run/1]).-import_record(tmux_model, [pane]).
-spec run(binary()) -> map().run(Executable) -> {ok, Server, Ownership} = tmux_server:start_owned(#{executable => Executable}), try {ok, Session} = tmux_session:create(Server, #{name => <<"workspace">>, window_name => <<"editor">>, width => 120, height => 30, launch => #{argv => [<<"/bin/cat">>]}}), {ok, Worker} = tmux_window:create(Session, #{name => <<"workers">>, launch => #{argv => [<<"/bin/cat">>]}}), {ok, Right} = tmux_pane:split(Worker, #{direction => horizontal, launch => #{argv => [<<"/bin/cat">>]}}), {ok, _} = tmux_window:select_layout(Worker, even_horizontal), {ok, _} = tmux_pane:resize(Right, #{width => 40}), {ok, Snapshot} = tmux_server:snapshot(Server), Windows = tmux_select:windows(Snapshot), Workers = #{name => <<"workers">>, panes => #{count => 2, every => #{current_command => <<"cat">>}}}, {ok, Captured} = tmux_select:one(Workers, Windows), {ok, Children} = tmux_snapshot:related(panes, Captured, Snapshot), Ids = [Id || #pane{id = Id} <:- Children], 2 = length(Ids), Criteria = #{width => #{gte => 20}, window => #{is => #{name => <<"workers">>}}}, Query = tmux_query:where(Criteria, tmux_query:panes(Server)), {ok, Fetched} = tmux_query:fetch(Query), 2 = tmux_select:size(Fetched), {ok, _} = tmux_window:rename(Worker, <<"finished">>), <<"workers">> = tmux_model:get(name, Captured), {ok, Fresh} = tmux_query:fetch(tmux_query:where( #{name => <<"finished">>}, tmux_query:windows(Server))), {ok, Renamed} = tmux_select:one(Fresh), <<"finished">> = tmux_model:get(name, Renamed), 1 = length(tmux_snapshot:sessions(Snapshot)), 2 = length(tmux_snapshot:windows(Snapshot)), 3 = length(tmux_snapshot:panes(Snapshot)), #{sessions => 1, windows => 2, panes => 3, matched_panes => 2, captured_name => <<"workers">>, immutable => true} after ok = tmux_server:stop_owned(Ownership) end.-module(readme_demo).-moduledoc "Create a workspace, query its graph and preserve captured values.".-export([run/1]).-import_record(tmux_model, [pane]).
-spec run(binary()) -> map().run(Executable) -> {ok, Server, Ownership} = tmux_server:start_owned(#{executable => Executable}), try {ok, Session} = tmux_session:create(Server, #{name => <<"workspace">>, window_name => <<"editor">>, width => 120, height => 30, launch => #{argv => [<<"/bin/cat">>]}}), try {ok, Worker} = tmux_window:create(Session, #{name => <<"workers">>, launch => #{argv => [<<"/bin/cat">>]}}), {ok, Right} = tmux_pane:split(Worker, #{direction => horizontal, launch => #{argv => [<<"/bin/cat">>]}}), {ok, _} = tmux_window:select_layout(Worker, even_horizontal), {ok, _} = tmux_pane:resize(Right, #{width => 40}), {ok, Snapshot} = tmux_server:snapshot(Server), Windows = tmux_select:windows(Snapshot), Workers = #{name => <<"workers">>, panes => #{count => 2, every => #{current_command => <<"cat">>}}}, {ok, Captured} = tmux_select:one(Workers, Windows), {ok, Children} = tmux_snapshot:related(panes, Captured, Snapshot), Ids = [Id || #pane{id = Id} <:- Children], 2 = length(Ids), Criteria = #{width => #{gte => 20}, window => #{is => #{name => <<"workers">>}}}, Query = tmux_query:where(Criteria, tmux_query:panes(Server)), {ok, Fetched} = tmux_query:fetch(Query), 2 = tmux_select:size(Fetched), {ok, _} = tmux_window:rename(Worker, <<"finished">>), <<"workers">> = tmux_model:get(name, Captured), {ok, Fresh} = tmux_query:fetch(tmux_query:where( #{name => <<"finished">>}, tmux_query:windows(Server))), {ok, Renamed} = tmux_select:one(Fresh), <<"finished">> = tmux_model:get(name, Renamed), 1 = length(tmux_snapshot:sessions(Snapshot)), 2 = length(tmux_snapshot:windows(Snapshot)), 3 = length(tmux_snapshot:panes(Snapshot)), #{sessions => 1, windows => 2, panes => 3, matched_panes => 2, captured_name => <<"workers">>, immutable => true} after {ok, _} = tmux_session:kill(Session) end after ok = tmux_server:stop_owned(Ownership) end.-module(readme_demo).-moduledoc "Create a workspace, query its graph and preserve captured values.".-export([run/1]).-import_record(tmux_model, [pane]).
-spec run(binary()) -> map().run(Executable) -> {ok, Server, Ownership} = tmux_server:start_owned(#{executable => Executable}), try {ok, Session} = tmux_session:create(Server, #{name => <<"workspace">>, window_name => <<"editor">>, width => 120, height => 30, launch => #{argv => [<<"/bin/cat">>]}}), try {ok, Worker} = tmux_window:create(Session, #{name => <<"workers">>, launch => #{argv => [<<"/bin/cat">>]}}), {ok, Right} = tmux_pane:split(Worker, #{direction => horizontal, launch => #{argv => [<<"/bin/cat">>]}}), {ok, _} = tmux_window:select_layout(Worker, even_horizontal), {ok, _} = tmux_pane:resize(Right, #{width => 40}), {ok, Snapshot} = tmux_server:snapshot(Server), Windows = tmux_select:windows(Snapshot), Workers = #{name => <<"workers">>, panes => #{count => 2, every => #{current_command => <<"cat">>}}}, {ok, Captured} = tmux_select:one(Workers, Windows), {ok, Children} = tmux_snapshot:related(panes, Captured, Snapshot), Ids = [Id || #pane{id = Id} <:- Children], 2 = length(Ids), Criteria = #{width => #{gte => 20}, window => #{is => #{name => <<"workers">>}}}, Query = tmux_query:where(Criteria, tmux_query:panes(Server)), {ok, Fetched} = tmux_query:fetch(Query), 2 = tmux_select:size(Fetched), {ok, _} = tmux_window:rename(Worker, <<"finished">>), <<"workers">> = tmux_model:get(name, Captured), {ok, Fresh} = tmux_query:fetch(tmux_query:where( #{name => <<"finished">>}, tmux_query:windows(Server))), {ok, Renamed} = tmux_select:one(Fresh), <<"finished">> = tmux_model:get(name, Renamed), 1 = length(tmux_snapshot:sessions(Snapshot)), 2 = length(tmux_snapshot:windows(Snapshot)), 3 = length(tmux_snapshot:panes(Snapshot)), #{sessions => 1, windows => 2, panes => 3, matched_panes => 2, captured_name => <<"workers">>, immutable => true} after {ok, _} = tmux_session:kill(Session) end after ok = tmux_server:stop_owned(Ownership) end.Run this example
Linux, tmux 3.3a–3.8, Erlang/OTP 29.1.2, Rebar3 3.28.0, Git, make and a C11 compiler. Save the displayed files in an empty project directory.
Save the program above as src/readme_demo.erl in a new directory, along with the files below.
Every version retires its owned server in try/after. Cleanup also removes the created session explicitly. The program stops its private tmux server and removes any temporary files it created when it finishes. Unmatched tagged results raise with a stack trace. The program propagates failures through try/after cleanup.
rebar.config
{minimum_otp_vsn, "29.1.2"}.{erl_opts, [debug_info, warnings_as_errors]}.{deps, [ {libtmux_erlang, {git_subdir, "https://github.com/libtmux/libtmux-erlang.git", {ref, "9a1ca749f7bf6f082c910c99f961bb586781ab0b"}, "apps/libtmux_erlang"}}]}.src/tmux_example.app.src
{application, tmux_example, [ {vsn, "0.1.0"}, {applications, [kernel, stdlib, libtmux_erlang]}, {modules, []}]}.Setup and run
rebar3 compileenv \ -u TMUX \ -u TMUX_PANE \ erl \ -noshell \ -pa _build/default/lib/tmux_example/ebin \ -pa _build/default/lib/libtmux_erlang/apps/libtmux_erlang/ebin \ -eval ' {ok, _} = application:ensure_all_started(libtmux_erlang), Tmux = unicode:characters_to_binary(os:find_executable("tmux")), Stats = readme_demo:run(Tmux), io:format("~B session, ~B windows, ~B panes; ~B matches~n", [ maps:get(sessions, Stats), maps:get(windows, Stats), maps:get(panes, Stats), maps:get(matched_panes, Stats)]), halt().'Expected output
1 session, 2 windows, 3 panes; 2 matchesInstall libtmux_erlang
rebar.config
rebar3 compileMerge this deps entry into rebar.config. Requires Erlang/OTP 29.1.2, Rebar3 3.28.0, Linux, tmux 3.3a–3.8, make and a C11 compiler. Commit rebar.lock to retain the resolved Git revision. The application is unreleased; no Hex package is advertised.
Topics
Subject-by-subject, with Erlang in every code block.
Guides
Task guides for Erlang.
Examples
Complete programs, sourced from the code each port tests.
Concepts
The domain model every port shares.