Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions data/menu.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@
</action>
</item>

<separator/>

<item label="Configure" icon="preferences-system">
<action name="Execute">
<execute>wbconf</execute>
</action>
</item>

<menu id="waybox-menu" label="Waybox" icon="applications-system">
<item label="Reconfigure" icon="view-refresh">
<action name="Reconfigure"/>
Expand Down
91 changes: 91 additions & 0 deletions include/waybox/wbconf.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#ifndef WB_WBCONF_HPP
#define WB_WBCONF_HPP

#include <optional>
#include <string>
#include <vector>

namespace wb {

/*
* The waybox settings that wbconf (our obconf-style GUI) reads from and writes
* to rc.xml. Every field is optional: std::nullopt means "absent from rc.xml",
* so the compositor falls back to its built-in default. Standard Openbox keys
* (theme/name, placement/policy, margins) live as child-element text; the
* waybox extensions (titlebar/menu/switcher) live as attributes on child
* elements of <waybox>.
*/
struct WayboxSettings {
/* Appearance */
std::optional<std::string> theme_name; /* theme/name */
std::optional<int> titlebar_pad_y; /* waybox/titlebar@paddingY */
std::optional<int> titlebar_button_size; /* waybox/titlebar@buttonSize */
std::optional<int> titlebar_resize_grab; /* waybox/titlebar@resizeGrab */

/* Windows */
std::optional<std::string> placement_policy; /* placement/policy:
* Smart|Center|UnderMouse */

/* Margins (reserved screen-edge space, in px) */
std::optional<int> margin_top; /* margins/top */
std::optional<int> margin_bottom; /* margins/bottom */
std::optional<int> margin_left; /* margins/left */
std::optional<int> margin_right; /* margins/right */

/* Menu (waybox extension) */
std::optional<std::string> menu_source; /* builtin | <command> */
std::optional<std::string> menu_submenu_open; /* hover | click */
std::optional<int> menu_hover_delay; /* ms */
std::optional<bool> menu_wrap;
std::optional<bool> menu_icons;

/* Task switcher (waybox extension) */
std::optional<std::string> switcher_order; /* mru | stacking | spatial */
std::optional<bool> switcher_osd;
std::optional<bool> switcher_wrap;
};

/*
* A loaded rc.xml document. Reads/writes only the nodes wbconf manages, leaving
* the rest of the user's configuration (keybindings, application rules, …) and
* its comments intact. Backed by libxml2; movable, non-copyable.
*/
class RcDocument {
public:
RcDocument(RcDocument &&) noexcept;
RcDocument &operator=(RcDocument &&) noexcept;
RcDocument(const RcDocument &) = delete;
RcDocument &operator=(const RcDocument &) = delete;
~RcDocument();

/* Parse rc.xml from a file or an in-memory string. Returns nullopt if the
* document cannot be parsed or has no root element. */
static std::optional<RcDocument> load_file(const std::string &path);
static std::optional<RcDocument> from_string(const std::string &xml);

/* The settings currently present in the document. */
WayboxSettings read() const;

/* Write the settings into the document: each engaged field is created or
* updated, each std::nullopt field is removed (so the default applies). */
void apply(const WayboxSettings &settings);

/* Serialise the whole document back to XML / to a file. */
std::string to_string() const;
bool save_file(const std::string &path) const;

private:
explicit RcDocument(void *doc) : doc_(doc) {}
void *doc_ = nullptr; /* xmlDocPtr, owned */
};

/*
* The names of installed Openbox/waybox themes — every directory under the
* theme search paths that contains an openbox-3/themerc. Sorted, de-duplicated.
* Reads the real environment ($XDG_DATA_HOME, $HOME, $XDG_DATA_DIRS).
*/
std::vector<std::string> installed_theme_names();

} // namespace wb

#endif /* WB_WBCONF_HPP */
7 changes: 7 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,16 @@ if not tests_only
subdir('po')
endif

# wbconf: an optional GTK4 settings GUI (obconf-style). Built only when GTK4
# is available, so the compositor still builds on hosts without it.
gtk4 = dependency('gtk4', required: get_option('wbconf'))

subdir('data')
subdir('protocol')
subdir('waybox')
if gtk4.found()
subdir('wbconf')
endif
endif

subdir('test')
1 change: 1 addition & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ option('wlroots-version', type: 'string', value: '', description: 'The version o
option('sanitize', type: 'feature', value: 'auto', description: 'Build with AddressSanitizer and UndefinedBehaviorSanitizer (auto: enabled for debug buildtypes)')
option('tests_only', type: 'boolean', value: false, description: 'Build only the dependency-free unit tests (no wlroots/compositor); used for a sanitized CI job on stock distros')
option('svg', type: 'feature', value: 'auto', description: 'Build with librsvg for SVG menu-icon support (auto: enabled when librsvg is present)')
option('wbconf', type: 'feature', value: 'auto', description: 'Build the wbconf GTK4 settings GUI (auto: enabled when GTK4 is present)')
13 changes: 7 additions & 6 deletions po/POTFILES.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
data/waybox.sh
waybox/config.c
waybox/main.c
waybox/output.c
waybox/seat.c
waybox/server.c
waybox/xdg_shell.c
waybox/config.cpp
waybox/main.cpp
waybox/output.cpp
waybox/seat.cpp
waybox/server.cpp
waybox/xdg_shell.cpp
wbconf/main.cpp
2 changes: 1 addition & 1 deletion po/meson.build
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
add_project_arguments('-DGETTEXT_PACKAGE="' + meson.project_name().to_lower() + '"',
'-DLOCALEDIR="' + get_option('prefix') / get_option('localedir') + '"',
'-DUSE_NLS=1', language: 'c')
'-DUSE_NLS=1', language: ['c', 'cpp'])

i18n = import('i18n', required: false)
if i18n.found()
Expand Down
100 changes: 98 additions & 2 deletions po/waybox.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: waybox\n"
"Report-Msgid-Bugs-To: https://github.com/wizbright/waybox/issues\n"
"POT-Creation-Date: 2024-01-25 21:21-0500\n"
"POT-Creation-Date: 2026-06-21 15:16+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand All @@ -20,6 +20,9 @@ msgstr ""
msgid "WARNING: Using files from Openbox. These may not work correctly."
msgstr ""

msgid "ERROR: No menu file found."
msgstr ""

msgid "ERROR: No configuration file found."
msgstr ""

Expand All @@ -32,6 +35,9 @@ msgstr ""
msgid "No nodeset"
msgstr ""

msgid "Unable to determine the configuration file path."
msgstr ""

msgid ""
"Unable to parse the configuration file. Consult stderr for more information."
msgstr ""
Expand Down Expand Up @@ -131,6 +137,12 @@ msgstr ""
msgid "Failed to create allocator"
msgstr ""

msgid "Failed to create seat"
msgstr ""

msgid "Failed to create cursor"
msgstr ""

msgid "Failed to start backend"
msgstr ""

Expand All @@ -143,5 +155,89 @@ msgstr ""
msgid "Keyboard focus is now on surface"
msgstr ""

msgid "Focusing next toplevel"
msgid "Could not determine the rc.xml path."
msgstr ""

msgid "Failed to write "
msgstr ""

msgid "Saved and reloaded waybox."
msgstr ""

msgid "Saved to "
msgstr ""

msgid "(no running waybox to reload)."
msgstr ""

msgid "Waybox Configuration"
msgstr ""

msgid "Theme:"
msgstr ""

msgid "Titlebar padding (px):"
msgstr ""

msgid "Button size (px, 0 = auto):"
msgstr ""

msgid "Resize grab margin (px):"
msgstr ""

msgid "Appearance"
msgstr ""

msgid "Placement:"
msgstr ""

msgid "Windows"
msgstr ""

msgid "Top:"
msgstr ""

msgid "Bottom:"
msgstr ""

msgid "Left:"
msgstr ""

msgid "Right:"
msgstr ""

msgid "Margins"
msgstr ""

msgid "Source (builtin or command):"
msgstr ""

msgid "Submenu opens on:"
msgstr ""

msgid "Submenu hover delay (ms):"
msgstr ""

msgid "Wrap selection:"
msgstr ""

msgid "Show icons:"
msgstr ""

msgid "Menu"
msgstr ""

msgid "Order:"
msgstr ""

msgid "Show OSD:"
msgstr ""

msgid "Switcher"
msgstr ""

msgid "Close"
msgstr ""

msgid "Save & Apply"
msgstr ""
12 changes: 12 additions & 0 deletions test/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ if not get_option('tests_only')
test('menu_parse', menu_parse_test)
endif

# wbconf rc.xml model round-trip test; links libxml2 + theme, full-build only.
if not get_option('tests_only')
wbconf_test = executable(
'wbconf_test',
['wbconf_test.cpp', 'wb_test_main.cpp', files('../waybox/wbconf.cpp'),
theme_src],
include_directories: [inc_dir],
dependencies: [libxml2],
)
test('wbconf', wbconf_test)
endif

# Headless integration test driving real synthesized input via the
# virtual-keyboard protocol. Skips (exit 77) when foot/wtype or a headless
# display are unavailable, so CI stays green; run a sanitized local build to
Expand Down
Loading