App: controller hooks impl
[staging/windowmanager.git] / src / app.cpp
index 9b6486a..42f6f16 100644 (file)
@@ -80,10 +80,10 @@ App::App(wl::display *d)
      controller{},
      outputs(),
      config(),
-     layouts(),
      layers(),
      id_alloc{},
-     pending_events(false) {
+     pending_events(false),
+     policy{} {
    assert(g_app == nullptr);
    g_app = this;
 
@@ -229,7 +229,6 @@ int App::init_layers() {
       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?)
@@ -305,7 +304,9 @@ void App::surface_set_layout(int surface_id, optional<int> sub_surface_id) {
 
    if (sub_surface_id) {
       if (o_layer_id != this->layers.get_layer_id(*sub_surface_id)) {
-         logerror("surface_set_layout: layers of surfaces (%d and %d) don't match!", surface_id, *sub_surface_id);
+         logerror(
+            "surface_set_layout: layers of surfaces (%d and %d) don't match!",
+            surface_id, *sub_surface_id);
          return;
       }
 
@@ -323,8 +324,8 @@ void App::surface_set_layout(int surface_id, optional<int> sub_surface_id) {
 
       auto &ss = this->controller->surfaces[*sub_surface_id];
 
-      logdebug("surface_set_layout for sub surface %u on layer %u", *sub_surface_id,
-               layer_id);
+      logdebug("surface_set_layout for sub surface %u on layer %u",
+               *sub_surface_id, layer_id);
 
       // configure surface to wxh dimensions
       ss->set_configuration(w, h);
@@ -401,8 +402,6 @@ char const *App::api_activate_surface(char const *drawing_name) {
          flush = true;
       }
 
-      l.state.s = LayoutState::Single;
-
       if (flush) {
          this->layout_commit();
       }
@@ -412,54 +411,58 @@ char const *App::api_activate_surface(char const *drawing_name) {
       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 (state.main == -1) {
-      this->emit_syncdraw(drawing_name);
-
-      this->surface_set_layout(*surface_id);
-      this->activate(*surface_id);  // XXX do we need to activate after enddraw?
-      state.main = *surface_id;
-      state.sub = -1;
-      state.s = LayoutState::Single;
-
-      this->layout_commit();
-      this->enqueue_flushdraw(state.main);
+      this->try_layout(
+         state, LayoutState{*surface_id}, [&] (LayoutState const &nl) {
+            this->surface_set_layout(*surface_id);
+            // XXX do we need to activate after enddraw?
+            this->activate(*surface_id);
+            state = nl;
+            this->layout_commit();
+            this->emit_syncdraw(drawing_name);
+            this->enqueue_flushdraw(state.main);
+         });
    } else {
       bool can_split = this->can_split(state, *surface_id);
 
-      if (state.sub == -1) {
          if (can_split) {
-            if (state.main != *surface_id) {
-               std::string main = std::move(*this->lookup_name(state.main));
-               this->emit_syncdraw(drawing_name);
-               this->emit_syncdraw(main.c_str());
-
-               this->surface_set_layout(state.main, surface_id);
-               this->activate(*surface_id);
-               state.sub = *surface_id;
-
-               this->layout_commit();
-               this->enqueue_flushdraw(state.main);
-               this->enqueue_flushdraw(state.sub);
-            }
+            this->try_layout(
+               state,
+               LayoutState{state.main, *surface_id},
+               [&] (LayoutState const &nl) {
+                  std::string main =
+                     std::move(*this->lookup_name(state.main));
+
+                  this->surface_set_layout(state.main, surface_id);
+                  this->activate(*surface_id);
+                  if (state.sub != -1) {
+                     this->deactivate(state.sub);
+                  }
+                  state = nl;
+
+                  this->layout_commit();
+                  this->emit_syncdraw(drawing_name);
+                  this->emit_syncdraw(main.c_str());
+                  this->enqueue_flushdraw(state.main);
+                  this->enqueue_flushdraw(state.sub);
+               });
          } else {
-            this->emit_syncdraw(drawing_name);
-
-            this->surface_set_layout(*surface_id);
-            this->deactivate(state.main);
-            this->activate(*surface_id);
-            this->deactivate(state.sub);
-            state.main = *surface_id;
-            state.sub = -1;
-            state.s = LayoutState::Single;
-
-            this->layout_commit();
-            this->enqueue_flushdraw(state.main);
+            this->try_layout(
+               state, LayoutState{*surface_id}, [&] (LayoutState const &nl) {
+                  this->surface_set_layout(*surface_id);
+                  this->deactivate(state.main);
+                  this->activate(*surface_id);
+                  if (state.sub != -1) {
+                     this->deactivate(state.sub);
+                  }
+                  state = nl;
+
+                  this->layout_commit();
+                  this->emit_syncdraw(drawing_name);
+                  this->enqueue_flushdraw(state.main);
+               });
          }
-      }
    }
 
    // no error
@@ -490,6 +493,8 @@ char const *App::api_deactivate_surface(char const *drawing_name) {
       return "No surface active";
    }
 
+   // XXX: check against main_surface, main_surface_name is the configuration
+   // item.
    if (*surface_id == this->layers.main_surface) {
       logdebug("Refusing to deactivate main_surface %d", *surface_id);
       return nullptr;
@@ -497,33 +502,38 @@ char const *App::api_deactivate_surface(char const *drawing_name) {
 
    if (state.main == *surface_id) {
       if (state.sub != -1) {
-         std::string sub = std::move(*this->lookup_name(state.sub));
-         this->emit_syncdraw(sub.c_str());
+         this->try_layout(
+            state, LayoutState{state.sub, -1}, [&] (LayoutState const &nl) {
+               std::string sub = std::move(*this->lookup_name(state.sub));
 
-         this->deactivate(*surface_id);
-         this->surface_set_layout(state.sub);
-         state.main = state.sub;
-         state.sub = -1;
-         state.s = LayoutState::Single;
+               this->deactivate(*surface_id);
+               this->surface_set_layout(state.sub);
+               state = nl;
 
-         this->layout_commit();
-         this->enqueue_flushdraw(state.sub);
+               this->layout_commit();
+               this->emit_syncdraw(sub.c_str());
+               this->enqueue_flushdraw(state.sub);
+            });
       } else {
-         this->deactivate(*surface_id);
-         state.main = -1;
+         this->try_layout(state, LayoutState{-1, -1}, [&] (LayoutState const &nl) {
+            this->deactivate(*surface_id);
+            state = nl;
+            this->layout_commit();
+         });
       }
    } else if (state.sub == *surface_id) {
-      std::string main = std::move(*this->lookup_name(state.main));
-      this->emit_syncdraw(main.c_str());
+      this->try_layout(
+         state, LayoutState{state.main, -1}, [&] (LayoutState const &nl) {
+            std::string main = std::move(*this->lookup_name(state.main));
 
-      this->deactivate(*surface_id);
-      this->deactivate(*surface_id);
-      this->surface_set_layout(state.main);
-      state.sub = -1;
-      state.s = LayoutState::Single;
+            this->deactivate(*surface_id);
+            this->surface_set_layout(state.main);
+            state = nl;
 
-      this->layout_commit();
-      this->enqueue_flushdraw(state.main);
+            this->layout_commit();
+            this->emit_syncdraw(main.c_str());
+            this->enqueue_flushdraw(state.main);
+         });
    } else {
       return "Surface is not active";
    }
@@ -545,7 +555,7 @@ void App::check_flushdraw(int surface_id) {
       logerror("Application %s (%d) has pending EndDraw call(s)!",
                n ? n->c_str() : "unknown-name", surface_id);
       std::swap(this->pending_end_draw[std::distance(
-         std::begin(this->pending_end_draw), i)],
+                   std::begin(this->pending_end_draw), i)],
                 this->pending_end_draw.back());
       this->pending_end_draw.resize(this->pending_end_draw.size() - 1);
    }
@@ -565,9 +575,7 @@ char const *App::api_enddraw(char const *drawing_name) {
    return "No EndDraw pending for surface";
 }
 
-void App::api_ping() {
-   this->dispatch_pending_events();
-}
+void App::api_ping() { this->dispatch_pending_events(); }
 
 //                      _          _   _____                 _
 //  _ __  _ __ _____  _(_) ___  __| | | ____|_   _____ _ __ | |_ ___
@@ -600,12 +608,19 @@ void App::surface_created(uint32_t surface_id) {
 void App::surface_removed(uint32_t surface_id) {
    logdebug("surface_id is %u", surface_id);
 
-   auto drawing_name = this->lookup_name(surface_id);
-   if (drawing_name) {
-      this->api_deactivate_surface(drawing_name->c_str());
+   // We cannot normally deactivate the main_surface, so be explicit
+   // about it:
+   if (int(surface_id) == this->layers.main_surface) {
+      this->deactivate_main_surface();
+   } else {
+      auto drawing_name = this->lookup_name(surface_id);
+      if (drawing_name) {
+         this->api_deactivate_surface(drawing_name->c_str());
+      }
    }
 
    this->id_alloc.remove_id(surface_id);
+   this->layers.remove_surface(surface_id);
 }
 
 void App::emit_activated(char const *label) {
@@ -663,7 +678,8 @@ result<int> App::api_request_surface(char const *drawing_name) {
 }
 
 void App::activate(int id) {
-   if (this->controller->sprops[id].visibility == 0) {
+   auto ip = this->controller->sprops.find(id);
+   if (ip != this->controller->sprops.end() && ip->second.visibility == 0) {
       this->controller->surfaces[id]->set_visibility(1);
       char const *label =
          this->lookup_name(id).value_or("unknown-name").c_str();
@@ -673,7 +689,8 @@ void App::activate(int id) {
 }
 
 void App::deactivate(int id) {
-   if (this->controller->sprops[id].visibility != 0) {
+   auto ip = this->controller->sprops.find(id);
+   if (ip != this->controller->sprops.end() && ip->second.visibility != 0) {
       this->controller->surfaces[id]->set_visibility(0);
       char const *label =
          this->lookup_name(id).value_or("unknown-name").c_str();
@@ -682,6 +699,11 @@ void App::deactivate(int id) {
    }
 }
 
+void App::deactivate_main_surface() {
+   this->layers.main_surface = -1;
+   this->api_deactivate_surface(this->layers.main_surface_name.c_str());
+}
+
 bool App::can_split(struct LayoutState const &state, int new_id) {
    if (state.main != -1 && state.main != new_id) {
       auto new_id_layer = this->layers.get_layer_id(new_id).value();
@@ -721,6 +743,14 @@ bool App::can_split(struct LayoutState const &state, int new_id) {
    return false;
 }
 
+void App::try_layout(struct LayoutState &state,
+                     struct LayoutState const &new_layout,
+                     std::function<void(LayoutState const &nl)> apply) {
+   if (this->policy.layout_is_valid(new_layout)) {
+      apply(new_layout);
+   }
+}
+
 //                  _             _ _            _                 _
 //   ___ ___  _ __ | |_ _ __ ___ | | | ___ _ __ | |__   ___   ___ | | _____
 //  / __/ _ \| '_ \| __| '__/ _ \| | |/ _ \ '__|| '_ \ / _ \ / _ \| |/ / __|
@@ -735,4 +765,10 @@ void controller_hooks::surface_removed(uint32_t surface_id) {
    this->app->surface_removed(surface_id);
 }
 
+void controller_hooks::surface_visibility(uint32_t surface_id, uint32_t v) {
+}
+
+void controller_hooks::surface_destination_rectangle(uint32_t surface_id, uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
+}
+
 }  // namespace wm