Helps you control your GNU/Linux computer from Emacs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

463 lines
17 KiB

  1. ;;; desktop-environment.el --- Helps you control your GNU/Linux computer -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2018 Damien Cassou
  3. ;; Author: Damien Cassou <damien@cassou.me>, Nicolas Petton <nicolas@petton.fr>
  4. ;; Url: https://gitlab.petton.fr/DamienCassou/desktop-environment
  5. ;; Package-requires: ((emacs "25.1"))
  6. ;; Version: 0.3.0
  7. ;; This program is free software; you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; This program is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;; This package helps you control your GNU/Linux desktop from Emacs.
  19. ;; With desktop-environment, you can control the brightness and volume
  20. ;; as well as take screenshots and lock your screen. The package
  21. ;; depends on the availability of shell commands to do the hard work
  22. ;; for us. These commands can be changed by customizing the
  23. ;; appropriate variables.
  24. ;; The global minor mode `desktop-environment-mode' binds standard
  25. ;; keys to provided commands: e.g., <XF86AudioRaiseVolume> to raise
  26. ;; the volume, <print> to take a screenshot, and <s-l> to lock the
  27. ;; screen.
  28. ;;; Code:
  29. (require 'dbus)
  30. (defgroup desktop-environment nil
  31. "Configure desktop-environment."
  32. :group 'environment)
  33. ;;; Customization - keyboard backlight
  34. (defcustom desktop-environment-keyboard-backlight-normal-increment 1
  35. "Normal keyboard increment value."
  36. :type 'integer)
  37. (defcustom desktop-environment-keyboard-backlight-normal-decrement -1
  38. "Normal keyboard decrement value."
  39. :type 'integer)
  40. ;;; Customization - brightness
  41. (defcustom desktop-environment-brightness-normal-increment "10%+"
  42. "Normal brightness increment value."
  43. :type 'string)
  44. (defcustom desktop-environment-brightness-normal-decrement "10%-"
  45. "Normal brightness decrement value."
  46. :type 'string)
  47. (defcustom desktop-environment-brightness-small-increment "5%+"
  48. "Small brightness increment value."
  49. :type 'string)
  50. (defcustom desktop-environment-brightness-small-decrement "5%-"
  51. "Small brightness decrement value."
  52. :type 'string)
  53. (defcustom desktop-environment-brightness-get-command "brightnessctl"
  54. "Shell command getting current screen brightness level.
  55. If you change this variable, you might want to change
  56. `desktop-environment-brightness-get-regexp' as well."
  57. :type 'string)
  58. (defcustom desktop-environment-brightness-get-regexp "\\([0-9]+%\\)"
  59. "Regular expression matching brightness value.
  60. This regular expression will be tested against the result of
  61. `desktop-environment-brightness-get-command' and group 1 must
  62. match the current brightness level."
  63. :type 'regexp)
  64. (defcustom desktop-environment-brightness-set-command "brightnessctl set %s"
  65. "Shell command setting the brightness level.
  66. The value must contain 1 occurrence of '%s' that will be
  67. replaced by the desired new brightness level."
  68. :type 'string)
  69. ;;; Customization - volume
  70. (defcustom desktop-environment-volume-normal-increment "5%+"
  71. "Normal volume increment value."
  72. :type 'string)
  73. (defcustom desktop-environment-volume-normal-decrement "5%-"
  74. "Normal volume decrement value."
  75. :type 'string)
  76. (defcustom desktop-environment-volume-small-increment "1%+"
  77. "Small volume increment value."
  78. :type 'string)
  79. (defcustom desktop-environment-volume-small-decrement "1%-"
  80. "Small volume decrement value."
  81. :type 'string)
  82. (defcustom desktop-environment-volume-get-command "amixer get Master"
  83. "Shell command getting current volume level.
  84. If you change this variable, you might want to change
  85. `desktop-environment-volume-get-regexp' as well."
  86. :type 'string)
  87. (defcustom desktop-environment-volume-get-regexp "\\([0-9]+%\\)"
  88. "Regular expression matching volume value.
  89. This regular expression will be tested against the result of
  90. `desktop-environment-volume-get-command' and group 1 must
  91. match the current volume level."
  92. :type 'regexp)
  93. (defcustom desktop-environment-volume-set-command "amixer set Master %s"
  94. "Shell command setting the volume level.
  95. The value must contain 1 occurrence of '%s' that will be
  96. replaced by the desired new volume level."
  97. :type 'string)
  98. (defcustom desktop-environment-volume-toggle-command "amixer set Master toggle"
  99. "Shell command toggling between muted and unmuted."
  100. :type 'string)
  101. (defcustom desktop-environment-volume-toggle-microphone-command "amixer set Capture toggle"
  102. "Shell command toggling microphone between muted and unmuted."
  103. :type 'string)
  104. ;;; Customization - screenshots
  105. (defcustom desktop-environment-screenshot-command "scrot"
  106. "Shell command taking a screenshot in the current working directory."
  107. :type 'string)
  108. (defcustom desktop-environment-screenshot-partial-command "scrot -s"
  109. "Shell command taking a partial screenshot in the current working directory.
  110. The shell command should let the user interactively select the
  111. portion of the screen."
  112. :type 'string)
  113. (defcustom desktop-environment-screenshot-directory "~/Pictures"
  114. "Directory where to save screenshots."
  115. :type 'directory)
  116. ;;; Customization - screen locking
  117. (defcustom desktop-environment-screenlock-command "slock"
  118. "Shell command locking the screen."
  119. :type 'string)
  120. ;;; Customization - wifi
  121. (defcustom desktop-environment-wifi-command "wifi toggle"
  122. "Shell command toggling wifi."
  123. :type 'string)
  124. ;;; Customization - bluetooth
  125. (defcustom desktop-environment-bluetooth-command "bluetooth toggle"
  126. "Shell command toggling bluetooth."
  127. :type 'string)
  128. ;;; Customization - EXWM keybindings
  129. (defcustom desktop-environment-update-exwm-global-keys :global
  130. "Determines interacting with EXWM bindings when enabling/disabling the mode."
  131. :type '(radio
  132. (const :tag "Global" :doc "Use `exwm-input-set-key' on mode activation to set bindings." :global)
  133. (const :tag "Prefix" :doc "Add/Remove keys to `exwm-input-prefix-keys' when enabling/disabling the mode." :prefix)
  134. (const :tag "Off" :doc "Do not touch EXWM key bindings." nil)))
  135. ;;; Helper functions - brightness
  136. (defun desktop-environment-brightness-get ()
  137. "Return a string representing current brightness level."
  138. (let ((output (shell-command-to-string desktop-environment-brightness-get-command)))
  139. (save-match-data
  140. (string-match desktop-environment-brightness-get-regexp output)
  141. (match-string 1 output))))
  142. (defun desktop-environment-brightness-set (value)
  143. "Set brightness to VALUE."
  144. (shell-command-to-string (format desktop-environment-brightness-set-command value))
  145. (message "New brightness value: %s" (desktop-environment-brightness-get)))
  146. ;;; Helper functions - volume
  147. (defun desktop-environment-volume-get ()
  148. "Return a string representing current volume level."
  149. (let ((output (shell-command-to-string desktop-environment-volume-get-command)))
  150. (save-match-data
  151. (string-match desktop-environment-volume-get-regexp output)
  152. (match-string 1 output))))
  153. (defun desktop-environment-volume-set (value)
  154. "Set volume to VALUE."
  155. (shell-command-to-string (format desktop-environment-volume-set-command value))
  156. (message "New volume value: %s" (desktop-environment-volume-get)))
  157. ;;; Helper functions - keyboard backlight
  158. (defun desktop-environment-keyboard-backlight-percent ()
  159. "Return the new keyboard backlight value as a % of maximum backlight."
  160. (let ((backlight-level (desktop-environment-keyboard-backlight-get)))
  161. (if (eq backlight-level 0)
  162. "0.0"
  163. (*
  164. (/ (* backlight-level 1.0)
  165. (* (desktop-environment-keyboard-backlight-get-max) 1.0))
  166. 100))))
  167. (defun desktop-environment-keyboard-backlight-get ()
  168. "Return a number representing keyboard backlight current level."
  169. (dbus-call-method :system
  170. "org.freedesktop.UPower"
  171. "/org/freedesktop/UPower/KbdBacklight"
  172. "org.freedesktop.UPower.KbdBacklight"
  173. "GetBrightness"))
  174. (defun desktop-environment-keyboard-backlight-get-max ()
  175. "Return a number representing keyboard backlight maximum level."
  176. (dbus-call-method :system
  177. "org.freedesktop.UPower"
  178. "/org/freedesktop/UPower/KbdBacklight"
  179. "org.freedesktop.UPower.KbdBacklight"
  180. "GetMaxBrightness"))
  181. (defun desktop-environment-keyboard-backlight-set (value)
  182. "Set keyboard backlight to VALUE."
  183. (dbus-call-method :system
  184. "org.freedesktop.UPower"
  185. "/org/freedesktop/UPower/KbdBacklight"
  186. "org.freedesktop.UPower.KbdBacklight"
  187. "SetBrightness"
  188. :int32 value)
  189. (message "New keyboard value: %s%%" (desktop-environment-keyboard-backlight-percent)))
  190. ;;; Commands - brightness
  191. ;;;###autoload
  192. (defun desktop-environment-brightness-increment ()
  193. "Increment brightness by `desktop-environment-brightness-normal-increment'."
  194. (interactive)
  195. (desktop-environment-brightness-set desktop-environment-brightness-normal-increment))
  196. ;;;###autoload
  197. (defun desktop-environment-brightness-decrement ()
  198. "Decrement brightness by `desktop-environment-brightness-normal-decrement'."
  199. (interactive)
  200. (desktop-environment-brightness-set desktop-environment-brightness-normal-decrement))
  201. ;;;###autoload
  202. (defun desktop-environment-brightness-increment-slowly ()
  203. "Increment brightness by `desktop-environment-brightness-small-increment'."
  204. (interactive)
  205. (desktop-environment-brightness-set desktop-environment-brightness-small-increment))
  206. ;;;###autoload
  207. (defun desktop-environment-brightness-decrement-slowly ()
  208. "Decrement brightness by `desktop-environment-brightness-small-decrement'."
  209. (interactive)
  210. (desktop-environment-brightness-set desktop-environment-brightness-small-decrement))
  211. ;;; Commands - volume
  212. ;;;###autoload
  213. (defun desktop-environment-volume-increment ()
  214. "Increment volume by `desktop-environment-volume-normal-increment'."
  215. (interactive)
  216. (desktop-environment-volume-set desktop-environment-volume-normal-increment))
  217. ;;;###autoload
  218. (defun desktop-environment-volume-decrement ()
  219. "Decrement volume by `desktop-environment-volume-normal-decrement'."
  220. (interactive)
  221. (desktop-environment-volume-set desktop-environment-volume-normal-decrement))
  222. ;;;###autoload
  223. (defun desktop-environment-volume-increment-slowly ()
  224. "Increment volume by `desktop-environment-volume-small-increment'."
  225. (interactive)
  226. (desktop-environment-volume-set desktop-environment-volume-small-increment))
  227. ;;;###autoload
  228. (defun desktop-environment-volume-decrement-slowly ()
  229. "Decrement volume by `desktop-environment-volume-small-decrement'."
  230. (interactive)
  231. (desktop-environment-volume-set desktop-environment-volume-small-decrement))
  232. ;;;###autoload
  233. (defun desktop-environment-toggle-mute ()
  234. "Toggle between muted and un-muted."
  235. (interactive)
  236. (message "%s"
  237. (shell-command-to-string desktop-environment-volume-toggle-command)))
  238. ;;;###autoload
  239. (defun desktop-environment-toggle-microphone-mute ()
  240. "Toggle microphone between muted and un-muted."
  241. (interactive)
  242. (message "%s"
  243. (shell-command-to-string desktop-environment-volume-toggle-microphone-command)))
  244. ;;; Commands - keyboard backlight
  245. ;;;###autoload
  246. (defun desktop-environment-keyboard-backlight-increment ()
  247. "Increment keyboard backlight by `desktop-environment-keyboard-backlight-normal-increment'."
  248. (interactive)
  249. (desktop-environment-keyboard-backlight-set
  250. (+ desktop-environment-keyboard-backlight-normal-increment
  251. (desktop-environment-keyboard-backlight-get))))
  252. (defun desktop-environment-keyboard-backlight-decrement ()
  253. "Decrement keyboard backlight by `desktop-environment-keyboard-backlight-normal-decrement'."
  254. (interactive)
  255. (desktop-environment-keyboard-backlight-set
  256. (+ desktop-environment-keyboard-backlight-normal-decrement
  257. (desktop-environment-keyboard-backlight-get))))
  258. ;;; Commands - screenshots
  259. ;;;###autoload
  260. (defun desktop-environment-screenshot ()
  261. "Take a screenshot of the screen in the current working directory."
  262. (interactive)
  263. (let ((default-directory (expand-file-name desktop-environment-screenshot-directory)))
  264. (start-process-shell-command "desktop-environment-screenshot" nil desktop-environment-screenshot-command)))
  265. ;;;###autoload
  266. (defun desktop-environment-screenshot-part ()
  267. "Take a partial screenshot in the current working directory.
  268. The command asks the user to interactively select a portion of
  269. the screen."
  270. (interactive)
  271. (let ((default-directory (expand-file-name desktop-environment-screenshot-directory)))
  272. (message "Please select the part of your screen to shoot.")
  273. (start-process-shell-command "desktop-environment-screenshot" nil desktop-environment-screenshot-partial-command)))
  274. ;;; Commands - screen locking
  275. ;;;###autoload
  276. (defun desktop-environment-lock-screen ()
  277. "Lock the screen, preventing anyone without a password from using the system."
  278. (interactive)
  279. ;; Run command asynchronously so that Emacs does not wait in the background.
  280. (start-process-shell-command "lock" nil desktop-environment-screenlock-command))
  281. ;;; Commands - wifi
  282. ;;;###autoload
  283. (defun desktop-environment-toggle-wifi ()
  284. "Toggle wifi adapter on and off."
  285. (interactive)
  286. (let ((async-shell-command-buffer 'new-buffer))
  287. (async-shell-command desktop-environment-wifi-command)))
  288. ;;; Commands - bluetooth
  289. ;;;###autoload
  290. (defun desktop-environment-toggle-bluetooth ()
  291. "Toggle bluetooth on and off."
  292. (interactive)
  293. (let ((async-shell-command-buffer 'new-buffer))
  294. (async-shell-command desktop-environment-bluetooth-command)))
  295. ;;; Minor mode
  296. (defvar desktop-environment-mode-map
  297. (let ((desktop-environment--keybindings
  298. `(;; Brightness
  299. (,(kbd "<XF86MonBrightnessUp>") . ,(function desktop-environment-brightness-increment))
  300. (,(kbd "<XF86MonBrightnessDown>") . ,(function desktop-environment-brightness-decrement))
  301. (,(kbd "S-<XF86MonBrightnessUp>") . ,(function desktop-environment-brightness-increment-slowly))
  302. (,(kbd "S-<XF86MonBrightnessDown>") . ,(function desktop-environment-brightness-decrement-slowly))
  303. ;; Volume
  304. (,(kbd "<XF86AudioRaiseVolume>") . ,(function desktop-environment-volume-increment))
  305. (,(kbd "<XF86AudioLowerVolume>") . ,(function desktop-environment-volume-decrement))
  306. (,(kbd "S-<XF86AudioRaiseVolume>") . ,(function desktop-environment-volume-increment-slowly))
  307. (,(kbd "S-<XF86AudioLowerVolume>") . ,(function desktop-environment-volume-decrement-slowly))
  308. (,(kbd "<XF86AudioMute>") . ,(function desktop-environment-toggle-mute))
  309. (,(kbd "<XF86AudioMicMute>") . ,(function desktop-environment-toggle-microphone-mute))
  310. ;; Volume
  311. (,(kbd "S-<print>") . ,(function desktop-environment-screenshot-part))
  312. (,(kbd "<print>") . ,(function desktop-environment-screenshot))
  313. ;; Screen locking
  314. (,(kbd "s-l") . ,(function desktop-environment-lock-screen))
  315. ;; Wifi controls
  316. (,(kbd "<XF86WLAN>") . ,(function desktop-environment-toggle-wifi))
  317. ;; Bluetooth controls
  318. (,(kbd "<XF86Bluetooth>") . ,(function desktop-environment-toggle-bluetooth))))
  319. (map (make-sparse-keymap)))
  320. (dolist (keybinding desktop-environment--keybindings)
  321. (define-key map (car keybinding) (cdr keybinding)))
  322. map)
  323. "Keymap for `desktop-environment-mode'.")
  324. (declare-function exwm-input-set-key "ext:exwm-input")
  325. (defun desktop-environment-exwm-set-global-keybindings (enable)
  326. "When using EXWM, add `desktop-environment-mode-map' to global keys.
  327. When ENABLE is non-nil, the bindings will be installed depending
  328. on the value of `desktop-environment-update-exwm-global-keys'.
  329. If set to `:prefix', the bindings will be disabled when ENABLE is
  330. nil."
  331. (when (featurep 'exwm-input)
  332. (cl-case desktop-environment-update-exwm-global-keys
  333. (:global
  334. (when enable
  335. (map-keymap (lambda (event definition)
  336. (exwm-input-set-key (vector event) definition))
  337. desktop-environment-mode-map)))
  338. (:prefix
  339. (when (boundp 'exwm-input-prefix-keys)
  340. (map-keymap (lambda (event definition)
  341. (ignore definition)
  342. (setq exwm-input-prefix-keys (if enable
  343. (cons event exwm-input-prefix-keys)
  344. (delq event exwm-input-prefix-keys))))
  345. desktop-environment-mode-map)))
  346. ((nil) nil)
  347. (t
  348. (message "Ignoring unknown value %s for `desktop-environment-update-exwm-global-keys'"
  349. desktop-environment-update-exwm-global-keys)))))
  350. ;;;###autoload
  351. (define-minor-mode desktop-environment-mode
  352. "Activate keybindings to control your desktop environment.
  353. \\{desktop-environment-mode-map}"
  354. :global t
  355. :require 'desktop-environment
  356. :lighter " DE"
  357. (desktop-environment-exwm-set-global-keybindings desktop-environment-mode))
  358. (provide 'desktop-environment)
  359. ;;; desktop-environment.el ends here