<|start_filename|>datetime.c<|end_filename|>
/* See LICENSE file for copyright and license details. */

#if defined(__linux)
# include <alloca.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <X11/Xlib.h>

#include "bspwmbar.h"
#include "util.h"

void
datetime(draw_context_t *dc, module_option_t *opts)
{
	time_t timer = time(NULL);
	struct tm *tptr = localtime(&timer);

	if (!opts->date.format)
		die("datetime(): arg is required for datetime");
	if (!opts->date.prefix)
		opts->date.prefix = "";
	if (!opts->date.suffix)
		opts->date.suffix = "";
	int size = SMALLER(strlen(opts->date.format) + strlen(opts->date.prefix) +
	                   strlen(opts->date.prefix) + 1, 128);
	char *format = alloca(size);
	snprintf(format, size, "%s%s%s", opts->date.prefix, opts->date.format,
	         opts->date.suffix);
	strftime(buf, sizeof(buf), format, tptr);

	draw_text(dc, buf);
}


<|start_filename|>backlight.c<|end_filename|>
/* See LICENSE file for copyright and license details. */

#if defined(__linux)
# include <alloca.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#include <xcb/xcb.h>
#include <xcb/randr.h>

#include "bspwmbar.h"
#include "util.h"

typedef struct {
	int32_t min;
	int32_t max;
	int32_t cur;
} backlight_t;

static bool init_atom(xcb_connection_t *);
static bool backlight_load(backlight_t *, xcb_connection_t *);
static bool backlight_load_info(xcb_connection_t *, xcb_randr_output_t, backlight_t *);
static void backlight_set(xcb_connection_t *, xcb_randr_output_t, int32_t);

static xcb_atom_t atom_backlight;
static xcb_randr_output_t output_cache;

void
backlight(draw_context_t *dc, module_option_t *opts)
{
	backlight_t backlight = { 0 };
	uint32_t blightness = 0;

	if (!backlight_load(&backlight, xcb_connection()))
		return;

	blightness = (backlight.cur - backlight.min) * 100 / (backlight.max - backlight.min);

	sprintf(buf, "%s%d%s", opts->backlight.prefix, blightness, opts->backlight.suffix);
	draw_text(dc, buf);
}

bool
backlight_load(backlight_t *backlight, xcb_connection_t *xcb)
{
	size_t i;
	xcb_randr_get_screen_resources_reply_t *screen_reply;
	xcb_randr_output_t *outputs;

	xcb_screen_t *scr = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data;

	screen_reply = xcb_randr_get_screen_resources_reply(xcb, xcb_randr_get_screen_resources(xcb, scr->root), NULL);
	outputs = xcb_randr_get_screen_resources_outputs(screen_reply);

	for (i = 0; i < screen_reply->num_outputs; i++) {
		if (backlight_load_info(xcb, outputs[i], backlight)) {
			output_cache = outputs[i];
			break;
		}
	}
	free(screen_reply);

	return true;
}

bool
backlight_load_info(xcb_connection_t *xcb, xcb_randr_output_t output, backlight_t *backlight)
{
	xcb_randr_get_output_property_reply_t *prop_reply;
	xcb_randr_query_output_property_reply_t *query_reply;
	int32_t cur, *values;

	/* get atom for backlight */
	if (!atom_backlight && !init_atom(xcb))
		return false;

	prop_reply = xcb_randr_get_output_property_reply(xcb, xcb_randr_get_output_property(xcb, output, atom_backlight, XCB_ATOM_NONE, 0, 4, 0, 0), NULL);
	if (!prop_reply)
		return false;
	cur = *((int32_t *)xcb_randr_get_output_property_data(prop_reply));
	free(prop_reply);

	query_reply = xcb_randr_query_output_property_reply(xcb, xcb_randr_query_output_property(xcb, output, atom_backlight), NULL);
	if (!query_reply)
		return false;
	if (query_reply->range && xcb_randr_query_output_property_valid_values_length(query_reply) == 2) {
		backlight->cur = cur;
		values = xcb_randr_query_output_property_valid_values(query_reply);

		backlight->min = values[0];
		backlight->max = values[1];
	}
	free(query_reply);

	return true;
}

void
backlight_set(xcb_connection_t *xcb, xcb_randr_output_t output, int32_t value)
{
	/* get atom for backlight */
	if (!atom_backlight && !init_atom(xcb))
		return;

	xcb_randr_change_output_property(xcb, output, atom_backlight, XCB_ATOM_INTEGER, 32, XCB_PROP_MODE_REPLACE, 1, (unsigned char *)&value);
}

void
backlight_ev(xcb_generic_event_t *ev)
{
	backlight_t backlight;
	xcb_button_press_event_t *button;
	int32_t cur, step;

	xcb_connection_t *xcb = xcb_connection();
	if (!backlight_load(&backlight, xcb))
		return;

	cur = (backlight.cur - backlight.min);
	step = (backlight.max - backlight.min) * 0.05 + 1;

	switch (ev->response_type & ~0x80) {
	case XCB_BUTTON_PRESS:
		button = (xcb_button_press_event_t *)ev;
		switch (button->detail) {
		case XCB_BUTTON_INDEX_4:
			cur = (cur / step) * step + step;
			if (cur > backlight.max)
				cur = backlight.max;
			backlight_set(xcb, output_cache, cur);
			break;
		case XCB_BUTTON_INDEX_5:
			cur = (cur / step) * step - step;
			if (cur < backlight.min)
				cur = backlight.min;
			backlight_set(xcb, output_cache, cur);
			break;
		}
		break;
	}
}

bool
init_atom(xcb_connection_t *xcb)
{
	xcb_intern_atom_reply_t *backlight_reply;

	backlight_reply = xcb_intern_atom_reply(xcb, xcb_intern_atom(xcb, true, strlen("Backlight"), "Backlight"), NULL);
	if (!backlight_reply)
		return false;

	atom_backlight = backlight_reply->atom;
	free(backlight_reply);

	return true;
}


<|start_filename|>util.c<|end_filename|>
/* See LICENSE file for copyright and license details. */

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <xcb/xcb.h>

#include "util.h"

int
pscanf(const char *path, const char *fmt, ...)
{
	FILE *fp;
	va_list ap;
	int n;

	if (!(fp = fopen(path, "r"))) {
		return -1;
	}
	va_start(ap, fmt);
	n = vfscanf(fp, fmt, ap);
	va_end(ap);
	fclose(fp);

	return (n == EOF) ? -1 : n;
}

void
list_init(list_head *head, list_head *prev, list_head *next)
{
	next->prev = head;
	head->next = next;
	head->prev = prev;
	prev->next = head;
}

void
list_add(list_head *head, list_head *entry)
{
	list_init(entry, head, head->next);
}

void
list_add_tail(list_head *head, list_head *entry)
{
	list_init(entry, head->prev, head);
}

void
list_del(list_head *head)
{
	list_head *prev = head->prev;
	list_head *next = head->next;

	next->prev = prev;
	prev->next = next;
}

xcb_atom_t
xcb_atom_get(xcb_connection_t *xcb, const char *name, bool only_exists)
{
	xcb_atom_t atom;
	xcb_intern_atom_reply_t *atom_reply;

	if (!(atom_reply = xcb_intern_atom_reply(xcb, xcb_intern_atom(xcb, only_exists, strlen(name), name), NULL)))
		return 0;

	atom = atom_reply->atom;
	free(atom_reply);

	return atom;
}


<|start_filename|>systray.c<|end_filename|>
#if defined(__linux)
# include <alloca.h>
#elif defined(__OpenBSD__) || defined(__FreeBSD__)
# define __BSD_VISIBLE 1
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <xcb/xcb.h>
#include <xcb/xcb_util.h>

#include "bspwmbar.h"
#include "systray.h"

#define ATOM_SYSTRAY "_NET_SYSTEM_TRAY_S0"

#define XEMBED_EMBEDDED_NOTIFY        0
#define XEMBED_WINDOW_ACTIVATE        1
#define XEMBED_WINDOW_DEACTIVATE      2
#define XEMBED_REQUEST_FOCUS          3
#define XEMBED_FOCUS_IN               4
#define XEMBED_FOCUS_OUT              5
#define XEMBED_FOCUS_NEXT             6
#define XEMBED_FOCUS_PREV             7
/* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */
#define XEMBED_MODALITY_ON            10
#define XEMBED_MODALITY_OFF           11
#define XEMBED_REGISTER_ACCELERATOR   12
#define XEMBED_UNREGISTER_ACCELERATOR 13
#define XEMBED_ACTIVATE_ACCELERATOR   14

#define XEMBED_MAPPED (1 << 0)

enum {
	SYSTRAY_TIME,
	SYSTRAY_OPCODE,
	SYSTRAY_DATA1,
	SYSTRAY_DATA2,
	SYSTRAY_DATA3,
};

enum {
	SYSTRAY_REQUEST_DOCK,
	SYSTRAY_BEGIN_MESSAGE,
	SYSTRAY_CANCEL_MESSAGE,
};

struct _systray_t {
	xcb_connection_t *xcb;
	xcb_screen_t *scr;
	xcb_window_t win;
	int icon_size;
	list_head items;
};

/* functions */
static bool xembed_send(xcb_connection_t *, xcb_window_t, long, long, long, long);
static bool xembed_embedded_notify(systray_t *, xcb_window_t, long);
static int xembed_unembed_window(systray_t *, xcb_window_t);
static bool xembed_getinfo(systray_t *, xcb_window_t, xembed_info_t *);
static xcb_atom_t get_systray_atom(xcb_connection_t *);
static bool systray_set_selection_owner(systray_t *, xcb_atom_t);
static bool systray_get_ownership(systray_t *);
static systray_item_t *systray_append_item(systray_t *, xcb_window_t);
static systray_item_t *systray_find_item(systray_t *, xcb_window_t);

xcb_atom_t
get_systray_atom(xcb_connection_t *xcb)
{
	static xcb_atom_t systray_atom = 0;
	if (systray_atom)
		return systray_atom;
	systray_atom = xcb_atom_get(xcb, ATOM_SYSTRAY, false);
	return systray_atom;
}

bool
systray_set_selection_owner(systray_t *tray, xcb_atom_t atom)
{
	return xcb_request_check(tray->xcb, xcb_set_selection_owner(tray->xcb, tray->win, atom, XCB_TIME_CURRENT_TIME)) == NULL;
}

xcb_window_t
get_selection_owner(xcb_connection_t *xcb, xcb_atom_t atom)
{
	xcb_window_t win;
	xcb_get_selection_owner_reply_t *or;
	or = xcb_get_selection_owner_reply(xcb, xcb_get_selection_owner(xcb, atom), NULL);
	win = or->owner;
	free(or);
	return win;
}

bool
systray_get_ownership(systray_t *tray)
{
	xcb_atom_t atom = get_systray_atom(tray->xcb);
	if (get_selection_owner(tray->xcb, atom))
		return false;

	return systray_set_selection_owner(tray, atom);
}

/**
 * systray_new() - Initialize systray_t *object.
 * @xcb: A display pointer of win.
 * @win: A window for system tray.
 *
 * Return: A new system tray object.
 */
systray_t *
systray_new(xcb_connection_t *xcb, xcb_screen_t *scr, xcb_window_t win)
{
	xcb_client_message_event_t ev = { 0 };
	systray_t *tray = (systray_t *)calloc(1, sizeof(struct _systray_t));
	list_head_init(&tray->items);
	tray->xcb = xcb;
	tray->scr = scr;
	tray->win = win;

	if (!systray_get_ownership(tray)) {
		free(tray);
		return NULL;
	}

	ev.response_type = XCB_CLIENT_MESSAGE;
	ev.type = xcb_atom_get(tray->xcb, "MANAGER", false);
	ev.format = 32;
	ev.data.data32[0] = XCB_TIME_CURRENT_TIME;
	ev.data.data32[1] = get_systray_atom(tray->xcb);
	ev.data.data32[2] = tray->win;
	ev.data.data32[3] = 0;
	ev.data.data32[4] = 0;
	xcb_send_event(xcb, 0, tray->win, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *)&ev);

	return tray;
}

bool
xembed_send(xcb_connection_t *xcb, xcb_window_t win, long message, long d1, long d2, long d3)
{
	xcb_client_message_event_t ev = { 0 };

	ev.response_type = XCB_CLIENT_MESSAGE;
	ev.window = win;
	ev.type = xcb_atom_get(xcb, "_XEMBED", false);
	ev.format = 32;
	ev.data.data32[0] = XCB_TIME_CURRENT_TIME;
	ev.data.data32[1] = message;
	ev.data.data32[2] = d1;
	ev.data.data32[3] = d2;
	ev.data.data32[4] = d3;

	if (xcb_request_check(xcb, xcb_send_event(xcb, 0, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *)&ev)))
		return false;
	xcb_flush(xcb);

	return true;
}

bool
xembed_embedded_notify(systray_t *tray, xcb_window_t win, long version)
{
	return xembed_send(tray->xcb, win, XEMBED_EMBEDDED_NOTIFY, 0, tray->win, version);
}

int
xembed_unembed_window(systray_t *tray, xcb_window_t child)
{
	xcb_unmap_window(tray->xcb, child);
	xcb_reparent_window(tray->xcb, child, tray->scr->root, 0, 0);

	return 0;
}

bool
xembed_getinfo(systray_t *tray, xcb_window_t win, xembed_info_t *info)
{
	xcb_atom_t infoatom;
	xcb_get_property_reply_t *prop;
	uint32_t *xembed;

	if (!(infoatom = xcb_atom_get(tray->xcb, "_XEMBED_INFO", false)))
		return false;
	if (!(prop = xcb_get_property_reply(tray->xcb, xcb_get_property(tray->xcb, 0, win, infoatom, XCB_GET_PROPERTY_TYPE_ANY, 0, 8), NULL)))
		return false;
	xembed = (uint32_t *)xcb_get_property_value(prop);

	info->version = xembed[0];
	info->flags = xembed[1];

	free(prop);
	return true;
}

systray_item_t *
systray_append_item(systray_t *tray, xcb_window_t win)
{
	systray_item_t *item = calloc(1, sizeof(systray_item_t));
	item->win = win;

	list_add_tail(&tray->items, &item->head);

	return item;
}

void
systray_set_icon_size(systray_t *tray, int size)
{
	tray->icon_size = size;
}

int
systray_icon_size(systray_t *tray)
{
	return tray->icon_size;
}

systray_item_t *
systray_find_item(systray_t *tray, xcb_window_t win)
{
	list_head *pos;
	list_for_each(&tray->items, pos) {
		systray_item_t *item = list_entry(pos, systray_item_t, head);
		if (item->win == win)
			return item;
	}
	return NULL;
}

void
systray_remove_item(systray_t *tray, xcb_window_t win)
{
	list_head *pos;
	list_for_each(&tray->items, pos) {
		systray_item_t *item = list_entry(pos, systray_item_t, head);
		if (item->win == win) {
			list_del(pos);
			free(item);
			return;
		}
	}
}

int
systray_handle(systray_t *tray, xcb_generic_event_t *ev)
{
	xcb_atom_t atom;
	xcb_selection_clear_event_t *selection;
	xcb_client_message_event_t *client;
	xcb_property_notify_event_t *property;
	xcb_change_window_attributes_value_list_t attrs = { 0 };
	xcb_configure_window_value_list_t config = { 0 };
	systray_item_t *item;

	switch (ev->response_type & ~0x80) {
	case XCB_SELECTION_CLEAR:
		selection = (xcb_selection_clear_event_t *)ev;
		atom = get_systray_atom(tray->xcb);
		if (selection->selection == atom)
			systray_set_selection_owner(tray, get_systray_atom(tray->xcb));
		break;
	case XCB_CLIENT_MESSAGE:
		client = (xcb_client_message_event_t *)ev;
		atom = xcb_atom_get(tray->xcb, "_NET_SYSTEM_TRAY_OPCODE", true);
		if (client->type != atom)
			return 1;

		xcb_window_t win = 0;
		switch (client->data.data32[SYSTRAY_OPCODE]) {
		case SYSTRAY_REQUEST_DOCK:
			win = client->data.data32[SYSTRAY_DATA1];

			attrs.event_mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE;
			xcb_change_window_attributes_aux(tray->xcb, win, XCB_CW_EVENT_MASK, &attrs);
			if (tray->icon_size) {
				config.width = tray->icon_size;
				config.height = tray->icon_size;
				xcb_configure_window_aux(tray->xcb, win, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, &config);
			}
			if (xcb_request_check(tray->xcb, xcb_reparent_window(tray->xcb, win, tray->win, 0, 0)))
				break;

			/* notify to the window */
			xembed_embedded_notify(tray, win, 0);
			item = systray_append_item(tray, win);
			xembed_getinfo(tray, win, &item->info);

			break;
		}
		break;
	case XCB_PROPERTY_NOTIFY:
		property = (xcb_property_notify_event_t *)ev;
		if (property->state == XCB_PROPERTY_NEW_VALUE) {
			if (!(item = systray_find_item(tray, property->window)))
				return 1;
			xembed_info_t info = { 0 };
			xembed_getinfo(tray, property->window, &info);

			if (!(item->info.flags ^ info.flags))
				return 0;

			item->info.flags = info.flags;
			if (item->info.flags & XEMBED_MAPPED) {
				config.stack_mode = XCB_STACK_MODE_ABOVE;
				xcb_configure_window_aux(tray->xcb, property->window, XCB_CONFIG_WINDOW_STACK_MODE, &config);
			} else {
				xcb_unmap_window(tray->xcb, item->win);
			}
		}
		break;
	}
	return 0;
}

list_head *
systray_get_items(systray_t *tray)
{
	return &tray->items;
}

xcb_connection_t *
systray_get_connection(systray_t *tray)
{
	return tray->xcb;
}

xcb_window_t
systray_get_window(systray_t *tray)
{
	return tray->win;
}

void
systray_destroy(systray_t *tray)
{
	if (!tray)
		return;
	xcb_atom_t atom = get_systray_atom(tray->xcb);
	if (atom)
		xcb_set_selection_owner(tray->xcb, XCB_NONE, atom, XCB_TIME_CURRENT_TIME);

	list_head *pos, *tmp;
	list_for_each_safe(&tray->items, pos, tmp) {
		systray_item_t *item = list_entry(pos, systray_item_t, head);
		xembed_unembed_window(tray, item->win);
		list_del(pos);
		free(item);
	}
	free(tray);
}


<|start_filename|>volume.c<|end_filename|>
/* See LICENSE file for copyright and license details. */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/audioio.h>
#include <xcb/xcb.h>

#include "bspwmbar.h"
#include "util.h"

#define check_device(dinfo) \
(((dinfo).type == AUDIO_MIXER_VALUE && \
  !strncmp((dinfo).label.name, AudioNmaster, MAX_AUDIO_DEV_LEN)) || \
 ((dinfo).type == AUDIO_MIXER_ENUM && \
  !strncmp((dinfo).label.name, AudioNmute, MAX_AUDIO_DEV_LEN)))

/* functions */
static int get_volume(int);
static int is_muted(int);
static void init_devinfo(int);
static void toggle_mute(int);
static void set_volume(int, int);

static char *file = NULL;
static mixer_ctrl_t vctrl = { 0 };
static mixer_ctrl_t mctrl = { 0 };
static int initialized = 0;

int
get_volume(int fd)
{
	if (ioctl(fd, AUDIO_MIXER_READ, &vctrl) < 0) {
		return 0;
	}
	return vctrl.un.value.num_channels == 1 ?
	       vctrl.un.value.level[AUDIO_MIXER_LEVEL_MONO] :
	       (vctrl.un.value.level[AUDIO_MIXER_LEVEL_LEFT] >
	        vctrl.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] ?
	        vctrl.un.value.level[AUDIO_MIXER_LEVEL_LEFT] :
	        vctrl.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]);
}

int
is_muted(int fd)
{
	if (ioctl(fd, AUDIO_MIXER_READ, &mctrl) < 0) {
		return 0;
	}
	return mctrl.un.ord;
}

void
init_devinfo(int fd)
{
	mixer_devinfo_t dinfo;
	mixer_ctrl_t ctrl;

	int cls = -1;
	for (dinfo.index = 0; cls == -1; dinfo.index++) {
		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo)) {
			close(fd);
			die("volume: failed to get device info\n");
		}
		if (dinfo.type == AUDIO_MIXER_CLASS &&
		    !strncmp(dinfo.label.name, AudioCoutputs, MAX_AUDIO_DEV_LEN))
			cls = dinfo.index;
	}

	int vol = 1, mute = 1;
	for (dinfo.index = 0; vol || mute; dinfo.index++) {
		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0) {
			close(fd);
			die("volume: failed to get device info\n");
		}
		if (dinfo.mixer_class == cls && check_device(dinfo)) {
			ctrl.dev = dinfo.index;
			ctrl.type = dinfo.type;
			ctrl.un.value.num_channels = 2;
			if (ioctl(fd, AUDIO_MIXER_READ, &ctrl) < 0) {
				ctrl.un.value.num_channels = 1;
				if (ioctl(fd, AUDIO_MIXER_READ, &ctrl) < 0) {
					close(fd);
					die("volume: failed to get mixer info\n");
				}
			}
			if (ctrl.type == AUDIO_MIXER_VALUE) {
				vctrl = ctrl;
				vol = 0;
			} else if (ctrl.type == AUDIO_MIXER_ENUM) {
				mctrl = ctrl;
				mute = 0;
			}
		}
	}

	initialized = 1;
}

void
volume(draw_context_t *dc, module_option_t *opts)
{
	if (!file) {
		if ((file = getenv("MIXERDEVICE")) == 0 || *file == '\0')
			file = "/dev/mixer";
	}

	int fd;
	if ((fd = open(file, O_RDONLY)) < 0)
		die("volume: failed to open %s\n", file);

	if (!initialized)
		init_devinfo(fd);

	if (!opts->vol.prefix)
		opts->vol.prefix = "";
	if (!opts->vol.suffix)
		opts->vol.suffix = "";

	const char *mark = is_muted(fd) ? opts->vol.muted : opts->vol.unmuted;
	sprintf(buf, "%s%s %d%s", opts->vol.prefix, mark, get_volume(fd) * 100 / 255,
	                          opts->vol.suffix);
	draw_text(dc, buf);

	close(fd);
}

void
toggle_mute(int fd)
{
	if (is_muted(fd))
		mctrl.un.ord = 0;
	else
		mctrl.un.ord = 1;

	ioctl(fd, AUDIO_MIXER_WRITE, &mctrl);
}

void
set_volume(int fd, int vol)
{
	if (vctrl.un.value.num_channels == 1) {
		vctrl.un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol;
	} else {
		vctrl.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = vol;
		vctrl.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = vol;
	}

	ioctl(fd, AUDIO_MIXER_WRITE, &vctrl);
}

void
volume_ev(xcb_generic_event_t *ev)
{
	xcb_button_press_event_t *button;
	int fd, vol;

	if ((fd = open(file, O_RDWR)) < 0)
		die("volume: failed to open %s\n", file);

	vol = get_volume(fd);

	switch (ev->response_type & ~0x80) {
	case XCB_BUTTON_PRESS:
		button = (xcb_button_press_event_t *)ev;
		switch (button->detail) {
		case XCB_BUTTON_INDEX_1:
			toggle_mute(fd);
			break;
		case XCB_BUTTON_INDEX_4:
			set_volume(fd, SMALLER(vol + 12, 255));
			break;
		case XCB_BUTTON_INDEX_5:
			set_volume(fd, BIGGER(vol - 12, 0));
			break;
		}
		break;
	}
	close(fd);
}


<|start_filename|>alsa.c<|end_filename|>
/* See LICENSE file for copyright and license details. */

#include <alloca.h>
#include <alsa/asoundlib.h>
#include <xcb/xcb.h>

#include "bspwmbar.h"

enum {
	ALSACTL_GETINFO = 1,
	ALSACTL_TOGGLE_MUTE,
	ALSACTL_VOLUME_UP,
	ALSACTL_VOLUME_DOWN,
};

typedef struct {
	long volume;
	int  unmuted;
	long max, min;
	long oneper;
	int  has_switch;
} alsa_info_t;

static snd_ctl_t *ctl;
static snd_mixer_t *mixer;
static int initialized = 0;
static alsa_info_t info = { 0 };
static poll_fd_t pfd = { 0 };

/* functions */
static void get_info(snd_mixer_elem_t *);
static void toggle_mute(snd_mixer_elem_t *);
static void set_volume(snd_mixer_elem_t *, long);
static void alsa_control(uint8_t);
static int alsa_connect();
static int alsa_disconnect();
static poll_result_t alsa_update(int);

void
get_info(snd_mixer_elem_t *elem)
{
	if (!initialized) {
		snd_mixer_selem_get_playback_volume_range(elem, &info.min, &info.max);
		info.has_switch = snd_mixer_selem_has_playback_switch(elem);
		info.oneper = BIGGER((info.max - info.min) / 100, 1);
		initialized = 1;
	}

	snd_mixer_selem_get_playback_volume(elem, 0, &info.volume);
	if (info.has_switch)
		snd_mixer_selem_get_playback_switch(elem, 0, &info.unmuted);
	else
		info.unmuted = 1;
}

void
toggle_mute(snd_mixer_elem_t *elem)
{
	if (!info.has_switch)
		return;
	info.unmuted = info.unmuted ? 0 : 1;
	snd_mixer_selem_set_playback_switch_all(elem, info.unmuted);
}

void
set_volume(snd_mixer_elem_t *elem, long volume)
{
	if (!BETWEEN(volume, info.min, info.max))
		volume = SMALLER(BIGGER(volume, info.min), info.max);
	snd_mixer_selem_set_playback_volume_all(elem, volume);
}

void
alsa_control(uint8_t ctlno)
{
	snd_mixer_elem_t *elem = NULL;
	snd_mixer_selem_id_t *sid = NULL;

	snd_mixer_selem_id_alloca(&sid);
	snd_mixer_selem_id_set_index(sid, 0);
	snd_mixer_selem_id_set_name(sid, "Master");
	if (!(elem = snd_mixer_find_selem(mixer, sid))) {
		info.max = 1;
		return;
	}

	switch (ctlno) {
	case ALSACTL_GETINFO:
		get_info(elem);
		break;
	case ALSACTL_TOGGLE_MUTE:
		toggle_mute(elem);
		break;
	case ALSACTL_VOLUME_UP:
		set_volume(elem, info.volume + info.oneper * 5);
		break;
	case ALSACTL_VOLUME_DOWN:
		set_volume(elem, info.volume - info.oneper * 5);
		break;
	}
}

int
alsa_connect()
{
	if (snd_ctl_open(&ctl, "default", SND_CTL_READONLY | SND_CTL_NONBLOCK))
		return -1;

	if (snd_ctl_subscribe_events(ctl, 1)) {
		snd_ctl_close(ctl);
		return -1;
	}

	/* mixer initialization */
	if (snd_mixer_open(&mixer, 0)) {
		snd_ctl_close(ctl);
		return -1;
	}
	snd_mixer_attach(mixer, "default");
	snd_mixer_selem_register(mixer, NULL, NULL);
	snd_mixer_load(mixer);

	/* get poll fd */
	struct pollfd pfds;
	if (!snd_ctl_poll_descriptors(ctl, &pfds, 1)) {
		snd_ctl_close(ctl);
		return -1;
	}
	return pfds.fd;
}

poll_result_t
alsa_update(int fd)
{
	(void)fd;
	snd_ctl_event_t *event;

	snd_ctl_event_alloca(&event);
	if (snd_ctl_read(ctl, event) < 0)
		return PR_REINIT;
	if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM)
		return PR_NOOP;

	snd_mixer_handle_events(mixer);

	unsigned int mask = snd_ctl_event_elem_get_mask(event);
	if (!(mask & SND_CTL_EVENT_MASK_VALUE))
		return PR_NOOP;

	alsa_control(ALSACTL_GETINFO);

	return PR_UPDATE;
}

int
alsa_disconnect()
{
	return snd_ctl_close(ctl);
}

void
alsa_init()
{
	pfd.fd = alsa_connect();
	pfd.init = alsa_connect;
	pfd.deinit = alsa_disconnect;
	pfd.handler = alsa_update;
	poll_add(&pfd);
}

void
volume(draw_context_t *dc, module_option_t *opts)
{
	if (!pfd.fd)
		alsa_init();

	if (!opts->vol.prefix)
		opts->vol.prefix = "";
	if (!opts->vol.suffix)
		opts->vol.suffix = "";

	if (!info.volume)
		alsa_control(ALSACTL_GETINFO);

	const char *mark = (info.unmuted) ? opts->vol.unmuted : opts->vol.muted;
	sprintf(buf, "%s%s %.0lf%s", opts->vol.prefix,  mark,
	                             (double)info.volume / info.max * 100,
	                             opts->vol.suffix);
	draw_text(dc, buf);
}

void
volume_ev(xcb_generic_event_t *ev)
{
	xcb_button_press_event_t *button;
	switch (ev->response_type & ~0x80) {
	case XCB_BUTTON_PRESS:
		button = (xcb_button_press_event_t *)ev;
		switch (button->detail) {
		case XCB_BUTTON_INDEX_1:
			alsa_control(ALSACTL_TOGGLE_MUTE);
			break;
		case XCB_BUTTON_INDEX_4:
			alsa_control(ALSACTL_VOLUME_UP);
			break;
		case XCB_BUTTON_INDEX_5:
			alsa_control(ALSACTL_VOLUME_DOWN);
			break;
		}
		break;
	}
}


<|start_filename|>util.h<|end_filename|>
/* See LICENSE file for copyright and license details. */

#ifndef BSPWMBAR_UTIL_H_
#define BSPWMBAR_UTIL_H_

#include <stdbool.h>
#include <xcb/xcb.h>

/* utility macros */
#define LENGTH(X)        (sizeof (X) / sizeof (X[0]))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
#define SMALLER(A, B)    ((A) < (B) ? (A) : (B))
#define BIGGER(A, B)     ((A) > (B) ? (A) : (B))
#define DIVCEIL(n, d)    (((n) + ((d) - 1)) / (d))

#define die(...) { fprintf(stderr, __VA_ARGS__); exit(1); }
#define err(...) { fprintf(stderr, __VA_ARGS__); }

int pscanf(const char *, const char *, ...);

typedef struct _list_head {
	struct _list_head *prev, *next;
} list_head;

/* list */
#define list_head_init(ptr) { (ptr)->prev = (ptr); (ptr)->next = (ptr); }
#define list_for_each(head, pos) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_reverse(head, pos) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(char *)(&((type *)0)->member)))
#define list_empty(head) ((head)->next == (head))
#define list_count(head, num) \
for (list_head *pos = (head)->next; pos != (head); pos = pos->next) num++
#define list_for_each_safe(head, pos, n) \
for (pos = (head)->next, n = pos->next; pos != (head); pos = n, n = pos->next)

void list_init(list_head *head, list_head *prev, list_head *next);
void list_add(list_head *head, list_head *entry);
void list_add_tail(list_head *head, list_head *entry);
void list_del(list_head *head);

/* Xlib error catch */
xcb_atom_t xcb_atom_get(xcb_connection_t *, const char *, bool);

#endif


<|start_filename|>systray.h<|end_filename|>
/* See LICENSE file for copyright and license details. */

#ifndef SYSTRAY_H_
#define SYSTRAY_H_

#include <stdbool.h>
#include <xcb/xcb.h>

#include "util.h"

typedef struct {
	unsigned long version;
	unsigned long flags;
} xembed_info_t;

typedef struct _systray_item_t {
	xcb_window_t win;
	xembed_info_t info;
	int x;
	bool mapped;

	list_head head;
} systray_item_t;

typedef struct _systray_t systray_t;

systray_t *systray_new(xcb_connection_t *, xcb_screen_t *, xcb_window_t);
int systray_handle(systray_t *, xcb_generic_event_t *);
void systray_destroy(systray_t *);
void systray_remove_item(systray_t *, xcb_window_t);
xcb_window_t systray_get_window(systray_t *);
xcb_connection_t *systray_get_connection(systray_t *);
list_head *systray_get_items(systray_t *);
int systray_icon_size(systray_t *);
void systray_set_icon_size(systray_t *, int);

#endif /* SYSTRAY_H_ */


<|start_filename|>bspwm.h<|end_filename|>
/* See LICENSE file for copyright and license details. */

#ifndef BSPWMBAR_BSPWM_H_
#define BSPWMBAR_BSPWM_H_

/* bspwm commands */
#define SUBSCRIBE_REPORT "subscribe\0report"

typedef enum {
	BSPWM_DESKTOP_FREE     = 0,
	BSPWM_DESKTOP_FOCUSED  = 1 << 1,
	BSPWM_DESKTOP_OCCUPIED = 1 << 2,
	BSPWM_DESKTOP_URGENT   = 1 << 3,
} bspwm_desktop_state_t;

typedef struct _bspwm_desktop_t bspwm_desktop_t;
typedef struct _bspwm_monitor_t bspwm_monitor_t;

bspwm_desktop_state_t
bspwm_desktop_state(bspwm_desktop_t *);

#endif // BSPWM_BAR_BSPWM_H_


<|start_filename|>bspwm.c<|end_filename|>
/* See LICENSE file for copyright and license details. */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <unistd.h>

#include "bspwmbar.h"
#include "bspwm.h"
#include "util.h"

struct _bspwm_desktop_t {
	char *name;
	bspwm_desktop_state_t state;

	list_head head;
};

struct _bspwm_monitor_t {
	char *name;
	bool is_active;

	/* cache of number of desktops */
	list_head desktops;

	list_head head;
};

typedef struct {
	list_head monitors;
} bspwm_t;

static void bspwm_init();
static int bspwm_connect();
static int bspwm_disconnect();
static int bspwm_send(const char*, size_t);
static void bspwm_parse(const char *);
static poll_result_t bspwm_handle(int);

/* file descriptior for bspwm */
static poll_fd_t pfd = { 0 };
static bspwm_t *bspwm = NULL;

/**
 * bspwm_connect() - connect to bspwm socket.
 *
 * Return: file descripter or -1.
 */
int
bspwm_connect()
{
	struct sockaddr_un sock;
	int fd, dpyno = 0, scrno = 0;
	char *sp = NULL;

	sock.sun_family = AF_UNIX;
	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
		return -1;

	sp = getenv("BSPWM_SOCKET");
	if (sp) {
		snprintf(sock.sun_path, sizeof(sock.sun_path), "%s", sp);
	} else {
		if (xcb_parse_display(NULL, &sp, &dpyno, &scrno))
			snprintf(sock.sun_path, sizeof(sock.sun_path),
			         "/tmp/bspwm%s_%i_%i-socket", sp, dpyno, scrno);
		free(sp);
	}

	if (connect(fd, (struct sockaddr *)&sock, sizeof(sock)) == -1)
		return -1;

	return fd;
}

int
bspwm_disconnect()
{
	bspwm_monitor_t *mon;
	bspwm_desktop_t *desk;
	list_head *cur, *tmp, *cur2, *tmp2;

	close(pfd.fd);
	list_for_each_safe(&bspwm->monitors, cur, tmp) {
		mon = list_entry(cur, bspwm_monitor_t, head);
		list_for_each_safe(&mon->desktops, cur2, tmp2) {
			desk = list_entry(cur2, bspwm_desktop_t, head);
			free(desk->name);
			free(desk);
		}
		free(mon->name);
		free(mon);
	}
	free(bspwm);
	return 0;
}

void
bspwm_init()
{
	bspwm = calloc(1, sizeof(bspwm_t));

	list_head_init(&bspwm->monitors);

	pfd.fd = bspwm_connect();
	pfd.init = bspwm_connect;
	pfd.deinit = bspwm_disconnect;
	pfd.handler = bspwm_handle;
	poll_add(&pfd);

	/* subscribe bspwm report */
	bspwm_send(SUBSCRIBE_REPORT, LENGTH(SUBSCRIBE_REPORT));
}

/**
 * bspwm_send() - send specified command to bspwm.
 * @bspwm: bspwm.
 * @cmd: bspwm command.
 * @len: length of cmd.
 *
 * Return: sent bytes length.
 */
int
bspwm_send(const char *cmd, size_t len)
{
	return send(pfd.fd, cmd, len, 0);
}

/**
 * bspwm_desktop_state_parse() - parse char to bspwm desktop state.
 * @s: desktop state char.
 *
 * Retrun: DesktopState
 * 'o'         - STATE_OCCUPIED
 * 'u'         - STATE_URGENT
 * 'F','U','O' - STATE_ACTIVE
 * not match   - STATE_FREE
 */
bspwm_desktop_state_t
bspwm_desktop_state_parse(char s)
{
	bspwm_desktop_state_t state = BSPWM_DESKTOP_FREE;
	if ((s | 0x20) == 'o')
		state = BSPWM_DESKTOP_OCCUPIED;
	if ((s | 0x20) == 'u')
		state = BSPWM_DESKTOP_URGENT;
	if (s == 'F' || s == 'U' || s == 'O')
		return state | BSPWM_DESKTOP_FOCUSED;
	return state;
}

void
bspwm_parse(const char *report)
{
	int i, j;
	int len = strlen(report);
	char tok;
	list_head *cur;

	bspwm_monitor_t *curmon = NULL, *mon = NULL;
	bspwm_desktop_t *desktop = NULL, *desk = NULL;

	for (i = 0; i < len; i++) {
		switch (tok = report[i]) {
		case 'M':
		case 'm':
			curmon = NULL;
			for (j = ++i; j < len; j++)
				if (report[j] == ':')
					break;
			list_for_each(&bspwm->monitors, cur) {
				mon = list_entry(cur, bspwm_monitor_t, head);
				if (!strncmp(mon->name, &report[i], j - i)) {
					curmon = mon;
					break;
				}
			}
			if (!curmon) {
				curmon = calloc(1, sizeof(bspwm_monitor_t));
				list_add_tail(&bspwm->monitors, &curmon->head);
				curmon->name = strndup(&report[i], j - i);
				curmon->name[j - i] = '\0';
				list_head_init(&curmon->desktops);
			}
			curmon->is_active = (tok == 'M');
			i = j;
			break;
		case 'o':
		case 'O':
		case 'f':
		case 'F':
		case 'u':
		case 'U':
			desktop = NULL;
			for (j = ++i; j < len; j++)
				if (report[j] == ':')
					break;
			list_for_each(&curmon->desktops, cur) {
				desk = list_entry(cur, bspwm_desktop_t, head);
				if (!strncmp(desk->name, &report[i], j - i)) {
					desktop = desk;
					break;
				}
			}
			if (!desktop) {
				desktop = calloc(1, sizeof(bspwm_desktop_t));
				list_add_tail(&curmon->desktops, &desktop->head);
				desktop->name = strndup(&report[i], j - i);
				desktop->name[j - i] = '\0';
			}
			desktop->state = bspwm_desktop_state_parse(tok);
			i = j;
			break;
		case 'L':
		case 'T':
			i++; /* skip next char. */
			break;
		case 'G':
			/* skip current node flags. */
			while (report[i + 1] != ':' && report[i + 1] != '\n')
				i++;
			break;
		}
	}
}

/**
 * bspwm_handle() - bspwm event handling function.
 * @fd: a file descriptor for bspwm socket.
 *
 * This function expects call after bspwm_connect().
 * Read and parse bspwm report from fd.
 *
 * Return: poll_result_t
 *
 * success and not need more action - PR_NOOP
 * success and need rerendering     - PR_UPDATE
 * failed to read from fd           - PR_FAILED
 */
poll_result_t
bspwm_handle(int fd)
{
	ssize_t len;

	if ((len = recv(fd, buf, sizeof(buf) - 1, 0)) > 0) {
		buf[len] = '\0';
		if (buf[0] == '\x07') {
			err("bspwm: %s", buf + 1);
			return PR_FAILED;
		}
		bspwm_parse(buf);
		return PR_UPDATE;
	}
	return PR_FAILED;
}

bspwm_desktop_state_t
bspwm_desktop_state(bspwm_desktop_t *desktop)
{
	return desktop->state;
}

void
draw_desktop(draw_context_t *dc, bspwm_desktop_t *desktop, module_desktop_t *opts)
{
	const char *ws;
	bspwm_desktop_state_t state = bspwm_desktop_state(desktop);

	color_t *col;
	static color_t *fg = NULL, *fg_free = NULL;
	if (!fg)
		fg = opts->fg ? color_load(opts->fg) : color_default_fg();
	if (!fg_free)
		fg_free = opts->fg_free ? color_load(opts->fg_free) : color_default_fg();

	ws = (state & BSPWM_DESKTOP_FOCUSED) ? opts->focused : opts->unfocused;
	col = (state == BSPWM_DESKTOP_FREE) ? fg_free : fg;

	draw_color_text(dc, col, ws);
}

/**
 * desktops() - render bspwm desktop states.
 * @dc: draw context.
 * @opts: module options.
 */
void
desktops(draw_context_t *dc, module_option_t *opts)
{
	const char *name = draw_context_monitor_name(dc);
	bspwm_monitor_t *mon = NULL;
	bspwm_desktop_t *desktop;
	list_head *cur;

	if (!bspwm)
		bspwm_init();

	list_for_each(&bspwm->monitors, cur) {
		mon = list_entry(cur, bspwm_monitor_t, head);
		if (!strncmp(mon->name, name, strlen(name)))
			break;
	}
	if (!mon)
		return;

	list_for_each(&mon->desktops, cur) {
		desktop = list_entry(cur, bspwm_desktop_t, head);
		draw_desktop(dc, desktop, &opts->desk);
		if (&mon->desktops != cur->next)
			draw_padding_em(dc, 1);
	}
}
