--- a/src/backgrounds.cpp +++ b/src/backgrounds.cpp @@ -1,8 +1,6 @@ #include "backgrounds.hpp" #include "ipc.hpp" -#include #include -#include namespace { @@ -12,25 +10,6 @@ void clear(Gtk::Box& box) { for (auto child : box.get_children()) box.remove(*child); } void label_style(Gtk::Label& label) { label.set_halign(Gtk::ALIGN_START); label.set_line_wrap(); label.set_max_width_chars(75); label.set_selectable(true); } -json backdrop_request(json request) -{ - GError *error = nullptr; - auto child = g_subprocess_new(GSubprocessFlags(G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE | - G_SUBPROCESS_FLAGS_STDERR_PIPE), &error, "wf-appearance", "backgrounds-api", nullptr); - if (!child) - { - std::string message = error ? error->message : "Cannot start wf-appearance"; - g_clear_error(&error); throw std::runtime_error(message + ". Install the matching wf-appearance package."); - } - std::unique_ptr guard(child, g_object_unref); - gchar *out = nullptr, *err = nullptr; - bool ok = g_subprocess_communicate_utf8(child, request.dump().c_str(), nullptr, &out, &err, &error); - std::string bytes = out ? out : "", message = err ? err : ""; - g_free(out); g_free(err); - if (error) { message = error->message; g_clear_error(&error); } - if (!ok || !g_subprocess_get_successful(child)) throw std::runtime_error(message); - return json::parse(bytes); -} } BackgroundsPage::json BackgroundsPage::request(const std::string& method, const json& data) { @@ -40,10 +19,10 @@ BackgroundsPage::BackgroundsPage(const std::string& config) : Gtk::Box(Gtk::ORIENTATION_VERTICAL, 10), config_file(config) { set_border_width(12); pack_start(tabs, true, true); - workspace.set_border_width(10); backdrop.set_border_width(10); - tabs.append_page(workspace, "Workspace images"); tabs.append_page(backdrop, "Fixed backdrop"); - help.set_text("Each workspace has its own image. Transparent areas reveal the other cube faces, including while the cube is at rest. The fixed backdrop stays behind the cube. Selecting a workspace here does not switch your desktop."); - label_style(help); label_style(status); label_style(memory); label_style(backdrop_help); label_style(backdrop_status); + workspace.set_border_width(10); + tabs.append_page(workspace, "Workspace images"); + help.set_text("Each workspace has its own image. Transparent areas reveal the other cube faces, including while the cube is at rest. Selecting a workspace here does not switch your desktop."); + label_style(help); label_style(status); label_style(memory); workspace.pack_start(help, false, false); outputs.get_accessible()->set_name("Background desktop output"); selection.get_accessible()->set_name("Background workspace"); mode.get_accessible()->set_name("Workspace background layout"); @@ -55,15 +34,6 @@ workspace.pack_start(buttons, false, false); workspace.pack_start(status, false, false); workspace.pack_start(memory, false, false); all.set_tooltip_text("Replace every workspace override on this output with the selected layout."); - backdrop_help.set_text("This image remains behind the cube. Use rotation keeps the existing Wallhaven wallpaper service in control. Workspace backgrounds have a separate Apply button."); - backdrop.pack_start(backdrop_help, false, false); - backdrop_mode.append("individual", "Separate physical monitor images"); backdrop_mode.append("combined", "One image spanning VIRTUAL"); - for (auto name : {"fill", "fit", "tile"}) backdrop_sizing.append(name, name); - backdrop_mode.get_accessible()->set_name("Fixed backdrop layout"); backdrop_sizing.get_accessible()->set_name("Fixed backdrop sizing"); - backdrop.pack_start(backdrop_mode, false, false); backdrop.pack_start(backdrop_sizing, false, false); - backdrop.pack_start(backdrop_editor, false, false); - backdrop_buttons.pack_start(backdrop_reload, false, false); backdrop_buttons.pack_end(backdrop_apply, false, false); - backdrop.pack_start(backdrop_buttons, false, false); backdrop.pack_start(backdrop_status, false, false); outputs.signal_changed().connect([this] { if (!loading) select_output(); }); selection.signal_changed().connect([this] { if (!loading) { selected_workspace = selection.get_active_id(); show_layout(); } }); mode.signal_changed().connect([this] @@ -95,13 +65,7 @@ target["workspaces"][std::to_string(x)+","+std::to_string(y)] = layout; changed(); show_layout(); }); - backdrop_reload.signal_clicked().connect([this] { fixed_request(false); }); - backdrop_apply.signal_clicked().connect([this] { fixed_request(true); }); - backdrop_mode.signal_changed().connect([this] - { if (!loading && fixed_draft.is_object()) { fixed_draft["mode"] = backdrop_mode.get_active_id().raw(); show_backdrop(); } }); - backdrop_sizing.signal_changed().connect([this] - { if (!loading && fixed_draft.is_object()) { fixed_draft["sizing"] = backdrop_sizing.get_active_id().raw(); backdrop_status.set_text("Unapplied backdrop changes"); } }); - refresh(); fixed_request(false); + refresh(); timer = Glib::signal_timeout().connect([this] { if (pending_job) @@ -118,20 +82,10 @@ } } catch (const std::exception& e) { pending_job = 0; changed(); status.set_text(e.what()); } } - if (fixed_worker.valid() && fixed_worker.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - try - { - fixed_state = fixed_worker.get(); fixed_draft = fixed_state["settings"]; - show_backdrop(); backdrop_status.set_text(fixed_state["status"].get()); - } catch (const std::exception& e) { backdrop_status.set_text(e.what()); } - backdrop_reload.set_sensitive(true); backdrop_apply.set_sensitive(fixed_draft.is_object()); - backdrop_editor.set_sensitive(true); backdrop_mode.set_sensitive(true); backdrop_sizing.set_sensitive(true); - } return true; }, 200); } -BackgroundsPage::~BackgroundsPage() { timer.disconnect(); if (fixed_worker.valid()) fixed_worker.wait(); } +BackgroundsPage::~BackgroundsPage() { timer.disconnect(); } void BackgroundsPage::show_memory(const json& renderer) { const auto mib = 1024 * 1024; @@ -219,15 +173,15 @@ loading = false; editor.show_all(); changed(); } void BackgroundsPage::image_row(Gtk::Box& parent, const std::string& label, const json& value, - std::function change, bool rotation) + std::function change) { auto box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 6)); parent.pack_start(*box, false, false); auto heading = Gtk::manage(new Gtk::Label(label)); heading->set_halign(Gtk::ALIGN_START); box->pack_start(*heading, false, false); auto row = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 6)); box->pack_start(*row, false, false); auto entry = Gtk::manage(new Gtk::Entry()); entry->set_text(value["image"].get()); - entry->set_placeholder_text(rotation ? "Wallhaven rotation" : "Transparent (or choose a color below)"); + entry->set_placeholder_text("Transparent (or choose a color below)"); entry->get_accessible()->set_name(label + " image"); - auto browse = Gtk::manage(new Gtk::Button("Choose image…")); auto reset = Gtk::manage(new Gtk::Button(rotation ? "Use rotation" : "Clear image")); + auto browse = Gtk::manage(new Gtk::Button("Choose image…")); auto reset = Gtk::manage(new Gtk::Button("Clear image")); row->pack_start(*entry, true, true); row->pack_start(*browse, false, false); row->pack_start(*reset, false, false); auto slot = std::make_shared(value); entry->signal_changed().connect([entry, slot, change] { (*slot)["image"] = entry->get_text().raw(); change(*slot); }); @@ -239,7 +193,6 @@ auto filter = Gtk::FileFilter::create(); filter->set_name("Images"); filter->add_pixbuf_formats(); dialog.add_filter(filter); if (dialog.run() == Gtk::RESPONSE_OK) entry->set_text(dialog.get_filename()); }); - if (rotation) return; auto options = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 10)); box->pack_start(*options, false, false); auto sizing = Gtk::manage(new Gtk::ComboBoxText()); sizing->append("fill", "Fill — crop to cover"); sizing->append("fit", "Fit — show the whole image"); sizing->append("tile", "Tile — repeat original size"); @@ -264,41 +217,3 @@ pending_job = response["job"]; status.set_text("Preparing workspace background images…"); changed(); } catch (const std::exception& e) { status.set_text(e.what()); } } -void BackgroundsPage::fixed_request(bool save) -{ - if (fixed_worker.valid()) return; - json input = {{"method", save ? "apply" : "get"}}; - if (save) { input["revision"] = fixed_state["revision"]; input["settings"] = fixed_draft; } - fixed_worker = std::async(std::launch::async, backdrop_request, input); - backdrop_status.set_text(save ? "Validating and saving fixed backdrop…" : "Loading fixed backdrop…"); - backdrop_apply.set_sensitive(false); backdrop_reload.set_sensitive(false); backdrop_editor.set_sensitive(false); - backdrop_mode.set_sensitive(false); backdrop_sizing.set_sensitive(false); -} -void BackgroundsPage::show_backdrop() -{ - if (!fixed_draft.is_object()) return; - loading = true; clear(backdrop_editor); - backdrop_mode.set_active_id(fixed_draft["mode"].get()); backdrop_sizing.set_active_id(fixed_draft["sizing"].get()); - auto add = [this] (const std::string& name, const std::string& path) - { - auto slot = empty_slot(); slot["image"] = path; - image_row(backdrop_editor, name, slot, [this,name] (json slot) - { - if (name == "VIRTUAL") fixed_draft["combined_image"] = slot["image"]; - else fixed_draft["monitor_images"][name] = slot["image"]; - backdrop_status.set_text("Unapplied backdrop changes"); - }, true); - }; - if (fixed_draft["mode"] == "combined") add("VIRTUAL", fixed_draft["combined_image"]); - std::set names, combined; - for (auto& [name, value] : fixed_draft["monitor_images"].items()) names.insert(name); - for (auto& output : fixed_state.value("outputs", json::array())) - { - if (output.contains("members") && !output["members"].empty()) - for (auto& member : output["members"]) { names.insert(member["name"]); combined.insert(member["name"]); } - else names.insert(output["name"]); - } - for (auto& name : names) - if (fixed_draft["mode"] != "combined" || !combined.count(name)) add(name, fixed_draft["monitor_images"].value(name, "")); - loading = false; backdrop_editor.show_all(); -} --- a/src/backgrounds.hpp +++ b/src/backgrounds.hpp @@ -1,24 +1,21 @@ #pragma once #include #include -#include class BackgroundsPage : public Gtk::Box { using json = nlohmann::json; Gtk::Notebook tabs; - Gtk::Box workspace{Gtk::ORIENTATION_VERTICAL, 10}, backdrop{Gtk::ORIENTATION_VERTICAL, 10}; - Gtk::Box editor{Gtk::ORIENTATION_VERTICAL, 8}, backdrop_editor{Gtk::ORIENTATION_VERTICAL, 8}; - Gtk::Box buttons{Gtk::ORIENTATION_HORIZONTAL, 8}, backdrop_buttons{Gtk::ORIENTATION_HORIZONTAL, 8}; - Gtk::Label help, status, memory, backdrop_help, backdrop_status; - Gtk::ComboBoxText outputs, selection, mode, backdrop_mode, backdrop_sizing; + Gtk::Box workspace{Gtk::ORIENTATION_VERTICAL, 10}; + Gtk::Box editor{Gtk::ORIENTATION_VERTICAL, 8}; + Gtk::Box buttons{Gtk::ORIENTATION_HORIZONTAL, 8}; + Gtk::Label help, status, memory; + Gtk::ComboBoxText outputs, selection, mode; Gtk::Button apply{"Apply workspace backgrounds"}, reload{"Revert workspace changes"}, all{"Copy to all workspaces"}; - Gtk::Button backdrop_apply{"Apply fixed backdrop"}, backdrop_reload{"Revert backdrop changes"}; - json state, draft, original, fixed_state, fixed_draft; + json state, draft, original; std::string config_file, selected_output, selected_workspace = "default"; bool loading = false, available = false; uint64_t pending_job = 0; - std::future fixed_worker; sigc::connection timer; static json request(const std::string& method, const json& data = json::object()); void refresh(); @@ -29,10 +26,8 @@ void store_layout(const json& layout); void changed(); void save(); - void fixed_request(bool save); - void show_backdrop(); void image_row(Gtk::Box& parent, const std::string& label, const json& value, - std::function change, bool rotation = false); + std::function change); public: explicit BackgroundsPage(const std::string& config); ~BackgroundsPage() override;