From 793166f301c058503c37553204c4d4c2b106ff23 Mon Sep 17 00:00:00 2001 From: Marcus Fritzsch Date: Tue, 12 Sep 2017 11:29:27 +0200 Subject: [PATCH] app/main: dispatch wayland events using ping(), WIP on split layout * 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 --- layers.json | 6 ++ src/app.cpp | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- src/app.hpp | 28 ++++++-- src/main.cpp | 19 +++-- 4 files changed, 245 insertions(+), 38 deletions(-) diff --git a/layers.json b/layers.json index d452971..0d5a817 100644 --- a/layers.json +++ b/layers.json @@ -47,6 +47,12 @@ "main_match": "^App Navi Map", "sub_match": "^App Navi Guidance", "priority": 1000 + }, + { + "name": "App1 example", + "main_match": "^App1", + "sub_match": ".*", + "priority": 1000 } ] }, diff --git a/src/app.cpp b/src/app.cpp index 98db0b4..e42d05f 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -32,8 +32,9 @@ #include #include #include -#include #include +#include +#include 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(r, name, v); + this->controller = + std::make_unique(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 &s, int x, int y, int w, int h) { - { // XXX: Work around weston redraw issues +void redraw_fix(App *app, std::unique_ptr &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 &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(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(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 App::request_surface(char const *drawing_name) { auto lid = this->layers.get_layer_id(std::string(drawing_name)); @@ -446,7 +596,8 @@ result 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; } // _ _ _ _ _ diff --git a/src/app.hpp b/src/app.hpp index 37122ef..00a2ba2 100644 --- a/src/app.hpp +++ b/src/app.hpp @@ -18,10 +18,12 @@ #define TMCAGLWM_APP_HPP #include + +#include #include #include #include -#include +#include #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 layouts; layer_map layers; // ID allocation and proxy methods for lookup @@ -135,6 +148,8 @@ struct App { struct LayoutState state; + std::atomic 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 request_surface(char const *drawing_name); diff --git a/src/main.cpp b/src/main.cpp index d944c24..4bb53b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,7 +30,7 @@ extern "C" { } namespace { -std::mutex binding_m; +//std::mutex binding_m; struct afb_instance { std::unique_ptr 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 guard(binding_m); + ST(); + // std::lock_guard 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); } } -- 2.16.6