docs(api): document and test platform utilities
This commit is contained in:
@@ -75,3 +75,11 @@ if(IZO_BUILD_EXAMPLE)
|
||||
add_executable(izo_example examples/dialogs.cpp)
|
||||
target_link_libraries(izo_example PRIVATE iZo::izo)
|
||||
endif()
|
||||
|
||||
option(IZO_BUILD_TESTS "Build the iZo tests" OFF)
|
||||
if(IZO_BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_executable(izo_smoke_test tests/smoke.cpp)
|
||||
target_link_libraries(izo_smoke_test PRIVATE iZo::izo)
|
||||
add_test(NAME izo_smoke COMMAND izo_smoke_test)
|
||||
endif()
|
||||
|
||||
69
README.md
69
README.md
@@ -1,27 +1,30 @@
|
||||
# iZo
|
||||
|
||||
iZo is a small C++17 static library for native file-system dialogs on Windows
|
||||
and Linux.
|
||||
iZo is a small C++17 library that exposes common operating-system services through
|
||||
one consistent API on Windows and Linux.
|
||||
|
||||
## Features
|
||||
|
||||
- Open file, including multi-select and file filters
|
||||
- Save file
|
||||
- Pick folder
|
||||
- Open a path with its default application
|
||||
- Reveal a path in Explorer or the desktop file manager
|
||||
- Explicit selected, cancelled, and error result states
|
||||
- Native open, save, folder, and message dialogs
|
||||
- UTF-8 text clipboard access
|
||||
- Standard application, user, temporary, and executable paths
|
||||
- Non-blocking process launch
|
||||
- Move-only dynamic library handles with automatic cleanup
|
||||
- Environment variables and basic system information
|
||||
- Monotonic timing and sleeping
|
||||
- Recursive or non-recursive directory watching
|
||||
- Crash callbacks, debugger detection, debug breaks, and debug output
|
||||
- Opening paths and revealing files in the platform file manager
|
||||
|
||||
Windows uses the native Explorer `IFileDialog` API. Linux discovers `zenity` or
|
||||
`kdialog` at runtime; at least one must be installed for dialogs. Path opening
|
||||
uses `xdg-open`. Revealing uses the FreeDesktop file-manager D-Bus interface
|
||||
when available and otherwise opens the parent folder.
|
||||
Linux dialogs discover `zenity` or `kdialog` at runtime. Linux clipboard access
|
||||
discovers `wl-clipboard` or `xclip`. No GUI toolkit is linked into applications.
|
||||
|
||||
## Build
|
||||
|
||||
```sh
|
||||
cmake -S . -B build -DIZO_BUILD_EXAMPLE=ON
|
||||
cmake -S . -B build -DIZO_BUILD_EXAMPLE=ON -DIZO_BUILD_TESTS=ON
|
||||
cmake --build build
|
||||
ctest --test-dir build
|
||||
```
|
||||
|
||||
To consume an installed copy:
|
||||
@@ -33,17 +36,45 @@ target_link_libraries(your_target PRIVATE iZo::izo)
|
||||
|
||||
## Usage
|
||||
|
||||
Include individual feature headers or the complete API:
|
||||
|
||||
```cpp
|
||||
#include <izo/dialogs.hpp>
|
||||
#include <izo/izo.hpp>
|
||||
|
||||
izo::dialog_options options;
|
||||
options.title = "Choose an image";
|
||||
options.filters = {{"Images", {"*.png", "*.jpg"}}};
|
||||
|
||||
auto result = izo::open_file(options);
|
||||
if (result) {
|
||||
auto selected_path = result.paths.front();
|
||||
} else if (result.status == izo::dialog_status::error) {
|
||||
// result.error_message contains the platform failure.
|
||||
if (auto selection = izo::open_file(options)) {
|
||||
izo::reveal_in_file_manager(selection.paths.front());
|
||||
}
|
||||
|
||||
auto config = izo::get_known_path(izo::known_path::config);
|
||||
|
||||
izo::process_options process;
|
||||
process.executable = "tool";
|
||||
process.arguments = {"--input", "asset.png"};
|
||||
if (auto launched = izo::launch_process(process)) {
|
||||
// launched.process_id identifies the new process.
|
||||
}
|
||||
```
|
||||
|
||||
Resource-owning APIs are move-only and clean themselves up:
|
||||
|
||||
```cpp
|
||||
std::string error;
|
||||
auto library = izo::load_dynamic_library("plugin.dll", &error);
|
||||
if (library) {
|
||||
void* entry = library.symbol("initialize_plugin", &error);
|
||||
}
|
||||
|
||||
auto watcher = izo::watch_directory(
|
||||
{{"assets"}, true},
|
||||
[](const izo::file_event& event) {
|
||||
// Handle added, removed, or modified files.
|
||||
},
|
||||
&error);
|
||||
```
|
||||
|
||||
Dialog calls block until dismissed. Directory callbacks run on the watcher's worker
|
||||
thread and must not destroy their own watcher.
|
||||
|
||||
52
tests/smoke.cpp
Normal file
52
tests/smoke.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <izo/izo.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
|
||||
int main() {
|
||||
std::string error;
|
||||
|
||||
assert(izo::set_environment_variable("IZO_SMOKE_TEST", "utf8-value", &error));
|
||||
const auto variable = izo::get_environment_variable("IZO_SMOKE_TEST");
|
||||
assert(variable && *variable == "utf8-value");
|
||||
assert(izo::unset_environment_variable("IZO_SMOKE_TEST", &error));
|
||||
|
||||
assert(!izo::get_known_path(izo::known_path::temporary, &error).empty());
|
||||
assert(!izo::get_known_path(izo::known_path::executable_directory, &error).empty());
|
||||
|
||||
const auto system = izo::get_system_info();
|
||||
assert(system.logical_cpu_count > 0);
|
||||
assert(system.total_memory_bytes > 0);
|
||||
assert(!system.os_name.empty());
|
||||
|
||||
const auto before = izo::monotonic_now();
|
||||
izo::sleep_for(std::chrono::milliseconds(2));
|
||||
assert(izo::monotonic_now() > before);
|
||||
|
||||
#ifdef _WIN32
|
||||
auto library = izo::load_dynamic_library("kernel32.dll", &error);
|
||||
assert(library && library.symbol("GetCurrentProcessId", &error));
|
||||
#else
|
||||
auto library = izo::load_dynamic_library("libc.so.6", &error);
|
||||
assert(library && library.symbol("getpid", &error));
|
||||
#endif
|
||||
|
||||
const auto directory = std::filesystem::temp_directory_path() / "izo-smoke-watch";
|
||||
std::filesystem::remove_all(directory);
|
||||
std::filesystem::create_directory(directory);
|
||||
std::atomic<bool> observed{false};
|
||||
auto watcher = izo::watch_directory(
|
||||
{directory, false, std::chrono::milliseconds(10)},
|
||||
[&](const izo::file_event& event) {
|
||||
if (event.change == izo::file_change::added && event.path.filename() == "created.txt")
|
||||
observed = true;
|
||||
}, &error);
|
||||
assert(watcher);
|
||||
izo::sleep_for(std::chrono::milliseconds(30));
|
||||
{ std::ofstream file(directory / "created.txt"); file << "test"; }
|
||||
for (int i = 0; i < 100 && !observed; ++i) izo::sleep_for(std::chrono::milliseconds(10));
|
||||
assert(observed);
|
||||
watcher.stop();
|
||||
std::filesystem::remove_all(directory);
|
||||
}
|
||||
Reference in New Issue
Block a user