Add support for device profiles
This is especially useful with Bluetooth devices.
This commit is contained in:
		
							
								
								
									
										48
									
								
								pw-access.el
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								pw-access.el
									
									
									
									
									
								
							| @@ -73,6 +73,30 @@ PROPERTIES is an association list in the same format as in | ||||
| `pw-access-properties'.  It needn't contain all the properties, just | ||||
| the properties to be changed.") | ||||
|  | ||||
| (cl-defgeneric pw-access-current-profile (class device-id) | ||||
|   "Return current profile of the given device. | ||||
| DEVICE-ID is a numeric PipeWire Device id (other kinds of PipeWire | ||||
| objects are not supported in this method). | ||||
|  | ||||
| The profile is an association list with elements of the form | ||||
| (PROPERTY . VALUE), in the same format as properties in | ||||
| `pw-access-properties'.") | ||||
|  | ||||
| (cl-defgeneric pw-access-profiles (class device-id) | ||||
|   "Return available profiles of the given device. | ||||
| DEVICE-ID is a numeric PipeWire Device id (other kinds of PipeWire | ||||
| objects are not supported in this method). | ||||
|  | ||||
| Return a list of profiles, which are in the same format as in | ||||
| `pw-access-current-profile'.") | ||||
|  | ||||
| (cl-defgeneric pw-access-set-profile (class device-id profile-index) | ||||
|   "Set the profile of the given device. | ||||
| DEVICE-ID is a numeric PipeWire Device id (other kinds of PipeWire | ||||
| objects are not supported in this method). | ||||
| PROFILE-INDEX is a numeric index of the profile to set, as returned | ||||
| from PipeWire.") | ||||
|  | ||||
| (cl-defgeneric pw-access-defaults (class) | ||||
|   "Return default sinks and sources. | ||||
| An association lists is returned.  Each list element is of the form | ||||
| @@ -183,11 +207,29 @@ Note this interface may not work with all PipeWire versions.") | ||||
| (defun pw-cli--format-property (property) | ||||
|   (format "%s: %s" (car property) (pw-cli--format-property-value (cdr property)))) | ||||
|  | ||||
|   (let* ((formatted (mapconcat #'pw-cli--format-property properties ", ")) | ||||
|          (props (concat "{ " formatted " }"))) | ||||
| (defun pw-cli--set-parameter (object-id parameter value) | ||||
|   (let* ((formatted (mapconcat #'pw-cli--format-property value ", ")) | ||||
|          (param-value (concat "{ " formatted " }"))) | ||||
|     (call-process pw-cli-command nil pw-cli-command nil | ||||
|                   "set-param" (number-to-string node-id) "Props" props))) | ||||
|                   "set-param" (number-to-string object-id) parameter param-value))) | ||||
|  | ||||
| (cl-defmethod pw-access-set-properties ((_class pw-cli-accessor) node-id properties) | ||||
|   (pw-cli--set-parameter node-id "Props" properties)) | ||||
|  | ||||
| (cl-defmethod pw-access-current-profile ((_class pw-cli-accessor) device-id) | ||||
|   (with-temp-buffer | ||||
|     (pw-cli--command pw-cli-command `("enum-params" ,(number-to-string device-id) "Profile")) | ||||
|     (pw-cli--parse-properties))) | ||||
|  | ||||
| (cl-defmethod pw-access-profiles ((_class pw-cli-accessor) device-id) | ||||
|   (with-temp-buffer | ||||
|     (pw-cli--command pw-cli-command `("enum-params" ,(number-to-string device-id) "EnumProfile")) | ||||
|     (cl-loop for profile = (pw-cli--parse-properties) then (pw-cli--parse-properties) | ||||
|              while profile | ||||
|              collect profile))) | ||||
|  | ||||
| (cl-defmethod pw-access-set-profile ((_class pw-cli-accessor) device-id profile-index) | ||||
|   (pw-cli--set-parameter device-id "Profile" `(("index" . ,profile-index) ("save" . "true")))) | ||||
|  | ||||
| (defun pw-cli--parse-metadata () | ||||
|   (let ((metadata '())) | ||||
|   | ||||
							
								
								
									
										30
									
								
								pw-lib.el
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								pw-lib.el
									
									
									
									
									
								
							| @@ -81,6 +81,36 @@ If the given KEY doesn't exist in OBJECT, return DEFAULT." | ||||
| E.g. \"Device\", \"Node\", \"Port\", \"Client\", ..." | ||||
|   (pw-lib-object-value object 'type)) | ||||
|  | ||||
| (defun pw-lib--profile-name (profile) | ||||
|   (cdr (or (assoc "description" profile) | ||||
|            (assoc "name" profile)))) | ||||
|  | ||||
| (defun pw-lib-current-profile (device-id) | ||||
|   "Return the current profile name of the given device. | ||||
| DEVICE-ID is the numeric id of the device. | ||||
| The returned profile name is a string, or nil if it cannot be found." | ||||
|   (pw-lib--profile-name (pw-access-current-profile pw-lib--accessor device-id))) | ||||
|  | ||||
| (defun pw-lib-profiles (device-id) | ||||
|   "Return list of available profiles of the given device. | ||||
| DEVICE-ID is the numeric id of the device. | ||||
| A list of strings (possibly empty) is returned." | ||||
|   (mapcar #'pw-lib--profile-name (pw-access-profiles pw-lib--accessor device-id))) | ||||
|  | ||||
| (defun pw-lib-set-profile (device-id profile) | ||||
|   "Set the profile of the given device. | ||||
| DEVICE-ID is the numeric id of the device. | ||||
| PROFILE is a string name of the profile, it must be one of the values | ||||
| returned from `pw-lib-profiles'. " | ||||
|   (let* ((all-profiles (pw-access-profiles pw-lib--accessor device-id)) | ||||
|          (properties (cl-find profile all-profiles :key #'pw-lib--profile-name :test #'equal))) | ||||
|     (unless properties | ||||
|       (error "Profile %s of device %s not found" profile device-id)) | ||||
|     (let ((index (cdr (assoc "index" properties)))) | ||||
|       (unless index | ||||
|         (error "Index of %s profile of device %s not found" profile device-id)) | ||||
|       (pw-access-set-profile pw-lib--accessor device-id index)))) | ||||
|  | ||||
| (defun pw-lib--node (object) | ||||
|   (if (equal (pw-lib-object-type object) "Node") | ||||
|       object | ||||
|   | ||||
							
								
								
									
										18
									
								
								pw-ui.el
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								pw-ui.el
									
									
									
									
									
								
							| @@ -110,10 +110,14 @@ The indicator is displayed only on graphical terminals." | ||||
|   (let* ((id (pw-lib-object-id object)) | ||||
|          (type (pw-lib-object-type object)) | ||||
|          (text (format "%4s: %s" id (pw-ui--object-name object))) | ||||
|          (profile (when (equal type "Device") | ||||
|                     (pw-lib-current-profile (pw-lib-object-id object)))) | ||||
|          (face (if (member id default-ids) 'pipewire-default-object-face 'default)) | ||||
|          (media-class (pw-lib-object-value object "media.class"))) | ||||
|     (when media-class | ||||
|       (setq text (format "%s (%s)" text media-class))) | ||||
|     (when profile | ||||
|       (setq text (format "%s: %s" text profile))) | ||||
|     (let ((volume-p (member type '("Node" "Port")))) | ||||
|       (when (and volume-p (pw-lib-muted-p object)) | ||||
|         (setq face `(:inherit (pipewire-muted-face ,face)))) | ||||
| @@ -337,10 +341,24 @@ Otherwise ask for the Node to set as the default Node." | ||||
|     (pw-lib-set-default object t) | ||||
|     (pw-ui--update))) | ||||
|  | ||||
| (defun pipewire-set-profile () | ||||
|   "Set profile of the device at the current point." | ||||
|   (interactive) | ||||
|   (if-let ((device (pw-ui--current-object nil '("Device"))) | ||||
|            (device-id (pw-lib-object-id device)) | ||||
|            (profiles (pw-lib-profiles device-id))) | ||||
|       (progn | ||||
|         (pw-lib-set-profile device-id (completing-read "Select profile: " profiles nil t)) | ||||
|         ;; Without this, ports of the device may not be displayed on the update: | ||||
|         (sit-for 0) | ||||
|         (pw-ui--update)) | ||||
|     (error "Nothing to set a profile for here"))) | ||||
|  | ||||
| (defvar pipewire-mode-map | ||||
|   (let ((map (make-sparse-keymap))) | ||||
|     (define-key map "d" 'pipewire-set-default) | ||||
|     (define-key map "m" 'pipewire-toggle-muted) | ||||
|     (define-key map "p" 'pipewire-set-profile) | ||||
|     (define-key map "v" 'pipewire-set-volume) | ||||
|     (define-key map "=" 'pipewire-increase-volume) | ||||
|     (define-key map "-" 'pipewire-decrease-volume) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user