Add support for OSD of volume changes

This commit is contained in:
Milan Zamazal 2022-06-13 19:14:22 +02:00
parent 2e63bcff13
commit 94bbd26205

128
pw-ui.el
View File

@ -33,6 +33,39 @@
:type 'number :type 'number
:group 'pipewire) :group 'pipewire)
(defcustom pipewire-osd-enable t
"If non-nil, display on screen display indicator for some operations.
The indicator is displayed only on graphical terminals."
:type 'boolean
:group 'pipewire)
(defcustom pipewire-osd-timeout 3
"Number of seconds to show an on screen display indicator."
:type 'number
:group 'pipewire)
(defcustom pipewire-osd-width (/ 100 pipewire-volume-step)
"Width of the on screen display indicator in characters."
:type 'natnum
:group 'pipewire)
(defcustom pipewire-osd-volume-on-color "lime green"
"Color to use in the on screen display indicator for active volume."
:type 'color
:group 'pipewire)
(defcustom pipewire-osd-volume-off-color "pale green"
"Color to use in the on screen display indicator for inactive volume."
:type 'color
:group 'pipewire)
(defcustom pipewire-osd-frame-parameters
`((left . (+ 50))
(top . (- 50)))
"Alist of frame parameters for the on screen display indicator."
:type '(alist :key-type symbol :value-type sexp)
:group 'pip-frame)
(defface pipewire-label-face (defface pipewire-label-face
'((t (:weight bold :overline t))) '((t (:weight bold :overline t)))
"Face to use for PipeWire node group labels." "Face to use for PipeWire node group labels."
@ -121,16 +154,11 @@
(goto-char (point-min)) (goto-char (point-min))
(forward-line (1- current-line)))) (forward-line (1- current-line))))
(defun pw-ui--update (&optional message) (defun pw-ui--current-object-id ()
(if (get-buffer pipewire-buffer) (get-text-property (point) 'pw-object-id))
(with-current-buffer pipewire-buffer
(pipewire-refresh))
(pw-lib-refresh))
(when message
(message message)))
(defun pw-ui--current-object (&optional use-default-p allowed-types) (defun pw-ui--current-object (&optional use-default-p allowed-types)
(let* ((id (get-text-property (point) 'pw-object-id)) (let* ((id (pw-ui--current-object-id))
(object (when id (pw-lib-get-object id)))) (object (when id (pw-lib-get-object id))))
(when (and object (when (and object
(not (null allowed-types)) (not (null allowed-types))
@ -140,6 +168,84 @@
(setq object (pw-lib-default-audio-sink))) (setq object (pw-lib-default-audio-sink)))
object)) object))
(defvar pw-ui--osd-timer nil)
(defvar pw-ui--osd-frame nil)
(defvar pw-ui--osd-buffer nil)
(defvar pw-ui--osd-buffer-name "*pipewire-osd*")
(defun pw-ui--osd-display (string)
(when pw-ui--osd-timer
(cancel-timer pw-ui--osd-timer))
(let ((frame-width (+ 2 (length string))))
(when (and pw-ui--osd-frame
(not (= frame-width (frame-width pw-ui--osd-frame))))
(delete-frame pw-ui--osd-frame)
(setq pw-ui--osd-frame nil))
(with-current-buffer (setq pw-ui--osd-buffer (get-buffer-create pw-ui--osd-buffer-name))
(erase-buffer)
(insert " " string)
(setq mode-line-format nil)
(unless pw-ui--osd-frame
(setq pw-ui--osd-frame (make-frame `((unsplittable . t)
,@pipewire-osd-frame-parameters
(minibuffer . nil)
(width . ,(+ 2 (length string)))
(height . 1)
(min-width . 1)
(min-height . 1)
(left-fringe . 0)
(right-fringe . 0)
(no-other-frame . t)
(undecorated . t)
(vertical-scroll-bars . nil)
(horizontal-scroll-bars . nil)
(menu-bar-lines . 0)
(tool-bar-lines . 0)
(tab-bar-lines . 0)
(cursor-type . nil)))))))
(setq pw-ui--osd-timer
(run-with-timer
pipewire-osd-timeout nil
#'(lambda ()
(when pw-ui--osd-frame
(ignore-errors (delete-frame pw-ui--osd-frame)))
(when pw-ui--osd-buffer
(ignore-errors (kill-buffer pw-ui--osd-buffer)))
(setq pw-ui--osd-frame nil
pw-ui--osd-timer nil
pw-ui--osd-buffer nil)))))
(defmacro pw-ui--osd (&rest body)
(declare (indent defun))
(let (($string (gensym)))
`(when (and window-system pipewire-osd-enable)
(if-let ((,$string (progn ,@body)))
(pw-ui--osd-display ,$string)))))
(defun pw-ui--update (&optional message)
(if (get-buffer pipewire-buffer)
(with-current-buffer pipewire-buffer
(pipewire-refresh))
(pw-lib-refresh))
(when message
(message message)))
(defun pw-ui--osd-volume (object)
(pw-ui--osd
(unless (eq (pw-ui--current-object-id) (pw-lib-object-id object))
(let* ((object* (pw-lib-get-object (pw-lib-object-id object))) ; refreshed version
(volume (pw-lib-volume object*))
(muted-p (pw-lib-muted-p object*))
(step (/ 100.0 pipewire-osd-width))
(mark (if muted-p ?- ?|))
(n-active (round (/ volume step)))
(n-inactive (- pipewire-osd-width n-active)))
(format "%s%s"
(propertize (make-string n-active mark)
'face `(:background ,pipewire-osd-volume-on-color))
(propertize (make-string n-inactive mark)
'face `(:background ,pipewire-osd-volume-off-color)))))))
;;;###autoload ;;;###autoload
(defun pipewire-toggle-muted () (defun pipewire-toggle-muted ()
"Switch mute status of an audio output or input. "Switch mute status of an audio output or input.
@ -148,7 +254,8 @@ object. Otherwise apply it on the default audio sink."
(interactive) (interactive)
(let* ((object (pw-ui--current-object t '("Node" "Port"))) (let* ((object (pw-ui--current-object t '("Node" "Port")))
(muted-p (pw-lib-toggle-mute object))) (muted-p (pw-lib-toggle-mute object)))
(pw-ui--update (format "%s %s" (pw-ui--object-name object) (if muted-p "muted" "unmuted"))))) (pw-ui--update (format "%s %s" (pw-ui--object-name object) (if muted-p "muted" "unmuted")))
(pw-ui--osd-volume object)))
;;;###autoload ;;;###autoload
(defun pipewire-set-volume (volume &optional object single-p) (defun pipewire-set-volume (volume &optional object single-p)
@ -162,7 +269,8 @@ Otherwise apply it on the default audio sink."
(unless object (unless object
(setq object (pw-ui--current-object t '("Node" "Port")))) (setq object (pw-ui--current-object t '("Node" "Port"))))
(pw-lib-set-volume volume object single-p) (pw-lib-set-volume volume object single-p)
(pw-ui--update (format "Volume %s for %s" volume (pw-ui--object-name object)))) (pw-ui--update (format "Volume %s for %s" volume (pw-ui--object-name object)))
(pw-ui--osd-volume object))
(defun pw-ui--change-volume (step &optional single-p) (defun pw-ui--change-volume (step &optional single-p)
(let* ((object (pw-ui--current-object t '("Node" "Port"))) (let* ((object (pw-ui--current-object t '("Node" "Port")))