master ohmu/fastfetch-openbsd / src / detection / sound / sound_obsd.c
ohmu  ·  2026-10-15
 1#include "sound.h"
 2#include "common/debug.h"
 3#include "common/strutil.h"
 4
 5#include <fcntl.h>
 6#include <sndio.h>
 7
 8static void close_hdl(struct sioctl_hdl** phdl) {
 9    assert(phdl);
10    if (*phdl) {
11        sioctl_close(*phdl);
12    }
13}
14
15enum { MAX_CHANNEL_NUM = 8 };
16
17typedef struct FFSoundDeviceBundle {
18    char name[SIOCTL_DISPLAYMAX];
19    double level[MAX_CHANNEL_NUM];
20    uint8_t iLevel;
21    bool mute[MAX_CHANNEL_NUM];
22    uint8_t iMute;
23} FFSoundDeviceBundle;
24
25static void enumerate_props(FFSoundDeviceBundle* bundle, struct sioctl_desc* desc, int val) {
26    if (!desc) {
27        return;
28    }
29
30    if (desc->type == SIOCTL_SEL) {
31        if (desc->display[0] != '\0' && ffStrEquals(desc->node0.name, "server")) {
32            ffStrCopy(bundle->name, desc->display, SIOCTL_DISPLAYMAX);
33        }
34        return;
35    }
36
37    if (desc->type != SIOCTL_NUM && desc->type != SIOCTL_SW) {
38        return;
39    }
40
41    if (!ffStrEquals(desc->node0.name, "output")) {
42        return;
43    }
44
45    if (ffStrEquals(desc->func, "level")) {
46        if (__builtin_expect(bundle->iLevel == MAX_CHANNEL_NUM, false)) {
47            return;
48        }
49        bundle->level[bundle->iLevel] = (double) val / (double) desc->maxval;
50        ++bundle->iLevel;
51    } else if (ffStrEquals(desc->func, "mute")) {
52        if (__builtin_expect(bundle->iMute == MAX_CHANNEL_NUM, false)) {
53            return;
54        }
55        bundle->mute[bundle->iMute] = !!val;
56        ++bundle->iMute;
57    }
58}
59
60const char* ffDetectSound([[maybe_unused]] FFSoundOptions* options, FFlist* devices) {
61    [[gnu::cleanup(close_hdl)]] struct sioctl_hdl* hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
62    if (!hdl) {
63        FF_DEBUG("sio_open() failed");
64        return "sio_open() failed";
65    }
66
67    FFSoundDeviceBundle bundle = {};
68    if (sioctl_ondesc(hdl, (void*) enumerate_props, &bundle) == 0) {
69        FF_DEBUG("sioctl_ondesc() failed");
70        return "sioctl_ondesc() failed";
71    }
72
73    if (bundle.iLevel != bundle.iMute || bundle.iLevel == 0) {
74        FF_DEBUG("Unexpected sioctl_ondesc() result");
75        return "Unexpected sioctl_ondesc() result";
76    }
77
78    FFSoundDevice* device = FF_LIST_ADD(FFSoundDevice, *devices);
79    ffStrbufInitS(&device->name, bundle.name);
80    ffStrbufInitS(&device->identifier, SIO_DEVANY);
81    ffStrbufInitStatic(&device->platformApi, "sndio");
82    device->volume = 0;
83    device->type = FF_SOUND_TYPE_ACTIVE | FF_SOUND_TYPE_MAIN;
84
85    double totalLevel = 0;
86    for (uint8_t i = 0; i < bundle.iLevel; ++i) {
87        if (!bundle.mute[i]) {
88            totalLevel += bundle.level[i];
89        }
90    }
91    device->volume = (uint8_t) ((totalLevel * 100 + bundle.iLevel / 2) / bundle.iLevel);
92
93    return nullptr;
94}