├── .gitignore ├── img ├── bounded.png ├── boxed.png ├── gridded.png ├── plain.png └── screenshot.png ├── Readme.org └── symon.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | 3 | -------------------------------------------------------------------------------- /img/bounded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk-phi/symon/HEAD/img/bounded.png -------------------------------------------------------------------------------- /img/boxed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk-phi/symon/HEAD/img/boxed.png -------------------------------------------------------------------------------- /img/gridded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk-phi/symon/HEAD/img/gridded.png -------------------------------------------------------------------------------- /img/plain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk-phi/symon/HEAD/img/plain.png -------------------------------------------------------------------------------- /img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zk-phi/symon/HEAD/img/screenshot.png -------------------------------------------------------------------------------- /Readme.org: -------------------------------------------------------------------------------- 1 | * symon.el 2 | 3 | コンパクトでおしゃれなシステムモニター 4 | 5 | tiny graphical system monitor 6 | 7 | ** Screenshot 8 | 9 | [[file:img/screenshot.png]] 10 | 11 | ** NOTE: RECENT INCOMPATIBLE CHANGES 12 | 13 | - sparkline types are renamed: 14 | - =symon-sparkline-plain= -> =plain= 15 | - =symon-sparkline-bounded= -> =bounded= 16 | - =symon-sparkline-boxed= -> =boxed= 17 | - =symon-sparkline-gridded= -> =gridded= 18 | 19 | ** Usage 20 | 21 | Load =symon= 22 | 23 | : (require 'symon) 24 | 25 | and turn on =symon-mode=. 26 | 27 | : (symon-mode) 28 | 29 | then a tiny system monitor is displayed in minibuffer, during idle. 30 | 31 | ** Customization 32 | 33 | - =symon-monitors= :: List of symon monitors used to fetch system 34 | statuses. You can set this variable to specify what metrics to 35 | display and what to not. List of available monitors is provided 36 | below. 37 | 38 | - =symon-refresh-rate= :: Refresh rate of symon display. 39 | 40 | - =symon-delay= :: Delay in seconds until symon is displayed. 41 | 42 | - =symon-history-size= :: Number of old values to keep. Sparklines 43 | grow faster when set smaller. 44 | 45 | You need to restart =symon-mode= in order to reflect changes of these 46 | 4 options above. 47 | 48 | - =symon-sparkline-height= :: Height of sparklines. 49 | 50 | - =symon-sparkline-width= :: Width of sparklines. 51 | 52 | - =symon-sparkline-thickness= :: Line width of sparklines. 53 | 54 | - =symon-sparkline-ascent= :: Adjust vertical position of sparklines. 55 | 56 | - =symon-sparkline-type= :: Type of sparklines. following 57 | preconfigured types are available: 58 | 59 | - =plain= 60 | 61 | [[file:img/plain.png]] 62 | 63 | - =bounded= 64 | 65 | [[file:img/bounded.png]] 66 | 67 | - =boxed= 68 | 69 | [[file:img/boxed.png]] 70 | 71 | - =gridded= 72 | 73 | [[file:img/gridded.png]] 74 | 75 | ** Preconfigured Monitors 76 | 77 | - GNU/Linux 78 | 79 | - =symon-linux-memory-monitor= :: memory usage (%) and swapped 80 | memory (MB) via =/proc/meminfo= 81 | 82 | - =symon-linux-cpu-monitor= :: CPU load (%) via =/proc/stat= 83 | 84 | - =symon-linux-battery-monitor= :: remaining battery (%) via 85 | built-in library =battery.el= 86 | 87 | - =symon-linux-network-rx-monitor= :: network RX (KB/s) via 88 | =/proc/net/dev= 89 | 90 | - =symon-linux-network-tx-monitor= :: network TX (KB/s) via 91 | =/proc/net/dev= 92 | 93 | - Darwin (Mac OS X) 94 | 95 | - =symon-darwin-memory-monitor= :: memory usage (%) and swapped 96 | memory (MB) via =sysctl= 97 | 98 | - =symon-darwin-cpu-monitor= :: CPU load (%) via =hostinfo= 99 | 100 | - =symon-darwin-battery-monitor= :: remaining battery (%) via 101 | built-in library =battery.el= 102 | 103 | - =symon-darwin-network-rx-monitor= :: network RX (KB/s) via 104 | =netstat= 105 | 106 | - =symon-darwin-network-tx-monitor= :: network TX (KB/s) via 107 | =netstat= 108 | 109 | - Windows 110 | 111 | - =symon-windows-memory-monitor= :: memory usage (%) via =WMI= 112 | 113 | - =symon-windows-page-file-monitor= :: page file usage (MB) via =WMI= 114 | 115 | - =symon-windows-cpu-monitor= :: CPU load (%) via =WMI= 116 | 117 | - =symon-windows-battery-monitor= :: remaining battery (%) via =WMI= 118 | 119 | - =symon-windows-network-rx-monitor= :: network RX (KB/s) via =WMI= 120 | 121 | - =symon-windows-network-tx-monitor= :: network TX (KB/s) via =WMI= 122 | 123 | - misc 124 | 125 | - =symon-current-time-monitor= :: current Time (hh::mm) via built-in 126 | function =format-time-string= 127 | 128 | ** Contributors 129 | 130 | - [[https://github.com/pierre-lecocq][Pierre Lecocq]] added darwin support. 131 | 132 | Thanks! 133 | -------------------------------------------------------------------------------- /symon.el: -------------------------------------------------------------------------------- 1 | ;;; symon.el --- tiny graphical system monitor 2 | 3 | ;; Copyright (C) 2015 zk_phi 4 | 5 | ;; This program is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation; either version 2 of the License, or 8 | ;; (at your option) any later version. 9 | ;; 10 | ;; This program is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | ;; 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program; if not, write to the Free Software 17 | ;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | 19 | ;; Author: zk_phi 20 | ;; URL: http://hins11.yu-yake.com/ 21 | ;; Version: 1.2.0 22 | 23 | ;;; Commentary: 24 | 25 | ;; Load this script 26 | ;; 27 | ;; (require 'symon) 28 | ;; 29 | ;; and turn on `symon-mode'. 30 | ;; 31 | ;; (symon-mode) 32 | ;; 33 | ;; then a tiny system monitor is displayed in minibuffer, during idle. 34 | 35 | ;;; Change Log: 36 | 37 | ;; 1.0.0 first release 38 | ;; 1.1.0 add option symon-sparkline-thickness 39 | ;; 1.1.1 add symon-windows-page-file-monitor 40 | ;; 1.1.2 add darwin support (mac os x) 41 | ;; 1.2.0 add paging feature 42 | 43 | ;;; Code: 44 | 45 | (require 'battery) 46 | (require 'ring) 47 | 48 | (defconst symon-version "1.2.0") 49 | 50 | (defgroup symon nil 51 | "tiny graphical system monitor" 52 | :group 'emacs) 53 | 54 | ;; + customs 55 | 56 | ;; core 57 | 58 | (defcustom symon-refresh-rate 4 59 | "refresh rate of symon display. *set this option BEFORE 60 | enabling `symon-mode'.*" 61 | :group 'symon) 62 | 63 | (defcustom symon-delay 2 64 | "delay in seconds until symon is displayed. *set this option 65 | BEFORE enabling `symon-mode'.*" 66 | :group 'symon) 67 | 68 | (defcustom symon-history-size 50 69 | "number of old values to keep. sparklines grow faster when set 70 | smaller. *set this option BEFORE enabling `symon-mode'.*" 71 | :group 'symon) 72 | 73 | (defcustom symon-monitors 74 | (cond ((memq system-type '(gnu/linux cygwin)) 75 | '(symon-linux-memory-monitor 76 | symon-linux-cpu-monitor 77 | symon-linux-network-rx-monitor 78 | symon-linux-network-tx-monitor)) 79 | ((memq system-type '(darwin)) 80 | '(symon-darwin-memory-monitor 81 | symon-darwin-cpu-monitor 82 | symon-darwin-network-rx-monitor 83 | symon-darwin-network-tx-monitor)) 84 | ((memq system-type '(windows-nt)) 85 | '(symon-windows-memory-monitor 86 | symon-windows-cpu-monitor 87 | symon-windows-network-rx-monitor 88 | symon-windows-network-tx-monitor))) 89 | "List of monitors used to read system statuses. This variable 90 | also can be a list of lists from version 1.2, that case 91 | monitors are displayed in multiple pages. *set this option 92 | BEFORE enabling `symon-mode'.*") 93 | 94 | ;; sparkline 95 | 96 | (defcustom symon-sparkline-height 11 97 | "height of sparklines." 98 | :group 'symon) 99 | 100 | (defcustom symon-sparkline-width 80 101 | "width of sparklines." 102 | :group 'symon) 103 | 104 | (defcustom symon-sparkline-ascent 100 105 | "`:ascent' property for sparklines." 106 | :group 'symon) 107 | 108 | (defcustom symon-sparkline-thickness 2 109 | "line width of sparklines." 110 | :group 'symon) 111 | 112 | (defcustom symon-sparkline-type 'gridded 113 | "type of sparklines." 114 | :group 'symon) 115 | 116 | ;; some darwin builds cannot render xbm images (foreground color is 117 | ;; always black), so convert to xpm before rendering. 118 | (defcustom symon-sparkline-use-xpm (eq system-type 'darwin) 119 | "when non-nil, convert sparklines to xpm from xbm before 120 | rendering." 121 | :group 'symon) 122 | 123 | ;; network monitor 124 | 125 | (defcustom symon-network-rx-upper-bound 300 126 | "upper-bound of sparkline for network RX status." 127 | :group 'symon) 128 | 129 | (defcustom symon-network-tx-upper-bound 100 130 | "upper-bound of sparkline for network TX status." 131 | :group 'symon) 132 | 133 | (defcustom symon-network-rx-lower-bound 0 134 | "lower-bound of sparkline for network RX status." 135 | :group 'symon) 136 | 137 | (defcustom symon-network-tx-lower-bound 0 138 | "lower-bound of sparkline for network TX status." 139 | :group 'symon) 140 | 141 | ;; page-file monitor 142 | 143 | (defcustom symon-windows-page-file-upper-bound 2000 144 | "upper-bound of sparkline for page file usage." 145 | :group 'symon) 146 | 147 | ;; + utilities 148 | ;; + general 149 | 150 | (defun symon--flatten (lst) 151 | "flatten LST" 152 | (if (consp lst) 153 | (apply 'nconc (mapcar 'symon--flatten lst)) 154 | (list lst))) 155 | 156 | ;; + sparkline generator 157 | 158 | ;; sparkline-types are internally a symbol with property 159 | ;; 'symon-sparkline-type associated to a function that generates a 160 | ;; 2d-bool-vector. 161 | 162 | (defvar symon--sparkline-base-cache 163 | [nil symon-sparkline-width symon-sparkline-height nil]) 164 | (defun symon--get-sparkline-base () 165 | (unless (and (eq (aref symon--sparkline-base-cache 0) symon-sparkline-type) 166 | (= (aref symon--sparkline-base-cache 1) symon-sparkline-width) 167 | (= (aref symon--sparkline-base-cache 2) symon-sparkline-height)) 168 | (aset symon--sparkline-base-cache 0 symon-sparkline-type) 169 | (aset symon--sparkline-base-cache 1 symon-sparkline-width) 170 | (aset symon--sparkline-base-cache 2 symon-sparkline-height) 171 | (aset symon--sparkline-base-cache 3 172 | (funcall (get symon-sparkline-type 'symon-sparkline-type)))) 173 | (copy-sequence (aref symon--sparkline-base-cache 3))) 174 | 175 | (defun symon--make-sparkline (list &optional minimum maximum) 176 | "make sparkline image from LIST." 177 | (let ((num-samples (length list))) 178 | (unless (zerop num-samples) 179 | (let* ((image-data (symon--get-sparkline-base)) 180 | (maximum (if maximum (float maximum) 100.0)) 181 | (minimum (if minimum (float minimum) 0.0)) 182 | (topmargin (1- symon-sparkline-thickness)) 183 | (height (- symon-sparkline-height topmargin)) 184 | (height-per-point (/ height (1+ (- maximum minimum)))) 185 | (width-per-sample (/ symon-sparkline-width (float num-samples))) 186 | (samples (apply 'vector list)) 187 | sample y ix) 188 | (dotimes (x symon-sparkline-width) 189 | (setq sample (aref samples (floor (/ x width-per-sample)))) 190 | (when (numberp sample) 191 | (setq y (floor (* (- sample minimum) height-per-point))) 192 | (when (and (<= 0 y) (< y height)) 193 | (dotimes (dy symon-sparkline-thickness) 194 | (aset image-data 195 | (+ (* (- symon-sparkline-height (+ y dy) 1) symon-sparkline-width) x) 196 | t))))) 197 | `(image :type xbm :data ,image-data :ascent ,symon-sparkline-ascent 198 | :height ,symon-sparkline-height :width ,symon-sparkline-width))))) 199 | 200 | (defun symon--convert-sparkline-to-xpm (sparkline) 201 | "convert sparkline to an xpm image." 202 | (let ((data (plist-get (cdr sparkline) :data))) 203 | (with-temp-buffer 204 | (insert (format "/* XPM */ 205 | static char * sparkline_xpm[] = { \"%d %d 2 1\", \"@ c %s\", \". c none\"" 206 | symon-sparkline-width symon-sparkline-height 207 | (face-foreground 'default))) 208 | (let ((ix 0)) 209 | (dotimes (x symon-sparkline-height) 210 | (insert ",\n\"") 211 | (dotimes (y symon-sparkline-width) 212 | (insert (if (aref data ix) ?@ ?.)) 213 | (setq ix (1+ ix))) 214 | (insert "\""))) 215 | (insert "};") 216 | `(image :type xpm :data ,(buffer-string) :ascent ,symon-sparkline-ascent 217 | :height ,symon-sparkline-height :width ,symon-sparkline-width)))) 218 | 219 | ;; + symon monitor generator 220 | 221 | ;; a symon monitor is internally a symbol with property 'symon-monitor 222 | ;; associated to a vector of 3 functions: [SETUP-FN CLEANUP-FN 223 | ;; DISPLAY-FN]. SETUP-FN is called on activation of `symon-mode', and 224 | ;; expected to setup Emacs to fetch status values in a specific 225 | ;; interval. CLEANUP-FN is called on deactivation and expected to tell 226 | ;; Emacs to stop fetching. DISPLAY-FN is called just before displaying 227 | ;; monitor, and must return display string for the monitor. 228 | 229 | (defun symon--make-history-ring () 230 | "like `(make-ring symon-history-size)' but filled with `nil'." 231 | (cons 0 (cons symon-history-size (make-vector symon-history-size nil)))) 232 | 233 | (defmacro define-symon-monitor (name &rest plist) 234 | "define a new symon monitor NAME. following keywords are 235 | supoprted in PLIST: 236 | 237 | :setup (default: nil) 238 | 239 | an expression evaluated when activating symon-mode, and 240 | expected to do some preparation. 241 | 242 | :cleanup (default: nil) 243 | 244 | an expression evaluated when deactivating symon-mode, and 245 | expected to do some cleanup. 246 | 247 | :fetch (default: nil) 248 | 249 | an expression that evaluates to the latest status value. the 250 | value must be a number (otherwise `N/A' is displayed as the 251 | value). 252 | 253 | :interval (default: symon-refresh-rate) 254 | 255 | fetch interval in seconds. 256 | 257 | :index (default: \"\") 258 | 259 | string prepended to the status value (\"MEM:\" for memory 260 | monitor, for example). 261 | 262 | :unit (default: \"\") 263 | 264 | string appended to the status value (\"%\" for memory 265 | monitor, for example). 266 | 267 | :annotation (default: nil) 268 | 269 | an expression that evaluates to the annotation string for the 270 | metrics (\"xxxKB Swapped\" for memory monitor, for 271 | example). if this expression returns a non-nil value, it is 272 | surrounded with parentheses and appended to the status value. 273 | 274 | :display (default: nil) 275 | 276 | an expression evaluated before updating symon display. when 277 | this expression evaluates to a non-nil value, it will be 278 | displayed instead of standard symon display format. 279 | 280 | :sparkline (default: nil) 281 | 282 | when non-nil, sparklines are rendered. 283 | 284 | :lower-bound (default: 100.0) 285 | 286 | upper bound of sparkline. 287 | 288 | :upper-bound (default: 0.0) 289 | 290 | lower bound of sparkline." 291 | (let* ((cell (make-vector 2 nil)) 292 | (sparkline (plist-get plist :sparkline)) 293 | (interval (or (plist-get plist :interval) 'symon-refresh-rate)) 294 | (display (plist-get plist :display)) 295 | (update-fn 296 | `(lambda () 297 | (ring-insert (aref ,cell 0) ,(plist-get plist :fetch)))) 298 | (setup-fn 299 | `(lambda () 300 | (aset ,cell 0 (symon--make-history-ring)) 301 | (aset ,cell 1 (run-with-timer 0 ,interval ,update-fn)) 302 | ,(plist-get plist :setup) 303 | (funcall ,update-fn))) 304 | (cleanup-fn 305 | `(lambda () 306 | (cancel-timer (aref ,cell 1)) 307 | ,(plist-get plist :cleanup))) 308 | (display-fn 309 | (if display `(lambda () (concat ,display " ")) 310 | `(lambda () 311 | (let* ((lst (ring-elements (aref ,cell 0))) 312 | (val (car lst))) 313 | (concat ,(plist-get plist :index) 314 | (if (not (numberp val)) "N/A " 315 | (concat (format "%d%s " val ,(or (plist-get plist :unit) "")) 316 | (let ((annot ,(plist-get plist :annotation))) 317 | (when annot (concat "(" annot ") "))))) 318 | ,(when sparkline 319 | `(when (window-system) 320 | (let ((sparkline (symon--make-sparkline 321 | lst 322 | ,(plist-get plist :lower-bound) 323 | ,(plist-get plist :upper-bound)))) 324 | (when symon-sparkline-use-xpm 325 | (setq sparkline 326 | (symon--convert-sparkline-to-xpm sparkline))) 327 | (concat (propertize " " 'display sparkline) " ")))))))))) 328 | `(put ',name 'symon-monitor (vector ,setup-fn ,cleanup-fn ,display-fn)))) 329 | 330 | ;; + process management 331 | 332 | (defvar symon--process-buffer-name " *symon-process*") 333 | (defvar symon--process-reference-count 0) 334 | 335 | (defun symon--read-value-from-process-buffer (index) 336 | "Read a value from a specific buffer" 337 | (when (get-buffer symon--process-buffer-name) 338 | (with-current-buffer symon--process-buffer-name 339 | (when (save-excursion 340 | (search-backward-regexp (concat index ":\\([0-9]+\\)\\>") nil t)) 341 | (read (match-string 1)))))) 342 | 343 | (defun symon--maybe-start-process (cmd) 344 | (setq symon--process-reference-count 345 | (1+ symon--process-reference-count)) 346 | (unless (get-buffer symon--process-buffer-name) 347 | (let ((proc (start-process-shell-command 348 | "symon-process" symon--process-buffer-name cmd)) 349 | (filter (lambda (proc str) 350 | (when (get-buffer symon--process-buffer-name) 351 | (with-current-buffer symon--process-buffer-name 352 | (when (and (string-match "-" str) (search-backward "----" nil t)) 353 | (delete-region 1 (point))) 354 | (goto-char (1+ (buffer-size))) 355 | (insert str)))))) 356 | (set-process-query-on-exit-flag proc nil) 357 | (set-process-filter proc filter)))) 358 | 359 | (defun symon--maybe-kill-process () 360 | (setq symon--process-reference-count 361 | (1- symon--process-reference-count)) 362 | (when (and (zerop symon--process-reference-count) 363 | (get-buffer symon--process-buffer-name)) 364 | (kill-buffer symon--process-buffer-name))) 365 | 366 | ;; + predefined monitors 367 | ;; + linux monitors 368 | 369 | (defun symon-linux--read-lines (file reader indices) 370 | (with-temp-buffer 371 | (insert-file-contents file) 372 | (goto-char 1) 373 | (mapcar (lambda (index) 374 | (save-excursion 375 | (when (search-forward-regexp (concat "^" index "\\(.*\\)$") nil t) 376 | (if reader 377 | (funcall reader (match-string 1)) 378 | (match-string 1))))) 379 | indices))) 380 | 381 | (defvar symon-linux--last-cpu-ticks nil) 382 | 383 | (define-symon-monitor symon-linux-cpu-monitor 384 | :index "CPU:" :unit "%" :sparkline t 385 | :setup (setq symon-linux--last-cpu-ticks nil) 386 | :fetch (cl-destructuring-bind (cpu) 387 | (symon-linux--read-lines 388 | "/proc/stat" (lambda (str) (mapcar 'read (split-string str nil t))) '("cpu")) 389 | (let ((total (apply '+ cpu)) (idle (nth 3 cpu))) 390 | (prog1 (when symon-linux--last-cpu-ticks 391 | (let ((total-diff (- total (car symon-linux--last-cpu-ticks))) 392 | (idle-diff (- idle (cdr symon-linux--last-cpu-ticks)))) 393 | (unless (zerop total-diff) 394 | (/ (* (- total-diff idle-diff) 100) total-diff)))) 395 | (setq symon-linux--last-cpu-ticks (cons total idle)))))) 396 | 397 | (define-symon-monitor symon-linux-memory-monitor 398 | :index "MEM:" :unit "%" :sparkline t 399 | :fetch (cl-destructuring-bind (memtotal memavailable memfree buffers cached) 400 | (symon-linux--read-lines 401 | "/proc/meminfo" (lambda (str) (and str (read str))) 402 | '("MemTotal:" "MemAvailable:" "MemFree:" "Buffers:" "Cached:")) 403 | (if memavailable 404 | (/ (* (- memtotal memavailable) 100) memtotal) 405 | (/ (* (- memtotal (+ memfree buffers cached)) 100) memtotal))) 406 | :annotation (cl-destructuring-bind (swaptotal swapfree) 407 | (symon-linux--read-lines 408 | "/proc/meminfo" 'read '("SwapTotal:" "SwapFree:")) 409 | (let ((swapped (/ (- swaptotal swapfree) 1000))) 410 | (unless (zerop swapped) (format "%dMB Swapped" swapped))))) 411 | 412 | (define-symon-monitor symon-linux-battery-monitor 413 | :index "BAT:" :unit "%" :sparkline t 414 | :fetch (when battery-status-function 415 | (read (cdr (assoc ?p (funcall battery-status-function)))))) 416 | 417 | (defvar symon-linux--last-network-rx nil) 418 | 419 | (define-symon-monitor symon-linux-network-rx-monitor 420 | :index "RX:" :unit "KB/s" :sparkline t 421 | :upper-bound symon-network-rx-upper-bound 422 | :lower-bound symon-network-rx-lower-bound 423 | :setup (setq symon-linux--last-network-rx nil) 424 | :fetch (with-temp-buffer 425 | (insert-file-contents "/proc/net/dev") 426 | (goto-char 1) 427 | (let ((rx 0)) 428 | (while (search-forward-regexp "^[\s\t]*\\(.*\\):" nil t) 429 | (unless (string= (match-string 1) "lo") 430 | (setq rx (+ rx (read (current-buffer)))))) 431 | (prog1 (when symon-linux--last-network-rx 432 | (/ (- rx symon-linux--last-network-rx) symon-refresh-rate 1000)) 433 | (setq symon-linux--last-network-rx rx))))) 434 | 435 | (defvar symon-linux--last-network-tx nil) 436 | 437 | (define-symon-monitor symon-linux-network-tx-monitor 438 | :index "TX:" :unit "KB/s" :sparkline t 439 | :upper-bound symon-network-tx-upper-bound 440 | :lower-bound symon-network-tx-lower-bound 441 | :setup (setq symon-linux--last-network-tx nil) 442 | :fetch (with-temp-buffer 443 | (insert-file-contents "/proc/net/dev") 444 | (goto-char 1) 445 | (let ((tx 0)) 446 | (while (search-forward-regexp "^[\s\t]*\\(.*\\):" nil t) 447 | (unless (string= (match-string 1) "lo") 448 | (forward-word 8) 449 | (setq tx (+ tx (read (current-buffer)))))) 450 | (prog1 (when symon-linux--last-network-tx 451 | (/ (- tx symon-linux--last-network-tx) symon-refresh-rate 1000)) 452 | (setq symon-linux--last-network-tx tx))))) 453 | 454 | ;; + darwin monitors 455 | 456 | (defun symon-darwin--maybe-start-process () 457 | (symon--maybe-start-process (format " 458 | while true; do 459 | echo \"----\" 460 | 461 | interface=`route get 0.0.0.0 | grep interface | awk '{print $2}'` 462 | s=`netstat -bi -I $interface | tail -1`; 463 | echo $s | awk '{print \"rx:\"$7}' 464 | echo $s | awk '{print \"tx:\"$8}' 465 | 466 | s=`hostinfo | grep 'Load average' | awk '{print \"cpu:\"$3}' | sed 's/,//'` 467 | echo $s 468 | 469 | m1=`sysctl hw.memsize | sed 's/.*:\s*//'` 470 | m_active=`vm_stat | grep 'Pages active' | sed 's/.*: *//'` 471 | m_wired=`vm_stat | grep 'Pages wired' | sed 's/.*: *//'` 472 | 473 | s=`echo \"scale=2; (($m_active+$m_wired)*4096*100 / $m1)\"| bc -l` 474 | echo \"mem:$s\" 475 | 476 | sleep %d 477 | done" symon-refresh-rate))) 478 | 479 | (define-symon-monitor symon-darwin-cpu-monitor 480 | :index "CPU:" :unit "%" :sparkline t 481 | :setup (symon-darwin--maybe-start-process) 482 | :cleanup (symon--maybe-kill-process) 483 | :fetch (symon--read-value-from-process-buffer "cpu")) 484 | 485 | (define-symon-monitor symon-darwin-memory-monitor 486 | :index "MEM:" :unit "%" :sparkline t 487 | :setup (symon-darwin--maybe-start-process) 488 | :cleanup (symon--maybe-kill-process) 489 | :fetch (symon--read-value-from-process-buffer "mem")) 490 | 491 | (defvar symon-darwin--last-network-rx nil) 492 | 493 | (define-symon-monitor symon-darwin-network-rx-monitor 494 | :index "RX:" :unit "KB/s" :sparkline t 495 | :upper-bound symon-network-rx-upper-bound 496 | :lower-bound symon-network-rx-lower-bound 497 | :setup (progn 498 | (symon-darwin--maybe-start-process) 499 | (setq symon-darwin--last-network-rx nil)) 500 | :cleanup (symon--maybe-kill-process) 501 | :fetch (let ((rx (symon--read-value-from-process-buffer "rx"))) 502 | (prog1 (when symon-darwin--last-network-rx 503 | (/ (- rx symon-darwin--last-network-rx) symon-refresh-rate 1000)) 504 | (setq symon-darwin--last-network-rx rx)))) 505 | 506 | (defvar symon-darwin--last-network-tx nil) 507 | 508 | (define-symon-monitor symon-darwin-network-tx-monitor 509 | :index "TX:" :unit "KB/s" :sparkline t 510 | :upper-bound symon-network-tx-upper-bound 511 | :lower-bound symon-network-tx-lower-bound 512 | :setup (progn 513 | (symon-darwin--maybe-start-process) 514 | (setq symon-darwin--last-network-tx nil)) 515 | :cleanup (symon--maybe-kill-process) 516 | :fetch (let ((tx (symon--read-value-from-process-buffer "tx"))) 517 | (prog1 (when symon-darwin--last-network-tx 518 | (/ (- tx symon-darwin--last-network-tx) symon-refresh-rate 1000)) 519 | (setq symon-darwin--last-network-tx tx)))) 520 | 521 | (define-symon-monitor symon-darwin-battery-monitor 522 | :index "BAT:" :unit "%" :sparkline t 523 | :fetch (when battery-status-function 524 | (read (cdr (assoc ?p (funcall battery-status-function)))))) 525 | 526 | ;; + windows monitors 527 | 528 | (defun symon-windows--maybe-start-wmi-process () 529 | (symon--maybe-start-process (format "powershell -command \ 530 | $last = 0; \ 531 | while(1) \ 532 | { \ 533 | echo ----; \ 534 | \ 535 | $t = (gwmi Win32_ComputerSystem).TotalPhysicalMemory / 1000; \ 536 | $f = (gwmi Win32_OperatingSystem).FreePhysicalMemory; \ 537 | echo mem:$(($t - $f) * 100 / $t); \ 538 | \ 539 | echo swap:$((gwmi Win32_PageFileUsage).CurrentUsage); \ 540 | \ 541 | echo bat:$((gwmi Win32_Battery).EstimatedChargeRemaining); \ 542 | \ 543 | $r = 0; \ 544 | $t = 0; \ 545 | $w = gwmi Win32_PerfRawData_Tcpip_NetworkInterface; \ 546 | foreach($x in $w){ \ 547 | $r = $r + $x.BytesReceivedPersec; \ 548 | $t = $t + $x.BytesSentPersec \ 549 | } \ 550 | echo rx:$($r / 1000); \ 551 | echo tx:$($t / 1000); \ 552 | \ 553 | $p = (gwmi Win32_PerfRawData_Counters_ProcessorInformation)[0]; \ 554 | if($last) \ 555 | { \ 556 | $dt = $p.Timestamp_Sys100NS - $last.Timestamp_Sys100NS; \ 557 | $dp = $p.PercentProcessorTime - $last.PercentProcessorTime; \ 558 | echo cpu:$((1 - ($dp / $dt)) * 100); \ 559 | } \ 560 | $last = $p; \ 561 | \ 562 | sleep %d \ 563 | }" symon-refresh-rate))) 564 | 565 | (define-symon-monitor symon-windows-cpu-monitor 566 | :index "CPU:" :unit "%" :sparkline t 567 | :setup (symon-windows--maybe-start-wmi-process) 568 | :cleanup (symon--maybe-kill-process) 569 | :fetch (symon--read-value-from-process-buffer "cpu")) 570 | 571 | (define-symon-monitor symon-windows-memory-monitor 572 | :index "MEM:" :unit "%" :sparkline t 573 | :setup (symon-windows--maybe-start-wmi-process) 574 | :cleanup (symon--maybe-kill-process) 575 | :fetch (symon--read-value-from-process-buffer "mem")) 576 | 577 | (define-symon-monitor symon-windows-page-file-monitor 578 | :index "PF:" :unit "MB" :sparkline t 579 | :upper-bound symon-windows-page-file-upper-bound 580 | :setup (symon-windows--maybe-start-wmi-process) 581 | :cleanup (symon--maybe-kill-process) 582 | :fetch (symon--read-value-from-process-buffer "swap")) 583 | 584 | (define-symon-monitor symon-windows-battery-monitor 585 | :index "BAT:" :unit "%" :sparkline t 586 | :setup (symon-windows--maybe-start-wmi-process) 587 | :cleanup (symon--maybe-kill-process) 588 | :fetch (symon--read-value-from-process-buffer "bat")) 589 | 590 | (defvar symon-windows--last-network-rx nil) 591 | 592 | (define-symon-monitor symon-windows-network-rx-monitor 593 | :index "RX:" :unit "KB/s" :sparkline t 594 | :upper-bound symon-network-rx-upper-bound 595 | :lower-bound symon-network-rx-lower-bound 596 | :setup (progn 597 | (symon-windows--maybe-start-wmi-process) 598 | (setq symon-windows--last-network-rx nil)) 599 | :cleanup (symon--maybe-kill-process) 600 | :fetch (let ((rx (symon--read-value-from-process-buffer "rx"))) 601 | (prog1 (when symon-windows--last-network-rx 602 | (/ (- rx symon-windows--last-network-rx) symon-refresh-rate)) 603 | (setq symon-windows--last-network-rx rx)))) 604 | 605 | (defvar symon-windows--last-network-tx nil) 606 | 607 | (define-symon-monitor symon-windows-network-tx-monitor 608 | :index "TX:" :unit "KB/s" :sparkline t 609 | :upper-bound symon-network-tx-upper-bound 610 | :lower-bound symon-network-tx-lower-bound 611 | :setup (progn 612 | (symon-windows--maybe-start-wmi-process) 613 | (setq symon-windows--last-network-tx nil)) 614 | :cleanup (symon--maybe-kill-process) 615 | :fetch (let ((tx (symon--read-value-from-process-buffer "tx"))) 616 | (prog1 (when symon-windows--last-network-tx 617 | (/ (- tx symon-windows--last-network-tx) symon-refresh-rate)) 618 | (setq symon-windows--last-network-tx tx)))) 619 | 620 | ;; + misc monitors 621 | 622 | (define-symon-monitor symon-current-time-monitor 623 | :display (format-time-string "%H:%M")) 624 | 625 | ;; + predefined sparkline types 626 | 627 | (defun symon--sparkline-draw-horizontal-grid (vec y) 628 | (dotimes (x/2 (/ symon-sparkline-width 2)) 629 | (aset vec (+ (* y symon-sparkline-width) (* x/2 2)) t))) 630 | 631 | (defun symon--sparkline-draw-vertical-grid (vec x) 632 | (dotimes (y/2 (/ symon-sparkline-height 2)) 633 | (aset vec (+ (* (* y/2 2) symon-sparkline-width) x) t))) 634 | 635 | (defun symon--make-plain-sparkline () 636 | (make-bool-vector (* symon-sparkline-height symon-sparkline-width) nil)) 637 | 638 | (defun symon--make-bounded-sparkline () 639 | (let ((vec (symon--make-plain-sparkline))) 640 | (symon--sparkline-draw-horizontal-grid vec 0) 641 | (symon--sparkline-draw-horizontal-grid vec (1- symon-sparkline-height)) 642 | vec)) 643 | 644 | (defun symon--make-boxed-sparkline () 645 | (let ((vec (symon--make-bounded-sparkline))) 646 | (symon--sparkline-draw-vertical-grid vec 0) 647 | (symon--sparkline-draw-vertical-grid vec (1- symon-sparkline-width)) 648 | vec)) 649 | 650 | (defun symon--make-gridded-sparkline () 651 | (let ((vec (symon--make-boxed-sparkline))) 652 | (symon--sparkline-draw-horizontal-grid vec (/ symon-sparkline-height 2)) 653 | (symon--sparkline-draw-vertical-grid vec (/ symon-sparkline-width 4)) 654 | (symon--sparkline-draw-vertical-grid vec (/ symon-sparkline-width 2)) 655 | (symon--sparkline-draw-vertical-grid vec (/ (* symon-sparkline-width 3) 4)) 656 | vec)) 657 | 658 | (put 'plain 'symon-sparkline-type 'symon--make-plain-sparkline) 659 | (put 'bounded 'symon-sparkline-type 'symon--make-bounded-sparkline) 660 | (put 'boxed 'symon-sparkline-type 'symon--make-boxed-sparkline) 661 | (put 'gridded 'symon-sparkline-type 'symon--make-gridded-sparkline) 662 | 663 | ;; + symon core 664 | 665 | (defvar symon--cleanup-fns nil) ; List[Fn] 666 | (defvar symon--display-fns nil) ; List[List[Fn]] 667 | (defvar symon--display-active nil) 668 | (defvar symon--active-page nil) 669 | (defvar symon--total-page-num nil) 670 | (defvar symon--timer-objects nil) 671 | 672 | (defun symon--initialize () 673 | (unless symon-monitors 674 | (message "Warning: `symon-monitors' is empty.")) 675 | (let* ((symon-monitors ; for backward-compatibility 676 | (if (symbolp (car symon-monitors)) 677 | (list symon-monitors) 678 | symon-monitors)) 679 | (monitors 680 | (mapcar (lambda (lst) 681 | (mapcar (lambda (s) (get s 'symon-monitor)) lst)) 682 | symon-monitors)) 683 | (monitors-flattened 684 | (symon--flatten monitors))) 685 | (mapc (lambda (m) (funcall (aref m 0))) monitors-flattened) ; setup-fns 686 | (setq symon--cleanup-fns (mapcar (lambda (m) (aref m 1)) monitors-flattened) 687 | symon--display-fns (mapcar (lambda (l) (mapcar (lambda (m) (aref m 2)) l)) monitors) 688 | symon--display-active nil 689 | symon--total-page-num (length symon-monitors) 690 | symon--timer-objects 691 | (list (run-with-timer 0 symon-refresh-rate 'symon--redisplay) 692 | (run-with-idle-timer symon-delay t 'symon-display))) 693 | (add-hook 'pre-command-hook 'symon--display-end) 694 | (add-hook 'kill-emacs-hook 'symon--cleanup))) 695 | 696 | (defun symon--cleanup () 697 | (remove-hook 'kill-emacs-hook 'symon--cleanup) 698 | (remove-hook 'pre-command-hook 'symon--display-end) 699 | (mapc 'cancel-timer symon--timer-objects) 700 | (mapc 'funcall symon--cleanup-fns)) 701 | 702 | (defun symon--display-update () 703 | "update symon display" 704 | (unless (or cursor-in-echo-area (active-minibuffer-window)) 705 | (let ((message-log-max nil) ; do not insert to *Messages* buffer 706 | (display-string nil) 707 | (page 0)) 708 | (dolist (lst symon--display-fns) 709 | (if (= page symon--active-page) 710 | (message "%s" (apply 'concat (mapcar 'funcall lst))) 711 | (mapc 'funcall lst)) 712 | (setq page (1+ page)))) 713 | (setq symon--display-active t))) 714 | 715 | (defun symon-display () 716 | "activate symon display." 717 | (interactive) 718 | (setq symon--active-page 0) 719 | (symon--display-update)) 720 | 721 | (defun symon--redisplay () 722 | "update symon display." 723 | (when symon--display-active 724 | (setq symon--active-page (% (1+ symon--active-page) symon--total-page-num)) 725 | (symon--display-update))) 726 | 727 | (defun symon--display-end () 728 | "deactivate symon display." 729 | (setq symon--display-active nil)) 730 | 731 | ;;;###autoload 732 | (define-minor-mode symon-mode 733 | "tiny graphical system monitor" 734 | :init-value nil 735 | :global t 736 | (if symon-mode (symon--initialize) (symon--cleanup))) 737 | 738 | ;; + provide 739 | 740 | (provide 'symon) 741 | 742 | ;;; symon.el ends here 743 | --------------------------------------------------------------------------------