master ohmu/fastfetch-openbsd / src / detection / sound / sound_bsd.c
ohmu  ·  2026-10-15
 1#include "sound.h"
 2#include "common/debug.h"
 3#include "common/io.h"
 4#include "common/sysctl.h"
 5
 6#include <fcntl.h>
 7#include <sys/soundcard.h>
 8#include <unistd.h>
 9
10const char* ffDetectSound(FFSoundOptions* options, FFlist* devices) {
11#ifndef __NetBSD__
12    int defaultDev = ffSysctlGetInt("hw.snd.default_unit", -1);
13    if (defaultDev == -1) {
14        FF_DEBUG("sysctl(hw.snd.default_unit) failed");
15        return "sysctl(hw.snd.default_unit) failed";
16    }
17#else
18    int defaultDev;
19    {
20        char mixerp[12];
21        ssize_t plen = readlink("/dev/mixer", mixerp, ARRAY_SIZE(mixerp));
22        if (plen < 6) {
23            FF_DEBUG("readlink(/dev/mixer) failed: %s", strerror(errno));
24            return "readlink(/dev/mixer) failed";
25        }
26        defaultDev = mixerp[plen - 1] - '0';
27        if (defaultDev < 0 || defaultDev > 9) {
28            FF_DEBUG("Invalid mixer device");
29            return "Invalid mixer device";
30        }
31    }
32#endif
33
34    char path[] = "/dev/mixer0";
35
36    struct oss_sysinfo info = { .nummixers = 9 };
37
38    for (int idev = 0; idev <= info.nummixers; ++idev) {
39        bool isMain = idev == defaultDev;
40        if ((options->soundType & FF_SOUND_TYPE_MAIN) && !isMain) {
41            continue;
42        }
43
44        path[strlen("/dev/mixer")] = (char) ('0' + idev);
45        FF_AUTO_CLOSE_FD int fd = open(path, O_RDWR | O_CLOEXEC);
46        if (fd < 0) {
47            break;
48        }
49
50        if (idev == 0) {
51            if (ioctl(fd, SNDCTL_SYSINFO, &info) != 0) {
52                FF_DEBUG("ioctl(SNDCTL_SYSINFO) failed: %s", strerror(errno));
53                return "ioctl(SNDCTL_SYSINFO) failed";
54            }
55        }
56
57        uint32_t devmask = 0;
58        if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
59            continue;
60        }
61        if (!(devmask & SOUND_MASK_VOLUME)) {
62            continue;
63        }
64
65#if defined(SOUND_MIXER_MUTE) && (SOUND_MIXER_MUTE != SOUND_MIXER_NONE)
66    #define FF_SOUND_HAVE_MIXER_MUTE 1
67        uint32_t mutemask = 0;
68        ioctl(fd, SOUND_MIXER_READ_MUTE, &mutemask);
69#endif
70
71        struct oss_card_info ci = { .card = idev };
72        if (ioctl(fd, SNDCTL_CARDINFO, &ci) < 0) {
73            continue;
74        }
75
76        uint32_t volume;
77        if (ioctl(fd, SOUND_MIXER_READ_VOLUME, &volume) < 0) {
78            continue;
79        }
80
81        FFSoundDevice* device = FF_LIST_ADD(FFSoundDevice, *devices);
82        ffStrbufInitS(&device->identifier, path);
83        ffStrbufInitF(&device->name, "%s %s", ci.longname, ci.hw_info);
84        ffStrbufTrimRightSpace(&device->name);
85        ffStrbufInitF(&device->platformApi, "%s %s", info.product, info.version);
86        device->volume =
87#ifdef FF_SOUND_HAVE_MIXER_MUTE
88            mutemask & SOUND_MASK_VOLUME ? 0 :
89#endif
90                                         ((uint8_t) volume /*left*/ + (uint8_t) (volume >> 8) /*right*/) / 2;
91        device->type = FF_SOUND_TYPE_ACTIVE | (isMain ? FF_SOUND_TYPE_MAIN : FF_SOUND_TYPE_NONE);
92    }
93
94    return nullptr;
95}