app/main: dispatch wayland events using ping(), WIP on split layout
[staging/windowmanager.git] / src / app.cpp
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;
 }
 
 //                  _             _ _            _                 _