aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBasil L. Contovounesios2020-06-11 13:48:37 +0100
committerBasil L. Contovounesios2020-06-18 12:58:28 +0100
commit23a148c9506f2a5bce71bd5c8822bb7cde6697e8 (patch)
tree560e8278331540cfdbd6b30073f7de8f40f631da
parent0185d76e7426eb1b58a9b60b0d18e763ddf57dea (diff)
downloademacs-23a148c9506f2a5bce71bd5c8822bb7cde6697e8.tar.gz
emacs-23a148c9506f2a5bce71bd5c8822bb7cde6697e8.zip
Various battery.el improvements (bug#41808)
* lisp/battery.el: Mention BSD support in Commentary. Don't load preloaded lisp/emacs-lisp/timer.el. (battery--files): New function. (battery--find-linux-sysfs-batteries): Use it and make fewer syscalls. (battery-status-function): Perform GNU/Linux checks in increasing order of obsolescence: sysfs, ACPI, and then APM. Simplify Darwin check. Add :version tag now that battery-upower is the default. (battery-echo-area-format, battery-mode-line-format): Mention %s. (battery-load-low, battery-load-critical): New faces. (battery-update): Display battery-mode-line-format even if percentage is N/A. Apply faces battery-load-low or battery-load-critical according to the percentage, but append them so they don't override user customizations. Update all mode lines since we are in global-mode-string. (battery-linux-proc-apm-regexp): Mark as obsolete, replacing with... (battery--linux-proc-apm): ...this new rx definition. (battery-linux-proc-apm): Use it. Fix indentation. Simplify. (battery--acpi-rate, battery--acpi-capacity): New rx definitions. (battery-linux-proc-acpi): Use them. Fix pathological whitespace regexps. Simplify. (battery-linux-sysfs): Fix docstring and indentation. Reduce number of file searches. Simplify. (battery-bsd-apm): Fix docstring. Simplify. (battery-pmset): Fix docstring. Simplify ID regexp. * lisp/emacs-lisp/rx.el (rx-define): Indent as a defun. * test/lisp/battery-tests.el (battery-linux-proc-apm-regexp): Test new battery--linux-proc-apm rx definition. (battery-acpi-rate-regexp, battery-acpi-capacity-regexp): New tests.
-rw-r--r--lisp/battery.el363
-rw-r--r--lisp/emacs-lisp/rx.el2
-rw-r--r--test/lisp/battery-tests.el39
3 files changed, 229 insertions, 175 deletions
diff --git a/lisp/battery.el b/lisp/battery.el
index 38728196507..f6f70b2f16d 100644
--- a/lisp/battery.el
+++ b/lisp/battery.el
@@ -23,18 +23,18 @@
23 23
24;;; Commentary: 24;;; Commentary:
25 25
26;; There is at present support for GNU/Linux, macOS, and Windows. 26;; There is at present support for GNU/Linux, BSD, macOS, and Windows.
27;; This library supports: 27;; This library supports:
28;; - UPower (https://upower.freedesktop.org) via D-Bus API. 28;; - UPower (https://upower.freedesktop.org) via D-Bus API.
29;; - the `/sys/class/power_supply/' files of Linux >= 2.6.39. 29;; - The `/sys/class/power_supply/' files of Linux >= 2.6.39.
30;; - the `/proc/acpi/' directory structure of Linux 2.4.20 and 2.6. 30;; - The `/proc/acpi/' directory structure of Linux 2.4.20 and 2.6.
31;; - the `/proc/apm' file format of Linux version 1.3.58 or newer. 31;; - The `/proc/apm' file format of Linux version 1.3.58 or newer.
32;; - BSD by using the `apm' program.
32;; - Darwin (macOS) by using the `pmset' program. 33;; - Darwin (macOS) by using the `pmset' program.
33;; - Windows via the GetSystemPowerStatus API call. 34;; - Windows via the GetSystemPowerStatus API call.
34 35
35;;; Code: 36;;; Code:
36 37
37(require 'timer)
38(require 'dbus) 38(require 'dbus)
39(eval-when-compile (require 'cl-lib)) 39(eval-when-compile (require 'cl-lib))
40 40
@@ -60,37 +60,39 @@ If set to nil, then autodetect `:battery' device."
60(defconst battery-upower-dbus-service "org.freedesktop.UPower" 60(defconst battery-upower-dbus-service "org.freedesktop.UPower"
61 "Well-known UPower service name for the D-Bus system.") 61 "Well-known UPower service name for the D-Bus system.")
62 62
63(defun battery--files (dir)
64 "Return a list of absolute file names in DIR or nil on error.
65Value does not include \".\" or \"..\"."
66 (ignore-errors (directory-files dir t directory-files-no-dot-files-regexp)))
67
63(defun battery--find-linux-sysfs-batteries () 68(defun battery--find-linux-sysfs-batteries ()
64 (let ((dirs nil)) 69 "Return a list of all sysfs battery directories."
65 (dolist (file (directory-files "/sys/class/power_supply/" t)) 70 (let (dirs)
66 (when (and (or (file-directory-p file) 71 (dolist (dir (battery--files "/sys/class/power_supply/"))
67 (file-symlink-p file)) 72 (when (file-exists-p (expand-file-name "capacity" dir))
68 (file-exists-p (expand-file-name "capacity" file))) 73 (push dir dirs)))
69 (push file dirs)))
70 (nreverse dirs))) 74 (nreverse dirs)))
71 75
72(defcustom battery-status-function 76(defcustom battery-status-function
73 (cond ((dbus-ping :system battery-upower-dbus-service) 77 (cond ((dbus-ping :system battery-upower-dbus-service)
74 #'battery-upower) 78 #'battery-upower)
75 ((and (eq system-type 'gnu/linux) 79 ((and (eq system-type 'gnu/linux)
76 (file-readable-p "/proc/apm")) 80 (battery--find-linux-sysfs-batteries))
77 #'battery-linux-proc-apm) 81 #'battery-linux-sysfs)
78 ((and (eq system-type 'gnu/linux) 82 ((and (eq system-type 'gnu/linux)
79 (file-directory-p "/proc/acpi/battery")) 83 (file-directory-p "/proc/acpi/battery"))
80 #'battery-linux-proc-acpi) 84 #'battery-linux-proc-acpi)
81 ((and (eq system-type 'gnu/linux) 85 ((and (eq system-type 'gnu/linux)
82 (file-directory-p "/sys/class/power_supply/") 86 (file-readable-p "/proc/apm"))
83 (battery--find-linux-sysfs-batteries)) 87 #'battery-linux-proc-apm)
84 #'battery-linux-sysfs)
85 ((and (eq system-type 'berkeley-unix) 88 ((and (eq system-type 'berkeley-unix)
86 (file-executable-p "/usr/sbin/apm")) 89 (file-executable-p "/usr/sbin/apm"))
87 #'battery-bsd-apm) 90 #'battery-bsd-apm)
88 ((and (eq system-type 'darwin) 91 ((and (eq system-type 'darwin)
89 (condition-case nil 92 (ignore-errors
90 (with-temp-buffer 93 (with-temp-buffer
91 (and (eq (call-process "pmset" nil t nil "-g" "ps") 0) 94 (and (eq (call-process "pmset" nil t nil "-g" "ps") 0)
92 (> (buffer-size) 0))) 95 (not (bobp))))))
93 (error nil)))
94 #'battery-pmset) 96 #'battery-pmset)
95 ((fboundp 'w32-battery-status) 97 ((fboundp 'w32-battery-status)
96 #'w32-battery-status)) 98 #'w32-battery-status))
@@ -102,6 +104,7 @@ Its cons cells are of the form
102 104
103CONVERSION is the character code of a \"conversion specification\" 105CONVERSION is the character code of a \"conversion specification\"
104introduced by a `%' character in a control string." 106introduced by a `%' character in a control string."
107 :version "28.1"
105 :type '(choice (const nil) function)) 108 :type '(choice (const nil) function))
106 109
107(defcustom battery-echo-area-format 110(defcustom battery-echo-area-format
@@ -113,12 +116,13 @@ string are substituted as defined by the current value of the variable
113`battery-status-function'. Here are the ones generally available: 116`battery-status-function'. Here are the ones generally available:
114%c Current capacity (mAh or mWh) 117%c Current capacity (mAh or mWh)
115%r Current rate of charge or discharge 118%r Current rate of charge or discharge
119%L AC line status (verbose)
116%B Battery status (verbose) 120%B Battery status (verbose)
117%b Battery status: empty means high, `-' means low, 121%b Battery status: empty means high, `-' means low,
118 `!' means critical, and `+' means charging 122 `!' means critical, and `+' means charging
119%d Temperature (in degrees Celsius) 123%d Temperature (in degrees Celsius)
120%L AC line status (verbose)
121%p Battery load percentage 124%p Battery load percentage
125%s Remaining time (to charge or discharge) in seconds
122%m Remaining time (to charge or discharge) in minutes 126%m Remaining time (to charge or discharge) in minutes
123%h Remaining time (to charge or discharge) in hours 127%h Remaining time (to charge or discharge) in hours
124%t Remaining time (to charge or discharge) in the form `h:min' 128%t Remaining time (to charge or discharge) in the form `h:min'
@@ -137,7 +141,7 @@ The full `format-spec' formatting syntax is supported."
137 :type 'integer) 141 :type 'integer)
138 142
139(defcustom battery-mode-line-format 143(defcustom battery-mode-line-format
140 (cond ((eq battery-status-function 'battery-linux-proc-acpi) 144 (cond ((eq battery-status-function #'battery-linux-proc-acpi)
141 "[%b%p%%,%d°C]") 145 "[%b%p%%,%d°C]")
142 (battery-status-function 146 (battery-status-function
143 "[%b%p%%]")) 147 "[%b%p%%]"))
@@ -148,12 +152,13 @@ string are substituted as defined by the current value of the variable
148`battery-status-function'. Here are the ones generally available: 152`battery-status-function'. Here are the ones generally available:
149%c Current capacity (mAh or mWh) 153%c Current capacity (mAh or mWh)
150%r Current rate of charge or discharge 154%r Current rate of charge or discharge
155%L AC line status (verbose)
151%B Battery status (verbose) 156%B Battery status (verbose)
152%b Battery status: empty means high, `-' means low, 157%b Battery status: empty means high, `-' means low,
153 `!' means critical, and `+' means charging 158 `!' means critical, and `+' means charging
154%d Temperature (in degrees Celsius) 159%d Temperature (in degrees Celsius)
155%L AC line status (verbose)
156%p Battery load percentage 160%p Battery load percentage
161%s Remaining time (to charge or discharge) in seconds
157%m Remaining time (to charge or discharge) in minutes 162%m Remaining time (to charge or discharge) in minutes
158%h Remaining time (to charge or discharge) in hours 163%h Remaining time (to charge or discharge) in hours
159%t Remaining time (to charge or discharge) in the form `h:min' 164%t Remaining time (to charge or discharge) in the form `h:min'
@@ -176,6 +181,18 @@ A battery load percentage below this number is considered low."
176A battery load percentage below this number is considered critical." 181A battery load percentage below this number is considered critical."
177 :type 'integer) 182 :type 'integer)
178 183
184(defface battery-load-low
185 '((t :inherit warning))
186 "Face used in mode line string when battery load is low.
187See the option `battery-load-low'."
188 :version "28.1")
189
190(defface battery-load-critical
191 '((t :inherit error))
192 "Face used in mode line string when battery load is critical.
193See the option `battery-load-critical'."
194 :version "28.1")
195
179(defvar battery-update-timer nil 196(defvar battery-update-timer nil
180 "Interval timer object.") 197 "Interval timer object.")
181 198
@@ -208,7 +225,7 @@ seconds."
208 (delq 'battery-mode-line-string global-mode-string)) 225 (delq 'battery-mode-line-string global-mode-string))
209 (add-to-list 'global-mode-string 'battery-mode-line-string t) 226 (add-to-list 'global-mode-string 'battery-mode-line-string t)
210 (setq battery-update-timer (run-at-time nil battery-update-interval 227 (setq battery-update-timer (run-at-time nil battery-update-interval
211 'battery-update-handler)) 228 #'battery-update-handler))
212 (battery-update)) 229 (battery-update))
213 (message "Battery status not available") 230 (message "Battery status not available")
214 (setq display-battery-mode nil))) 231 (setq display-battery-mode nil)))
@@ -220,34 +237,42 @@ seconds."
220(defun battery-update () 237(defun battery-update ()
221 "Update battery status information in the mode line." 238 "Update battery status information in the mode line."
222 (let* ((data (and battery-status-function (funcall battery-status-function))) 239 (let* ((data (and battery-status-function (funcall battery-status-function)))
223 (percentage (car (read-from-string (cdr (assq ?p data)))))) 240 (percentage (car (read-from-string (cdr (assq ?p data)))))
224 (setq battery-mode-line-string 241 (res (and battery-mode-line-format
225 (propertize (if (and battery-mode-line-format 242 (or (not (numberp percentage))
226 (numberp percentage) 243 (<= percentage battery-mode-line-limit))
227 (<= percentage battery-mode-line-limit)) 244 (battery-format battery-mode-line-format data)))
228 (battery-format battery-mode-line-format data) 245 (len (length res)))
229 "") 246 (unless (zerop len)
230 'face 247 (cond ((not (numberp percentage)))
231 (and (numberp percentage) 248 ((< percentage battery-load-critical)
232 (<= percentage battery-load-critical) 249 (add-face-text-property 0 len 'battery-load-critical t res))
233 'error) 250 ((< percentage battery-load-low)
234 'help-echo "Battery status information"))) 251 (add-face-text-property 0 len 'battery-load-low t res)))
235 (force-mode-line-update)) 252 (put-text-property 0 len 'help-echo "Battery status information" res))
253 (setq battery-mode-line-string (or res "")))
254 (force-mode-line-update t))
255
236 256
237;;; `/proc/apm' interface for Linux. 257;;; `/proc/apm' interface for Linux.
238 258
239(defconst battery-linux-proc-apm-regexp 259;; Regular expression matching contents of `/proc/apm'.
240 (concat "^\\([^ ]+\\)" ; Driver version. 260(rx-define battery--linux-proc-apm
241 " \\([^ ]+\\)" ; APM BIOS version. 261 (: bol (group (+ (not ?\s))) ; Driver version.
242 " 0x\\([0-9a-f]+\\)" ; APM BIOS flags. 262 " " (group (+ (not ?\s))) ; APM BIOS version.
243 " 0x\\([0-9a-f]+\\)" ; AC line status. 263 " 0x" (group (+ xdigit)) ; APM BIOS flags.
244 " 0x\\([0-9a-f]+\\)" ; Battery status. 264 " 0x" (group (+ xdigit)) ; AC line status.
245 " 0x\\([0-9a-f]+\\)" ; Battery flags. 265 " 0x" (group (+ xdigit)) ; Battery status.
246 " \\(-?[0-9]+\\)%" ; Load percentage. 266 " 0x" (group (+ xdigit)) ; Battery flags.
247 " \\(-?[0-9]+\\)" ; Remaining time. 267 " " (group (? ?-) (+ digit)) ?% ; Load percentage.
248 " \\(.*\\)" ; Time unit. 268 " " (group (? ?-) (+ digit)) ; Remaining time.
249 "$") 269 " " (group (* nonl)) ; Time unit
270 eol))
271
272(defconst battery-linux-proc-apm-regexp (rx battery--linux-proc-apm)
250 "Regular expression matching contents of `/proc/apm'.") 273 "Regular expression matching contents of `/proc/apm'.")
274(make-obsolete-variable 'battery-linux-proc-apm-regexp
275 "it is no longer used." "28.1")
251 276
252(defun battery-linux-proc-apm () 277(defun battery-linux-proc-apm ()
253 "Get APM status information from Linux (the kernel). 278 "Get APM status information from Linux (the kernel).
@@ -267,12 +292,12 @@ The following %-sequences are provided:
267%m Remaining time (to charge or discharge) in minutes 292%m Remaining time (to charge or discharge) in minutes
268%h Remaining time (to charge or discharge) in hours 293%h Remaining time (to charge or discharge) in hours
269%t Remaining time (to charge or discharge) in the form `h:min'" 294%t Remaining time (to charge or discharge) in the form `h:min'"
270 (let (driver-version bios-version bios-interface line-status 295 (let ( driver-version bios-version bios-interface line-status
271 battery-status battery-status-symbol load-percentage 296 battery-status battery-status-symbol load-percentage
272 seconds minutes hours remaining-time tem) 297 seconds minutes hours remaining-time tem )
273 (with-temp-buffer 298 (with-temp-buffer
274 (ignore-errors (insert-file-contents "/proc/apm")) 299 (ignore-errors (insert-file-contents "/proc/apm"))
275 (when (re-search-forward battery-linux-proc-apm-regexp) 300 (when (re-search-forward (rx battery--linux-proc-apm) nil t)
276 (setq driver-version (match-string 1)) 301 (setq driver-version (match-string 1))
277 (setq bios-version (match-string 2)) 302 (setq bios-version (match-string 2))
278 (setq tem (string-to-number (match-string 3) 16)) 303 (setq tem (string-to-number (match-string 3) 16))
@@ -285,9 +310,7 @@ The following %-sequences are provided:
285 (cond ((= tem 0) (setq line-status "off-line")) 310 (cond ((= tem 0) (setq line-status "off-line"))
286 ((= tem 1) (setq line-status "on-line")) 311 ((= tem 1) (setq line-status "on-line"))
287 ((= tem 2) (setq line-status "on backup"))) 312 ((= tem 2) (setq line-status "on backup")))
288 (setq tem (string-to-number (match-string 6) 16)) 313 (unless (= (string-to-number (match-string 6) 16) 255)
289 (if (= tem 255)
290 (setq battery-status "N/A")
291 (setq tem (string-to-number (match-string 5) 16)) 314 (setq tem (string-to-number (match-string 5) 16))
292 (cond ((= tem 0) (setq battery-status "high" 315 (cond ((= tem 0) (setq battery-status "high"
293 battery-status-symbol "")) 316 battery-status-symbol ""))
@@ -304,7 +327,7 @@ The following %-sequences are provided:
304 (setq minutes (/ seconds 60) 327 (setq minutes (/ seconds 60)
305 hours (/ seconds 3600)) 328 hours (/ seconds 3600))
306 (setq remaining-time 329 (setq remaining-time
307 (format "%d:%02d" hours (- minutes (* 60 hours)))))))) 330 (format "%d:%02d" hours (% minutes 60)))))))
308 (list (cons ?v (or driver-version "N/A")) 331 (list (cons ?v (or driver-version "N/A"))
309 (cons ?V (or bios-version "N/A")) 332 (cons ?V (or bios-version "N/A"))
310 (cons ?I (or bios-interface "N/A")) 333 (cons ?I (or bios-interface "N/A"))
@@ -312,27 +335,31 @@ The following %-sequences are provided:
312 (cons ?B (or battery-status "N/A")) 335 (cons ?B (or battery-status "N/A"))
313 (cons ?b (or battery-status-symbol "")) 336 (cons ?b (or battery-status-symbol ""))
314 (cons ?p (or load-percentage "N/A")) 337 (cons ?p (or load-percentage "N/A"))
315 (cons ?s (or (and seconds (number-to-string seconds)) "N/A")) 338 (cons ?s (if seconds (number-to-string seconds) "N/A"))
316 (cons ?m (or (and minutes (number-to-string minutes)) "N/A")) 339 (cons ?m (if minutes (number-to-string minutes) "N/A"))
317 (cons ?h (or (and hours (number-to-string hours)) "N/A")) 340 (cons ?h (if hours (number-to-string hours) "N/A"))
318 (cons ?t (or remaining-time "N/A"))))) 341 (cons ?t (or remaining-time "N/A")))))
319 342
320 343
321;;; `/proc/acpi/' interface for Linux. 344;;; `/proc/acpi/' interface for Linux.
322 345
346(rx-define battery--acpi-rate (&rest hour)
347 (: (group (+ digit)) " " (group ?m (in "AW") hour)))
348(rx-define battery--acpi-capacity (battery--acpi-rate ?h))
349
323(defun battery-linux-proc-acpi () 350(defun battery-linux-proc-acpi ()
324 "Get ACPI status information from Linux (the kernel). 351 "Get ACPI status information from Linux (the kernel).
325This function works only with the `/proc/acpi/' format introduced 352This function works only with the `/proc/acpi/' interface
326in Linux version 2.4.20 and 2.6.0. 353introduced in Linux version 2.4.20 and 2.6.0.
327 354
328The following %-sequences are provided: 355The following %-sequences are provided:
329%c Current capacity (mAh) 356%c Current capacity (mAh)
330%r Current rate 357%r Current rate of charge or discharge
358%L AC line status (verbose)
331%B Battery status (verbose) 359%B Battery status (verbose)
332%b Battery status, empty means high, `-' means low, 360%b Battery status, empty means high, `-' means low,
333 `!' means critical, and `+' means charging 361 `!' means critical, and `+' means charging
334%d Temperature (in degrees Celsius) 362%d Temperature (in degrees Celsius)
335%L AC line status (verbose)
336%p Battery load percentage 363%p Battery load percentage
337%m Remaining time (to charge or discharge) in minutes 364%m Remaining time (to charge or discharge) in minutes
338%h Remaining time (to charge or discharge) in hours 365%h Remaining time (to charge or discharge) in hours
@@ -348,45 +375,51 @@ The following %-sequences are provided:
348 ;; information together since displaying for a variable amount of 375 ;; information together since displaying for a variable amount of
349 ;; batteries seems overkill for format-strings. 376 ;; batteries seems overkill for format-strings.
350 (with-temp-buffer 377 (with-temp-buffer
351 (dolist (dir (ignore-errors (directory-files "/proc/acpi/battery/" 378 (dolist (dir (battery--files "/proc/acpi/battery/"))
352 t "\\`[^.]"))) 379 (ignore-errors
353 (erase-buffer) 380 (insert-file-contents (expand-file-name "state" dir) nil nil nil t))
354 (ignore-errors (insert-file-contents (expand-file-name "state" dir))) 381 (goto-char (point-min))
355 (when (re-search-forward "present: +yes$" nil t) 382 (when (re-search-forward (rx "present:" (+ space) "yes" eol) nil t)
356 (and (re-search-forward "charging state: +\\(.*\\)$" nil t) 383 (and (re-search-forward (rx "charging state:" (+ space)
384 (group (not space) (* nonl)) eol)
385 nil t)
357 (member charging-state '("unknown" "charged" nil)) 386 (member charging-state '("unknown" "charged" nil))
358 ;; On most multi-battery systems, most of the time only one 387 ;; On most multi-battery systems, most of the time only one
359 ;; battery is "charging"/"discharging", the others are 388 ;; battery is "charging"/"discharging", the others are
360 ;; "unknown". 389 ;; "unknown".
361 (setq charging-state (match-string 1))) 390 (setq charging-state (match-string 1)))
362 (when (re-search-forward "present rate: +\\([0-9]+\\) \\(m[AW]\\)$" 391 (when (re-search-forward (rx "present rate:" (+ space)
392 (battery--acpi-rate) eol)
363 nil t) 393 nil t)
364 (setq rate (+ (or rate 0) (string-to-number (match-string 1)))) 394 (setq rate (+ (or rate 0) (string-to-number (match-string 1))))
365 (when (> rate 0) 395 (when (> rate 0)
366 (setq rate-type (or (and rate-type 396 (cond ((not rate-type)
367 (if (string= rate-type (match-string 2)) 397 (setq rate-type (match-string 2)))
368 rate-type 398 ((not (string= rate-type (match-string 2)))
369 (error 399 (error "Inconsistent rate types (%s vs. %s)"
370 "Inconsistent rate types (%s vs. %s)" 400 rate-type (match-string 2))))))
371 rate-type (match-string 2)))) 401 (when (re-search-forward (rx "remaining capacity:" (+ space)
372 (match-string 2))))) 402 battery--acpi-capacity eol)
373 (when (re-search-forward "remaining capacity: +\\([0-9]+\\) m[AW]h$"
374 nil t) 403 nil t)
375 (setq capacity 404 (setq capacity
376 (+ (or capacity 0) (string-to-number (match-string 1)))))) 405 (+ (or capacity 0) (string-to-number (match-string 1))))))
377 (goto-char (point-max)) 406 (goto-char (point-max))
378 (ignore-errors (insert-file-contents (expand-file-name "info" dir))) 407 (ignore-errors (insert-file-contents (expand-file-name "info" dir)))
379 (when (re-search-forward "present: +yes$" nil t) 408 (when (re-search-forward (rx "present:" (+ space) "yes" eol) nil t)
380 (when (re-search-forward "design capacity: +\\([0-9]+\\) m[AW]h$" 409 (when (re-search-forward (rx "design capacity:" (+ space)
410 battery--acpi-capacity eol)
381 nil t) 411 nil t)
382 (cl-incf design-capacity (string-to-number (match-string 1)))) 412 (cl-incf design-capacity (string-to-number (match-string 1))))
383 (when (re-search-forward "last full capacity: +\\([0-9]+\\) m[AW]h$" 413 (when (re-search-forward (rx "last full capacity:" (+ space)
414 battery--acpi-capacity eol)
384 nil t) 415 nil t)
385 (cl-incf last-full-capacity (string-to-number (match-string 1)))) 416 (cl-incf last-full-capacity (string-to-number (match-string 1))))
386 (when (re-search-forward 417 (when (re-search-forward (rx "design capacity warning:" (+ space)
387 "design capacity warning: +\\([0-9]+\\) m[AW]h$" nil t) 418 battery--acpi-capacity eol)
419 nil t)
388 (cl-incf warn (string-to-number (match-string 1)))) 420 (cl-incf warn (string-to-number (match-string 1))))
389 (when (re-search-forward "design capacity low: +\\([0-9]+\\) m[AW]h$" 421 (when (re-search-forward (rx "design capacity low:" (+ space)
422 battery--acpi-capacity eol)
390 nil t) 423 nil t)
391 (cl-incf low (string-to-number (match-string 1))))))) 424 (cl-incf low (string-to-number (match-string 1)))))))
392 (setq full-capacity (if (> last-full-capacity 0) 425 (setq full-capacity (if (> last-full-capacity 0)
@@ -400,79 +433,70 @@ The following %-sequences are provided:
400 60) 433 60)
401 rate)) 434 rate))
402 hours (/ minutes 60))) 435 hours (/ minutes 60)))
403 (list (cons ?c (or (and capacity (number-to-string capacity)) "N/A")) 436 (list (cons ?c (if capacity (number-to-string capacity) "N/A"))
404 (cons ?L (or (battery-search-for-one-match-in-files 437 (cons ?L (or (battery-search-for-one-match-in-files
405 (mapcar (lambda (e) (concat e "/state")) 438 (mapcar (lambda (d) (expand-file-name "state" d))
406 (ignore-errors 439 (battery--files "/proc/acpi/ac_adapter/"))
407 (directory-files "/proc/acpi/ac_adapter/" 440 (rx "state:" (+ space) (group (not space) (* nonl)) eol)
408 t "\\`[^.]"))) 441 1)
409 "state: +\\(.*\\)$" 1)
410
411 "N/A")) 442 "N/A"))
412 (cons ?d (or (battery-search-for-one-match-in-files 443 (cons ?d (or (battery-search-for-one-match-in-files
413 (mapcar (lambda (e) (concat e "/temperature")) 444 (mapcar (lambda (d) (expand-file-name "temperature" d))
414 (ignore-errors 445 (battery--files "/proc/acpi/thermal_zone/"))
415 (directory-files "/proc/acpi/thermal_zone/" 446 (rx "temperature:" (+ space) (group (+ digit)) " C" eol)
416 t "\\`[^.]"))) 447 1)
417 "temperature: +\\([0-9]+\\) C$" 1)
418
419 "N/A")) 448 "N/A"))
420 (cons ?r (or (and rate (concat (number-to-string rate) " " 449 (cons ?r (if rate
421 rate-type)) "N/A")) 450 (concat (number-to-string rate) " " rate-type)
451 "N/A"))
422 (cons ?B (or charging-state "N/A")) 452 (cons ?B (or charging-state "N/A"))
423 (cons ?b (or (and (string= charging-state "charging") "+") 453 (cons ?b (cond ((string= charging-state "charging") "+")
424 (and capacity (< capacity low) "!") 454 ((and capacity (< capacity low)) "!")
425 (and capacity (< capacity warn) "-") 455 ((and capacity (< capacity warn)) "-")
426 "")) 456 ("")))
427 (cons ?h (or (and hours (number-to-string hours)) "N/A")) 457 (cons ?h (if hours (number-to-string hours) "N/A"))
428 (cons ?m (or (and minutes (number-to-string minutes)) "N/A")) 458 (cons ?m (if minutes (number-to-string minutes) "N/A"))
429 (cons ?t (or (and minutes 459 (cons ?t (if minutes (format "%d:%02d" hours (% minutes 60)) "N/A"))
430 (format "%d:%02d" hours (- minutes (* 60 hours)))) 460 (cons ?p (if (and full-capacity capacity (> full-capacity 0))
431 "N/A")) 461 (number-to-string (floor (* 100 capacity) full-capacity))
432 (cons ?p (or (and full-capacity capacity 462 "N/A")))))
433 (> full-capacity 0)
434 (number-to-string
435 (floor (* 100 capacity) full-capacity)))
436 "N/A")))))
437 463
438 464
439;;; `/sys/class/power_supply/BATN' interface for Linux. 465;;; `/sys/class/power_supply/BATN' interface for Linux.
440 466
441(defun battery-linux-sysfs () 467(defun battery-linux-sysfs ()
442 "Get ACPI status information from Linux kernel. 468 "Get sysfs status information from Linux kernel.
443This function works only with the new `/sys/class/power_supply/' 469This function works only with the new `/sys/class/power_supply/'
444format introduced in Linux version 2.4.25. 470interface introduced in Linux version 2.4.25.
445 471
446The following %-sequences are provided: 472The following %-sequences are provided:
447%c Current capacity (mAh or mWh) 473%c Current capacity (mAh or mWh)
448%r Current rate 474%r Current rate of charge or discharge
475%L Power source (verbose)
449%B Battery status (verbose) 476%B Battery status (verbose)
450%b Battery status, empty means high, `-' means low, 477%b Battery status, empty means high, `-' means low,
451 `!' means critical, and `+' means charging 478 `!' means critical, and `+' means charging
452%d Temperature (in degrees Celsius) 479%d Temperature (in degrees Celsius)
453%p Battery load percentage 480%p Battery load percentage
454%L AC line status (verbose)
455%m Remaining time (to charge or discharge) in minutes 481%m Remaining time (to charge or discharge) in minutes
456%h Remaining time (to charge or discharge) in hours 482%h Remaining time (to charge or discharge) in hours
457%t Remaining time (to charge or discharge) in the form `h:min'" 483%t Remaining time (to charge or discharge) in the form `h:min'"
458 (let (charging-state temperature hours percentage-now 484 (let (;; Some batteries report charges and current, others energy and power.
459 ;; Some batteries report charges and current, other energy and power.
460 ;; In order to reliably be able to combine those data, we convert them 485 ;; In order to reliably be able to combine those data, we convert them
461 ;; all to energy/power (since we can't combine different charges if 486 ;; all to energy/power (since we can't combine different charges if
462 ;; they're not at the same voltage). 487 ;; they're not at the same voltage).
463 (energy-full 0.0) 488 (energy-full 0.0)
464 (energy-now 0.0) 489 (energy-now 0.0)
465 (power-now 0.0) 490 (power-now 0.0)
466 (voltage-now 10.8)) ;Arbitrary default, in case the info is missing. 491 (voltage-now 10.8) ; Arbitrary default, in case the info is missing.
492 charging-state temperature hours percentage-now)
467 ;; SysFS provides information about each battery present in the 493 ;; SysFS provides information about each battery present in the
468 ;; system in a separate subdirectory. We are going to merge the 494 ;; system in a separate subdirectory. We are going to merge the
469 ;; available information together. 495 ;; available information together.
470 (with-temp-buffer 496 (with-temp-buffer
471 (dolist (dir (ignore-errors 497 (dolist (dir (battery--find-linux-sysfs-batteries))
472 (battery--find-linux-sysfs-batteries))) 498 (ignore-errors
473 (erase-buffer) 499 (insert-file-contents (expand-file-name "uevent" dir) nil nil nil t))
474 (ignore-errors (insert-file-contents
475 (expand-file-name "uevent" dir)))
476 (goto-char (point-min)) 500 (goto-char (point-min))
477 (when (re-search-forward 501 (when (re-search-forward
478 "POWER_SUPPLY_VOLTAGE_NOW=\\([0-9]*\\)$" nil t) 502 "POWER_SUPPLY_VOLTAGE_NOW=\\([0-9]*\\)$" nil t)
@@ -508,7 +532,7 @@ The following %-sequences are provided:
508 voltage-now)) 532 voltage-now))
509 (cl-incf energy-now (* (string-to-number now-string) 533 (cl-incf energy-now (* (string-to-number now-string)
510 voltage-now))) 534 voltage-now)))
511 ((and (progn (goto-char (point-min)) t) 535 ((and (goto-char (point-min))
512 (re-search-forward 536 (re-search-forward
513 "POWER_SUPPLY_ENERGY_FULL=\\([0-9]*\\)$" nil t) 537 "POWER_SUPPLY_ENERGY_FULL=\\([0-9]*\\)$" nil t)
514 (setq full-string (match-string 1)) 538 (setq full-string (match-string 1))
@@ -517,7 +541,6 @@ The following %-sequences are provided:
517 (setq now-string (match-string 1))) 541 (setq now-string (match-string 1)))
518 (cl-incf energy-full (string-to-number full-string)) 542 (cl-incf energy-full (string-to-number full-string))
519 (cl-incf energy-now (string-to-number now-string))))) 543 (cl-incf energy-now (string-to-number now-string)))))
520 (goto-char (point-min))
521 (unless (zerop power-now) 544 (unless (zerop power-now)
522 (let ((remaining (if (string= charging-state "Discharging") 545 (let ((remaining (if (string= charging-state "Discharging")
523 energy-now 546 energy-now
@@ -525,9 +548,9 @@ The following %-sequences are provided:
525 (setq hours (/ remaining power-now))))))) 548 (setq hours (/ remaining power-now)))))))
526 (when (and (> energy-full 0) (> energy-now 0)) 549 (when (and (> energy-full 0) (> energy-now 0))
527 (setq percentage-now (/ (* 100 energy-now) energy-full))) 550 (setq percentage-now (/ (* 100 energy-now) energy-full)))
528 (list (cons ?c (cond ((or (> energy-full 0) (> energy-now 0)) 551 (list (cons ?c (if (or (> energy-full 0) (> energy-now 0))
529 (number-to-string (/ energy-now voltage-now))) 552 (number-to-string (/ energy-now voltage-now))
530 (t "N/A"))) 553 "N/A"))
531 (cons ?r (if (> power-now 0.0) 554 (cons ?r (if (> power-now 0.0)
532 (format "%.1f" (/ power-now 1000000.0)) 555 (format "%.1f" (/ power-now 1000000.0))
533 "N/A")) 556 "N/A"))
@@ -538,27 +561,20 @@ The following %-sequences are provided:
538 "N/A")) 561 "N/A"))
539 (cons ?d (or temperature "N/A")) 562 (cons ?d (or temperature "N/A"))
540 (cons ?B (or charging-state "N/A")) 563 (cons ?B (or charging-state "N/A"))
541 (cons ?b (or (and (string= charging-state "Charging") "+") 564 (cons ?b (cond ((string= charging-state "Charging") "+")
542 (and percentage-now (< percentage-now battery-load-critical) "!") 565 ((not percentage-now) "")
543 (and percentage-now (< percentage-now battery-load-low) "-") 566 ((< percentage-now battery-load-critical) "!")
544 "")) 567 ((< percentage-now battery-load-low) "-")
545 (cons ?p (cond 568 ("")))
546 ((and percentage-now (format "%.1f" percentage-now))) 569 (cons ?p (if percentage-now (format "%.1f" percentage-now) "N/A"))
547 (t "N/A"))) 570 (cons ?L (pcase (battery-search-for-one-match-in-files
548 (cons ?L (cond 571 '("/sys/class/power_supply/AC/online"
549 ((battery-search-for-one-match-in-files 572 "/sys/class/power_supply/ACAD/online"
550 (list "/sys/class/power_supply/AC/online" 573 "/sys/class/power_supply/ADP1/online")
551 "/sys/class/power_supply/ACAD/online" 574 (rx (in "01")) 0)
552 "/sys/class/power_supply/ADP1/online") 575 ("0" "BAT")
553 "1" 0) 576 ("1" "AC")
554 "AC") 577 (_ "N/A"))))))
555 ((battery-search-for-one-match-in-files
556 (list "/sys/class/power_supply/AC/online"
557 "/sys/class/power_supply/ACAD/online"
558 "/sys/class/power_supply/ADP1/online")
559 "0" 0)
560 "BAT")
561 (t "N/A"))))))
562 578
563 579
564;;; `upowerd' interface. 580;;; `upowerd' interface.
@@ -681,19 +697,20 @@ The following %-sequences are provided:
681 697
682 698
683;;; `apm' interface for BSD. 699;;; `apm' interface for BSD.
700
684(defun battery-bsd-apm () 701(defun battery-bsd-apm ()
685 "Get APM status information from BSD apm binary. 702 "Get APM status information from BSD apm binary.
686The following %-sequences are provided: 703The following %-sequences are provided:
704%P Advanced power saving mode state (verbose)
687%L AC line status (verbose) 705%L AC line status (verbose)
688%B Battery status (verbose) 706%B Battery status (verbose)
689%b Battery status, empty means high, `-' means low, 707%b Battery status, empty means high, `-' means low,
690 `!' means critical, and `+' means charging 708 `!' means critical, and `+' means charging
691%P Advanced power saving mode state (verbose) 709%p Battery load percentage
692%p Battery charge percentage 710%s Remaining time (to charge or discharge) in seconds
693%s Remaining battery charge time in seconds 711%m Remaining time (to charge or discharge) in minutes
694%m Remaining battery charge time in minutes 712%h Remaining time (to charge or discharge) in hours
695%h Remaining battery charge time in hours 713%t Remaining time (to charge or discharge) in the form `h:min'"
696%t Remaining battery charge time in the form `h:min'"
697 (let* ((os-name (car (split-string 714 (let* ((os-name (car (split-string
698 ;; FIXME: Can't we use something like `system-type'? 715 ;; FIXME: Can't we use something like `system-type'?
699 (shell-command-to-string "/usr/bin/uname")))) 716 (shell-command-to-string "/usr/bin/uname"))))
@@ -759,7 +776,7 @@ The following %-sequences are provided:
759 (setq seconds (string-to-number battery-life) 776 (setq seconds (string-to-number battery-life)
760 minutes (truncate seconds 60))) 777 minutes (truncate seconds 60)))
761 (setq hours (truncate minutes 60) 778 (setq hours (truncate minutes 60)
762 remaining-time (format "%d:%02d" hours (mod minutes 60)))) 779 remaining-time (format "%d:%02d" hours (% minutes 60))))
763 (list (cons ?L (or line-status "N/A")) 780 (list (cons ?L (or line-status "N/A"))
764 (cons ?B (or (car battery-status) "N/A")) 781 (cons ?B (or (car battery-status) "N/A"))
765 (cons ?b (or (cdr battery-status) "N/A")) 782 (cons ?b (or (cdr battery-status) "N/A"))
@@ -767,9 +784,9 @@ The following %-sequences are provided:
767 "N/A" 784 "N/A"
768 battery-percentage)) 785 battery-percentage))
769 (cons ?P (or apm-mode "N/A")) 786 (cons ?P (or apm-mode "N/A"))
770 (cons ?s (or (and seconds (number-to-string seconds)) "N/A")) 787 (cons ?s (if seconds (number-to-string seconds) "N/A"))
771 (cons ?m (or (and minutes (number-to-string minutes)) "N/A")) 788 (cons ?m (if minutes (number-to-string minutes) "N/A"))
772 (cons ?h (or (and hours (number-to-string hours)) "N/A")) 789 (cons ?h (if hours (number-to-string hours) "N/A"))
773 (cons ?t (or remaining-time "N/A"))))) 790 (cons ?t (or remaining-time "N/A")))))
774 791
775 792
@@ -784,21 +801,25 @@ The following %-sequences are provided:
784%b Battery status, empty means high, `-' means low, 801%b Battery status, empty means high, `-' means low,
785 `!' means critical, and `+' means charging 802 `!' means critical, and `+' means charging
786%p Battery load percentage 803%p Battery load percentage
787%h Remaining time in hours 804%m Remaining time (to charge or discharge) in minutes
788%m Remaining time in minutes 805%h Remaining time (to charge or discharge) in hours
789%t Remaining time in the form `h:min'" 806%t Remaining time (to charge or discharge) in the form `h:min'"
790 (let (power-source load-percentage battery-status battery-status-symbol 807 (let ( power-source load-percentage battery-status battery-status-symbol
791 remaining-time hours minutes) 808 remaining-time hours minutes )
792 (with-temp-buffer 809 (with-temp-buffer
793 (ignore-errors (call-process "pmset" nil t nil "-g" "ps")) 810 (ignore-errors (call-process "pmset" nil t nil "-g" "ps"))
794 (goto-char (point-min)) 811 (goto-char (point-min))
795 (when (re-search-forward "\\(?:Currentl?y\\|Now\\) drawing from '\\(AC\\|Battery\\) Power'" nil t) 812 (when (re-search-forward ;; Handle old typo in output.
813 "\\(?:Currentl?y\\|Now\\) drawing from '\\(AC\\|Battery\\) Power'"
814 nil t)
796 (setq power-source (match-string 1)) 815 (setq power-source (match-string 1))
797 (when (re-search-forward "^ -InternalBattery-0\\([ \t]+(id=[0-9]+)\\)*[ \t]+" nil t) 816 (when (re-search-forward (rx bol " -InternalBattery-0" (+ space)
817 (* "(id=" (+ digit) ")" (+ space)))
818 nil t)
798 (when (looking-at "\\([0-9]\\{1,3\\}\\)%") 819 (when (looking-at "\\([0-9]\\{1,3\\}\\)%")
799 (setq load-percentage (match-string 1)) 820 (setq load-percentage (match-string 1))
800 (goto-char (match-end 0)) 821 (goto-char (match-end 0))
801 (cond ((looking-at "; charging") 822 (cond ((looking-at-p "; charging")
802 (setq battery-status "charging" 823 (setq battery-status "charging"
803 battery-status-symbol "+")) 824 battery-status-symbol "+"))
804 ((< (string-to-number load-percentage) battery-load-critical) 825 ((< (string-to-number load-percentage) battery-load-critical)
diff --git a/lisp/emacs-lisp/rx.el b/lisp/emacs-lisp/rx.el
index aa4b2addd47..88bb0a8bd6c 100644
--- a/lisp/emacs-lisp/rx.el
+++ b/lisp/emacs-lisp/rx.el
@@ -1381,7 +1381,7 @@ To make local rx extensions, use `rx-let' for `rx',
1381For more details, see Info node `(elisp) Extending Rx'. 1381For more details, see Info node `(elisp) Extending Rx'.
1382 1382
1383\(fn NAME [(ARGS...)] RX)" 1383\(fn NAME [(ARGS...)] RX)"
1384 (declare (indent 1)) 1384 (declare (indent defun))
1385 `(eval-and-compile 1385 `(eval-and-compile
1386 (put ',name 'rx-definition ',(rx--make-binding name definition)) 1386 (put ',name 'rx-definition ',(rx--make-binding name definition))
1387 ',name)) 1387 ',name))
diff --git a/test/lisp/battery-tests.el b/test/lisp/battery-tests.el
index 4cb7470d884..92ab013f040 100644
--- a/test/lisp/battery-tests.el
+++ b/test/lisp/battery-tests.el
@@ -22,9 +22,9 @@
22(require 'battery) 22(require 'battery)
23 23
24(ert-deftest battery-linux-proc-apm-regexp () 24(ert-deftest battery-linux-proc-apm-regexp ()
25 "Test `battery-linux-proc-apm-regexp'." 25 "Test `rx' definition `battery--linux-proc-apm'."
26 (let ((str "1.16 1.2 0x07 0x01 0xff 0x80 -1% -1 ?")) 26 (let ((str "1.16 1.2 0x07 0x01 0xff 0x80 -1% -1 ?"))
27 (should (string-match battery-linux-proc-apm-regexp str)) 27 (should (string-match (rx battery--linux-proc-apm) str))
28 (should (equal (match-string 0 str) str)) 28 (should (equal (match-string 0 str) str))
29 (should (equal (match-string 1 str) "1.16")) 29 (should (equal (match-string 1 str) "1.16"))
30 (should (equal (match-string 2 str) "1.2")) 30 (should (equal (match-string 2 str) "1.2"))
@@ -36,7 +36,7 @@
36 (should (equal (match-string 8 str) "-1")) 36 (should (equal (match-string 8 str) "-1"))
37 (should (equal (match-string 9 str) "?"))) 37 (should (equal (match-string 9 str) "?")))
38 (let ((str "1.16 1.2 0x03 0x00 0x00 0x01 99% 1792 min")) 38 (let ((str "1.16 1.2 0x03 0x00 0x00 0x01 99% 1792 min"))
39 (should (string-match battery-linux-proc-apm-regexp str)) 39 (should (string-match (rx battery--linux-proc-apm) str))
40 (should (equal (match-string 0 str) str)) 40 (should (equal (match-string 0 str) str))
41 (should (equal (match-string 1 str) "1.16")) 41 (should (equal (match-string 1 str) "1.16"))
42 (should (equal (match-string 2 str) "1.2")) 42 (should (equal (match-string 2 str) "1.2"))
@@ -48,6 +48,39 @@
48 (should (equal (match-string 8 str) "1792")) 48 (should (equal (match-string 8 str) "1792"))
49 (should (equal (match-string 9 str) "min")))) 49 (should (equal (match-string 9 str) "min"))))
50 50
51(ert-deftest battery-acpi-rate-regexp ()
52 "Test `rx' definition `battery--acpi-rate'."
53 (let ((str "01 mA"))
54 (should (string-match (rx (battery--acpi-rate)) str))
55 (should (equal (match-string 0 str) str))
56 (should (equal (match-string 1 str) "01"))
57 (should (equal (match-string 2 str) "mA")))
58 (let ((str "23 mW"))
59 (should (string-match (rx (battery--acpi-rate)) str))
60 (should (equal (match-string 0 str) str))
61 (should (equal (match-string 1 str) "23"))
62 (should (equal (match-string 2 str) "mW")))
63 (let ((str "23 mWh"))
64 (should (string-match (rx (battery--acpi-rate)) str))
65 (should (equal (match-string 0 str) "23 mW"))
66 (should (equal (match-string 1 str) "23"))
67 (should (equal (match-string 2 str) "mW")))
68 (should-not (string-match (rx (battery--acpi-rate) eos) "45 mWh")))
69
70(ert-deftest battery-acpi-capacity-regexp ()
71 "Test `rx' definition `battery--acpi-capacity'."
72 (let ((str "01 mAh"))
73 (should (string-match (rx battery--acpi-capacity) str))
74 (should (equal (match-string 0 str) str))
75 (should (equal (match-string 1 str) "01"))
76 (should (equal (match-string 2 str) "mAh")))
77 (let ((str "23 mWh"))
78 (should (string-match (rx battery--acpi-capacity) str))
79 (should (equal (match-string 0 str) str))
80 (should (equal (match-string 1 str) "23"))
81 (should (equal (match-string 2 str) "mWh")))
82 (should-not (string-match (rx battery--acpi-capacity eos) "45 mW")))
83
51(ert-deftest battery-format () 84(ert-deftest battery-format ()
52 "Test `battery-format'." 85 "Test `battery-format'."
53 (should (equal (battery-format "" ()) "")) 86 (should (equal (battery-format "" ()) ""))