diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 129 | ||||
-rw-r--r-- | src/afb_binding_api.cpp | 114 | ||||
-rw-r--r-- | src/app.cpp | 733 | ||||
-rw-r--r-- | src/app.hpp | 188 | ||||
-rw-r--r-- | src/config.cpp | 26 | ||||
-rw-r--r-- | src/config.hpp | 49 | ||||
-rw-r--r-- | src/controller_hooks.hpp | 40 | ||||
-rw-r--r-- | src/json_helper.cpp | 102 | ||||
-rw-r--r-- | src/json_helper.hpp | 30 | ||||
-rw-r--r-- | src/layers.cpp | 154 | ||||
-rw-r--r-- | src/layers.hpp | 118 | ||||
-rw-r--r-- | src/layout.cpp | 17 | ||||
-rw-r--r-- | src/layout.hpp | 43 | ||||
-rwxr-xr-x | src/main.cpp | 175 | ||||
-rw-r--r-- | src/policy.hpp | 35 | ||||
-rw-r--r-- | src/redraw_fixer.cpp | 128 | ||||
-rw-r--r-- | src/result.hpp | 77 | ||||
-rw-r--r-- | src/util.cpp | 39 | ||||
-rw-r--r-- | src/util.hpp | 109 | ||||
-rw-r--r-- | src/wayland.cpp | 765 | ||||
-rw-r--r-- | src/wayland.hpp | 354 |
21 files changed, 3425 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..7bbc35d --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,129 @@ +# +# Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +wlproto(IVI_CON ivi-controller) + +include(FindPkgConfig) +pkg_check_modules(AFB REQUIRED afb-daemon) +pkg_check_modules(SD REQUIRED libsystemd>=222) + +# We do not want a prefix for our module +set(CMAKE_SHARED_MODULE_PREFIX "") + +add_custom_command( + OUTPUT afb_binding_api.hpp afb_binding_glue.inl + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../generate-binding-glue.py + COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/../generate-binding-glue.py) + +set(TARGETS_WM windowmanager-service) + +add_library(${TARGETS_WM} MODULE + main.cpp + wayland.cpp + wayland.hpp + util.cpp + util.hpp + layout.cpp + layout.hpp + ${IVI_CON_PROTO} + json_helper.cpp + json_helper.hpp + app.hpp app.cpp + afb_binding_api.cpp + result.hpp + afb_binding_api.hpp + afb_binding_glue.inl + layers.cpp + layers.hpp + controller_hooks.hpp + config.cpp + config.hpp + policy.hpp) + +target_include_directories(${TARGETS_WM} + PRIVATE + ${AFB_INCLUDE_DIRS} + ${SD_INCLUDE_DIRS} + ../include + ../src) + +target_link_libraries(${TARGETS_WM} + PRIVATE + ${AFB_LIBRARIES} + ${WLC_LIBRARIES} + ${SD_LIBRARIES}) + +target_compile_definitions(${TARGETS_WM} + PRIVATE + AFB_BINDING_VERSION=2 + # We do not want source location of messages + AFB_BINDING_PRAGMA_NO_VERBOSE_DETAILS + WINMAN_VERSION_STRING="${PACKAGE_VERSION}" + _GNU_SOURCE) # XXX should I define this here?! + +if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Release") + target_compile_definitions(${TARGETS_WM} + PRIVATE + _GLIBCXX_DEBUG) +endif() + +target_compile_options(${TARGETS_WM} + PRIVATE + -Wall -Wextra -Wno-unused-parameter -Wno-comment) + +set_target_properties(${TARGETS_WM} + PROPERTIES + # INTERPROCEDURAL_OPTIMIZATION ON + + CXX_EXTENSIONS OFF + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + + C_EXTENSIONS OFF + C_STANDARD 99 + C_STANDARD_REQUIRED ON + + LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/../export.map") + +if (LINK_LIBCXX) + set_target_properties(${TARGETS_WM} + PROPERTIES + LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/../export.map -lc++") +endif() + +if (NOT ${SANITIZER_MODE} STREQUAL "none" AND NOT ${SANITIZER_MODE} STREQUAL "") + target_compile_options(${TARGETS_WM} + PRIVATE + -fsanitize=${SANITIZER_MODE} -g -fno-omit-frame-pointer) + set_target_properties(${TARGETS_WM} + PROPERTIES + LINK_FLAGS "-fsanitize=${SANITIZER_MODE} -g") +endif() + +if(NOT EXISTS ${PROJECT_BINARY_DIR}/package) + add_custom_command(TARGET ${TARGETS_WM} POST_BUILD + COMMAND cp -rf ${PROJECT_SOURCE_DIR}/package ${PROJECT_BINARY_DIR} + ) +endif() + +add_custom_command(TARGET ${TARGETS_WM} POST_BUILD + COMMAND mkdir -p ${PROJECT_BINARY_DIR}/package/root/lib + COMMAND cp -rf ${PROJECT_BINARY_DIR}/src/${TARGETS_WM}.so ${PROJECT_BINARY_DIR}/package/root/lib +) + +add_custom_target(package DEPENDS ${PROJECT_BINARY_DIR}/package/root + COMMAND wgtpkg-pack -f -o ${PROJECT_BINARY_DIR}/package/${TARGETS_WM}-2017.wgt ${PROJECT_BINARY_DIR}/package/root +) diff --git a/src/afb_binding_api.cpp b/src/afb_binding_api.cpp new file mode 100644 index 0000000..9311700 --- /dev/null +++ b/src/afb_binding_api.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "app.hpp" +#include "json_helper.hpp" + +#include <csignal> + +#include <json.hpp> + +using json = nlohmann::json; + +#include <json-c/json.h> + +namespace wm { +// _ _ _ _ _ _ _ +// | |__ (_)_ __ __| (_)_ __ __ _ __ _ _ __ (_) (_)_ __ ___ _ __ | | +// | '_ \| | '_ \ / _` | | '_ \ / _` | / _` | '_ \| | | | '_ ` _ \| '_ \| | +// | |_) | | | | | (_| | | | | | (_| | | (_| | |_) | | | | | | | | | |_) | | +// |_.__/|_|_| |_|\__,_|_|_| |_|\__, |___\__,_| .__/|_| |_|_| |_| |_| .__/|_| +// |___/_____| |_| |_| +binding_api::result_type binding_api::requestsurface( + char const *drawing_name) { + auto r = this->app->api_request_surface(drawing_name); + if (r.is_err()) { + return Err<json_object *>(r.unwrap_err()); + } + return Ok(json_object_new_int(r.unwrap())); +} + +binding_api::result_type binding_api::activatesurface( + char const *drawing_name) { + logdebug("%s drawing_name %s", __func__, drawing_name); + auto r = this->app->api_activate_surface(drawing_name); + if (r != nullptr) { + logdebug("%s failed with error: %s", __func__, r); + return Err<json_object *>(r); + } + return Ok(json_object_new_object()); +} + +binding_api::result_type binding_api::deactivatesurface(char const* drawing_name) { + logdebug("%s drawing_name %s", __func__, drawing_name); + auto r = this->app->api_deactivate_surface(drawing_name); + if (r != nullptr) { + logdebug("%s failed with error: %s", __func__, r); + return Err<json_object *>(r); + } + return Ok(json_object_new_object()); +} + +binding_api::result_type binding_api::enddraw(char const* drawing_name) { + logdebug("%s drawing_name %s", __func__, drawing_name); + auto r = this->app->api_enddraw(drawing_name); + if (r != nullptr) { + logdebug("%s failed with error: %s", __func__, r); + return Err<json_object *>(r); + } + return Ok(json_object_new_object()); +} + +binding_api::result_type binding_api::list_drawing_names() { + logdebug("%s", __func__); + json j = this->app->id_alloc.name2id; + return Ok(json_tokener_parse(j.dump().c_str())); +} + +binding_api::result_type binding_api::debug_layers() { + logdebug("%s", __func__); + return Ok(json_tokener_parse(this->app->layers.to_json().dump().c_str())); +} + +binding_api::result_type binding_api::debug_surfaces() { + logdebug("%s", __func__); + return Ok(to_json(this->app->controller->sprops)); +} + +binding_api::result_type binding_api::debug_status() { + logdebug("%s", __func__); + json_object *jr = json_object_new_object(); + json_object_object_add(jr, "surfaces", + to_json(this->app->controller->sprops)); + json_object_object_add(jr, "layers", to_json(this->app->controller->lprops)); + return Ok(jr); +} + +binding_api::result_type binding_api::debug_terminate() { + logdebug("%s", __func__); + if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr) { + raise(SIGKILL); // XXX afb-daemon kills it's pgroup using TERM, which + // doesn't play well with perf + } + return Ok(json_object_new_object()); +} + +binding_api::result_type binding_api::ping() { + this->app->api_ping(); + return Ok(json_object_new_object()); +} + +} // namespace wm diff --git a/src/app.cpp b/src/app.cpp new file mode 100644 index 0000000..75df8d7 --- /dev/null +++ b/src/app.cpp @@ -0,0 +1,733 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "app.hpp" +#include "json_helper.hpp" +#include "layers.hpp" +#include "layout.hpp" +#include "util.hpp" +#include "wayland.hpp" + +#include <cstdio> +#include <memory> + +#include <cassert> + +#include <json-c/json.h> + +#include <algorithm> +#include <bits/signum.h> +#include <csignal> +#include <fstream> +#include <json.hpp> +#include <regex> +#include <thread> + +namespace wm { + +namespace { + +using nlohmann::json; + +result<json> file_to_json(char const *filename) { + std::ifstream i(filename); + if (i.fail()) { + return Err<json>("Could not open config file"); + } + json j; + i >> j; + return Ok(j); +} + +struct result<layer_map> load_layer_map(char const *filename) { + logdebug("loading IDs from %s", filename); + + auto j = file_to_json(filename); + if (j.is_err()) { + return Err<layer_map>(j.unwrap_err()); + } + json jids = j.unwrap(); + + return to_layer_map(jids); +} + +} // namespace + +// _ _ _ _ +// ___| | __ _ ___ ___ / \ _ __ _ __ (_)_ __ ___ _ __ | | +// / __| |/ _` / __/ __| / _ \ | '_ \| '_ \ | | '_ ` _ \| '_ \| | +// | (__| | (_| \__ \__ \ / ___ \| |_) | |_) | | | | | | | | |_) | | +// \___|_|\__,_|___/___/ /_/ \_\ .__/| .__/ |_|_| |_| |_| .__/|_| +// |_| |_| |_| +App::App(wl::display *d) + : api{this}, + chooks{this}, + display{d}, + controller{}, + outputs(), + config(), + layers(), + id_alloc{}, + pending_events(false), + policy{} { + try { + { + auto l = load_layer_map( + this->config.get_string("layers.json").value().c_str()); + if (l.is_ok()) { + this->layers = l.unwrap(); + } else { + logerror("%s", l.err().value()); + } + } + } catch (std::exception &e) { + logerror("Loading of configuration failed: %s", e.what()); + } +} + +int App::init() { + if (!this->display->ok()) { + return -1; + } + + if (this->layers.mapping.empty()) { + logerror("No surface -> layer mapping loaded"); + return -1; + } + + this->display->add_global_handler( + "wl_output", [this](wl_registry *r, uint32_t name, uint32_t v) { + this->outputs.emplace_back(std::make_unique<wl::output>(r, name, v)); + }); + + 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); + + // Init controller hooks + this->controller->chooks = &this->chooks; + + // XXX: This protocol needs the output, so lets just add our mapping + // here... + this->controller->add_proxy_to_id_mapping( + this->outputs.back()->proxy.get(), + wl_proxy_get_id(reinterpret_cast<struct wl_proxy *>( + this->outputs.back()->proxy.get()))); + }); + + // First level objects + this->display->roundtrip(); + // Second level objects + this->display->roundtrip(); + // Third level objects + this->display->roundtrip(); + + return init_layers(); +} + +int App::dispatch_events() { + if (this->dispatch_events() == 0) { + return 0; + } + + int ret = this->display->dispatch(); + if (ret == -1) { + logerror("wl_display_dipatch() returned error %d", + this->display->get_error()); + return -1; + } + this->display->flush(); + + return 0; +} + +int App::dispatch_pending_events() { + if (this->pop_pending_events()) { + this->display->dispatch_pending(); + return 0; +} + return -1; +} + +bool App::pop_pending_events() { + bool x{true}; + return this->pending_events.compare_exchange_strong( + x, false, std::memory_order_consume); +} + +void App::set_pending_events() { + this->pending_events.store(true, std::memory_order_release); +} + +optional<int> App::lookup_id(char const *name) { + return this->id_alloc.lookup(std::string(name)); +} +optional<std::string> App::lookup_name(int id) { + return this->id_alloc.lookup(id); +} + +// _ _ _ _ _ ____ +// (_)_ __ (_) |_ | | __ _ _ _ ___ _ _| |_ / /\ \ +// | | '_ \| | __| | |/ _` | | | |/ _ \| | | | __| | | | +// | | | | | | |_ | | (_| | |_| | (_) | |_| | |_| | | | +// |_|_| |_|_|\__|___|_|\__,_|\__, |\___/ \__,_|\__| | | | +// |_____| |___/ \_\/_/ +int App::init_layers() { + if (!this->controller) { + logerror("ivi_controller global not available"); + return -1; + } + + if (this->outputs.empty()) { + logerror("no output was set up!"); + return -1; + } + + auto &c = this->controller; + + auto &o = this->outputs.front(); + auto &s = c->screens.begin()->second; + auto &layers = c->layers; + + // XXX: Write output dimensions to ivi controller... + c->output_size = genivi::size{uint32_t(o->width), uint32_t(o->height)}; + + // Clear scene + layers.clear(); + + // Clear screen + s->clear(); + + // Quick and dirty setup of layers + // XXX: This likely needs to be sorted by order (note, we don't (yet?) + // do any zorder arrangement). + for (auto const &i : this->layers.mapping) { + c->layer_create(i.second.layer_id, o->width, o->height); + auto &l = layers[i.second.layer_id]; + l->set_destination_rectangle(0, 0, o->width, o->height); + l->set_visibility(1); + logdebug("Setting up layer %s (%d) for surface role match \"%s\"", + i.second.name.c_str(), i.second.layer_id, i.second.role.c_str()); + } + + // Add layers to screen (XXX: are they sorted correctly?) + s->set_render_order(this->layers.layers); + + this->layout_commit(); + + return 0; +} + +void App::surface_set_layout(int surface_id, optional<int> sub_surface_id) { + if (!this->controller->surface_exists(surface_id)) { + logerror("Surface %d does not exist", 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!", surface_id); + return; + } + + uint32_t layer_id = *o_layer_id; + + 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; + int y = rect.y; + int w = rect.w; + int h = rect.h; + + // 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; + } + + 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); + return; + } + + 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; + } + + auto &ss = this->controller->surfaces[*sub_surface_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); + // 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); + } + + logdebug("surface_set_layout for surface %u on layer %u", surface_id, + layer_id); + + // 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); + + logdebug("Surface %u now on layer %u with rect { %d, %d, %d, %d }", + surface_id, layer_id, x, y, w, h); +} + +void App::layout_commit() { + this->controller->commit_changes(); + this->display->flush(); +} + +char const *App::api_activate_surface(char const *drawing_name) { + ST(); + auto const &surface_id = this->lookup_id(drawing_name); + + if (!surface_id) { + return "Surface does not exist"; + } + + if (!this->controller->surface_exists(*surface_id)) { + return "Surface does not exist in controller!"; + } + + auto layer_id = this->layers.get_layer_id(*surface_id); + + if (!layer_id) { + return "Surface is not on any layer!"; + } + + auto o_state = *this->layers.get_layout_state(*surface_id); + + if (o_state == nullptr) { + return "Could not find layer for surface"; +} + + struct LayoutState &state = *o_state; + + // disable layers that are above our current layer + for (auto const &l : this->layers.mapping) { + if (l.second.layer_id <= *layer_id) { + continue; + } + + bool flush = false; + if (l.second.state.main != -1) { + this->deactivate(l.second.state.main); + l.second.state.main = -1; + flush = true; + } + + if (l.second.state.sub != -1) { + this->deactivate(l.second.state.sub); + l.second.state.sub = -1; + flush = true; + } + + if (flush) { + this->layout_commit(); + } + } + + if (state.main == *surface_id || state.sub == *surface_id) { + return "Surface already active"; + } + + if (state.main == -1) { + this->try_layout( + state, LayoutState{*surface_id}, [&] (LayoutState const &nl) { + this->surface_set_layout(*surface_id); + // XXX do we need to activate after enddraw? + state = nl; + this->emit_syncdraw(drawing_name); + this->enqueue_flushdraw(state.main); + }); + } else { + bool can_split = this->can_split(state, *surface_id); + + if (can_split) { + 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); + if (state.sub != -1) { + this->deactivate(state.sub); + } + state = nl; + + this->emit_syncdraw(drawing_name); + this->emit_syncdraw(main.c_str()); + this->enqueue_flushdraw(state.main); + this->enqueue_flushdraw(state.sub); + }); + } else { + this->try_layout( + state, LayoutState{*surface_id}, [&] (LayoutState const &nl) { + this->surface_set_layout(*surface_id); + this->deactivate(state.main); + if (state.sub != -1) { + this->deactivate(state.sub); + } + state = nl; + + this->emit_syncdraw(drawing_name); + this->enqueue_flushdraw(state.main); + }); + } + } + + // no error + return nullptr; +} + +char const *App::api_deactivate_surface(char const *drawing_name) { + ST(); + auto const &surface_id = this->lookup_id(drawing_name); + + if (!surface_id) { + return "Surface does not exist"; + } + + if (*surface_id == this->layers.main_surface) { + return "Cannot deactivate main_surface"; + } + + auto o_state = *this->layers.get_layout_state(*surface_id); + + if (o_state == nullptr) { + return "Could not find layer for surface"; + } + + struct LayoutState &state = *o_state; + + if (state.main == -1) { + 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; + } + + if (state.main == *surface_id) { + if (state.sub != -1) { + 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 = nl; + + this->layout_commit(); + this->emit_syncdraw(sub.c_str()); + this->enqueue_flushdraw(state.sub); + }); + } else { + 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) { + 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->surface_set_layout(state.main); + state = nl; + + this->layout_commit(); + this->emit_syncdraw(main.c_str()); + this->enqueue_flushdraw(state.main); + }); + } else { + return "Surface is not active"; + } + + return nullptr; +} + +void App::enqueue_flushdraw(int surface_id) { + this->check_flushdraw(surface_id); + logdebug("Enqueuing EndDraw for surface_id %d", surface_id); + this->pending_end_draw.push_back(surface_id); +} + +void App::check_flushdraw(int surface_id) { + auto i = std::find(std::begin(this->pending_end_draw), + std::end(this->pending_end_draw), surface_id); + if (i != std::end(this->pending_end_draw)) { + auto n = this->lookup_name(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)], + this->pending_end_draw.back()); + this->pending_end_draw.resize(this->pending_end_draw.size() - 1); + } +} + +char const *App::api_enddraw(char const *drawing_name) { + for (unsigned i = 0, iend = this->pending_end_draw.size(); i < iend; i++) { + auto n = this->lookup_name(this->pending_end_draw[i]); + if (n && *n == drawing_name) { + std::swap(this->pending_end_draw[i], this->pending_end_draw[iend - 1]); + this->pending_end_draw.resize(iend - 1); + // XXX: Please tell the compositor to thaw the surface... + this->activate(this->pending_end_draw[i]); + this->layout_commit(); + this->emit_flushdraw(drawing_name); + return nullptr; + } + } + return "No EndDraw pending for surface"; +} + +void App::api_ping() { this->dispatch_pending_events(); } + +// _ _ _____ _ +// _ __ _ __ _____ _(_) ___ __| | | ____|_ _____ _ __ | |_ ___ +// | '_ \| '__/ _ \ \/ / |/ _ \/ _` | | _| \ \ / / _ \ '_ \| __/ __| +// | |_) | | | (_) > <| | __/ (_| | | |___ \ V / __/ | | | |_\__ \ +// | .__/|_| \___/_/\_\_|\___|\__,_| |_____| \_/ \___|_| |_|\__|___/ +// |_| +void App::surface_created(uint32_t surface_id) { + auto layer_id = this->layers.get_layer_id(surface_id); + if (!layer_id) { + logdebug("Newly created surfce %d is not associated with any layer!", + surface_id); + return; + } + + logdebug("surface_id is %u, layer_id is %u", surface_id, *layer_id); + + this->controller->layers[*layer_id]->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->api_activate_surface( + this->lookup_name(surface_id).value_or("unknown-name").c_str()); + }*/ +} + +void App::surface_removed(uint32_t surface_id) { + logdebug("surface_id is %u", surface_id); + + // 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) { + this->api.send_event("active", label); +} + +void App::emit_deactivated(char const *label) { + this->api.send_event("inactive", label); +} + +void App::emit_syncdraw(char const *label) { + this->api.send_event("syncdraw", label); +} + +void App::emit_flushdraw(char const *label) { + this->api.send_event("flushdraw", label); +} + +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, false); +} + +void App::emit_visible(char const *label) { return emit_visible(label, true); } + +result<int> App::api_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->lookup_id(drawing_name); + if (!rname) { + // name does not exist yet, allocate surface id... + auto id = int(this->id_alloc.generate_id(drawing_name)); + this->layers.add_surface(id, *lid); + + // XXX: we set the main_surface[_name] here and now, + // not sure if we want this, but it worked so far. + if (!this->layers.main_surface_name.empty() && + this->layers.main_surface_name == drawing_name) { + this->layers.main_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"); +} + +void App::activate(int id) { + 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(); + this->emit_activated(label); + this->emit_visible(label); + } +} + +void App::deactivate(int id) { + 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(); + this->emit_deactivated(label); + this->emit_invisible(label); + } +} + +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(); + auto current_id_layer = this->layers.get_layer_id(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(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)) { + logdebug("layout matched!"); + return true; +} + } + } + } + + 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); + } +} + +// _ _ _ _ _ +// ___ ___ _ __ | |_ _ __ ___ | | | ___ _ __ | |__ ___ ___ | | _____ +// / __/ _ \| '_ \| __| '__/ _ \| | |/ _ \ '__|| '_ \ / _ \ / _ \| |/ / __| +// | (_| (_) | | | | |_| | | (_) | | | __/ | | | | | (_) | (_) | <\__ \ +// \___\___/|_| |_|\__|_| \___/|_|_|\___|_|___|_| |_|\___/ \___/|_|\_\___/ +// |_____| +void controller_hooks::surface_created(uint32_t surface_id) { + this->app->surface_created(surface_id); +} + +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 diff --git a/src/app.hpp b/src/app.hpp new file mode 100644 index 0000000..9424d9f --- /dev/null +++ b/src/app.hpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_APP_HPP +#define TMCAGLWM_APP_HPP + +#include <json-c/json.h> + +#include <atomic> +#include <memory> +#include <unordered_map> +#include <unordered_set> +#include <experimental/optional> + +#include "afb_binding_api.hpp" +#include "config.hpp" +#include "controller_hooks.hpp" +#include "layers.hpp" +#include "layout.hpp" +#include "policy.hpp" +#include "result.hpp" +#include "wayland.hpp" + +namespace wl { +struct display; +} + +namespace genivi { +struct controller; +} + +namespace wm { + +using std::experimental::optional; + +struct id_allocator { + unsigned next = 1; + + // Surfaces that where requested but not yet created + std::unordered_map<unsigned, std::string> id2name; + // std::unordered_set<unsigned> pending_surfaces; + std::unordered_map<std::string, unsigned> name2id; + + id_allocator(id_allocator const &) = delete; + id_allocator(id_allocator &&) = delete; + id_allocator &operator=(id_allocator const &); + id_allocator &operator=(id_allocator &&) = delete; + + // Insert and return a new ID + unsigned generate_id(std::string const &name) { + unsigned sid = this->next++; + this->id2name[sid] = name; + // this->pending_surfaces.insert({sid}); + this->name2id[name] = sid; + logdebug("allocated new id %u with name %s", sid, name.c_str()); + return sid; + } + + // Lookup by ID or by name + optional<unsigned> lookup(std::string const &name) const { + auto i = this->name2id.find(name); + return i == this->name2id.end() ? nullopt : optional<unsigned>(i->second); + } + + optional<std::string> lookup(unsigned id) const { + auto i = this->id2name.find(id); + return i == this->id2name.end() ? nullopt + : optional<std::string>(i->second); + } + + // Remove a surface id and name + // I don't think I will need this, do I? + void remove_id(std::string const &name) { + auto i = this->name2id.find(name); + if (i != this->name2id.end()) { + this->id2name.erase(i->second); + this->name2id.erase(i); + } + } + + void remove_id(unsigned id) { + auto i = this->id2name.find(id); + if (i != this->id2name.end()) { + this->name2id.erase(i->second); + this->id2name.erase(i); + } + } +}; + +struct App { + struct binding_api api; + struct controller_hooks chooks; + + // This is the one thing, we do not own. + struct wl::display *display; + + std::unique_ptr<struct genivi::controller> controller; + std::vector<std::unique_ptr<struct wl::output>> outputs; + + struct config config; + + // track current layouts separately + layer_map layers; + + // ID allocation and proxy methods for lookup + struct id_allocator id_alloc; + + // Set by AFB API when wayland events need to be dispatched + std::atomic<bool> pending_events; + + std::vector<int> pending_end_draw; + + Policy policy; + + explicit App(wl::display *d); + ~App() = default; + + App(App const &) = delete; + App &operator=(App const &) = delete; + App(App &&) = delete; + App &operator=(App &&) = delete; + + int init(); + + int dispatch_events(); + int dispatch_pending_events(); + + void set_pending_events(); + + result<int> api_request_surface(char const *drawing_name); + char const *api_activate_surface(char const *drawing_name); + char const *api_deactivate_surface(char const *drawing_name); + char const *api_enddraw(char const *drawing_name); + void api_ping(); + + // Events from the compositor we are interested in + void surface_created(uint32_t surface_id); + void surface_removed(uint32_t surface_id); + +private: + optional<int> lookup_id(char const *name); + optional<std::string> lookup_name(int id); + + bool pop_pending_events(); + + void enqueue_flushdraw(int surface_id); + void check_flushdraw(int surface_id); + + int init_layers(); + + void surface_set_layout(int surface_id, optional<int> sub_surface_id = nullopt); + void layout_commit(); + + // TMC WM Events to clients + void emit_activated(char const *label); + void emit_deactivated(char const *label); + void emit_syncdraw(char const *label); + void emit_flushdraw(char const *label); + void emit_visible(char const *label, bool is_visible); + void emit_invisible(char const *label); + void emit_visible(char const *label); + + void activate(int id); + void deactivate(int id); + void deactivate_main_surface(); + + bool can_split(struct LayoutState const &state, int new_id); + void try_layout(struct LayoutState &state, + struct LayoutState const &new_layout, + std::function<void(LayoutState const &nl)> apply); +}; + +} // namespace wm + +#endif // TMCAGLWM_APP_HPP diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..d5a549a --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.hpp" + +namespace wm { + +config::config() : cfg() { + // Supply default values for these... + this->cfg["layers.json"] = getenv("LAYERS_JSON") ?: "/etc/windowmanager/layers.json"; +} + +} // namespace wm diff --git a/src/config.hpp b/src/config.hpp new file mode 100644 index 0000000..d470b85 --- /dev/null +++ b/src/config.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_CONFIG_HPP +#define TMCAGLWM_CONFIG_HPP + +#include <experimental/optional> +#include <map> + +namespace wm { + +using std::experimental::optional; +using std::experimental::nullopt; + +struct config { + typedef std::map<std::string, std::string> map; + + map cfg; + + config(); + + optional<std::string> get_string(char const *s) { + auto i = this->cfg.find(s); + return i != this->cfg.end() ? optional<std::string>(i->second) : nullopt; + } + + optional<int> get_int(char const *s) { + auto i = this->cfg.find(s); + return i != this->cfg.end() ? optional<int>(std::stoi(i->second)) + : nullopt; + } +}; + +} // namespace wm + +#endif // TMCAGLWM_CONFIG_HPP diff --git a/src/controller_hooks.hpp b/src/controller_hooks.hpp new file mode 100644 index 0000000..31962ee --- /dev/null +++ b/src/controller_hooks.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_CONTROLLER_HOOKS_HPP +#define TMCAGLWM_CONTROLLER_HOOKS_HPP + +#include <cstdint> + +#include <functional> + +namespace wm { + +struct App; + +struct controller_hooks { + struct App *app; + + void surface_created(uint32_t surface_id); + + void surface_removed(uint32_t surface_id); + void surface_visibility(uint32_t surface_id, uint32_t v); + void surface_destination_rectangle(uint32_t surface_id, uint32_t x, uint32_t y, uint32_t w, uint32_t h); +}; + +} // namespace wm + +#endif // TMCAGLWM_CONTROLLER_HOOKS_HPP diff --git a/src/json_helper.cpp b/src/json_helper.cpp new file mode 100644 index 0000000..179c8cc --- /dev/null +++ b/src/json_helper.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "json_helper.hpp" + +#include <json.h> + +json_object *to_json(genivi::surface_properties const &s) { + // auto j = json::object({ + auto j = json_object_new_object(); + + // {"id", s.id}, + json_object_object_add(j, "id", json_object_new_int(s.id)); + + // {"size", {{"width", s.size.w}, {"height", s.size.h}}}, + auto jsize = json_object_new_object(); + json_object_object_add(jsize, "width", json_object_new_int(s.size.w)); + json_object_object_add(jsize, "height", json_object_new_int(s.size.h)); + json_object_object_add(j, "size", jsize); + + // {"dst", + // {{"width", s.dst_rect.w}, + // {"height", s.dst_rect.h}, + // {"x", s.dst_rect.x}, + // {"y", s.dst_rect.y}}}, + auto jdst = json_object_new_object(); + json_object_object_add(jdst, "width", json_object_new_int(s.dst_rect.w)); + json_object_object_add(jdst, "height", json_object_new_int(s.dst_rect.h)); + json_object_object_add(jdst, "x", json_object_new_int(s.dst_rect.x)); + json_object_object_add(jdst, "y", json_object_new_int(s.dst_rect.y)); + json_object_object_add(j, "dst", jdst); + + // {"src", + // {{"width", s.src_rect.w}, + // {"height", s.src_rect.h}, + // {"x", s.src_rect.x}, + // {"y", s.src_rect.y}}}, + auto jsrc = json_object_new_object(); + json_object_object_add(jsrc, "width", json_object_new_int(s.src_rect.w)); + json_object_object_add(jsrc, "height", json_object_new_int(s.src_rect.h)); + json_object_object_add(jsrc, "x", json_object_new_int(s.src_rect.x)); + json_object_object_add(jsrc, "y", json_object_new_int(s.src_rect.y)); + json_object_object_add(j, "src", jsrc); + + // {"visibility", s.visibility}, + json_object_object_add( + j, "visibility", + json_object_new_boolean(static_cast<json_bool>(s.visibility == 1))); + + // {"opacity", s.opacity}, + json_object_object_add(j, "opacity", json_object_new_double(s.opacity)); + + // {"orientation", s.orientation}, + json_object_object_add(j, "orientation", json_object_new_int(s.orientation)); + + // }); + return j; +} + +json_object *to_json(genivi::screen const *s) { + auto o = json_object_new_object(); + json_object_object_add(o, "id", json_object_new_int(s->id)); + return o; +} + +template <typename T> +json_object *to_json_(T const &s) { + auto a = json_object_new_array(); + + if (!s.empty()) { + for (auto const &i : s) { + json_object_array_add(a, to_json(i.second)); + } + } + + return a; +} + +json_object *to_json(genivi::controller::props_map const &s) { + return to_json_(s); +} + +json_object *to_json(std::vector<uint32_t> const &v) { + auto a = json_object_new_array(); + for (const auto i : v) { + json_object_array_add(a, json_object_new_int(i)); + } + return a; +} diff --git a/src/json_helper.hpp b/src/json_helper.hpp new file mode 100644 index 0000000..15d72c3 --- /dev/null +++ b/src/json_helper.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_JSON_HELPER_HPP +#define TMCAGLWM_JSON_HELPER_HPP + +#include "result.hpp" +#include "wayland.hpp" +#include <json.hpp> + +struct json_object; + +json_object *to_json(genivi::screen const *s); +json_object *to_json(genivi::controller::props_map const &s); +json_object *to_json(std::vector<uint32_t> const &v); + +#endif // TMCAGLWM_JSON_HELPER_HPP diff --git a/src/layers.cpp b/src/layers.cpp new file mode 100644 index 0000000..9219766 --- /dev/null +++ b/src/layers.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <algorithm> +#include <regex> + +#include "json_helper.hpp" +#include "layers.hpp" +#include "util.hpp" + +namespace wm { + +using json = nlohmann::json; + +layer::layer(nlohmann::json const &j) { + this->role = j["role"]; + this->name = j["name"]; + this->layer_id = j["layer_id"]; + this->rect = genivi::full_rect; + if (j["area"]["type"] == "rect") { + auto jr = j["area"]["rect"]; + this->rect = genivi::rect{ + jr["width"], jr["height"], jr["x"], jr["y"], + }; + } + auto split_layouts = j.find("split_layouts"); + if (split_layouts != j.end()) { + auto &sls = j["split_layouts"]; + // this->layouts.reserve(sls.size()); + std::transform(std::cbegin(sls), std::cend(sls), + std::back_inserter(this->layouts), [this](json const &sl) { + struct split_layout l { + sl["name"], sl["main_match"], sl["sub_match"] }; + logdebug( + "layer %d add split_layout \"%s\" (main: \"%s\") (sub: " + "\"%s\")", this->layer_id, + l.name.c_str(), l.main_match.c_str(), + l.sub_match.c_str()); + return l; + }); + } +} + +struct result<struct layer_map> to_layer_map(nlohmann::json const &j) { + try { + layer_map stl{}; + auto m = j["mappings"]; + + std::transform(std::cbegin(m), std::cend(m), + std::inserter(stl.mapping, stl.mapping.end()), + [](nlohmann::json const &j) { + return std::pair<int, struct layer>( + j.value("layer_id", -1), 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](std::pair<int, struct layer> const &k) { + stl.roles.emplace_back( + std::make_pair(k.second.role, k.second.layer_id)); + return unsigned(k.second.layer_id); + }); + + std::sort(stl.layers.begin(), stl.layers.end()); + + for (auto i : stl.mapping) { + if (i.second.name.empty()) { + return Err<struct layer_map>("Found mapping w/o name"); + } + if (i.second.layer_id == -1) { + return Err<struct layer_map>("Found invalid/unset IDs in mapping"); + } + } + + auto msi = j.find("main_surface"); + if (msi != j.end()) { + stl.main_surface_name = msi->value("surface_role", ""); + stl.main_surface = -1; + } + + return Ok(stl); + } catch (std::exception &e) { + return Err<struct layer_map>(e.what()); + } +} + +optional<int> layer_map::get_layer_id(int surface_id) { + auto i = this->surfaces.find(surface_id); + if (i != this->surfaces.end()) { + return optional<int>(i->second); + } + return nullopt; +} + +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; +} + +json layer::to_json() const { + auto is_full = this->rect == genivi::full_rect; + + json r{}; + if (is_full) { + r = {{"type", "full"}}; + } else { + r = {{"type", "rect"}, + {"rect", + {{"x", this->rect.x}, + {"y", this->rect.y}, + {"width", this->rect.w}, + {"height", this->rect.h}}}}; + } + + return { + {"name", this->name}, {"role", this->role}, + {"layer_id", this->layer_id}, {"area", r}, + }; +} + +json layer_map::to_json() const { + json j{}; + for (auto const &i : this->mapping) { + j.push_back(i.second.to_json()); + } + return j; +} + +} // namespace wm diff --git a/src/layers.hpp b/src/layers.hpp new file mode 100644 index 0000000..0603d24 --- /dev/null +++ b/src/layers.hpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_LAYERS_H +#define TMCAGLWM_LAYERS_H + +#include <json.hpp> + +#include <regex> +#include <set> +#include <string> + +#include "layout.hpp" +#include "result.hpp" +#include "wayland.hpp" + +namespace wm { + +struct split_layout { + std::string name; + std::string main_match; + std::string sub_match; +}; + +struct layer { + using json = nlohmann::json; + + // A more or less descriptive name? + std::string name = ""; + // The actual layer ID + int layer_id = -1; + // The rectangular region surfaces are allowed to draw on + // this layer, note however, width and hieght of the rect + // can be negative, in which case they specify that + // the actual value is computed using MAX + 1 - w + // 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? + std::vector<struct split_layout> layouts; + // XXX need to change the way we store these things... + mutable struct LayoutState state; + + explicit layer(nlohmann::json const &j); + + json to_json() const; +}; + +struct layer_map { + using json = nlohmann::json; + + using storage_type = std::map<int, struct layer>; + using layers_type = std::vector<uint32_t>; + using role_to_layer_map = std::vector<std::pair<std::string, int>>; + using addsurf_layer_map = std::map<int, int>; + + // 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 LayoutState*> get_layout_state(int surface_id) { + int layer_id = *this->get_layer_id(surface_id); + auto i = this->mapping.find(layer_id); + return i == this->mapping.end() + ? nullopt + : optional<struct LayoutState *>(&i->second.state); + } + optional<struct layer> get_layer(int layer_id) { + auto i = this->mapping.find(layer_id); + return i == this->mapping.end() ? nullopt + : optional<struct layer>(i->second); + } + + layers_type::size_type get_layers_count() const { + return this->layers.size(); + } + + void add_surface(int surface_id, int layer_id) { + this->surfaces[surface_id] = layer_id; + } + + void remove_surface(int surface_id) { + this->surfaces.erase(surface_id); + } + + json to_json() const; +}; + +struct result<struct layer_map> to_layer_map(nlohmann::json const &j); + +} // namespace wm + +#endif // TMCAGLWM_LAYERS_H diff --git a/src/layout.cpp b/src/layout.cpp new file mode 100644 index 0000000..1589ee9 --- /dev/null +++ b/src/layout.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "layout.hpp" diff --git a/src/layout.hpp b/src/layout.hpp new file mode 100644 index 0000000..b7a3c28 --- /dev/null +++ b/src/layout.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_LAYOUT_HPP +#define TMCAGLWM_LAYOUT_HPP + +#include <cstdint> +#include <string> + +#include "result.hpp" +#include "wayland.hpp" + +namespace wm { + +struct LayoutState { + int main{-1}; + int sub{-1}; + + bool operator==(const LayoutState &b) const { + return main == b.main && sub == b.sub; + } + + bool operator!=(const LayoutState &b) const { + return !(*this == b); + } +}; + +} // namespace wm + +#endif // TMCAGLWM_LAYOUT_HPP diff --git a/src/main.cpp b/src/main.cpp new file mode 100755 index 0000000..a1e3db8 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <unistd.h> +#include "app.hpp" +#include "json_helper.hpp" +#include "util.hpp" +#include "wayland.hpp" + +#include <algorithm> +#include <mutex> + +#include <json.h> + +extern "C" { +#include <afb/afb-binding.h> +#include <systemd/sd-event.h> +} + +namespace { + +struct afb_instance { + std::unique_ptr<wl::display> display; + wm::App app; + + afb_instance() : display{new wl::display}, app{this->display.get()} {} + + int init(); +}; + +struct afb_instance *g_afb_instance; + +int afb_instance::init() { + return this->app.init(); +} + +int display_event_callback(sd_event_source *evs, int /*fd*/, uint32_t events, + void * /*data*/) { + ST(); + + if ((events & EPOLLHUP) != 0) { + logerror("The compositor hung up, dying now."); + delete g_afb_instance; + g_afb_instance = nullptr; + goto error; + } + + if ((events & EPOLLIN) != 0u) { + { + STN(display_read_events); + g_afb_instance->app.display->read_events(); + g_afb_instance->app.set_pending_events(); + } + { + // We want do dispatch pending wayland events from within + // the API context + STN(winman_ping_api_call); + afb_service_call("windowmanager", "ping", json_object_new_object(), + [](void *c, int st, json_object *j) { + STN(winman_ping_api_call_return); + }, + nullptr); + } + } + + return 0; + +error: + sd_event_source_unref(evs); + if (getenv("WINMAN_EXIT_ON_HANGUP") != nullptr) { + exit(1); +} + return -1; +} + +// _ _ _ _ _ _ _ ____ +// | |__ (_)_ __ __| (_)_ __ __ _ (_)_ __ (_) |_ / /\ \ +// | '_ \| | '_ \ / _` | | '_ \ / _` | | | '_ \| | __| | | | +// | |_) | | | | | (_| | | | | | (_| | | | | | | | |_| | | | +// |_.__/|_|_| |_|\__,_|_|_| |_|\__, |___|_|_| |_|_|\__| | | | +// |___/_____| \_\/_/ +int binding_init_() { + lognotice("WinMan ver. %s", WINMAN_VERSION_STRING); + + if (g_afb_instance != nullptr) { + logerror("Wayland context already initialized?"); + return 0; + } + + if (getenv("XDG_RUNTIME_DIR") == nullptr) { + logerror("Environment variable XDG_RUNTIME_DIR not set"); + goto error; + } + + { + // wait until wayland compositor starts up. + int cnt = 0; + g_afb_instance = new afb_instance; + while (!g_afb_instance->display->ok()) { + cnt++; + if (20 <= cnt) { + logerror("Could not connect to compositor"); + goto error; + } + logerror("Wait to start weston ..."); + sleep(1); + delete g_afb_instance; + g_afb_instance = new afb_instance; + } + } + + if (g_afb_instance->init() == -1) { + logerror("Could not connect to compositor"); + goto error; + } + + { + int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr, + g_afb_instance->display->get_fd(), EPOLLIN, + display_event_callback, g_afb_instance); + if (ret < 0) { + logerror("Could not initialize afb_instance event handler: %d", -ret); + goto error; + } + } + + atexit([] { delete g_afb_instance; }); + + return 0; + +error: + delete g_afb_instance; + g_afb_instance = nullptr; + return -1; +} + +int binding_init() noexcept { + try { + return binding_init_(); + } catch (std::exception &e) { + logerror("Uncaught exception in binding_init(): %s", e.what()); + } + return -1; +} + +} // namespace + +#include "afb_binding_glue.inl" + +// XXX implement send_event right here... +namespace wm { +void binding_api::send_event(char const *evname, char const *label) { + logdebug("%s: %s(%s)", __func__, evname, label); + int ret = afb_daemon_broadcast_event(evname, json_object_new_string(label)); + if (ret != 0) { + logdebug("afb_event_broadcast failed: %m"); + } +} +} // namespace wm + +extern "C" const struct afb_binding_v2 afbBindingV2 = { + "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0}; diff --git a/src/policy.hpp b/src/policy.hpp new file mode 100644 index 0000000..ed5d6ba --- /dev/null +++ b/src/policy.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_POLICY_HPP +#define TMCAGLWM_POLICY_HPP + +#include "layout.hpp" + +namespace wm { + +class Policy { +public: + bool layout_is_valid(LayoutState const & /* layout */) { + // We do not check for policy currently + logdebug("Policy check returns positive"); + return true; + } +}; + +} // namespace wm + +#endif //TMCAGLWM_POLICY_HPP diff --git a/src/redraw_fixer.cpp b/src/redraw_fixer.cpp new file mode 100644 index 0000000..94964af --- /dev/null +++ b/src/redraw_fixer.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wayland.hpp" + +#include <algorithm> +#include <chrono> +#include <thread> + +using namespace std::chrono_literals; + +// pretend we are a WM ... +namespace wm { + +struct App { + controller_hooks chooks; + std::unique_ptr<wl::display> display; + std::unique_ptr<genivi::controller> controller; + std::vector<std::unique_ptr<wl::output>> outputs; + + App(); + void commit(); + void surface_visibility(uint32_t surface_id, uint32_t v); + void surface_destination_rectangle(uint32_t surface_id, uint32_t x, + uint32_t y, uint32_t w, uint32_t h); + void try_fix(uint32_t surface_id); +}; + +void controller_hooks::surface_created(uint32_t /*surface_id*/) {} + +void controller_hooks::surface_removed(uint32_t /*surface_id*/) {} + +void controller_hooks::surface_visibility(uint32_t surface_id, uint32_t v) { + this->app->surface_visibility(surface_id, v); +} + +void controller_hooks::surface_destination_rectangle(uint32_t surface_id, + uint32_t x, uint32_t y, + uint32_t w, uint32_t h) { + this->app->surface_destination_rectangle(surface_id, x, y, w, h); +} + +App::App() : chooks{this}, display{new wl::display}, controller{}, outputs{} { + // The same init, the WM does, at least we can reuse the wayland stuff + if (!this->display->ok()) { + return; + } + + this->display->add_global_handler( + "wl_output", [this](wl_registry *r, uint32_t name, uint32_t v) { + this->outputs.emplace_back(std::make_unique<wl::output>(r, name, v)); + }); + + 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); + + // Init controller hooks + this->controller->chooks = &this->chooks; + + this->controller->add_proxy_to_id_mapping( + this->outputs.back()->proxy.get(), + wl_proxy_get_id(reinterpret_cast<struct wl_proxy *>( + this->outputs.back()->proxy.get()))); + }); + + for (int i : {1, 2, 3}) { + this->display->roundtrip(); +} +} + +void App::commit() { + this->controller->commit_changes(); + this->display->roundtrip(); // read: flush()++ +} + +void App::surface_visibility(uint32_t surface_id, uint32_t v) { + fprintf(stderr, "surface %u visibility %u\n", surface_id, v); + + if (v == 1) { + this->try_fix(surface_id); + } +} + +void App::surface_destination_rectangle(uint32_t surface_id, uint32_t x, + uint32_t y, uint32_t w, uint32_t h) { + fprintf(stderr, "surface %u dst %u %u %u %u\n", surface_id, x, y, w, h); + + if (w != 1 && h != 1 && this->controller->sprops[surface_id].visibility != 0) { + this->try_fix(surface_id); + } +} + +void App::try_fix(uint32_t surface_id) { + this->controller->surfaces[surface_id]->set_opacity(255); + this->commit(); + std::this_thread::sleep_for(200ms); + this->controller->surfaces[surface_id]->set_opacity(256); + this->commit(); +} + +} // namespace wm + +int main(int /*argc*/, char ** /*argv*/) { + wm::App app; + if (!app.display->ok()) { + fputs("Could not init wayland display\n", stderr); + return 1; + } + while (app.display->dispatch() != -1) { + ; + } + return 0; +} diff --git a/src/result.hpp b/src/result.hpp new file mode 100644 index 0000000..e14f92b --- /dev/null +++ b/src/result.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TMCAGLWM_RESULT_HPP +#define TMCAGLWM_RESULT_HPP + +#include <experimental/optional> +#include <functional> + +namespace wm { + +using std::experimental::optional; +using std::experimental::nullopt; + +// We only ever return a string as an error - so just parametrize +// this over result type T +template <typename T> +struct result { + char const *e; + optional<T> t; + + bool is_ok() const { return this->t != nullopt; } + bool is_err() const { return this->e != nullptr; } + + T unwrap() { + if (this->e != nullptr) { + throw std::logic_error(this->e); + } + return this->t.value(); + } + + operator T() { return this->unwrap(); } + + char const *unwrap_err() { return this->e; } + + optional<T> const &ok() const { return this->t; } + optional<char const *> err() const { + return this->e ? optional<char const *>(this->e) : nullopt; + } + + result<T> map_err(std::function<char const *(char const *)> f); +}; + +template <typename T> +struct result<T> Err(char const *e) { + return result<T>{e, nullopt}; +} + +template <typename T> +struct result<T> Ok(T t) { + return result<T>{nullptr, t}; +} + +template <typename T> +result<T> result<T>::map_err(std::function<char const *(char const *)> f) { + if (this->is_err()) { + return Err<T>(f(this->e)); + } + return *this; +} + +} // namespace wm + +#endif // TMCAGLWM_RESULT_HPP diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..c178d90 --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "util.hpp" + +#include <cerrno> +#include <cstdarg> +#include <cstdio> +#include <cstdlib> +#include <ctime> + +#include <unistd.h> + +#ifdef SCOPE_TRACING +thread_local int ScopeTrace::indent = 0; +ScopeTrace::ScopeTrace(char const *func) : f(func) { + fprintf(stderr, "%lu %*s%s -->\n", pthread_self(), 2 * indent++, "", this->f); +} +ScopeTrace::~ScopeTrace() { fprintf(stderr, "%lu %*s%s <--\n", pthread_self(), 2 * --indent, "", this->f); } +#endif + +unique_fd::~unique_fd() { + if (this->fd != -1) { + close(this->fd); + } +} diff --git a/src/util.hpp b/src/util.hpp new file mode 100644 index 0000000..b3f43de --- /dev/null +++ b/src/util.hpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WM_UTIL_HPP +#define WM_UTIL_HPP + +#include <functional> +#include <thread> +#include <vector> + +#include <sys/poll.h> + +#ifndef DO_NOT_USE_AFB +extern "C" { +#include <afb/afb-binding.h> +}; +#endif + +#define CONCAT_(X, Y) X##Y +#define CONCAT(X, Y) CONCAT_(X, Y) + +#ifdef __GNUC__ +#define ATTR_FORMAT(stringindex, firsttocheck) \ + __attribute__((format(printf, stringindex, firsttocheck))) +#define ATTR_NORETURN __attribute__((noreturn)) +#else +#define ATTR_FORMAT(stringindex, firsttocheck) +#define ATTR_NORETURN +#endif + +#ifdef AFB_BINDING_VERSION +#define lognotice(...) AFB_NOTICE(__VA_ARGS__) +#define logerror(...) AFB_ERROR(__VA_ARGS__) +#define fatal(...) \ + do { \ + AFB_ERROR(__VA_ARGS__); \ + abort(); \ + } while (0) +#else +#define lognotice(...) +#define logerror(...) +#define fatal(...) \ + do { \ + abort(); \ + } while (0) +#endif + +#ifdef DEBUG_OUTPUT +#ifdef AFB_BINDING_VERSION +#define logdebug(...) AFB_DEBUG(__VA_ARGS__) +#else +#define logdebug(...) +#endif +#else +#define logdebug(...) +#endif + +#ifndef SCOPE_TRACING +#define ST() +#define STN(N) +#else +#define ST() \ + ScopeTrace __attribute__((unused)) CONCAT(trace_scope_, __LINE__)(__func__) +#define STN(N) \ + ScopeTrace __attribute__((unused)) CONCAT(named_trace_scope_, __LINE__)(#N) + +struct ScopeTrace { + thread_local static int indent; + char const *f{}; + explicit ScopeTrace(char const *func); + ~ScopeTrace(); +}; +#endif + +// _ _ _ __ _ +// ___| |_ _ __ _ _ ___| |_ _ _ _ __ (_) __ _ _ _ ___ / _| __| | +// / __| __| '__| | | |/ __| __| | | | | '_ \| |/ _` | | | |/ _ \ | |_ / _` | +// \__ \ |_| | | |_| | (__| |_ | |_| | | | | | (_| | |_| | __/ | _| (_| | +// |___/\__|_| \__,_|\___|\__| \__,_|_| |_|_|\__, |\__,_|\___|___|_| \__,_| +// |_| |_____| +struct unique_fd { + int fd{-1}; + unique_fd() = default; + explicit unique_fd(int f) : fd{f} {} + operator int() const { return fd; } + ~unique_fd(); + unique_fd(unique_fd const &) = delete; + unique_fd &operator=(unique_fd const &) = delete; + unique_fd(unique_fd &&o) : fd(o.fd) { o.fd = -1; } + unique_fd &operator=(unique_fd &&o) { + std::swap(this->fd, o.fd); + return *this; + } +}; + +#endif // !WM_UTIL_HPP diff --git a/src/wayland.cpp b/src/wayland.cpp new file mode 100644 index 0000000..05e155f --- /dev/null +++ b/src/wayland.cpp @@ -0,0 +1,765 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utility> + +#include "util.hpp" +#include "wayland.hpp" + +// _ +// _ __ __ _ _ __ ___ ___ ___ _ __ __ _ ___ ___ __ _| | +// | '_ \ / _` | '_ ` _ \ / _ \/ __| '_ \ / _` |/ __/ _ \ \ \ /\ / / | +// | | | | (_| | | | | | | __/\__ \ |_) | (_| | (_| __/ \ V V /| | +// |_| |_|\__,_|_| |_| |_|\___||___/ .__/ \__,_|\___\___| \_/\_/ |_| +// |_| +namespace wl { + +// _ _ _ +// __| (_)___ _ __ | | __ _ _ _ +// / _` | / __| '_ \| |/ _` | | | | +// | (_| | \__ \ |_) | | (_| | |_| | +// \__,_|_|___/ .__/|_|\__,_|\__, | +// |_| |___/ +display::display() + : d(std::unique_ptr<struct wl_display, void (*)(struct wl_display *)>( + wl_display_connect(nullptr), &wl_display_disconnect)), + r(d.get()) {} + +bool display::ok() const { return d && wl_display_get_error(d.get()) == 0; } + +void display::roundtrip() { wl_display_roundtrip(this->d.get()); } + +int display::dispatch() { return wl_display_dispatch(this->d.get()); } + +int display::dispatch_pending() { return wl_display_dispatch_pending(this->d.get()); } + +int display::read_events() { + ST(); + // XXX: uhm, how?! + while (wl_display_prepare_read(this->d.get()) == -1) { + STN(pending_events_dispatch); + if (wl_display_dispatch_pending(this->d.get()) == -1) { + return -1; + } + } + + if (wl_display_flush(this->d.get()) == -1) { + return -1; + } + + if (wl_display_read_events(this->d.get()) == -1) { + wl_display_cancel_read(this->d.get()); + } + + return 0; +} + +void display::flush() { wl_display_flush(this->d.get()); } + +int display::get_fd() const { return wl_display_get_fd(this->d.get()); } + +int display::get_error() { return wl_display_get_error(this->d.get()); } + +// _ _ +// _ __ ___ __ _(_)___| |_ _ __ _ _ +// | '__/ _ \/ _` | / __| __| '__| | | | +// | | | __/ (_| | \__ \ |_| | | |_| | +// |_| \___|\__, |_|___/\__|_| \__, | +// |___/ |___/ +namespace { +void registry_global(void *data, struct wl_registry * /*r*/, uint32_t name, + char const *iface, uint32_t v) { + static_cast<struct registry *>(data)->global(name, iface, v); +} + +void registry_global_remove(void *data, struct wl_registry * /*r*/, + uint32_t name) { + static_cast<struct registry *>(data)->global_remove(name); +} + +constexpr struct wl_registry_listener registry_listener = { + registry_global, registry_global_remove}; +} // namespace + +registry::registry(struct wl_display *d) + : wayland_proxy(d == nullptr ? nullptr : wl_display_get_registry(d)) { + if (this->proxy != nullptr) { + wl_registry_add_listener(this->proxy.get(), ®istry_listener, this); + } +} + +void registry::add_global_handler(char const *iface, binder bind) { + this->bindings[iface] = std::move(bind); +} + +void registry::global(uint32_t name, char const *iface, uint32_t v) { + auto b = this->bindings.find(iface); + if (b != this->bindings.end()) { + b->second(this->proxy.get(), name, v); + } + logdebug("wl::registry @ %p global n %u i %s v %u", this->proxy.get(), name, + iface, v); +} + +void registry::global_remove(uint32_t /*name*/) {} + +// _ _ +// ___ _ _| |_ _ __ _ _| |_ +// / _ \| | | | __| '_ \| | | | __| +// | (_) | |_| | |_| |_) | |_| | |_ +// \___/ \__,_|\__| .__/ \__,_|\__| +// |_| +namespace { +void output_geometry(void *data, struct wl_output * /*wl_output*/, int32_t x, + int32_t y, int32_t physical_width, int32_t physical_height, + int32_t subpixel, const char *make, const char *model, + int32_t transform) { + static_cast<struct output *>(data)->geometry( + x, y, physical_width, physical_height, subpixel, make, model, transform); +} + +void output_mode(void *data, struct wl_output * /*wl_output*/, uint32_t flags, + int32_t width, int32_t height, int32_t refresh) { + static_cast<struct output *>(data)->mode(flags, width, height, refresh); +} + +void output_done(void *data, struct wl_output * /*wl_output*/) { + static_cast<struct output *>(data)->done(); +} + +void output_scale(void *data, struct wl_output * /*wl_output*/, + int32_t factor) { + static_cast<struct output *>(data)->scale(factor); +} + +constexpr struct wl_output_listener output_listener = { + output_geometry, output_mode, output_done, output_scale}; +} // namespace + +output::output(struct wl_registry *r, uint32_t name, uint32_t v) + : wayland_proxy(wl_registry_bind(r, name, &wl_output_interface, v)) { + wl_output_add_listener(this->proxy.get(), &output_listener, this); +} + +void output::geometry(int32_t x, int32_t y, int32_t pw, int32_t ph, + int32_t subpel, char const *make, char const *model, + int32_t tx) { + logdebug( + "wl::output %s @ %p x %i y %i w %i h %i spel %x make %s model %s tx %i", + __func__, this->proxy.get(), x, y, pw, ph, subpel, make, model, tx); + this->transform = tx; +} + +void output::mode(uint32_t flags, int32_t w, int32_t h, int32_t r) { + logdebug("wl::output %s @ %p f %x w %i h %i r %i", __func__, + this->proxy.get(), flags, w, h, r); + if ((flags & WL_OUTPUT_MODE_CURRENT) != 0u) { + this->width = w; + this->height = h; + this->refresh = r; + } +} + +void output::done() { + logdebug("wl::output %s @ %p done", __func__, this->proxy.get()); + // Let's just disregard the flipped ones... + if (this->transform == WL_OUTPUT_TRANSFORM_90 || + this->transform == WL_OUTPUT_TRANSFORM_270) { + std::swap(this->width, this->height); + } +} + +void output::scale(int32_t factor) { + logdebug("wl::output %s @ %p f %i", __func__, this->proxy.get(), factor); +} +} // namespace wl + +// _ __ __ _ _ __ ___ ___ ___ _ __ __ _ ___ ___ +// | '_ \ / _` | '_ ` _ \ / _ \/ __| '_ \ / _` |/ __/ _ \ +// | | | | (_| | | | | | | __/\__ \ |_) | (_| | (_| __/ +// |_| |_|\__,_|_| |_| |_|\___||___/ .__/ \__,_|\___\___| +// |_| +// _ _ +// __ _ ___ _ __ (_)_ _(_) +// / _` |/ _ \ '_ \| \ \ / / | +// | (_| | __/ | | | |\ V /| | +// \__, |\___|_| |_|_| \_/ |_| +// |___/ +namespace genivi { + +// _ _ _ +// ___ ___ _ __ | |_ _ __ ___ | | | ___ _ __ +// / __/ _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| +// | (_| (_) | | | | |_| | | (_) | | | __/ | +// \___\___/|_| |_|\__|_| \___/|_|_|\___|_| +// +namespace { +void controller_screen(void *data, struct ivi_controller * /*ivi_controller*/, + uint32_t id_screen, + struct ivi_controller_screen *screen) { + static_cast<struct controller *>(data)->controller_screen(id_screen, screen); +} + +void controller_layer(void *data, struct ivi_controller * /*ivi_controller*/, + uint32_t id_layer) { + static_cast<struct controller *>(data)->controller_layer(id_layer); +} + +void controller_surface(void *data, struct ivi_controller * /*ivi_controller*/, + uint32_t id_surface) { + static_cast<struct controller *>(data)->controller_surface(id_surface); +} + +void controller_error(void *data, struct ivi_controller * /*ivi_controller*/, + int32_t object_id, int32_t object_type, + int32_t error_code, const char *error_text) { + static_cast<struct controller *>(data)->controller_error( + object_id, object_type, error_code, error_text); +} + +constexpr struct ivi_controller_listener listener = { + controller_screen, controller_layer, controller_surface, controller_error}; +} // namespace + +controller::controller(struct wl_registry *r, uint32_t name, uint32_t version) + : wayland_proxy( + wl_registry_bind(r, name, &ivi_controller_interface, version)), + output_size{} { + ivi_controller_add_listener(this->proxy.get(), &listener, this); +} + +void controller::layer_create(uint32_t id, int32_t w, int32_t h) { + this->layers[id] = std::make_unique<struct layer>(id, w, h, this); +} + +void controller::surface_create(uint32_t id) { + this->surfaces[id] = std::make_unique<struct surface>(id, this); +} + +void controller::controller_screen(uint32_t id, + struct ivi_controller_screen *screen) { + logdebug("genivi::controller @ %p screen %u (%x) @ %p", this->proxy.get(), + id, id, screen); + this->screens[id] = std::make_unique<struct screen>(id, this, screen); +} + +void controller::controller_layer(uint32_t id) { + logdebug("genivi::controller @ %p layer %u (%x)", this->proxy.get(), id, id); + if (this->layers.find(id) != this->layers.end()) { + logerror("Someone created a layer without asking US! (%d)", id); + } else { + auto &l = this->layers[id] = std::make_unique<struct layer>(id, this); + l->clear_surfaces(); + } +} + +void controller::controller_surface(uint32_t id) { + logdebug("genivi::controller @ %p surface %u (%x)", this->proxy.get(), id, + id); + if (this->surfaces.find(id) == this->surfaces.end()) { + this->surfaces[id] = std::make_unique<struct surface>(id, this); + this->chooks->surface_created(id); + } +} + +void controller::controller_error(int32_t object_id, int32_t object_type, + int32_t error_code, const char *error_text) { + logdebug("genivi::controller @ %p error o %i t %i c %i text %s", + this->proxy.get(), object_id, object_type, error_code, error_text); +} + +// _ +// | | __ _ _ _ ___ _ __ +// | |/ _` | | | |/ _ \ '__| +// | | (_| | |_| | __/ | +// |_|\__,_|\__, |\___|_| +// |___/ +namespace { +void layer_visibility(void *data, + struct ivi_controller_layer * /*ivi_controller_layer*/, + int32_t visibility) { + auto l = static_cast<struct layer *>(data); + l->parent->layer_visibility(l, visibility); +} + +void layer_opacity(void *data, + struct ivi_controller_layer * /*ivi_controller_layer*/, + wl_fixed_t opacity) { + auto l = static_cast<struct layer *>(data); + l->parent->layer_opacity(l, float(wl_fixed_to_double(opacity))); +} + +void layer_source_rectangle( + void *data, struct ivi_controller_layer * /*ivi_controller_layer*/, + int32_t x, int32_t y, int32_t width, int32_t height) { + auto l = static_cast<struct layer *>(data); + l->parent->layer_source_rectangle(l, x, y, width, height); +} + +void layer_destination_rectangle( + void *data, struct ivi_controller_layer * /*ivi_controller_layer*/, + int32_t x, int32_t y, int32_t width, int32_t height) { + auto l = static_cast<struct layer *>(data); + l->parent->layer_destination_rectangle(l, x, y, width, height); +} + +void layer_configuration(void *data, + struct ivi_controller_layer * /*ivi_controller_layer*/, + int32_t width, int32_t height) { + auto l = static_cast<struct layer *>(data); + l->parent->layer_configuration(l, width, height); +} + +void layer_orientation(void *data, + struct ivi_controller_layer * /*ivi_controller_layer*/, + int32_t orientation) { + auto l = static_cast<struct layer *>(data); + l->parent->layer_orientation(l, orientation); +} + +void layer_screen(void *data, + struct ivi_controller_layer * /*ivi_controller_layer*/, + struct wl_output *screen) { + auto l = static_cast<struct layer *>(data); + l->parent->layer_screen(l, screen); +} + +void layer_destroyed(void *data, + struct ivi_controller_layer * /*ivi_controller_layer*/) { + auto l = static_cast<struct layer *>(data); + l->parent->layer_destroyed(l); +} + +constexpr struct ivi_controller_layer_listener layer_listener = { + layer_visibility, layer_opacity, + layer_source_rectangle, layer_destination_rectangle, + layer_configuration, layer_orientation, + layer_screen, layer_destroyed, +}; +} // namespace + +layer::layer(uint32_t i, struct controller *c) : layer(i, 0, 0, c) {} + +layer::layer(uint32_t i, int32_t w, int32_t h, struct controller *c) + : wayland_proxy(ivi_controller_layer_create(c->proxy.get(), i, w, h), + [c, i](ivi_controller_layer *l) { + logdebug("~layer layer %i @ %p", i, l); + c->remove_proxy_to_id_mapping(l); + ivi_controller_layer_destroy(l, 1); + }), + controller_child(c, i) { + this->parent->add_proxy_to_id_mapping(this->proxy.get(), i); + ivi_controller_layer_add_listener(this->proxy.get(), &layer_listener, this); +} + +void layer::set_visibility(uint32_t visibility) { + ivi_controller_layer_set_visibility(this->proxy.get(), visibility); +} + +void layer::set_opacity(wl_fixed_t opacity) { + ivi_controller_layer_set_opacity(this->proxy.get(), opacity); +} + +void layer::set_source_rectangle(int32_t x, int32_t y, int32_t width, + int32_t height) { + ivi_controller_layer_set_source_rectangle(this->proxy.get(), x, y, width, + height); +} + +void layer::set_destination_rectangle(int32_t x, int32_t y, int32_t width, + int32_t height) { + ivi_controller_layer_set_destination_rectangle(this->proxy.get(), x, y, + width, height); +} + +void layer::set_configuration(int32_t width, int32_t height) { + ivi_controller_layer_set_configuration(this->proxy.get(), width, height); +} + +void layer::set_orientation(int32_t orientation) { + ivi_controller_layer_set_orientation(this->proxy.get(), orientation); +} + +void layer::screenshot(const char *filename) { + ivi_controller_layer_screenshot(this->proxy.get(), filename); +} + +void layer::clear_surfaces() { + ivi_controller_layer_clear_surfaces(this->proxy.get()); +} + +void layer::add_surface(struct surface *surface) { + ivi_controller_layer_add_surface(this->proxy.get(), surface->proxy.get()); +} + +void layer::remove_surface(struct surface *surface) { + ivi_controller_layer_remove_surface(this->proxy.get(), surface->proxy.get()); +} + +void layer::set_render_order(std::vector<uint32_t> const &ro) { + struct wl_array wlro { + .size = ro.size() * sizeof(ro[0]), .alloc = ro.capacity() * sizeof(ro[0]), + .data = const_cast<void *>(static_cast<void const *>(ro.data())) + }; + ivi_controller_layer_set_render_order(this->proxy.get(), &wlro); +} + +void controller::layer_visibility(struct layer *l, int32_t visibility) { + logdebug("genivi::layer %s @ %d v %i", __func__, l->id, visibility); + this->lprops[l->id].visibility = visibility; +} + +void controller::layer_opacity(struct layer *l, float opacity) { + logdebug("genivi::layer %s @ %d o %f", __func__, l->id, opacity); + this->lprops[l->id].opacity = opacity; +} + +void controller::layer_source_rectangle(struct layer *l, int32_t x, int32_t y, + int32_t width, int32_t height) { + logdebug("genivi::layer %s @ %d x %i y %i w %i h %i", __func__, + l->id, x, y, width, height); + this->lprops[l->id].src_rect = rect{width, height, x, y}; +} + +void controller::layer_destination_rectangle(struct layer *l, int32_t x, + int32_t y, int32_t width, + int32_t height) { + logdebug("genivi::layer %s @ %d x %i y %i w %i h %i", __func__, + l->id, x, y, width, height); + this->lprops[l->id].dst_rect = rect{width, height, x, y}; +} + +void controller::layer_configuration(struct layer *l, int32_t width, + int32_t height) { + logdebug("genivi::layer %s @ %d w %i h %i", __func__, l->id, + width, height); + this->lprops[l->id].size = size{uint32_t(width), uint32_t(height)}; +} + +void controller::layer_orientation(struct layer *l, int32_t orientation) { + logdebug("genivi::layer %s @ %d o %i", __func__, l->id, + orientation); + this->lprops[l->id].orientation = orientation; +} + +void controller::layer_screen(struct layer *l, struct wl_output *screen) { + logdebug("genivi::layer %s @ %d s %p", __func__, l->id, screen); +} + +void controller::layer_destroyed(struct layer *l) { + logdebug("genivi::layer %s @ %d", __func__, l->id); + this->lprops.erase(l->id); + this->layers.erase(l->id); +} + +// __ +// ___ _ _ _ __ / _| __ _ ___ ___ +// / __| | | | '__| |_ / _` |/ __/ _ \ +// \__ \ |_| | | | _| (_| | (_| __/ +// |___/\__,_|_| |_| \__,_|\___\___| +// +namespace { + +void surface_visibility( + void *data, struct ivi_controller_surface * /*ivi_controller_surface*/, + int32_t visibility) { + auto s = static_cast<struct surface *>(data); + s->parent->surface_visibility(s, visibility); +} + +void surface_opacity(void *data, + struct ivi_controller_surface * /*ivi_controller_surface*/, + wl_fixed_t opacity) { + auto s = static_cast<struct surface *>(data); + s->parent->surface_opacity(s, float(wl_fixed_to_double(opacity))); +} + +void surface_source_rectangle( + void *data, struct ivi_controller_surface * /*ivi_controller_surface*/, + int32_t x, int32_t y, int32_t width, int32_t height) { + auto s = static_cast<struct surface *>(data); + s->parent->surface_source_rectangle(s, x, y, width, height); +} + +void surface_destination_rectangle( + void *data, struct ivi_controller_surface * /*ivi_controller_surface*/, + int32_t x, int32_t y, int32_t width, int32_t height) { + auto s = static_cast<struct surface *>(data); + s->parent->surface_destination_rectangle(s, x, y, width, height); +} + +void surface_configuration( + void *data, struct ivi_controller_surface * /*ivi_controller_surface*/, + int32_t width, int32_t height) { + auto s = static_cast<struct surface *>(data); + s->parent->surface_configuration(s, width, height); +} + +void surface_orientation( + void *data, struct ivi_controller_surface * /*ivi_controller_surface*/, + int32_t orientation) { + auto s = static_cast<struct surface *>(data); + s->parent->surface_orientation(s, orientation); +} + +void surface_pixelformat( + void *data, struct ivi_controller_surface * /*ivi_controller_surface*/, + int32_t pixelformat) { + auto s = static_cast<struct surface *>(data); + s->parent->surface_pixelformat(s, pixelformat); +} + +void surface_layer(void *data, + struct ivi_controller_surface * /*ivi_controller_surface*/, + struct ivi_controller_layer *layer) { + auto s = static_cast<struct surface *>(data); + s->parent->surface_layer(s, layer); +} + +void surface_stats(void *data, + struct ivi_controller_surface * /*ivi_controller_surface*/, + uint32_t redraw_count, uint32_t frame_count, + uint32_t update_count, uint32_t pid, + const char *process_name) { + auto s = static_cast<struct surface *>(data); + s->parent->surface_stats(s, redraw_count, frame_count, update_count, pid, + process_name); +} + +void surface_destroyed( + void *data, struct ivi_controller_surface * /*ivi_controller_surface*/) { + auto s = static_cast<struct surface *>(data); + s->parent->surface_destroyed(s); +} + +void surface_content(void *data, + struct ivi_controller_surface * /*ivi_controller_surface*/, + int32_t content_state) { + auto s = static_cast<struct surface *>(data); + s->parent->surface_content(s, content_state); +} + +constexpr struct ivi_controller_surface_listener surface_listener = { + surface_visibility, + surface_opacity, + surface_source_rectangle, + surface_destination_rectangle, + surface_configuration, + surface_orientation, + surface_pixelformat, + surface_layer, + surface_stats, + surface_destroyed, + surface_content, +}; +} // namespace + +surface::surface(uint32_t i, struct controller *c) + : wayland_proxy(ivi_controller_surface_create(c->proxy.get(), i), + [c, i](ivi_controller_surface *s) { + logdebug("~surface surface %i @ %p", i, s); + c->remove_proxy_to_id_mapping(s); + ivi_controller_surface_destroy(s, 1); + }), + controller_child(c, i) { + this->parent->add_proxy_to_id_mapping(this->proxy.get(), i); + ivi_controller_surface_add_listener(this->proxy.get(), &surface_listener, + this); +} + +void surface::set_visibility(uint32_t visibility) { + ivi_controller_surface_set_visibility(this->proxy.get(), visibility); +} + +void surface::set_opacity(wl_fixed_t opacity) { + ivi_controller_surface_set_opacity(this->proxy.get(), opacity); +} + +void surface::set_source_rectangle(int32_t x, int32_t y, int32_t width, + int32_t height) { + ivi_controller_surface_set_source_rectangle(this->proxy.get(), x, y, width, + height); +} + +void surface::set_destination_rectangle(int32_t x, int32_t y, int32_t width, + int32_t height) { + ivi_controller_surface_set_destination_rectangle(this->proxy.get(), x, y, + width, height); +} + +void surface::set_configuration(int32_t width, int32_t height) { + ivi_controller_surface_set_configuration(this->proxy.get(), width, height); +} + +void surface::set_orientation(int32_t orientation) { + ivi_controller_surface_set_orientation(this->proxy.get(), orientation); +} + +void surface::screenshot(const char *filename) { + ivi_controller_surface_screenshot(this->proxy.get(), filename); +} + +void surface::send_stats() { + ivi_controller_surface_send_stats(this->proxy.get()); +} + +void surface::destroy(int32_t destroy_scene_object) { + ivi_controller_surface_destroy(this->proxy.get(), destroy_scene_object); +} + +void controller::surface_visibility(struct surface *s, int32_t visibility) { + logdebug("genivi::surface %s @ %d v %i", __func__, s->id, + visibility); + this->sprops[s->id].visibility = visibility; + this->chooks->surface_visibility(s->id, visibility); +} + +void controller::surface_opacity(struct surface *s, float opacity) { + logdebug("genivi::surface %s @ %d o %f", __func__, s->id, + opacity); + this->sprops[s->id].opacity = opacity; +} + +void controller::surface_source_rectangle(struct surface *s, int32_t x, + int32_t y, int32_t width, + int32_t height) { + logdebug("genivi::surface %s @ %d x %i y %i w %i h %i", __func__, + s->id, x, y, width, height); + this->sprops[s->id].src_rect = rect{width, height, x, y}; +} + +void controller::surface_destination_rectangle(struct surface *s, int32_t x, + int32_t y, int32_t width, + int32_t height) { + logdebug("genivi::surface %s @ %d x %i y %i w %i h %i", __func__, + s->id, x, y, width, height); + this->sprops[s->id].dst_rect = rect{width, height, x, y}; + this->chooks->surface_destination_rectangle(s->id, x, y, width, height); +} + +void controller::surface_configuration(struct surface *s, int32_t width, + int32_t height) { + logdebug("genivi::surface %s @ %d w %i h %i", __func__, s->id, + width, height); + this->sprops[s->id].size = size{uint32_t(width), uint32_t(height)}; +} + +void controller::surface_orientation(struct surface *s, int32_t orientation) { + logdebug("genivi::surface %s @ %d o %i", __func__, s->id, + orientation); + this->sprops[s->id].orientation = orientation; +} + +void controller::surface_pixelformat(struct surface * s, + int32_t pixelformat) { + logdebug("genivi::surface %s @ %d f %i", __func__, s->id, + pixelformat); +} + +void controller::surface_layer(struct surface * s, + struct ivi_controller_layer *layer) { + logdebug("genivi::surface %s @ %d l %u @ %p", __func__, s->id, + this->layer_proxy_to_id[uintptr_t(layer)], layer); +} + +void controller::surface_stats(struct surface *s, uint32_t redraw_count, + uint32_t frame_count, uint32_t update_count, + uint32_t pid, const char *process_name) { + logdebug("genivi::surface %s @ %d r %u f %u u %u pid %u p %s", __func__, + s->id, redraw_count, frame_count, update_count, pid, + process_name); +} + +void controller::surface_destroyed(struct surface *s) { + logdebug("genivi::surface %s @ %d", __func__, s->id); + this->chooks->surface_removed(s->id); + // XXX: do I need to actually remove the surface late, i.e. using add_task()? + this->sprops.erase(s->id); + this->surfaces.erase(s->id); +} + +void controller::surface_content(struct surface *s, int32_t content_state) { + logdebug("genivi::surface %s @ %d s %i", __func__, s->id, + content_state); + if (content_state == IVI_CONTROLLER_SURFACE_CONTENT_STATE_CONTENT_REMOVED) { + // XXX is this the right thing to do? + this->chooks->surface_removed(s->id); + this->sprops.erase(s->id); + this->surfaces.erase(s->id); + } +} + +void controller::add_proxy_to_id_mapping(struct ivi_controller_surface *p, + uint32_t id) { + logdebug("Add surface proxy mapping for %p (%u)", p, id); + this->surface_proxy_to_id[uintptr_t(p)] = id; + this->sprops[id].id = id; +} + +void controller::remove_proxy_to_id_mapping(struct ivi_controller_surface *p) { + logdebug("Remove surface proxy mapping for %p", p); + this->surface_proxy_to_id.erase(uintptr_t(p)); +} + +void controller::add_proxy_to_id_mapping(struct ivi_controller_layer *p, + uint32_t id) { + logdebug("Add layer proxy mapping for %p (%u)", p, id); + this->layer_proxy_to_id[uintptr_t(p)] = id; + this->lprops[id].id = id; +} + +void controller::remove_proxy_to_id_mapping(struct ivi_controller_layer *p) { + logdebug("Remove layer proxy mapping for %p", p); + this->layer_proxy_to_id.erase(uintptr_t(p)); +} + +void controller::add_proxy_to_id_mapping(struct wl_output *p, uint32_t id) { + logdebug("Add screen proxy mapping for %p (%u)", p, id); + this->screen_proxy_to_id[uintptr_t(p)] = id; +} + +void controller::remove_proxy_to_id_mapping(struct wl_output *p) { + logdebug("Remove screen proxy mapping for %p", p); + this->screen_proxy_to_id.erase(uintptr_t(p)); +} + +// +// ___ ___ _ __ ___ ___ _ __ +// / __|/ __| '__/ _ \/ _ \ '_ \ +// \__ \ (__| | | __/ __/ | | | +// |___/\___|_| \___|\___|_| |_| +// +screen::screen(uint32_t i, struct controller *c, + struct ivi_controller_screen *p) + : wayland_proxy(p), controller_child(c, i) { + logdebug("genivi::screen @ %p id %u", p, i); +} + +void screen::clear() { ivi_controller_screen_clear(this->proxy.get()); } + +void screen::add_layer(layer *l) { + ivi_controller_screen_add_layer(this->proxy.get(), l->proxy.get()); +} + +void screen::set_render_order(std::vector<uint32_t> const &ro) { + struct wl_array wlro { + .size = ro.size() * sizeof(ro[0]), .alloc = ro.capacity() * sizeof(ro[0]), + .data = const_cast<void *>(static_cast<void const *>(ro.data())) + }; + ivi_controller_screen_set_render_order(this->proxy.get(), &wlro); +} + +} // namespace genivi diff --git a/src/wayland.hpp b/src/wayland.hpp new file mode 100644 index 0000000..61a840d --- /dev/null +++ b/src/wayland.hpp @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WM_WAYLAND_HPP +#define WM_WAYLAND_HPP + +#include "controller_hooks.hpp" +#include "ivi-controller-client-protocol.h" +#include "util.hpp" + +#include <functional> +#include <memory> +#include <unordered_map> +#include <vector> + +// _ _ +// __ ____ _ _ _| | __ _ _ __ __| | _ __ _ __ _____ ___ _ +// \ \ /\ / / _` | | | | |/ _` | '_ \ / _` | | '_ \| '__/ _ \ \/ / | | | +// \ V V / (_| | |_| | | (_| | | | | (_| | | |_) | | | (_) > <| |_| | +// \_/\_/ \__,_|\__, |_|\__,_|_| |_|\__,_|____| .__/|_| \___/_/\_\\__, | +// |___/ |_____|_| |___/ +template <typename ProxyT> +struct wayland_proxy { + std::unique_ptr<ProxyT, std::function<void(ProxyT *)>> proxy; + wayland_proxy(wayland_proxy const &) = delete; + wayland_proxy &operator=(wayland_proxy const &) = delete; + wayland_proxy(void *p) + : wayland_proxy(p, + reinterpret_cast<void (*)(ProxyT *)>(wl_proxy_destroy)) {} + wayland_proxy(void *p, std::function<void(ProxyT *)> &&p_del) + : proxy(std::unique_ptr<ProxyT, std::function<void(ProxyT *)>>( + static_cast<ProxyT *>(p), p_del)) {} +}; + +// _ +// _ __ __ _ _ __ ___ ___ ___ _ __ __ _ ___ ___ __ _| | +// | '_ \ / _` | '_ ` _ \ / _ \/ __| '_ \ / _` |/ __/ _ \ \ \ /\ / / | +// | | | | (_| | | | | | | __/\__ \ |_) | (_| | (_| __/ \ V V /| | +// |_| |_|\__,_|_| |_| |_|\___||___/ .__/ \__,_|\___\___| \_/\_/ |_| +// |_| +namespace wl { +// _ _ +// _ __ ___ __ _(_)___| |_ _ __ _ _ +// | '__/ _ \/ _` | / __| __| '__| | | | +// | | | __/ (_| | \__ \ |_| | | |_| | +// |_| \___|\__, |_|___/\__|_| \__, | +// |___/ |___/ +struct registry : public wayland_proxy<struct wl_registry> { + typedef std::function<void(struct wl_registry *, uint32_t, uint32_t)> binder; + std::unordered_map<std::string, binder> bindings; + + registry(registry const &) = delete; + registry &operator=(registry const &) = delete; + registry(struct wl_display *d); + + void add_global_handler(char const *iface, binder bind); + + // Events + void global(uint32_t name, char const *iface, uint32_t v); + void global_remove(uint32_t name); +}; + +// _ _ _ +// __| (_)___ _ __ | | __ _ _ _ +// / _` | / __| '_ \| |/ _` | | | | +// | (_| | \__ \ |_) | | (_| | |_| | +// \__,_|_|___/ .__/|_|\__,_|\__, | +// |_| |___/ +struct display { + std::unique_ptr<struct wl_display, void (*)(struct wl_display *)> d; + struct registry r; + + display(display const &) = delete; + display &operator=(display const &) = delete; + display(); + bool ok() const; + void roundtrip(); + int dispatch(); + int dispatch_pending(); + int read_events(); + void flush(); + int get_fd() const; + int get_error(); + + // Lets just proxy this for the registry + inline void add_global_handler(char const *iface, registry::binder bind) { + this->r.add_global_handler(iface, bind); + } +}; + +// _ _ +// ___ _ _| |_ _ __ _ _| |_ +// / _ \| | | | __| '_ \| | | | __| +// | (_) | |_| | |_| |_) | |_| | |_ +// \___/ \__,_|\__| .__/ \__,_|\__| +// |_| +struct output : wayland_proxy<struct wl_output> { + int width{}; + int height{}; + int refresh{}; + int transform{}; + + output(output const &) = delete; + output &operator=(output const &) = delete; + output(struct wl_registry *r, uint32_t name, uint32_t v); + + // Events + void geometry(int32_t x, int32_t y, int32_t pw, int32_t ph, int32_t subpel, + char const *make, char const *model, int32_t tx); + void mode(uint32_t flags, int32_t w, int32_t h, int32_t r); + void done(); + void scale(int32_t factor); +}; +} // namespace wl + +// _ __ __ _ _ __ ___ ___ ___ _ __ __ _ ___ ___ +// | '_ \ / _` | '_ ` _ \ / _ \/ __| '_ \ / _` |/ __/ _ \ +// | | | | (_| | | | | | | __/\__ \ |_) | (_| | (_| __/ +// |_| |_|\__,_|_| |_| |_|\___||___/ .__/ \__,_|\___\___| +// |_| +// _ _ +// __ _ ___ _ __ (_)_ _(_) +// / _` |/ _ \ '_ \| \ \ / / | +// | (_| | __/ | | | |\ V /| | +// \__, |\___|_| |_|_| \_/ |_| +// |___/ +namespace genivi { + +struct size { + uint32_t w, h; +}; + +struct rect { + int32_t w, h; + int32_t x, y; +}; + +static const constexpr rect full_rect = rect{-1, -1, 0, 0}; + +inline bool operator==(struct rect a, struct rect b) { + return a.w == b.w && a.h == b.h && a.x == b.x && a.y == b.y; +} + +struct controller; + +struct controller_child { + struct controller *parent; + uint32_t id; + + controller_child(controller_child const &) = delete; + controller_child &operator=(controller_child const &) = delete; + controller_child(struct controller *c, uint32_t i) : parent(c), id(i) {} + virtual ~controller_child() {} +}; + +struct surface_properties { + uint32_t id; // let's just save an ID here too + struct rect dst_rect; + struct rect src_rect; + struct size size; + int32_t orientation; + int32_t visibility; + float opacity; +}; + +// __ +// ___ _ _ _ __ / _| __ _ ___ ___ +// / __| | | | '__| |_ / _` |/ __/ _ \ +// \__ \ |_| | | | _| (_| | (_| __/ +// |___/\__,_|_| |_| \__,_|\___\___| +// +struct surface : public wayland_proxy<struct ivi_controller_surface>, + controller_child { + surface(surface const &) = delete; + surface &operator=(surface const &) = delete; + surface(uint32_t i, struct controller *c); + + // Requests + void set_visibility(uint32_t visibility); + void set_opacity(wl_fixed_t opacity); + void set_source_rectangle(int32_t x, int32_t y, int32_t width, + int32_t height); + void set_destination_rectangle(int32_t x, int32_t y, int32_t width, + int32_t height); + void set_configuration(int32_t width, int32_t height); + void set_orientation(int32_t orientation); + void screenshot(const char *filename); + void send_stats(); + void destroy(int32_t destroy_scene_object); +}; + +// _ +// | | __ _ _ _ ___ _ __ +// | |/ _` | | | |/ _ \ '__| +// | | (_| | |_| | __/ | +// |_|\__,_|\__, |\___|_| +// |___/ +struct layer : public wayland_proxy<struct ivi_controller_layer>, + controller_child { + layer(layer const &) = delete; + layer &operator=(layer const &) = delete; + layer(uint32_t i, struct controller *c); + layer(uint32_t i, int32_t w, int32_t h, struct controller *c); + + // Requests + void set_visibility(uint32_t visibility); + void set_opacity(wl_fixed_t opacity); + void set_source_rectangle(int32_t x, int32_t y, int32_t width, + int32_t height); + void set_destination_rectangle(int32_t x, int32_t y, int32_t width, + int32_t height); + void set_configuration(int32_t width, int32_t height); + void set_orientation(int32_t orientation); + void screenshot(const char *filename); + void clear_surfaces(); + void add_surface(struct surface *surface); + void remove_surface(struct surface *surface); + void set_render_order(std::vector<uint32_t> const &ro); +}; + +// +// ___ ___ _ __ ___ ___ _ __ +// / __|/ __| '__/ _ \/ _ \ '_ \ +// \__ \ (__| | | __/ __/ | | | +// |___/\___|_| \___|\___|_| |_| +// +struct screen : public wayland_proxy<struct ivi_controller_screen>, + controller_child { + screen(screen const &) = delete; + screen &operator=(screen const &) = delete; + screen(uint32_t i, struct controller *c, struct ivi_controller_screen *p); + void clear(); + void add_layer(layer *l); + void set_render_order(std::vector<uint32_t> const &ro); +}; + +// _ _ _ +// ___ ___ _ __ | |_ _ __ ___ | | | ___ _ __ +// / __/ _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| +// | (_| (_) | | | | |_| | | (_) | | | __/ | +// \___\___/|_| |_|\__|_| \___/|_|_|\___|_| +// +struct controller : public wayland_proxy<struct ivi_controller> { + // This controller is still missing ivi-input + + typedef std::unordered_map<uintptr_t, uint32_t> proxy_to_id_map_type; + typedef std::unordered_map<uint32_t, std::unique_ptr<struct surface>> + surface_map_type; + typedef std::unordered_map<uint32_t, std::unique_ptr<struct layer>> + layer_map_type; + typedef std::unordered_map<uint32_t, std::unique_ptr<struct screen>> + screen_map_type; + + typedef std::unordered_map<uint32_t, struct surface_properties> props_map; + + // HACK: + // The order of these member is mandatory, as when objects are destroyed + // they will call their parent (that's us right here!) and remove their + // proxy-to-id mapping. I.e. the *_proxy_to_id members need to be valid + // when the surfaces/layers/screens maps are destroyed. This sucks, but + // I cannot see a better solution w/o globals or some other horrible + // call-our-parent construct. + proxy_to_id_map_type surface_proxy_to_id; + proxy_to_id_map_type layer_proxy_to_id; + proxy_to_id_map_type screen_proxy_to_id; + + props_map sprops; + props_map lprops; + + surface_map_type surfaces; + layer_map_type layers; + screen_map_type screens; + + size output_size; + + wm::controller_hooks *chooks; + + void add_proxy_to_id_mapping(struct ivi_controller_surface *p, uint32_t id); + void remove_proxy_to_id_mapping(struct ivi_controller_surface *p); + void add_proxy_to_id_mapping(struct ivi_controller_layer *p, uint32_t id); + void remove_proxy_to_id_mapping(struct ivi_controller_layer *p); + void add_proxy_to_id_mapping(struct wl_output *p, uint32_t id); + void remove_proxy_to_id_mapping(struct wl_output *p); + + bool surface_exists(uint32_t id) const { + return this->surfaces.find(id) != this->surfaces.end(); + } + + bool layer_exists(uint32_t id) const { + return this->layers.find(id) != this->layers.end(); + } + + controller(struct wl_registry *r, uint32_t name, uint32_t version); + + // Requests + void commit_changes() const { + ivi_controller_commit_changes(this->proxy.get()); + } + void layer_create(uint32_t id, int32_t w, int32_t h); + void surface_create(uint32_t id); + + // Events + // controller + void controller_screen(uint32_t id, struct ivi_controller_screen *screen); + void controller_layer(uint32_t id); + void controller_surface(uint32_t id); + void controller_error(int32_t object_id, int32_t object_type, + int32_t error_code, char const *error_text); + + // surface + void surface_visibility(struct surface *s, int32_t visibility); + void surface_opacity(struct surface *s, float opacity); + void surface_source_rectangle(struct surface *s, int32_t x, int32_t y, + int32_t width, int32_t height); + void surface_destination_rectangle(struct surface *s, int32_t x, int32_t y, + int32_t width, int32_t height); + void surface_configuration(struct surface *s, int32_t width, int32_t height); + void surface_orientation(struct surface *s, int32_t orientation); + void surface_pixelformat(struct surface *s, int32_t pixelformat); + void surface_layer(struct surface *s, struct ivi_controller_layer *layer); + void surface_stats(struct surface *s, uint32_t redraw_count, + uint32_t frame_count, uint32_t update_count, uint32_t pid, + const char *process_name); + void surface_destroyed(struct surface *s); + void surface_content(struct surface *s, int32_t content_state); + + // layer + void layer_visibility(struct layer *l, int32_t visibility); + void layer_opacity(struct layer *l, float opacity); + void layer_source_rectangle(struct layer *l, int32_t x, int32_t y, + int32_t width, int32_t height); + void layer_destination_rectangle(struct layer *l, int32_t x, int32_t y, + int32_t width, int32_t height); + void layer_configuration(struct layer *l, int32_t width, int32_t height); + void layer_orientation(struct layer *l, int32_t orientation); + void layer_screen(struct layer *l, struct wl_output *screen); + void layer_destroyed(struct layer *l); +}; +} // namespace genivi + +#endif // !WM_WAYLAND_HPP |