| 1 | ;;; battery.el --- display battery status information. |
| 2 | |
| 3 | ;; Copyright (C) 1997, 1998, 2000 Free Software Foundation, Inc. |
| 4 | |
| 5 | ;; Author: Ralph Schleicher <rs@nunatak.allgaeu.org> |
| 6 | ;; Keywords: hardware |
| 7 | |
| 8 | ;; This file is part of GNU Emacs. |
| 9 | |
| 10 | ;; GNU Emacs is free software; you can redistribute it and/or modify |
| 11 | ;; it under the terms of the GNU General Public License as published by |
| 12 | ;; the Free Software Foundation; either version 2, or (at your option) |
| 13 | ;; any later version. |
| 14 | |
| 15 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 18 | ;; GNU General Public License for more details. |
| 19 | |
| 20 | ;; You should have received a copy of the GNU General Public License |
| 21 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
| 22 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| 23 | ;; Boston, MA 02111-1307, USA. |
| 24 | |
| 25 | ;;; Commentary: |
| 26 | |
| 27 | ;; There is at present only a function interpreting the new `/proc/apm' |
| 28 | ;; file format of Linux version 1.3.58 or newer. That is, what a lucky |
| 29 | ;; coincidence, exactly the interface provided by the author's labtop. |
| 30 | |
| 31 | ;;; Code: |
| 32 | |
| 33 | (require 'timer) |
| 34 | |
| 35 | \f |
| 36 | |
| 37 | (defgroup battery nil |
| 38 | "Display battery status information." |
| 39 | :prefix "battery-" |
| 40 | :group 'hardware) |
| 41 | |
| 42 | (defcustom battery-status-function |
| 43 | (cond ((and (eq system-type 'gnu/linux) |
| 44 | (file-readable-p "/proc/apm")) |
| 45 | 'battery-linux-proc-apm)) |
| 46 | "*Function for getting battery status information. |
| 47 | The function have to return an alist of conversion definitions. |
| 48 | Cons cells are of the form |
| 49 | |
| 50 | (CONVERSION . REPLACEMENT-TEXT) |
| 51 | |
| 52 | CONVERSION is the character code of a \"conversion specification\" |
| 53 | introduced by a `%' character in a control string." |
| 54 | :type 'function |
| 55 | :group 'battery) |
| 56 | |
| 57 | (defcustom battery-echo-area-format |
| 58 | (cond ((eq battery-status-function 'battery-linux-proc-apm) |
| 59 | "Power %L, battery %B (%p%% load, remaining time %t)")) |
| 60 | "*Control string formatting the string to display in the echo area. |
| 61 | Ordinary characters in the control string are printed as-is, while |
| 62 | conversion specifications introduced by a `%' character in the control |
| 63 | string are substituted as defined by the current value of the variable |
| 64 | `battery-status-function'." |
| 65 | :type '(choice string (const nil)) |
| 66 | :group 'battery) |
| 67 | |
| 68 | (defvar battery-mode-line-string nil |
| 69 | "String to display in the mode line.") |
| 70 | |
| 71 | (defcustom battery-mode-line-format |
| 72 | (cond ((eq battery-status-function 'battery-linux-proc-apm) |
| 73 | " [%b%p%%]")) |
| 74 | "*Control string formatting the string to display in the mode line. |
| 75 | Ordinary characters in the control string are printed as-is, while |
| 76 | conversion specifications introduced by a `%' character in the control |
| 77 | string are substituted as defined by the current value of the variable |
| 78 | `battery-status-function'." |
| 79 | :type '(choice string (const nil)) |
| 80 | :group 'battery) |
| 81 | |
| 82 | (defcustom battery-update-interval 60 |
| 83 | "*Seconds after which the battery status will be updated." |
| 84 | :type 'integer |
| 85 | :group 'battery) |
| 86 | |
| 87 | (defvar battery-update-timer nil |
| 88 | "Interval timer object.") |
| 89 | |
| 90 | ;;;###autoload |
| 91 | (defun battery () |
| 92 | "Display battery status information in the echo area. |
| 93 | The text being displayed in the echo area is controlled by the variables |
| 94 | `battery-echo-area-format' and `battery-status-function'." |
| 95 | (interactive) |
| 96 | (message "%s" (if (and battery-echo-area-format battery-status-function) |
| 97 | (battery-format battery-echo-area-format |
| 98 | (funcall battery-status-function)) |
| 99 | "Battery status not available"))) |
| 100 | |
| 101 | ;;;###autoload |
| 102 | (defun display-battery () |
| 103 | "Display battery status information in the mode line. |
| 104 | The text being displayed in the mode line is controlled by the variables |
| 105 | `battery-mode-line-format' and `battery-status-function'. |
| 106 | The mode line will be updated automatically every `battery-update-interval' |
| 107 | seconds." |
| 108 | (interactive) |
| 109 | (setq battery-mode-line-string "") |
| 110 | (or global-mode-string (setq global-mode-string '(""))) |
| 111 | (add-to-list 'global-mode-string 'battery-mode-line-string t) |
| 112 | (and battery-update-timer (cancel-timer battery-update-timer)) |
| 113 | (setq battery-update-timer (run-at-time nil battery-update-interval |
| 114 | 'battery-update-handler)) |
| 115 | (battery-update)) |
| 116 | |
| 117 | (defun battery-update-handler () |
| 118 | (battery-update) |
| 119 | (sit-for 0)) |
| 120 | |
| 121 | (defun battery-update () |
| 122 | "Update battery status information in the mode line." |
| 123 | (setq battery-mode-line-string (if (and battery-mode-line-format |
| 124 | battery-status-function) |
| 125 | (battery-format |
| 126 | battery-mode-line-format |
| 127 | (funcall battery-status-function)) |
| 128 | "")) |
| 129 | (force-mode-line-update)) |
| 130 | |
| 131 | \f |
| 132 | ;;; `/proc/apm' interface for Linux. |
| 133 | |
| 134 | (defconst battery-linux-proc-apm-regexp |
| 135 | (concat "^\\([^ ]+\\)" ; Driver version. |
| 136 | " \\([^ ]+\\)" ; APM BIOS version. |
| 137 | " 0x\\([0-9a-f]+\\)" ; APM BIOS flags. |
| 138 | " 0x\\([0-9a-f]+\\)" ; AC line status. |
| 139 | " 0x\\([0-9a-f]+\\)" ; Battery status. |
| 140 | " 0x\\([0-9a-f]+\\)" ; Battery flags. |
| 141 | " \\(-?[0-9]+\\)%" ; Load percentage. |
| 142 | " \\(-?[0-9]+\\)" ; Remaining time. |
| 143 | " \\(.*\\)" ; Time unit. |
| 144 | "$") |
| 145 | "Regular expression matching contents of `/proc/apm'.") |
| 146 | |
| 147 | (defun battery-linux-proc-apm () |
| 148 | "Get APM status information from Linux kernel. |
| 149 | This function works only with the new `/proc/apm' format introduced |
| 150 | in Linux version 1.3.58. |
| 151 | |
| 152 | The following %-sequences are provided: |
| 153 | %v Linux driver version |
| 154 | %V APM BIOS version |
| 155 | %I APM BIOS status (verbose) |
| 156 | %L AC line status (verbose) |
| 157 | %B Battery status (verbose) |
| 158 | %b Battery status, empty means high, `-' means low, |
| 159 | `!' means critical, and `+' means charging |
| 160 | %p battery load percentage |
| 161 | %s Remaining time in seconds |
| 162 | %m Remaining time in minutes |
| 163 | %h Remaining time in hours |
| 164 | %t Remaining time in the form `h:min'" |
| 165 | (let (driver-version bios-version bios-interface line-status |
| 166 | battery-status battery-status-symbol load-percentage |
| 167 | seconds minutes hours remaining-time buffer tem) |
| 168 | (unwind-protect |
| 169 | (save-excursion |
| 170 | (setq buffer (get-buffer-create " *battery*")) |
| 171 | (set-buffer buffer) |
| 172 | (erase-buffer) |
| 173 | (battery-insert-file-contents "/proc/apm") |
| 174 | (re-search-forward battery-linux-proc-apm-regexp) |
| 175 | (setq driver-version (match-string 1)) |
| 176 | (setq bios-version (match-string 2)) |
| 177 | (setq tem (battery-hex-to-int-2 (match-string 3))) |
| 178 | (if (not (logand tem 2)) |
| 179 | (setq bios-interface "not supported") |
| 180 | (setq bios-interface "enabled") |
| 181 | (cond ((logand tem 16) (setq bios-interface "disabled")) |
| 182 | ((logand tem 32) (setq bios-interface "disengaged"))) |
| 183 | (setq tem (battery-hex-to-int-2 (match-string 4))) |
| 184 | (cond ((= tem 0) (setq line-status "off-line")) |
| 185 | ((= tem 1) (setq line-status "on-line")) |
| 186 | ((= tem 2) (setq line-status "on backup"))) |
| 187 | (setq tem (battery-hex-to-int-2 (match-string 6))) |
| 188 | (if (= tem 255) |
| 189 | (setq battery-status "N/A") |
| 190 | (setq tem (battery-hex-to-int-2 (match-string 5))) |
| 191 | (cond ((= tem 0) (setq battery-status "high" |
| 192 | battery-status-symbol "")) |
| 193 | ((= tem 1) (setq battery-status "low" |
| 194 | battery-status-symbol "-")) |
| 195 | ((= tem 2) (setq battery-status "critical" |
| 196 | battery-status-symbol "!")) |
| 197 | ((= tem 3) (setq battery-status "charging" |
| 198 | battery-status-symbol "+"))) |
| 199 | (setq load-percentage (match-string 7)) |
| 200 | (setq seconds (string-to-number (match-string 8))) |
| 201 | (and (string-equal (match-string 9) "min") |
| 202 | (setq seconds (* 60 seconds))) |
| 203 | (setq minutes (/ seconds 60) |
| 204 | hours (/ seconds 3600)) |
| 205 | (setq remaining-time |
| 206 | (format "%d:%02d" hours (- minutes (* 60 hours)))))))) |
| 207 | (list (cons ?v (or driver-version "N/A")) |
| 208 | (cons ?V (or bios-version "N/A")) |
| 209 | (cons ?I (or bios-interface "N/A")) |
| 210 | (cons ?L (or line-status "N/A")) |
| 211 | (cons ?B (or battery-status "N/A")) |
| 212 | (cons ?b (or battery-status-symbol "")) |
| 213 | (cons ?p (or load-percentage "N/A")) |
| 214 | (cons ?s (or (and seconds (number-to-string seconds)) "N/A")) |
| 215 | (cons ?m (or (and minutes (number-to-string minutes)) "N/A")) |
| 216 | (cons ?h (or (and hours (number-to-string hours)) "N/A")) |
| 217 | (cons ?t (or remaining-time "N/A"))))) |
| 218 | |
| 219 | \f |
| 220 | ;;; Private functions. |
| 221 | |
| 222 | (defun battery-format (format alist) |
| 223 | "Substitute %-sequences in FORMAT." |
| 224 | (let ((index 0) |
| 225 | (length (length format)) |
| 226 | (result "") |
| 227 | char flag elem) |
| 228 | (while (< index length) |
| 229 | (setq char (aref format index)) |
| 230 | (if (not flag) |
| 231 | (if (char-equal char ?%) |
| 232 | (setq flag t) |
| 233 | (setq result (concat result (char-to-string char)))) |
| 234 | (cond ((char-equal char ?%) |
| 235 | (setq result (concat result "%"))) |
| 236 | ((setq elem (assoc char alist)) |
| 237 | (setq result (concat result (cdr elem))))) |
| 238 | (setq flag nil)) |
| 239 | (setq index (1+ index))) |
| 240 | (or (null flag) |
| 241 | (setq result (concat result "%"))) |
| 242 | result)) |
| 243 | |
| 244 | (defun battery-insert-file-contents (file-name) |
| 245 | "Insert contents of file FILE-NAME after point. |
| 246 | FILE-NAME can be a non-ordinary file, for example, a named pipe. |
| 247 | Return t if file exists." |
| 248 | (let ((load-read-function 'battery-read-function) |
| 249 | (load-source-file-function nil) |
| 250 | (load-path '(".")) |
| 251 | (load-history nil)) |
| 252 | (save-excursion |
| 253 | (load file-name nil t t)))) |
| 254 | |
| 255 | (defun battery-read-function (&optional stream) |
| 256 | "Function for reading expressions from STREAM. |
| 257 | Value is always nil." |
| 258 | (let (char) |
| 259 | (while (not (< (setq char (get-file-char)) 0)) |
| 260 | (insert char)))) |
| 261 | |
| 262 | (defconst battery-hex-map '((?0 . 0) (?1 . 1) (?2 . 2) (?3 . 3) |
| 263 | (?4 . 4) (?5 . 5) (?6 . 6) (?7 . 7) |
| 264 | (?8 . 8) (?9 . 9) (?a . 10) (?b . 11) |
| 265 | (?c . 12) (?d . 13) (?e . 14) (?f . 15))) |
| 266 | |
| 267 | (defun battery-hex-to-int (string) |
| 268 | "Convert a hexadecimal number (a string) into a number." |
| 269 | (save-match-data |
| 270 | (and (string-match "^[ \t]+" string) |
| 271 | (setq string (substring string (match-end 0)))) |
| 272 | (and (string-match "^0[xX]" string) |
| 273 | (setq string (substring string (match-end 0))))) |
| 274 | (battery-hex-to-int-2 string)) |
| 275 | |
| 276 | (defun battery-hex-to-int-2 (string) |
| 277 | (let ((index 0) |
| 278 | (length (length string)) |
| 279 | (value 0) |
| 280 | (elem nil)) |
| 281 | (while (and (< index length) |
| 282 | (setq elem (assoc (downcase (aref string index)) |
| 283 | battery-hex-map))) |
| 284 | (setq value (+ (* 16 value) (cdr elem)) |
| 285 | index (1+ index))) |
| 286 | value)) |
| 287 | |
| 288 | \f |
| 289 | (provide 'battery) |
| 290 | |
| 291 | ;;; battery.el ends here |