Add 2012 to FSF copyright years for Emacs files
[bpt/emacs.git] / lisp / battery.el
index f10245f..a3be2fe 100644 (file)
@@ -1,17 +1,16 @@
-;;; battery.el --- display battery status information
+;;; battery.el --- display battery status information  -*- coding: iso-8859-1 -*-
 
-;; Copyright (C) 1997, 1998, 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007 Free Software Foundation, Inc.
+;; Copyright (C) 1997-1998, 2000-2012 Free Software Foundation, Inc.
 
 ;; Author: Ralph Schleicher <rs@nunatak.allgaeu.org>
 ;; Keywords: hardware
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
-;; There is at present support for GNU/Linux and OS X.  This library
-;; supports both the `/proc/apm' file format of Linux version 1.3.58
-;; or newer and the `/proc/acpi/' directory structure of Linux 2.4.20
-;; and 2.6.  Darwin (OS X) is supported by using the `pmset' program.
+;; There is at present support for GNU/Linux, OS X and Windows.  This
+;; library supports both the `/proc/apm' file format of Linux version
+;; 1.3.58 or newer and the `/proc/acpi/' directory structure of Linux
+;; 2.4.20 and 2.6.  Darwin (OS X) is supported by using the `pmset'
+;; program.  Windows is supported by the GetSystemPowerStatus API call.
 
 ;;; Code:
 
        ((and (eq system-type 'gnu/linux)
              (file-directory-p "/proc/acpi/battery"))
         'battery-linux-proc-acpi)
+       ((and (eq system-type 'gnu/linux)
+             (file-directory-p "/sys/class/power_supply/")
+             (directory-files "/sys/class/power_supply/" nil "BAT[0-9]$"))
+        'battery-linux-sysfs)
+       ((and (eq system-type 'gnu/linux)
+             (file-directory-p "/sys/class/power_supply/yeeloong-bat/")
+             (directory-files "/sys/class/power_supply/yeeloong-bat/" nil "charge_"))
+        'battery-yeeloong-sysfs)
        ((and (eq system-type 'darwin)
              (condition-case nil
                  (with-temp-buffer
                    (and (eq (call-process "pmset" nil t nil "-g" "ps") 0)
                         (> (buffer-size) 0)))
                (error nil)))
-        'battery-pmset))
-  "*Function for getting battery status information.
+        'battery-pmset)
+       ((eq system-type 'windows-nt)
+        'w32-battery-status))
+  "Function for getting battery status information.
 The function has to return an alist of conversion definitions.
 Its cons cells are of the form
 
@@ -67,13 +75,17 @@ introduced by a `%' character in a control string."
   :group 'battery)
 
 (defcustom battery-echo-area-format
-  (cond ((eq battery-status-function 'battery-linux-proc-apm)
-        "Power %L, battery %B (%p%% load, remaining time %t)")
-       ((eq battery-status-function 'battery-linux-proc-acpi)
+  (cond ((eq battery-status-function 'battery-linux-proc-acpi)
         "Power %L, battery %B at %r (%p%% load, remaining time %t)")
+       ((eq battery-status-function 'battery-linux-sysfs)
+        "Power %L, battery %B (%p%% load)")
        ((eq battery-status-function 'battery-pmset)
-        "%L power, battery %B (%p%% load, remaining time %t)"))
-  "*Control string formatting the string to display in the echo area.
+        "%L power, battery %B (%p%% load, remaining time %t)")
+       ((eq battery-status-function 'battery-yeeloong-sysfs)
+        "%L power, battery %B (%p%% load, remaining time %t)")
+       (battery-status-function
+        "Power %L, battery %B (%p%% load, remaining time %t)"))
+  "Control string formatting the string to display in the echo area.
 Ordinary characters in the control string are printed as-is, while
 conversion specifications introduced by a `%' character in the control
 string are substituted as defined by the current value of the variable
@@ -96,14 +108,17 @@ string are substituted as defined by the current value of the variable
   "String to display in the mode line.")
 ;;;###autoload (put 'battery-mode-line-string 'risky-local-variable t)
 
+(defcustom battery-mode-line-limit 100
+  "Percentage of full battery load below which display battery status"
+  :type 'integer
+  :group 'battery)
+
 (defcustom battery-mode-line-format
-  (cond ((eq battery-status-function 'battery-linux-proc-apm)
-        "[%b%p%%]")
-       ((eq battery-status-function 'battery-linux-proc-acpi)
+  (cond ((eq battery-status-function 'battery-linux-proc-acpi)
         "[%b%p%%,%d°C]")
-       ((eq battery-status-function 'battery-pmset)
+       (battery-status-function
         "[%b%p%%]"))
-  "*Control string formatting the string to display in the mode line.
+  "Control string formatting the string to display in the mode line.
 Ordinary characters in the control string are printed as-is, while
 conversion specifications introduced by a `%' character in the control
 string are substituted as defined by the current value of the variable
@@ -123,18 +138,18 @@ string are substituted as defined by the current value of the variable
   :group 'battery)
 
 (defcustom battery-update-interval 60
-  "*Seconds after which the battery status will be updated."
+  "Seconds after which the battery status will be updated."
   :type 'integer
   :group 'battery)
 
 (defcustom battery-load-low 25
-  "*Upper bound of low battery load percentage.
+  "Upper bound of low battery load percentage.
 A battery load percentage below this number is considered low."
   :type 'integer
   :group 'battery)
 
 (defcustom battery-load-critical 10
-  "*Upper bound of critical battery load percentage.
+  "Upper bound of critical battery load percentage.
 A battery load percentage below this number is considered critical."
   :type 'integer
   :group 'battery)
@@ -155,22 +170,29 @@ The text being displayed in the echo area is controlled by the variables
 
 ;;;###autoload
 (define-minor-mode display-battery-mode
-  "Display battery status information in the mode line.
-The text being displayed in the mode line is controlled by the variables
+  "Toggle battery status display in mode line (Display Battery mode).
+With a prefix argument ARG, enable Display Battery mode if ARG is
+positive, and disable it otherwise.  If called from Lisp, enable
+the mode if ARG is omitted or nil.
+
+The text displayed in the mode line is controlled by
 `battery-mode-line-format' and `battery-status-function'.
-The mode line will be updated automatically every `battery-update-interval'
+The mode line is be updated every `battery-update-interval'
 seconds."
   :global t :group 'battery
   (setq battery-mode-line-string "")
   (or global-mode-string (setq global-mode-string '("")))
   (and battery-update-timer (cancel-timer battery-update-timer))
-  (if (not display-battery-mode)
-      (setq global-mode-string
-           (delq 'battery-mode-line-string global-mode-string))
-    (add-to-list 'global-mode-string 'battery-mode-line-string t)
-    (setq battery-update-timer (run-at-time nil battery-update-interval
-                                           'battery-update-handler))
-    (battery-update)))
+  (if (and battery-status-function battery-mode-line-format)
+      (if (not display-battery-mode)
+         (setq global-mode-string
+               (delq 'battery-mode-line-string global-mode-string))
+       (add-to-list 'global-mode-string 'battery-mode-line-string t)
+       (setq battery-update-timer (run-at-time nil battery-update-interval
+                                               'battery-update-handler))
+       (battery-update))
+    (message "Battery status not available")
+    (setq display-battery-mode nil)))
 
 (defun battery-update-handler ()
   (battery-update)
@@ -178,16 +200,21 @@ seconds."
 
 (defun battery-update ()
   "Update battery status information in the mode line."
-  (setq battery-mode-line-string
-       (propertize (if (and battery-mode-line-format
-                            battery-status-function)
-                       (battery-format
-                        battery-mode-line-format
-                        (funcall battery-status-function))
-                     "")
-                   'help-echo "Battery status information"))
+  (let ((data (and battery-status-function (funcall battery-status-function))))
+    (setq battery-mode-line-string
+         (propertize (if (and battery-mode-line-format
+                              (<= (car (read-from-string (cdr (assq ?p data))))
+                                  battery-mode-line-limit))
+                         (battery-format
+                          battery-mode-line-format
+                          data)
+                       "")
+                     'face
+                     (and (<= (car (read-from-string (cdr (assq ?p data))))
+                                  battery-load-critical)
+                          'error)
+                     'help-echo "Battery status information")))
   (force-mode-line-update))
-
 \f
 ;;; `/proc/apm' interface for Linux.
 
@@ -205,7 +232,7 @@ seconds."
   "Regular expression matching contents of `/proc/apm'.")
 
 (defun battery-linux-proc-apm ()
-  "Get APM status information from Linux kernel.
+  "Get APM status information from Linux (the kernel).
 This function works only with the new `/proc/apm' format introduced
 in Linux version 1.3.58.
 
@@ -276,8 +303,8 @@ The following %-sequences are provided:
 ;;; `/proc/acpi/' interface for Linux.
 
 (defun battery-linux-proc-acpi ()
-  "Get ACPI status information from Linux kernel.
-This function works only with the new `/proc/acpi/' format introduced
+  "Get ACPI status information from Linux (the kernel).
+This function works only with the `/proc/acpi/' format introduced
 in Linux version 2.4.20 and 2.6.0.
 
 The following %-sequences are provided:
@@ -391,6 +418,197 @@ The following %-sequences are provided:
                       "N/A")))))
 
 \f
+;;; `/sys/class/power_supply/BATN' interface for Linux.
+
+(defun battery-linux-sysfs ()
+  "Get ACPI status information from Linux kernel.
+This function works only with the new `/sys/class/power_supply/'
+format introduced in Linux version 2.4.25.
+
+The following %-sequences are provided:
+%c Current capacity (mAh or mWh)
+%r Current rate
+%B Battery status (verbose)
+%d Temperature (in degrees Celsius)
+%p Battery load percentage
+%L AC line status (verbose)
+%m Remaining time (to charge or discharge) in minutes
+%h Remaining time (to charge or discharge) in hours
+%t Remaining time (to charge or discharge) in the form `h:min'"
+  (let (charging-state rate temperature hours
+       (charge-full 0.0)
+       (charge-now 0.0)
+       (energy-full 0.0)
+       (energy-now 0.0))
+    ;; SysFS provides information about each battery present in the
+    ;; system in a separate subdirectory.  We are going to merge the
+    ;; available information together.
+    (with-temp-buffer
+      (dolist (dir (ignore-errors
+                   (directory-files
+                    "/sys/class/power_supply/" t "BAT[0-9]$")))
+       (erase-buffer)
+       (ignore-errors (insert-file-contents
+                       (expand-file-name "uevent" dir)))
+       (when (re-search-forward "POWER_SUPPLY_PRESENT=1$" nil t)
+         (goto-char (point-min))
+         (and (re-search-forward "POWER_SUPPLY_STATUS=\\(.*\\)$" nil t)
+              (member charging-state '("Unknown" "Full" nil))
+              (setq charging-state (match-string 1)))
+         (when (re-search-forward
+                 "POWER_SUPPLY_\\(CURRENT\\|POWER\\)_NOW=\\([0-9]*\\)$"
+                 nil t)
+           (setq rate (float (string-to-number (match-string 2)))))
+         (when (re-search-forward "POWER_SUPPLY_TEMP=\\([0-9]*\\)$" nil t)
+           (setq temperature (match-string 1)))
+         (let (full-string now-string)
+           ;; Sysfs may list either charge (mAh) or energy (mWh).
+           ;; Keep track of both, and choose which to report later.
+           (cond ((and (re-search-forward
+                        "POWER_SUPPLY_CHARGE_FULL=\\([0-9]*\\)$" nil t)
+                       (setq full-string (match-string 1))
+                       (re-search-forward
+                        "POWER_SUPPLY_CHARGE_NOW=\\([0-9]*\\)$" nil t)
+                       (setq now-string (match-string 1)))
+                  (setq charge-full (+ charge-full
+                                       (string-to-number full-string))
+                        charge-now  (+ charge-now
+                                       (string-to-number now-string))))
+                 ((and (re-search-forward
+                        "POWER_SUPPLY_ENERGY_FULL=\\([0-9]*\\)$" nil t)
+                       (setq full-string (match-string 1))
+                       (re-search-forward
+                        "POWER_SUPPLY_ENERGY_NOW=\\([0-9]*\\)$" nil t)
+                       (setq now-string (match-string 1)))
+                  (setq energy-full (+ energy-full
+                                       (string-to-number full-string))
+                        energy-now  (+ energy-now
+                                       (string-to-number now-string))))))
+         (goto-char (point-min))
+         (when (and energy-now rate (not (zerop rate))
+                    (re-search-forward
+                      "POWER_SUPPLY_VOLTAGE_NOW=\\([0-9]*\\)$" nil t))
+           (let ((remaining (if (string= charging-state "Discharging")
+                                energy-now
+                              (- energy-full energy-now))))
+             (setq hours (/ (/ (* remaining (string-to-number
+                                              (match-string 1)))
+                                rate)
+                            10000000.0)))))))
+    (list (cons ?c (cond ((or (> charge-full 0) (> charge-now 0))
+                         (number-to-string charge-now))
+                        ((or (> energy-full 0) (> energy-now 0))
+                         (number-to-string energy-now))
+                        (t "N/A")))
+         (cons ?r (if rate (format "%.1f" (/ rate 1000000.0)) "N/A"))
+         (cons ?m (if hours (format "%d" (* hours 60)) "N/A"))
+         (cons ?h (if hours (format "%d" hours) "N/A"))
+         (cons ?t (if hours
+                      (format "%d:%02d" hours (* (- hours (floor hours)) 60))
+                    "N/A"))
+         (cons ?d (or temperature "N/A"))
+         (cons ?B (or charging-state "N/A"))
+         (cons ?p (cond ((> charge-full 0)
+                         (format "%.1f"
+                                 (/ (* 100 charge-now) charge-full)))
+                        ((> energy-full 0)
+                         (format "%.1f"
+                                 (/ (* 100 energy-now) energy-full)))
+                        (t "N/A")))
+         (cons ?L (if (file-readable-p "/sys/class/power_supply/AC/online")
+                      (if (battery-search-for-one-match-in-files
+                           (list "/sys/class/power_supply/AC/online"
+                                 "/sys/class/power_supply/ACAD/online")
+                           "1" 0)
+                          "AC"
+                        "BAT")
+                    "N/A")))))
+
+(defun battery-yeeloong-sysfs ()
+  "Get ACPI status information from Linux (the kernel).
+This function works only on the Lemote Yeeloong.
+
+The following %-sequences are provided:
+%c Current capacity (mAh)
+%r Current rate
+%B Battery status (verbose)
+%b Battery status, empty means high, `-' means low,
+   `!' means critical, and `+' means charging
+%L AC line status (verbose)
+%p Battery load percentage
+%m Remaining time (to charge or discharge) in minutes
+%h Remaining time (to charge or discharge) in hours
+%t Remaining time (to charge or discharge) in the form `h:min'"
+
+  (let (capacity
+       capacity-level
+       status
+       ac-online
+       hours
+       current-now
+       charge-full
+       charge-now)
+
+    (with-temp-buffer
+      (ignore-errors
+       (insert-file-contents "/sys/class/power_supply/yeeloong-bat/uevent")
+       (goto-char 1)
+       (search-forward "POWER_SUPPLY_CHARGE_NOW=")
+       (setq charge-now (read (current-buffer)))
+       (goto-char 1)
+       (search-forward "POWER_SUPPLY_CHARGE_FULL=")
+       (setq charge-full (read (current-buffer)))
+       (goto-char 1)
+       (search-forward "POWER_SUPPLY_CURRENT_NOW=")
+       (setq current-now (read (current-buffer)))
+       (goto-char 1)
+       (search-forward "POWER_SUPPLY_CAPACITY_LEVEL=")
+       (setq capacity-level (buffer-substring (point) (line-end-position)))
+       (goto-char 1)
+       (search-forward "POWER_SUPPLY_STATUS=")
+       (setq status (buffer-substring (point) (line-end-position))))
+       
+      (erase-buffer)
+      (ignore-errors
+       (insert-file-contents
+        "/sys/class/power_supply/yeeloong-ac/online")
+       (goto-char 1)
+       (setq ac-online (read (current-buffer)))
+       (erase-buffer)))
+
+
+    (setq capacity (round (/ (* charge-now 100.0) charge-full)))
+    (when (and current-now (not (= current-now 0)))
+      (if (< current-now 0)
+         ;; Charging
+         (setq hours (/ (- charge-now charge-full) (+ 0.0 current-now)))
+       ;; Discharging
+       (setq hours (/ charge-now (+ 0.0 current-now)))))
+
+    (list (cons ?c (if charge-now
+                      (number-to-string charge-now)
+                    "N/A"))
+         (cons ?r current-now)
+         (cons ?B (cond ((equal capacity-level "Full") "full")
+                        ((equal status "Charging") "charging")
+                        ((equal capacity-level "Low") "low")
+                        ((equal capacity-level "Critical") "critical")
+                        (t "high")))
+         (cons ?b (cond ((equal capacity-level "Full") " ")
+                        ((equal status "Charging") "+")
+                        ((equal capacity-level "Low") "-")
+                        ((equal capacity-level "Critical") "!")
+                        (t " ")))
+         (cons ?h (if hours (number-to-string hours) "N/A"))
+         (cons ?m (if hours (number-to-string (* 60 hours)) "N/A"))
+         (cons ?t (if hours
+                      (format "%d:%d"
+                              (/ (round (* 60 hours)) 60)
+                              (% (round (* 60 hours)) 60))
+                    "N/A"))
+         (cons ?p (if capacity (number-to-string capacity) "N/A"))
+         (cons ?L (if (eq ac-online 1) "AC" "BAT")))))
+\f
 ;;; `pmset' interface for Darwin (OS X).
 
 (defun battery-pmset ()
@@ -469,5 +687,4 @@ MATCH-NUM in the match.  Otherwise, return nil."
 \f
 (provide 'battery)
 
-;; arch-tag: 65916f50-4754-4b6b-ac21-0b510f545a37
 ;;; battery.el ends here