0d2c9c2e58d08d44341fccd70f4f14fb737ee2ac
[delightful.git] / delightful / widgets / memory.lua
1 -------------------------------------------------------------------------------
2 --
3 -- Memory widget for Awesome 3.5
4 -- Copyright (C) 2011-2016 Tuomas Jormola <tj@solitudo.net>
5 --
6 -- Licensed under the terms of GNU General Public License Version 2.0.
7 --
8 -- Description:
9 --
10 -- Displays memory and swap usage statistics as a vertical progress bars.
11 -- Also displays a memory icon next to the memory graph. Clicking the icon
12 -- launches an external application (if configured).
13 --
14 -- Widget extends vicious.widgets.mem from Vicious widget framework.
15 --
16 -- Widget tries to use an icon from the package mate-sensors-applet-common
17 -- if available.
18 --
19 --
20 -- Configuration:
21 --
22 -- The load() function can be supplied with configuration.
23 -- Format of the configuration is as follows.
24 -- {
25 -- -- Command to execute when left-clicking the widget icon.
26 -- -- Empty by default.
27 --            command         = 'gnome-system-monitor',
28 -- -- Don't try to display any icons. Default is false (i.e. display icons).
29 --        no_icon         = true,
30 -- -- How often update the widget data. Default is 10 second.
31 --        update_interval = 20
32 -- }
33 --
34 --
35 -- Theme:
36 --
37 -- The widget uses following colors and icons if available in
38 -- the Awesome theme.
39 --
40 -- theme.bg_widget        - widget background color
41 -- theme.fg_widget        - widget foreground color
42 -- theme.fg_center_widget - widget gradient color, middle
43 -- theme.fg_end_widget    - widget gradient color, end
44 -- theme.delightful_mem   - icon shown next to the memory progress bars
45 --
46 -------------------------------------------------------------------------------
47
48 local awful      = require('awful')
49 local wibox      = require('wibox')
50
51 local delightful = { utils = require('delightful.utils') }
52 local vicious    = require('vicious')
53
54 local io       = { lines = io.lines }
55 local math     = { floor = math.floor }
56 local pairs    = pairs
57 local string   = { format = string.format }
58 local table    = { insert = table.insert }
59 local tonumber = tonumber
60
61 module('delightful.widgets.memory')
62
63 -- Helper
64 function is_swap_available()
65         for line in io.lines('/proc/meminfo') do
66                 local total_swap = tonumber(line:match('^SwapTotal:%s+(%d+)%s+kB4'))
67                 if total_swap and total_swap > 0 then
68                         return true
69                 end
70         end
71         return false
72 end
73
74 local memory_config
75 local fatal_error
76 local icon_tooltip
77 local has_swap = is_swap_available()
78
79 local config_description = {
80         {
81                 name     = 'command',
82                 validate = function(value) return delightful.utils.config_string(value) end
83         },
84         {
85                 name     = 'no_icon',
86                 validate = function(value) return delightful.utils.config_boolean(value) end
87         },
88         {
89                 name     = 'update_interval',
90                 required = true,
91                 default  = 10,
92                 validate = function(value) return delightful.utils.config_int(value) end
93         },
94 }
95
96 local icon_description = {
97         memory = { beautiful_name = 'delightful_mem', default_icon = 'mate-sensors-applet-memory' },
98 }
99
100 -- Configuration handler
101 function handle_config(user_config)
102         local empty_config = delightful.utils.get_empty_config(config_description)
103         if not user_config then
104                 user_config = empty_config
105         end
106         local config_data = delightful.utils.normalize_config(user_config, config_description)
107         local validation_errors = delightful.utils.validate_config(config_data, config_description)
108         if validation_errors then
109                 fatal_error = 'Configuration errors: \n'
110                 for error_index, error_entry in pairs(validation_errors) do
111                         fatal_error = string.format('%s %s', fatal_error, error_entry)
112                         if error_index < #validation_errors then
113                                 fatal_error = string.format('%s \n', fatal_error)
114                         end
115                 end
116                 memory_config = empty_config
117                 return
118         end
119         memory_config = config_data
120 end
121
122 -- Initalization
123 function load(self, config)
124         handle_config(config)
125         if fatal_error then
126                 delightful.utils.print_error('memory', fatal_error)
127                 return nil, nil
128         end
129         local icon
130         local icon_files
131         if not memory_config.no_icon then
132                 icon_files = delightful.utils.find_icon_files(icon_description)
133         end
134         local icon_file = icon_files and icon_files.memory
135         if icon_file then
136                 local buttons = awful.button({}, 1, function()
137                         if not fatal_error and memory_config.command then
138                                 awful.util.spawn(memory_config.command, true)
139                         end
140                 end)
141                 icon = wibox.widget.imagebox()
142                 icon:buttons(buttons)
143                 icon:set_image(icon_file)
144                 icon_tooltip = awful.tooltip({ objects = { icon } })
145         end
146         local icons = { icon }
147
148         local bg_color        = delightful.utils.find_theme_color({ 'bg_widget', 'bg_normal'                     })
149         local fg_color        = delightful.utils.find_theme_color({ 'fg_widget', 'fg_normal'                     })
150         local fg_center_color = delightful.utils.find_theme_color({ 'fg_center_widget', 'fg_widget', 'fg_normal' })
151         local fg_end_color    = delightful.utils.find_theme_color({ 'fg_end_widget', 'fg_widget', 'fg_normal'    })
152
153         local memory_widget = awful.widget.progressbar()
154         if bg_color then
155                 memory_widget:set_background_color(bg_color)
156                 memory_widget:set_border_color(bg_color)
157         end
158         local color_args = fg_color
159         local width  = 8
160         local height = 19
161         if fg_color and fg_center_color and fg_end_color then
162                 color_args = {
163                         type = 'linear',
164                         from = { 0, 0 },
165                         to = { width, height },
166                         stops = {{ 0, fg_end_color }, { 0.5, fg_center_color }, { 1, fg_color }},
167                 }
168         end
169         memory_widget:set_color(color_args)
170         memory_widget:set_width(width)
171         memory_widget:set_height(height)
172         memory_widget:set_vertical(true)
173         vicious.register(memory_widget, vicious.widgets.mem, vicious_formatter_memory, memory_config.update_interval)
174         local widgets = { memory_widget }
175
176
177         if has_swap then
178                 local swap_widget = awful.widget.progressbar()
179                 if bg_color then
180                         swap_widget:set_background_color(bg_color)
181                         swap_widget:set_border_color(bg_color)
182                 end
183                 swap_widget:set_color(color_args)
184                 swap_widget:set_width(width)
185                 swap_widget:set_height(height)
186                 swap_widget:set_vertical(true)
187                 vicious.register(swap_widget, vicious.widgets.mem, vicious_formatter_swap, memory_config.update_interval)
188                 table.insert(widgets, swap_widget)
189         end
190
191         vicious.cache(vicious.widgets.mem)
192
193         return widgets, icons
194 end
195
196 -- Vicious display formatter for memory graph, also update widget tooltip
197 function vicious_formatter_memory(widget, data)
198         local memory_percentage = math.floor(((data[2] / data[3]) * 100) + 0.5)
199         if icon_tooltip then
200                 local swap_string = ''
201                 if has_swap then
202                         swap_string = 'and swap '
203                 end
204                 local tooltip_text = string.format(' Memory %susage statistics \n Current memory usage: %d%% (%dMB out of %dMB) ', swap_string, memory_percentage, data[2], data[3])
205                 if has_swap then
206                         local swap_percentage = math.floor(((data[6] / data[7]) * 100) + 0.5)
207                         tooltip_text = string.format('%s\n Current swap usage: %d%% (%dMB out of %dMB) ', tooltip_text, swap_percentage, data[6], data[7])
208                 end
209                 icon_tooltip:set_text(tooltip_text)
210         end
211         return memory_percentage
212 end
213
214 -- Vicious display formatter for swap graph
215 function vicious_formatter_swap(widget, data)
216         return (data[6] / data[7]) * 100
217 end