Media keys working again

This commit is contained in:
2026-10-18 03:50:33 +02:00
parent 726a7ae92d
commit 798708b3e7
20 changed files with 590 additions and 112 deletions

View File

@@ -90,8 +90,17 @@ void DaemonClient::on_daemon_signal(GDBusConnection* /*connection*/, const gchar
return;
}
// ConfigReloaded and DeviceChanged carry no state the footer does not
// already get from GetStatus, so just refresh it.
if (g_strcmp0(signalName, "ConfigReloaded") == 0) {
// The daemon keeps the previous mappings when the file cannot be read,
// so remember that and let the footer show it. It stays set until a
// reload succeeds.
gboolean ok = FALSE;
g_variant_get(parameters, "(b)", &ok);
self->configOk_ = ok != FALSE;
}
// DeviceChanged and the config state carry nothing GetStatus does not
// report, so a single status refresh covers both.
self->fetch_status();
}
@@ -147,6 +156,7 @@ void DaemonClient::on_status_reply(GObject* /*source*/, GAsyncResult* result, gp
DaemonStatus status;
status.reachable = true;
status.configOk = self->configOk_;
status.version = version != nullptr ? version : "";
status.device = device != nullptr ? device : "";
status.configPath = configPath != nullptr ? configPath : "";

View File

@@ -12,6 +12,7 @@ namespace gkeymapper {
// What the footer needs to know about the daemon.
struct DaemonStatus {
bool reachable = false;
bool configOk = true; // false after a ConfigReloaded(false); see fetch_status
std::string version;
std::string device;
std::string configPath;
@@ -60,6 +61,7 @@ private:
guint signal_subscription_ = 0;
guint name_subscription_ = 0;
bool started_ = false;
bool configOk_ = true;
StatusFn onStatus_;
KeyFn onKey_;
};

View File

@@ -36,6 +36,7 @@ std::vector<KeyRow> rows;
// for the daemon.
struct DaemonFooter {
bool reachable = false;
bool configOk = true;
std::string device;
std::string lastKey;
} daemonFooter;
@@ -54,12 +55,20 @@ static void refresh_footer() {
text = _("Daemon: not reachable");
} else {
text = _("Daemon: running");
if (!daemonFooter.device.empty()) {
text += " · ";
if (daemonFooter.device.empty()) {
// Reachable but no keyboard: unplugged, unsupported, or not found
// yet. Saying so is the difference between "broken" and "unplugged".
text += _("Device: not found");
} else {
g_autofree gchar* device =
g_strdup_printf(_("Device: %s"), daemonFooter.device.c_str());
text += " · ";
text += device;
}
if (!daemonFooter.configOk) {
text += " · ";
text += _("Configuration error");
}
if (!daemonFooter.lastKey.empty()) {
text += " · ";
text += daemonFooter.lastKey;
@@ -70,6 +79,7 @@ static void refresh_footer() {
static void on_daemon_status(const gkeymapper::DaemonStatus& status) {
daemonFooter.reachable = status.reachable;
daemonFooter.configOk = status.configOk;
daemonFooter.device = status.device;
if (!status.reachable) {
daemonFooter.lastKey.clear(); // stale once the daemon is gone
@@ -239,7 +249,9 @@ static void show_about_dialog(GtkWindow* parent) {
g_object_unref(texture);
}
gtk_window_set_transient_for(GTK_WINDOW(dialog), parent);
g_signal_connect(dialog, "response", G_CALLBACK(gtk_window_destroy), NULL);
// No "response" handler: GtkAboutDialog has no such signal in GTK4 (only
// GtkDialog subclasses with buttons, like GtkFileChooserDialog, do). Closing
// it goes through close-request, whose default handler destroys the window.
gtk_window_present(GTK_WINDOW(dialog));
}
@@ -423,6 +435,14 @@ void build_ui(GtkApplication* app) {
update_row_state(&rows[i]);
}
// Start from the saved mappings. Without this the rows are empty and the
// first "Save mappings" of a freshly opened window would overwrite the
// user's file with empty rows. A missing file is not an error (first start).
const std::string configPath = gkeymapper::default_config_path();
if (g_file_test(configPath.c_str(), G_FILE_TEST_EXISTS)) {
gkeymapper::read_mappings(configPath, rows);
}
// Footer with save button and the daemon status line
GtkWidget* footer = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
gtk_widget_set_margin_top(footer, 8);