From 6a4504b1fe5e17a09a019edf0377646cc5dd72aa Mon Sep 17 00:00:00 2001 From: Marcus Fritzsch Date: Thu, 17 Aug 2017 16:45:56 +0200 Subject: [PATCH] Implement surface names * request_surface(name: string) -> id: int. * activate_surface(name: string). * names will be mapped to their respective layers by use of the layers' surface rola match, a regex. * the generated IDs are global and not reused. * allow wp-request to use -p, disable use of pygments even if found. Things missing: * surface removal does not remove already established mappings/names. * Mostly untested. Signed-off-by: Marcus Fritzsch --- generate-binding-glue.py | 17 ++++++--- layers.json | 6 ++++ scripts/wm-request | 9 ++++- src/app.cpp | 91 ++++++++++++++++++++++++++++++++++++++++-------- src/app.hpp | 54 +++++++++++++++++++++++++++- src/layers.cpp | 47 +++++++++++++++++++++---- src/layers.hpp | 32 ++++++++++++++--- 7 files changed, 225 insertions(+), 31 deletions(-) diff --git a/generate-binding-glue.py b/generate-binding-glue.py index 43bc742..c6fda53 100644 --- a/generate-binding-glue.py +++ b/generate-binding-glue.py @@ -100,14 +100,23 @@ API = { 'api': 'g_afb_instance->app.api.', # where are our API functions 'functions': [ { - 'name': 'register_surface', + 'name': 'request_surface', #'return_type': 'int', # Or do they return all just some json? 'args': [ # describes the functions arguments, and their names as found in the json request - { 'name': 'appid', 'type': 'uint32_t', 'jtype': 'int' }, # XXX: lookup jtypes automatically? i.e. char*|const char* would be string? - { 'name': 'surfaceid', 'type': 'uint32_t', 'jtype': 'int' }, + { 'name': 'drawing_name', 'type': 'char const*', 'jtype': 'string' }, ], }, - { 'name': 'demo_activate_surface', 'args': [ { 'name': 'surfaceid', 'type': 'uint32_t', 'jtype': 'int' } ] }, + { + 'name': 'activate_surface', + 'args': [ + { 'name': 'drawing_name', 'type': 'char const*', 'jtype': 'string' }, + ], + }, + { 'name': 'list_drawing_names', }, + { + 'name': 'demo_activate_surface', + 'args': [ { 'name': 'surfaceid', 'type': 'uint32_t', 'jtype': 'int' } ] + }, { 'name': 'demo_activate_all' }, { 'name': 'debug_status', }, { 'name': 'debug_layers', }, diff --git a/layers.json b/layers.json index 0208502..280dd2e 100644 --- a/layers.json +++ b/layers.json @@ -3,9 +3,12 @@ "main_surface": { "surface_id": 1000, + "surface_role": "HomeScreen", "comment": "This surface should never be made invisible (The HomeScreen)" }, + "dynamic_ids_start": 16777216, + "layers": [ { "name": "HomeScreen", @@ -28,6 +31,7 @@ { "type": "single", "surface_id": 1000, + "role": "^HomeScreen$", "name": "HomeScreen", "layer_id": 1000, "area": { "type": "full" }, @@ -37,6 +41,7 @@ "type": "range", "first_surface_id": 2000, "last_surface_id": 2999, + "role": "^App.*", "name": "apps", "layer_id": 1001, "area": { "type": "rect", "rect": { "x": 0, "y": 100, "width": -1, "height": -201 } }, @@ -46,6 +51,7 @@ "type": "range", "first_surface_id": 3000, "last_surface_id": 3999, + "role": "^OnScreen.*", "name": "popups", "layer_id": 9999, "area": { "type": "rect", "rect": { "x": 0, "y": 100, "width": -1, "height": -201 } }, diff --git a/scripts/wm-request b/scripts/wm-request index 618da62..c98e748 100755 --- a/scripts/wm-request +++ b/scripts/wm-request @@ -1,5 +1,12 @@ #!/bin/sh +nopygments=0 +if [ "$1" = "-p" ] +then + nopygments=1 + shift +fi + if ! [ "$1" ] then echo "Usage: $0 VERB [ARGS]" >&2 @@ -15,7 +22,7 @@ set -eu if which python 2>/dev/null 1>&2 && echo '{ "test": "1" }' | python -m json.tool 2>/dev/null 1>&2 then - if which pygmentize 2>/dev/null 1>&2 + if [ $nopygments = 0 ] && which pygmentize 2>/dev/null 1>&2 then json_pretty() { python -m json.tool | pygmentize -l json diff --git a/src/app.cpp b/src/app.cpp index db0a5e3..c38a7f4 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include namespace wm { @@ -44,8 +45,7 @@ struct wm::area area_from_json(json const &j) { return wm::area{ j["name"], { - j["width"], j["height"], - j["x"], j["y"], + j["width"], j["height"], j["x"], j["y"], }, j["zorder"], }; @@ -126,7 +126,11 @@ App::App(wl::display *d) outputs(), config(), layouts(), - layers() { + layers(), + pending(), + name_mapping(), + id_alloc{} +{ assert(g_app == nullptr); g_app = this; @@ -280,8 +284,10 @@ void App::surface_set_layout(uint32_t surface_id) { } uint32_t layer_id = o_layer_id.value(); + logdebug("surface_set_layout for surface %u on layer %u", surface_id, layer_id); - auto rect = this->layers.get_layer_rect(surface_id).value(); + auto const &layer = this->layers.get_layer(layer_id); + auto rect = layer.value().rect; auto &s = this->controller->surfaces[surface_id]; int x = rect.x; @@ -305,7 +311,7 @@ void App::surface_set_layout(uint32_t surface_id) { // XXX: visibility should be determined independently of our // layer + geometry setup. - s->set_visibility(1); + s->set_visibility(surface_id == (unsigned)this->layers.main_surface ? 1 : 0); this->controller->layers[layer_id]->add_surface(s.get()); logdebug("Surface %u now on layer %u with rect { %d, %d, %d, %d }", @@ -317,7 +323,7 @@ char const *App::activate_surface(uint32_t surface_id) { return "Surface does not exist"; } - // This shouild involve a policy check, but as we do not (yet) have + // This should involve a policy check, but as we do not (yet) have // such a thing, we will just switch to this surface. // XXX: input focus missing!!1 @@ -376,24 +382,81 @@ void App::surface_removed(uint32_t surface_id) { logdebug("surface_id is %u", surface_id); } +result App::request_surface(char const *drawing_name) { + auto lid = this->layers.get_layer_id(std::string(drawing_name)); + if (!lid) { + // XXX: to we need to put these applications on the App layer? + return Err("Drawing name does not match any role"); + } + + auto rname = this->id_alloc[drawing_name]; + if (! rname) { + // name does not exist yet, allocate surface id... + // XXX: how to allocate surface IDs? + // * allocate by running a counter for each layer? + // * allocate IDs globally, i.e. do not have layers contain + // ID ranges (only define the surfaces on the layer by + // role?) + auto id = int(this->id_alloc(drawing_name)); + this->layers.add_surface(id, lid.value()); + + // XXX: setup the main_surface id if we registered HomeScreen + // XXX: you should fix this! + if (!this->layers.main_surface_name.empty() && + this->layers.main_surface_name == drawing_name) { + this->layers.main_surface = id; + this->activate_surface(id); + logdebug("Set main_surface id to %u", id); + } + + return Ok(id); + } + + // Check currently registered drawing names if it is already there. + return Err("Surface already present"); +} + +char const* App::activate_surface(char const *drawing_name) { + auto osid = this->id_alloc[drawing_name]; + + if (osid) { + logdebug("ativate surface with name %s and id %u", drawing_name, osid.value()); + this->activate_surface(osid.value()); + return nullptr; + } + + logerror("surface %s unknown", drawing_name); + return "Surface unknown"; +} + // _ _ _ _ _ _ _ // | |__ (_)_ __ __| (_)_ __ __ _ __ _ _ __ (_) (_)_ __ ___ _ __ | | // | '_ \| | '_ \ / _` | | '_ \ / _` | / _` | '_ \| | | | '_ ` _ \| '_ \| | // | |_) | | | | | (_| | | | | | (_| | | (_| | |_) | | | | | | | | | |_) | | // |_.__/|_|_| |_|\__,_|_|_| |_|\__, |___\__,_| .__/|_| |_|_| |_| |_| .__/|_| // |___/_____| |_| |_| -binding_api::result_type binding_api::register_surface(uint32_t appid, - uint32_t surfid) { - logdebug("%s appid %u surfid %u", __func__, appid, surfid); - if (appid > 0xff) { - return Err("invalid appid"); +binding_api::result_type binding_api::request_surface( + char const *drawing_name) { + auto r = this->app->request_surface(drawing_name); + if (r.is_err()) { + return Err(r.unwrap_err()); } + return Ok(json_object_new_int(r.unwrap())); +} - if (surfid > 0xffff) { - return Err("invalid surfaceid"); +binding_api::result_type binding_api::activate_surface( + char const *drawing_name) { + logdebug("%s drawing_name %s", __func__, drawing_name); + auto r = this->app->activate_surface(drawing_name); + if (r) { + return Err(r); } + return Ok(json_object_new_object()); +} - return Ok(json_object_new_int((appid << 16) + surfid)); +binding_api::result_type binding_api::list_drawing_names() { + json j = this->app->id_alloc.names; + return Ok(json_tokener_parse(j.dump().c_str())); } binding_api::result_type binding_api::debug_layers() { diff --git a/src/app.hpp b/src/app.hpp index 90ab42c..12d26b4 100644 --- a/src/app.hpp +++ b/src/app.hpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include "afb_binding_api.hpp" #include "config.hpp" @@ -38,6 +40,41 @@ struct controller; namespace wm { +struct id_allocator { + unsigned next = 0x0100'0000; + + // Surfaces that where requested but not yet created + std::unordered_map surfaces; + // std::unordered_set pending_surfaces; + std::unordered_map names; + + id_allocator(id_allocator const &) = delete; + id_allocator(id_allocator &&) = delete; + id_allocator &operator=(id_allocator const &); + id_allocator &operator=(id_allocator &&) = delete; + + // Allocate a new ID + unsigned operator()(std::string const &name) { + unsigned sid = this->next++; + this->surfaces[sid] = name; + // this->pending_surfaces.insert({sid}); + this->names[name] = sid; + logdebug("allocated new id %u with name %s", sid, name.c_str()); + return sid; + } + + // Lookup by ID or by name + optional operator[](std::string const &name) { + auto i = this->names.find(name); + return i == this->names.end() ? nullopt : optional(i->second); + } + + optional operator[](unsigned id) { + auto i = this->surfaces.find(id); + return i == this->surfaces.end() ? nullopt : optional(i->second); + } +}; + struct App { struct binding_api api; struct controller_hooks chooks; @@ -56,6 +93,11 @@ struct App { typedef std::pair> name_task_pair; std::vector pending; + typedef std::map drawing_name_map; + drawing_name_map name_mapping; + + struct id_allocator id_alloc; + explicit App(wl::display *d); ~App(); @@ -65,14 +107,24 @@ struct App { App &operator=(App &&) = delete; int init(); - int dispatch_events(); int init_layout(); + + int dispatch_events(); + void surface_set_layout(uint32_t surface_id); char const *activate_surface(uint32_t surface_id); + // Allocate a surface ID for this role + result request_surface(char const *drawing_name); + + // Activate (i.e. make visible, if allowed!) a surface + char const *activate_surface(char const *drawng_name); + + // add tasks, executed after dispatch_events() void add_task(char const *name, std::function &&f); void execute_pending(); + // Events from the compositor we are interested in void surface_created(uint32_t surface_id); void surface_removed(uint32_t surface_id); }; diff --git a/src/layers.cpp b/src/layers.cpp index 8f79451..464e20b 100644 --- a/src/layers.cpp +++ b/src/layers.cpp @@ -15,6 +15,7 @@ */ #include +#include #include "json_helper.hpp" #include "layers.hpp" @@ -31,7 +32,8 @@ layer::layer(nlohmann::json const &j) { } else { this->id_min = this->id_max = j["surface_id"]; } - this->name = j["name"].get(); + this->role = j["role"]; + this->name = j["name"]; this->layer_id = j["layer_id"]; this->rect = genivi::full_rect; if (j["area"]["type"] == "rect") { @@ -47,14 +49,25 @@ struct result to_layer_map(nlohmann::json const &j) { try { layer_map stl{}; auto m = j["mappings"]; - stl.layers.reserve(m.size()); + std::transform(std::cbegin(m), std::cend(m), std::inserter(stl.mapping, stl.mapping.end()), - [&stl](nlohmann::json const &j) { - auto k = layer(j); - stl.layers.push_back(unsigned(k.layer_id)); - return k; + [](nlohmann::json const &j) { + return layer(j); }); + + // XXX: add sanity checks here? + // * check for double IDs + // * check for double names/roles + + stl.layers.reserve(m.size()); + std::transform(std::cbegin(stl.mapping), std::cend(stl.mapping), + std::back_inserter(stl.layers), + [&stl](struct layer const &k) { + stl.roles.emplace_back(std::make_pair(k.role, k.layer_id)); + return unsigned(k.layer_id); + }); + // XXX need to sort layers? for (auto i : stl.mapping) { if (i.name.empty()) { @@ -68,6 +81,7 @@ struct result to_layer_map(nlohmann::json const &j) { auto msi = j.find("main_surface"); if (msi != j.end()) { stl.main_surface = (*msi)["surface_id"]; + stl.main_surface_name = msi->value("surface_role", ""); } // Check lookup @@ -125,7 +139,26 @@ optional get_surface_id_to_layer(struct layer_map const *s2l, optional layer_map::get_layer_id(int surface_id) { auto e = get_surface_id_to_layer(this, surface_id); - return e ? optional(e->layer_id) : nullopt; + if (! e) { + auto i = this->surfaces.find(surface_id); + if (i != this->surfaces.end()) { + return optional(int(i->second)); + } + return nullopt; + } + return optional(e->layer_id); +} + +optional layer_map::get_layer_id(std::string const &role) { + for (auto const &r : this->roles) { + auto re = std::regex(r.first); + if (std::regex_match(role, re)) { + logdebug("role %s matches layer %d", role.c_str(), r.second); + return optional(r.second); + } + } + logdebug("role %s does NOT match any layer", role.c_str()); + return nullopt; } optional layer_map::get_layer_rect(int surface_id) { diff --git a/src/layers.hpp b/src/layers.hpp index 525a8b1..ae609fc 100644 --- a/src/layers.hpp +++ b/src/layers.hpp @@ -43,6 +43,9 @@ struct layer { // That is; allow us to specify dimensions dependent on // e.g. screen dimension, w/o knowing the actual screen size. genivi::rect rect; + // Specify a role prefix for surfaces that should be + // put on this layer. + std::string role; // XXX perhaps a zorder is needed here? explicit layer(nlohmann::json const &j); @@ -54,24 +57,45 @@ struct layer { json to_json() const; }; -// Actually, we shouldn't need a struct here ... but let's just keep it at that -// for now, to contain its mapping type and the _single_ useful method. struct layer_map { using json = nlohmann::json; typedef std::set storage_type; typedef std::vector layers_type; + typedef std::vector> role_to_layer_map; + typedef std::map addsurf_layer_map; - storage_type mapping; - layers_type layers; + // XXX: we also will need a layer_id to layer map, perhaps + // make this the primary map, and the surface_id->layer a + // secondary map. + + storage_type mapping; // map surface_id to layer + layers_type layers; // the actual layer IDs we have int main_surface; + std::string main_surface_name; + role_to_layer_map roles; + addsurf_layer_map surfaces; // additional surfaces on layers optional get_layer_id(int surface_id); + optional get_layer_id(std::string const &role); + optional get_layer(int layer_id) { + auto i = std::find_if(std::cbegin(this->mapping), + std::cend(this->mapping), + [layer_id](struct layer const &l) { + return layer_id == l.layer_id; + }); + return i == this->mapping.end() ? nullopt : optional(*i); + } + optional get_layer_rect(int surface_id); layers_type::size_type get_layers_count() const { return this->layers.size(); } + void add_surface(unsigned surface_id, unsigned layer_id) { + this->surfaces[surface_id] = layer_id; + } + json to_json() const; }; -- 2.16.6