rename default_patterns to fit module naming conventions
This commit is contained in:
parent
17117a914c
commit
dabe69bb8d
2
core
2
core
|
@ -1 +1 @@
|
|||
Subproject commit 9a3912cc2879099d154ec6e267106db885e0fd7f
|
||||
Subproject commit bc98d64370d9fcd164d73514976238e806d862d1
|
|
@ -1,31 +0,0 @@
|
|||
local Widget = require 'Widget'
|
||||
local FillRect = require 'FillRect'
|
||||
|
||||
local left = Widget.Panel{
|
||||
x = _G_INIT_DATA_.LEFT_X - _G_INIT_DATA_.PANEL_MARGIN_X,
|
||||
y = _G_INIT_DATA_.TOP_Y - _G_INIT_DATA_.PANEL_MARGIN_Y,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH + _G_INIT_DATA_.PANEL_MARGIN_X * 2,
|
||||
height = _G_INIT_DATA_.SIDE_HEIGHT + _G_INIT_DATA_.PANEL_MARGIN_Y * 2,
|
||||
}
|
||||
local center = Widget.Panel{
|
||||
x = _G_INIT_DATA_.CENTER_LEFT_X - _G_INIT_DATA_.PANEL_MARGIN_X,
|
||||
y = _G_INIT_DATA_.TOP_Y - _G_INIT_DATA_.PANEL_MARGIN_Y,
|
||||
width = _G_INIT_DATA_.CENTER_WIDTH + _G_INIT_DATA_.PANEL_MARGIN_Y * 2 + _G_INIT_DATA_.CENTER_PAD,
|
||||
height = _G_INIT_DATA_.CENTER_HEIGHT + _G_INIT_DATA_.PANEL_MARGIN_Y * 2,
|
||||
}
|
||||
local right = Widget.Panel{
|
||||
x = _G_INIT_DATA_.RIGHT_X - _G_INIT_DATA_.PANEL_MARGIN_X,
|
||||
y = _G_INIT_DATA_.TOP_Y - _G_INIT_DATA_.PANEL_MARGIN_Y,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH + _G_INIT_DATA_.PANEL_MARGIN_X * 2,
|
||||
height = _G_INIT_DATA_.SIDE_HEIGHT + _G_INIT_DATA_.PANEL_MARGIN_Y * 2,
|
||||
}
|
||||
|
||||
Widget = nil
|
||||
|
||||
local draw = function(cr)
|
||||
FillRect.draw(left, cr)
|
||||
FillRect.draw(center, cr)
|
||||
FillRect.draw(right, cr)
|
||||
end
|
||||
|
||||
return draw
|
3
main.lua
3
main.lua
|
@ -83,8 +83,7 @@ _G_INIT_DATA_.CENTER_WIDTH = _G_INIT_DATA_.SECTION_WIDTH * 2 + _G_INIT_DATA_.CEN
|
|||
_G_INIT_DATA_.RIGHT_X = _G_INIT_DATA_.CENTER_LEFT_X + _G_INIT_DATA_.CENTER_WIDTH + _G_INIT_DATA_.PANEL_MARGIN_X * 2 + _G_INIT_DATA_.PANEL_HORZ_SPACING
|
||||
|
||||
package.path = _G_INIT_DATA_.ABS_PATH..'/?.lua;'..
|
||||
_G_INIT_DATA_.ABS_PATH..'/interface/?.lua;'..
|
||||
_G_INIT_DATA_.ABS_PATH..'/module/?.lua;'..
|
||||
_G_INIT_DATA_.ABS_PATH..'/drawing/?.lua;'..
|
||||
_G_INIT_DATA_.ABS_PATH..'/schema/?.lua;'..
|
||||
_G_INIT_DATA_.ABS_PATH..'/core/func/?.lua;'..
|
||||
_G_INIT_DATA_.ABS_PATH..'/core/super/?.lua;'..
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
local Widget = require 'Widget'
|
||||
local Text = require 'Text'
|
||||
local Line = require 'Line'
|
||||
local TextColumn = require 'TextColumn'
|
||||
local CompoundBar = require 'CompoundBar'
|
||||
local util = require 'util'
|
||||
local schema = require 'default_patterns'
|
||||
|
||||
local __string_match = string.match
|
||||
|
||||
local _FS_PATHS_ = {'/', '/boot', '/var', '/home', '/mnt/data', '/usr/local/opt'}
|
||||
local _MODULE_Y_ = 165
|
||||
local _SPACING_ = 20
|
||||
local _BAR_PAD_ = 100
|
||||
|
||||
local FS_NUM = #_FS_PATHS_
|
||||
|
||||
local header = Widget.Header{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _MODULE_Y_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
header = 'FILE SYSTEMS'
|
||||
}
|
||||
|
||||
local labels = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = header.bottom_y,
|
||||
spacing = _SPACING_,
|
||||
'root',
|
||||
'boot',
|
||||
'var',
|
||||
'home',
|
||||
'data',
|
||||
'lopt'
|
||||
}
|
||||
|
||||
local conky_used_perc = {}
|
||||
|
||||
for i, v in pairs(_FS_PATHS_) do
|
||||
conky_used_perc[i] = '${fs_used_perc '..v..'}'
|
||||
end
|
||||
|
||||
local bars = Widget.CompoundBar{
|
||||
x = _G_INIT_DATA_.RIGHT_X + _BAR_PAD_,
|
||||
y = header.bottom_y,
|
||||
length = _G_INIT_DATA_.SECTION_WIDTH - _BAR_PAD_,
|
||||
spacing = _SPACING_,
|
||||
num_bars = FS_NUM,
|
||||
critical_limit = '>0.8'
|
||||
}
|
||||
|
||||
Widget = nil
|
||||
schema = nil
|
||||
|
||||
_SPACING_ = nil
|
||||
_BAR_PAD_ = nil
|
||||
_FS_PATHS_ = nil
|
||||
|
||||
local update = function(cr)
|
||||
for i = 1, FS_NUM do
|
||||
local percent = util.conky_numeric(conky_used_perc[i])
|
||||
CompoundBar.set(bars, i, percent * 0.01)
|
||||
end
|
||||
end
|
||||
|
||||
local draw = function(cr, current_interface, trigger)
|
||||
if trigger == 0 then update(cr) end
|
||||
|
||||
if current_interface == 0 then
|
||||
Text.draw(header.text, cr)
|
||||
Line.draw(header.underline, cr)
|
||||
TextColumn.draw(labels, cr)
|
||||
CompoundBar.draw(bars, cr)
|
||||
end
|
||||
end
|
||||
|
||||
return draw
|
|
@ -1,333 +0,0 @@
|
|||
local Widget = require 'Widget'
|
||||
local CriticalText = require 'CriticalText'
|
||||
local Text = require 'Text'
|
||||
local TextColumn = require 'TextColumn'
|
||||
local Line = require 'Line'
|
||||
local LabelPlot = require 'LabelPlot'
|
||||
local util = require 'util'
|
||||
local schema = require 'default_patterns'
|
||||
|
||||
local __tonumber = tonumber
|
||||
local __string_find = string.find
|
||||
local __string_match = string.match
|
||||
|
||||
local _MODULE_Y_ = 145
|
||||
local _SEPARATOR_SPACING_ = 20
|
||||
local _TEXT_SPACING_ = 20
|
||||
local _PLOT_SEC_BREAK_ = 20
|
||||
local _PLOT_HEIGHT_ = 56
|
||||
|
||||
local header = Widget.Header{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _MODULE_Y_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
header = 'NVIDIA GRAPHICS'
|
||||
}
|
||||
|
||||
local _RIGHT_X_ = _G_INIT_DATA_.LEFT_X + _G_INIT_DATA_.SECTION_WIDTH
|
||||
|
||||
local status = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = header.bottom_y,
|
||||
text = 'Status'
|
||||
},
|
||||
value = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = header.bottom_y,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
text = '<status>'
|
||||
}
|
||||
}
|
||||
|
||||
local _SEP_Y_1_ = header.bottom_y + _SEPARATOR_SPACING_
|
||||
|
||||
local separator1 = Widget.Line{
|
||||
p1 = {x = _G_INIT_DATA_.LEFT_X, y = _SEP_Y_1_},
|
||||
p2 = {x = _RIGHT_X_, y = _SEP_Y_1_}
|
||||
}
|
||||
|
||||
local _INTERNAL_TEMP_Y_ = _SEP_Y_1_ + _SEPARATOR_SPACING_
|
||||
|
||||
local internal_temp = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _INTERNAL_TEMP_Y_,
|
||||
text = 'Internal Temperature'
|
||||
},
|
||||
value = Widget.CriticalText{
|
||||
x = _RIGHT_X_,
|
||||
y = _INTERNAL_TEMP_Y_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
text = '<gpu_temp>'
|
||||
}
|
||||
}
|
||||
|
||||
local _PCI_UTIL_Y_ = _INTERNAL_TEMP_Y_ + _TEXT_SPACING_
|
||||
|
||||
local pci_util = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _PCI_UTIL_Y_,
|
||||
text = 'PCI Utilization'
|
||||
},
|
||||
value = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = _PCI_UTIL_Y_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
text = '<pci_util>'
|
||||
}
|
||||
}
|
||||
|
||||
local _SEP_Y_2_ = _PCI_UTIL_Y_ + _SEPARATOR_SPACING_
|
||||
|
||||
local separator2 = Widget.Line{
|
||||
p1 = {x = _G_INIT_DATA_.LEFT_X, y = _SEP_Y_2_},
|
||||
p2 = {x = _RIGHT_X_, y = _SEP_Y_2_}
|
||||
}
|
||||
|
||||
local _CLOCK_SPEED_Y_ = _SEP_Y_2_ + _SEPARATOR_SPACING_
|
||||
|
||||
local clock_speed = {
|
||||
labels = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _CLOCK_SPEED_Y_,
|
||||
spacing = _TEXT_SPACING_,
|
||||
'GPU Clock Speed',
|
||||
'Memory Clock Speed'
|
||||
},
|
||||
values = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.LEFT_X + _G_INIT_DATA_.SECTION_WIDTH,
|
||||
y = _CLOCK_SPEED_Y_,
|
||||
spacing = _TEXT_SPACING_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
num_rows = 2
|
||||
}
|
||||
}
|
||||
|
||||
local _SEP_Y_3_ = _CLOCK_SPEED_Y_ + _TEXT_SPACING_ * 2
|
||||
|
||||
local separator3 = Widget.Line{
|
||||
p1 = {x = _G_INIT_DATA_.LEFT_X, y = _SEP_Y_3_},
|
||||
p2 = {x = _RIGHT_X_, y = _SEP_Y_3_}
|
||||
}
|
||||
|
||||
local _GPU_UTIL_Y_ = _SEP_Y_3_ + _SEPARATOR_SPACING_
|
||||
|
||||
local gpu_util = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _GPU_UTIL_Y_,
|
||||
text = 'GPU Utilization'
|
||||
},
|
||||
value = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = _GPU_UTIL_Y_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
text = '<gpu_util>'
|
||||
},
|
||||
plot = Widget.LabelPlot{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _GPU_UTIL_Y_ + _PLOT_SEC_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_
|
||||
}
|
||||
}
|
||||
|
||||
local _MEM_UTIL_Y_ = _GPU_UTIL_Y_ + _PLOT_HEIGHT_ + _PLOT_SEC_BREAK_ * 2
|
||||
|
||||
local mem_util = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _MEM_UTIL_Y_,
|
||||
text = 'Memory Utilization'
|
||||
},
|
||||
value = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = _MEM_UTIL_Y_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
text = '<mem_util>'
|
||||
},
|
||||
plot = Widget.LabelPlot{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _MEM_UTIL_Y_ + _PLOT_SEC_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_
|
||||
}
|
||||
}
|
||||
|
||||
local _VID_UTIL_Y_ = _MEM_UTIL_Y_ + _PLOT_HEIGHT_ + _PLOT_SEC_BREAK_ * 2
|
||||
|
||||
local vid_util = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _VID_UTIL_Y_,
|
||||
text = 'Video Utilization'
|
||||
},
|
||||
value = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = _VID_UTIL_Y_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
text = '<vid_util>'
|
||||
},
|
||||
plot = Widget.LabelPlot{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _VID_UTIL_Y_ + _PLOT_SEC_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_
|
||||
}
|
||||
}
|
||||
|
||||
--[[
|
||||
vars to process the nv settings glob
|
||||
|
||||
glob will be of the form:
|
||||
<used_mem>
|
||||
<total_mem>
|
||||
<temp>
|
||||
<gpu_freq>,<mem_freq>
|
||||
graphics=<gpu_util>, memory=<mem_util>, video=<vid_util>, PCIe=<pci_util>
|
||||
--]]
|
||||
local NV_QUERY = 'optirun nvidia-settings -c :8 -t'..
|
||||
' -q UsedDedicatedGPUMemory'..
|
||||
' -q TotalDedicatedGPUMemory'..
|
||||
' -q ThermalSensorReading'..
|
||||
' -q [gpu:0]/GPUCurrentClockFreqs'..
|
||||
' -q [gpu:0]/GPUUtilization'
|
||||
|
||||
local NV_REGEX = '(%d+)\n'..
|
||||
'(%d+)\n'..
|
||||
'(%d+)\n'..
|
||||
'(%d+),(%d+)\n'..
|
||||
'graphics=(%d+), memory=%d+, video=(%d+), PCIe=(%d+)\n'
|
||||
|
||||
local NA = 'N/A'
|
||||
|
||||
local nvidia_off = function(cr)
|
||||
CriticalText.set(internal_temp.value, cr, NA, 1)
|
||||
Text.set(pci_util.value, cr, NA)
|
||||
|
||||
TextColumn.set(clock_speed.values, cr, 1, NA)
|
||||
TextColumn.set(clock_speed.values, cr, 2, NA)
|
||||
|
||||
Text.set(gpu_util.value, cr, NA)
|
||||
Text.set(mem_util.value, cr, NA)
|
||||
Text.set(vid_util.value, cr, NA)
|
||||
|
||||
LabelPlot.update(gpu_util.plot, 0)
|
||||
LabelPlot.update(mem_util.plot, 0)
|
||||
LabelPlot.update(vid_util.plot, 0)
|
||||
end
|
||||
|
||||
local update = function(cr)
|
||||
-- check if bbswitch is on
|
||||
if util.read_file('/proc/acpi/bbswitch', '.+%s+(%u+)') == 'ON' then
|
||||
|
||||
-- bbswitch might be on, but only because conky is constantly querying
|
||||
-- it and there appears to be some lag between closing all optirun
|
||||
-- processes and flipping bbswitch off. If bbswitch is on and there are
|
||||
-- no optirun processes, we call this "Mixed." In this case we don't
|
||||
-- check anything (to allow bbswitch to actually switch off) and set all
|
||||
-- values to N/A and 0.
|
||||
if __string_find(util.execute_cmd('ps -A -o comm'), 'optirun') == nil then
|
||||
Text.set(status.value, cr, 'Mixed')
|
||||
nvidia_off(cr)
|
||||
else
|
||||
Text.set(status.value, cr, 'On')
|
||||
local nvidia_settings_glob = util.execute_cmd(NV_QUERY)
|
||||
|
||||
local used_memory, total_memory, temp_reading, gpu_frequency,
|
||||
memory_frequency, gpu_utilization, vid_utilization,
|
||||
pci_utilization = __string_match(nvidia_settings_glob, NV_REGEX)
|
||||
|
||||
local is_critical = 1
|
||||
if __tonumber(temp_reading) > 80 then is_critical = 0 end
|
||||
|
||||
CriticalText.set(internal_temp.value, cr, temp_reading..'°C', is_critical)
|
||||
Text.set(pci_util.value, cr, pci_utilization..'%')
|
||||
|
||||
TextColumn.set(clock_speed.values, cr, 1, gpu_frequency..' Mhz')
|
||||
TextColumn.set(clock_speed.values, cr, 2, memory_frequency..' Mhz')
|
||||
|
||||
local percent_used_memory = used_memory / total_memory
|
||||
|
||||
Text.set(gpu_util.value, cr, gpu_utilization..'%')
|
||||
Text.set(mem_util.value, cr, util.round(percent_used_memory * 100)..'%')
|
||||
Text.set(vid_util.value, cr, vid_utilization..'%')
|
||||
|
||||
LabelPlot.update(gpu_util.plot, gpu_utilization * 0.01)
|
||||
LabelPlot.update(mem_util.plot, percent_used_memory)
|
||||
LabelPlot.update(vid_util.plot, vid_utilization * 0.01)
|
||||
end
|
||||
else
|
||||
Text.set(status.value, cr, 'Off')
|
||||
nvidia_off(cr)
|
||||
end
|
||||
end
|
||||
|
||||
Widget = nil
|
||||
schema = nil
|
||||
_MODULE_Y_ = nil
|
||||
_SEPARATOR_SPACING_ = nil
|
||||
_TEXT_SPACING_ = nil
|
||||
_PLOT_SEC_BREAK_ = nil
|
||||
_PLOT_HEIGHT_ = nil
|
||||
_RIGHT_X_ = nil
|
||||
_SEP_Y_1_ = nil
|
||||
_SEP_Y_2_ = nil
|
||||
_SEP_Y_3_ = nil
|
||||
_INTERNAL_TEMP_Y_ = nil
|
||||
_PCI_UTIL_Y_ = nil
|
||||
_CLOCK_SPEED_Y_ = nil
|
||||
_GPU_UTIL_Y_ = nil
|
||||
_MEM_UTIL_Y_ = nil
|
||||
_VID_UTIL_Y_ = nil
|
||||
|
||||
local draw = function(cr, current_interface)
|
||||
update(cr)
|
||||
|
||||
if current_interface == 0 then
|
||||
Text.draw(header.text, cr)
|
||||
Line.draw(header.underline, cr)
|
||||
|
||||
Text.draw(status.label, cr)
|
||||
Text.draw(status.value, cr)
|
||||
|
||||
Line.draw(separator1, cr)
|
||||
|
||||
Text.draw(internal_temp.label, cr)
|
||||
Text.draw(internal_temp.value, cr)
|
||||
|
||||
Text.draw(pci_util.label, cr)
|
||||
Text.draw(pci_util.value, cr)
|
||||
|
||||
Line.draw(separator2, cr)
|
||||
|
||||
TextColumn.draw(clock_speed.labels, cr)
|
||||
TextColumn.draw(clock_speed.values, cr)
|
||||
|
||||
Line.draw(separator3, cr)
|
||||
|
||||
Text.draw(gpu_util.label, cr)
|
||||
Text.draw(gpu_util.value, cr)
|
||||
LabelPlot.draw(gpu_util.plot, cr)
|
||||
|
||||
Text.draw(mem_util.label, cr)
|
||||
Text.draw(mem_util.value, cr)
|
||||
LabelPlot.draw(mem_util.plot, cr)
|
||||
|
||||
Text.draw(vid_util.label, cr)
|
||||
Text.draw(vid_util.value, cr)
|
||||
LabelPlot.draw(vid_util.plot, cr)
|
||||
end
|
||||
end
|
||||
|
||||
return draw
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
local Widget = require 'Widget'
|
||||
local Arc = require 'Arc'
|
||||
local Dial = require 'Dial'
|
||||
local CriticalText = require 'CriticalText'
|
||||
local Text = require 'Text'
|
||||
local TextColumn = require 'TextColumn'
|
||||
local Line = require 'Line'
|
||||
local LabelPlot = require 'LabelPlot'
|
||||
local Table = require 'Table'
|
||||
local util = require 'util'
|
||||
local schema = require 'default_patterns'
|
||||
|
||||
local __string_match = string.match
|
||||
local __cairo_path_destroy = cairo_path_destroy
|
||||
|
||||
local _MODULE_Y_ = 712
|
||||
local _DIAL_THICKNESS_ = 8
|
||||
local _DIAL_SPACING_ = 1
|
||||
local _TEXT_Y_OFFSET_ = 7
|
||||
local _TEXT_LEFT_X_OFFSET_ = 30
|
||||
local _TEXT_SPACING_ = 20
|
||||
local _PLOT_SECTION_BREAK_ = 30
|
||||
local _PLOT_HEIGHT_ = 56
|
||||
local _TABLE_SECTION_BREAK_ = 20
|
||||
local _TABLE_HEIGHT_ = 114
|
||||
|
||||
local MEM_TOTAL_KB = tonumber(util.read_file('/proc/meminfo', '^MemTotal:%s+(%d+)'))
|
||||
|
||||
local MEMINFO_REGEX = '\nMemFree:%s+(%d+).+'..
|
||||
'\nBuffers:%s+(%d+).+'..
|
||||
'\nCached:%s+(%d+).+'..
|
||||
'\nSwapTotal:%s+(%d+).+'..
|
||||
'\nSwapFree:%s+(%d+).+'..
|
||||
'\nSReclaimable:%s+(%d+)'
|
||||
|
||||
local NUM_ROWS = 5
|
||||
local TABLE_CONKY = {{}, {}, {}}
|
||||
|
||||
for r = 1, NUM_ROWS do
|
||||
TABLE_CONKY[1][r] = '${top_mem name '..r..'}'
|
||||
TABLE_CONKY[2][r] = '${top_mem pid '..r..'}'
|
||||
TABLE_CONKY[3][r] = '${top_mem mem '..r..'}'
|
||||
end
|
||||
|
||||
local header = Widget.Header{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _MODULE_Y_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
header = 'MEMORY'
|
||||
}
|
||||
|
||||
local DIAL_RADIUS = 32
|
||||
local DIAL_THETA_0 = math.rad(90)
|
||||
local DIAL_THETA_1 = math.rad(360)
|
||||
local DIAL_X = _G_INIT_DATA_.RIGHT_X + DIAL_RADIUS + _DIAL_THICKNESS_ / 2
|
||||
local DIAL_Y = header.bottom_y + DIAL_RADIUS + _DIAL_THICKNESS_ / 2
|
||||
|
||||
local dial = Widget.Dial{
|
||||
x = DIAL_X,
|
||||
y = DIAL_Y,
|
||||
radius = DIAL_RADIUS,
|
||||
thickness = _DIAL_THICKNESS_,
|
||||
critical_limit = '>0.8'
|
||||
}
|
||||
local cache_arc = Widget.Arc{
|
||||
x = DIAL_X,
|
||||
y = DIAL_Y,
|
||||
radius = DIAL_RADIUS,
|
||||
thickness = _DIAL_THICKNESS_,
|
||||
arc_pattern = schema.PURPLE_ROUNDED
|
||||
}
|
||||
|
||||
local total_used = Widget.CriticalText{
|
||||
x = DIAL_X,
|
||||
y = DIAL_Y,
|
||||
x_align = 'center',
|
||||
y_align = 'center',
|
||||
append_end = '%'
|
||||
}
|
||||
|
||||
local inner_ring = Widget.Arc{
|
||||
x = DIAL_X,
|
||||
y = DIAL_Y,
|
||||
radius = DIAL_RADIUS - _DIAL_THICKNESS_ / 2 - 2,
|
||||
theta0 = 0,
|
||||
theta1 = 360
|
||||
}
|
||||
|
||||
local _LINE_1_Y_ = header.bottom_y + _TEXT_Y_OFFSET_
|
||||
local _TEXT_LEFT_X_ = _G_INIT_DATA_.RIGHT_X + DIAL_RADIUS * 2 + _TEXT_LEFT_X_OFFSET_
|
||||
local _RIGHT_X_ = _G_INIT_DATA_.RIGHT_X + _G_INIT_DATA_.SECTION_WIDTH
|
||||
|
||||
local swap= {
|
||||
label = Widget.Text{
|
||||
x = _TEXT_LEFT_X_,
|
||||
y = _LINE_1_Y_,
|
||||
spacing = _TEXT_SPACING_,
|
||||
text = 'Swap Usage'
|
||||
},
|
||||
percent = Widget.CriticalText{
|
||||
x = _RIGHT_X_,
|
||||
y = _LINE_1_Y_,
|
||||
x_align = 'right',
|
||||
append_end = ' %',
|
||||
},
|
||||
}
|
||||
|
||||
local cache = {
|
||||
labels = Widget.TextColumn{
|
||||
x = _TEXT_LEFT_X_,
|
||||
y = _LINE_1_Y_ + _TEXT_SPACING_,
|
||||
spacing = _TEXT_SPACING_,
|
||||
'Page Cache',
|
||||
'Buffers',
|
||||
'Kernel Slab'
|
||||
},
|
||||
percents = Widget.TextColumn{
|
||||
x = _RIGHT_X_,
|
||||
y = _LINE_1_Y_ + _TEXT_SPACING_,
|
||||
x_align = 'right',
|
||||
append_end = ' %',
|
||||
text_color = schema.PURPLE,
|
||||
'<cached_kb>',
|
||||
'<buffers_kb>',
|
||||
'<kernel_slab>'
|
||||
},
|
||||
}
|
||||
|
||||
local _PLOT_Y_ = _PLOT_SECTION_BREAK_ + header.bottom_y + DIAL_RADIUS * 2
|
||||
|
||||
local plot = Widget.LabelPlot{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _PLOT_Y_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_
|
||||
}
|
||||
|
||||
local tbl = Widget.Table{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _PLOT_Y_ + _PLOT_HEIGHT_ + _TABLE_SECTION_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _TABLE_HEIGHT_,
|
||||
'Name',
|
||||
'PID',
|
||||
'Mem (%)'
|
||||
}
|
||||
|
||||
local update = function(cr)
|
||||
-- see source for the 'free' command (sysinfo.c) for formulas
|
||||
|
||||
local memfree_kb, buffers_kb, cached_kb, swap_total_kb, swap_free_kb,
|
||||
slab_reclaimable_kb = __string_match(util.read_file('/proc/meminfo'), MEMINFO_REGEX)
|
||||
|
||||
local used_percent = (MEM_TOTAL_KB - memfree_kb - cached_kb - buffers_kb - slab_reclaimable_kb) / MEM_TOTAL_KB
|
||||
|
||||
Dial.set(dial, used_percent)
|
||||
CriticalText.set(total_used, cr, util.round(used_percent * 100))
|
||||
|
||||
local cache_theta = (DIAL_THETA_0 - DIAL_THETA_1) / MEM_TOTAL_KB * memfree_kb + DIAL_THETA_1
|
||||
__cairo_path_destroy(cache_arc.path)
|
||||
cache_arc.path = Arc.create_path(cr, DIAL_X, DIAL_Y, DIAL_RADIUS, dial.dial_angle, cache_theta)
|
||||
|
||||
CriticalText.set(swap.percent, cr, util.precision_round_to_string(
|
||||
(swap_total_kb - swap_free_kb) / swap_total_kb * 100))
|
||||
|
||||
local _percents = cache.percents
|
||||
|
||||
TextColumn.set(_percents, cr, 1, util.precision_round_to_string(
|
||||
cached_kb / MEM_TOTAL_KB * 100))
|
||||
|
||||
TextColumn.set(_percents, cr, 2, util.precision_round_to_string(
|
||||
buffers_kb / MEM_TOTAL_KB * 100))
|
||||
|
||||
TextColumn.set(_percents, cr, 3, util.precision_round_to_string(
|
||||
slab_reclaimable_kb / MEM_TOTAL_KB * 100))
|
||||
|
||||
LabelPlot.update(plot, used_percent)
|
||||
|
||||
for c = 1, 3 do
|
||||
local column = TABLE_CONKY[c]
|
||||
for r = 1, NUM_ROWS do
|
||||
Table.set(tbl, cr, c, r, util.conky(column[r], '(%S+)'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Widget = nil
|
||||
schema = nil
|
||||
_MODULE_Y_ = nil
|
||||
_DIAL_THICKNESS_ = nil
|
||||
_DIAL_SPACING_ = nil
|
||||
_TEXT_Y_OFFSET_ = nil
|
||||
_TEXT_LEFT_X_OFFSET_ = nil
|
||||
_TEXT_SPACING_ = nil
|
||||
_PLOT_SECTION_BREAK_ = nil
|
||||
_PLOT_HEIGHT_ = nil
|
||||
_TABLE_SECTION_BREAK_ = nil
|
||||
_TABLE_HEIGHT_ = nil
|
||||
_LINE_1_Y_ = nil
|
||||
_TEXT_LEFT_X_ = nil
|
||||
_RIGHT_X_ = nil
|
||||
_PLOT_Y_ = nil
|
||||
|
||||
local draw = function(cr, current_interface)
|
||||
update(cr)
|
||||
|
||||
if current_interface == 0 then
|
||||
Text.draw(header.text, cr)
|
||||
Line.draw(header.underline, cr)
|
||||
Dial.draw(dial, cr)
|
||||
Arc.draw(cache_arc, cr)
|
||||
Arc.draw(inner_ring, cr)
|
||||
CriticalText.draw(total_used, cr)
|
||||
|
||||
Text.draw(swap.label, cr)
|
||||
CriticalText.draw(swap.percent, cr)
|
||||
TextColumn.draw(cache.labels, cr)
|
||||
TextColumn.draw(cache.percents, cr)
|
||||
|
||||
LabelPlot.draw(plot, cr)
|
||||
|
||||
Table.draw(tbl, cr)
|
||||
end
|
||||
end
|
||||
|
||||
return draw
|
|
@ -1,154 +0,0 @@
|
|||
local Widget = require 'Widget'
|
||||
local Text = require 'Text'
|
||||
local Line = require 'Line'
|
||||
local ScalePlot = require 'ScalePlot'
|
||||
local util = require 'util'
|
||||
local schema = require 'default_patterns'
|
||||
|
||||
local __string_gmatch = string.gmatch
|
||||
|
||||
local _PLOT_SEC_BREAK_ = 20
|
||||
local _PLOT_HEIGHT_ = 56
|
||||
|
||||
local network_label_function = function(bytes)
|
||||
local new_unit = util.get_unit(bytes)
|
||||
|
||||
local converted = util.convert_bytes(bytes, 'B', new_unit)
|
||||
local precision = 0
|
||||
if converted < 10 then precision = 1 end
|
||||
|
||||
return util.round_to_string(converted, precision)..' '..new_unit..'/s'
|
||||
end
|
||||
|
||||
local header = Widget.Header{
|
||||
x = _G_INIT_DATA_.CENTER_RIGHT_X,
|
||||
y = _G_INIT_DATA_.TOP_Y,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
header = 'NETWORK'
|
||||
}
|
||||
|
||||
local _RIGHT_X_ = _G_INIT_DATA_.CENTER_RIGHT_X + _G_INIT_DATA_.SECTION_WIDTH
|
||||
|
||||
local dnload = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.CENTER_RIGHT_X,
|
||||
y = header.bottom_y,
|
||||
text = 'Download',
|
||||
},
|
||||
speed = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = header.bottom_y,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE
|
||||
},
|
||||
plot = Widget.ScalePlot{
|
||||
x = _G_INIT_DATA_.CENTER_RIGHT_X,
|
||||
y = header.bottom_y + _PLOT_SEC_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_,
|
||||
y_label_func = network_label_function
|
||||
}
|
||||
}
|
||||
|
||||
local _UPLOAD_Y_ = header.bottom_y + _PLOT_HEIGHT_ + _PLOT_SEC_BREAK_ * 2
|
||||
|
||||
local upload = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.CENTER_RIGHT_X,
|
||||
y = _UPLOAD_Y_,
|
||||
text = 'Upload',
|
||||
},
|
||||
speed = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = _UPLOAD_Y_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE
|
||||
},
|
||||
plot = Widget.ScalePlot{
|
||||
x = _G_INIT_DATA_.CENTER_RIGHT_X,
|
||||
y = _UPLOAD_Y_ + _PLOT_SEC_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_,
|
||||
y_label_func = network_label_function
|
||||
}
|
||||
}
|
||||
|
||||
local interface_counters_tbl = {}
|
||||
|
||||
local update = function(cr, update_frequency)
|
||||
local dspeed, uspeed = 0, 0
|
||||
|
||||
local rx_delta, tx_delta
|
||||
|
||||
-- iterate through the route file and filter on interfaces that are gateways (flag = 0003)
|
||||
local iterator = __string_gmatch(util.read_file('/proc/net/route'),
|
||||
'(%w+)%s+%w+%s+%w+%s+0003%s+')
|
||||
|
||||
for interface in iterator do
|
||||
local interface_counters = interface_counters_tbl[interface]
|
||||
|
||||
if not interface_counters then
|
||||
local rx_path = '/sys/class/net/'..interface..'/statistics/rx_bytes'
|
||||
local tx_path = '/sys/class/net/'..interface..'/statistics/tx_bytes'
|
||||
|
||||
interface_counters = {
|
||||
rx_path = rx_path,
|
||||
tx_path = tx_path,
|
||||
prev_rx_byte_cnt = util.read_file(rx_path, nil, '*n'),
|
||||
prev_tx_byte_cnt = util.read_file(tx_path, nil, '*n'),
|
||||
}
|
||||
interface_counters_tbl[interface] = interface_counters
|
||||
end
|
||||
|
||||
local rx_byte_cnt = util.read_file(interface_counters.rx_path, nil, '*n')
|
||||
local tx_byte_cnt = util.read_file(interface_counters.tx_path, nil, '*n')
|
||||
|
||||
rx_delta = rx_byte_cnt - interface_counters.prev_rx_byte_cnt
|
||||
tx_delta = tx_byte_cnt - interface_counters.prev_tx_byte_cnt
|
||||
|
||||
interface_counters.prev_rx_byte_cnt = rx_byte_cnt
|
||||
interface_counters.prev_tx_byte_cnt = tx_byte_cnt
|
||||
|
||||
-- mask overflow
|
||||
if rx_delta > 0 then dspeed = dspeed + rx_delta * update_frequency end
|
||||
if tx_delta > 0 then uspeed = uspeed + tx_delta * update_frequency end
|
||||
end
|
||||
|
||||
local dspeed_unit = util.get_unit(dspeed)
|
||||
local uspeed_unit = util.get_unit(uspeed)
|
||||
|
||||
dnload.speed.append_end = ' '..dspeed_unit..'/s'
|
||||
upload.speed.append_end = ' '..uspeed_unit..'/s'
|
||||
|
||||
Text.set(dnload.speed, cr, util.precision_convert_bytes(dspeed, 'B', dspeed_unit, 3))
|
||||
Text.set(upload.speed, cr, util.precision_convert_bytes(uspeed, 'B', uspeed_unit, 3))
|
||||
|
||||
ScalePlot.update(dnload.plot, cr, dspeed)
|
||||
ScalePlot.update(upload.plot, cr, uspeed)
|
||||
end
|
||||
|
||||
Widget = nil
|
||||
schema = nil
|
||||
_PLOT_SEC_BREAK_ = nil
|
||||
_PLOT_HEIGHT_ = nil
|
||||
_RIGHT_X_ = nil
|
||||
_UPLOAD_Y_ = nil
|
||||
|
||||
local draw = function(cr, current_interface, update_frequency)
|
||||
update(cr, update_frequency)
|
||||
|
||||
if current_interface == 0 then
|
||||
Text.draw(header.text, cr)
|
||||
Line.draw(header.underline, cr)
|
||||
|
||||
Text.draw(dnload.label, cr)
|
||||
Text.draw(dnload.speed, cr)
|
||||
ScalePlot.draw(dnload.plot, cr)
|
||||
|
||||
Text.draw(upload.label, cr)
|
||||
Text.draw(upload.speed, cr)
|
||||
ScalePlot.draw(upload.plot, cr)
|
||||
end
|
||||
end
|
||||
|
||||
return draw
|
|
@ -1,65 +0,0 @@
|
|||
local Widget = require 'Widget'
|
||||
local Text = require 'Text'
|
||||
local Line = require 'Line'
|
||||
local TextColumn = require 'TextColumn'
|
||||
local util = require 'util'
|
||||
local schema = require 'default_patterns'
|
||||
|
||||
local PACMAN_TABLE = {
|
||||
'pacman -Qq',
|
||||
'pacman -Qeq',
|
||||
'pacman -Quq',
|
||||
'pacman -Qdtq',
|
||||
'pacman -Qmq'
|
||||
}
|
||||
|
||||
local _TEXT_SPACING_ = 20
|
||||
|
||||
local header = Widget.Header{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _G_INIT_DATA_.TOP_Y,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
header = 'PACMAN'
|
||||
}
|
||||
|
||||
local labels = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = header.bottom_y,
|
||||
spacing = _TEXT_SPACING_,
|
||||
'Total',
|
||||
'Explicit',
|
||||
'Outdated',
|
||||
'Orphaned',
|
||||
'Local'
|
||||
}
|
||||
local info = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.RIGHT_X + _G_INIT_DATA_.SECTION_WIDTH,
|
||||
y = header.bottom_y,
|
||||
spacing = _TEXT_SPACING_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
num_rows = 5
|
||||
}
|
||||
|
||||
Widget = nil
|
||||
schema = nil
|
||||
_TEXT_SPACING_ = nil
|
||||
|
||||
local update = function(cr)
|
||||
for i, cmd in pairs(PACMAN_TABLE) do
|
||||
TextColumn.set(info, cr, i, util.line_count(util.execute_cmd(cmd)))
|
||||
end
|
||||
end
|
||||
|
||||
local draw = function(cr, current_interface, log_is_changed)
|
||||
if log_is_changed then update(cr) end
|
||||
|
||||
if current_interface == 0 then
|
||||
Text.draw(header.text, cr)
|
||||
Line.draw(header.underline, cr)
|
||||
TextColumn.draw(labels, cr)
|
||||
TextColumn.draw(info, cr)
|
||||
end
|
||||
end
|
||||
|
||||
return draw
|
221
module/Power.lua
221
module/Power.lua
|
@ -1,221 +0,0 @@
|
|||
local Widget = require 'Widget'
|
||||
local Text = require 'Text'
|
||||
local TextColumn = require 'TextColumn'
|
||||
local Line = require 'Line'
|
||||
local ScalePlot = require 'ScalePlot'
|
||||
local util = require 'util'
|
||||
local schema = require 'default_patterns'
|
||||
|
||||
local _MODULE_Y_ = 328
|
||||
local _SEPARATOR_SPACING_ = 20
|
||||
local _TEXT_SPACING_ = 20
|
||||
local _PLOT_SEC_BREAK_ = 20
|
||||
local _PLOT_HEIGHT_ = 56
|
||||
|
||||
local power_label_function = function(watts) return watts..' W' end
|
||||
|
||||
local calculate_power = function(cr, prev_cnt, cnt, update_frequency)
|
||||
if cnt > prev_cnt then
|
||||
return (cnt - prev_cnt) * update_frequency * 0.000001
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local header = Widget.Header{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _MODULE_Y_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
header = 'POWER'
|
||||
}
|
||||
|
||||
local _RIGHT_X_ = _G_INIT_DATA_.RIGHT_X + _G_INIT_DATA_.SECTION_WIDTH
|
||||
|
||||
local pp01 = {
|
||||
labels = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = header.bottom_y,
|
||||
spacing = _TEXT_SPACING_,
|
||||
'Core',
|
||||
'iGPU'
|
||||
},
|
||||
values = Widget.TextColumn{
|
||||
x = _RIGHT_X_,
|
||||
y = header.bottom_y,
|
||||
spacing = _TEXT_SPACING_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
append_end = ' W',
|
||||
num_rows = 2
|
||||
}
|
||||
}
|
||||
|
||||
local _SEP_Y_ = header.bottom_y + _TEXT_SPACING_ + _SEPARATOR_SPACING_
|
||||
|
||||
local separator = Widget.Line{
|
||||
p1 = {x = _G_INIT_DATA_.RIGHT_X, y = _SEP_Y_},
|
||||
p2 = {x = _RIGHT_X_, y = _SEP_Y_}
|
||||
}
|
||||
|
||||
local _PKG0_Y_ = _SEP_Y_ + _SEPARATOR_SPACING_
|
||||
|
||||
local pkg0 = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _PKG0_Y_,
|
||||
text = 'PKG 0'
|
||||
},
|
||||
value = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = _PKG0_Y_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
text = '<pkg0>',
|
||||
append_end = ' W'
|
||||
},
|
||||
plot = Widget.ScalePlot{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _PKG0_Y_ + _PLOT_SEC_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_,
|
||||
y_label_func = power_label_function,
|
||||
}
|
||||
}
|
||||
|
||||
local _DRAM_Y_ = _PKG0_Y_ + _PLOT_SEC_BREAK_ * 2 + _PLOT_HEIGHT_
|
||||
|
||||
local dram = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _DRAM_Y_,
|
||||
text = 'DRAM'
|
||||
},
|
||||
value = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = _DRAM_Y_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
text = '<dram>',
|
||||
append_end = ' W'
|
||||
},
|
||||
plot = Widget.ScalePlot{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _DRAM_Y_ + _PLOT_SEC_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_,
|
||||
y_label_func = power_label_function,
|
||||
}
|
||||
}
|
||||
|
||||
local _BATTERY_DRAW_Y_ = _DRAM_Y_ + _PLOT_SEC_BREAK_ * 2 + _PLOT_HEIGHT_
|
||||
|
||||
local battery_draw = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _BATTERY_DRAW_Y_,
|
||||
spacing = _TEXT_SPACING_,
|
||||
text = 'Battery Draw'
|
||||
},
|
||||
value = Widget.CriticalText{
|
||||
x = _RIGHT_X_,
|
||||
y = _BATTERY_DRAW_Y_,
|
||||
x_align = 'right',
|
||||
},
|
||||
plot = Widget.ScalePlot{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _BATTERY_DRAW_Y_ + _PLOT_SEC_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_,
|
||||
y_label_func = power_label_function,
|
||||
}
|
||||
}
|
||||
|
||||
local PKG0_PATH = '/sys/class/powercap/intel-rapl:0/energy_uj'
|
||||
local CORE_PATH = '/sys/class/powercap/intel-rapl:0:0/energy_uj'
|
||||
local IGPU_PATH = '/sys/class/powercap/intel-rapl:0:1/energy_uj'
|
||||
local DRAM_PATH = '/sys/class/powercap/intel-rapl:0:2/energy_uj'
|
||||
|
||||
local prev_pkg0_uj_cnt = util.read_file(PKG0_PATH, nil, '*n')
|
||||
local prev_core_uj_cnt = util.read_file(CORE_PATH, nil, '*n')
|
||||
local prev_igpu_uj_cnt = util.read_file(IGPU_PATH, nil, '*n')
|
||||
local prev_dram_uj_cnt = util.read_file(DRAM_PATH, nil, '*n')
|
||||
|
||||
local update = function(cr, update_frequency, is_using_ac)
|
||||
local pkg0_uj_cnt = util.read_file(PKG0_PATH, nil, '*n')
|
||||
local core_uj_cnt = util.read_file(CORE_PATH, nil, '*n')
|
||||
local igpu_uj_cnt = util.read_file(IGPU_PATH, nil, '*n')
|
||||
local dram_uj_cnt = util.read_file(DRAM_PATH, nil, '*n')
|
||||
|
||||
TextColumn.set(pp01.values, cr, 1, util.precision_round_to_string(
|
||||
calculate_power(cr, prev_core_uj_cnt, core_uj_cnt, update_frequency), 3))
|
||||
|
||||
TextColumn.set(pp01.values, cr, 2, util.precision_round_to_string(
|
||||
calculate_power(cr, prev_igpu_uj_cnt, igpu_uj_cnt, update_frequency), 3))
|
||||
|
||||
local pkg0_power = calculate_power(cr, prev_pkg0_uj_cnt, pkg0_uj_cnt, update_frequency)
|
||||
local dram_power = calculate_power(cr, prev_dram_uj_cnt, dram_uj_cnt, update_frequency)
|
||||
|
||||
Text.set(pkg0.value, cr, util.precision_round_to_string(pkg0_power, 3))
|
||||
ScalePlot.update(pkg0.plot, cr, pkg0_power)
|
||||
|
||||
Text.set(dram.value, cr, util.precision_round_to_string(dram_power, 3))
|
||||
ScalePlot.update(dram.plot, cr, dram_power)
|
||||
|
||||
prev_pkg0_uj_cnt = pkg0_uj_cnt
|
||||
prev_core_uj_cnt = core_uj_cnt
|
||||
prev_igpu_uj_cnt = igpu_uj_cnt
|
||||
prev_dram_uj_cnt = dram_uj_cnt
|
||||
|
||||
if is_using_ac then
|
||||
Text.set(battery_draw.value, cr, 'A/C')
|
||||
ScalePlot.update(battery_draw.plot, cr, 0)
|
||||
else
|
||||
local current = util.read_file('/sys/class/power_supply/BAT0/current_now', nil, '*n')
|
||||
local voltage = util.read_file('/sys/class/power_supply/BAT0/voltage_now', nil, '*n')
|
||||
local power = current * voltage * 0.000000000001
|
||||
|
||||
Text.set(battery_draw.value, cr, util.precision_round_to_string(power, 3)..' W')
|
||||
ScalePlot.update(battery_draw.plot, cr, power)
|
||||
end
|
||||
end
|
||||
|
||||
Widget = nil
|
||||
schema = nil
|
||||
_MODULE_Y_ = nil
|
||||
_SEPARATOR_SPACING_ = nil
|
||||
_TEXT_SPACING_ = nil
|
||||
_PLOT_SEC_BREAK_ = nil
|
||||
_PLOT_HEIGHT_ = nil
|
||||
_RIGHT_X_ = nil
|
||||
_SEP_Y_ = nil
|
||||
_PKG0_Y_ = nil
|
||||
_DRAM_Y_ = nil
|
||||
_BATTERY_DRAW_Y_ = nil
|
||||
|
||||
local draw = function(cr, current_interface, update_frequency, is_using_ac)
|
||||
update(cr, update_frequency, is_using_ac)
|
||||
|
||||
if current_interface == 0 then
|
||||
Text.draw(header.text, cr)
|
||||
Line.draw(header.underline, cr)
|
||||
|
||||
TextColumn.draw(pp01.labels, cr)
|
||||
TextColumn.draw(pp01.values, cr)
|
||||
|
||||
Line.draw(separator, cr)
|
||||
|
||||
Text.draw(pkg0.label, cr)
|
||||
Text.draw(pkg0.value, cr)
|
||||
ScalePlot.draw(pkg0.plot, cr)
|
||||
|
||||
Text.draw(dram.label, cr)
|
||||
Text.draw(dram.value, cr)
|
||||
ScalePlot.draw(dram.plot, cr)
|
||||
|
||||
Text.draw(battery_draw.label, cr)
|
||||
Text.draw(battery_draw.value, cr)
|
||||
ScalePlot.draw(battery_draw.plot, cr)
|
||||
end
|
||||
end
|
||||
|
||||
return draw
|
|
@ -1,248 +0,0 @@
|
|||
local Widget = require 'Widget'
|
||||
local Arc = require 'Arc'
|
||||
local CompoundDial = require 'CompoundDial'
|
||||
local CriticalText = require 'CriticalText'
|
||||
local Text = require 'Text'
|
||||
local Line = require 'Line'
|
||||
local LabelPlot = require 'LabelPlot'
|
||||
local Table = require 'Table'
|
||||
local util = require 'util'
|
||||
local schema = require 'default_patterns'
|
||||
|
||||
local CORETEMP_PATH = '/sys/devices/platform/coretemp.0/hwmon/hwmon%i/%s'
|
||||
|
||||
local NUM_PHYSICAL_CORES = 4
|
||||
local NUM_THREADS_PER_CORE = 2
|
||||
|
||||
local NUM_ROWS = 5
|
||||
local TABLE_CONKY = {{}, {}, {}}
|
||||
|
||||
for r = 1, NUM_ROWS do
|
||||
TABLE_CONKY[1][r] = '${top name '..r..'}'
|
||||
TABLE_CONKY[2][r] = '${top pid '..r..'}'
|
||||
TABLE_CONKY[3][r] = '${top cpu '..r..'}'
|
||||
end
|
||||
|
||||
local _MODULE_Y_ = 636
|
||||
local _DIAL_INNER_RADIUS_ = 30
|
||||
local _DIAL_OUTER_RADIUS_ = 42
|
||||
local _DIAL_SPACING_ = 1
|
||||
local _TEXT_Y_OFFSET_ = 15
|
||||
local _SEPARATOR_SPACING_ = 20
|
||||
local _PLOT_SECTION_BREAK_ = 23
|
||||
local _PLOT_HEIGHT_ = 56
|
||||
local _TABLE_SECTION_BREAK_ = 20
|
||||
local _TABLE_HEIGHT_ = 114
|
||||
|
||||
local _create_core_ = function(cores, id, x, y)
|
||||
local conky_threads = {}
|
||||
|
||||
for c = 0, NUM_PHYSICAL_CORES * NUM_THREADS_PER_CORE - 1 do
|
||||
if util.read_file('/sys/devices/system/cpu/cpu'..c..'/topology/core_id', nil, '*n') == id then
|
||||
table.insert(conky_threads, '${cpu cpu'..c..'}')
|
||||
end
|
||||
end
|
||||
|
||||
local hwmon_index = -1
|
||||
while util.read_file(string.format(CORETEMP_PATH, hwmon_index, 'name'), nil, '*l') ~= 'coretemp' do
|
||||
hwmon_index = hwmon_index + 1
|
||||
end
|
||||
|
||||
cores[id +1] = {
|
||||
dials = Widget.CompoundDial{
|
||||
x = x,
|
||||
y = y,
|
||||
inner_radius = _DIAL_INNER_RADIUS_,
|
||||
outer_radius = _DIAL_OUTER_RADIUS_,
|
||||
spacing = _DIAL_SPACING_,
|
||||
num_dials = NUM_THREADS_PER_CORE,
|
||||
critical_limit = '>0.8'
|
||||
},
|
||||
inner_ring = Widget.Arc{
|
||||
x = x,
|
||||
y = y,
|
||||
radius = _DIAL_INNER_RADIUS_ - 2,
|
||||
theta0 = 0,
|
||||
theta1 = 360
|
||||
},
|
||||
coretemp_text = Widget.CriticalText{
|
||||
x = x,
|
||||
y = y,
|
||||
x_align = 'center',
|
||||
y_align = 'center',
|
||||
append_end = '°C',
|
||||
critical_limit = '>90'
|
||||
},
|
||||
coretemp_path = string.format(CORETEMP_PATH, hwmon_index, 'temp'..(id + 2)..'_input'),
|
||||
conky_threads = conky_threads
|
||||
}
|
||||
end
|
||||
|
||||
local header = Widget.Header{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _MODULE_Y_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
header = "PROCESSOR"
|
||||
}
|
||||
|
||||
--we assume that this cpu has 4 physical cores with 2 logical each
|
||||
local cores = {}
|
||||
|
||||
for c = 0, NUM_PHYSICAL_CORES - 1 do
|
||||
local dial_x = _G_INIT_DATA_.LEFT_X + _DIAL_OUTER_RADIUS_ +
|
||||
(_G_INIT_DATA_.SECTION_WIDTH - 2 * _DIAL_OUTER_RADIUS_) * c / 3
|
||||
local dial_y = header.bottom_y + _DIAL_OUTER_RADIUS_
|
||||
_create_core_(cores, c, dial_x, dial_y)
|
||||
end
|
||||
|
||||
local _RIGHT_X_ = _G_INIT_DATA_.LEFT_X + _G_INIT_DATA_.SECTION_WIDTH
|
||||
|
||||
local _PROCESS_Y_ = header.bottom_y + _DIAL_OUTER_RADIUS_ * 2 + _PLOT_SECTION_BREAK_
|
||||
|
||||
local process = {
|
||||
labels = Widget.Text{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _PROCESS_Y_,
|
||||
text = 'R | S | D | T | Z'
|
||||
},
|
||||
values = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = _PROCESS_Y_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
text = '<R.S.D.T.Z>'
|
||||
}
|
||||
}
|
||||
|
||||
local _SEP_Y_ = _PROCESS_Y_ + _SEPARATOR_SPACING_
|
||||
|
||||
local separator = Widget.Line{
|
||||
p1 = {x = _G_INIT_DATA_.LEFT_X, y = _SEP_Y_},
|
||||
p2 = {x = _RIGHT_X_, y = _SEP_Y_}
|
||||
}
|
||||
|
||||
local _LOAD_Y_ = _SEP_Y_ + _SEPARATOR_SPACING_
|
||||
|
||||
local total_load = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _LOAD_Y_,
|
||||
text = 'Total Load'
|
||||
},
|
||||
value = Widget.CriticalText{
|
||||
x = _RIGHT_X_,
|
||||
y = _LOAD_Y_,
|
||||
x_align = 'right',
|
||||
append_end = '%',
|
||||
critical_limit = '>80'
|
||||
}
|
||||
}
|
||||
|
||||
local _PLOT_Y_ = _LOAD_Y_ + _PLOT_SECTION_BREAK_
|
||||
|
||||
local plot = Widget.LabelPlot{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _PLOT_Y_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_
|
||||
}
|
||||
|
||||
local tbl = Widget.Table{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _PLOT_Y_ + _PLOT_HEIGHT_ + _TABLE_SECTION_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _TABLE_HEIGHT_,
|
||||
num_rows = NUM_ROWS,
|
||||
'Name',
|
||||
'PID',
|
||||
'CPU (%)'
|
||||
}
|
||||
|
||||
local update = function(cr)
|
||||
local conky = util.conky
|
||||
local char_count = util.char_count
|
||||
|
||||
local sum = 0
|
||||
for c = 1, NUM_PHYSICAL_CORES do
|
||||
local core = cores[c]
|
||||
|
||||
local conky_threads = core.conky_threads
|
||||
for t = 1, NUM_THREADS_PER_CORE do
|
||||
local percent = util.conky_numeric(conky_threads[t]) * 0.01
|
||||
CompoundDial.set(core.dials, t, percent)
|
||||
sum = sum + percent
|
||||
end
|
||||
|
||||
CriticalText.set(core.coretemp_text, cr, util.round(0.001 * util.read_file(core.coretemp_path, nil, '*n')))
|
||||
end
|
||||
|
||||
local process_glob = util.execute_cmd('ps -A -o s')
|
||||
|
||||
--subtract one from running b/c ps will always be "running"
|
||||
Text.set(process.values, cr, (char_count(process_glob, 'R') - 1)..' | '..
|
||||
char_count(process_glob, 'S')..' | '..
|
||||
char_count(process_glob, 'D')..' | '..
|
||||
char_count(process_glob, 'T')..' | '..
|
||||
char_count(process_glob, 'Z'))
|
||||
|
||||
local load_percent = util.round(sum / NUM_PHYSICAL_CORES / NUM_THREADS_PER_CORE, 2)
|
||||
CriticalText.set(total_load.value, cr, load_percent * 100)
|
||||
|
||||
LabelPlot.update(plot, load_percent)
|
||||
|
||||
for c = 1, 3 do
|
||||
local column = TABLE_CONKY[c]
|
||||
for r = 1, NUM_ROWS do
|
||||
Table.set(tbl, cr, c, r, conky(column[r], '(%S+)'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Widget = nil
|
||||
schema = nil
|
||||
_MODULE_Y_ = nil
|
||||
_DIAL_INNER_RADIUS_ = nil
|
||||
_DIAL_OUTER_RADIUS_ = nil
|
||||
_DIAL_SPACING_ = nil
|
||||
_TEXT_Y_OFFSET_ = nil
|
||||
_SEPARATOR_SPACING_ = nil
|
||||
_PLOT_SECTION_BREAK_ = nil
|
||||
_PLOT_HEIGHT_ = nil
|
||||
_TABLE_SECTION_BREAK_ = nil
|
||||
_TABLE_HEIGHT_ = nil
|
||||
_create_core_ = nil
|
||||
_LOAD_Y_ = nil
|
||||
_RIGHT_X_ = nil
|
||||
_SEP_Y_ = nil
|
||||
_PROCESS_Y_ = nil
|
||||
_PLOT_Y_ = nil
|
||||
|
||||
local draw = function(cr, current_interface)
|
||||
update(cr)
|
||||
|
||||
if current_interface == 0 then
|
||||
Text.draw(header.text, cr)
|
||||
Line.draw(header.underline, cr)
|
||||
|
||||
for c = 1, NUM_PHYSICAL_CORES do
|
||||
local core = cores[c]
|
||||
CompoundDial.draw(core.dials, cr)
|
||||
Arc.draw(core.inner_ring, cr)
|
||||
CriticalText.draw(core.coretemp_text, cr)
|
||||
end
|
||||
|
||||
Text.draw(process.labels, cr)
|
||||
Text.draw(process.values, cr)
|
||||
|
||||
Line.draw(separator, cr)
|
||||
|
||||
Text.draw(total_load.label, cr)
|
||||
CriticalText.draw(total_load.value, cr)
|
||||
|
||||
LabelPlot.draw(plot, cr)
|
||||
|
||||
Table.draw(tbl, cr)
|
||||
end
|
||||
end
|
||||
|
||||
return draw
|
|
@ -1,141 +0,0 @@
|
|||
local Widget = require 'Widget'
|
||||
local Text = require 'Text'
|
||||
local Line = require 'Line'
|
||||
local ScalePlot = require 'ScalePlot'
|
||||
local util = require 'util'
|
||||
local schema = require 'default_patterns'
|
||||
|
||||
local __tonumber = tonumber
|
||||
local __string_match = string.match
|
||||
|
||||
local _PLOT_SEC_BREAK_ = 20
|
||||
local _PLOT_HEIGHT_ = 56
|
||||
|
||||
local BLOCK_SIZE_BYTES = 512
|
||||
local STAT_FILE = '/sys/block/sda/stat'
|
||||
|
||||
-- fields 3 and 7 (sectors read and written)
|
||||
local RW_REGEX = '%s+%d+%s+%d+%s+(%d+)%s+%d+%s+%d+%s+%d+%s+(%d+)'
|
||||
|
||||
local read_stat_file = function()
|
||||
local bytes_r, bytes_w = __string_match(util.read_file(STAT_FILE), RW_REGEX)
|
||||
return __tonumber(bytes_r) * BLOCK_SIZE_BYTES, __tonumber(bytes_w) * BLOCK_SIZE_BYTES
|
||||
end
|
||||
|
||||
local update_stat = function(cr, stat, byte_cnt, update_frequency)
|
||||
local delta_bytes = byte_cnt - stat.prev_byte_cnt
|
||||
stat.prev_byte_cnt = byte_cnt
|
||||
|
||||
if delta_bytes > 0 then
|
||||
local bps = delta_bytes * update_frequency
|
||||
local unit = util.get_unit(bps)
|
||||
stat.rate.append_end = ' '..unit..'/s'
|
||||
Text.set(stat.rate, cr, util.precision_convert_bytes(bps, 'B', unit, 3))
|
||||
ScalePlot.update(stat.plot, cr, bps)
|
||||
else
|
||||
stat.rate.append_end = ' B/s'
|
||||
Text.set(stat.rate, cr, '0.00')
|
||||
ScalePlot.update(stat.plot, cr, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local io_label_function = function(bytes)
|
||||
local new_unit = util.get_unit(bytes)
|
||||
|
||||
local converted = util.convert_bytes(bytes, 'B', new_unit)
|
||||
local precision = 0
|
||||
if converted < 10 then precision = 1 end
|
||||
|
||||
return util.round_to_string(converted, precision)..' '..new_unit..'/s'
|
||||
end
|
||||
|
||||
local header = Widget.Header{
|
||||
x = _G_INIT_DATA_.CENTER_LEFT_X,
|
||||
y = _G_INIT_DATA_.TOP_Y,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
header = 'INPUT / OUTPUT'
|
||||
}
|
||||
|
||||
local _RIGHT_X_ = _G_INIT_DATA_.CENTER_LEFT_X + _G_INIT_DATA_.SECTION_WIDTH
|
||||
|
||||
local reads = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.CENTER_LEFT_X,
|
||||
y = header.bottom_y,
|
||||
text = 'Reads',
|
||||
},
|
||||
rate = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = header.bottom_y,
|
||||
x_align = 'right',
|
||||
append_end=' B/s',
|
||||
text_color = schema.BLUE
|
||||
},
|
||||
plot = Widget.ScalePlot{
|
||||
x = _G_INIT_DATA_.CENTER_LEFT_X,
|
||||
y = header.bottom_y + _PLOT_SEC_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_,
|
||||
y_label_func = io_label_function,
|
||||
}
|
||||
}
|
||||
|
||||
local _WRITE_Y_ = header.bottom_y + _PLOT_HEIGHT_ + _PLOT_SEC_BREAK_ * 2
|
||||
|
||||
local writes = {
|
||||
label = Widget.Text{
|
||||
x = _G_INIT_DATA_.CENTER_LEFT_X,
|
||||
y = _WRITE_Y_,
|
||||
text = 'Writes',
|
||||
},
|
||||
rate = Widget.Text{
|
||||
x = _RIGHT_X_,
|
||||
y = _WRITE_Y_,
|
||||
x_align = 'right',
|
||||
append_end =' B/s',
|
||||
text_color = schema.BLUE
|
||||
},
|
||||
plot = Widget.ScalePlot{
|
||||
x = _G_INIT_DATA_.CENTER_LEFT_X,
|
||||
y = _WRITE_Y_ + _PLOT_SEC_BREAK_,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
height = _PLOT_HEIGHT_,
|
||||
y_label_func = io_label_function,
|
||||
}
|
||||
}
|
||||
|
||||
Widget = nil
|
||||
schema = nil
|
||||
_PLOT_SEC_BREAK_ = nil
|
||||
_PLOT_HEIGHT_ = nil
|
||||
_RIGHT_X_ = nil
|
||||
_WRITE_Y_ = nil
|
||||
|
||||
reads.byte_cnt = 0
|
||||
writes.byte_cnt = 0
|
||||
reads.prev_byte_cnt, writes.prev_byte_cnt = read_stat_file()
|
||||
|
||||
local update = function(cr, update_frequency)
|
||||
local read_byte_cnt, write_byte_cnt = read_stat_file()
|
||||
update_stat(cr, reads, read_byte_cnt, update_frequency)
|
||||
update_stat(cr, writes, write_byte_cnt, update_frequency)
|
||||
end
|
||||
|
||||
local draw = function(cr, current_interface, update_frequency)
|
||||
update(cr, update_frequency)
|
||||
|
||||
if current_interface == 0 then
|
||||
Text.draw(header.text, cr)
|
||||
Line.draw(header.underline, cr)
|
||||
|
||||
Text.draw(reads.label, cr)
|
||||
Text.draw(reads.rate, cr)
|
||||
ScalePlot.draw(reads.plot, cr)
|
||||
|
||||
Text.draw(writes.label, cr)
|
||||
Text.draw(writes.rate, cr)
|
||||
ScalePlot.draw(writes.plot, cr)
|
||||
end
|
||||
end
|
||||
|
||||
return draw
|
|
@ -1,67 +0,0 @@
|
|||
local Widget = require 'Widget'
|
||||
local Text = require 'Text'
|
||||
local Line = require 'Line'
|
||||
local TextColumn = require 'TextColumn'
|
||||
local util = require 'util'
|
||||
local schema = require 'default_patterns'
|
||||
|
||||
local __string_match = string.match
|
||||
|
||||
local _TEXT_SPACING_ = 20
|
||||
|
||||
local extract_date = function(cmd)
|
||||
local yyyy, mm_dd = __string_match(util.execute_cmd(cmd), '%[(%d-)%-(%d-%-%d-)%s')
|
||||
return mm_dd..'-'..yyyy
|
||||
end
|
||||
|
||||
local header = Widget.Header{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _G_INIT_DATA_.TOP_Y,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
header = 'SYSTEM'
|
||||
}
|
||||
|
||||
local labels = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = header.bottom_y,
|
||||
spacing = _TEXT_SPACING_,
|
||||
'Kernel',
|
||||
'Uptime',
|
||||
'Last Upgrade',
|
||||
'Last Sync'
|
||||
}
|
||||
local info = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.LEFT_X + _G_INIT_DATA_.SECTION_WIDTH,
|
||||
y = header.bottom_y,
|
||||
spacing = _TEXT_SPACING_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
util.conky('$kernel'),
|
||||
'<row2>',
|
||||
'<row3>',
|
||||
'<row4>'
|
||||
}
|
||||
|
||||
Widget = nil
|
||||
schema = nil
|
||||
_TEXT_SPACING_ = nil
|
||||
|
||||
local draw = function(cr, current_interface, log_is_changed)
|
||||
TextColumn.set(info, cr, 2, util.conky('$uptime'))
|
||||
|
||||
if log_is_changed then
|
||||
TextColumn.set(info, cr, 3, extract_date("sed -n "..
|
||||
"'/ starting full system upgrade/p' /var/log/pacman.log | tail -1"))
|
||||
TextColumn.set(info, cr, 4, extract_date("sed -n "..
|
||||
"'/ synchronizing package lists/p' /var/log/pacman.log | tail -1"))
|
||||
end
|
||||
|
||||
if current_interface == 0 then
|
||||
Text.draw(header.text, cr)
|
||||
Line.draw(header.underline, cr)
|
||||
TextColumn.draw(labels, cr)
|
||||
TextColumn.draw(info, cr)
|
||||
end
|
||||
end
|
||||
|
||||
return draw
|
|
@ -1,482 +0,0 @@
|
|||
local Widget = require 'Widget'
|
||||
local Text = require 'Text'
|
||||
local Line = require 'Line'
|
||||
local TextColumn = require 'TextColumn'
|
||||
local ScaledImage = require 'ScaledImage'
|
||||
local util = require 'util'
|
||||
local json = require 'json'
|
||||
local schema = require 'default_patterns'
|
||||
|
||||
local __string_match = string.match
|
||||
local __string_sub = string.sub
|
||||
local __string_upper = string.upper
|
||||
local __os_execute = os.execute
|
||||
|
||||
local TIME_FORMAT = '%-I:%M %p'
|
||||
local DATE_FORMAT = '%A'
|
||||
|
||||
local NUM_ROWS = 8
|
||||
local WEATHER_UPDATE_INTERVAL = 900
|
||||
|
||||
local WEATHER_JSON_PATH = '/tmp/weather.json'
|
||||
local ICON_DIR_PATH = _G_INIT_DATA_.ABS_PATH .. '/images/weather/'
|
||||
local RECENTLY_UPDATED_PATH = '/tmp/weather_recently_updated'
|
||||
local NA = 'N/A'
|
||||
local NA_IMAGE_PATH = ICON_DIR_PATH .. 'na.png'
|
||||
|
||||
local _SPACING_ = 20
|
||||
local _HEADER_PAD_ = 20
|
||||
local _ICON_SIDE_LENGTH_ = 75
|
||||
local _TEMP_SECTION_WIDTH_ = 220
|
||||
local _SECTION_HEIGHT_ = _HEADER_PAD_ + _ICON_SIDE_LENGTH_ + 30
|
||||
|
||||
local create_side_rows = function(side_rows_x, side_rows_y, side_rows_tbl)
|
||||
for i = 1, NUM_ROWS do
|
||||
side_rows_tbl[i] = {}
|
||||
local current_row = side_rows_tbl[i]
|
||||
local current_row_y = side_rows_y + (i - 1) * _SECTION_HEIGHT_
|
||||
|
||||
current_row.desc = Widget.Text{
|
||||
x = side_rows_x,
|
||||
y = current_row_y,
|
||||
text_color = schema.BLUE,
|
||||
}
|
||||
|
||||
current_row.period = Widget.Text{
|
||||
x = side_rows_x + _G_INIT_DATA_.SECTION_WIDTH,
|
||||
y = current_row_y,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE
|
||||
}
|
||||
|
||||
current_row.icon = Widget.ScaledImage{
|
||||
x = side_rows_x,
|
||||
y = current_row_y + _HEADER_PAD_,
|
||||
width = _ICON_SIDE_LENGTH_,
|
||||
height = _ICON_SIDE_LENGTH_
|
||||
}
|
||||
|
||||
current_row.temp1 = Widget.Text{
|
||||
x = side_rows_x + _ICON_SIDE_LENGTH_ + _TEMP_SECTION_WIDTH_ / 2,
|
||||
y = current_row_y + _HEADER_PAD_ + 25,
|
||||
x_align = 'center',
|
||||
font_size = 28,
|
||||
text_color = schema.BLUE
|
||||
}
|
||||
|
||||
current_row.temp2 = Widget.Text{
|
||||
x = side_rows_x + _ICON_SIDE_LENGTH_ + _TEMP_SECTION_WIDTH_ / 2,
|
||||
y = current_row_y + _HEADER_PAD_ + 55,
|
||||
x_align = 'center',
|
||||
font_size = 11
|
||||
}
|
||||
|
||||
current_row.label_column = Widget.TextColumn{
|
||||
x = side_rows_x + _ICON_SIDE_LENGTH_ + _TEMP_SECTION_WIDTH_,
|
||||
y = current_row_y + _HEADER_PAD_ + 15,
|
||||
spacing = _SPACING_,
|
||||
'H',
|
||||
'P',
|
||||
'W'
|
||||
}
|
||||
|
||||
current_row.info_column = Widget.TextColumn{
|
||||
x = side_rows_x + _G_INIT_DATA_.SECTION_WIDTH,
|
||||
y = current_row_y + _HEADER_PAD_ + 15,
|
||||
spacing = _SPACING_,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
num_rows = 3
|
||||
}
|
||||
|
||||
if i < NUM_ROWS then
|
||||
current_row.separator = Widget.Line{
|
||||
p1 = {
|
||||
x = side_rows_x,
|
||||
y = current_row_y + _SECTION_HEIGHT_ - 18
|
||||
},
|
||||
p2 = {
|
||||
x = side_rows_x + _G_INIT_DATA_.SECTION_WIDTH,
|
||||
y = current_row_y + _SECTION_HEIGHT_ - 18
|
||||
},
|
||||
line_pattern = schema.MID_GREY
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- LEFT
|
||||
local left = {
|
||||
header = Widget.Header{
|
||||
x = _G_INIT_DATA_.LEFT_X,
|
||||
y = _G_INIT_DATA_.TOP_Y,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
header = 'HOURLY FORECAST'
|
||||
},
|
||||
hours = {}
|
||||
}
|
||||
|
||||
create_side_rows(_G_INIT_DATA_.LEFT_X, left.header.bottom_y, left.hours)
|
||||
|
||||
-- CENTER
|
||||
local center = {}
|
||||
|
||||
center.header = Widget.Header{
|
||||
x = _G_INIT_DATA_.CENTER_LEFT_X,
|
||||
y = _G_INIT_DATA_.TOP_Y,
|
||||
width = _G_INIT_DATA_.CENTER_WIDTH,
|
||||
header = 'CURRENT CONDITIONS'
|
||||
}
|
||||
|
||||
center.current_desc = Widget.Text{
|
||||
x = _G_INIT_DATA_.CENTER_LEFT_X,
|
||||
y = center.header.bottom_y + 8,
|
||||
text_color = schema.BLUE,
|
||||
font_size = 24
|
||||
}
|
||||
|
||||
local _CENTER_X_1_ = _G_INIT_DATA_.CENTER_LEFT_X + _G_INIT_DATA_.SECTION_WIDTH * 0.25
|
||||
local _CENTER_ICON_WIDTH_ = 120
|
||||
|
||||
center.icon = Widget.ScaledImage{
|
||||
x = _CENTER_X_1_ - _CENTER_ICON_WIDTH_ / 2,
|
||||
y = center.header.bottom_y + 105 - _CENTER_ICON_WIDTH_ / 2,
|
||||
width = _CENTER_ICON_WIDTH_,
|
||||
height = _CENTER_ICON_WIDTH_
|
||||
}
|
||||
|
||||
local _CENTER_X_2_ = _G_INIT_DATA_.CENTER_LEFT_X + _G_INIT_DATA_.SECTION_WIDTH * 0.70
|
||||
local _INFO_Y_ = center.header.bottom_y + 70
|
||||
|
||||
center.current_temp = Widget.Text{
|
||||
x = _CENTER_X_2_,
|
||||
y = _INFO_Y_,
|
||||
x_align = 'center',
|
||||
font_size = 48,
|
||||
text_color = schema.BLUE
|
||||
}
|
||||
|
||||
center.obs_time = Widget.Text{
|
||||
x = _CENTER_X_2_,
|
||||
y = _INFO_Y_ + 42,
|
||||
x_align = 'center',
|
||||
font_size = 12,
|
||||
}
|
||||
|
||||
center.place = Widget.Text{
|
||||
x = _CENTER_X_2_,
|
||||
y = _INFO_Y_ + 66,
|
||||
x_align = 'center',
|
||||
font_size = 12,
|
||||
}
|
||||
|
||||
local _COLUMN_PADDING_ = 15
|
||||
local _CENTER_SPACING_ = _SPACING_ + 7
|
||||
|
||||
center.label_column_1 = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.CENTER_RIGHT_X,
|
||||
y = center.header.bottom_y,
|
||||
spacing = _CENTER_SPACING_,
|
||||
font_size = 14,
|
||||
'Feels Like',
|
||||
'Dewpoint',
|
||||
'Humidity',
|
||||
'Sky Coverage',
|
||||
'Visibility',
|
||||
'Ceiling',
|
||||
'Precipitation'
|
||||
}
|
||||
|
||||
center.info_column_1 = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.CENTER_RIGHT_X + (_G_INIT_DATA_.SECTION_WIDTH - _COLUMN_PADDING_) / 2,
|
||||
y = center.header.bottom_y,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
spacing = _CENTER_SPACING_,
|
||||
font_size = 14,
|
||||
num_rows = 7
|
||||
}
|
||||
|
||||
center.label_column_2 = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.CENTER_RIGHT_X + (_G_INIT_DATA_.SECTION_WIDTH + _COLUMN_PADDING_) / 2,
|
||||
y = center.header.bottom_y,
|
||||
spacing = _CENTER_SPACING_,
|
||||
font_size = 14,
|
||||
'WindSpd',
|
||||
'WindGust',
|
||||
'WindDir',
|
||||
'Pressure',
|
||||
'Sunrise',
|
||||
'Sunset',
|
||||
'Light Rate'
|
||||
}
|
||||
|
||||
center.info_column_2 = Widget.TextColumn{
|
||||
x = _G_INIT_DATA_.CENTER_RIGHT_X + _G_INIT_DATA_.SECTION_WIDTH,
|
||||
y = center.header.bottom_y,
|
||||
x_align = 'right',
|
||||
text_color = schema.BLUE,
|
||||
spacing = _CENTER_SPACING_,
|
||||
font_size = 14,
|
||||
num_rows = 7
|
||||
}
|
||||
|
||||
-- RIGHT
|
||||
local right = {
|
||||
header = Widget.Header{
|
||||
x = _G_INIT_DATA_.RIGHT_X,
|
||||
y = _G_INIT_DATA_.TOP_Y,
|
||||
width = _G_INIT_DATA_.SECTION_WIDTH,
|
||||
header = '8 DAY FORECAST'
|
||||
},
|
||||
days = {}
|
||||
}
|
||||
|
||||
create_side_rows(_G_INIT_DATA_.RIGHT_X, right.header.bottom_y, right.days)
|
||||
|
||||
Widget = nil
|
||||
schema = nil
|
||||
|
||||
_SPACING_ = nil
|
||||
_HEADER_PAD_ = nil
|
||||
_ICON_SIDE_LENGTH_ = nil
|
||||
_TEMP_SECTION_WIDTH_ = nil
|
||||
_SECTION_HEIGHT_ = nil
|
||||
_CENTER_X_1_ = nil
|
||||
_CENTER_ICON_WIDTH_ = nil
|
||||
_CENTER_X_2_ = nil
|
||||
_INFO_Y_ = nil
|
||||
_COLUMN_PADDING_ = nil
|
||||
_CENTER_SPACING_ = nil
|
||||
|
||||
local populate_row = function(current_section, cr, desc, period, icon_path,
|
||||
temp1, temp2, humidity, pop, wind)
|
||||
if desc then
|
||||
Text.set(current_section.desc, cr, Text.trim_to_length(desc, 20))
|
||||
else
|
||||
Text.set(current_section.desc, cr, NA)
|
||||
end
|
||||
|
||||
Text.set(current_section.period, cr, period or NA)
|
||||
|
||||
ScaledImage.set(current_section.icon, icon_path or NA_IMAGE_PATH)
|
||||
|
||||
Text.set(current_section.temp1, cr, temp1 or NA)
|
||||
Text.set(current_section.temp2, cr, temp2 or NA)
|
||||
|
||||
TextColumn.set(current_section.info_column, cr, 1, humidity or NA)
|
||||
TextColumn.set(current_section.info_column, cr, 2, pop or NA)
|
||||
TextColumn.set(current_section.info_column, cr, 3, wind or NA)
|
||||
end
|
||||
|
||||
local populate_center = function(center_section, cr, desc, icon_path, temp,
|
||||
obs_time, place, feels_like, dewpoint, humidity, coverage, visibility, ceiling,
|
||||
precip, wind_spd, wind_gust_spd, wind_dir, pressure, sunrise, sunset, light)
|
||||
|
||||
if desc then
|
||||
Text.set(center_section.current_desc, cr, Text.trim_to_length(desc, 20))
|
||||
else
|
||||
Text.set(center_section.current_desc, cr, NA)
|
||||
end
|
||||
|
||||
ScaledImage.set(center_section.icon, icon_path or NA_IMAGE_PATH)
|
||||
|
||||
Text.set(center_section.current_temp, cr, temp or NA)
|
||||
Text.set(center_section.obs_time, cr, obs_time or NA)
|
||||
Text.set(center_section.place, cr, place or NA)
|
||||
|
||||
local info_column_1 = center_section.info_column_1
|
||||
|
||||
TextColumn.set(info_column_1, cr, 1, feels_like or NA)
|
||||
TextColumn.set(info_column_1, cr, 2, dewpoint or NA)
|
||||
TextColumn.set(info_column_1, cr, 3, humidity or NA)
|
||||
TextColumn.set(info_column_1, cr, 4, coverage or NA)
|
||||
TextColumn.set(info_column_1, cr, 5, visibility or NA)
|
||||
TextColumn.set(info_column_1, cr, 6, ceiling or NA)
|
||||
TextColumn.set(info_column_1, cr, 7, precip or NA)
|
||||
|
||||
local info_column_2 = center_section.info_column_2
|
||||
|
||||
TextColumn.set(info_column_2, cr, 1, wind_spd or NA)
|
||||
TextColumn.set(info_column_2, cr, 2, wind_gust_spd or NA)
|
||||
TextColumn.set(info_column_2, cr, 3, wind_dir or NA)
|
||||
TextColumn.set(info_column_2, cr, 4, pressure or NA)
|
||||
TextColumn.set(info_column_2, cr, 5, sunrise or NA)
|
||||
TextColumn.set(info_column_2, cr, 6, sunset or NA)
|
||||
TextColumn.set(info_column_2, cr, 7, light or NA)
|
||||
end
|
||||
|
||||
local update_interface = function(cr)
|
||||
local file = util.read_file(WEATHER_JSON_PATH)
|
||||
local data = (file ~= '') and json.decode(file)
|
||||
|
||||
if data then
|
||||
data = data.response.responses
|
||||
|
||||
if data[1].success == false then
|
||||
for i = 1, NUM_ROWS do populate_row(left.hours[i], cr) end
|
||||
|
||||
populate_center(center, cr, nil, nil, nil, nil, 'Invalid Location')
|
||||
|
||||
for i = 1, NUM_ROWS do populate_row(right.days[i], cr) end
|
||||
else
|
||||
-- LEFT
|
||||
local hourly = data[2].response[1].periods
|
||||
|
||||
for i = 1, NUM_ROWS do
|
||||
local hour_data = hourly[i]
|
||||
|
||||
populate_row(
|
||||
left.hours[i],
|
||||
cr,
|
||||
hour_data.weatherPrimary,
|
||||
hour_data.timestamp and util.convert_unix_time(hour_data.timestamp, TIME_FORMAT),
|
||||
hour_data.icon and ICON_DIR_PATH..hour_data.icon,
|
||||
hour_data.avgTempF and hour_data.avgTempF..'°F',
|
||||
hour_data.feelslikeF and 'Feels like '..hour_data.feelslikeF..'°F',
|
||||
hour_data.humidity and hour_data.humidity..' %',
|
||||
hour_data.pop and hour_data.pop..' %',
|
||||
hour_data.windSpeedMPH and hour_data.windSpeedMPH..' mph'
|
||||
)
|
||||
end
|
||||
|
||||
-- CENTER
|
||||
local current_data = data[1].response
|
||||
local ob = current_data.ob
|
||||
|
||||
local place
|
||||
if current_data.place then
|
||||
place = current_data.place.name
|
||||
if place then place = util.capitalize_each_word(__string_match(place, '([%w%s]+)/?')) end
|
||||
|
||||
local state = current_data.place.state
|
||||
if state == '' then state = nil end
|
||||
|
||||
if place and state then
|
||||
place = place..', '..__string_upper(state)
|
||||
elseif place then
|
||||
local country = current_data.place.country
|
||||
if country then place = place..', '..__string_upper(country) end
|
||||
end
|
||||
end
|
||||
|
||||
populate_center(
|
||||
center,
|
||||
cr,
|
||||
ob.weather,
|
||||
ob.icon and ICON_DIR_PATH..ob.icon,
|
||||
ob.tempF and ob.tempF..'°F',
|
||||
ob.timestamp and util.convert_unix_time(ob.timestamp, TIME_FORMAT),
|
||||
place,
|
||||
ob.feelslikeF and ob.feelslikeF..'°F',
|
||||
ob.dewpointF and ob.dewpointF..'°F',
|
||||
ob.humidity and ob.humidity..' %',
|
||||
ob.sky and ob.sky..' %',
|
||||
ob.visibilityMI and ob.visibilityMI..' mi',
|
||||
ob.ceilingFT and ob.ceilingFT..' ft',
|
||||
ob.precipIN and ob.precipIN..' in',
|
||||
ob.windSpeedMPH and ob.windSpeedMPH..' mph',
|
||||
ob.windGustMPH and ob.windGustMPH..' mph',
|
||||
ob.windDirDEG and ob.windDirDEG..' deg',
|
||||
ob.pressureMB and ob.pressureMB..' mbar',
|
||||
ob.sunrise and util.convert_unix_time(ob.sunrise, TIME_FORMAT),
|
||||
ob.sunset and util.convert_unix_time(ob.sunset, TIME_FORMAT),
|
||||
ob.light and ob.light..' %'
|
||||
)
|
||||
|
||||
-- RIGHT
|
||||
local daily = data[3].response[1].periods
|
||||
|
||||
for i = 1, NUM_ROWS do
|
||||
local day_data = daily[i]
|
||||
|
||||
populate_row(
|
||||
right.days[i],
|
||||
cr,
|
||||
day_data.weatherPrimary,
|
||||
day_data.timestamp and __string_sub(util.convert_unix_time(
|
||||
day_data.timestamp, DATE_FORMAT), 1, 3),
|
||||
day_data.icon and ICON_DIR_PATH..day_data.icon,
|
||||
day_data.maxTempF and day_data.maxTempF..'°F',
|
||||
day_data.minTempF and 'Low of '..day_data.minTempF..'°F',
|
||||
day_data.humidity and day_data.humidity..' %',
|
||||
day_data.pop and day_data.pop..' %',
|
||||
day_data.windSpeedMPH and day_data.windSpeedMPH..' mph'
|
||||
)
|
||||
end
|
||||
end
|
||||
else
|
||||
for i = 1, NUM_ROWS do
|
||||
populate_row(left.hours[i], cr)
|
||||
populate_row(right.days[i], cr)
|
||||
end
|
||||
|
||||
populate_center(center, cr)
|
||||
end
|
||||
end
|
||||
|
||||
local draw_sections = function(section_group, cr)
|
||||
for i = 1, NUM_ROWS do
|
||||
local section = section_group[i]
|
||||
|
||||
if i < NUM_ROWS then Line.draw(section.separator, cr) end
|
||||
|
||||
Text.draw(section.desc, cr)
|
||||
Text.draw(section.period, cr)
|
||||
ScaledImage.draw(section.icon)
|
||||
Text.draw(section.temp1, cr)
|
||||
Text.draw(section.temp2, cr)
|
||||
TextColumn.draw(section.label_column, cr)
|
||||
TextColumn.draw(section.info_column, cr)
|
||||
end
|
||||
end
|
||||
|
||||
__os_execute('get_weather.sh')
|
||||
|
||||
local update_cycle = WEATHER_UPDATE_INTERVAL
|
||||
|
||||
local draw = function(cr, interface, interface_is_changed)
|
||||
if update_cycle == 0 then __os_execute('get_weather.sh') end
|
||||
|
||||
local json_is_recently_updated = (util.read_file(RECENTLY_UPDATED_PATH, nil, '*l') == 'true')
|
||||
|
||||
if json_is_recently_updated then
|
||||
update_cycle = WEATHER_UPDATE_INTERVAL
|
||||
util.write_file(RECENTLY_UPDATED_PATH, 'false')
|
||||
end
|
||||
|
||||
if json_is_recently_updated or interface_is_changed then update_interface(cr) end
|
||||
|
||||
update_cycle = update_cycle - 1
|
||||
|
||||
if interface == 1 then
|
||||
-- LEFT
|
||||
Text.draw(left.header.text, cr)
|
||||
Line.draw(left.header.underline, cr)
|
||||
|
||||
draw_sections(left.hours, cr)
|
||||
|
||||
-- CENTER
|
||||
Text.draw(center.header.text, cr)
|
||||
Line.draw(center.header.underline, cr)
|
||||
|
||||
Text.draw(center.current_desc, cr)
|
||||
ScaledImage.draw(center.icon)
|
||||
Text.draw(center.current_temp, cr)
|
||||
Text.draw(center.obs_time, cr)
|
||||
Text.draw(center.place, cr)
|
||||
|
||||
TextColumn.draw(center.label_column_1, cr)
|
||||
TextColumn.draw(center.info_column_1, cr)
|
||||
TextColumn.draw(center.label_column_2, cr)
|
||||
TextColumn.draw(center.info_column_2, cr)
|
||||
|
||||
-- RIGHT
|
||||
Text.draw(right.header.text, cr)
|
||||
Line.draw(right.header.underline, cr)
|
||||
|
||||
draw_sections(right.days, cr)
|
||||
end
|
||||
end
|
||||
|
||||
return draw
|
|
@ -1,68 +0,0 @@
|
|||
local M = {}
|
||||
|
||||
local Color = require 'Color'
|
||||
|
||||
local WHITE = 0xffffffff
|
||||
|
||||
local GREY1 = 0xeeeeeeff
|
||||
local GREY2 = 0xbfbfbfff
|
||||
local GREY3 = 0xd6d6d6ff
|
||||
local GREY4 = 0x888888ff
|
||||
local GREY5 = 0x565656ff
|
||||
local GREY6 = 0x2f2f2fb2
|
||||
local BLACK = 0x000000ff
|
||||
|
||||
local BLUE1 = 0x99CEFFff
|
||||
local BLUE2 = 0xBFE1FFff
|
||||
local BLUE3 = 0x316BA6ff
|
||||
|
||||
local RED1 = 0xFF3333ff
|
||||
local RED2 = 0xFF8282ff
|
||||
local RED3 = 0xFFB8B8ff
|
||||
|
||||
local PURPLE1 = 0xeecfffff
|
||||
local PURPLE2 = 0xcb91ffff
|
||||
local PURPLE3 = 0x9523ffff
|
||||
|
||||
M.WHITE = Color.init{hex_rgba = WHITE}
|
||||
|
||||
M.LIGHT_GREY = Color.init{hex_rgba = GREY1}
|
||||
M.MID_GREY = Color.init{hex_rgba = GREY3}
|
||||
M.DARK_GREY = Color.init{hex_rgba = GREY4}
|
||||
|
||||
M.BLUE = Color.init{hex_rgba = BLUE2}
|
||||
M.RED = Color.init{hex_rgba = RED2}
|
||||
M.PURPLE = Color.init{hex_rgba = PURPLE2}
|
||||
|
||||
M.GREY_ROUNDED = Color.Gradient{
|
||||
Color.ColorStop{hex_rgba = GREY5, stop = 0.0},
|
||||
Color.ColorStop{hex_rgba = GREY2, stop = 0.5},
|
||||
Color.ColorStop{hex_rgba = GREY5, stop = 1.0}
|
||||
}
|
||||
|
||||
M.BLUE_ROUNDED = Color.Gradient{
|
||||
Color.ColorStop{hex_rgba = BLUE3, stop = 0.0},
|
||||
Color.ColorStop{hex_rgba = BLUE1, stop = 0.5},
|
||||
Color.ColorStop{hex_rgba = BLUE3, stop = 1.0}
|
||||
}
|
||||
|
||||
M.RED_ROUNDED = Color.Gradient{
|
||||
Color.ColorStop{hex_rgba = RED1, stop = 0.0},
|
||||
Color.ColorStop{hex_rgba = RED3, stop = 0.5},
|
||||
Color.ColorStop{hex_rgba = RED1, stop = 1.0}
|
||||
}
|
||||
|
||||
M.PURPLE_ROUNDED = Color.Gradient{
|
||||
Color.ColorStop{hex_rgba = PURPLE3, stop = 0.0},
|
||||
Color.ColorStop{hex_rgba = PURPLE1, stop = 0.5},
|
||||
Color.ColorStop{hex_rgba = PURPLE3, stop = 1.0}
|
||||
}
|
||||
|
||||
M.TRANSPARENT_BLACK = Color.init{hex_rgba = BLACK, force_alpha = 0.7}
|
||||
|
||||
M.TRANSPARENT_BLUE = Color.Gradient{
|
||||
Color.ColorStop{hex_rgba = BLUE3, stop = 0.0, force_alpha = 0.2},
|
||||
Color.ColorStop{hex_rgba = BLUE1, stop = 1.0, force_alpha = 1.0}
|
||||
}
|
||||
|
||||
return M
|
Loading…
Reference in New Issue