commit c464cf5b33932465447b047ab2bba046af0131b5
parent 95438ce8dc56e4c4c1f3abd82d536e040bd799ce
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Sun, 27 Jun 2021 14:52:39 -0500
overlay: initial
Diffstat:
A | clients/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, ®listener, 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);
+}