37 Commits

Author SHA1 Message Date
e770c87e10 v1.5.0 2014-11-09 16:43:44 +01:00
601366be6d Support for restoring neovim sessions 2014-11-09 16:42:53 +01:00
059686ab6c Merge pull request #55 from rburny/master
Fixes to #51 and #52
2014-10-26 16:59:41 +01:00
488f086fa5 Add a note about HISTCONTROL to README. 2014-10-26 02:17:12 +02:00
fcf7ca13f0 Only save pane history if its not running any program (other than Bash
shell). Fixes a bug where 'history -w' was sent to pane running Bash
script.
2014-10-26 01:30:00 +02:00
e38eed7dae v1.4.0 2014-10-25 18:46:15 +02:00
a35d5f9b90 Update changelog & readme for the zoomed windows feature
Closes #47, #54
2014-10-25 18:43:56 +02:00
0b496dd228 Removing the last_resized variable
Instead resizing only if pane_active is set so we are sure the resizep
is called only once
2014-10-25 18:29:52 +02:00
ad52ade4bf Preserving layout of zoomed windows across restores
The problem is that tmux list-window shows only the current pane layout
if a pane is maximized. This is a bug in tmux. In order to avoid this
bug we unzoom the window when saving and zoom in again after saving.
This implies that the Z flag is no longer set in list-windows, and so it
can't be used when restoring. Instead we use the Z flag of the panes
(which still have it) to restore the zoom.
2014-10-25 18:29:52 +02:00
3ba092459a Merge pull request #49 from tmux-plugins/bash_history
Bash save and restore history feature
2014-10-20 23:17:46 +02:00
6a6d65b98a Update the readme 2014-10-20 23:16:56 +02:00
f3fe4acc39 Document bash restore history feature 2014-10-17 22:40:15 +02:00
8684d4592b Flag gate the bash history restore feature 2014-10-17 22:33:29 +02:00
8328de41d8 Update changelog 2014-10-17 17:47:10 +02:00
94985fc500 Extract save_shell_history to a separate function 2014-10-17 17:45:49 +02:00
81982b5114 Add bash history saving and restoring (first version).
This does not yet have flag to turn the feature off.
2014-10-17 17:14:33 +02:00
a73c465e47 Add issue video screenshot 2014-10-07 13:55:23 +02:00
4ba0e398b9 Document tmux env save dir 2014-10-06 12:59:14 +02:00
8fd38588c0 Fix ps command flags for FreeBSD
This was reported by @duck in #45
2014-09-29 13:39:41 +02:00
a7fe9dcac3 Command line script that fully restores tmux environment 2014-09-24 14:33:46 +02:00
ec9f68cad5 Quote arguments in tmux display-message 2014-09-24 14:30:05 +02:00
94594efdb0 Small bugfix: text command arguments 2014-09-24 14:25:30 +02:00
1b79eb2f63 Rename default strategy to ps 2014-09-21 15:12:35 +02:00
8ebda79f68 Implement save command strategy gdb
@danschumann originally came up with this strategy in #44
2014-09-21 00:08:41 +02:00
ae9083e695 Implement save command strategy: pgrep 2014-09-20 23:55:19 +02:00
9f7050aaae Use a strategy when fetching pane full command 2014-09-20 23:47:15 +02:00
99abfa5f13 Small readme update 2014-09-20 22:45:37 +02:00
95303946b2 v1.3.0 2014-09-20 22:36:42 +02:00
1d09f07d2b Remove dependency on pgrep; use ps to get process names
Fixes #43
2014-09-20 22:33:14 +02:00
618769b62f Update readme 2014-09-20 13:23:42 +02:00
fac377bf8c Add bash to the dependency list 2014-09-10 21:53:38 +02:00
dc7561df74 Small readme tweak 2014-09-10 13:26:14 +02:00
eb2cd31d4b Update readme
Do not invite people to negotiate default program restore list.
2014-09-07 10:19:03 +02:00
e242ea6d8d v1.2.1 2014-09-02 22:48:50 +02:00
a0a3f2fd56 When a pane is not restored, don't restore it's program 2014-09-02 22:47:38 +02:00
d606106f1c Fix: command prompt not ideal after a restore
Fixes #36
2014-09-02 22:34:00 +02:00
d5598d1c61 Update readme 2014-09-01 21:18:15 +02:00
14 changed files with 287 additions and 37 deletions

View File

@ -2,6 +2,30 @@
### master ### master
### v1.5.0, 2014-11-09
- add support for restoring neovim sessions
### v1.4.0, 2014-10-25
- plugin now uses strategies when fetching pane full command. Implemented
'default' strategy.
- save command strategy: 'pgrep'. It's here only if fallback is needed.
- save command strategy: 'gdb'
- rename default strategy name to 'ps'
- create `expect` script that can fully restore tmux environment
- fix default save command strategy `ps` command flags. Flags are different for
FreeBSD.
- add bash history saving and restoring (@rburny)
- preserving layout of zoomed windows across restores (@Azrael3000)
### v1.3.0, 2014-09-20
- remove dependency on `pgrep` command. Use `ps` for fetching process names.
### v1.2.1, 2014-09-02
- tweak 'new_pane' creation strategy to fix #36
- when running multiple tmux server and for a large number of panes (120 +) when
doing a restore, some panes might not be created. When that is the case also
don't restore programs for those panes.
### v1.2.0, 2014-09-01 ### v1.2.0, 2014-09-01
- new feature: inline strategies when restoring a program - new feature: inline strategies when restoring a program

View File

@ -12,7 +12,7 @@ projects.
can be completely restored after a system restart (or when you feel like it). can be completely restored after a system restart (or when you feel like it).
No configuration is required. You should feel like you never quit tmux. No configuration is required. You should feel like you never quit tmux.
It even (optionally) [restores vim sessions](#restoring-vim-sessions)! It even (optionally) [restores vim and neovim sessions](#restoring-vim-and-neovim-sessions)!
### Screencast ### Screencast
@ -20,15 +20,15 @@ It even (optionally) [restores vim sessions](#restoring-vim-sessions)!
### Key bindings ### Key bindings
- `prefix + Ctrl-s` or `prefix + Alt-s` - save - `prefix + Ctrl-s` - save
- `prefix + Ctrl-r` or `prefix + Alt-r` - restore - `prefix + Ctrl-r` - restore
Some people can't get `Alt` key mappings to work so they are deprecated. `prefix + Alt-s` and `prefix + Alt-r` key bindings are now deprecated.
For custom key bindings, add to `.tmux.conf`: For custom key bindings, add to `.tmux.conf`:
set -g @resurrect-save "S" set -g @resurrect-save 'S'
set -g @resurrect-restore "R" set -g @resurrect-restore 'R'
### About ### About
@ -37,26 +37,31 @@ This plugin goes to great lengths to save and restore all the details from your
- all sessions, windows, panes and their order - all sessions, windows, panes and their order
- current working directory for each pane - current working directory for each pane
- **exact pane layouts** within windows - **exact pane layouts** within windows (even when zoomed)
- active and alternative session - active and alternative session
- active and alternative window for each session - active and alternative window for each session
- windows with focus - windows with focus
- active pane for each window - active pane for each window
- programs running within a pane! More details in the - programs running within a pane! More details in the
[configuration section](#configuration). [configuration section](#configuration).
- restoring vim sessions (optional). More details in - restoring vim/neovim sessions (optional). More details in
[restoring vim sessions](#restoring-vim-sessions). [restoring vim and neovim sessions](#restoring-vim-and-neovim-sessions).
- restoring bash history (optional, *experimental*). More details in
[restoring bash history](#restoring-bash-history-experimental).
Requirements / dependencies: `tmux 1.9` or higher, `pgrep` Requirements / dependencies: `tmux 1.9` or higher, `bash`.
`tmux-resurrect` is idempotent! It will not try to restore panes or windows that
already exist.
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended) ### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
Add plugin to the list of TPM plugins in `.tmux.conf`: Add plugin to the list of TPM plugins in `.tmux.conf`:
set -g @tpm_plugins " \ set -g @tpm_plugins ' \
tmux-plugins/tpm \ tmux-plugins/tpm \
tmux-plugins/tmux-resurrect \ tmux-plugins/tmux-resurrect \
" '
Hit `prefix + I` to fetch the plugin and source it. You should now be able to Hit `prefix + I` to fetch the plugin and source it. You should now be able to
use the plugin. use the plugin.
@ -83,8 +88,7 @@ You should now be able to use the plugin.
Configuration is not required, but it enables extra features. Configuration is not required, but it enables extra features.
Only a conservative list of programs is restored by default:<br/> Only a conservative list of programs is restored by default:<br/>
`vi vim emacs man less more tail top htop irssi`. `vi vim nvim emacs man less more tail top htop irssi`.
Open a GitHub issue if you think some other program should be on the default list.
- Restore additional programs with the setting in `.tmux.conf`: - Restore additional programs with the setting in `.tmux.conf`:
@ -98,8 +102,8 @@ Open a GitHub issue if you think some other program should be on the default lis
set -g @resurrect-processes 'irb pry "~rails server" "~rails console"' set -g @resurrect-processes 'irb pry "~rails server" "~rails console"'
- If the wrong command is restored, try specifying inline strategy for the - Use `->` to specify a command to be used when restoring a program (useful if
program with `->`: the default restore command fails ):
set -g @resurrect-processes 'some_program "grunt->grunt development"' set -g @resurrect-processes 'some_program "grunt->grunt development"'
@ -111,15 +115,39 @@ Open a GitHub issue if you think some other program should be on the default lis
set -g @resurrect-processes ':all:' set -g @resurrect-processes ':all:'
#### Restoring vim sessions #### Restoring vim and neovim sessions
- save vim sessions. I recommend [tpope/vim-obsession](https://github.com/tpope/vim-obsession). - save vim/neovim sessions. I recommend
[tpope/vim-obsession](https://github.com/tpope/vim-obsession) (as almost every
plugin, it works for both vim and neovim).
- in `.tmux.conf`: - in `.tmux.conf`:
set -g @resurrect-strategy-vim "session" # for vim
set -g @resurrect-strategy-vim 'session'
# for neovim
set -g @resurrect-strategy-nvim 'session'
`tmux-resurrect` will now restore vim sessions if `Sessions.vim` file is `tmux-resurrect` will now restore vim and neovim sessions if `Sessions.vim` file
present. is present.
#### Resurrect save dir
By default Tmux environment is saved to a file in `~/.tmux/resurrect` dir.
Change this with:
set -g @resurrect-dir '/some/path'
#### Restoring bash history (experimental)
In `.tmux.conf`:
set -g @resurrect-save-bash-history 'on'
Bash `history` for individual panes will now be saved and restored. Due to
technical limitations, this only works for panes which have no program running in
foreground when saving. `tmux-resurrect` will send history write command
to each such pane. To prevent these commands from being added to history themselves,
add `HISTCONTROL=ignoreboth` to your `.bashrc` (this is set by default in Ubuntu).
### Other goodies ### Other goodies

22
save_command_strategies/gdb.sh Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PANE_PID="$1"
exit_safely_if_empty_ppid() {
if [ -z "$PANE_PID" ]; then
exit 0
fi
}
full_command() {
gdb -batch --eval "attach $PANE_PID" --eval "call write_history(\"/tmp/bash_history-${PANE_PID}.txt\")" --eval 'detach' --eval 'q' >/dev/null 2>&1
\tail -1 "/tmp/bash_history-${PANE_PID}.txt"
}
main() {
exit_safely_if_empty_ppid
full_command
}
main

View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PANE_PID="$1"
exit_safely_if_empty_ppid() {
if [ -z "$PANE_PID" ]; then
exit 0
fi
}
full_command() {
\pgrep -lf -P "$PANE_PID" |
cut -d' ' -f2-
}
main() {
exit_safely_if_empty_ppid
full_command
}
main

31
save_command_strategies/ps.sh Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PANE_PID="$1"
exit_safely_if_empty_ppid() {
if [ -z "$PANE_PID" ]; then
exit 0
fi
}
ps_command_flags() {
case $(uname -s) in
FreeBSD) echo "-ao" ;;
*) echo "-eo" ;;
esac
}
full_command() {
ps "$(ps_command_flags)" "ppid command" |
sed "s/^ *//" |
grep "^${PANE_PID}" |
cut -d' ' -f2-
}
main() {
exit_safely_if_empty_ppid
full_command
}
main

View File

@ -49,6 +49,11 @@ remove_first_char() {
echo "$1" | cut -c2- echo "$1" | cut -c2-
} }
save_bash_history_option_on() {
local option="$(get_tmux_option "$bash_history_option" "off")"
[ "$option" == "on" ]
}
# path helpers # path helpers
resurrect_dir() { resurrect_dir() {
@ -63,3 +68,15 @@ resurrect_file_path() {
last_resurrect_file() { last_resurrect_file() {
echo "$(resurrect_dir)/last" echo "$(resurrect_dir)/last"
} }
resurrect_history_file() {
local pane_id="$1"
echo "$(resurrect_dir)/bash_history-${pane_id}"
}
restore_zoomed_windows() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $6 ~ /Z/ && $9 == 1 { print $2, $3; }' $(last_resurrect_file) |
while IFS=$'\t' read session_name window_number; do
tmux resize-pane -t "${session_name}:${window_number}" -Z
done
}

View File

@ -43,6 +43,9 @@ _process_should_be_restored() {
# Scenario where pane existed before restoration, so we're not # Scenario where pane existed before restoration, so we're not
# restoring the proces either. # restoring the proces either.
return 1 return 1
elif ! pane_exists "$session_name" "$window_number" "$pane_index"; then
# pane number limit exceeded, pane does not exist
return 1
elif _restore_all_processes; then elif _restore_all_processes; then
return 0 return 0
elif _process_on_the_restore_list "$pane_full_command"; then elif _process_on_the_restore_list "$pane_full_command"; then
@ -54,7 +57,7 @@ _process_should_be_restored() {
_restore_all_processes() { _restore_all_processes() {
local restore_processes="$(get_tmux_option "$restore_processes_option" "$restore_processes")" local restore_processes="$(get_tmux_option "$restore_processes_option" "$restore_processes")"
if [ $restore_processes == ":all:" ]; then if [ "$restore_processes" == ":all:" ]; then
return 0 return 0
else else
return 1 return 1
@ -105,7 +108,7 @@ _get_proc_restore_element() {
_restore_list() { _restore_list() {
local user_processes="$(get_tmux_option "$restore_processes_option" "$restore_processes")" local user_processes="$(get_tmux_option "$restore_processes_option" "$restore_processes")"
local default_processes="$(get_tmux_option "$default_proc_list_option" "$default_proc_list")" local default_processes="$(get_tmux_option "$default_proc_list_option" "$default_proc_list")"
if [ -z $user_processes ]; then if [ -z "$user_processes" ]; then
# user didn't define any processes # user didn't define any processes
echo "$default_processes" echo "$default_processes"
else else

14
scripts/restore.exp Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env expect
# start tmux
spawn tmux -S/tmp/foo
# delay with sleep to compensate for tmux starting time
sleep 2
# run restore script directly
send "~/.tmux/plugins/tmux-resurrect/scripts/restore.sh\r"
# long wait until tmux restore is complete
# (things get messed up if expect client isn't attached)
sleep 100

View File

@ -99,8 +99,9 @@ new_pane() {
local window_number="$2" local window_number="$2"
local window_name="$3" local window_name="$3"
local dir="$4" local dir="$4"
tmux split-window -t "${session_name}:${window_number}" -c "$dir" -h tmux split-window -t "${session_name}:${window_number}" -c "$dir"
tmux resize-pane -t "${session_name}:${window_number}" -L "999" # minimize window so more panes can fit
tmux resize-pane -t "${session_name}:${window_number}" -U "999"
} }
restore_pane() { restore_pane() {
@ -140,6 +141,21 @@ restore_all_panes() {
done < $(last_resurrect_file) done < $(last_resurrect_file)
} }
restore_shell_history() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ { print $2, $3, $7, $10; }' $(last_resurrect_file) |
while IFS=$'\t' read session_name window_number pane_index pane_command; do
if ! is_pane_registered_as_existing "$session_name" "$window_number" "$pane_index"; then
if [ "$pane_command" = "bash" ]; then
local pane_id="$session_name:$window_number.$pane_index"
# tmux send-keys has -R option that should reset the terminal.
# However, appending 'clear' to the command seems to work more reliably.
local read_command="history -r '$(resurrect_history_file "$pane_id")'; clear"
tmux send-keys -t "$pane_id" "$read_command" C-m
fi
fi
done
}
restore_all_pane_processes() { restore_all_pane_processes() {
if restore_pane_processes_enabled; then if restore_pane_processes_enabled; then
local pane_full_command local pane_full_command
@ -167,13 +183,6 @@ restore_active_pane_for_each_window() {
done done
} }
restore_zoomed_windows() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^window/ && $5 ~ /Z/ { print $2, $3; }' $(last_resurrect_file) |
while IFS=$'\t' read session_name window_number; do
tmux resize-pane -t "${session_name}:${window_number}" -Z
done
}
restore_active_and_alternate_windows() { restore_active_and_alternate_windows() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^window/ && $5 ~ /[*-]/ { print $2, $4, $3; }' $(last_resurrect_file) | awk 'BEGIN { FS="\t"; OFS="\t" } /^window/ && $5 ~ /[*-]/ { print $2, $4, $3; }' $(last_resurrect_file) |
sort -u | sort -u |
@ -195,6 +204,9 @@ main() {
start_spinner "Restoring..." "Tmux restore complete!" start_spinner "Restoring..." "Tmux restore complete!"
restore_all_panes restore_all_panes
restore_pane_layout_for_each_window >/dev/null 2>&1 restore_pane_layout_for_each_window >/dev/null 2>&1
if save_bash_history_option_on; then
restore_shell_history
fi
restore_all_pane_processes restore_all_pane_processes
# below functions restore exact cursor positions # below functions restore exact cursor positions
restore_active_pane_for_each_window restore_active_pane_for_each_window

View File

@ -65,10 +65,36 @@ dump_panes_raw() {
tmux list-panes -a -F "$(pane_format)" tmux list-panes -a -F "$(pane_format)"
} }
_save_command_strategy_file() {
local save_command_strategy="$(get_tmux_option "$save_command_strategy_option" "$default_save_command_strategy")"
local strategy_file="$CURRENT_DIR/../save_command_strategies/${save_command_strategy}.sh"
local default_strategy_file="$CURRENT_DIR/../save_command_strategies/${default_save_command_strategy}.sh"
if [ -e "$strategy_file" ]; then # strategy file exists?
echo "$strategy_file"
else
echo "$default_strategy_file"
fi
}
pane_full_command() { pane_full_command() {
pane_pid="$1" local pane_pid="$1"
\pgrep -lf -P "$pane_pid" | local strategy_file="$(_save_command_strategy_file)"
cut -d' ' -f2- # execute strategy script to get pane full command
$strategy_file "$pane_pid"
}
save_shell_history() {
local pane_id="$1"
local pane_command="$2"
local full_command="$3"
if [ "$pane_command" = "bash" ] && [ "$full_command" = ":" ]; then
# leading space prevents the command from being saved to history
# (assuming default HISTCONTROL settings)
local write_command=" history -w '$(resurrect_history_file "$pane_id")'"
# C-e C-u is a Bash shortcut sequence to clear whole line. It is necessary to
# delete any pending input so it does not interfere with our history command.
tmux send-keys -t "$pane_id" C-e C-u "$write_command" C-m
fi
} }
# translates pane pid to process command running inside a pane # translates pane pid to process command running inside a pane
@ -77,6 +103,11 @@ dump_panes() {
local d=$'\t' # delimiter local d=$'\t' # delimiter
dump_panes_raw | dump_panes_raw |
while IFS=$'\t' read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_pid; do while IFS=$'\t' read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_pid; do
# check if current pane is part of a maximized window and if the pane is active
if [[ "${window_flags}" == *Z* ]] && [[ ${pane_active} == 1 ]]; then
# unmaximize the pane
tmux resize-pane -Z -t "${session_name}:${window_number}"
fi
full_command="$(pane_full_command $pane_pid)" full_command="$(pane_full_command $pane_pid)"
echo "${line_type}${d}${session_name}${d}${window_number}${d}${window_name}${d}${window_active}${d}${window_flags}${d}${pane_index}${d}${dir}${d}${pane_active}${d}${pane_command}${d}:${full_command}" echo "${line_type}${d}${session_name}${d}${window_number}${d}${window_name}${d}${window_active}${d}${window_flags}${d}${pane_index}${d}${dir}${d}${pane_active}${d}${pane_command}${d}:${full_command}"
done done
@ -90,6 +121,13 @@ dump_state() {
tmux display-message -p "$(state_format)" tmux display-message -p "$(state_format)"
} }
dump_bash_history() {
dump_panes |
while IFS=$'\t' read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command full_command; do
save_shell_history "$session_name:$window_number.$pane_index" "$pane_command" "$full_command"
done
}
save_all() { save_all() {
local resurrect_file_path="$(resurrect_file_path)" local resurrect_file_path="$(resurrect_file_path)"
mkdir -p "$(resurrect_dir)" mkdir -p "$(resurrect_dir)"
@ -97,6 +135,10 @@ save_all() {
dump_windows >> $resurrect_file_path dump_windows >> $resurrect_file_path
dump_state >> $resurrect_file_path dump_state >> $resurrect_file_path
ln -fs "$resurrect_file_path" "$(last_resurrect_file)" ln -fs "$resurrect_file_path" "$(last_resurrect_file)"
if save_bash_history_option_on; then
dump_bash_history
fi
restore_zoomed_windows
} }
main() { main() {

View File

@ -16,7 +16,7 @@ MESSAGE="$1"
END_MESSAGE="$2" END_MESSAGE="$2"
SPIN='-\|/' SPIN='-\|/'
trap "tmux display-message $END_MESSAGE; exit" SIGINT SIGTERM trap "tmux display-message '$END_MESSAGE'; exit" SIGINT SIGTERM
main() { main() {
local i=0 local i=0

View File

@ -7,7 +7,7 @@ restore_option="@resurrect-restore"
# default processes that are restored # default processes that are restored
default_proc_list_option="@resurrect-default-processes" default_proc_list_option="@resurrect-default-processes"
default_proc_list='vi vim emacs man less more tail top htop irssi' default_proc_list='vi vim nvim emacs man less more tail top htop irssi'
# User defined processes that are restored # User defined processes that are restored
# 'false' - nothing is restored # 'false' - nothing is restored
@ -23,3 +23,8 @@ restore_processes=""
restore_process_strategy_option="@resurrect-strategy-" restore_process_strategy_option="@resurrect-strategy-"
inline_strategy_token="->" inline_strategy_token="->"
save_command_strategy_option="@resurrect-save-command-strategy"
default_save_command_strategy="ps"
bash_history_option="@resurrect-save-bash-history"

30
strategies/nvim_session.sh Executable file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env bash
# "nvim session strategy"
#
# Same as vim strategy, see file 'vim_session.sh'
ORIGINAL_COMMAND="$1"
DIRECTORY="$2"
nvim_session_file_exists() {
[ -e "${DIRECTORY}/Session.vim" ]
}
original_command_contains_session_flag() {
[[ "$ORIGINAL_COMMAND" =~ "-S" ]]
}
main() {
if nvim_session_file_exists; then
echo "nvim -S"
elif original_command_contains_session_flag; then
# Session file does not exist, yet the original nvim command contains
# session flag `-S`. This will cause an error, so we're falling back to
# starting plain nvim.
echo "nvim"
else
echo "$ORIGINAL_COMMAND"
fi
}
main

BIN
video/issue_vid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB