My personal dotfiles
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

symon.el 28KB

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