Implement surface names
authorMarcus Fritzsch <marcus_fritzsch@mentor.com>
Thu, 17 Aug 2017 14:45:56 +0000 (16:45 +0200)
committerMarcus Fritzsch <marcus_fritzsch@mentor.com>
Mon, 4 Sep 2017 14:53:49 +0000 (16:53 +0200)
* 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 <marcus_fritzsch@mentor.com>
generate-binding-glue.py
layers.json
scripts/wm-request
src/app.cpp
src/app.hpp
src/layers.cpp
src/layers.hpp

index 43bc742..c6fda53 100644 (file)
@@ -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', },
index 0208502..280dd2e 100644 (file)
@@ -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 } },
index 618da62..c98e748 100755 (executable)
@@ -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
index db0a5e3..c38a7f4 100644 (file)
@@ -31,6 +31,7 @@
 #include <bits/signum.h>
 #include <csignal>
 #include <fstream>
+#include <algorithm>
 #include <json.hpp>
 
 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<int> 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<int>("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<int>(id);
+   }
+
+   // Check currently registered drawing names if it is already there.
+   return Err<int>("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<json_object *>("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<json_object*>(r.unwrap_err());
    }
+   return Ok(json_object_new_int(r.unwrap()));
+}
 
-   if (surfid > 0xffff) {
-      return Err<json_object *>("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<json_object *>(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() {
index 90ab42c..12d26b4 100644 (file)
@@ -19,6 +19,8 @@
 
 #include <json-c/json.h>
 #include <memory>
+#include <unordered_map>
+#include <unordered_set>
 
 #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<unsigned, std::string> surfaces;
+   // std::unordered_set<unsigned> pending_surfaces;
+   std::unordered_map<std::string, unsigned> 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<unsigned> operator[](std::string const &name) {
+      auto i = this->names.find(name);
+      return i == this->names.end() ? nullopt : optional<unsigned>(i->second);
+   }
+
+   optional<std::string> operator[](unsigned id) {
+      auto i = this->surfaces.find(id);
+      return i == this->surfaces.end() ? nullopt : optional<std::string>(i->second);
+   }
+};
+
 struct App {
    struct binding_api api;
    struct controller_hooks chooks;
@@ -56,6 +93,11 @@ struct App {
    typedef std::pair<char const *, std::function<void()>> name_task_pair;
    std::vector<name_task_pair> pending;
 
+   typedef std::map<std::string, int> 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<int> 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<void()> &&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);
 };
index 8f79451..464e20b 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <algorithm>
+#include <regex>
 
 #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<std::string>();
+   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<struct layer_map> 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<struct layer_map> 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<layer> get_surface_id_to_layer(struct layer_map const *s2l,
 
 optional<int> layer_map::get_layer_id(int surface_id) {
    auto e = get_surface_id_to_layer(this, surface_id);
-   return e ? optional<int>(e->layer_id) : nullopt;
+   if (! e) {
+      auto i = this->surfaces.find(surface_id);
+      if (i != this->surfaces.end()) {
+         return optional<int>(int(i->second));
+      }
+      return nullopt;
+   }
+   return optional<int>(e->layer_id);
+}
+
+optional<int> 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<int>(r.second);
+      }
+   }
+   logdebug("role %s does NOT match any layer", role.c_str());
+   return nullopt;
 }
 
 optional<genivi::rect> layer_map::get_layer_rect(int surface_id) {
index 525a8b1..ae609fc 100644 (file)
@@ -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<struct layer> storage_type;
    typedef std::vector<unsigned int> layers_type;
+   typedef std::vector<std::pair<std::string, int>> role_to_layer_map;
+   typedef std::map<unsigned, unsigned> 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<int> get_layer_id(int surface_id);
+   optional<int> get_layer_id(std::string const &role);
+   optional<struct layer> 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<struct layer>(*i);
+   }
+
    optional<genivi::rect> 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;
 };