FlatImage
A configurable Linux containerization system
Loading...
Searching...
No Matches
desktop.hpp
Go to the documentation of this file.
1
8
9#pragma once
10
11#include <filesystem>
12#include <print>
13#include <sstream>
14
16#include "../../db/desktop.hpp"
20#include "../../lib/image.hpp"
21#include "../../lib/env.hpp"
23#include "../../macro.hpp"
24#include "../../config.hpp"
25#include "icon.hpp"
26
36namespace ns_desktop
37{
38
39using IntegrationItem = ns_db::ns_desktop::IntegrationItem;
40
41namespace
42{
43
44// MIME type icon
45constexpr std::string_view const template_dir_mimetype = "icons/hicolor/{}x{}/mimetypes";
46constexpr std::string_view const template_file_mime = "application-flatimage_{}.png";
47// Application icon
48constexpr std::string_view const template_dir_apps = "icons/hicolor/{}x{}/apps";
49constexpr std::string_view const template_file_app = "flatimage_{}.png";
50// MIME type icon (scalable)
51constexpr std::string_view const template_dir_mime_scalable = "icons/hicolor/scalable/mimetypes";
52constexpr std::string_view const template_file_mime_scalable = "application-flatimage_{}.svg";
53// Application type icon (scalable)
54constexpr std::string_view const template_dir_apps_scalable = "icons/hicolor/scalable/apps";
55constexpr std::string_view const template_file_app_scalable = "flatimage_{}.svg";
56// PNG target image sizes
57constexpr const std::array<uint32_t, 9> arr_sizes {16,22,24,32,48,64,96,128,256};
58
59namespace fs = std::filesystem;
60
61
70[[nodiscard]] Value<std::pair<fs::path,fs::path>> get_path_file_icon_png(std::string_view name_app, uint32_t size)
71{
72 fs::path path_file_mime = Pop(ns_env::xdg_data_home<fs::path>())
73 / std::vformat(template_dir_mimetype, std::make_format_args(size, size))
74 / std::vformat(template_file_mime, std::make_format_args(name_app));
75 fs::path path_file_app = Pop(ns_env::xdg_data_home<fs::path>())
76 / std::vformat(template_dir_apps, std::make_format_args(size, size))
77 / std::vformat(template_file_app, std::make_format_args(name_app));
78 return std::make_pair(path_file_mime,path_file_app);
79}
80
88[[nodiscard]] Value<std::pair<fs::path,fs::path>> get_path_file_icon_svg(std::string_view name_app)
89{
90 fs::path path_file_mime = Pop(ns_env::xdg_data_home<fs::path>())
91 / template_dir_mime_scalable
92 / std::vformat(template_file_mime_scalable, std::make_format_args(name_app));
93 fs::path path_file_app = Pop(ns_env::xdg_data_home<fs::path>())
94 / template_dir_apps_scalable
95 / std::vformat(template_file_app_scalable, std::make_format_args(name_app));
96 return std::make_pair(path_file_mime,path_file_app);
97}
98
105{
106 auto xdg_data_home = Pop(ns_env::xdg_data_home<fs::path>());
107 return xdg_data_home / std::format("applications/flatimage-{}.desktop", desktop.get_name());
108}
109
116{
117 fs::path xdg_data_home = Pop(ns_env::xdg_data_home<fs::path>());
118 return xdg_data_home / std::format("mime/packages/flatimage-{}.xml", desktop.get_name());
119}
120
127{
128 fs::path xdg_data_home = Pop(ns_env::xdg_data_home<fs::path>());
129 return xdg_data_home / "mime/packages/flatimage.xml";
130}
131
141 , fs::path const& path_file_binary
142 , std::ostream& os)
143{
144 // Create desktop entry
145 os << "[Desktop Entry]" << '\n';
146 os << std::format("Name={}", desktop.get_name()) << '\n';
147 os << "Type=Application" << '\n';
148 os << std::format("Comment=FlatImage distribution of \"{}\"", desktop.get_name()) << '\n';
149 os << std::format(R"(Exec="{}" %F)", path_file_binary.string()) << '\n';
150 os << std::format("Icon=flatimage_{}", desktop.get_name()) << '\n';
151 os << std::format("MimeType=application/flatimage_{};", desktop.get_name()) << '\n';
152 os << std::format("Categories={};", ns_string::from_container(desktop.get_categories(), ';'));
153 return{};
154}
155
163[[nodiscard]] Value<void> integrate_desktop_entry(ns_db::ns_desktop::Desktop const& desktop, fs::path const& path_file_binary)
164{
165 // Create path to entry
166 fs::path path_file_desktop = Pop(get_path_file_desktop(desktop));
167 // Create parent directories for entry
168 Try(fs::create_directories(path_file_desktop.parent_path()));
169 // Open desktop file
170 std::ofstream file_desktop(path_file_desktop, std::ios::out | std::ios::trunc);
171 return_if(not file_desktop.is_open() , Error("E::Could not open desktop file {}", path_file_desktop));
172 // Generate entry
173 Pop(generate_desktop_entry(desktop, path_file_binary, file_desktop));
174 return {};
175}
176
183[[nodiscard]] inline bool is_update_mime_database(fs::path const& path_file_binary, fs::path const& path_entry_mimetype)
184{
185 // Update if file does not exist
186 return_if(not fs::exists(path_entry_mimetype), true, "D::Update mime due to missing source file");
187 // Update if can not open file
188 std::ifstream file_mimetype(path_entry_mimetype);
189 return_if(not file_mimetype.is_open(), true, "D::Update mime due to unaccessible source file for read");
190 // Update if file name has changed
191 std::string pattern = std::format(R"(pattern="{}")", path_file_binary.filename().string());
192 for (std::string line; std::getline(file_mimetype, line);)
193 {
194 return_if(line.contains(pattern), false, "D::Mime pattern file name checks");
195 } // for
196 return true;
197}
198
204[[nodiscard]] inline Value<void> update_mime_database()
205{
206 fs::path xdg_data_home = Pop(ns_env::xdg_data_home());
207 fs::path path_bin_mime = Pop(ns_env::search_path("update-mime-database"));
208 logger("I::Updating mime database '{}'", xdg_data_home);
209 Pop(ns_subprocess::Subprocess(path_bin_mime)
210 .with_args(xdg_data_home / "mime")
211 .with_streams(ns_subprocess::stream::null(), std::cout, std::cerr)
212 .spawn()->wait());
213 return {};
214}
215
220{
221 std::error_code ec;
222 fs::path path_file_xml_generic = Pop(get_path_file_mimetype_generic());
223 std::ofstream file_xml_generic(path_file_xml_generic, std::ios::out | std::ios::trunc);
224 return_if(not file_xml_generic.is_open(), Error("E::Could not open '{}'", path_file_xml_generic));
225 file_xml_generic << R"(<?xml version="1.0" encoding="UTF-8"?>)" << '\n';
226 file_xml_generic << R"(<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">)" << '\n';
227 file_xml_generic << R"( <mime-type type="application/flatimage">)" << '\n';
228 file_xml_generic << R"( <comment>FlatImage Application</comment>)" << '\n';
229 file_xml_generic << R"( <magic>)" << '\n';
230 file_xml_generic << R"( <match value="ELF" type="string" offset="1">)" << '\n';
231 file_xml_generic << R"( <match value="0x46" type="byte" offset="8">)" << '\n';
232 file_xml_generic << R"( <match value="0x49" type="byte" offset="9">)" << '\n';
233 file_xml_generic << R"( <match value="0x01" type="byte" offset="10"/>)" << '\n';
234 file_xml_generic << R"( </match>)" << '\n';
235 file_xml_generic << R"( </match>)" << '\n';
236 file_xml_generic << R"( </match>)" << '\n';
237 file_xml_generic << R"( </magic>)" << '\n';
238 file_xml_generic << R"( <glob weight="50" pattern="*.flatimage"/>)" << '\n';
239 file_xml_generic << R"( <sub-class-of type="application/x-executable"/>)" << '\n';
240 file_xml_generic << R"( <generic-icon name="application-flatimage"/>)" << '\n';
241 file_xml_generic << R"( </mime-type>)" << '\n';
242 file_xml_generic << R"(</mime-info>)" << '\n';
243 file_xml_generic.close();
244 return {};
245}
246
255 , fs::path const& path_file_binary
256 , std::ostream& os
257)
258{
259 os << R"(<?xml version="1.0" encoding="UTF-8"?>)" << '\n';
260 os << R"(<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">)" << '\n';
261 os << std::format(R"( <mime-type type="application/flatimage_{}">)", desktop.get_name()) << '\n';
262 os << R"( <comment>FlatImage Application</comment>)" << '\n';
263 os << std::format(R"( <glob weight="100" pattern="{}"/>)", path_file_binary.filename().string()) << '\n';
264 os << R"( <sub-class-of type="application/x-executable"/>)" << '\n';
265 os << R"( <generic-icon name="application-flatimage"/>)" << '\n';
266 os << R"( </mime-type>)" << '\n';
267 os << R"(</mime-info>)";
268 return {};
269}
270
278 , fs::path const& path_file_binary
279)
280{
281 // Get application specific mimetype location
282 fs::path path_file_xml = Pop(get_path_file_mimetype(desktop));
283 // Create parent directories
284 fs::path path_dir_xml = path_file_xml.parent_path();
285 Pop(ns_fs::create_directories(path_dir_xml)
286 , "E::Could not create upper mimetype directories: {}", path_dir_xml
287 );
288 // Check if should update mime database
289 if(is_update_mime_database(path_file_binary, path_file_xml) )
290 {
291 logger("I::Integrating mime database");
292 }
293 else
294 {
295 logger("D::Skipping mime database update");
296 return {};
297 }
298 // Create application mimetype file
299 std::ofstream file_xml(path_file_xml, std::ios::out | std::ios::trunc);
300 return_if(not file_xml.is_open(), Error("E::Could not open '{}'", path_file_xml));
301 Pop(generate_mime_database(desktop, path_file_binary, file_xml));
302 file_xml.close();
305 return {};
306}
307
314[[nodiscard]] inline Value<void> integrate_icons_svg(ns_db::ns_desktop::Desktop const& desktop, fs::path const& path_file_icon)
315{
316 // Path to mimetype icon
317 auto [path_icon_mimetype, path_icon_app] = Pop(get_path_file_icon_svg(desktop.get_name()));
318 Pop(ns_fs::create_directories(path_icon_mimetype.parent_path()));
319 Pop(ns_fs::create_directories(path_icon_app.parent_path()));
320 auto f_copy_icon = [](fs::path const& path_icon_src, fs::path const& path_icon_dst)
321 {
322 logger("D::Copy '{}' to '{}'", path_icon_src, path_icon_dst);
323 std::error_code ec;
324 if (not fs::exists(path_icon_dst, ec)
325 and not fs::copy_file(path_icon_src, path_icon_dst, ec))
326 {
327 logger("E::Could not copy file '{}' to '{}': '{}'"
328 , path_icon_src
329 , path_icon_dst
330 , (ec)? ec.message() : "Unknown error"
331 );
332 }
333 };
334 // Copy mimetype and app icons
335 f_copy_icon(path_file_icon, path_icon_mimetype);
336 f_copy_icon(path_file_icon, path_icon_app);
337 return {};
338}
339
346[[nodiscard]] inline Value<void> integrate_icons_png(ns_db::ns_desktop::Desktop const& desktop, fs::path const& path_file_icon)
347{
348 std::error_code ec;
349 auto f_create_parent = [](fs::path const& path_src) -> Value<void>
350 {
351 Pop(ns_fs::create_directories(path_src.parent_path()));
352 return {};
353 };
354 for(auto&& size : arr_sizes)
355 {
356 // Path to mimetype and application icons
357 auto [path_icon_mimetype,path_icon_app] = Pop(get_path_file_icon_png(desktop.get_name(), size));
358 Pop(f_create_parent(path_icon_mimetype));
359 Pop(f_create_parent(path_icon_app));
360 // Avoid overwrite
361 continue_if(fs::exists(path_icon_mimetype, ec));
362 // Copy icon with target widthXheight
363 Pop(ns_image::resize(path_file_icon, path_icon_mimetype, size, size));
364 // Duplicate icon to app directory
365 if (not fs::copy_file(path_icon_mimetype, path_icon_app, fs::copy_options::skip_existing, ec))
366 {
367 logger("E::Could not copy file '{}': '{}'", path_icon_app, ec.message());
368 }
369 }
370 return {};
371}
372
379{
380 auto f_write_icon = [](fs::path const& path_src) -> Value<void>
381 {
382 Pop(ns_fs::create_directories(path_src.parent_path()));
383 std::ofstream file_src(path_src, std::ios::out | std::ios::trunc);
384 return_if(not file_src.is_open(), Error("E::Failed to open file '{}'", path_src));
385 file_src << ns_icon::FLATIMAGE;
386 file_src.close();
387 return {};
388 };
389 fs::path path_dir_xdg = Pop(ns_env::xdg_data_home<fs::path>());
390 // Path to mimetype icon
391 fs::path path_icon_mime = path_dir_xdg / fs::path{template_dir_mime_scalable} / "application-flatimage.svg";
392 Pop(f_write_icon(path_icon_mime));
393 // Path to app icon
394 fs::path path_icon_app = path_dir_xdg / fs::path{template_dir_apps_scalable} / "flatimage.svg";
395 Pop(f_write_icon(path_icon_app));
396 return {};
397}
398
406[[nodiscard]] inline Value<void> integrate_icons(ns_config::FlatImage const& fim, ns_db::ns_desktop::Desktop const& desktop)
407{
408 std::error_code ec;
409 // Try to get a valid icon path to a png or svg file
410 fs::path path_file_icon = ({
411 fs::path path_file_icon_png = Pop(get_path_file_icon_png(desktop.get_name(), 64)).second;
412 fs::path path_file_icon_svg = Pop(get_path_file_icon_svg(desktop.get_name())).second;
413 fs::exists(path_file_icon_png, ec)? path_file_icon_png : path_file_icon_svg;
414 });
415 // Check for existing integration
416 if (fs::exists(path_file_icon, ec))
417 {
418 logger("D::Icons are integrated, found {}", path_file_icon.string());
419 return {};
420 }
421 return_if(ec, Error("E::Could not check if icon exists: {}", ec.message()));
422 // Read picture from flatimage binary
424 // Create temporary file to write icon to
425 auto path_file_tmp_icon = fim.path.dir.app / std::format("icon.{}", icon.m_ext);
426 // Write icon to temporary file
427 std::ofstream file_icon(path_file_tmp_icon, std::ios::out | std::ios::trunc);
428 return_if(not file_icon.is_open(), Error("E::Could not open temporary icon file for desktop integration"));
429 file_icon.write(icon.m_data, icon.m_size);
430 file_icon.close();
431 // Create icons
432 if ( path_file_tmp_icon.string().ends_with(".svg") )
433 {
434 Pop(integrate_icons_svg(desktop, path_file_tmp_icon));
435 }
436 else
437 {
438 Pop(integrate_icons_png(desktop, path_file_tmp_icon));
439 }
441 // Remove temporary file
442 fs::remove(path_file_tmp_icon, ec);
443 return_if(ec, Error("E::Could not remove temporary icon file: {}", ec.message()));
444 return {};
445}
446
447} // namespace
448
455[[nodiscard]] inline Value<void> integrate(ns_config::FlatImage const& fim)
456{
457 // Deserialize json from binary
458 auto str_raw_json = Pop(ns_reserved::ns_desktop::read(fim.path.bin.self)
459 , "E::Could not read desktop json from binary"
460 );
461 auto desktop = Pop(ns_db::ns_desktop::deserialize(str_raw_json)
462 , "D::Missing or misconfigured desktop integration"
463 );
464 logger("D::Json desktop data: {}", str_raw_json);
465
466 // Create desktop entry
467 if(desktop.get_integrations().contains(IntegrationItem::ENTRY))
468 {
469 logger("I::Integrating desktop entry");
470 Pop(integrate_desktop_entry(desktop, fim.path.bin.self));
471 }
472 // Create and update mime
473 if(desktop.get_integrations().contains(IntegrationItem::MIMETYPE))
474 {
475 logger("I::Integrating mime database");
476 Pop(integrate_mime_database(desktop, fim.path.bin.self));
477 }
478 // Create desktop icons
479 if(desktop.get_integrations().contains(IntegrationItem::ICON))
480 {
481 logger("I::Integrating desktop icons");
482 if(auto ret = integrate_icons(fim, desktop); not ret)
483 {
484 logger("D::Could not integrate icons: '{}'", ret.error());
485 }
486 }
487 // Check if should notify
488 if (fim.flags.is_notify)
489 {
490 // Get bash binary
491 fs::path path_file_binary_bash = Pop(ns_env::search_path("bash"));
492 // Get possible icon paths
493 fs::path path_file_icon = ({
494 fs::path path_file_icon_png = Pop(get_path_file_icon_png(desktop.get_name(), 64)).second;
495 fs::path path_file_icon_svg = Pop(get_path_file_icon_svg(desktop.get_name())).second;
496 Try(fs::exists(path_file_icon_png))? path_file_icon_png : path_file_icon_svg;
497 });
498 // Path to mimetype icon
499 using enum ns_subprocess::Stream;
500 ns_subprocess::Subprocess(path_file_binary_bash)
501 .with_args("-c", R"(notify-send "$@")", "--")
502 .with_args("-i", path_file_icon, std::format("Started '{}' FlatImage", desktop.get_name()))
503 .spawn()->wait().discard("E::Failed to send notification");
504 }
505 else
506 {
507 logger("D::Notify is disabled");
508 }
509
510 return {};
511}
512
523[[nodiscard]] inline Value<fs::path> setup_resolve_icon(std::string_view icon_path_or_url)
524{
525 std::string icon_str{icon_path_or_url};
526 bool is_url = icon_str.starts_with("http://") or icon_str.starts_with("https://");
527 // Check if is a local path
528 return_if (not is_url, fs::path{icon_str}, "D::Icon is a local file at '{}'", icon_str);
529 // Download remote icon
530 logger("I::Icon is an URL: {}", icon_str);
531 // Resolve extension from URL (after last dot)
532 std::string extension = ({
533 auto pos = icon_str.find_last_of('.');
534 return_if(pos == std::string::npos, Error("E::Could not get icon file extension from url"));
535 icon_str.substr(pos);
536 });
537 logger("D::Resolved extension from URL: '{}'", extension);
538 // Find wget binary
539 fs::path path_bin_wget = Pop(ns_env::search_path("wget"));
540 // Create temporary file path for downloaded icon
541 fs::path path_file_icon_downloaded = fs::temp_directory_path() / ("icon" + extension);
542 // Download the icon using wget
543 auto child = ns_subprocess::Subprocess(path_bin_wget)
544 .with_args(icon_str, "-O", path_file_icon_downloaded)
545 .with_stdio(ns_subprocess::Stream::Inherit)
546 .spawn();
547 return_if(not child, Error("E::Failed to spawn wget process"));
548 // Check return code
549 return_if (int exit_code = child->wait().value_or(-1); exit_code != 0
550 , Error("E::wget failed with exit code: {} for URL: {}", exit_code, icon_str);
551 );
552 logger("I::Successfully downloaded icon to: {}", path_file_icon_downloaded);
553 return path_file_icon_downloaded;
554}
555
563[[nodiscard]] inline Value<void> setup(ns_config::FlatImage const& fim, fs::path const& path_file_json_src)
564{
565 std::error_code ec;
566 // Create desktop struct with input json
567 std::ifstream file_json_src{path_file_json_src};
568 return_if(not file_json_src.is_open()
569 , Error("E::Failed to open file '{}' for desktop integration", path_file_json_src)
570 );
571 auto desktop = Pop(ns_db::ns_desktop::deserialize(file_json_src), "E::Failed to deserialize json");
572 return_if(desktop.get_name().contains('/'), Error("E::Application name cannot contain the '/' character"));
573 // Validate icon
574 auto icon_path_or_url = Pop(desktop.get_path_file_icon(), "E::Could not retrieve icon path field from json");
575 fs::path path_file_icon = Pop(setup_resolve_icon(icon_path_or_url.string()), "E::Failed to resolve icon path or URL");
576 std::string str_ext = (path_file_icon.extension() == ".svg")? "svg"
577 : (path_file_icon.extension() == ".png")? "png"
578 : (path_file_icon.extension() == ".jpg" or path_file_icon.extension() == ".jpeg")? "jpg"
579 : "";
580 return_if(str_ext.empty(), Error("E::Icon extension '{}' is not supported", path_file_icon.extension()));
581 // Read icon into memory
582 auto image_data = ({
583 std::streamsize size_file_icon = fs::file_size(path_file_icon, ec);
584 return_if(ec, Error("E::Could not get size of file '{}': {}", path_file_icon, ec.message()));
585 return_if(static_cast<uint64_t>(size_file_icon) >= ns_reserved::FIM_RESERVED_OFFSET_ICON_END - ns_reserved::FIM_RESERVED_OFFSET_ICON_BEGIN
586 , Error("E::File is too large, '{}' bytes", size_file_icon)
587 );
588 std::unique_ptr<char[]> ptr_data = std::make_unique<char[]>(size_file_icon);
589 std::streamsize bytes = Pop(ns_reserved::read(path_file_icon, 0, ptr_data.get(), size_file_icon));
590 return_if(bytes != size_file_icon
591 , Error("E::Icon read bytes '{}' do not match target size of '{}'", bytes, size_file_icon)
592 );
593 std::make_pair(std::move(ptr_data), bytes);
594 });
595 // Create icon struct
597 std::memset(icon.m_ext, 0, sizeof(icon.m_ext));
598 std::memcpy(icon.m_ext, str_ext.data(), std::min(str_ext.size(), sizeof(icon.m_ext)-1));
599 std::memcpy(icon.m_data, image_data.first.get(), image_data.second);
600 icon.m_size = image_data.second;
601 // Write icon struct to the flatimage binary
602 Pop(ns_reserved::ns_icon::write(fim.path.bin.self, icon), "E::Could not write image data");
603 // Write json to flatimage binary, excluding the input icon path
604 auto str_raw_json = Pop(ns_db::ns_desktop::serialize(desktop), "E::Failed to serialize desktop integration" );
605 auto db = Pop(ns_db::from_string(str_raw_json), "E::Could not parse serialized json source");
606 return_if(not db.erase("icon"), Error("E::Could not erase icon field"));
607 Pop(ns_reserved::ns_desktop::write(fim.path.bin.self, Pop(db.dump())));
608 // Print written json
609 std::println("{}", Pop(db.dump()));
610 return {};
611}
612
620[[nodiscard]] inline Value<void> enable(ns_config::FlatImage const& fim, std::set<IntegrationItem> set_integrations)
621{
622 // Read json
623 auto str_json = Pop(ns_reserved::ns_desktop::read(fim.path.bin.self));
624 // Deserialize json
625 auto desktop = Pop(ns_db::ns_desktop::deserialize(str_json));
626 // Update integrations value
627 desktop.set_integrations(set_integrations);
628 // Print to standard output
629 for(auto const& str_integration : set_integrations)
630 {
631 std::println("{}", std::string{str_integration});
632 }
633 // Serialize json
634 auto str_raw_json = Pop(ns_db::ns_desktop::serialize(desktop));
635 // Write json
636 Pop(ns_reserved::ns_desktop::write(fim.path.bin.self, str_raw_json));
637 return {};
638}
639
646[[nodiscard]] inline Value<void> clean(ns_config::FlatImage const& fim)
647{
648 // Read json
649 auto str_json = Pop(ns_reserved::ns_desktop::read(fim.path.bin.self), "E::Failed to read from reserved space");
650 // Deserialize json
651 auto desktop = Pop(ns_db::ns_desktop::deserialize(str_json), "E::Failed to de-serialize desktop integration");
652 // Get integrations
653 auto integrations = desktop.get_integrations();
654 auto f_try_erase = [](fs::path const& path)
655 {
656 std::error_code ec;
657 fs::remove(path, ec);
658 if(ec)
659 {
660 logger("E::Could not remove '{}': {}", path, ec.message());
661 }
662 else
663 {
664 logger("I::Removed file '{}'", path);
665 }
666 };
667 // Remove entry
668 if( integrations.contains(ns_db::ns_desktop::IntegrationItem::ENTRY))
669 {
670 f_try_erase(Pop(get_path_file_desktop(desktop)));
671 }
672 // Remove mimetype database
673 if(integrations.contains(ns_db::ns_desktop::IntegrationItem::MIMETYPE))
674 {
675 f_try_erase(Pop(get_path_file_mimetype(desktop)));
676 Pop(update_mime_database());
677 }
678 // Remove icons
679 if(integrations.contains(ns_db::ns_desktop::IntegrationItem::ICON))
680 {
682 if(std::string_view(icon.m_ext) == "png")
683 {
684 for(auto size : arr_sizes)
685 {
686 auto [path_icon_mime,path_icon_app] = Pop(get_path_file_icon_png(desktop.get_name(), size));
687 f_try_erase(path_icon_mime);
688 f_try_erase(path_icon_app);
689 }
690 }
691 else
692 {
693 auto [path_icon_mime,path_icon_app] = Pop(get_path_file_icon_svg(desktop.get_name()));
694 f_try_erase(path_icon_mime);
695 f_try_erase(path_icon_app);
696 }
697 }
698 return {};
699}
700
708[[nodiscard]] inline Value<void> dump_icon(ns_config::FlatImage const& fim, fs::path path_file_dst)
709{
710 // Read icon data
712 // Make sure it has valid data
713 return_if(std::all_of(icon.m_data, icon.m_data+sizeof(icon.m_data), [](char c){ return c == 0; })
714 , Error("E::Empty icon data");
715 );
716 // Get extension
717 std::string_view ext = icon.m_ext;
718 // Check if extension is valid
719 return_if(ext != "png" and ext != "svg", Error("E::Invalid file extension saved in desktop configuration"));
720 // Append extension to output destination file
721 if(not path_file_dst.extension().string().ends_with(ext))
722 {
723 path_file_dst = path_file_dst.parent_path() / (path_file_dst.filename().string() + std::format(".{}", ext));
724 }
725 // Open output file
726 std::fstream file_dst(path_file_dst, std::ios::out | std::ios::trunc);
727 return_if(not file_dst.is_open(), Error("E::Could not open output file '{}'", path_file_dst));
728 // Write data to output file
729 file_dst.write(icon.m_data, icon.m_size);
730 // Check bytes written
731 return_if(not file_dst
732 , Error("E::Could not write all '{}' bytes to output file", icon.m_size)
733 );
734 return {};
735}
736
743[[nodiscard]] inline Value<std::string> dump_entry(ns_config::FlatImage const& fim)
744{
745 // Get desktop object
746 auto desktop = Pop(ns_db::ns_desktop::deserialize(
748 ));
749 // Generate desktop entry
750 std::stringstream ss;
751 Pop(generate_desktop_entry(desktop, fim.path.bin.self, ss));
752 // Dump contents
753 return ss.str();
754}
755
763{
764 // Get desktop object
765 auto desktop = Pop(ns_db::ns_desktop::deserialize(
767 ));
768 // Generate mime database
769 std::stringstream ss;
770 Pop(generate_mime_database(desktop, fim.path.bin.self, ss));
771 // Dump contents
772 return ss.str();
773}
774
775} // namespace ns_desktop
776
777/* vim: set expandtab fdm=marker ts=2 sw=2 tw=100 et :*/
std::unique_ptr< Child > spawn()
Spawns (forks) the child process and begins execution.
Subprocess & with_stdio(Stream mode)
Sets the stdio redirection mode for the child process.
Subprocess & with_args(Args &&... args)
Arguments forwarded as the process' arguments.
FlatImage configuration object.
Defines a class that manages FlatImage's desktop integration.
Enhanced error handling framework built on std::expected.
A library for operations on image files.
A library for manipulating environment variables.
#define logger(fmt,...)
Compile-time log level dispatch macro with automatic location capture.
Definition log.hpp:682
Simplified macros for common control flow patterns with optional logging.
Value< std::string > serialize(Desktop const &desktop) noexcept
Serializes a Desktop class into a json string.
Definition desktop.hpp:109
Value< Desktop > deserialize(std::string_view str_raw_json) noexcept
Deserializes a string into a Desktop class.
Definition desktop.hpp:69
Value< Db > from_string(S &&s)
Parses a JSON string and creates a Db object.
Definition db.hpp:496
Value< void > generate_mime_database(ns_db::ns_desktop::Desktop const &desktop, fs::path const &path_file_binary, std::ostream &os)
Generates the flatimage app specific mime package.
Definition desktop.hpp:254
Value< std::pair< fs::path, fs::path > > get_path_file_icon_png(std::string_view name_app, uint32_t size)
Constructs the path to the png icon file.
Definition desktop.hpp:70
Value< std::pair< fs::path, fs::path > > get_path_file_icon_svg(std::string_view name_app)
Constructs the path to the svg icon file.
Definition desktop.hpp:88
Value< void > integrate_icons_svg(ns_db::ns_desktop::Desktop const &desktop, fs::path const &path_file_icon)
Integrates an svg icon for the flatimage mimetype.
Definition desktop.hpp:314
Value< void > integrate_mime_database(ns_db::ns_desktop::Desktop const &desktop, fs::path const &path_file_binary)
Generates the flatimage app specific mime package.
Definition desktop.hpp:277
Value< void > integrate_icons(ns_config::FlatImage const &fim, ns_db::ns_desktop::Desktop const &desktop)
Integrate flatimage icons in the current $HOME directory.
Definition desktop.hpp:406
Value< void > integrate_desktop_entry(ns_db::ns_desktop::Desktop const &desktop, fs::path const &path_file_binary)
Integrates the desktop entry '.desktop'.
Definition desktop.hpp:163
Value< fs::path > get_path_file_mimetype(ns_db::ns_desktop::Desktop const &desktop)
Get the file path to the application specific mimetype file.
Definition desktop.hpp:115
Value< void > generate_desktop_entry(ns_db::ns_desktop::Desktop const &desktop, fs::path const &path_file_binary, std::ostream &os)
Generates the desktop entry.
Definition desktop.hpp:140
Value< void > update_mime_database()
Runs update-mime-database on the current XDG_DATA_HOME diretory.
Definition desktop.hpp:204
Value< fs::path > get_path_file_mimetype_generic()
Get the file path to the generic mimetype file.
Definition desktop.hpp:126
bool is_update_mime_database(fs::path const &path_file_binary, fs::path const &path_entry_mimetype)
Checks if the mime database should be updated based on <glob pattern=
Definition desktop.hpp:183
Value< void > integrate_icons_png(ns_db::ns_desktop::Desktop const &desktop, fs::path const &path_file_icon)
Integrates PNG icons for the flatimage mimetype.
Definition desktop.hpp:346
Value< fs::path > get_path_file_desktop(ns_db::ns_desktop::Desktop const &desktop)
Get the file path to the desktop entry.
Definition desktop.hpp:104
Value< void > integrate_mime_database_generic()
Generates the flatimage generic mime package.
Definition desktop.hpp:219
Value< void > integrate_icon_flatimage()
Integrates the svg icon for the 'flatimage' mimetype.
Definition desktop.hpp:378
Desktop integration command implementation.
Value< void > enable(ns_config::FlatImage const &fim, std::set< IntegrationItem > set_integrations)
Enables desktop integration for FlatImage.
Definition desktop.hpp:620
Value< std::string > dump_entry(ns_config::FlatImage const &fim)
Dumps the desktop entry if integration is configured.
Definition desktop.hpp:743
Value< void > clean(ns_config::FlatImage const &fim)
Cleans desktop integration files.
Definition desktop.hpp:646
Value< void > setup(ns_config::FlatImage const &fim, fs::path const &path_file_json_src)
Setup desktop integration in FlatImage.
Definition desktop.hpp:563
Value< std::string > dump_mimetype(ns_config::FlatImage const &fim)
Dumps the application mime type file if integration is configured.
Definition desktop.hpp:762
Value< void > dump_icon(ns_config::FlatImage const &fim, fs::path path_file_dst)
Dumps the png or svg icon data to a file.
Definition desktop.hpp:708
Value< fs::path > setup_resolve_icon(std::string_view icon_path_or_url)
Resolves icon path or URL to a local file path.
Definition desktop.hpp:523
Value< void > integrate(ns_config::FlatImage const &fim)
Integrates flatimage desktop data in current system.
Definition desktop.hpp:455
Value< fs::path > search_path(std::string const &query)
Search the directories in the PATH variable for the given input file name.
Definition env.hpp:150
Value< T > xdg_data_home() noexcept
Returns or computes the value of XDG_DATA_HOME.
Definition env.hpp:135
Value< fs::path > create_directories(fs::path const &p)
Creates directories recursively.
Value< void > resize(fs::path const &path_file_src, fs::path const &path_file_dst, uint32_t width, uint32_t height)
Resizes an input image to the specified width and height.
Definition image.hpp:115
Value< void > write(fs::path const &path_file_binary, std::string_view const &json)
Writes the desktop json string to the target binary.
Definition desktop.hpp:44
Value< std::string > read(fs::path const &path_file_binary)
Reads the desktop json string from the target binary.
Definition desktop.hpp:64
Value< Icon > read(fs::path const &path_file_binary)
Reads the Icon struct from the target binary.
Definition icon.hpp:104
Value< void > write(fs::path const &path_file_binary, Icon const &icon)
Writes the Icon struct to the target binary.
Definition icon.hpp:84
Value< std::streamsize > read(fs::path const &path_file_binary, uint64_t offset, char *data, uint64_t length) noexcept
Reads data from a file in binary format.
Definition reserved.hpp:172
std::string from_container(T &&t, std::optional< char > sep=std::nullopt) noexcept
Converts a container into a string if it has string convertible elements.
Definition string.hpp:140
std::fstream & null()
Redirects to /dev/null (silent)
Stream
Stream redirection modes for child process stdio.
Contains the SVG data of the FlatImage icon.
Manages the desktop reserved space.
Manages the icon reserved space.
Filesystem helpers.
Enhanced expected type with integrated logging capabilities.
Definition expected.hpp:44
bool is_notify
Show desktop notifications? Stored in reserved space.
Definition config.hpp:484
Main FlatImage configuration object.
Definition config.hpp:519
Path path
Directory, file, and binary paths.
Definition config.hpp:525
Flags flags
Runtime feature flags.
Definition config.hpp:522
fs::path const self
Path to the FlatImage binary itself.
Definition config.hpp:228
fs::path const app
Application directory (versioned by commit/timestamp)
Definition config.hpp:152
Stores icon data in reserved space.
Definition icon.hpp:45
A library to spawn sub-processes in linux.