You are not logged in.
Likely well after every other BSD has, and one little bite at a time.
He has already made his position on this issue clear.
In OpenBSD there is a strict requirement that base builds base.
So we cannot replace any base utility, unless the toolchain to build
it is in the base. Adding such a toolchain would take make build time
from 40 minutes to hours. I don't see how that would happen.
I remember being confused by halt very early on and switching to shutdown --poweroff now and eventually just poweroff. (Confusingly, according to the manual, shutdown --halt -h or shutdown -H -h will not power down!)
sudo isn't always easy, but this particular task isn't too difficult. You can give all members of a particular group privileges to run specific commands without a password. Then, you add your regular user account to that group. Generally, people use the existing sudo group for this purpose, although you can create a special group if you want, such as a shutdown group. For example, add this line to your /etc/sudoers file (remember to use visudo to edit!)
%sudo ALL=NOPASSWD: /sbin/shutdown,/sbin/poweroff,/sbin/rebootThen add yourself to the sudo group:
usermod -aG sudo your_usernameNow you can reboot without a password prompt:
sudo rebootThis is my preferred method, as a member of the nonsensical hair-shirt brigade! (I don't even use a display manager, just good old xinit.)
I recall having similar issues with this system a few years ago; specifically, search would not work. Unfortunately, I wasn't able to find a solution. There is a README file at the source repository which says you need to enable CGI in your local web server, so maybe that is worth a try.
I can't even imagine being responsible for a project of this magnitude. I watched as all these big issues came up, usrmerge, 64-bit time_t, and more, and I wondered how it would ever be completely finished. But you folks did it! Thank you so much for everything. After all these years, it still amazes me when I set up a new system, and everything just works.
I assume you folks discussing size differences are using the standard installer? I didn't really notice a huge difference between the sizes of Daedalus and Excalibur, but I install manually, and my systems are pretty minimal. My kid was laughing at me last night because I don't even have a graphical file manager!
Are you trying to invoke tcplay as a regular user, but don't have /sbin/ in your path? I just ran apt-file list tcplay, and it says the package installs the executable to /usr/sbin/tcplay.
Are you talking about too small text in the grub menu specifically, or just small text in general? Changing the console font size is pretty easy; you can do this with dpkg-reconfigure console-setup. I'm not sure it affects the grub menu, though. You actually got me curious about how to change the grub menu text, because mine is really small as well. A quick search turned up this askubuntu post, which goes into a lot of detail about how to use the GRUB_FONT setting in /etc/default/grub (along with grub-mkfont) to get bigger text in the menu. I may give it a try later if I get some free time.
I don't even have that folder on my Daedalus installs, just ~/.dbus/session-bus/
Oops, that's it, I just misspelled it.
If you switch to console without stopping the desktop, log in and then start a second desktop on :1, you'll get another file in ~/.dbus/session-bus with the same number as /var/lib/dbus/machine-id except with -1 at the end
That makes sense. I have a vague memory of doing something like that once.
on my system, the machine-id is defined in /var/lib/dbus/machine-id.
I have that file too, which would explain why I'm not seeing the accumulation of randomly named files.
The content of /etc/apt is changed: the item sources.list appears in "grey" I would say in inactive color.
The repository configuration has been moved to the file /etc/apt/sources.list.d/devuan.sources, which uses a new format. It contains basically the same information as the old sources.list, it's just broken out into separate lines with descriptive headings.
This is odd: although my Excalibur system is showing the same behavior that everyone everyone describes here, my Ceres system completely lacks the file at /etc/default/dbus. Nonetheless, dbus is definitely installed and running. I'm also missing /etc/machine-id.
Another puzzle: there are exactly two files in ~/.dbus/session-id/. The files have almost the exact same names, except that one ends in -0, and the other ends in -1. The -0 file gets overwritten on each reboot, but with the exact same name. The -1 file was created back on 8-24, and hasn't been touched since.
I don't recall S3 being problematic (unless you wanted 16-bit color, and had more than 16MB RAM, and were still running a VLB card from the '90s... but I digress) but 3DFX sure was tongue
I had a Savage3D, which was known for having hardware issues. The card seemed to have a tendency to overheat, even on Windows. It was also pretty new at the time, so Linux/XFree86 driver support may not have been entirely solid yet.
Did you have problems with sound and sound quality in 2000? Or it worked out of the box?
Wow, that's a great question! I actually don't even remember what sound card I was using at the time. I think I had basic functionality working. It's hard to remember anything about sound because I was so focused on video issues. The main thing I remember was trouble getting X11 to start on my S3 video card. My first attempt at Linux was with RedHat, but I just could not get it working. I ended up on Debian because I could actually get the desktop to appear, but only with twm at first.
Unless something has changed very recently, The ESR version of Firefox included with Debian/Devuan never "auto-updates" the way mainline Firefox does. It used to display a message that said updates were being "managed by your organization," but this confused people, so they removed it.
Putting a hold on the package with apt-mark means it will never be updated, not even manually. For an application exposed to the Internet such as a web browser, this can be very dangerous. You won't get patches even for serious vulnerabilities.
apt just invokes apt-get or apt-cache on the back-end. There's no difference in the underlying functionality. When I first started using Debian in 2000, apt didn't even exist yet, you had to run apt-get. apt was added (around 2014, I think) to provide a more convenient high-level interface for interactive use.
The main advantage of using apt-get or apt-cache directly is that you can access a wider variety of options, such as --no-recommends or --names-only. apt is fine to use when you don't need to do anything complicated. It formats output in color and pipes to a pager.
Well of course there is, i'm an idiot, lol, didn't scroll down far enough.
I probably added that screenshot after your post!
It wasn't there when I first uploaded.
Sorry I haven't checked back here in a while, I've been super busy. As for EFL, I considered it, but I was already familiar with the X Toolkit from working with the Athena widgets. I also kind of just wanted to see if it was still possible to use Motif for anything. Believe it or not, I also wrote an ALSA mixer for GNUStep! I was exploring a lot of obscure stuff.
greenjeans, I added some stuff to your code so it would update the UI on external mixer changes. I didn't do it for the EQ controls, as I'm not familiar with the ALSA EQ plugin. But I would guess the principle is probably similar. I called out the changes with comments. There are a couple of added functions, and an added section in main(). I think that's it.
#include <gtk/gtk.h>
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <string.h>
#include <glib/gstdio.h>
typedef struct {
snd_mixer_t *mixer;
snd_mixer_elem_t *elem;
GtkWidget *scale;
const char *channel_name;
} MixerChannel;
typedef struct {
snd_mixer_t *mixer;
MixerChannel *channels;
int num_channels;
} MixerData;
typedef struct {
snd_ctl_t *ctl;
snd_ctl_elem_value_t *val;
GtkWidget *scale;
const char *band_name;
} EQBand;
typedef struct {
snd_ctl_t *ctl;
EQBand *bands;
int num_bands;
} EQData;
/* ADDED SECTION: mixer event handling callbacks. */
static int elem_callback(snd_mixer_elem_t *elem, unsigned int mask) {
if (mask == SND_CTL_EVENT_MASK_REMOVE)
return 0;
long current, min, max;
snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, ¤t);
snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
double value = (double)current / (double)(max - min);
gtk_range_set_value(GTK_RANGE(snd_mixer_elem_get_callback_private(elem)),
value);
return 0;
}
static gboolean fd_dispatch(G_GNUC_UNUSED GSource *source,
G_GNUC_UNUSED GSourceFunc callback,
gpointer user_data) {
snd_mixer_handle_events(user_data);
return G_SOURCE_CONTINUE;
}
/* END OF ADDED SECTION */
// Callback to save preset to ~/.local/share/mxeq/presets.csv
static void save_preset(GtkWidget *button, gpointer user_data) {
EQData *eq_data = (EQData *)user_data;
GtkWidget *entry = g_object_get_data(G_OBJECT(button), "preset_entry");
GtkWidget *combo = g_object_get_data(G_OBJECT(button), "preset_combo");
GtkWidget *window = g_object_get_data(G_OBJECT(button), "window");
const char *preset_name = gtk_entry_get_text(GTK_ENTRY(entry));
if (!preset_name || strlen(preset_name) == 0) {
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
"Please enter a preset name.");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return;
}
// Check for duplicate preset name
const char *home = g_get_home_dir();
char *preset_path = g_build_filename(home, ".local", "share", "mxeq", "presets.csv", NULL);
FILE *fp_check = fopen(preset_path, "r");
if (fp_check) {
char line[256];
while (fgets(line, sizeof(line), fp_check)) {
char *name = strtok(line, ":");
if (name && strcmp(name, preset_name) == 0) {
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
"Preset name already exists. Choose a different name.");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
fclose(fp_check);
g_free(preset_path);
return;
}
}
fclose(fp_check);
}
// Create directory if it doesn't exist
char *dir_path = g_build_filename(home, ".local", "share", "mxeq", NULL);
g_mkdir_with_parents(dir_path, 0755);
g_free(dir_path);
// Open file in append mode
FILE *fp = fopen(preset_path, "a");
if (!fp) {
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
"Failed to save preset: Could not open file.");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
g_free(preset_path);
return;
}
// Collect EQ values
GString *values = g_string_new("");
for (int i = 0; i < eq_data->num_bands; i++) {
gdouble value = gtk_range_get_value(GTK_RANGE(eq_data->bands[i].scale));
g_string_append_printf(values, "%.2f", value);
if (i < eq_data->num_bands - 1) {
g_string_append(values, ",");
}
}
// Write preset to file
fprintf(fp, "%s:%s\n", preset_name, values->str);
fclose(fp);
g_string_free(values, TRUE);
// Add to combo box and set as active
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), preset_name);
gtk_combo_box_set_active_id(GTK_COMBO_BOX(combo), preset_name);
// Show confirmation dialog
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
GTK_MESSAGE_INFO, GTK_BUTTONS_OK,
"Preset Saved!");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
// Clear entry
gtk_entry_set_text(GTK_ENTRY(entry), "");
g_free(preset_path);
}
// Callback to apply selected preset
static void apply_preset(GtkComboBox *combo, gpointer user_data) {
EQData *eq_data = (EQData *)user_data;
char *preset_name = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
if (!preset_name || g_strcmp0(preset_name, "Choose EQ Pre-set") == 0) {
g_free(preset_name);
return;
}
// Build preset file path
const char *home = g_get_home_dir();
char *preset_path = g_build_filename(home, ".local", "share", "mxeq", "presets.csv", NULL);
// Open preset file
FILE *fp = fopen(preset_path, "r");
if (!fp) {
fprintf(stderr, "Failed to open presets.csv\n");
g_free(preset_name);
g_free(preset_path);
return;
}
// Read file to find matching preset
char line[256];
while (fgets(line, sizeof(line), fp)) {
char *name = strtok(line, ":");
if (name && strcmp(name, preset_name) == 0) {
char *values = strtok(NULL, "\n");
if (values) {
char *token = strtok(values, ",");
int i = 0;
while (token && i < eq_data->num_bands) {
gdouble value = g_ascii_strtod(token, NULL);
gtk_range_set_value(GTK_RANGE(eq_data->bands[i].scale), value);
long alsa_value = (long)(value * 100.0);
snd_ctl_elem_value_set_integer(eq_data->bands[i].val, 0, alsa_value);
snd_ctl_elem_value_set_integer(eq_data->bands[i].val, 1, alsa_value);
snd_ctl_elem_write(eq_data->bands[i].ctl, eq_data->bands[i].val);
token = strtok(NULL, ",");
i++;
}
}
break;
}
}
fclose(fp);
g_free(preset_name);
g_free(preset_path);
}
// Load presets into combo box at startup
static void load_presets(GtkComboBoxText *combo) {
gtk_combo_box_text_append_text(combo, "Choose EQ Pre-set");
const char *home = g_get_home_dir();
char *preset_path = g_build_filename(home, ".local", "share", "mxeq", "presets.csv", NULL);
FILE *fp = fopen(preset_path, "r");
if (fp) {
char line[256];
while (fgets(line, sizeof(line), fp)) {
char *name = strtok(line, ":");
if (name) {
gtk_combo_box_text_append_text(combo, name);
}
}
fclose(fp);
}
gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); // Default to "Choose EQ Pre-set"
g_free(preset_path);
}
static void slider_changed(GtkRange *range, MixerChannel *channel) {
gdouble value = gtk_range_get_value(range);
long min, max;
snd_mixer_selem_get_playback_volume_range(channel->elem, &min, &max);
long alsa_value = (long)(value * (max - min) + min);
snd_mixer_selem_set_playback_volume_all(channel->elem, alsa_value);
}
static void eq_slider_changed(GtkRange *range, EQBand *band) {
gdouble value = gtk_range_get_value(range);
long alsa_value = (long)(value * 100.0);
snd_ctl_elem_value_set_integer(band->val, 0, alsa_value); // Left channel
snd_ctl_elem_value_set_integer(band->val, 1, alsa_value); // Right channel
snd_ctl_elem_write(band->ctl, band->val);
}
static void init_alsa_mixer(MixerData *data) {
if (snd_mixer_open(&data->mixer, 0) < 0) {
fprintf(stderr, "Failed to open ALSA mixer\n");
return;
}
if (snd_mixer_attach(data->mixer, "default") < 0) {
fprintf(stderr, "Failed to attach mixer to default device\n");
snd_mixer_close(data->mixer);
return;
}
if (snd_mixer_selem_register(data->mixer, NULL, NULL) < 0) {
fprintf(stderr, "Failed to register mixer\n");
snd_mixer_close(data->mixer);
return;
}
if (snd_mixer_load(data->mixer) < 0) {
fprintf(stderr, "Failed to load mixer\n");
snd_mixer_close(data->mixer);
return;
}
const char *channel_names[] = {"Master", "Speaker", "PCM", "Headphone", "Mic", "Mic Boost", "Beep"};
int max_channels = G_N_ELEMENTS(channel_names);
data->channels = g_new(MixerChannel, max_channels);
data->num_channels = 0;
for (int i = 0; i < max_channels; i++) {
snd_mixer_selem_id_t *sid;
snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_name(sid, channel_names[i]);
snd_mixer_elem_t *elem = snd_mixer_find_selem(data->mixer, sid);
if (elem) {
data->channels[data->num_channels].elem = elem;
data->channels[data->num_channels].channel_name = channel_names[i];
data->num_channels++;
}
}
}
static void init_alsa_eq(EQData *data) {
int err;
if ((err = snd_ctl_open(&data->ctl, "equal", 0)) < 0) {
fprintf(stderr, "Failed to open ALSA control 'equal': %s\n", snd_strerror(err));
return;
}
snd_ctl_elem_list_t *list;
snd_ctl_elem_list_alloca(&list);
if ((err = snd_ctl_elem_list(data->ctl, list)) < 0) {
fprintf(stderr, "Failed to get initial control list: %s\n", snd_strerror(err));
snd_ctl_close(data->ctl);
return;
}
int num_controls = snd_ctl_elem_list_get_count(list);
if (num_controls <= 0) {
fprintf(stderr, "No controls found for 'equal' device\n");
snd_ctl_close(data->ctl);
return;
}
if ((err = snd_ctl_elem_list_alloc_space(list, num_controls)) < 0) {
fprintf(stderr, "Failed to allocate control list space: %s\n", snd_strerror(err));
snd_ctl_close(data->ctl);
return;
}
if ((err = snd_ctl_elem_list(data->ctl, list)) < 0) {
fprintf(stderr, "Failed to list controls: %s\n", snd_strerror(err));
snd_ctl_close(data->ctl);
return;
}
num_controls = snd_ctl_elem_list_get_used(list);
data->bands = g_new(EQBand, num_controls);
data->num_bands = 0;
for (int i = 0; i < num_controls; i++) {
snd_ctl_elem_id_t *id;
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_list_get_id(list, i, id);
const char *name = snd_ctl_elem_id_get_name(id);
unsigned int numid = snd_ctl_elem_id_get_numid(id);
snd_ctl_elem_iface_t iface = snd_ctl_elem_id_get_interface(id);
if (iface != SND_CTL_ELEM_IFACE_MIXER) {
fprintf(stderr, "Skipping non-mixer control: %s (interface=%d)\n", name, iface);
continue;
}
snd_ctl_elem_value_t *val;
if ((err = snd_ctl_elem_value_malloc(&val)) < 0) {
fprintf(stderr, "Failed to allocate value for control '%s': %s\n", name, snd_strerror(err));
continue;
}
snd_ctl_elem_value_set_id(val, id);
if ((err = snd_ctl_elem_read(data->ctl, val)) < 0) {
fprintf(stderr, "Failed to read control '%s' (numid %u): %s\n", name, numid, snd_strerror(err));
snd_ctl_elem_value_free(val);
continue;
}
data->bands[data->num_bands].val = val;
data->bands[data->num_bands].band_name = g_strdup(name);
data->bands[data->num_bands].ctl = data->ctl;
data->num_bands++;
}
if (data->num_bands == 0) {
fprintf(stderr, "No EQ bands found\n");
}
snd_ctl_elem_list_free_space(list);
}
static void cleanup_alsa(MixerData *mixer_data, EQData *eq_data) {
if (mixer_data->mixer) {
snd_mixer_close(mixer_data->mixer);
g_free(mixer_data->channels);
}
if (eq_data->ctl) {
for (int i = 0; i < eq_data->num_bands; i++) {
if (eq_data->bands[i].val) {
snd_ctl_elem_value_free(eq_data->bands[i].val);
}
g_free((char *)eq_data->bands[i].band_name);
}
snd_ctl_close(eq_data->ctl);
g_free(eq_data->bands);
}
}
int main(int argc, char *argv[]) {
gtk_init(&argc, &argv);
// Set the default window icon
gtk_window_set_default_icon_name("audio-speakers");
MixerData mixer_data = {0};
EQData eq_data = {0};
init_alsa_mixer(&mixer_data);
init_alsa_eq(&eq_data);
/* ADDED SECTION: Set up mixer event polling. */
int fd_ct = snd_mixer_poll_descriptors_count(mixer_data.mixer);
if(fd_ct > 0) {
GSource *source = g_source_new(&(GSourceFuncs){.dispatch = fd_dispatch},
sizeof(GSource));
struct pollfd *pollfds = alloca(fd_ct * sizeof(struct pollfd));
snd_mixer_poll_descriptors(mixer_data.mixer, pollfds, fd_ct);
for(int i = 0; i < fd_ct; i++) {
g_source_add_unix_fd(source, pollfds[i].fd, G_IO_IN);
}
g_source_set_callback(source, 0, mixer_data.mixer, NULL);
g_source_attach(source, g_main_context_default());
}
/* END OF ADDED SECTION */
// Create CSS provider for custom styling
GtkCssProvider *css_provider = gtk_css_provider_new();
gtk_css_provider_load_from_data(css_provider,
"frame {"
" border-width: 2px;"
" border-style: solid;"
" border-color: #333333;"
" box-shadow: none;"
" border-radius: 4px;"
"}",
-1, NULL);
gtk_style_context_add_provider_for_screen(
gdk_screen_get_default(),
GTK_STYLE_PROVIDER(css_provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref(css_provider);
// Create window
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "AlsaTune");
gtk_window_set_default_size(GTK_WINDOW(window), 640, 510);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// Main vertical box to hold both mixer and EQ sections
GtkWidget *main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_set_border_width(GTK_CONTAINER(main_box), 5);
gtk_container_add(GTK_CONTAINER(window), main_box);
// Mixer frame with border
GtkWidget *mixer_frame = gtk_frame_new(NULL);
gtk_box_pack_start(GTK_BOX(main_box), mixer_frame, FALSE, FALSE, 0);
// Mixer horizontal box
GtkWidget *mixer_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
gtk_container_set_border_width(GTK_CONTAINER(mixer_box), 5);
gtk_container_add(GTK_CONTAINER(mixer_frame), mixer_box);
// Add sliders for each mixer channel
for (int i = 0; i < mixer_data.num_channels; i++) {
GtkWidget *channel_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_box_pack_start(GTK_BOX(mixer_box), channel_box, TRUE, TRUE, 5);
GtkWidget *label = gtk_label_new(mixer_data.channels[i].channel_name);
gtk_widget_set_halign(label, GTK_ALIGN_CENTER);
gtk_box_pack_start(GTK_BOX(channel_box), label, FALSE, FALSE, 5);
mixer_data.channels[i].scale = gtk_scale_new_with_range(GTK_ORIENTATION_VERTICAL, 0, 1, 0.01);
gtk_range_set_inverted(GTK_RANGE(mixer_data.channels[i].scale), TRUE);
gtk_scale_set_draw_value(GTK_SCALE(mixer_data.channels[i].scale), TRUE);
gtk_scale_set_value_pos(GTK_SCALE(mixer_data.channels[i].scale), GTK_POS_BOTTOM);
gtk_widget_set_size_request(mixer_data.channels[i].scale, -1, 150);
gtk_box_pack_start(GTK_BOX(channel_box), mixer_data.channels[i].scale, TRUE, TRUE, 0);
g_signal_connect(mixer_data.channels[i].scale, "value-changed", G_CALLBACK(slider_changed), &mixer_data.channels[i]);
long min, max, value;
snd_mixer_selem_get_playback_volume_range(mixer_data.channels[i].elem, &min, &max);
snd_mixer_selem_get_playback_volume(mixer_data.channels[i].elem, 0, &value);
gtk_range_set_value(GTK_RANGE(mixer_data.channels[i].scale), (double)(value - min) / (max - min));
GtkWidget *padding = gtk_label_new("");
gtk_box_pack_start(GTK_BOX(channel_box), padding, FALSE, FALSE, 5);
/* ADDED SECTION: Set element callbacks & closures for mixer events. */
snd_mixer_elem_set_callback(mixer_data.channels[i].elem, elem_callback);
snd_mixer_elem_set_callback_private(mixer_data.channels[i].elem,
mixer_data.channels[i].scale);
/* END OF ADDED SECTION */
}
// EQ frame with border
GtkWidget *eq_frame = gtk_frame_new(NULL);
gtk_box_pack_start(GTK_BOX(main_box), eq_frame, FALSE, FALSE, 0);
// EQ horizontal box
GtkWidget *eq_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
gtk_container_set_border_width(GTK_CONTAINER(eq_box), 2);
gtk_widget_set_size_request(eq_box, -1, 200);
gtk_container_add(GTK_CONTAINER(eq_frame), eq_box);
// Add sliders for each EQ band
for (int i = 0; i < eq_data.num_bands; i++) {
if (!eq_data.bands[i].val) {
fprintf(stderr, "Warning: NULL val for band %d: %s\n", i, eq_data.bands[i].band_name);
continue;
}
GtkWidget *band_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_box_pack_start(GTK_BOX(eq_box), band_box, TRUE, TRUE, 5);
// Use shorter labels (e.g., "31 Hz" or "1 kHz")
char short_label[16];
const char *freq_start = eq_data.bands[i].band_name + 4;
const char *end = strstr(freq_start, " Playback Volume");
if (end) {
strncpy(short_label, freq_start, end - freq_start);
short_label[end - freq_start] = '\0';
} else {
strncpy(short_label, freq_start, sizeof(short_label) - 1);
short_label[sizeof(short_label) - 1] = '\0';
}
// Strip existing Hz or kHz
char *unit = strstr(short_label, " Hz");
if (!unit) unit = strstr(short_label, " kHz");
if (unit) *unit = '\0';
// Append correct unit based on frequency
if (strcmp(short_label, "1") == 0 || strcmp(short_label, "2") == 0 || strcmp(short_label, "4") == 0 || strcmp(short_label, "8") == 0 || strcmp(short_label, "16") == 0) {
strcat(short_label, " kHz");
} else {
strcat(short_label, " Hz");
}
GtkWidget *label = gtk_label_new(short_label);
gtk_widget_set_halign(label, GTK_ALIGN_CENTER);
gtk_box_pack_start(GTK_BOX(band_box), label, FALSE, FALSE, 5);
eq_data.bands[i].scale = gtk_scale_new_with_range(GTK_ORIENTATION_VERTICAL, 0, 1, 0.01);
gtk_range_set_inverted(GTK_RANGE(eq_data.bands[i].scale), TRUE);
gtk_scale_set_draw_value(GTK_SCALE(eq_data.bands[i].scale), TRUE);
gtk_scale_set_value_pos(GTK_SCALE(eq_data.bands[i].scale), GTK_POS_BOTTOM);
gtk_widget_set_size_request(eq_data.bands[i].scale, -1, 150);
gtk_box_pack_start(GTK_BOX(band_box), eq_data.bands[i].scale, TRUE, TRUE, 0);
g_signal_connect(eq_data.bands[i].scale, "value-changed", G_CALLBACK(eq_slider_changed), &eq_data.bands[i]);
long value = snd_ctl_elem_value_get_integer(eq_data.bands[i].val, 0);
gtk_range_set_value(GTK_RANGE(eq_data.bands[i].scale), (double)value / 100.0);
GtkWidget *padding = gtk_label_new("");
gtk_box_pack_start(GTK_BOX(band_box), padding, FALSE, FALSE, 5);
}
// Preset controls: entry, save button, and combo box
GtkWidget *preset_box = gtk_grid_new();
gtk_grid_set_column_spacing(GTK_GRID(preset_box), 5);
gtk_widget_set_halign(preset_box, GTK_ALIGN_CENTER);
gtk_box_pack_start(GTK_BOX(main_box), preset_box, FALSE, FALSE, 1);
GtkWidget *preset_entry = gtk_entry_new();
gtk_entry_set_placeholder_text(GTK_ENTRY(preset_entry), "Enter name for new pre-set");
gtk_widget_set_size_request(preset_entry, 250, -1);
gtk_grid_attach(GTK_GRID(preset_box), preset_entry, 1, 0, 1, 1);
GtkWidget *save_button = gtk_button_new_with_label("Save Pre-set");
gtk_grid_attach(GTK_GRID(preset_box), save_button, 2, 0, 1, 1);
GtkWidget *preset_combo = gtk_combo_box_text_new();
gtk_widget_set_size_request(preset_combo, 250, -1);
gtk_grid_attach(GTK_GRID(preset_box), preset_combo, 3, 0, 1, 1);
// Add spacers for equal left/right margins
GtkWidget *spacer_left = gtk_label_new("");
gtk_grid_attach(GTK_GRID(preset_box), spacer_left, 0, 0, 1, 1);
gtk_widget_set_hexpand(spacer_left, TRUE);
GtkWidget *spacer_right = gtk_label_new("");
gtk_grid_attach(GTK_GRID(preset_box), spacer_right, 4, 0, 1, 1);
gtk_widget_set_hexpand(spacer_right, TRUE);
// Store references for callbacks
g_object_set_data(G_OBJECT(save_button), "preset_entry", preset_entry);
g_object_set_data(G_OBJECT(save_button), "preset_combo", preset_combo);
g_object_set_data(G_OBJECT(save_button), "window", window);
// Connect signals
g_signal_connect(save_button, "clicked", G_CALLBACK(save_preset), &eq_data);
g_signal_connect(preset_combo, "changed", G_CALLBACK(apply_preset), &eq_data);
// Load existing presets into combo box
load_presets(GTK_COMBO_BOX_TEXT(preset_combo));
gtk_widget_show_all(window);
gtk_main();
cleanup_alsa(&mixer_data, &eq_data);
return 0;
}^^ Cool! Let me know if it works. wink
It definitely works, but it took me a fair amount of trial and error to get it all hooked up properly. Working with ALSA is definitely fiddly, and the documentation is practically nonexistent. I wrote a fully functional ALSA system mixer last year using the Motif toolkit, for the very same reasons you started working on your mixer app. (It only covers input/outputs, though, I didn't try to implement EQ.) I set it aside at the time because I assumed there wouldn't be much interest from the general public. I'll see about getting it up somewhere that other people can take a look at it.
*EDIT: I pushed the ALSA mixer app I wrote to github: nxmixer. Maybe you will find it helpful. You will need meson to build it. The bottom of the README includes basic building instructions.
I know Motif is a strange choice, but I wanted to see what would be involved with writing a pure C GUI app without touching anything GNOME related. It was an interesting experience!
If you have both open at the same time, adjusting either one will change the sound, but they will not update each other while both are open. It seems to me that the logic needed to do that would be difficult, involving some kind of constant polling of each other while in operation, additional code would probably have to be written for both programs.
If you register a mixer element and a callback procedure with snd_mixer_elem_set_callback(), ALSA will invoke your callback whenever a control value on that element changes.
The XLibre project provides a tool named xorg-testing which builds and installs X in a chroot jail. This method allows you to test the distribution on an existing Debian system without overwriting the base X installation. Presumably, it would also work with Devuan, although I haven't found the time to to try it yet. I'll update the thread if I do.
From what you describe, it sounds as if you may be trying to log in via a display manager, but the full X server is failing to start. You could try logging into a text console instead. (Press Ctrl+Alt+F1 to bring up a console.) From there, you may be able to troubleshoot the issue.
I'm not sure if persistenced can cause the kind of problem you're having, but removing it definitely won't prevent you from using the graphics driver. Other users (including myself) have experienced the same error. It was discussed here in the Beowulf beta thread. It seems the init script may try to start the persistenced service without checking first to see if it's already running. One user in the above thread modified the script to address this issue, while another fixed it by removing and reinstalling the service. I chose to simply purge it from my system.
So, many Excalibur updates later, and this is still occurring. I tried manually changing the driver for both video cards to modesetting, and while X no longer loaded the nouveau driver, it still crashed with a backtrace pointing to /usr/lib/x86_66-linux-gnu/dri/nouveau_dri.so. As far as I can tell, then, the nouveau kernel module is causing X to crash even when I'm not using the nouveau display driver.
There doesn't seem to be a way to get around this problem as long as I'm using this hardware. I'm OK using Daedalus for now, but I'm not sure how close Excalibur is to a stable release. If this bug survives to it, I'll have to abandon the distro entirely.
I've noticed that Excalibur updates sometime remove packages, then install newer versions of the same package. I'm not sure why it does that, rather than just updating the package, but I'm sure there's a good reason.
The libc6 package is marked with priority "required," and contains the GNU version of the standard C library, along with some other core support libraries. I don't think it's even possible to install a usable system without it. The other three libraries are part of the LLVM toolchain, though. I've had to install them to compile C++ projects. Did you install clang at some point? If you never selected them manually, they might have been included with a meta-package.
There are also a couple of directories for 32-bit libraries that become links in a default merged system. Here is a full listing from a system I created by running debootstrap with the --merged-usr option:
$ find / -maxdepth 1 -type l -xtype d -printf '%p -> %l\n'
/libx32 -> usr/libx32
/sbin -> usr/sbin
/bin -> usr/bin
/lib32 -> usr/lib32
/lib64 -> usr/lib64
/lib -> usr/libI was looking over the setup scripts for schroot to get a better sense for how the system works, when I stumbled across this comment in the file /etc/schroot/setup.d/10mount:
# Work around systemd insanity.
#
# Mark this mountpoint as private; some systems have / as a shared mountpoint.
# As an example, assume /home/m/ch is the chroot directory.
# schroot will mount -o bind /home/m/ch to /var/lib/schroot/mount/ch-123
# Afterwards, it will bind-mount /dev to /var/lib/schroot/mount/ch-123.
# With shared mountpoints, that mount will also show up in the original
# /home/m/ch. This is a problem once schroot mounted /home: the following
# mount of /tmp will show up in /var/lib/schroot/mount/ch-123/tmp,
# /home/m/ch/tmp and /home/m/ch/home/m/ch/tmp (!), which leads to failure
# on unmounting.
if [ "$(uname -s)" = "Linux" ]; then
mount --make-private "$3"
fiI had no idea systemd made everything a shared mount by default, but now I'm wondering if it could have been the cause of some weird issues I had in the past. I guess it's another reason to be grateful I'm not using systemd anymore.
The line you may need to un-comment in /etc/default/grub to enable the OS prober is this one:
GRUB_DISABLE_OS_PROBER=falseThe default setting is true, which disables the prober. Un-commenting the line sets it to false, which enables the prober. If you install grub manually, the line is commented out by default. I'm not sure what happens when you use the installer, though.
I booted the system off another partition with Daedalus and X starts fine. If I'm understanding the backtrace, it appears that the nouveau kernel module (nouveau_dri.so) is causing a hardware exception (SIGILL) which would make sense, as this issue started to occur after a kernel upgrade. I'm not sure if I'm reading it right, though. I would like to make a proper bug report, but I don't where it should go, as this problem involves the X server, display driver, and kernel.
Update: Apparently it wasn't the kernel after all. I rebooted with multiple kernel versions, but the X server always fails to start. I guess I'll stick with Daedalus for now. Expecting this old nvidia card to work reliably on the testing branch may be asking too much.