mowc

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/mowc
Log | Files | Refs

commit c464cf5b33932465447b047ab2bba046af0131b5
parent 95438ce8dc56e4c4c1f3abd82d536e040bd799ce
Author: Nihal Jere <nihal@nihaljere.xyz>
Date:   Sun, 27 Jun 2021 14:52:39 -0500

overlay: initial

Diffstat:
Aclients/overlay.c | 631+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 631 insertions(+), 0 deletions(-)

diff --git a/clients/overlay.c b/clients/overlay.c @@ -0,0 +1,631 @@ +#include <stdbool.h> +#include <stddef.h> +#include <alsa/asoundlib.h> +#include <alsa/control.h> +#include <stdio.h> +#include <wld/wayland.h> +#include <wld/wld.h> +#include <wayland-client.h> +#include <xdg-shell-client-protocol.h> + +#include "drw.h" +#include "util.h" + +#define LENGTH(a) (sizeof((a)) / sizeof((a)[0])) + +snd_ctl_t *ctl; + +enum { + SchemeNormal, + SchemeOn, + SchemeLast, +}; + +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNormal] = { "#ffffff", "#000000" }, + [SchemeOn] = { "#00ff00", "#000000" }, +}; + +static Clr* scheme[SchemeLast]; + +struct { + struct wl_display *dpy; + struct wl_compositor *cmp; + struct wl_seat *seat; + struct wl_touch *touch; + struct wl_surface *surface; + struct xdg_wm_base *wm; + struct xdg_surface *xdgsurface; + struct xdg_toplevel *toplevel; + Drw *drw; + + int32_t w, h; +} wl; + +enum item { + ToggleMic, + ToggleSpk, + ToggleEar, + ToggleHPOut, + ToggleHPIn, + ItemNone +} pressed, current; + +struct text_data { + const char *text; + unsigned int tw, th; +} text_data[] = { + [ToggleMic] = { "Mic" }, + [ToggleSpk] = { "Spk" }, + [ToggleEar] = { "Ear" }, + [ToggleHPOut] = { "HPOut" }, + [ToggleHPIn] = { "HPIn" }, +}; + +struct toggle { + struct text_data *td; + bool on; +} toggles[] = { + [ToggleMic] = { &text_data[ToggleMic] }, + [ToggleSpk] = { &text_data[ToggleSpk] }, + [ToggleEar] = { &text_data[ToggleEar] }, + [ToggleHPOut] = { &text_data[ToggleHPOut] }, + [ToggleHPIn] = { &text_data[ToggleHPIn] }, +}; + +enum { + MIC1_BOOST_VOLUME, + MIC2_BOOST_VOLUME, + MIC1_CAPTURE_SWITCH, + MIC2_CAPTURE_SWITCH, + LINE_IN_CAPTURE_SWITCH, + MIXER_CAPTURE_SWITCH, + MIXER_REVERSED_CAPTURE_SWITCH, + ADC_GAIN_CAPTURE_VOLUME, + ADC_CAPTURE_VOLUME, + AIF1_DATA_DIGITAL_ADC_CAPTURE_SWITCH, + AIF1_SLOT_0_DIGITAL_ADC_CAPTURE_SWITCH, + AIF2_DIGITAL_ADC_CAPTURE_SWITCH, + AIF2_INV_DIGITAL_ADC_CAPTURE_SWITCH, + AIF1_AD0_CAPTURE_VOLUME, + AIF1_AD0_STEREO_CAPTURE_ROUTE, + AIF1_DA0_PLAYBACK_VOLUME, + AIF1_DA0_STEREO_PLAYBACK_ROUTE, + AIF2_ADC_MIXER_ADC_CAPTURE_SWITCH, + AIF2_ADC_MIXER_AIF1_DA0_CAPTURE_SWITCH, + AIF2_ADC_MIXER_AIF2_DAC_REV_CAPTURE_SWITCH, + AIF2_ADC_CAPTURE_VOLUME, + AIF2_DAC_PLAYBACK_VOLUME, + AIF2_ADC_STEREO_CAPTURE_ROUTE, + AIF2_DAC_STEREO_PLAYBACK_ROUTE, + AIF3_ADC_SOURCE_CAPTURE_ROUTE, + AIF2_DAC_SOURCE_PLAYBACK_ROUTE, + ADC_DIGITAL_DAC_PLAYBACK_SWITCH, + AIF1_SLOT_0_DIGITAL_DAC_PLAYBACK_SWITCH, + AIF2_DIGITAL_DAC_PLAYBACK_SWITCH, + DAC_PLAYBACK_SWITCH, + DAC_REVERSED_PLAYBACK_SWITCH, + DAC_PLAYBACK_VOLUME, + MIC1_PLAYBACK_SWITCH, + MIC1_PLAYBACK_VOLUME, + MIC2_PLAYBACK_SWITCH, + MIC2_PLAYBACK_VOLUME, + LINE_IN_PLAYBACK_SWITCH, + LINE_IN_PLAYBACK_VOLUME, + EARPIECE_SOURCE_PLAYBACK_ROUTE, + EARPIECE_PLAYBACK_SWITCH, + EARPIECE_PLAYBACK_VOLUME, + HEADPHONE_SOURCE_PLAYBACK_ROUTE, + HEADPHONE_PLAYBACK_SWITCH, + HEADPHONE_PLAYBACK_VOLUME, + LINE_OUT_SOURCE_PLAYBACK_ROUTE, + LINE_OUT_PLAYBACK_SWITCH, + LINE_OUT_PLAYBACK_VOLUME, +}; + +struct audio_control_state { + char name[128]; + /* default values */ + union { + int64_t i[4]; + const char* e[4]; + } vals; + snd_ctl_elem_info_t *info; + snd_ctl_elem_value_t *value; + bool used; +}; + + +struct audio_control_state controls[] = { + // + // Analog input: + // + + // Mic 1 (daughterboard) + [MIC1_BOOST_VOLUME] = { .name = "Mic1 Boost Volume", .vals.i = { 1 } }, + + // Mic 2 (headphones) + [MIC2_BOOST_VOLUME] = { .name = "Mic2 Boost Volume", .vals.i = { 1 } }, + + // Line in (unused on PP) + // no controls yet + + // Input mixers before ADC + + [MIC1_CAPTURE_SWITCH] = { .name = "Mic1 Capture Switch", .vals.i = { false, false } }, + [MIC2_CAPTURE_SWITCH] = { .name = "Mic2 Capture Switch", .vals.i = { false, false } }, + [LINE_IN_CAPTURE_SWITCH] = { .name = "Line In Capture Switch", .vals.i = { 0, 0 } }, // Out Mix -> In Mix + [MIXER_CAPTURE_SWITCH] = { .name = "Mixer Capture Switch", .vals.i = { 0, 0 } }, + [MIXER_REVERSED_CAPTURE_SWITCH] = { .name = "Mixer Reversed Capture Switch", .vals.i = { 0, 0 } }, + + // ADC + [ADC_GAIN_CAPTURE_VOLUME] = { .name = "ADC Gain Capture Volume", .vals.i = { 0 } }, + [ADC_CAPTURE_VOLUME] = { .name = "ADC Capture Volume", .vals.i = { 160, 160 } }, // digital gain + + // + // Digital paths: + // + + // AIF1 (SoC) + + // AIF1 slot0 capture mixer sources + [AIF1_DATA_DIGITAL_ADC_CAPTURE_SWITCH] = { .name = "AIF1 Data Digital ADC Capture Switch", .vals.i = { 1, 0 } }, + [AIF1_SLOT_0_DIGITAL_ADC_CAPTURE_SWITCH] = { .name = "AIF1 Slot 0 Digital ADC Capture Switch", .vals.i = { 0, 0 } }, + [AIF2_DIGITAL_ADC_CAPTURE_SWITCH] = { .name = "AIF2 Digital ADC Capture Switch", .vals.i = { 0, 1 } }, + [AIF2_INV_DIGITAL_ADC_CAPTURE_SWITCH] = { .name = "AIF2 Inv Digital ADC Capture Switch", .vals.i = { 0, 0 } }, //XXX: capture right from the left AIF2? + + // AIF1 slot0 capture/playback mono mixing/digital volume + [AIF1_AD0_CAPTURE_VOLUME] = { .name = "AIF1 AD0 Capture Volume", .vals.i = { 160, 160 } }, + [AIF1_AD0_STEREO_CAPTURE_ROUTE] = { .name = "AIF1 AD0 Stereo Capture Route", .vals.e = { "Stereo", "Stereo" } }, + [AIF1_DA0_PLAYBACK_VOLUME] = { .name = "AIF1 DA0 Playback Volume", .vals.i = { 160, 160 } }, + [AIF1_DA0_STEREO_PLAYBACK_ROUTE] = { .name = "AIF1 DA0 Stereo Playback Route", .vals.e = { "Stereo", "Stereo" } }, + + // AIF2 (modem) + + // AIF2 capture mixer sources + [AIF2_ADC_MIXER_ADC_CAPTURE_SWITCH] = { .name = "AIF2 ADC Mixer ADC Capture Switch", .vals.i = { 0, 0 } }, // from adc/mic + [AIF2_ADC_MIXER_AIF1_DA0_CAPTURE_SWITCH] = { .name = "AIF2 ADC Mixer AIF1 DA0 Capture Switch", .vals.i = { 0, 1 } }, // from aif1 R + [AIF2_ADC_MIXER_AIF2_DAC_REV_CAPTURE_SWITCH] = { .name = "AIF2 ADC Mixer AIF2 DAC Rev Capture Switch", .vals.i = { 0, 0 } }, + + // AIF2 capture/playback mono mixing/digital volume + [AIF2_ADC_CAPTURE_VOLUME] = { .name = "AIF2 ADC Capture Volume", .vals.i = { 160, 160 } }, + [AIF2_DAC_PLAYBACK_VOLUME] = { .name = "AIF2 DAC Playback Volume", .vals.i = { 160, 160 } }, + [AIF2_ADC_STEREO_CAPTURE_ROUTE] = { .name = "AIF2 ADC Stereo Capture Route", .vals.e = { "Mix Mono", "Mix Mono" } }, // we mix because we're sending two channels (from mic and AIF1 R) + [AIF2_DAC_STEREO_PLAYBACK_ROUTE] = { .name = "AIF2 DAC Stereo Playback Route", .vals.e = { "Sum Mono", "Sum Mono" } }, // we sum because modem is sending a single channel + + // AIF3 (bluetooth) + + [AIF3_ADC_SOURCE_CAPTURE_ROUTE] = { .name = "AIF3 ADC Source Capture Route", .vals.e = { "None" } }, + [AIF2_DAC_SOURCE_PLAYBACK_ROUTE] = { .name = "AIF2 DAC Source Playback Route", .vals.e = { "AIF2" } }, + + // DAC + + // DAC input mixers (sources from ADC, and AIF1/2) + [ADC_DIGITAL_DAC_PLAYBACK_SWITCH] = { .name = "ADC Digital DAC Playback Switch", .vals.i = { 0, 0 } }, // we don't play our mic to ourselves + [AIF1_SLOT_0_DIGITAL_DAC_PLAYBACK_SWITCH] = { .name = "AIF1 Slot 0 Digital DAC Playback Switch", .vals.i = { 1, 0 } }, + [AIF2_DIGITAL_DAC_PLAYBACK_SWITCH] = { .name = "AIF2 Digital DAC Playback Switch", .vals.i = { 0, 0 } }, + + // + // Analog output: + // + + // Output mixer after DAC + + [DAC_PLAYBACK_SWITCH] = { .name = "DAC Playback Switch", .vals.i = { 1, 1 } }, + [DAC_REVERSED_PLAYBACK_SWITCH] = { .name = "DAC Reversed Playback Switch", .vals.i = { 1, 1 } }, + [DAC_PLAYBACK_VOLUME] = { .name = "DAC Playback Volume", .vals.i = { 160, 160 } }, + [MIC1_PLAYBACK_SWITCH] = { .name = "Mic1 Playback Switch", .vals.i = { 0, 0 } }, + [MIC1_PLAYBACK_VOLUME] = { .name = "Mic1 Playback Volume", .vals.i = { 0 } }, + [MIC2_PLAYBACK_SWITCH] = { .name = "Mic2 Playback Switch", .vals.i = { 0, 0 } }, + [MIC2_PLAYBACK_VOLUME] = { .name = "Mic2 Playback Volume", .vals.i = { 0 } }, + [LINE_IN_PLAYBACK_SWITCH] = { .name = "Line In Playback Switch", .vals.i = { 0, 0 } }, + [LINE_IN_PLAYBACK_VOLUME] = { .name = "Line In Playback Volume", .vals.i = { 0 } }, + + // Outputs + + [EARPIECE_SOURCE_PLAYBACK_ROUTE] = { .name = "Earpiece Source Playback Route", .vals.e = { "Left Mixer" } }, + [EARPIECE_PLAYBACK_SWITCH] = { .name = "Earpiece Playback Switch", .vals.i = { 0 } }, + [EARPIECE_PLAYBACK_VOLUME] = { .name = "Earpiece Playback Volume", .vals.i = { 0 } }, + + [HEADPHONE_SOURCE_PLAYBACK_ROUTE] = { .name = "Headphone Source Playback Route", .vals.e = { "Mixer", "Mixer" } }, + [HEADPHONE_PLAYBACK_SWITCH] = { .name = "Headphone Playback Switch", .vals.i = { 0, 0 } }, + [HEADPHONE_PLAYBACK_VOLUME] = { .name = "Headphone Playback Volume", .vals.i = { 0 } }, + + // Loudspeaker + [LINE_OUT_SOURCE_PLAYBACK_ROUTE] = { .name = "Line Out Source Playback Route", .vals.e = { "Mono Differential", "Mono Differential" } }, + [LINE_OUT_PLAYBACK_SWITCH] = { .name = "Line Out Playback Switch", .vals.i = { 0, 0 } }, + [LINE_OUT_PLAYBACK_VOLUME] = { .name = "Line Out Playback Volume", .vals.i = { 0 } }, +}; + +#include "config.h" + +void wmping(void *data, struct xdg_wm_base *wm, uint32_t serial); +void xdgsurfconfigure(void *data, struct xdg_surface *surf, uint32_t serial); +void toplevelconfigure(void *data, struct xdg_toplevel *toplevel, int32_t w, int32_t h, struct wl_array *states); +void toplevelclose(void *data, struct xdg_toplevel *toplevel); +void regglobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); +void regglobalremove(void *data, struct wl_registry *registry, uint32_t name); +void draw(void); +void touchdown(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y); +void touchup(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id); +void touchmotion(void *data, struct wl_touch *wl_touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y); +void touchframe(void *data, struct wl_touch *wl_touch); +void touchcancel(void *data, struct wl_touch *wl_touch); + +void toggle_control(int control, enum item toggle); + +static struct wl_registry_listener reglistener = { regglobal, regglobalremove }; + +static struct xdg_wm_base_listener wmlistener = { wmping }; +static struct xdg_surface_listener xdgsurflistener = { xdgsurfconfigure }; +static struct xdg_toplevel_listener toplevellistener = + { toplevelconfigure, toplevelclose }; +static struct wl_touch_listener touchlistener = + { touchdown, touchup, touchmotion, touchframe, touchcancel }; + +void +wmping(void *data, struct xdg_wm_base *wm, uint32_t serial) +{ + xdg_wm_base_pong(wm, serial); +} + +void +xdgsurfconfigure(void *data, struct xdg_surface *surf, uint32_t serial) +{ + xdg_surface_ack_configure(surf, serial); +} + +void +toplevelconfigure(void *data, struct xdg_toplevel *toplevel, int32_t w, int32_t h, + struct wl_array *states) +{ + if (w == wl.w && h == wl.h) + return; + if (w == 0 && h == 0) + return; + + wl.w = w; + wl.h = h; +} + +void +toplevelclose(void *data, struct xdg_toplevel *toplevel) +{ + exit(0); +} + +void +regglobal(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) +{ + if (strcmp(interface, "wl_compositor") == 0) { + wl.cmp = wl_registry_bind(registry, name, + &wl_compositor_interface, 3); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + wl.wm = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(wl.wm, &wmlistener, NULL); + } else if (strcmp(interface, "wl_seat") == 0) { + wl.seat = wl_registry_bind(registry, name, + &wl_seat_interface, 4); + } +} + +void +regglobalremove(void *data, struct wl_registry *registry, uint32_t name) +{ +} + +struct { + bool down; + uint32_t x, y; + int32_t id; +} touchpoint; + +static enum item +getpressed() +{ + int height = wl.h / 8; + int width = wl.w / 2; + int idx = 2 * (touchpoint.y / height) + touchpoint.x / width; + + /* ItemNone is after last toggle */ + if (idx < ItemNone) + return idx; + + return ItemNone; +} + +void +touchdown(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y) +{ + if (touchpoint.down) + return; + + touchpoint.down = true; + touchpoint.x = wl_fixed_to_int(x); + touchpoint.y = wl_fixed_to_int(y); + touchpoint.id = id; + current = pressed = getpressed(); + draw(); +} + +void +touchup(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id) +{ + if (touchpoint.id != id) + return; + + touchpoint.down = false; + + if (pressed == current) { + switch (pressed) { + case ToggleMic: + toggle_control(MIC1_CAPTURE_SWITCH, ToggleMic); + fprintf(stderr, "toggle mic\n"); + break; + case ToggleSpk: + toggle_control(LINE_OUT_PLAYBACK_SWITCH, ToggleSpk); + fprintf(stderr, "toggle spk\n"); + break; + case ToggleEar: + toggle_control(EARPIECE_PLAYBACK_SWITCH, ToggleEar); + fprintf(stderr, "toggle ear\n"); + break; + case ToggleHPOut: + toggle_control(HEADPHONE_PLAYBACK_SWITCH, ToggleHPOut); + fprintf(stderr, "toggle hpout\n"); + break; + case ToggleHPIn: + toggle_control(MIC2_CAPTURE_SWITCH, ToggleHPIn); + fprintf(stderr, "toggle hpin\n"); + break; + } + } + + current = pressed = ItemNone; + draw(); +} + +void +touchmotion(void *data, struct wl_touch *wl_touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) +{ + if (touchpoint.id != id) + return; + + touchpoint.x = wl_fixed_to_int(x); + touchpoint.y = wl_fixed_to_int(y); + current = getpressed(); + draw(); +} + +void +touchframe(void *data, struct wl_touch *wl_touch) +{ +} + +void touchcancel(void *data, struct wl_touch *wl_touch) +{ + touchpoint.down = false; +} + +void +toggle_control(int control, enum item toggle) +{ + bool v = !snd_ctl_elem_value_get_boolean(controls[control].value, 0); + + snd_ctl_elem_value_set_boolean(controls[control].value, 0, v); + snd_ctl_elem_value_set_boolean(controls[control].value, 0, v); + + snd_ctl_elem_write(ctl, controls[control].value); + toggles[toggle].on = v; +} + +unsigned int +val_from_enum_string(snd_ctl_t *ctl, snd_ctl_elem_info_t *oldinfo, const char *string) +{ + snd_ctl_elem_info_t *info; + unsigned int count, ret = -1; + + if (string == NULL) + return -1; + if (snd_ctl_elem_info_malloc(&info) != 0) + return -1; + + snd_ctl_elem_info_copy(info, oldinfo); + + count = snd_ctl_elem_info_get_items(info); + for (int i = 0; i < count; ++i) { + snd_ctl_elem_info_set_item(info, i); + snd_ctl_elem_info(ctl, info); + if (strcmp(string, snd_ctl_elem_info_get_item_name(info)) == 0) { + ret = i; + break; + } + } + + snd_ctl_elem_info_free(info); + return ret; +} + + +void +init_alsa() +{ + int idx, count, err; + snd_ctl_elem_list_t* list; + snd_ctl_elem_info_t* info; + snd_ctl_elem_value_t* value; + char buf[128]; + + while (1) { + if ((err = snd_card_next(&idx)) < 0) { + fprintf(stderr, "snd_card_next: %s\n", snd_strerror(err)); + } + + if (idx < 0) + break; + + sprintf(buf, "hw:CARD=%d", idx); + + if (snd_ctl_open(&ctl, buf, SND_CTL_NONBLOCK) < 0) { + die("failed to open control socket\n"); + } + + snd_ctl_elem_list_alloca(&list); + + snd_ctl_elem_list(ctl, list); + count = snd_ctl_elem_list_get_count(list); + + snd_ctl_elem_list_alloc_space(list, count); + snd_ctl_elem_list(ctl, list); + + for (int i = 0; i < count; i++) { + + /* load info/value for all needed controls */ + for (int j = 0; j < LENGTH(controls); j++) { + if (strcmp(snd_ctl_elem_list_get_name(list, i), controls[j].name) != 0) + continue; + + snd_ctl_elem_info_malloc(&info); + snd_ctl_elem_value_malloc(&value); + + snd_ctl_elem_info_set_numid(info, snd_ctl_elem_list_get_numid(list, i)); + snd_ctl_elem_value_set_numid(value, snd_ctl_elem_list_get_numid(list, i)); + snd_ctl_elem_info(ctl, info); + snd_ctl_elem_read(ctl, value); + + controls[j].info = info; + controls[j].value = value; + + /* set default values */ + for (int k = 0; k < 4; k++) { + switch (snd_ctl_elem_info_get_type(info)) { + case SND_CTL_ELEM_TYPE_ENUMERATED: { + unsigned int val = val_from_enum_string(ctl, info, controls[j].vals.e[k]); + if (val == -1) + continue; + + snd_ctl_elem_value_set_enumerated(value, k, controls[j].vals.i[k]); + break; + } + + case SND_CTL_ELEM_TYPE_BOOLEAN: + snd_ctl_elem_value_set_integer(value, k, controls[j].vals.i[k]); + break; + case SND_CTL_ELEM_TYPE_INTEGER: + snd_ctl_elem_value_set_integer(value, k, controls[j].vals.i[k]); + break; + } + } + + snd_ctl_elem_write(ctl, value); + } + } + + snd_ctl_elem_list_free_space(list); + } +} + +void +draw() +{ + int height = wl.h / 8; + int width = wl.w / 2; + int x; + unsigned int tx, ty; + wld_set_target_surface(wl.drw->renderer, wl.drw->surface); + + drw_setscheme(wl.drw, scheme[SchemeNormal]); + drw_rect(wl.drw, 0, 0, wl.w, wl.h, 1, 1); + + for (int i = 0; i < ItemNone; ++i) { + x = (i % 2) * width; + tx = x + width / 2 - text_data[i].tw / 2; + ty = height * (i / 2) + height / 2 - text_data[i].th / 2; + drw_setscheme(wl.drw, scheme[toggles[i].on ? SchemeOn : SchemeNormal]); + drw_rect(wl.drw, x, height * (i / 2), width, height, 1, 0); + drw_rect(wl.drw, x, height * (i / 2), width, height, 0, 1); + drw_text(wl.drw, tx, ty, text_data[i].tw, text_data[i].th, 0, text_data[i].text, 1); + } + + drw_map(wl.drw, wl.surface, 0, 0, wl.w, wl.h); +} + +void +wlinit(void) +{ + struct wl_registry *registry; + size_t len; + int j; + + wl.w = wl.h = 500; + + if (!(wl.dpy = wl_display_connect(NULL))) + die("Can't open display\n"); + + registry = wl_display_get_registry(wl.dpy); + wl_registry_add_listener(registry, &reglistener, NULL); + + wl_display_roundtrip(wl.dpy); + + wl.drw = drw_create(wl.dpy); + + if (!wl.seat) + die("Display has no seat\n"); + if (!wl.wm) + die("Display has no window manager\n"); + + if (!drw_fontset_create(wl.drw, (const char **) fonts, LENGTH(fonts))) + die("no fonts could be loaded"); + + wl.touch = wl_seat_get_touch(wl.seat); + wl_touch_add_listener(wl.touch, &touchlistener, NULL); + + wl_display_roundtrip(wl.dpy); + + if (!wl.touch) + die("seat has no touch support\n"); + + for (j = 0; j < ItemNone; ++j) { + len = strlen(text_data[j].text); + drw_font_getexts(&wl.drw->fonts[0], text_data[j].text, len, &text_data[j].tw, &text_data[j].th); + } + + /* init appearance */ + for (j = 0; j < SchemeLast; j++) + scheme[j] = drw_scm_create(wl.drw, (const char **) colors[j], 2); + + wl.surface = wl_compositor_create_surface(wl.cmp); + wl.xdgsurface = xdg_wm_base_get_xdg_surface(wl.wm, wl.surface); + xdg_surface_add_listener(wl.xdgsurface, &xdgsurflistener, NULL); + wl.toplevel = xdg_surface_get_toplevel(wl.xdgsurface); + xdg_toplevel_add_listener(wl.toplevel, &toplevellistener, NULL); + xdg_toplevel_set_app_id(wl.toplevel, "overlay"); + + wl_display_roundtrip(wl.dpy); + + if (wl.w == 0 || wl.h == 0) { + die("window has 0 dimension\n"); + } + + drw_resize(wl.drw, wl.surface, wl.w, wl.h); + draw(); +} + +int +main() +{ + init_alsa(); + + toggles[ToggleMic].on = controls[MIC1_CAPTURE_SWITCH].vals.i; + toggles[ToggleSpk].on = controls[LINE_OUT_PLAYBACK_SWITCH].vals.i; + toggles[ToggleEar].on = controls[EARPIECE_PLAYBACK_SWITCH].vals.i; + toggles[ToggleHPIn].on = controls[MIC2_CAPTURE_SWITCH].vals.i; + toggles[ToggleHPOut].on = controls[HEADPHONE_PLAYBACK_SWITCH].vals.i; + wlinit(); + + while (wl_display_dispatch(wl.dpy) != -1); + + snd_ctl_close(ctl); +}