tym is a Lua-configurable terminal emulator base on VTE.
$ yay -S tym
$ nix-env -iA nixos.tym
Download the latest release from Releases, extract it and run as below
$ ./configure
$ sudo make install
Build dependencies (click to open)
$ sudo pacman -S vte3 lua53
$ sudo apt install libgtk-3-dev libvte-2.91-dev liblua5.3-dev libpcre2-dev
$ sudo xbps-install -S vte3-devel lua-devel
We did not check which packages are needed to build on other distros or OS. We are waiting for your contribution ;)
If $XDG_CONFIG_HOME/tym/config.lua exists, it is executed when the app starts. You can change the path with the --use/-u option.
-- At first, you need to require tym module
local tym = require('tym')
-- set individually
tym.set('width', 100)
tym.set('font', 'DejaVu Sans Mono 11')
-- set by table
tym.set_config({
shell = '/usr/bin/fish',
cursor_shape = 'underline',
autohide = true,
color_foreground = 'red',
})See wiki to check out the advanced examples.
All available config values are shown below.
| field name | type | default value | description |
|---|---|---|---|
shell |
string | $SHELL → vte_get_user_shell() → '/bin/sh' |
Shell to execute. |
term |
string | 'xterm-256color' |
Value of $TERM. |
title |
string | 'tym' |
Initial window title. |
font |
string | '' |
You can specify font with 'FAMILY-LIST [SIZE]', for example 'Ubuntu Mono 12'. The value is parsed by pango_font_description_from_string(). If empty string is set, the system default fixed width font will be used. |
icon |
string | 'utilities-terminal' |
Name of icon. cf. Icon Naming Specification |
role |
string | '' |
Unique identifier for the window. If empty string is set, no value set. (cf. gtk_window_set_role()) |
cursor_shape |
string | 'block' |
'block', 'ibeam' or 'underline' can be used. |
cursor_blink_mode |
string | 'system' |
'system', 'on' or 'off' can be used. |
cjk_width |
string | 'narrow' |
'narrow' or 'wide' can be used. |
background_image |
string | '' |
Path to background image file. |
uri_schemes |
string | 'http https file mailto' |
Space-separated list of URI schemes to be highlighted and clickable. Specify empty string to disable highlighting. Specify '*' to accept any strings valid as schemes (according to RFC 3986). |
width |
integer | 80 |
Initial columns. |
height |
integer | 22 |
Initial rows. |
scale |
integer | 100 |
Font scale in percent(%) |
cell_width |
integer | 100 |
Cell width scale in percent(%). |
cell_height |
integer | 100 |
Cell height scale in percent(%). |
padding_top |
integer | 0 |
Top padding. |
padding_bottom |
integer | 0 |
Bottom padding. |
padding_left |
integer | 0 |
Left padding. |
padding_right |
integer | 0 |
Right padding. |
scrollback_length |
integer | 512 |
Length of the scrollback buffer. |
scrollback_on_output |
boolean | true |
Whether to scroll the buffer when the new data is output. |
ignore_default_keymap |
boolean | false |
Whether to use default keymap. |
autohide |
boolean | false |
Whether to hide mouse cursor when the user presses a key. |
silent |
boolean | false |
Whether to beep when bell sequence is sent. |
bold_is_bright |
boolean | false |
Whether to make bold texts bright. |
osc_clipboard |
boolean | false |
Whether to let the application write the clipboard. See the next section. Requires VTE >= 0.78. |
color_window_background |
string | '' |
Color of the terminal window. It is seen when 'padding_horizontal' 'padding_vertical' is not 0. If you set 'NONE', the window background will not be drawn. |
color_foreground, color_background, color_cursor, color_cursor_foreground, color_highlight, color_highlight_foreground, color_bold, color_0 ... color_15 |
string | See the next section | You can specify standard color string such as '#f00', '#ff0000', 'rgba(22, 24, 33, 0.7)' or 'red'. It will be parsed by gdk_rgba_parse(). If empty string is set, the VTE default color will be used. If you set 'NONE' for color_background, the terminal background will not be drawn. |
Most terminals let the running application put text into the clipboard with OSC 52, which is what makes copying work over SSH and inside tmux. VTE does not implement it (GNOME/vte#2495) and gives no hook to intercept it, so tym exposes the same capability through a VTE termprop instead.
Set osc_clipboard = true in your config to enable it, then write base64-encoded UTF-8 text to the vte.ext.tym.clipboard termprop:
printf '\033]666;vte.ext.tym.clipboard=%s\033\\' "$(printf 'hello' | base64 | tr -d '\n')"The sequence must end with ST (\033\\). VTE does not accept BEL as the terminator of OSC 666.
Text longer than 2048 bytes is dropped: that is the size limit VTE puts on the termprop. tym logs a warning about it when the tmux setup below is in use, but only as long as the sequence stays within the length limit of OSC 666 itself — past roughly 3000 bytes VTE throws the whole sequence away before tym hears anything, and the copy is lost without a word.
For tmux, point the Ms capability at the same sequence and set-clipboard will use it:
set -g set-clipboard on
set -as terminal-overrides ',*:Ms=\E]666;vte.ext.tym.clipboard.flags=%p1%s;vte.ext.tym.clipboard=%p2%s\E\\'tmux only expands Ms when the capability references its first parameter, which is the selection flags, so they are parked in vte.ext.tym.clipboard.flags (OSC 666 takes several ;-separated termprops at once). tym does not act on the flags themselves — the termprop exists to keep the payload a bare base64 string, and, since it arrives even when VTE has thrown the payload away, to let tym warn that a copy was lost.
This is disabled by default on purpose: any output the terminal receives can then replace your clipboard, so simply cating a hostile file would be enough to overwrite it. Reading the clipboard is not implemented, so the application can never learn its contents.
When $XDG_CONFIG_HOME/tym/theme.lua exists, it is loaded before loading config. You can change the path by using the --theme/-t option. The following is an example, whose color values are built-in default. They were ported from iceberg.
local bg = '#161821'
local fg = '#c6c8d1'
return {
color_background = bg,
color_foreground = fg,
color_bold = fg,
color_cursor = fg,
color_cursor_foreground = bg,
color_highlight = fg,
color_highlight_foreground = bg,
color_0 = bg,
color_1 = '#e27878',
color_2 = '#b4be82',
color_3 = '#e2a478',
color_4 = '#84a0c6',
color_5 = '#a093c7',
color_6 = '#89b8c2',
color_7 = fg,
color_8 = '#6b7089',
color_9 = '#e98989',
color_10 = '#c0ca8e',
color_11 = '#e9b189',
color_12 = '#91acd1',
color_13 = '#ada0d3',
color_14 = '#95c4ce',
color_15 = '#d2d4de',
}You need to return the color map as table.
Color correspondence (click to open)
color_0 : black (background)
color_1 : red
color_2 : green
color_3 : brown
color_4 : blue
color_5 : purple
color_6 : cyan
color_7 : light gray (foreground)
color_8 : gray
color_9 : light red
color_10 : light green
color_11 : yellow
color_12 : light blue
color_13 : pink
color_14 : light cyan
color_15 : white
| Key | Action |
|---|---|
| Ctrl Shift c | Copy selection to clipboard. |
| Ctrl Shift v | Paste from clipboard. |
| Ctrl Shift r | Reload config file. |
You can register keymap(s) using tym.set_keymap(accelerator, func) or tym.set_keymaps(table). accelerator must be in a format parsable by gtk_accelerator_parse(). If a truthy value is returned, the event propagation will not be stopped.
-- also can set keymap
tym.set_keymap('<Ctrl><Shift>o', function()
local h = tym.get('height')
tym.set('height', h + 1)
tym.notify('Set window height :' .. h)
end)
-- set by table
tym.set_keymaps({
['<Ctrl><Shift>t'] = function()
tym.reload()
tym.notify('reload config')
end,
['<Ctrl><Shift>v'] = function()
-- reload and notify
tym.send_key('<Ctrl><Shift>t')
end,
['<Shift>y'] = function()
tym.notify('Y has been pressed')
return true -- notification is shown and `Y` will be inserted
end,
['<Shift>w'] = function()
tym.notify('W has been pressed')
-- notification is shown but `W` is not inserted
end,
})| Name | Return value | Description |
|---|---|---|
tym.get(key) |
any | Get config value. |
tym.set(key, value) |
void | Set config value. |
tym.get_default_value(key) |
any | Get default config value. |
tym.get_config() |
table | Get whole config. |
tym.set_config(table) |
void | Set config by table. |
tym.reset_config() |
void | Reset all config. |
tym.set_keymap(accelerator, func) |
void | Set keymap. |
tym.unset_keymap(accelerator) |
void | Unset keymap. |
tym.set_keymaps(table) |
void | Set keymaps by table. |
tym.reset_keymaps() |
void | Reset all keymaps. |
tym.set_hook(hook_name, func) |
void | Set a hook. |
tym.set_hooks(table) |
void | Set hooks. |
tym.reload() |
void | Reload config file. |
tym.reload_theme() |
void | Reload theme file. |
tym.send_key() |
void | Send key press event. |
tym.signal(id, hook, {param...}) |
void | Send signal to the tym instance specified by id. |
tym.set_timeout(func, interval=0) |
int(tag) | Set timeout. return true in func to execute again. |
tym.clear_timeout(tag) |
void | Clear the timeout. |
tym.put(text) |
void | Feed text. |
tym.bell() |
void | Sound bell. |
tym.open(uri) |
void | Open URI via your system default app like xdg-open(1). |
tym.notify(message, title='tym') |
void | Show desktop notification. |
tym.copy(text, target='clipboard') |
void | Copy text to clipboard. As target, 'clipboard', 'primary' or secondary can be used. |
tym.copy_selection(target='clipboard') |
void | Copy current selection. |
tym.paste(target='clipboard') |
void | Paste clipboard. |
tym.check_mod_state(accelerator) |
bool | Check if the mod key(such as '<Ctrl>' or <Shift>) is being pressed. |
tym.color_to_rgba(color) |
r, g, b, a | Convert color string to RGB bytes and alpha float using gdk_rgba_parse(). |
tym.rgba_to_color(r, g, b, a) |
string | Convert RGB bytes and alpha float to color string like rgba(255, 128, 0, 0.5) can be used in color option such as color_background. |
tym.rgb_to_hex(r, g, b) |
string | Convert RGB bytes to 24bit HEX like #ABCDEF. |
tym.hex_to_rgb(hex) |
r, g, b | Convert 24bit HEX like #ABCDEF to RGB bytes. |
tym.get_monitor_model() |
string | Get monitor model on which the window is shown. |
tym.get_cursor_position() |
int, int | Get where column and row the cursor is. |
tym.get_clipboard(target='clipboard') |
string | Get content in the clipboard. |
tym.get_selection() |
string | Get selected text. |
tym.has_selection() |
bool | Get if selected. |
tym.select_all() |
void | Select all texts. |
tym.unselect_all() |
void | Unselect all texts. |
tym.get_text(start_row, start_col, end_row, end_col) |
string | Get text on the terminal screen. If you set -1 to end_row and end_col, the target area will be the size of termianl. |
tym.get_config_path() |
string | Get full path to config file. |
tym.get_theme_path() |
string | Get full path to theme file. |
tym.get_terminal_pid() |
integer | Get terminal pid. |
tym.get_pid() |
integer | Get child pid(usually shell's pid). |
tym.get_ids() |
table[int] | Get tym instance ids. |
tym.get_version() |
string | Get version string. |
| Name | Param | Default action | Description |
|---|---|---|---|
title |
title | changes title | If string is returned, it will be used as the new title. |
bell |
nil | makes the window urgent when it is inactive. | If true is returned, the window will not be urgent. |
clicked |
button, uri | If URI exists under cursor, opens it | Triggered when mouse button is pressed. |
scroll |
delta_x, delta_y, mouse_x, mouse_y | scroll buffer | Triggered when mouse wheel is scrolled. |
drag |
filepath | feed filepath to the console | Triggered when files are dragged to the screen. |
activated |
nil | nothing | Triggered when the window is activated. |
deactivated |
nil | nothing | Triggered when the window is deactivated. |
resized |
nil | nothing | Triggered when the window is resized. |
selected |
string | nothing | Triggered when the text in the terminal screen is selected. |
unselected |
nil | nothing | Triggered when the selection is unselected. |
signal |
string | nothing | Triggered when me.endaaman.tym.hook signal is received. |
If truthy value is returned in a callback function, the default action will be stopped.
tym.set_hooks({
title = function(t)
tym.set('title', 'tym - ' .. t)
return true -- this is needed to cancenl default title application
end,
})
--- NOTE:
-- If you set the hook to 'clicked' handler, you need to open URI manually like below,
tym.set_hook('clicked', function(button, uri)
print('you pressed button:', button) -- 1:left, 2:middle, 3:right
-- open URI only by middle click
if button == 2 then
if uri then
print('you clicked URI: ', uri)
tym.open(uri)
-- disable the default action 'put clipboard' when open URI
return true
end
end
end)Each tym window has an unique ID, which can be checked by tym.get_id() or $TYM_ID, and also listen to D-Bus signal/method call on the path /me/endaaman/tym<ID> and the interface name me.endaaman.tym.
| Name | Input(D-Bus signature) | Description |
|---|---|---|
hook |
s |
Triggers signal hook. |
For example, when you prepare the following config and command,
local tym = require('tym')
tym.set_hook('signal', function (p)
print('Hello from DBus signal')
print('param:', p)
end)$ dbus-send /me/endaaman/tym0 me.endaaman.tym.hook string:'THIS IS PARAM'
or
tym.signal(0, 'hook', {'THIS IS PARAM'}) -- NOTICE: param must be tableyou will get an output like below.
Hello from DBus signal
param: THIS IS PARAM
Alternatively, you can use tym command to send signal.
$ tym --signal hook --dest 0 --param 'THIS IS PARAM'
If the target window is its own one, it will the value of $TYM_ID and --dest can be omitted. So it is enough like below.
$ tym --signal hook --param 'THIS IS PARAM'
| Name | Input (D-Bus signature) | Output (D-Bus signature) | Description |
|---|---|---|---|
get_ids |
None | ai |
Get all tym instance IDs. |
echo |
s |
s |
Echo output the same as input. |
eval |
s |
s |
Evaluate one line lua script. return is needed. |
eval_file |
s |
s |
Evaluate a script file. return is needed. |
exec |
s |
None | Execute one line lua script without outputs. |
eval_file |
s |
None | Execute a script filt without outputs. |
For example, when you exec the command,
$ dbus-send --print-reply --type=method_call --dest=me.endaaman.tym /me/endaaman/tym0 me.endaaman.tym.eval string:'return "title is " .. tym.get("title")'
then you will get like below.
method return time=1646287109.007168 sender=:1.3633 -> destination=:1.3648 serial=39 reply_serial=2
string "title is tym"
As same as signals, you can use tym command to execute method calling.
$ tym --call eval --dest 0 --param 'return "title is " .. tym.get("title")'
Of course, --dest can be omitted as well.
$ tym -h
$ tym --use=/path/to/config.lua
If NONE is provided, all config will be default (user-defined config file will not be loaded).
$ tym -u NONE
$ tym --use=/path/to/theme.lua
If NONE is provided, default theme will be used.
$ tym -t NONE
$ tym --signal hook
Sends a D-Bus signal to the current instance (determined by $TYM_ID environment value). To send to another instance, use --dest (or -d) option.
Calls D-Bus method of the current instance (determined by $TYM_ID environment value). To call it of another instance, provide --dest (or -d) option.
$ tym --call eval --param 'return 1 + 2'
This makes tym a daemon process, which has no window or application context.
$ tym --daemon
To enable the daemon feature, set tym-daemon.desktop as auto-started on the DE's settings or add the line tym --daemon & in your .xinitrc.
This sets the terminal's working directory. <path> must be an absolute path. If unspecified tym will use the current working directory of the terminal invocation.
$ tym --cwd=/home/user/projectsYou can set config value via command line option.
$ tym --shell=/bin/zsh --color_background=red --width=40 --ignore_default_keymap$ tym --isolatedThis option enables tym to create a separate process for each instance. Then an app instance will be isolated from D-Bus and no longer have ability to handle D-Bus signals/method calls.
tym also accepts double dash -- option as the command line to spawn.
$ tym -- less -N DockerfileClone this repo and run as below
$ autoreconf -fvi
$ ./configure --enable-debug
$ make && ./src/tym -u ./path/to/config.lua # for debug
$ make check; cat src/tym-test.log # for unit testsRun tests in docker container
$ docker build -t tym .
$ docker run tymMIT