mirror of
https://github.com/genodelabs/genode.git
synced 2025-03-03 13:14:45 +00:00
window_layouter: assignment of screens to displays
This patch enhances the window layouter with the notion of displays and the assignment of screens to displays. Issue #5390
This commit is contained in:
parent
21acbed65b
commit
b3d99960e7
@ -2,15 +2,19 @@
|
||||
<report rules="yes"/>
|
||||
|
||||
<rules>
|
||||
<screen name="screen_1"/>
|
||||
<screen name="screen_2"/>
|
||||
<screen name="screen_3"/>
|
||||
<screen name="screen_4"/>
|
||||
<screen name="screen_5"/>
|
||||
<screen name="screen_6"/>
|
||||
<screen name="screen_7"/>
|
||||
<screen name="screen_8"/>
|
||||
<screen name="screen_9"/>
|
||||
<display name="primary"/>
|
||||
<display name="secondary"/>
|
||||
<display name="ternary"/>
|
||||
|
||||
<screen name="screen_1" display="primary"/>
|
||||
<screen name="screen_2" display="primary"/>
|
||||
<screen name="screen_3" display="primary"/>
|
||||
<screen name="screen_4" display="secondary"/>
|
||||
<screen name="screen_5" display="secondary"/>
|
||||
<screen name="screen_6" display="secondary"/>
|
||||
<screen name="screen_7" display="ternary"/>
|
||||
<screen name="screen_8" display="ternary"/>
|
||||
<screen name="screen_9" display="ternary"/>
|
||||
<screen name="screen_0"/>
|
||||
<assign label_prefix="" target="screen_1" xpos="any" ypos="any"/>
|
||||
</rules>
|
||||
|
@ -2,15 +2,19 @@
|
||||
<report rules="yes"/>
|
||||
|
||||
<rules>
|
||||
<screen name="screen_1"/>
|
||||
<screen name="screen_2"/>
|
||||
<screen name="screen_3"/>
|
||||
<screen name="screen_4"/>
|
||||
<screen name="screen_5"/>
|
||||
<screen name="screen_6"/>
|
||||
<screen name="screen_7"/>
|
||||
<screen name="screen_8"/>
|
||||
<screen name="screen_9"/>
|
||||
<display name="primary"/>
|
||||
<display name="secondary"/>
|
||||
<display name="ternary"/>
|
||||
|
||||
<screen name="screen_1" display="primary"/>
|
||||
<screen name="screen_2" display="primary"/>
|
||||
<screen name="screen_3" display="primary"/>
|
||||
<screen name="screen_4" display="secondary"/>
|
||||
<screen name="screen_5" display="secondary"/>
|
||||
<screen name="screen_6" display="secondary"/>
|
||||
<screen name="screen_7" display="ternary"/>
|
||||
<screen name="screen_8" display="ternary"/>
|
||||
<screen name="screen_9" display="ternary"/>
|
||||
<screen name="screen_0"/>
|
||||
<assign label_prefix="" target="screen_1" xpos="any" ypos="any"/>
|
||||
</rules>
|
||||
|
@ -101,6 +101,55 @@ or unmaximizing a window, the 'maximized' attribute of its '<assign>' rule is
|
||||
toggled.
|
||||
|
||||
|
||||
Multi-monitor support
|
||||
---------------------
|
||||
|
||||
The layouter rules can host any number of display declarations as follows.
|
||||
|
||||
! <display name="primary"/>
|
||||
|
||||
Optional attributes 'xpos', 'ypos', 'width', and 'height' can be specified to
|
||||
assign a specific rectangle of the panorama to the display. Otherwise, the
|
||||
window layouter applies the following policy. The captured rectangles present
|
||||
in the panorama are assigned to displays in left to right order. This gives the
|
||||
opportunity to assign the notion of a "primary" or "secondary" display to
|
||||
different parts of the panorama by the mere order of '<display>' nodes. If
|
||||
more displays are declared than present, all unassigned displays will refer to
|
||||
the left-most captured rectangle of the panorama.
|
||||
|
||||
To get more precise control over the assignment of captured areas to displays,
|
||||
a display node can host any number of '<capture>' sub nodes that are matched
|
||||
against the captured areas present within the panorama. The panorama areas are
|
||||
named after the labels of capture clients (i.e., display drivers) present at
|
||||
the nitpicker GUI server. The matching can be expressed via the attributes
|
||||
'label', 'label_prefix', and 'label_suffix'. The first match applies.
|
||||
E.g., the following configuration may be useful for a laptop that is sometimes
|
||||
connected to an HDMI display at work or a Display-Port display at home.
|
||||
|
||||
! <display name="primary">
|
||||
! <capture label_suffix="HDMI-1"/>
|
||||
! <capture label_suffix="DP-2"/>
|
||||
! <capture label_suffix="eDP-1"/>
|
||||
! </display>
|
||||
! <display name="secondary">
|
||||
! <capture label_suffix="eDP-1"/>
|
||||
! </display>
|
||||
|
||||
When neither the HDMI-1 display nor the DP-2 display is present, the laptop's
|
||||
internal eDP display is used as both primary and secondary display. Once an
|
||||
external display is connected, the external display acts as primary display
|
||||
while the laptop's internal display takes the role of the secondary display.
|
||||
|
||||
Once declared, the display names can be specified as 'display' attribute to
|
||||
'<screen>' nodes, thereby assigning virtual desktops to displays. Screens
|
||||
referring to the same portion of the panorama are organized as a stack
|
||||
where only the top-most screen is visible at a time. As each display has
|
||||
its own distinct stack of screens, one screen cannot be visible at multiple
|
||||
displays. To mirror the same content on multiple displays, it is best to
|
||||
leverage the '<merge>' feature of the display driver. Should a '<screen>' lack
|
||||
a valid display attribute, it spans the entire panorama.
|
||||
|
||||
|
||||
Keyboard shortcuts
|
||||
------------------
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
#define _ASSIGN_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/list_model.h>
|
||||
#include <base/registry.h>
|
||||
#include <os/buffered_xml.h>
|
||||
|
||||
@ -108,13 +107,17 @@ class Window_layouter::Assign : public List_model<Assign>::Element
|
||||
/**
|
||||
* Calculate window geometry
|
||||
*/
|
||||
Rect window_geometry(unsigned win_id, Area client_size, Rect target_geometry,
|
||||
Rect window_geometry(unsigned win_id, Area client_size, Area target_size,
|
||||
Decorator_margins const &decorator_margins) const
|
||||
{
|
||||
if (!_pos_defined)
|
||||
return target_geometry;
|
||||
return { .at = { }, .area = target_size };
|
||||
|
||||
Point const any_pos(150*win_id % 800, 30 + (100*win_id % 500));
|
||||
/* try to place new window such that it fits the target area */
|
||||
unsigned const max_x = max(1, int(target_size.w) - int(client_size.w)),
|
||||
max_y = max(1, int(target_size.h) - int(client_size.h));
|
||||
|
||||
Point const any_pos(150*win_id % max_x, 30 + (100*win_id % max_y));
|
||||
|
||||
Point const pos(_xpos_any ? any_pos.x : _pos.x,
|
||||
_ypos_any ? any_pos.y : _pos.y);
|
||||
@ -122,7 +125,7 @@ class Window_layouter::Assign : public List_model<Assign>::Element
|
||||
Rect const inner(pos, _size_defined ? _size : client_size);
|
||||
Rect const outer = decorator_margins.outer_geometry(inner);
|
||||
|
||||
return Rect(outer.p1() + target_geometry.p1(), outer.area);
|
||||
return Rect(outer.p1(), outer.area);
|
||||
}
|
||||
|
||||
bool maximized() const { return _maximized; }
|
||||
|
166
repos/gems/src/app/window_layouter/display_list.h
Normal file
166
repos/gems/src/app/window_layouter/display_list.h
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* \brief List of displays
|
||||
* \author Norman Feske
|
||||
* \date 2024-11-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 _DISPLAY_LIST_H_
|
||||
#define _DISPLAY_LIST_H_
|
||||
|
||||
/* local includes */
|
||||
#include <panorama.h>
|
||||
|
||||
namespace Window_layouter { class Display; }
|
||||
|
||||
|
||||
struct Window_layouter::Display : List_model<Display>::Element
|
||||
{
|
||||
Name const name;
|
||||
|
||||
struct Attr
|
||||
{
|
||||
Rect rect; /* within panorama */
|
||||
bool occupied; /* true if occupied by a screen */
|
||||
|
||||
} attr { };
|
||||
|
||||
Display(Name const &name) : name(name) { }
|
||||
|
||||
/**
|
||||
* List_model::Element
|
||||
*/
|
||||
void update(Panorama const &panorama, Xml_node const &node)
|
||||
{
|
||||
/* import explicitly configured panorama position */
|
||||
attr.rect = Rect::from_xml(node);
|
||||
|
||||
/* assign panorama rect according to matching display <capture> policy */
|
||||
node.for_each_sub_node("capture", [&] (Xml_node const &policy) {
|
||||
if (!attr.rect.valid())
|
||||
panorama.with_matching_capture_rect(policy, [&] (Rect r) {
|
||||
attr.rect = r; }); });
|
||||
}
|
||||
|
||||
/**
|
||||
* List_model::Element
|
||||
*/
|
||||
bool matches(Xml_node node) const
|
||||
{
|
||||
return name_from_xml(node) == name;
|
||||
}
|
||||
|
||||
/**
|
||||
* List_model::Element
|
||||
*/
|
||||
static bool type_matches(Xml_node const &node)
|
||||
{
|
||||
return node.has_type("display");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace Window_layouter { class Display_list; }
|
||||
|
||||
|
||||
class Window_layouter::Display_list : Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
Allocator &_alloc;
|
||||
|
||||
List_model<Display> _displays { };
|
||||
|
||||
Display::Attr _panorama_attr { }; /* fallback used if no display declared */
|
||||
|
||||
void _update_from_xml(Xml_node const &node, auto const &update_fn)
|
||||
{
|
||||
_displays.update_from_xml(node,
|
||||
|
||||
[&] (Xml_node const &node) -> Display & {
|
||||
return *new (_alloc) Display(name_from_xml(node)); },
|
||||
|
||||
[&] (Display &display) { destroy(_alloc, &display); },
|
||||
|
||||
update_fn
|
||||
);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Display_list(Allocator &alloc) : _alloc(alloc) { }
|
||||
|
||||
~Display_list()
|
||||
{
|
||||
_update_from_xml(Xml_node("<empty/>"), [&] (auto &, auto &) { });
|
||||
}
|
||||
|
||||
void update_from_xml(Panorama const &panorama, Xml_node const &node)
|
||||
{
|
||||
_panorama_attr.rect = panorama.rect;
|
||||
|
||||
/* import display definitions and their panoramic positions */
|
||||
_update_from_xml(node, [&] (Display &display, Xml_node const &node) {
|
||||
display.update(panorama, node); });
|
||||
|
||||
/* assign remaining unpositioned displays from left to right */
|
||||
int min_x = 0;
|
||||
_displays.for_each([&] (Display &display) {
|
||||
if (!display.attr.rect.valid())
|
||||
panorama.with_leftmost_captured_rect(min_x, [&] (Rect r) {
|
||||
display.attr.rect = r;
|
||||
min_x = r.x1() + 1; }); });
|
||||
|
||||
/* assign still unpositioned displays to leftmost captured rect */
|
||||
_displays.for_each([&] (Display &display) {
|
||||
if (!display.attr.rect.valid())
|
||||
panorama.with_leftmost_captured_rect(0, [&] (Rect r) {
|
||||
display.attr.rect = r; }); });
|
||||
|
||||
/* if nothing is captured assign the total panorama to the display */
|
||||
_displays.for_each([&] (Display &display) {
|
||||
if (!display.attr.rect.valid())
|
||||
display.attr.rect = panorama.rect; });
|
||||
}
|
||||
|
||||
/**
|
||||
* Call 'fn' with the panorama rectangle of the display named 'name'
|
||||
*/
|
||||
void with_display_attr(Name const &name, auto const &fn)
|
||||
{
|
||||
bool done = false;
|
||||
_displays.for_each([&] (Display &display) {
|
||||
if (!done && display.name == name) {
|
||||
fn(display.attr);
|
||||
done = true; } });
|
||||
|
||||
if (!done)
|
||||
fn(_panorama_attr);
|
||||
}
|
||||
|
||||
void mark_as_occupied(Rect const rect)
|
||||
{
|
||||
_displays.for_each([&] (Display &display) {
|
||||
if (rect == display.attr.rect)
|
||||
display.attr.occupied = true; });
|
||||
|
||||
if (rect == _panorama_attr.rect)
|
||||
_panorama_attr.occupied = true;
|
||||
}
|
||||
|
||||
void reset_occupied_flags()
|
||||
{
|
||||
_panorama_attr.occupied = false;
|
||||
_displays.for_each([&] (Display &display) {
|
||||
display.attr.occupied = false; });
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _DISPLAY_LIST_H_ */
|
@ -57,7 +57,7 @@ struct Window_layouter::Main : Operations,
|
||||
|
||||
Heap _heap { _env.ram(), _env.rm() };
|
||||
|
||||
Area _screen_size { };
|
||||
Display_list _display_list { _heap };
|
||||
|
||||
unsigned _to_front_cnt = 1;
|
||||
|
||||
@ -94,9 +94,10 @@ struct Window_layouter::Main : Operations,
|
||||
{
|
||||
_window_list.dissolve_windows_from_assignments();
|
||||
|
||||
_layout_rules.with_rules([&] (Xml_node rules) {
|
||||
_layout_rules.with_rules([&] (Xml_node const &rules) {
|
||||
_display_list.update_from_xml(_panorama, rules);
|
||||
_assign_list.update_from_xml(rules);
|
||||
_target_list.update_from_xml(rules, _screen_size);
|
||||
_target_list.update_from_xml(rules, _display_list);
|
||||
});
|
||||
|
||||
_assign_list.assign_windows(_window_list);
|
||||
@ -111,11 +112,11 @@ struct Window_layouter::Main : Operations,
|
||||
assign.for_each_member([&] (Assign::Member &member) {
|
||||
|
||||
member.window.floating(assign.floating());
|
||||
member.window.target_geometry(target.geometry());
|
||||
member.window.target_area(target.geometry().area);
|
||||
|
||||
Rect const rect = assign.window_geometry(member.window.id().value,
|
||||
member.window.client_size(),
|
||||
target.geometry(),
|
||||
target.geometry().area,
|
||||
_decorator_margins);
|
||||
member.window.outer_geometry(rect);
|
||||
member.window.maximized(assign.maximized());
|
||||
@ -154,13 +155,13 @@ struct Window_layouter::Main : Operations,
|
||||
{
|
||||
_config.update();
|
||||
|
||||
if (_config.xml().has_sub_node("report")) {
|
||||
Xml_node const report = _config.xml().sub_node("report");
|
||||
_rules_reporter.conditional(report.attribute_value("rules", false),
|
||||
_env, "rules", "rules");
|
||||
}
|
||||
Xml_node const config = _config.xml();
|
||||
|
||||
_layout_rules.update_config(_config.xml());
|
||||
config.with_optional_sub_node("report", [&] (Xml_node const &report) {
|
||||
_rules_reporter.conditional(report.attribute_value("rules", false),
|
||||
_env, "rules", "rules"); });
|
||||
|
||||
_layout_rules.update_config(config);
|
||||
}
|
||||
|
||||
User_state _user_state { *this, _focus_history };
|
||||
@ -328,12 +329,12 @@ struct Window_layouter::Main : Operations,
|
||||
|
||||
Gui::Connection _gui { _env };
|
||||
|
||||
Panorama _panorama { _heap };
|
||||
|
||||
void _handle_mode_change()
|
||||
{
|
||||
/* determine maximized window geometry */
|
||||
_screen_size = _gui.panorama().convert<Gui::Area>(
|
||||
[&] (Gui::Rect rect) { return rect.area; },
|
||||
[&] (Gui::Undefined) { return Gui::Area { 1, 1 }; });
|
||||
_gui.with_info([&] (Xml_node const &node) {
|
||||
_panorama.update_from_xml(node); });
|
||||
|
||||
_update_window_layout();
|
||||
}
|
||||
@ -513,6 +514,15 @@ void Window_layouter::Main::_gen_rules_with_frontmost_screen(Target::Name const
|
||||
|
||||
_rules_reporter->generate([&] (Xml_generator &xml) {
|
||||
|
||||
_layout_rules.with_rules([&] (Xml_node const &rules) {
|
||||
bool display_declared = false;
|
||||
rules.for_each_sub_node("display", [&] (Xml_node const &display) {
|
||||
display_declared = true;
|
||||
copy_node(xml, display); });
|
||||
if (display_declared)
|
||||
xml.append("\n");
|
||||
});
|
||||
|
||||
_target_list.gen_screens(xml, screen);
|
||||
|
||||
/*
|
||||
|
108
repos/gems/src/app/window_layouter/panorama.h
Normal file
108
repos/gems/src/app/window_layouter/panorama.h
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* \brief Internal Representation of GUI panorama
|
||||
* \author Norman Feske
|
||||
* \date 2024-11-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 _PANORAMA_H_
|
||||
#define _PANORAMA_H_
|
||||
|
||||
/* local includes */
|
||||
#include <types.h>
|
||||
|
||||
namespace Window_layouter { class Panorama; }
|
||||
|
||||
|
||||
struct Window_layouter::Panorama
|
||||
{
|
||||
Allocator &_alloc;
|
||||
|
||||
Rect rect { };
|
||||
|
||||
struct Capture;
|
||||
using Captures = List_model<Capture>;
|
||||
|
||||
Captures _captures { };
|
||||
|
||||
struct Capture : Captures::Element
|
||||
{
|
||||
Name const name { };
|
||||
|
||||
Rect rect { };
|
||||
|
||||
Capture(Name const name) : name(name) { }
|
||||
|
||||
/**
|
||||
* List_model::Element
|
||||
*/
|
||||
void update(Xml_node const &node) { rect = Rect::from_xml(node); }
|
||||
|
||||
/**
|
||||
* List_model::Element
|
||||
*/
|
||||
bool matches(Xml_node const &node) const
|
||||
{
|
||||
return name_from_xml(node) == name;
|
||||
}
|
||||
|
||||
/**
|
||||
* List_model::Element
|
||||
*/
|
||||
static bool type_matches(Xml_node const &node)
|
||||
{
|
||||
return node.has_type("capture");
|
||||
}
|
||||
};
|
||||
|
||||
Panorama(Allocator &alloc) : _alloc(alloc) { }
|
||||
|
||||
~Panorama() { update_from_xml("<empty/>"); }
|
||||
|
||||
void update_from_xml(Xml_node const &gui_info)
|
||||
{
|
||||
rect = Rect::from_xml(gui_info);
|
||||
|
||||
_captures.update_from_xml(gui_info,
|
||||
|
||||
[&] (Xml_node const &node) -> Capture & {
|
||||
return *new (_alloc) Capture(name_from_xml(node)); },
|
||||
|
||||
[&] (Capture &capture) { destroy(_alloc, &capture); },
|
||||
|
||||
[&] (Capture &capture, Xml_node const &node) { capture.update(node); }
|
||||
);
|
||||
}
|
||||
|
||||
void with_leftmost_captured_rect(int const min_x, auto const &fn) const
|
||||
{
|
||||
Rect rect { };
|
||||
int max_x = 1000000;
|
||||
_captures.for_each([&] (Capture const &capture) {
|
||||
if (capture.rect.x1() >= min_x && capture.rect.x1() < max_x) {
|
||||
max_x = capture.rect.x1();
|
||||
rect = capture.rect;
|
||||
}
|
||||
});
|
||||
if (rect.valid())
|
||||
fn(rect);
|
||||
};
|
||||
|
||||
void with_matching_capture_rect(Xml_node const &policy, auto const &fn) const
|
||||
{
|
||||
Rect rect { };
|
||||
_captures.for_each([&] (Capture const &capture) {
|
||||
if (!rect.valid() && !Xml_node_label_score(policy, capture.name).conflict())
|
||||
rect = capture.rect; });
|
||||
if (rect.valid())
|
||||
fn(rect);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _PANORAMA_H_ */
|
@ -26,7 +26,7 @@ class Window_layouter::Target : Noncopyable
|
||||
|
||||
using Name = String<64>;
|
||||
|
||||
enum class Visible { YES, NO };
|
||||
struct Visible { bool value; };
|
||||
|
||||
private:
|
||||
|
||||
@ -42,7 +42,7 @@ class Window_layouter::Target : Noncopyable
|
||||
_name (target.attribute_value("name", Name())),
|
||||
_layer(target.attribute_value("layer", 9999U)),
|
||||
_geometry(geometry),
|
||||
_visible(visible == Visible::YES)
|
||||
_visible(visible.value)
|
||||
{ }
|
||||
|
||||
/* needed to use class as 'Registered<Target>' */
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
/* local includes */
|
||||
#include <target.h>
|
||||
#include <display_list.h>
|
||||
|
||||
namespace Window_layouter { class Target_list; }
|
||||
|
||||
@ -167,7 +168,7 @@ class Window_layouter::Target_list
|
||||
|
||||
/* found target area, iterate though all assigned windows */
|
||||
assign.for_each_member([&] (Assign::Member const &member) {
|
||||
member.window.generate(xml); });
|
||||
member.window.generate(xml, target.geometry()); });
|
||||
});
|
||||
});
|
||||
|
||||
@ -180,32 +181,37 @@ class Window_layouter::Target_list
|
||||
|
||||
/*
|
||||
* The 'rules' XML node is expected to contain at least one <screen>
|
||||
* node. Subseqent <screen> nodes are ignored. The <screen> node may
|
||||
* contain any number of <column> nodes. Each <column> node may contain
|
||||
* any number of <row> nodes, which, in turn, can contain <column>
|
||||
* nodes.
|
||||
* node. A <screen> node may contain any number of <column> nodes. Each
|
||||
* <column> node may contain any number of <row> nodes, which, in turn,
|
||||
* can contain <column> nodes.
|
||||
*/
|
||||
void update_from_xml(Xml_node rules, Area screen_size)
|
||||
void update_from_xml(Xml_node rules, Display_list &display_list)
|
||||
{
|
||||
_targets.for_each([&] (Registered<Target> &target) {
|
||||
destroy(_alloc, &target); });
|
||||
|
||||
_rules.construct(_alloc, rules);
|
||||
|
||||
/* targets are only visible on first screen */
|
||||
Target::Visible visible = Target::Visible::YES;
|
||||
|
||||
display_list.reset_occupied_flags();
|
||||
rules.for_each_sub_node("screen", [&] (Xml_node const &screen) {
|
||||
|
||||
Rect const avail(Point(0, 0), screen_size);
|
||||
using Attr = Display::Attr;
|
||||
Name const display = screen.attribute_value("display", Name());
|
||||
|
||||
if (screen.attribute_value("name", Target::Name()).valid())
|
||||
new (_alloc)
|
||||
Registered<Target>(_targets, screen, avail, visible);
|
||||
display_list.with_display_attr(display, [&] (Attr &display) {
|
||||
|
||||
_process_rec(screen, avail, true, visible);
|
||||
/* show only one screen per display */
|
||||
Target::Visible const visible { !display.occupied };
|
||||
Rect const avail = display.rect;
|
||||
|
||||
visible = Target::Visible::NO;
|
||||
if (screen.attribute_value("name", Target::Name()).valid())
|
||||
new (_alloc)
|
||||
Registered<Target>(_targets, screen, avail, visible);
|
||||
|
||||
display_list.mark_as_occupied(display.rect);
|
||||
|
||||
_process_rec(screen, avail, true, visible);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/surface.h>
|
||||
#include <util/list_model.h>
|
||||
|
||||
namespace Window_layouter {
|
||||
|
||||
@ -46,6 +47,37 @@ namespace Window_layouter {
|
||||
};
|
||||
|
||||
class Window;
|
||||
|
||||
using Name = String<64>;
|
||||
|
||||
Name const name;
|
||||
|
||||
static Name name_from_xml(Xml_node const &node)
|
||||
{
|
||||
return node.attribute_value("name", Name());
|
||||
}
|
||||
|
||||
static inline void copy_attributes(Xml_generator &xml, Xml_node const &from)
|
||||
{
|
||||
using Value = String<64>;
|
||||
from.for_each_attribute([&] (Xml_attribute const &attr) {
|
||||
Value value { };
|
||||
attr.value(value);
|
||||
xml.attribute(attr.name().string(), value);
|
||||
});
|
||||
}
|
||||
|
||||
struct Xml_max_depth { unsigned value; };
|
||||
|
||||
static inline void copy_node(Xml_generator &xml, Xml_node const &from,
|
||||
Xml_max_depth max_depth = { 5 })
|
||||
{
|
||||
if (max_depth.value)
|
||||
xml.node(from.type().string(), [&] {
|
||||
copy_attributes(xml, from);
|
||||
from.for_each_sub_node([&] (Xml_node const &sub_node) {
|
||||
copy_node(xml, sub_node, { max_depth.value - 1 }); }); });
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _TYPES_H_ */
|
||||
|
@ -14,9 +14,6 @@
|
||||
#ifndef _WINDOW_H_
|
||||
#define _WINDOW_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/list_model.h>
|
||||
|
||||
/* local includes */
|
||||
#include <types.h>
|
||||
#include <focus_history.h>
|
||||
@ -106,9 +103,9 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
Area _dragged_size;
|
||||
|
||||
/**
|
||||
* Target geometry the window is assigned to, used while maximized
|
||||
* Target area the window can occupy, used while maximized
|
||||
*/
|
||||
Rect _target_geometry { };
|
||||
Area _target_area { };
|
||||
|
||||
/**
|
||||
* Desired size to be requested to the client
|
||||
@ -116,7 +113,7 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
Area _requested_size() const
|
||||
{
|
||||
return (_maximized || !_floating)
|
||||
? _decorator_margins.inner_geometry(_target_geometry).area
|
||||
? _decorator_margins.inner_geometry({ { }, _target_area }).area
|
||||
: _dragged_size;
|
||||
}
|
||||
|
||||
@ -371,7 +368,7 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
});
|
||||
}
|
||||
|
||||
void generate(Xml_generator &xml) const
|
||||
void generate(Xml_generator &xml, Rect const target_rect) const
|
||||
{
|
||||
/* omit window from the layout if hidden */
|
||||
if (_hidden)
|
||||
@ -392,11 +389,11 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
}
|
||||
|
||||
Rect const rect = _use_target_area()
|
||||
? _decorator_margins.inner_geometry(_target_geometry)
|
||||
? _decorator_margins.inner_geometry({ { }, _target_area })
|
||||
: effective_inner_geometry();
|
||||
|
||||
xml.attribute("xpos", rect.x1());
|
||||
xml.attribute("ypos", rect.y1());
|
||||
xml.attribute("xpos", rect.x1() + target_rect.x1());
|
||||
xml.attribute("ypos", rect.y1() + target_rect.y1());
|
||||
|
||||
/*
|
||||
* Constrain size of non-floating windows
|
||||
@ -481,7 +478,7 @@ class Window_layouter::Window : public List_model<Window>::Element
|
||||
|
||||
void close() { _dragged_size = Area(0, 0); }
|
||||
|
||||
void target_geometry(Rect rect) { _target_geometry = rect; }
|
||||
void target_area(Area area) { _target_area = area; };
|
||||
|
||||
bool maximized() const { return _maximized; }
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user