Skip to content

Instantly share code, notes, and snippets.

@orange-kao
Created July 27, 2018 13:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save orange-kao/2f40455369e9eda18b6037098c4c72bf to your computer and use it in GitHub Desktop.
Save orange-kao/2f40455369e9eda18b6037098c4c72bf to your computer and use it in GitHub Desktop.
ThinkPad X280 brightness control script
#!/usr/bin/env ruby
# encoding: utf-8
require "io/console"
require "rb-inotify"
CURSOR_ON = "\e[?25h"
CURSOR_OFF = "\e[?25l"
BRIGHTNESS_FILE = "/sys/class/backlight/intel_backlight/brightness"
MAX_BRIGHTNESS_FILE = "/sys/class/backlight/intel_backlight/max_brightness"
class StdoutSingleLineUpdate
def initialize
@length = 0
@mutex = Mutex.new
end
private
def self.get_str_console_length(str)
length = 0
str.each_char{|char|
if char.bytesize == 3
length += 2
else
length += 1
end
}
str.scan(/\x1B\[[0-9;]+m/).each{|colour_msg|
length -= colour_msg.size
}
return length
end
public
def update_message(msg)
@mutex.synchronize{
padding_length = @length - self.class.get_str_console_length(msg)
padding = ""
if padding_length > 0
padding = " " * padding_length
end
new_msg = msg + padding
$stdout.write"\r#{new_msg}"
@length = self.class.get_str_console_length(new_msg)
}
return
end
end
class Brightness
attr_reader :value
attr_reader :max_value
def initialize(filename, max_filename, &callback)
@filename = filename
File.open(max_filename, "rb"){|file|
@max_value = file.read().to_i
}
read_sysfs()
@callback = nil
if callback != nil
@callback = callback
@thread = Thread.new{
run_inotify()
}
end
end
def close
if @callback != nil
@notifier.stop
@notifier.close
@thread.terminate
@thread.join
end
end
def run_inotify
@notifier = INotify::Notifier.new
@notifier.watch(File.dirname(BRIGHTNESS_FILE), :modify){|event|
if event.name == File.basename(BRIGHTNESS_FILE)
read_sysfs()
@callback.call(self)
end
}
@notifier.run
end
def read_sysfs
File.open(@filename, "rb"){|file|
@value = file.read().to_i
}
end
def write_sysfs
File.open(@filename, "wb"){|file|
file.write(@value.to_s)
}
end
def read
return @value
end
def write(value)
if (Integer === value) != true
raise ArgumentError, "Invalid value"
end
if value < 0
return
elsif value > @max_value
return
end
@value = value
write_sysfs()
end
def increase(brightness_to_change)
write(@value + brightness_to_change)
end
end
class ReadKey
@@key_map = {}
(1..26).each{|x|
@@key_map[x.chr] = "ctrl_#{(96+x).chr}".to_sym
}
@@key_map["\e[A"] = :up
@@key_map["\e[B"] = :down
@@key_map["\e[D"] = :left
@@key_map["\e[C"] = :right
@@key_map["\x7f"] = :backspace
@@key_map["\e"] = :esc
@@key_map["\t"] = :tab
@@key_map["\r"] = :enter
@@key_map["\e[2~"] = :insert
@@key_map["\e[3~"] = :delete
@@key_map["\e[H"] = :home
@@key_map["\e[F"] = :end
@@key_map["\e[5~"] = :pageup
@@key_map["\e[6~"] = :pagedown
@@key_map["\eOP"] = :f1
@@key_map["\eOQ"] = :f2
@@key_map["\eOR"] = :f3
@@key_map["\eOS"] = :f4
@@key_map["\e[15~"] = :f5
@@key_map["\e[17~"] = :f6
@@key_map["\e[18~"] = :f7
@@key_map["\e[19~"] = :f8
@@key_map["\e[20~"] = :f9
@@key_map["\e[21~"] = :f10
@@key_map["\e[24~"] = :f11
@@key_map["\e[24~"] = :f12
def self.read
key = $stdin.getch
while true
temp = nil
temp = $stdin.read_nonblock(1) rescue nil
if temp == nil
break
end
key << temp
end
symbol = @@key_map[key]
if symbol != nil
return symbol
end
return key
end
end
def brightness_to_str(brightness)
return "Brightness: #{brightness.value} / #{brightness.max_value} (#{(brightness.value.to_f / brightness.max_value * 100).round(2)}%)"
end
def main
if File.writable?(BRIGHTNESS_FILE) != true
system("sudo chgrp sudo /sys/class/backlight/intel_backlight/brightness")
system("sudo chmod g+w /sys/class/backlight/intel_backlight/brightness")
end
begin
line_update = StdoutSingleLineUpdate.new
brightness = Brightness.new(BRIGHTNESS_FILE, MAX_BRIGHTNESS_FILE){|brightness_self|
line_update.update_message(brightness_to_str(brightness_self))
}
$stdout.puts(" up/a:+1 right/s:+10 PageDown/d:+100")
$stdout.puts("down/z:-1 left/x:-10 PageUp/c:-100")
$stdout.puts("esc/q: quit")
$stdin.echo = false
begin
$stdin.cursor = false
rescue NotImplementedError
$stdout.write(CURSOR_OFF)
end
line_update.update_message(brightness_to_str(brightness))
while true
brightness_updated = true
brightness_to_change = 0
key = ReadKey.read()
if String === key
key.downcase!
end
case key
when "a", :up
brightness_to_change = 1
when "s", :right
brightness_to_change = 10
when "d", :pagedown
brightness_to_change = 100
when "z", :down
brightness_to_change = -1
when "x", :left
brightness_to_change = -10
when "c", :pageup
brightness_to_change = -100
when "q", :esc, :ctrl_c
brightness.close
break
else
brightness_updated = false
line_update.update_message("Unsupported key #{key.inspect}")
end
if brightness_updated == true
brightness.increase(brightness_to_change)
line_update.update_message(brightness_to_str(brightness))
end
end
rescue Exception => e
raise e
ensure
$stdin.echo = true
begin
$stdin.cursor = true
rescue NotImplementedError
$stdout.write(CURSOR_ON)
end
$stdout.puts("")
end
end
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment