app/main: dispatch wayland events using ping(), WIP on split layout
authorMarcus Fritzsch <marcus_fritzsch@mentor.com>
Tue, 12 Sep 2017 09:29:27 +0000 (11:29 +0200)
committerMarcus Fritzsch <marcus_fritzsch@mentor.com>
Tue, 12 Sep 2017 09:29:27 +0000 (11:29 +0200)
* Dispatch ayland events with ping() API call, after having read events
  from wayland fd - in order to prevent indefinite-poll on wrongly read+
  dispatch from multiple threads (dispatcher and API call).
* Add scope trace to all API call thunks.
* Split layout advancements, still broken AF though.
* Add App1 example split layout to App layer.

Signed-off-by: Marcus Fritzsch <marcus_fritzsch@mentor.com>
layers.json
src/app.cpp
src/app.hpp
src/main.cpp

index d452971..0d5a817 100644 (file)
                "main_match": "^App Navi Map",
                "sub_match": "^App Navi Guidance",
                "priority": 1000
+            },
+            {
+               "name": "App1 example",
+               "main_match": "^App1",
+               "sub_match": ".*",
+               "priority": 1000
             }
          ]
       },
index 98db0b4..e42d05f 100644 (file)
@@ -32,8 +32,9 @@
 #include <bits/signum.h>
 #include <csignal>
 #include <fstream>
-#include <thread>
 #include <json.hpp>
+#include <regex>
+#include <thread>
 
 namespace wm {
 
@@ -82,7 +83,8 @@ App::App(wl::display *d)
      config(),
      layouts(),
      layers(),
-     id_alloc{} {
+     id_alloc{},
+     pending_events(false) {
    assert(g_app == nullptr);
    g_app = this;
 
@@ -120,7 +122,8 @@ int App::init() {
 
    this->display->add_global_handler(
       "ivi_controller", [this](wl_registry *r, uint32_t name, uint32_t v) {
-         this->controller = std::make_unique<struct genivi::controller>(r, name, v);
+         this->controller =
+            std::make_unique<struct genivi::controller>(r, name, v);
 
          // Init controller hooks
          this->controller->chooks = &this->chooks;
@@ -144,6 +147,11 @@ int App::init() {
 }
 
 int App::dispatch_events() {
+   if (this->pending_events.load(std::memory_order_consume)) {
+      this->pending_events.store(false, std::memory_order_release);
+      return this->display->dispatch_pending();
+   }
+
    int ret = this->display->dispatch();
    if (ret == -1) {
       logerror("wl_display_dipatch() returned error %d",
@@ -153,7 +161,7 @@ int App::dispatch_events() {
    this->display->flush();
 
    // execute pending tasks, that is layout changes etc.
-   //this->execute_pending();
+   // this->execute_pending();
 
    return 0;
 }
@@ -198,8 +206,9 @@ int App::init_layers() {
       auto &l = layers[i.layer_id];
       l->set_destination_rectangle(0, 0, o->width, o->height);
       l->set_visibility(1);
-      logdebug("Setting up layer %s (%d) for surfaces %d-%d", i.name.c_str(),
-               i.layer_id, i.id_min, i.id_max);
+      logdebug("Setting up layer %s (%d) for surfaces %d-%d and role match \"%s\"", i.name.c_str(),
+               i.layer_id, i.id_min, i.id_max, i.role.c_str());
+      this->layouts[l->id] = LayoutState{};
    }
 
    // Add layers to screen (XXX: are they sorted correctly?)
@@ -215,8 +224,9 @@ int App::init_layers() {
 namespace {
 
 // This can fix the HomeScreen...
-void redraw_fix(App *app, std::unique_ptr<genivi::surface> &s, int x, int y, int w, int h) {
-   { // XXX: Work around weston redraw issues
+void redraw_fix(App *app, std::unique_ptr<genivi::surface> &s, int x, int y,
+                int w, int h) {
+   {  // XXX: Work around weston redraw issues
       // trigger an update by changing the source dimensions!
       s->set_configuration(w + 1, h);
       s->set_source_rectangle(0, 0, w + 1, h);
@@ -239,7 +249,7 @@ void redraw_fix(App *app, std::unique_ptr<genivi::surface> &s, int x, int y, int
 
 }  // namespace
 
-void App::surface_init_layout(uint32_t surface_id) {
+void App::surface_set_layout_full(uint32_t surface_id) {
    if (!this->controller->surface_exists(surface_id)) {
       logerror("Surface %d does not exist", int(surface_id));
       return;
@@ -289,17 +299,90 @@ void App::surface_init_layout(uint32_t surface_id) {
    this->controller->commit_changes();
    this->display->roundtrip();
 
-   redraw_fix(this, s, x, y, w, h);
+   //redraw_fix(this, s, x, y, w, h);
 
-   this->controller->layers[layer_id]->add_surface(s.get());
+   logdebug("Surface %u now with rect { %d, %d, %d, %d }",
+            surface_id, x, y, w, h);
+}
 
-   // activate the main_surface right away
-   if (surface_id == static_cast<unsigned>(this->layers.main_surface)) {
-      logdebug("Activating main_surface (%d)", surface_id);
+void App::surface_set_layout_split(uint32_t surface_id, uint32_t sub_surface_id) {
+   if (!this->controller->surface_exists(surface_id)) {
+      logerror("Surface %d does not exist", int(surface_id));
+      return;
+   }
+
+   auto o_layer_id = this->layers.get_layer_id(surface_id);
+
+   if (!o_layer_id) {
+      logerror("Surface %d is not associated with any layer!", int(surface_id));
+      return;
+   }
+
+   uint32_t layer_id = o_layer_id.value();
+   logdebug("surface_set_layout for surface %u on layer %u", surface_id,
+            layer_id);
 
-      this->activate_surface(this->lookup_name(surface_id).value_or("unknown-name").c_str());
+   auto const &layer = this->layers.get_layer(layer_id);
+   auto rect = layer.value().rect;
+   auto &s = this->controller->surfaces[surface_id];
+   auto &ss = this->controller->surfaces[sub_surface_id];
+
+   int x = rect.x;
+   int y = rect.y;
+   int w = rect.w;
+   int h = rect.h;
+
+   this->state.main = surface_id;
+   this->state.sub = sub_surface_id;
+   this->state.state = LayoutState::LayoutSplit;
+
+   // less-than-0 values refer to MAX + 1 - $VALUE
+   // e.g. MAX is either screen width or height
+   if (w < 0) {
+      w = this->controller->output_size.w + 1 + w;
+   }
+   if (h < 0) {
+      h = this->controller->output_size.h + 1 + h;
    }
 
+   int x_off = 0;
+   int y_off = 0;
+
+   // split along major axis
+   if (w > h) {
+      w /= 2;
+      x_off = w;
+   } else {
+      h /= 2;
+      y_off = h;
+   }
+
+   // configure surface to wxh dimensions
+   s->set_configuration(w, h);
+   // set source reactangle, even if we should not need to set it.
+   s->set_source_rectangle(0, 0, w, h);
+   // set destination to the display rectangle
+   s->set_destination_rectangle(x, y, w, h);
+
+   s->set_visibility(1);
+   s->set_opacity(256);
+
+   // configure surface to wxh dimensions
+   ss->set_configuration(w, h);
+   // set source reactangle, even if we should not need to set it.
+   ss->set_source_rectangle(0, 0, w, h);
+   // set destination to the display rectangle
+   ss->set_destination_rectangle(x+x_off, y+y_off, w, h);
+
+   ss->set_visibility(1);
+   ss->set_opacity(256);
+
+   this->controller->commit_changes();
+   this->display->roundtrip();
+
+   //redraw_fix(this, s, x, y, w, h);
+   //redraw_fix(this, ss, x+x_off, y+y_off, w, h);
+
    logdebug("Surface %u now on layer %u with rect { %d, %d, %d, %d }",
             surface_id, layer_id, x, y, w, h);
 }
@@ -320,10 +403,34 @@ char const *App::activate_surface(char const *drawing_name) {
       return "Surface does not exist";
    }
 
+   if (this->state.main == surface_id || this->state.sub == surface_id) {
+      return "Surface already active";
+   }
+
    // 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
 
+   if (this->state != LayoutState{}) {
+      switch (this->state.state) {
+         case LayoutState::LayoutSingle:
+            if (this->can_split(surface_id)) {
+               this->surface_set_layout_split(this->state.main, surface_id);
+               return nullptr;
+            }
+            break;
+
+         case LayoutState::LayoutSplit:
+            if (this->can_split(surface_id)) {
+               this->deactivate(this->state.sub);
+               this->surface_set_layout_split(this->state.main, surface_id);
+            }
+
+         case LayoutState::LayoutNone:
+            break;
+      }
+   }
+
    // Make it visible, no (or little effect) if already visible
    auto &s = this->controller->surfaces[surface_id];
 
@@ -341,6 +448,9 @@ char const *App::activate_surface(char const *drawing_name) {
    this->controller->commit_changes();
    this->display->flush();
 
+   this->state.main = surface_id;
+   this->state.state = LayoutState::LayoutSingle;
+
    // no error
    return nullptr;
 }
@@ -361,7 +471,40 @@ char const *App::deactivate_surface(char const *drawing_name) {
       return "Cannot deactivate main_surface";
    }
 
-   this->deactivate(surface_id);
+   switch (this->state.state) {
+      case LayoutState::LayoutSplit:
+         if (surface_id == this->state.main) {
+            this->deactivate(surface_id);
+            this->surface_set_layout_full(this->state.main);
+
+            this->deactivate(this->state.sub);
+            this->surface_set_layout_full(this->state.sub);
+
+            this->state.main = -1;
+            this->state.sub = -1;
+            this->state.state = LayoutState::LayoutSingle;
+         } else if (surface_id == this->state.sub) {
+            this->deactivate(surface_id);
+            this->surface_set_layout_full(this->state.sub);
+            this->surface_set_layout_full(this->state.main);
+
+            // XXX send syndraw events....
+
+            this->state.sub = -1;
+            this->state.state = LayoutState::LayoutSingle;
+         }
+         break;
+
+      case LayoutState::LayoutSingle:
+         this->deactivate(surface_id);
+         this->state.main = -1;
+         this->state.sub = -1;
+         break;
+
+      case LayoutState::LayoutNone:
+         break;
+   }
+
 
    this->controller->commit_changes();
    this->display->flush();
@@ -378,7 +521,18 @@ char const *App::deactivate_surface(char const *drawing_name) {
 void App::surface_created(uint32_t surface_id) {
    logdebug("surface_id is %u", surface_id);
 
-   this->surface_init_layout(surface_id);
+   this->surface_set_layout_full(surface_id);
+
+   this->controller->layers[this->layers.get_layer_id(surface_id).value()]
+           ->add_surface(this->controller->surfaces[surface_id].get());
+
+   // activate the main_surface right away
+   if (surface_id == static_cast<unsigned>(this->layers.main_surface)) {
+      logdebug("Activating main_surface (%d)", surface_id);
+
+      this->activate_surface(
+              this->lookup_name(surface_id).value_or("unknown-name").c_str());
+   }
 }
 
 void App::surface_removed(uint32_t surface_id) {
@@ -407,13 +561,9 @@ void App::emit_visible(char const *label, bool is_visible) {
    this->api.send_event(is_visible ? "visible" : "invisible", label);
 }
 
-void App::emit_invisible(char const *label) {
-   return emit_visible(label, 0);
-}
+void App::emit_invisible(char const *label) { return emit_visible(label, 0); }
 
-void App::emit_visible(char const *label) {
-   return emit_visible(label, 1);
-}
+void App::emit_visible(char const *label) { return emit_visible(label, 1); }
 
 result<int> App::request_surface(char const *drawing_name) {
    auto lid = this->layers.get_layer_id(std::string(drawing_name));
@@ -446,7 +596,8 @@ result<int> App::request_surface(char const *drawing_name) {
 void App::activate(unsigned id) {
    if (this->controller->sprops[id].visibility == 0) {
       this->controller->surfaces[id]->set_visibility(1);
-      char const *label = this->lookup_name(id).value_or("unknown-name").c_str();
+      char const *label =
+         this->lookup_name(id).value_or("unknown-name").c_str();
       this->emit_activated(label);
       this->emit_visible(label);
    }
@@ -455,7 +606,8 @@ void App::activate(unsigned id) {
 void App::deactivate(unsigned id) {
    if (this->controller->sprops[id].visibility != 0) {
       this->controller->surfaces[id]->set_visibility(0);
-      char const *label = this->lookup_name(id).value_or("unknown-name").c_str();
+      char const *label =
+         this->lookup_name(id).value_or("unknown-name").c_str();
       this->emit_deactivated(label);
       this->emit_invisible(label);
    }
@@ -464,17 +616,41 @@ void App::deactivate(unsigned id) {
 bool App::can_split(unsigned new_id) {
    if (this->state.state == LayoutState::LayoutSingle) {
       auto new_id_layer = this->layers.get_layer_id(new_id).value();
-      auto current_id_layer = this->layers.get_layer_id(this->state.main).value();
+      auto current_id_layer =
+         this->layers.get_layer_id(this->state.main).value();
 
+      // surfaces are on separate layers, don't bother.
       if (new_id_layer != current_id_layer) {
          return false;
       }
 
       std::string const &new_id_str = this->lookup_name(new_id).value();
-      std::string const &cur_id_str = this->lookup_name(this->state.main).value();
+      std::string const &cur_id_str =
+         this->lookup_name(this->state.main).value();
+
+      auto const &layer = this->layers.get_layer(new_id_layer);
 
+      logdebug("layer info name: %s", layer->name.c_str());
 
+      if (layer->layouts.empty()) {
+         return false;
+      }
+
+      for (auto i = layer->layouts.cbegin(); i != layer->layouts.cend(); i++) {
+         logdebug("%d main_match '%s'", new_id_layer, i->main_match.c_str());
+         auto rem = std::regex(i->main_match);
+         if (std::regex_match(cur_id_str, rem)) {
+            // build the second one only if the first already matched
+            logdebug("%d sub_match '%s'", new_id_layer, i->sub_match.c_str());
+            auto res = std::regex(i->sub_match);
+            if (std::regex_match(new_id_str, res)) {
+               return true;
+            }
+         }
+      }
    }
+
+   return false;
 }
 
 //                  _             _ _            _                 _
index 37122ef..00a2ba2 100644 (file)
 #define TMCAGLWM_APP_HPP
 
 #include <json-c/json.h>
+
+#include <atomic>
 #include <memory>
 #include <unordered_map>
 #include <unordered_set>
-#include <deque>
+#include <experimental/optional>
 
 #include "afb_binding_api.hpp"
 #include "config.hpp"
@@ -41,6 +43,8 @@ struct controller;
 
 namespace wm {
 
+using std::experimental::optional;
+
 struct id_allocator {
    constexpr static const unsigned id_shift = 22;
    constexpr static const unsigned id_mask = (1 << id_shift) - 1;
@@ -100,13 +104,21 @@ struct id_allocator {
 
 struct LayoutState {
    enum States {
+      LayoutNone,  // Not useful...
       LayoutSingle,
       LayoutSplit,
    };
 
-   enum States state;
-   int main;
-   int sub;
+   enum States state{LayoutSingle};
+   int main{-1};
+   int sub{-1};
+
+   bool operator==(const LayoutState &b) const {
+      return state == b.state && main == b.main && sub == b.sub;
+   }
+   bool operator!=(const LayoutState &b) const {
+      return !(*this == b);
+   }
 };
 
 struct App {
@@ -121,7 +133,8 @@ struct App {
 
    struct config config;
 
-   layouts_type layouts;
+   // track current layouts separately
+   std::map<int, struct LayoutState> layouts;
    layer_map layers;
 
    // ID allocation and proxy methods for lookup
@@ -135,6 +148,8 @@ struct App {
 
    struct LayoutState state;
 
+   std::atomic<bool> pending_events;
+
    explicit App(wl::display *d);
    ~App();
 
@@ -148,7 +163,8 @@ struct App {
 
    int dispatch_events();
 
-   void surface_init_layout(uint32_t surface_id);
+   void surface_set_layout_full(uint32_t surface_id);
+   void surface_set_layout_split(uint32_t surface_id, uint32_t sub_surface_id);
 
    // Allocate a surface ID for this role
    result<int> request_surface(char const *drawing_name);
index d944c24..4bb53b3 100644 (file)
@@ -30,7 +30,7 @@ extern "C" {
 }
 
 namespace {
-std::mutex binding_m;
+//std::mutex binding_m;
 
 struct afb_instance {
    std::unique_ptr<wl::display> display;
@@ -51,9 +51,10 @@ int afb_instance::init() {
    return this->app.init();
 }
 
-int display_event_callback(sd_event_source *evs, int /*fd*/, uint32_t events,
+int display_event_callback(sd_event_source *evs, int fd, uint32_t events,
                            void * /*data*/) {
-   std::lock_guard<std::mutex> guard(binding_m);
+   ST();
+   // std::lock_guard<std::mutex> guard(binding_m);
 
    if ((events & EPOLLHUP) != 0) {
       logerror("The compositor hung up, dying now.");
@@ -63,8 +64,16 @@ int display_event_callback(sd_event_source *evs, int /*fd*/, uint32_t events,
    }
 
    if ((events & EPOLLIN) != 0u) {
-      if (g_afb_instance->app.dispatch_events() == -1) {
-         goto error;
+      {
+         STN(display_read_events);
+         g_afb_instance->app.display->read_events();
+         g_afb_instance->app.pending_events.store(true, std::memory_order_release);
+      }
+      {
+         STN(winman_ping_api_call);
+         afb_service_call("winman", "ping", json_object_new_object(), [](void *c, int st,  json_object* j) {
+            STN(winman_ping_api_call_return);
+         }, nullptr);
       }
    }