diff options
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | debian/control | 2 | ||||
| -rw-r--r-- | i3status.c | 15 | ||||
| -rw-r--r-- | include/i3status.h (renamed from i3status.h) | 1 | ||||
| -rw-r--r-- | include/queue.h | 527 | ||||
| -rw-r--r-- | man/i3status.man | 19 | ||||
| -rw-r--r-- | src/print_volume.c | 154 | 
7 files changed, 719 insertions, 3 deletions
| @@ -3,7 +3,7 @@ CFLAGS+=-g  CFLAGS+=-std=gnu99  CFLAGS+=-pedantic  CFLAGS+=-DPREFIX=\"\" -CFLAGS+=-I. +CFLAGS+=-Iinclude  LDFLAGS+=-lconfuse  VERSION=$(shell git describe --tags --abbrev=0) @@ -11,7 +11,7 @@ VERSION=$(shell git describe --tags --abbrev=0)  ifeq ($(shell uname),Linux)  CFLAGS+=-DLINUX  CFLAGS+=-D_GNU_SOURCE -LDFLAGS+=-liw +LDFLAGS+=-liw -lasound  endif  ifeq ($(shell uname),GNU/kFreeBSD) diff --git a/debian/control b/debian/control index 4d6bde3..f6b9e7d 100644 --- a/debian/control +++ b/debian/control @@ -3,7 +3,7 @@ Section: utils  Priority: extra  Maintainer: Michael Stapelberg <michael@stapelberg.de>  DM-Upload-Allowed: yes -Build-Depends: debhelper (>= 5), libiw-dev [!kfreebsd-i386 !kfreebsd-amd64 !hurd-i386], libconfuse-dev, asciidoc, xmlto, libcap2-bin +Build-Depends: debhelper (>= 5), libiw-dev [!kfreebsd-i386 !kfreebsd-amd64 !hurd-i386], libconfuse-dev, asciidoc, xmlto, libcap2-bin, libasound2-dev  Standards-Version: 3.8.4  Homepage: http://i3.zekjur.net/i3status @@ -151,6 +151,14 @@ int main(int argc, char *argv[]) {                  CFG_END()          }; +        cfg_opt_t volume_opts[] = { +                CFG_STR("format", "♪: %volume", CFGF_NONE), +                CFG_STR("device", "default", CFGF_NONE), +                CFG_STR("mixer", "Master", CFGF_NONE), +                CFG_INT("mixer_idx", 0, CFGF_NONE), +                CFG_END() +        }; +          cfg_opt_t opts[] = {                  CFG_STR_LIST("order", "{ipv6,\"run_watch DHCP\",\"wireless wlan0\",\"ethernet eth0\",\"battery 0\",\"cpu_temperature 0\",load,time}", CFGF_NONE),                  CFG_SEC("general", general_opts, CFGF_NONE), @@ -160,6 +168,7 @@ int main(int argc, char *argv[]) {                  CFG_SEC("battery", battery_opts, CFGF_TITLE | CFGF_MULTI),                  CFG_SEC("cpu_temperature", temp_opts, CFGF_TITLE | CFGF_MULTI),                  CFG_SEC("disk", disk_opts, CFGF_TITLE | CFGF_MULTI), +                CFG_SEC("volume", volume_opts, CFGF_TITLE | CFGF_MULTI),                  CFG_SEC("ipv6", ipv6_opts, CFGF_NONE),                  CFG_SEC("time", time_opts, CFGF_NONE),                  CFG_SEC("ddate", ddate_opts, CFGF_NONE), @@ -260,6 +269,12 @@ int main(int argc, char *argv[]) {                          CASE_SEC("ddate")                                  print_ddate(cfg_getstr(sec, "format")); +                        CASE_SEC("volume") +                                print_volume(cfg_getstr(sec, "format"), +                                             cfg_getstr(sec, "device"), +                                             cfg_getstr(sec, "mixer"), +                                             cfg_getint(sec, "mixer_idx")); +                          CASE_SEC_TITLE("cpu_temperature")                                  print_cpu_temperature_info(atoi(title), cfg_getstr(sec, "format"));                  } diff --git a/i3status.h b/include/i3status.h index 512b4ad..a79be18 100644 --- a/i3status.h +++ b/include/i3status.h @@ -69,6 +69,7 @@ void print_run_watch(const char *title, const char *pidfile, const char *format)  void print_cpu_temperature_info(int zone, const char *format);  void print_eth_info(const char *interface, const char *format_up, const char *format_down);  void print_load(); +void print_volume(const char *fmt, const char *device, const char *mixer, int mixer_idx);  bool process_runs(const char *path);  /* socket file descriptor for general purposes */ diff --git a/include/queue.h b/include/queue.h new file mode 100644 index 0000000..75bb957 --- /dev/null +++ b/include/queue.h @@ -0,0 +1,527 @@ +/*	$OpenBSD: queue.h,v 1.1 2007/10/26 03:14:08 niallo Exp $	*/ +/*	$NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $	*/ + +/* + * Copyright (c) 1991, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + *	@(#)queue.h	8.5 (Berkeley) 8/20/94 + */ + +#ifndef	_SYS_QUEUE_H_ +#define	_SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction.  Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALIDATE(a) (a) = ((void *)-1) +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type)						\ +struct name {								\ +	struct type *slh_first;	/* first element */			\ +} + +#define	SLIST_HEAD_INITIALIZER(head)					\ +	{ NULL } + +#define SLIST_ENTRY(type)						\ +struct {								\ +	struct type *sle_next;	/* next element */			\ +} + +/* + * Singly-linked List access methods. + */ +#define	SLIST_FIRST(head)	((head)->slh_first) +#define	SLIST_END(head)		NULL +#define	SLIST_EMPTY(head)	(SLIST_FIRST(head) == SLIST_END(head)) +#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next) + +#define	SLIST_FOREACH(var, head, field)					\ +	for((var) = SLIST_FIRST(head);					\ +	    (var) != SLIST_END(head);					\ +	    (var) = SLIST_NEXT(var, field)) + +#define	SLIST_FOREACH_PREVPTR(var, varp, head, field)			\ +	for ((varp) = &SLIST_FIRST((head));				\ +	    ((var) = *(varp)) != SLIST_END(head);			\ +	    (varp) = &SLIST_NEXT((var), field)) + +/* + * Singly-linked List functions. + */ +#define	SLIST_INIT(head) {						\ +	SLIST_FIRST(head) = SLIST_END(head);				\ +} + +#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\ +	(elm)->field.sle_next = (slistelm)->field.sle_next;		\ +	(slistelm)->field.sle_next = (elm);				\ +} while (0) + +#define	SLIST_INSERT_HEAD(head, elm, field) do {			\ +	(elm)->field.sle_next = (head)->slh_first;			\ +	(head)->slh_first = (elm);					\ +} while (0) + +#define	SLIST_REMOVE_NEXT(head, elm, field) do {			\ +	(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next;	\ +} while (0) + +#define	SLIST_REMOVE_HEAD(head, field) do {				\ +	(head)->slh_first = (head)->slh_first->field.sle_next;		\ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do {			\ +	if ((head)->slh_first == (elm)) {				\ +		SLIST_REMOVE_HEAD((head), field);			\ +	} else {							\ +		struct type *curelm = (head)->slh_first;		\ +									\ +		while (curelm->field.sle_next != (elm))			\ +			curelm = curelm->field.sle_next;		\ +		curelm->field.sle_next =				\ +		    curelm->field.sle_next->field.sle_next;		\ +		_Q_INVALIDATE((elm)->field.sle_next);			\ +	}								\ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type)						\ +struct name {								\ +	struct type *lh_first;	/* first element */			\ +} + +#define LIST_HEAD_INITIALIZER(head)					\ +	{ NULL } + +#define LIST_ENTRY(type)						\ +struct {								\ +	struct type *le_next;	/* next element */			\ +	struct type **le_prev;	/* address of previous next element */	\ +} + +/* + * List access methods + */ +#define	LIST_FIRST(head)		((head)->lh_first) +#define	LIST_END(head)			NULL +#define	LIST_EMPTY(head)		(LIST_FIRST(head) == LIST_END(head)) +#define	LIST_NEXT(elm, field)		((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field)					\ +	for((var) = LIST_FIRST(head);					\ +	    (var)!= LIST_END(head);					\ +	    (var) = LIST_NEXT(var, field)) + +/* + * List functions. + */ +#define	LIST_INIT(head) do {						\ +	LIST_FIRST(head) = LIST_END(head);				\ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do {			\ +	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\ +		(listelm)->field.le_next->field.le_prev =		\ +		    &(elm)->field.le_next;				\ +	(listelm)->field.le_next = (elm);				\ +	(elm)->field.le_prev = &(listelm)->field.le_next;		\ +} while (0) + +#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\ +	(elm)->field.le_prev = (listelm)->field.le_prev;		\ +	(elm)->field.le_next = (listelm);				\ +	*(listelm)->field.le_prev = (elm);				\ +	(listelm)->field.le_prev = &(elm)->field.le_next;		\ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do {				\ +	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\ +		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\ +	(head)->lh_first = (elm);					\ +	(elm)->field.le_prev = &(head)->lh_first;			\ +} while (0) + +#define LIST_REMOVE(elm, field) do {					\ +	if ((elm)->field.le_next != NULL)				\ +		(elm)->field.le_next->field.le_prev =			\ +		    (elm)->field.le_prev;				\ +	*(elm)->field.le_prev = (elm)->field.le_next;			\ +	_Q_INVALIDATE((elm)->field.le_prev);				\ +	_Q_INVALIDATE((elm)->field.le_next);				\ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do {				\ +	if (((elm2)->field.le_next = (elm)->field.le_next) != NULL)	\ +		(elm2)->field.le_next->field.le_prev =			\ +		    &(elm2)->field.le_next;				\ +	(elm2)->field.le_prev = (elm)->field.le_prev;			\ +	*(elm2)->field.le_prev = (elm2);				\ +	_Q_INVALIDATE((elm)->field.le_prev);				\ +	_Q_INVALIDATE((elm)->field.le_next);				\ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type)					\ +struct name {								\ +	struct type *sqh_first;	/* first element */			\ +	struct type **sqh_last;	/* addr of last next element */		\ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head)					\ +	{ NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type)						\ +struct {								\ +	struct type *sqe_next;	/* next element */			\ +} + +/* + * Simple queue access methods. + */ +#define	SIMPLEQ_FIRST(head)	    ((head)->sqh_first) +#define	SIMPLEQ_END(head)	    NULL +#define	SIMPLEQ_EMPTY(head)	    (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define	SIMPLEQ_NEXT(elm, field)    ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field)				\ +	for((var) = SIMPLEQ_FIRST(head);				\ +	    (var) != SIMPLEQ_END(head);					\ +	    (var) = SIMPLEQ_NEXT(var, field)) + +/* + * Simple queue functions. + */ +#define	SIMPLEQ_INIT(head) do {						\ +	(head)->sqh_first = NULL;					\ +	(head)->sqh_last = &(head)->sqh_first;				\ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do {			\ +	if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)	\ +		(head)->sqh_last = &(elm)->field.sqe_next;		\ +	(head)->sqh_first = (elm);					\ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do {			\ +	(elm)->field.sqe_next = NULL;					\ +	*(head)->sqh_last = (elm);					\ +	(head)->sqh_last = &(elm)->field.sqe_next;			\ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\ +	if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ +		(head)->sqh_last = &(elm)->field.sqe_next;		\ +	(listelm)->field.sqe_next = (elm);				\ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do {			\ +	if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ +		(head)->sqh_last = &(head)->sqh_first;			\ +} while (0) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type)						\ +struct name {								\ +	struct type *tqh_first;	/* first element */			\ +	struct type **tqh_last;	/* addr of last next element */		\ +} + +#define TAILQ_HEAD_INITIALIZER(head)					\ +	{ NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type)						\ +struct {								\ +	struct type *tqe_next;	/* next element */			\ +	struct type **tqe_prev;	/* address of previous next element */	\ +} + +/* + * tail queue access methods + */ +#define	TAILQ_FIRST(head)		((head)->tqh_first) +#define	TAILQ_END(head)			NULL +#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname)					\ +	(*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field)				\ +	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define	TAILQ_EMPTY(head)						\ +	(TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field)					\ +	for((var) = TAILQ_FIRST(head);					\ +	    (var) != TAILQ_END(head);					\ +	    (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field)		\ +	for((var) = TAILQ_LAST(head, headname);				\ +	    (var) != TAILQ_END(head);					\ +	    (var) = TAILQ_PREV(var, headname, field)) + +/* + * Tail queue functions. + */ +#define	TAILQ_INIT(head) do {						\ +	(head)->tqh_first = NULL;					\ +	(head)->tqh_last = &(head)->tqh_first;				\ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do {			\ +	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\ +		(head)->tqh_first->field.tqe_prev =			\ +		    &(elm)->field.tqe_next;				\ +	else								\ +		(head)->tqh_last = &(elm)->field.tqe_next;		\ +	(head)->tqh_first = (elm);					\ +	(elm)->field.tqe_prev = &(head)->tqh_first;			\ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do {			\ +	(elm)->field.tqe_next = NULL;					\ +	(elm)->field.tqe_prev = (head)->tqh_last;			\ +	*(head)->tqh_last = (elm);					\ +	(head)->tqh_last = &(elm)->field.tqe_next;			\ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\ +	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ +		(elm)->field.tqe_next->field.tqe_prev =			\ +		    &(elm)->field.tqe_next;				\ +	else								\ +		(head)->tqh_last = &(elm)->field.tqe_next;		\ +	(listelm)->field.tqe_next = (elm);				\ +	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\ +} while (0) + +#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\ +	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\ +	(elm)->field.tqe_next = (listelm);				\ +	*(listelm)->field.tqe_prev = (elm);				\ +	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do {				\ +	if (((elm)->field.tqe_next) != NULL)				\ +		(elm)->field.tqe_next->field.tqe_prev =			\ +		    (elm)->field.tqe_prev;				\ +	else								\ +		(head)->tqh_last = (elm)->field.tqe_prev;		\ +	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\ +	_Q_INVALIDATE((elm)->field.tqe_prev);				\ +	_Q_INVALIDATE((elm)->field.tqe_next);				\ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do {			\ +	if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)	\ +		(elm2)->field.tqe_next->field.tqe_prev =		\ +		    &(elm2)->field.tqe_next;				\ +	else								\ +		(head)->tqh_last = &(elm2)->field.tqe_next;		\ +	(elm2)->field.tqe_prev = (elm)->field.tqe_prev;			\ +	*(elm2)->field.tqe_prev = (elm2);				\ +	_Q_INVALIDATE((elm)->field.tqe_prev);				\ +	_Q_INVALIDATE((elm)->field.tqe_next);				\ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type)					\ +struct name {								\ +	struct type *cqh_first;		/* first element */		\ +	struct type *cqh_last;		/* last element */		\ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head)					\ +	{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type)						\ +struct {								\ +	struct type *cqe_next;		/* next element */		\ +	struct type *cqe_prev;		/* previous element */		\ +} + +/* + * Circular queue access methods + */ +#define	CIRCLEQ_FIRST(head)		((head)->cqh_first) +#define	CIRCLEQ_LAST(head)		((head)->cqh_last) +#define	CIRCLEQ_END(head)		((void *)(head)) +#define	CIRCLEQ_NEXT(elm, field)	((elm)->field.cqe_next) +#define	CIRCLEQ_PREV(elm, field)	((elm)->field.cqe_prev) +#define	CIRCLEQ_EMPTY(head)						\ +	(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field)				\ +	for((var) = CIRCLEQ_FIRST(head);				\ +	    (var) != CIRCLEQ_END(head);					\ +	    (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field)			\ +	for((var) = CIRCLEQ_LAST(head);					\ +	    (var) != CIRCLEQ_END(head);					\ +	    (var) = CIRCLEQ_PREV(var, field)) + +/* + * Circular queue functions. + */ +#define	CIRCLEQ_INIT(head) do {						\ +	(head)->cqh_first = CIRCLEQ_END(head);				\ +	(head)->cqh_last = CIRCLEQ_END(head);				\ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\ +	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\ +	(elm)->field.cqe_prev = (listelm);				\ +	if ((listelm)->field.cqe_next == CIRCLEQ_END(head))		\ +		(head)->cqh_last = (elm);				\ +	else								\ +		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\ +	(listelm)->field.cqe_next = (elm);				\ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {		\ +	(elm)->field.cqe_next = (listelm);				\ +	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\ +	if ((listelm)->field.cqe_prev == CIRCLEQ_END(head))		\ +		(head)->cqh_first = (elm);				\ +	else								\ +		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\ +	(listelm)->field.cqe_prev = (elm);				\ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do {			\ +	(elm)->field.cqe_next = (head)->cqh_first;			\ +	(elm)->field.cqe_prev = CIRCLEQ_END(head);			\ +	if ((head)->cqh_last == CIRCLEQ_END(head))			\ +		(head)->cqh_last = (elm);				\ +	else								\ +		(head)->cqh_first->field.cqe_prev = (elm);		\ +	(head)->cqh_first = (elm);					\ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do {			\ +	(elm)->field.cqe_next = CIRCLEQ_END(head);			\ +	(elm)->field.cqe_prev = (head)->cqh_last;			\ +	if ((head)->cqh_first == CIRCLEQ_END(head))			\ +		(head)->cqh_first = (elm);				\ +	else								\ +		(head)->cqh_last->field.cqe_next = (elm);		\ +	(head)->cqh_last = (elm);					\ +} while (0) + +#define	CIRCLEQ_REMOVE(head, elm, field) do {				\ +	if ((elm)->field.cqe_next == CIRCLEQ_END(head))			\ +		(head)->cqh_last = (elm)->field.cqe_prev;		\ +	else								\ +		(elm)->field.cqe_next->field.cqe_prev =			\ +		    (elm)->field.cqe_prev;				\ +	if ((elm)->field.cqe_prev == CIRCLEQ_END(head))			\ +		(head)->cqh_first = (elm)->field.cqe_next;		\ +	else								\ +		(elm)->field.cqe_prev->field.cqe_next =			\ +		    (elm)->field.cqe_next;				\ +	_Q_INVALIDATE((elm)->field.cqe_prev);				\ +	_Q_INVALIDATE((elm)->field.cqe_next);				\ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do {			\ +	if (((elm2)->field.cqe_next = (elm)->field.cqe_next) ==		\ +	    CIRCLEQ_END(head))						\ +		(head)->cqh_last = (elm2);				\ +	else								\ +		(elm2)->field.cqe_next->field.cqe_prev = (elm2);	\ +	if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) ==		\ +	    CIRCLEQ_END(head))						\ +		(head)->cqh_first = (elm2);				\ +	else								\ +		(elm2)->field.cqe_prev->field.cqe_next = (elm2);	\ +	_Q_INVALIDATE((elm)->field.cqe_prev);				\ +	_Q_INVALIDATE((elm)->field.cqe_next);				\ +} while (0) + +#endif	/* !_SYS_QUEUE_H_ */ diff --git a/man/i3status.man b/man/i3status.man index 18cfb19..0274cca 100644 --- a/man/i3status.man +++ b/man/i3status.man @@ -220,6 +220,25 @@ details on the format string.  *Example format*: +%{%a, %b %d%}, %Y%N - %H+ +=== Volume + +Outputs the volume of the specified mixer on the specified device. Works only +on Linux because it uses ALSA. + +*Example order*: +volume master+ + +*Example format*: +♪: %volume+ + +*Example configuration*: +------------------------------------------------------------- +volume master { +	format = "♪: %volume" +	device = "default" +	mixer = "Master" +	mixer_idx = 0 +} +------------------------------------------------------------- +  == Using i3status with dzen2  After installing dzen2, you can directly use it with i3status: diff --git a/src/print_volume.c b/src/print_volume.c new file mode 100644 index 0000000..0eca0c0 --- /dev/null +++ b/src/print_volume.c @@ -0,0 +1,154 @@ +// vim:ts=8:expandtab +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#ifdef LINUX +#include <alsa/asoundlib.h> +#include <alloca.h> +#endif + +#include "i3status.h" +#include "queue.h" + +#ifdef LINUX +struct mixer_hdl { +	char *device; +	char *mixer; +	int mixer_idx; +	snd_mixer_selem_id_t *sid; +	snd_mixer_t *m; +	snd_mixer_elem_t *elem; +	long min; +	long max; + +	TAILQ_ENTRY(mixer_hdl) handles; +}; + +TAILQ_HEAD(handles_head, mixer_hdl) cached = TAILQ_HEAD_INITIALIZER(cached); +#endif + +static void free_hdl(struct mixer_hdl *hdl) { +	free(hdl->device); +	free(hdl->mixer); +	free(hdl); +} + +void print_volume(const char *fmt, const char *device, const char *mixer, int mixer_idx) { +/* Printing volume only works with ALSA at the moment */ +#ifndef LINUX +	return; +#endif +	/* Check if we already opened the mixer and get the handle +	 * from cache if so */ +	bool found = false; +	int err; +	struct mixer_hdl *hdl; +	TAILQ_FOREACH(hdl, &cached, handles) { +		if (strcmp(hdl->device, device) != 0 || +		    strcmp(hdl->mixer, mixer) != 0 || +		    hdl->mixer_idx != mixer_idx) +			continue; +		found = true; +		break; +	} + +	if (!found) { +		if ((hdl = calloc(sizeof(struct mixer_hdl), 1)) == NULL) +			return; + +		if ((hdl->device = strdup(device)) == NULL) { +			free(hdl); +			return; +		} + +		if ((hdl->mixer = strdup(mixer)) == NULL) { +			free(hdl->device); +			free(hdl); +			return; +		} + +		hdl->mixer_idx = mixer_idx; +		snd_mixer_selem_id_malloc(&(hdl->sid)); +		if (hdl->sid == NULL) { +			free_hdl(hdl); +			return; +		} + +		if ((err = snd_mixer_open(&(hdl->m), 0)) < 0) { +			fprintf(stderr, "ALSA: Cannot open mixer: %s\n", snd_strerror(err)); +			free_hdl(hdl); +			return; +		} + +		/* Attach this mixer handle to the given device */ +		if ((err = snd_mixer_attach(hdl->m, device)) < 0) { +			fprintf(stderr, "ALSA: Cannot attach mixer to device: %s\n", snd_strerror(err)); +			snd_mixer_close(hdl->m); +			free_hdl(hdl); +			return; +		} + +		/* Register this mixer */ +		if ((err = snd_mixer_selem_register(hdl->m, NULL, NULL)) < 0) { +			fprintf(stderr, "ALSA: snd_mixer_selem_register: %s\n", snd_strerror(err)); +			snd_mixer_close(hdl->m); +			free_hdl(hdl); +			return; +		} + +		if ((err = snd_mixer_load(hdl->m)) < 0) { +			fprintf(stderr, "ALSA: snd_mixer_load: %s\n", snd_strerror(err)); +			snd_mixer_close(hdl->m); +			free_hdl(hdl); +			return; +		} + +		/* Find the given mixer */ +		snd_mixer_selem_id_set_index(hdl->sid, mixer_idx); +		snd_mixer_selem_id_set_name(hdl->sid, mixer); +		if (!(hdl->elem = snd_mixer_find_selem(hdl->m, hdl->sid))) { +			fprintf(stderr, "ALSA: Cannot find mixer %s (index %i)\n", +				snd_mixer_selem_id_get_name(hdl->sid), snd_mixer_selem_id_get_index(hdl->sid)); +			snd_mixer_close(hdl->m); +			free_hdl(hdl); +			return; +		} + +		/* Get the volume range to convert the volume later */ +		snd_mixer_selem_get_playback_volume_range(hdl->elem, &(hdl->min), &(hdl->max)); +		TAILQ_INSERT_TAIL(&cached, hdl, handles); +	} + +	long val; +	snd_mixer_handle_events (hdl->m); +	snd_mixer_selem_get_playback_volume (hdl->elem, 0, &val); +	int avg; +	if (hdl->max != 100) { +		float avgf = ((float)val / hdl->max) * 100; +		avg = (int)avgf; +		avg = (avgf - avg < 0.5 ? avg : (avg+1)); +	} else avg = (int)val; + +	/* Check for mute */ +	if (snd_mixer_selem_has_playback_switch(hdl->elem)) { +		int pbval; +		if ((err = snd_mixer_selem_get_playback_switch(hdl->elem, 0, &pbval)) < 0) +			fprintf (stderr, "ALSA: playback_switch: %s\n", snd_strerror(err)); +		if (!pbval) +			avg = 0; +	} + +	const char *walk = fmt; +	for (; *walk != '\0'; walk++) { +		if (*walk != '%') { +			putchar(*walk); +			continue; +		} +		if (BEGINS_WITH(walk+1, "volume")) { +			printf("%d%%", avg); +			walk += strlen("volume"); +		} +	} +} | 
