sculpt: generate panorama config

If the fb driver is configured to use discrete displays, sculpt now
automatically generates a panorama configuration for nitpicker that
shows all displays side by side. The effective nitpicker config is
now located at config/managed/nitpicker.

The automatism takes effect only when nitpicker's <capture> node
in config/nitpicker is empty. Hence, the managed panorama can be
overridden by a manually managed panorama.

Issue #5286
This commit is contained in:
Norman Feske 2024-10-25 18:08:31 +02:00 committed by Christian Helmuth
parent 8ed87dae71
commit 01c96cf537
5 changed files with 149 additions and 6 deletions

View File

@ -483,7 +483,7 @@ install_config {
</provides>
<route>
<service name="ROM" label="config">
<child name="config_fs_rom" label="nitpicker"/> </service>
<child name="config_fs_rom" label="managed/nitpicker"/> </service>
<service name="ROM" label="focus">
<child name="nit_focus"/> </service>
<service name="Report" label="panorama">
@ -848,7 +848,7 @@ set fd [open [managed_config_path depot_query] w]
puts $fd "<query/>"
close $fd
foreach config { fonts wifi runtime event_filter system } {
foreach config { fonts wifi runtime event_filter system nitpicker } {
set ingredient [single_ingredient $config "default"]
if {$ingredient != ""} {
set from [ingredient_path $config $ingredient]

View File

@ -231,6 +231,8 @@
<child name="config_fs_report" label="managed -> nic_router"/> </service>
<service name="Report" label="fb_config">
<child name="config_fs_report" label="managed -> fb"/> </service>
<service name="Report" label="nitpicker_config">
<child name="config_fs_report" label="managed -> nitpicker"/> </service>
<service name="Report" label="usb_config">
<child name="config_fs_report" label="managed -> usb"/> </service>
<service name="Report" label="system_config">

View File

@ -41,6 +41,7 @@
#include <model/screensaver.h>
#include <model/system_state.h>
#include <model/fb_config.h>
#include <model/panorama_config.h>
#include <view/download_status_widget.h>
#include <view/popup_dialog.h>
#include <view/panel_dialog.h>
@ -1609,6 +1610,32 @@ struct Sculpt::Main : Input_event_handler,
** Display driver configuration **
**********************************/
Managed_config<Main> _nitpicker_config {
_env, "config", "nitpicker", *this, &Main::_handle_nitpicker_config };
void _handle_nitpicker_config(Xml_node const &node)
{
_nitpicker_config.generate([&] (Xml_generator &xml) {
copy_attributes(xml, node);
node.for_each_sub_node([&] (Xml_node const &sub_node) {
if (sub_node.has_type("capture") && sub_node.num_sub_nodes() == 0) {
xml.node("capture", [&] {
/* generate panorama of fb-driver sessions */
Panorama_config(_fb_config).gen_policy_entries(xml);
/* default policy for capture applications like screenshot */
xml.node("default-policy", [&] { });
});
} else {
copy_node(xml, sub_node, { 5 });
}
});
});
}
Panorama_config _panorama_config { };
Fb_connectors _fb_connectors { };
Rom_handler<Main> _manual_fb_handler { _env, "config -> fb", *this, &Main::_handle_manual_fb };
@ -1621,12 +1648,16 @@ struct Sculpt::Main : Input_event_handler,
{
_managed_fb_reporter.generate([&] (Xml_generator &xml) {
_fb_config.generate_managed_fb(xml); });
/* update nitpicker config if the new fb config affects the panorama */
Panorama_config const orig = _panorama_config;
_panorama_config = Panorama_config(_fb_config);
if (orig != Panorama_config(_fb_config))
_nitpicker_config.trigger_update();
}
void _handle_manual_fb(Xml_node const &node)
{
log("_handle_manual_fb: ", node);
_fb_config = { };
_fb_config.import_manual_config(node);
_fb_config.apply_connectors(_fb_connectors);
@ -1734,6 +1765,7 @@ struct Sculpt::Main : Input_event_handler,
_gui.input.sigh(_input_handler);
_gui.info_sigh(_gui_mode_handler);
_handle_gui_mode();
_nitpicker_config.trigger_update();
/*
* Generate initial configurations

View File

@ -326,7 +326,7 @@ struct Sculpt::Fb_config
struct Merge_info { Entry::Name name; Area px; };
void _with_merge_info(auto const &fn) const
void with_merge_info(auto const &fn) const
{
Merge_info info { };
@ -354,7 +354,7 @@ struct Sculpt::Fb_config
void _gen_merge_node(Xml_generator &xml) const
{
_with_merge_info([&] (Merge_info const &info) {
with_merge_info([&] (Merge_info const &info) {
xml.node("merge", [&] {
xml.attribute("width", info.px.w);
xml.attribute("height", info.px.h);
@ -388,6 +388,15 @@ struct Sculpt::Fb_config
connectors.with_connector(entry.name, fn);
}
void for_each_discrete_entry(auto const &fn) const
{
for (unsigned i = _num_merged; i < MAX_ENTRIES; i++) {
Entry const &entry = _entries[i];
if (entry.defined && entry.present)
fn(entry);
}
}
unsigned num_present_merged() const
{
unsigned count = 0;

View File

@ -0,0 +1,100 @@
/*
* \brief Representation of nitpicker's <capture> configuration
* \author Norman Feske
* \date 2024-10-25
*/
/*
* Copyright (C) 2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _MODEL__PANORAMA_CONFIG_H_
#define _MODEL__PANORAMA_CONFIG_H_
#include <types.h>
#include <model/fb_config.h>
namespace Sculpt { struct Panorama_config; };
struct Sculpt::Panorama_config
{
struct Entry
{
using Name = Fb_config::Entry::Name;
Name name;
Rect rect;
bool operator != (Entry const &other) const
{
return (name != other.name) || (rect != other.rect);
}
void gen_policy(Xml_generator &xml) const
{
xml.node("policy", [&] {
xml.attribute("label_suffix", name);
xml.attribute("xpos", rect.x1());
xml.attribute("ypos", rect.y1());
xml.attribute("width", rect.w());
xml.attribute("height", rect.h());
});
}
};
static constexpr unsigned MAX_ENTRIES = 16;
Entry _entries[MAX_ENTRIES] { };
unsigned _num_entries = 0;
Panorama_config() { };
Panorama_config(Fb_config const &fb_config)
{
int xpos = 0;
auto append = [&] (auto const &name, auto const area)
{
if (_num_entries == MAX_ENTRIES)
return;
_entries[_num_entries] = Entry {
.name = name,
.rect = { .at = { .x = xpos, .y = 0 }, .area = area } };
_num_entries++;
xpos += area.w;
};
fb_config.with_merge_info([&] (Fb_config::Merge_info const &info) {
append(info.name, info.px); });
fb_config.for_each_discrete_entry([&] (Fb_config::Entry const &entry) {
append(entry.name, entry.mode_attr.px); });
}
void gen_policy_entries(Xml_generator &xml) const
{
for (unsigned i = 0; i < _num_entries; i++)
_entries[i].gen_policy(xml);
}
bool operator != (Panorama_config const &other) const
{
if (other._num_entries != _num_entries)
return true;
for (unsigned i = 0; i < _num_entries; i++)
if (other._entries[i] != _entries[i])
return true;
return false;
}
};
#endif /* _MODEL__PANORAMA_CONFIG_H_ */