module Termclock

Constants

ANTIFLICKER
CLEAR
EMPTY
EPSILON
FILESYSTEM_LABEL
GC_COMPACT_TIME
LANG
LANGS

All languages

NEWLINE
SPACE
TAB
TRANSLATIONS
TRANSLATION_FILES

Load translations

VERSION

Public Class Methods

center_puts(message) click to toggle source
# File lib/termclock/center_puts.rb, line 2
def self.center_puts(message)
        winsize = STDOUT.winsize

        puts "\e[2J\e[H\e[3J\e[#{winsize[0] / 2}H"\
        "#{?\s.*(winsize[1] / 2 - message.bytesize / 2)}#{message}"
end
hex2rgb(hex) click to toggle source
# File lib/termclock/hex2rgb.rb, line 2
def self.hex2rgb(hex)
        colour = hex.dup.to_s
        colour.strip!
        colour.downcase!
        colour[0] = EMPTY if colour[0] == ?#.freeze

        # out of range
        oor = colour.scan(/[^a-f0-9]/)

        unless oor.empty?
                invalids = colour.chars.map { |x|
                        oor.include?(x) ? "\e[1;31m#{x}\e[0m" : x
                }.join

                puts "Hex Colour ##{invalids} is Out of Range"
                raise ArgumentError
        end

        clen = colour.length
        if clen == 3
                colour.chars.map { |x| x.<<(x).to_i(16) }
        elsif clen == 6
                colour.chars.each_slice(2).map { |x| x.join.to_i(16) }
        else
                sli = clen > 6 ? 'too long'.freeze : clen < 3 ? 'too short'.freeze : 'invalid'.freeze
                raise ArgumentError, "Invalid Hex Colour ##{colour} (length #{sli})"
        end
end
start(colour1, colour2, colour3, colour4, textcolour1 = nil, textcolour2 = nil, refresh: 0.1, bold: false, italic: false, print_info: true, print_message: true, print_date: true, time_format: "%H %M %S %2N", date_format: '%a, %d %B %Y', no_logo: false, anti_flicker: false, logo_colour: [Termclock.hex2rgb('ff0'), Termclock.hex2rgb('f55'), Termclock.hex2rgb('55f')] ) click to toggle source
# File lib/termclock/start.rb, line 2
def self.start(colour1, colour2, colour3, colour4,
        textcolour1 = nil, textcolour2 = nil,
        refresh: 0.1,
        bold: false,
        italic: false,
        print_info: true, print_message: true,
        print_date: true,
        time_format: "%H %M %S %2N",
        date_format: '%a, %d %B %Y',
        no_logo: false,
        anti_flicker: false,
        logo_colour: [Termclock.hex2rgb('ff0'), Termclock.hex2rgb('f55'), Termclock.hex2rgb('55f')]
        )

        clear_character = anti_flicker ? ANTIFLICKER : CLEAR

        generate = proc do |start, stop, n = 5|
                r_op = r_val = nil
                ary = []

                if start > stop
                        r_op, r_val = :-, start.-(stop).fdiv(n - 1)
                elsif start < stop
                        r_op, r_val = :+, stop.-(start).fdiv(n - 1)
                end

                _r = r_op ? start.send(r_op, r_val * -1) : start
                n.times {
                        _r = _r.send(r_op, r_val) if r_op
                        ary << _r.clamp(0, 255).to_i
                }

                ary
        end

        gc_compact, gc_compacted = GC.respond_to?(:compact), Time.now.to_i + GC_COMPACT_TIME

        r1, g1, b1 = *colour1
        r2, g2, b2 = *colour2
        r3, g3, b3 = *colour3
        r4, g4, b4 = *colour4

        # colour1 -> colour3
        rs1 = generate.(r1, r3)
        gs1 = generate.(g1, g3)
        bs1 = generate.(b1, b3)

        # colour2 -> colour4
        rs2 = generate.(r2, r4)
        gs2 = generate.(g2, g4)
        bs2 = generate.(b2, b4)

        # All Gradient Colours
        colours = [rs1, gs1, bs1].transpose.zip([rs2, gs2, bs2].transpose)
        colours.unshift(colours[0])
        colours.push(colours[-1])

        # Text colours
        tc1 = textcolour1 ? hex2rgb(textcolour1) : hex2rgb('5555ff')
        tc2 = textcolour2 ? hex2rgb(textcolour2) : hex2rgb('3ce3b5')

        message_counter = -1
        message = ''
        message_final = ''
        message_align = 0
        message_temp = ''

        date, info = '', ''

        clock_emoji = ?\u{1F550}.ord.-(1).chr('utf-8')
        clock_emoji = 12.times.map { clock_emoji.next!.dup }

        braille = %W(\u2821 \u2811 \u2812 \u280A \u280C)

        get_message = proc {
                braille.rotate!
                braille_0 = braille[0]

                case Time.now.hour
                when 5...12
                        _m = translate('Good Morning')
                        "\u{1F304} #{braille_0} #{_m} #{braille_0} \u{1F304}"
                when 12...16
                        _m = translate('Good Afternoon')
                        "\u26C5 #{braille_0} #{_m} #{braille_0} \u26C5"
                when 16...18
                        _m = translate('Good Evening')
                        "\u{1F307} #{braille_0} #{_m} #{braille_0} \u{1F307}"
                when 18...20
                        _m = translate('Good Evening')
                        "\u{1F31F} #{braille_0} #{_m} #{braille_0} \u{1F31F}"
                when 20...24
                        _m = translate('Good Night')
                        "\u{1F303} #{braille_0} #{_m} #{braille_0} \u{1F303}"
                else
                        _m = translate('Good Night')
                        "\u{2728} #{braille_0} #{_m} #{braille_0} \u{2728}"
                end
        }

        version = "Termclock v#{Termclock::VERSION}"

        v_col1 = logo_colour[0]
        v_col2 = logo_colour[1]
        v_col3 = logo_colour[2]

        vl_2 = version.length / 2
        _term_clock_v = version[0...vl_2].gradient(v_col1, v_col2, underline: true, bold: bold, italic: italic) <<
                version[vl_2..-1].gradient(v_col2, v_col3, underline: true, bold: bold, italic: italic)

        term_clock_v = ''

        chop_message = 0
        deviation = 0

        time_seperators = [?:, ?$]
        time_seperator = time_seperators[0]
        point_five_tick = -0.5

        height, width = *STDOUT.winsize
        height2, width2 = *STDOUT.winsize

        print CLEAR

        while true
                monotonic_time_1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
                time_now = Time.now
                height, width = *STDOUT.winsize

                if time_now.to_f > point_five_tick
                        point_five_tick = time_now.to_f + 0.5
                        time_seperators.rotate!
                        clock_emoji.rotate!
                        time_seperator = time_seperators[0]
                end

                unless no_logo
                        term_clock_v = "\e[#{height}H #{clock_emoji[0]} #{_term_clock_v} \e[0m"
                end

                if print_message
                        message_temp = get_message.call
                        message_counter += 1
                        message_length = message.length

                        if (width - message_counter % width < 8)
                                unless chop_message == message_temp.length
                                        chop_message += 1
                                        message_counter -= 1
                                        message_align -= 1

                                        _chopped = message_temp[chop_message..-1]
                                        message.replace(_chopped) if _chopped
                                end
                        else
                                chop_message = 0 unless chop_message == 0
                                message.clear if width - message_counter % width == width
                                message_align = width - message_counter % width + message_length - 4

                                if message_temp != message
                                        if message_length < message_temp.length
                                                message.replace(message_temp[0..message_length])
                                        else
                                                message.replace(message_temp)
                                        end
                                end
                        end

                        message_final = message.rjust(message_align).gradient(
                                tc1, tc2, exclude_spaces: true, bold: bold, italic: italic
                        )
                end

                info = system_info(width, tc1, tc2, bold, italic) if print_info

                if print_date
                        _date = time_now.strftime(date_format)

                        unless LANG == :en
                                _date = _date.split(/(\W)/).map { |x|
                                        translate(
                                                x,
                                                breakword: !x[/[^0-9]/]
                                        )
                                }.join
                        end

                        date = _date.center(width)
                                .gradient(tc1, tc2, bold: bold, italic: italic, exclude_spaces: true)
                end

                time = time_now.strftime(time_format).split.join(time_seperator)
                art = Termclock::ParseCharacters.display(time).lines

                art_aligned = art.each_with_index do |x, i|
                        chomped = x.chomp(EMPTY).+(NEWLINE)
                        gr = chomped.gradient(*colours[i], bold: bold, italic: italic)

                        w = width./(2.0).-(chomped.length / 2.0) + 2
                        w = 0 if w < 0

                        x.replace(SPACE.*(w) + gr)
                end.join

                vertical_gap = "\e[#{height./(2.0).-(art.length / 2.0).to_i + 1}H"
                final_output = "#{info}#{vertical_gap}#{art_aligned}\n#{date}\n\n\e[K#{message_final}#{term_clock_v}"

                if anti_flicker && (height != height2 || width != width2)
                        height2, width2 = *STDOUT.winsize
                        print CLEAR
                end

                print "#{clear_character}#{final_output}"

                if gc_compact && time_now.to_i > gc_compacted
                        GC.compact
                        gc_compacted = time_now.to_i + GC_COMPACT_TIME
                end

                monotonic_time_2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
                time_diff = monotonic_time_2 - monotonic_time_1
                sleep_time = refresh.-(time_diff + deviation)
                sleep_time = 0 if sleep_time < 0

                sleep(sleep_time)
                deviation = Process.clock_gettime(Process::CLOCK_MONOTONIC).-(monotonic_time_2 + sleep_time + EPSILON)
        end
end
system_info(width, tc1, tc2, bold, italic) click to toggle source
# File lib/termclock/system_info.rb, line 18
def system_info(width, tc1, tc2, bold, italic)
        unless @@cpu_usage_t.alive?
                @@cpu_usage_t = Thread.new {
                        _cpu_usage = LS::CPU.usage(0.25)
                        @@cpu_usage = _cpu_usage ? "%0.2f".freeze % _cpu_usage : nil
                }
        end

        unless @@current_net_usage_t.alive?
                @@current_net_usage_t = Thread.new do
                        _m = LS::Net.current_usage(0.25)

                        _dl = _m[:received]
                        _ul = _m[:transmitted]

                        @@current_net_usage = if _dl && _ul
                                _tr = translate('Curr. DL/UL')
                                dl = LS::PrettifyBytes.convert_short_decimal(_dl)
                                ul = LS::PrettifyBytes.convert_short_decimal(_ul)

                                "\u{1F4CA} #{_tr}: #{t!("%-9s" % dl)} | #{t!("%9s" % ul)}"
                        else
                                EMPTY
                        end
                end
        end

        cpu = if @@cpu_usage
                _tr = translate('CPU')
                 "\u{1F9E0} #{_tr}: #{t!("%6s" % @@cpu_usage)}% (#{t!(LS::CPU.count_online)}/#{t!(LS::CPU.count)})"
        else
                EMPTY
        end

        battery = if LS::Battery.present?
                stat = LS::Battery.stat
                charge = stat[:charge].to_i

                emoji, plug = "\u{1F50B}".freeze, EMPTY

                if LS::Battery.charging?
                        emoji, plug = "\u{1F4A1}".freeze, "\u{1F50C} ".freeze
                end

                lives = "\u2665 ".freeze.*(charge.fdiv(20).ceil).chop

                _tr = translate('Battery')
                "#{emoji} #{_tr}: #{t!(charge)}% #{lives} (#{plug}#{translate(stat[:status])})"
        else
                EMPTY
        end

        _tr = translate('User')
        user = "\u{1F481} #{_tr}: #{LS::User.get_current_user.capitalize}"

        _tr = translate('Hostname')
        hostname = "\u{1F4BB} #{_tr}: #{LS::OS.hostname}"

        _tr = translate('IP Addr')
        _m = LS::Net.total_bytes
        ip = "\u{1F30F} #{_tr}: #{translate(LS::Net.ipv4_private, b: true)}"

        _received = _m[:received]
        _transmitted = _m[:transmitted]

        _tr = translate('Totl. DL/UL')
        tot_received = _received ? "\u{1F4C8} #{_tr}: #{t!('%-9s'.freeze % LS::PrettifyBytes.convert_short_decimal(_m[:received]))}" : nil
        tot_transmitted = _transmitted ? " | #{t!('%9s'.freeze % LS::PrettifyBytes.convert_short_decimal(_transmitted))}" : nil

        net_usage = if tot_received && tot_transmitted
                tot_received + tot_transmitted
        else
                EMPTY
        end

        _m = LS::Memory.stat
        _m.default = 0

        _tr = translate('Mem')
        memory = "\u{1F3B0} #{_tr}: #{t!(LS::PrettifyBytes.convert_short_decimal(_m[:used] * 1000))}"\
        " / #{t!(LS::PrettifyBytes.convert_short_decimal(_m[:total] * 1000))}"\
        " (#{t!("%.2f" % _m[:percent_used])}%)"

        _m = LS::Swap.stat
        _m.default = 0

        _tr = translate('Swap')
        swap = "\u{1F300} #{_tr}: #{t!(LS::PrettifyBytes.convert_short_decimal(_m[:used] * 1000))}"\
        " / #{t!(LS::PrettifyBytes.convert_short_decimal(_m[:total] * 1000))}"\
        " (#{t!("%.2f" % _m[:percent_used])}%)"

        _m = LS::Filesystem.stat(FILESYSTEM)
        _m.default = 0

        _tr = translate('FS')
        fs = "\u{1F4BD} #{_tr} (#{FILESYSTEM_LABEL}): #{t!(LS::PrettifyBytes.convert_short_decimal(_m[:used]))}"\
        " / #{t!(LS::PrettifyBytes.convert_short_decimal(_m[:total]))}"\
        " (#{t!("%.2f" % _m[:used].*(100).fdiv(_m[:total]).round(2))}%)"

        pt = LS::Process.types.values

        process = if pt.length > 0
                _tr = translate('Process')

                "\u{1F3ED} #{_tr}: T:#{t!("%4s" % pt.length)}|"\
                "R:#{"%3s" % t!(pt.count(:running))}|"\
                "S:#{"%3s" % t!(pt.count(:sleeping))}|"\
                "I:#{"%3s" % t!(pt.count(:idle))}"
        else
                EMPTY
        end

        @@os_v ||= unless LS::OS.version.empty?
                " (#{LS::OS.version})"
        else
                EMPTY
        end

        _tr = translate('Distrib')
        @@os ||= "\u{1F427} #{_tr}: #{LS::OS.distribution} #{LS::OS.machine}#{@@os_v}"

        _temp_uptime = LS::OS.uptime

        _uptime = unless _temp_uptime.empty?
                _temp_uptime
        else
                _u = LS::OS.uptime_i
                {
                        hour: _u / 3600,
                        minute: _u % 3600 / 60,
                        second: _u % 3600 % 60,
                        jiffy: 0
                }
        end

        _second = _uptime[:second]
        _second_i = _second.to_i

        hour = "%02d" % _uptime[:hour]
        minute = "%02d" % _uptime[:minute]
        second = "%02d" % _uptime[:second]
        jiffy = "%02d" % _uptime[:jiffy]

        _tr = translate('Uptime')
        uptime = "\u{1F3A1} #{_tr}: #{t! hour}:#{t! minute}:#{t! second}:#{t! jiffy} (#{t! LS::OS.uptime_i}s)"

        _tr = translate('LoadAvg')
        _loadavg = LS::Sysinfo.loads.map! { |x| "%.2f" % x }
        loadavg = "\u{1F525} #{_tr}: 1m #{translate(_loadavg[0], b: true)}|5m #{translate(_loadavg[1], b: true)}|15m #{translate(_loadavg[2], b: true)}"

        all_info = []
        max_l = 0
        i = -1

        [
                user, hostname,
                @@os, battery,
                cpu, ip,
                memory, @@current_net_usage,
                swap, net_usage,
                fs, process,
                uptime, loadavg
        ].each { |x|
                unless x.empty?
                        all_info << x
                        i += 1

                        if i.odd?
                                _x_len = x.length
                                max_l = _x_len if max_l < _x_len
                        end
                end
        }

        max_l += 4

        all_info.each_slice(2).map { |x, y|
                _diff = width.-(x.length + max_l)
                _diff = 0 if _diff < 1
                y_to_s = y.to_s

                padding = "#{SPACE * _diff}"
                str = SPACE + x + padding + y_to_s

                grads = SPACE + x.gradient(tc1, tc2, bold: bold, italic: italic) +
                        padding +
                        y_to_s.gradient(tc1, tc2, bold: bold, italic: italic)

                len = str.grapheme_clusters.map { |x|
                        _x = x.bytesize./(2)
                        _x == 0 ? 1 : _x
                }.sum

                w = width - 2

                len < w ? grads.+(SPACE.*(w - len)) : grads
        }.join(NEWLINE)
end
t!(keyword, breakword: true, b: true) click to toggle source
# File lib/termclock/translate.rb, line 57
def self.t!(keyword, breakword: true, b: true)
        return keyword if LANG == :en
        translate(keyword.to_s, breakword: true)
end
translate(keyword, breakword: nil, b: nil) click to toggle source
# File lib/termclock/translate.rb, line 2
def self.translate(keyword, breakword: nil, b: nil)
        return keyword if LANG == :en
        characters = keyword.grapheme_clusters

        b = breakword if breakword && !b
        breakword = b if b && !breakword

        upcased = characters.all? { |x| x.ord.between?(65, 90) }
        downcased = upcased ? false : characters.all? { |x| x.ord.between?(97, 122) }
        capitalized = if (upcased || downcased)
                false
        else
                characters[0].ord.between?(65, 90) &&
                        characters.drop(1).all? { |x| x.ord.between?(97, 122) }
        end

        camelized = if (upcased || downcased || capitalized)
                false
        else
                keyword.split(?\s.freeze).all? { |x|
                        x[0].ord.between?(65, 90) && x.chars.drop(1).all? { |y| y.ord.between?(97, 122) }
                }
        end

        if breakword
                return characters.map { |x|
                        tr = TRANSLATIONS[x]

                        if !tr
                                tr = TRANSLATIONS[x.downcase]
                        end

                        tr.upcase! if tr && upcased
                        tr.downcase! if tr && downcased
                        tr.capitalize! if tr && capitalized
                        tr.camelize! if tr && camelized

                        tr ? tr : x
                }.join
        end

        tr = TRANSLATIONS[keyword]

        if !tr
                tr = TRANSLATIONS[keyword.downcase]

                tr.upcase! if tr && upcased
                tr.downcase! if tr && downcased
                tr.capitalize! if tr && capitalized
                tr.camelize! if tr && camelized
        end

        tr ? tr : keyword
end