-
-
Save pmarreck/80ad97decadf42bfda26707b92d34c24 to your computer and use it in GitHub Desktop.
| local ffi = require("ffi") | |
| local bit = require("bit") | |
| ffi.cdef([[ struct winsize { | |
| unsigned short ws_row; | |
| unsigned short ws_col; | |
| unsigned short ws_xpixel; | |
| unsigned short ws_ypixel; | |
| }; | |
| int ioctl(int fd, unsigned long request, ...); | |
| typedef void (*sighandler_t)(int); | |
| int signal(int signum, sighandler_t handler); | |
| int usleep(unsigned int usec); | |
| unsigned int sleep(unsigned int seconds); | |
| ]]) | |
| local TIOCGWINSZ = 0x5413 | |
| local TIOCGWINSZ_BSD = 0x40087468 | |
| local SIGWINCH = 28 | |
| local SIGINT = 2 | |
| local STDOUT_FILENO = 1 | |
| local STDERR_FILENO = 2 | |
| local terminal_resized = false | |
| local interrupted = false | |
| local Timer | |
| do | |
| local _class_0 | |
| local _base_0 = { | |
| start = function(self) | |
| self.is_running = true | |
| self.start_time = os.time() | |
| end, | |
| stop = function(self) | |
| self.is_running = false | |
| end, | |
| advance_to = function(self, elapsed_seconds) | |
| self.elapsed_seconds = math.min(elapsed_seconds, self.total_seconds) | |
| self.remaining_seconds = math.max(0, self.total_seconds - self.elapsed_seconds) | |
| self.start_time = os.time() - self.elapsed_seconds | |
| end, | |
| update = function(self) | |
| if self.is_running then | |
| local current_time = os.time() | |
| self.elapsed_seconds = current_time - self.start_time | |
| self.remaining_seconds = math.max(0, self.total_seconds - self.elapsed_seconds) | |
| if self.remaining_seconds == 0 then | |
| self.is_running = false | |
| return true | |
| end | |
| end | |
| return false | |
| end, | |
| get_progress = function(self) | |
| if self.total_seconds == 0 then | |
| return 1.0 | |
| end | |
| return self.elapsed_seconds / self.total_seconds | |
| end, | |
| is_finished = function(self) | |
| return self.remaining_seconds <= 0 | |
| end | |
| } | |
| if _base_0.__index == nil then | |
| _base_0.__index = _base_0 | |
| end | |
| _class_0 = setmetatable({ | |
| __init = function(self, seconds) | |
| self.total_seconds = seconds | |
| self.remaining_seconds = seconds | |
| self.elapsed_seconds = 0 | |
| self.start_time = os.time() | |
| self.is_running = false | |
| end, | |
| __base = _base_0, | |
| __name = "Timer" | |
| }, { | |
| __index = _base_0, | |
| __call = function(cls, ...) | |
| local _self_0 = setmetatable({ }, _base_0) | |
| cls.__init(_self_0, ...) | |
| return _self_0 | |
| end | |
| }) | |
| _base_0.__class = _class_0 | |
| Timer = _class_0 | |
| end | |
| local Terminal | |
| do | |
| local _class_0 | |
| local _base_0 = { | |
| try_ioctl_detection = function(self) | |
| local winsize = ffi.new("struct winsize") | |
| local ioctls = { | |
| TIOCGWINSZ, | |
| TIOCGWINSZ_BSD | |
| } | |
| local fds = { | |
| STDOUT_FILENO, | |
| STDERR_FILENO, | |
| 0 | |
| } | |
| for _index_0 = 1, #ioctls do | |
| local ioctl_val = ioctls[_index_0] | |
| for _index_1 = 1, #fds do | |
| local fd = fds[_index_1] | |
| local result = ffi.C.ioctl(fd, ioctl_val, winsize) | |
| if result == 0 and winsize.ws_row > 0 and winsize.ws_col > 0 then | |
| return winsize.ws_row, winsize.ws_col, "ioctl(" .. tostring(fd) .. ", " .. tostring(string.format('0x%x', ioctl_val)) .. ")" | |
| end | |
| end | |
| end | |
| return nil, nil, "ioctl failed" | |
| end, | |
| try_tput_detection = function(self) | |
| local success, result = pcall(function() | |
| local rows_cmd = io.popen("tput lines 2>/dev/null") | |
| if rows_cmd then | |
| local rows_str = rows_cmd:read("*l") | |
| rows_cmd:close() | |
| local cols_cmd = io.popen("tput cols 2>/dev/null") | |
| if cols_cmd then | |
| local cols_str = cols_cmd:read("*l") | |
| cols_cmd:close() | |
| local rows = tonumber(rows_str) | |
| local cols = tonumber(cols_str) | |
| if rows and cols and rows > 0 and cols > 0 then | |
| return rows, cols, "tput command" | |
| end | |
| end | |
| end | |
| end) | |
| if success and result then | |
| return result | |
| end | |
| return nil, nil, "tput command failed" | |
| end, | |
| get_dimensions = function(self) | |
| if self.override_rows and self.override_cols then | |
| return self.override_rows, self.override_cols | |
| end | |
| local rows, cols, method = self:try_ioctl_detection() | |
| if rows and cols then | |
| self.last_rows = rows | |
| self.last_cols = cols | |
| self.detection_method = method | |
| return rows, cols | |
| end | |
| rows, cols, method = self:try_tput_detection() | |
| if rows and cols then | |
| self.last_rows = rows | |
| self.last_cols = cols | |
| self.detection_method = method | |
| return rows, cols | |
| end | |
| rows = tonumber(os.getenv("LINES")) | |
| cols = tonumber(os.getenv("COLUMNS")) | |
| if rows and cols and rows > 0 and cols > 0 then | |
| self.last_rows = rows | |
| self.last_cols = cols | |
| self.detection_method = "environment variables" | |
| return rows, cols | |
| end | |
| rows = self.last_rows or 24 | |
| cols = self.last_cols or 80 | |
| if rows <= 0 then | |
| rows = 24 | |
| end | |
| if cols <= 0 then | |
| cols = 80 | |
| end | |
| self.last_rows = rows | |
| self.last_cols = cols | |
| self.detection_method = "defaults" | |
| return rows, cols | |
| end, | |
| update_dimensions = function(self) | |
| self.rows, self.cols = self:get_dimensions() | |
| end, | |
| clear_screen = function(self) | |
| return io.write("\027[2J\027[H") | |
| end, | |
| move_cursor = function(self, row, col) | |
| return io.write(string.format("\027[%d;%dH", row, col)) | |
| end, | |
| hide_cursor = function(self) | |
| return io.write("\027[?25l") | |
| end, | |
| show_cursor = function(self) | |
| return io.write("\027[?25h") | |
| end | |
| } | |
| if _base_0.__index == nil then | |
| _base_0.__index = _base_0 | |
| end | |
| _class_0 = setmetatable({ | |
| __init = function(self, override_rows, override_cols) | |
| if override_rows == nil then | |
| override_rows = nil | |
| end | |
| if override_cols == nil then | |
| override_cols = nil | |
| end | |
| self.last_rows = 0 | |
| self.last_cols = 0 | |
| self.override_rows = override_rows | |
| self.override_cols = override_cols | |
| return self:update_dimensions() | |
| end, | |
| __base = _base_0, | |
| __name = "Terminal" | |
| }, { | |
| __index = _base_0, | |
| __call = function(cls, ...) | |
| local _self_0 = setmetatable({ }, _base_0) | |
| cls.__init(_self_0, ...) | |
| return _self_0 | |
| end | |
| }) | |
| _base_0.__class = _class_0 | |
| Terminal = _class_0 | |
| end | |
| local VisualDisplay | |
| do | |
| local _class_0 | |
| local _base_0 = { | |
| update_dimensions = function(self) | |
| self.terminal:update_dimensions() | |
| self.display_rows = self.terminal.rows - 2 | |
| self.rows = self.display_rows | |
| self.cols = self.terminal.cols | |
| self.total_blocks = self.display_rows * self.cols | |
| end, | |
| blocks_for_progress = function(self, progress) | |
| return math.floor(self.total_blocks * math.min(1.0, progress)) | |
| end, | |
| render = function(self, progress, remaining_time, total_time) | |
| local blocks_filled = self:blocks_for_progress(progress) | |
| self.terminal:clear_screen() | |
| self.terminal:hide_cursor() | |
| local block_count = 0 | |
| for row = 1, self.display_rows do | |
| self.terminal:move_cursor(row, 1) | |
| for col = 1, self.cols do | |
| block_count = block_count + 1 | |
| if block_count <= blocks_filled then | |
| io.write(self.fill_char) | |
| else | |
| io.write(self.empty_char) | |
| end | |
| end | |
| end | |
| self.terminal:move_cursor(self.display_rows + 1, 1) | |
| local progress_percent = math.floor(progress * 100) | |
| local time_str = self:format_time(remaining_time) | |
| local total_str = self:format_time(total_time) | |
| local info_line = string.format("Progress: %d%% | Time: %s / %s | Press Ctrl+C to exit", progress_percent, time_str, total_str) | |
| io.write(info_line) | |
| return io.flush() | |
| end, | |
| format_time = function(self, seconds) | |
| local hours = math.floor(seconds / 3600) | |
| local minutes = math.floor((seconds % 3600) / 60) | |
| local secs = seconds % 60 | |
| if hours > 0 then | |
| return string.format("%d:%02d:%02d", hours, minutes, secs) | |
| else | |
| return string.format("%d:%02d", minutes, secs) | |
| end | |
| end | |
| } | |
| if _base_0.__index == nil then | |
| _base_0.__index = _base_0 | |
| end | |
| _class_0 = setmetatable({ | |
| __init = function(self, terminal) | |
| self.terminal = terminal | |
| self.fill_char = "█" | |
| self.empty_char = "░" | |
| return self:update_dimensions() | |
| end, | |
| __base = _base_0, | |
| __name = "VisualDisplay" | |
| }, { | |
| __index = _base_0, | |
| __call = function(cls, ...) | |
| local _self_0 = setmetatable({ }, _base_0) | |
| cls.__init(_self_0, ...) | |
| return _self_0 | |
| end | |
| }) | |
| _base_0.__class = _class_0 | |
| VisualDisplay = _class_0 | |
| end | |
| local SignalHandler | |
| do | |
| local _class_0 | |
| local _base_0 = { | |
| setup_signal_handlers = function(self) | |
| local winch_handler = ffi.cast("sighandler_t", function(signum) | |
| terminal_resized = true | |
| end) | |
| local int_handler = ffi.cast("sighandler_t", function(signum) | |
| interrupted = true | |
| end) | |
| local winch_result = ffi.C.signal(SIGWINCH, winch_handler) | |
| local int_result = ffi.C.signal(SIGINT, int_handler) | |
| return (winch_result ~= ffi.cast("sighandler_t", -1)) and (int_result ~= ffi.cast("sighandler_t", -1)) | |
| end | |
| } | |
| if _base_0.__index == nil then | |
| _base_0.__index = _base_0 | |
| end | |
| _class_0 = setmetatable({ | |
| __init = function(self) | |
| return self:setup_signal_handlers() | |
| end, | |
| __base = _base_0, | |
| __name = "SignalHandler" | |
| }, { | |
| __index = _base_0, | |
| __call = function(cls, ...) | |
| local _self_0 = setmetatable({ }, _base_0) | |
| cls.__init(_self_0, ...) | |
| return _self_0 | |
| end | |
| }) | |
| _base_0.__class = _class_0 | |
| SignalHandler = _class_0 | |
| end | |
| local parse_time_preset | |
| parse_time_preset = function(preset) | |
| preset = preset:lower():gsub("%s+", "") | |
| if preset:match("^%d+s$") then | |
| local seconds = tonumber(preset:match("^(%d+)s$")) | |
| return seconds | |
| elseif preset:match("^%d+m$") then | |
| local minutes = tonumber(preset:match("^(%d+)m$")) | |
| return minutes * 60 | |
| elseif preset:match("^%d+h$") then | |
| local hours = tonumber(preset:match("^(%d+)h$")) | |
| return hours * 3600 | |
| elseif preset:match("^%d+$") then | |
| return tonumber(preset) * 60 | |
| else | |
| return error("Invalid time format: " .. tostring(preset) .. ". Use formats like '30s', '5m', '1h', etc.") | |
| end | |
| end | |
| local VisualTimer | |
| do | |
| local _class_0 | |
| local _base_0 = { | |
| play_completion_beeps = function(self) | |
| if not self.options.silence then | |
| for i = 1, 3 do | |
| os.execute("tput bel 2>/dev/null") | |
| if i < 3 then | |
| ffi.C.usleep(1000000) | |
| end | |
| end | |
| end | |
| end, | |
| setup_signal_handling = function(self) end, | |
| run = function(self, time_preset) | |
| local seconds = parse_time_preset(time_preset) | |
| self.timer = Timer(seconds) | |
| if not (self.options.single_frame or self.options.advance_time) then | |
| print("Visual Timer starting for " .. tostring(self.display:format_time(seconds))) | |
| print("Terminal: " .. tostring(self.terminal.rows) .. "x" .. tostring(self.terminal.cols) .. " (" .. tostring(self.display.display_rows) .. "x" .. tostring(self.display.cols) .. " display area)") | |
| print("Detection method: " .. tostring(self.terminal.detection_method or 'unknown')) | |
| print("Total blocks: " .. tostring(self.display.total_blocks)) | |
| print("Starting timer... (Press Ctrl+C to exit)") | |
| end | |
| self.timer:start() | |
| if self.options.advance_time then | |
| self.timer:advance_to(self.options.advance_time) | |
| end | |
| if not self.options.single_frame then | |
| for i = 1, self.terminal.rows do | |
| print("") | |
| end | |
| end | |
| if self.options.single_frame then | |
| local progress = self.timer:get_progress() | |
| self.display:render(progress, self.timer.remaining_seconds, self.timer.total_seconds) | |
| self.terminal:show_cursor() | |
| return | |
| end | |
| while not self.timer:is_finished() do | |
| if interrupted then | |
| self.terminal:show_cursor() | |
| self.terminal:move_cursor(self.terminal.rows, 1) | |
| print("\n\nTimer interrupted. Goodbye!") | |
| return | |
| end | |
| local completed = self.timer:update() | |
| if terminal_resized then | |
| terminal_resized = false | |
| self.display:update_dimensions() | |
| end | |
| local progress = self.timer:get_progress() | |
| self.display:render(progress, self.timer.remaining_seconds, self.timer.total_seconds) | |
| if completed then | |
| break | |
| end | |
| ffi.C.usleep(100000) | |
| end | |
| self.display:render(1.0, 0, self.timer.total_seconds) | |
| self:play_completion_beeps() | |
| self.terminal:move_cursor(self.terminal.rows, 1) | |
| print("\n\n🎉 Timer completed! Press Enter to exit.") | |
| self.terminal:show_cursor() | |
| return io.read() | |
| end | |
| } | |
| if _base_0.__index == nil then | |
| _base_0.__index = _base_0 | |
| end | |
| _class_0 = setmetatable({ | |
| __init = function(self, options) | |
| if options == nil then | |
| options = { } | |
| end | |
| self.options = options or { } | |
| self.terminal = Terminal(self.options.rows, self.options.cols) | |
| self.display = VisualDisplay(self.terminal) | |
| self.signal_handler = SignalHandler() | |
| self.timer = nil | |
| return self:setup_signal_handling() | |
| end, | |
| __base = _base_0, | |
| __name = "VisualTimer" | |
| }, { | |
| __index = _base_0, | |
| __call = function(cls, ...) | |
| local _self_0 = setmetatable({ }, _base_0) | |
| cls.__init(_self_0, ...) | |
| return _self_0 | |
| end | |
| }) | |
| _base_0.__class = _class_0 | |
| VisualTimer = _class_0 | |
| end | |
| local parse_args | |
| parse_args = function(args) | |
| local options = { } | |
| local time_preset = nil | |
| local i = 1 | |
| while i <= #args do | |
| local arg_val = args[i] | |
| if arg_val == "--dimensions" or arg_val == "-d" then | |
| if i + 1 <= #args then | |
| local dims = args[i + 1]:match("^(%d+)x(%d+)$") | |
| if dims then | |
| local rows, cols = args[i + 1]:match("^(%d+)x(%d+)$") | |
| options.rows = tonumber(rows) | |
| options.cols = tonumber(cols) | |
| i = i + 2 | |
| else | |
| error("Invalid dimensions format. Use: 24x80") | |
| end | |
| else | |
| error("--dimensions requires a value (e.g., 24x80)") | |
| end | |
| elseif arg_val == "--advance-time" or arg_val == "-a" then | |
| if i + 1 <= #args then | |
| options.advance_time = tonumber(args[i + 1]) | |
| i = i + 2 | |
| else | |
| error("--advance-time requires a value (seconds)") | |
| end | |
| elseif arg_val == "--single-frame" then | |
| options.single_frame = true | |
| i = i + 1 | |
| elseif arg_val == "--silence" or arg_val == "-s" then | |
| options.silence = true | |
| i = i + 1 | |
| elseif arg_val == "--help" or arg_val == "-h" then | |
| print("Visual Terminal Timer") | |
| print("") | |
| print("A visual countdown timer that fills your terminal with blocks as time progresses.") | |
| print("Perfect for pomodoro sessions, cooking timers, or any timed activity.") | |
| print("The display automatically adapts to your terminal size and redraws properly") | |
| print("when the window is resized. Plays 3 beeps when the timer completes.") | |
| print("") | |
| print("Usage: vtt <time> [options]") | |
| print("") | |
| print("Time formats:") | |
| print(" 30s # 30 seconds") | |
| print(" 5m # 5 minutes") | |
| print(" 55m # 55 minutes") | |
| print(" 1h # 1 hour") | |
| print(" 2h # 2 hours") | |
| print("") | |
| print("Options:") | |
| print(" --silence, -s Disable completion beeps") | |
| print("") | |
| print("Testing options:") | |
| print(" --dimensions, -d ROWSxCOLS Override terminal dimensions (e.g., 24x80)") | |
| print(" --advance-time, -a SECONDS Advance timer to specific time position") | |
| print(" --single-frame Output single frame and exit (no loop)") | |
| print(" --help, -h Show this help message") | |
| print("") | |
| print("Examples:") | |
| print(" vtt 5m # 5-minute timer with beeps") | |
| print(" vtt 30m --silence # 30-minute silent timer") | |
| print(" vtt 25m # Pomodoro timer") | |
| print("") | |
| print("The timer shows:") | |
| print(" • Filled blocks (█) for elapsed time") | |
| print(" • Empty blocks (░) for remaining time") | |
| print(" • Progress percentage and remaining time") | |
| print(" • Terminal dimensions detected automatically") | |
| os.exit(0) | |
| elseif not time_preset then | |
| if arg_val:match("^%-") then | |
| error("Unknown option: " .. tostring(arg_val) .. ". Use --help for available options.") | |
| else | |
| time_preset = arg_val | |
| i = i + 1 | |
| end | |
| else | |
| error("Unknown argument: " .. tostring(arg_val)) | |
| end | |
| end | |
| return time_preset, options | |
| end | |
| local main | |
| main = function() | |
| if #arg == 0 then | |
| print("Visual Terminal Timer") | |
| print("Usage: vtt <time> [options]") | |
| print("Use --help for more information") | |
| os.exit(2) | |
| end | |
| local success, err = pcall(function() | |
| local time_preset, options = parse_args(arg) | |
| if not time_preset then | |
| error("Time preset is required") | |
| end | |
| local app = VisualTimer(options) | |
| return app:run(time_preset) | |
| end) | |
| if not success then | |
| print("Error: " .. tostring(err)) | |
| if err:match("Unknown option:") or err:match("Invalid time format:") or err:match("Time preset is required") or err:match("requires a value") then | |
| return os.exit(2) | |
| else | |
| return os.exit(1) | |
| end | |
| end | |
| end | |
| if arg and arg[0] and arg[0]:match("vtt") then | |
| return main() | |
| end |
Definitely and totally cured of herpes virus by Doctor Razor's Quick cure for Herpes. My Sincere Gratitude to Dr Razor, I was infected with HERPES SIMPLEX VIRUS 2 November 13 2018, I went to many hospitals for cure but there was no solution, I was confused because the medical drugs has become my daily food, but i decided to sort after Natural Herbs. One day I sat beside the pool, Browsing and thinking where I could get a solution to end this predicament. I saw a blog on how Herbalist Razor cured people with his herbal medicine, I did not believe it but I just decided to give him a try, Visit Doctor's Razor WebPage and being satisfied. I contacted him and he prepared the herbs for me which I took, and he instructed me to go for a check up, after the test I was confirmed with herpes negative, I am so happy. . This testimony serves as a token of my gratitude.
He renders Treatment for :
HIV/AIDS
COLD SORE,
SHINGLES,
CANCER(Cannabis Oil),
HPV,
ASTHMA,
IMPOTENCE,
BARENESS/INFERTILITY P*NIS ENLARGEMENTSPELL TO BRING EX LOVER BACK.
You Could Also Reach out to him directly, kindly contact him now with his Email:drogdentestimony@gmail.com or WhatsApp/Telephone +234 915 412 9528 tiktok.com/@ex_back_spell_7
Definitely and totally cured of herpes virus by Doctor Razor's Quick cure for Herpes. My Sincere Gratitude to Dr Razor, I was infected with HERPES SIMPLEX VIRUS 2 November 13 2018, I went to many hospitals for cure but there was no solution, I was confused because the medical drugs has become my daily food, but i decided to sort after Natural Herbs. One day I sat beside the pool, Browsing and thinking where I could get a solution to end this predicament. I saw a blog on how Herbalist Razor cured people with his herbal medicine, I did not believe it but I just decided to give him a try, Visit Doctor's Razor WebPage and being satisfied. I contacted him and he prepared the herbs for me which I took, and he instructed me to go for a check up, after the test I was confirmed with herpes negative, I am so happy. . This testimony serves as a token of my gratitude.
He renders Treatment for :
HIV/AIDS
COLD SORE,
SHINGLES,
CANCER(Cannabis Oil),
HPV,
ASTHMA,
IMPOTENCE,
BARENESS/INFERTILITY P*NIS ENLARGEMENTSPELL TO BRING EX LOVER BACK.
You Could Also Reach out to him directly, kindly contact him now with his Email:drogdentestimony@gmail.com or WhatsApp/Telephone +234 915 412 9528 tiktok.com/@ex_back_spell_7
If the syntax seems a little off, it's because it was generated to Lua via YueScript. I should have a separate repo up for that soon.