From: Marcus Fritzsch Date: Wed, 12 Jul 2017 13:59:37 +0000 (+0200) Subject: Generating binding API glue code using generate-binding.py X-Git-Tag: 4.99.1~249 X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=commitdiff_plain;h=54c34361d08386c751897ee215554c9ea1efd108;p=staging%2Fwindowmanager.git Generating binding API glue code using generate-binding.py Signed-off-by: Marcus Fritzsch --- diff --git a/generate-binding.py b/generate-binding.py new file mode 100644 index 0000000..6c700de --- /dev/null +++ b/generate-binding.py @@ -0,0 +1,111 @@ +#!/usr/bin/python3 + +import sys + +OUT = sys.stdout + +def set_output(f): + global OUT + OUT = f + +def p(*args): + OUT.write('\n'.join(args)) + OUT.write('\n') + +def emit_func_impl(api, f): + args = f.get('args', []) + if len(args) > 0: + p(' json_object *jreq = afb_req_json(req);', '') + for arg in args: + arg['jtype'] = arg.get('jtype', arg['type']) # add jtype default + p(' json_object *j_%(name)s = nullptr;' % arg, + ' if (! json_object_object_get_ex(jreq, "%(name)s", &j_%(name)s)) {' % arg, + ' afb_req_fail(req, "failed", "Need %(type)s argument %(name)s");' % arg, + ' return;', + ' }', + ' %(type)s a_%(name)s = json_object_get_%(jtype)s(j_%(name)s);' % arg, '') + p(' auto ret = %(api)s' % api + '%(name)s(' % f + ', '.join(map(lambda x: 'a_' + x['name'], args)) + ');') + p(' if (ret.is_err()) {', + ' afb_req_fail(req, "failed", ret.unwrap_err());', + ' return;', + ' }', '') + p(' afb_req_success(req, ret.unwrap(), "success");') + +def emit_func(api, f): + p('void %(impl_name)s(afb_req req) noexcept {' % f) + p(' if (g_afb_instance == nullptr) {', + ' afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");', + ' return;', + ' }', '', + ' try {', ' // BEGIN impl') + emit_func_impl(api, f) + p(' // END impl', + ' } catch (std::exception &e) {', + ' afb_req_fail_f(req, "failed", "Uncaught exception while calling %(name)s: %%s", e.what());' % f, + ' return;', + ' }', '') + p('}', '') + +def emit_afb_verbs(api): + p('const struct afb_verb_v2 %(name)s_verbs[] = {' % api) + for f in api['functions']: + p(' { "%(name)s", %(impl_name)s, nullptr, nullptr, AFB_SESSION_NONE },' % f) + p(' {}', '};') + +def emit_binding(api): + p('namespace {', '') + for func in api['functions']: + emit_func(api, func) + p('} // namespace', '') + emit_afb_verbs(api) + +def generate_names(api): + for f in api['functions']: + f['impl_name'] = '%s_%s_thunk' % (api['name'], f['name']) + +def emit_afb_api(api): + p('#include "result.hpp"', '') + p('#include ', '') + p('namespace wm {', '') + p('struct App;', '') + p('struct binding_api {') + p(' typedef wm::result result_type;') + p(' struct wm::App *app;') + for f in api['functions']: + p(' result_type %(name)s(' % f + ', '.join(map(lambda x: '%(type)s %(name)s' % x, f.get('args', []))) + ');') + p('};', '') + p('} // namespace wm') + +# names must always be valid in c and unique for each function (that is its arguments) +# arguments will be looked up from json request, range checking needs to be implemented +# by the actual API call +API = { + 'name': 'winman', + 'api': 'g_afb_instance->app.api.', # where are our API functions + 'functions': [ + { + 'name': 'register_surface', + #'return_type': 'int', # Or do they return all just some json? + 'args': [ # describes the functions arguments, and their names as found in the json request + { 'name': 'appid', 'type': 'uint32_t', 'jtype': 'int' }, # XXX: lookup jtypes automatically? i.e. char*|const char* would be string? + { 'name': 'surfaceid', 'type': 'uint32_t', 'jtype': 'int' }, + ], + }, + { 'name': 'debug_status', }, + { 'name': 'debug_layers', }, + { 'name': 'debug_surfaces', }, + ] +} + +def main(): + with open('afb_binding.inl', 'w') as out: + set_output(out) + p('// This file was generated, do not edit', '') + generate_names(API) + emit_binding(API) + with open('afb_api.hpp', 'w') as out: + set_output(out) + p('// This file was generated, do not edit', '') + emit_afb_api(API) + +__name__ == '__main__' and main() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 15ecc3c..4bb082f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,13 +7,18 @@ 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_api.hpp afb_binding.inl + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../generate-binding.py + COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/../generate-binding.py) + add_library(winman MODULE main.cpp wayland.cpp wayland.hpp util.cpp util.hpp - ${IVI_CON_PROTO} json_helper.cpp json_helper.hpp app.hpp app.cpp) + ${IVI_CON_PROTO} json_helper.cpp json_helper.hpp app.hpp app.cpp result.hpp afb_api.hpp afb_binding.inl) target_include_directories(winman PRIVATE diff --git a/src/app.cpp b/src/app.cpp index 5da4f9d..74f3bfa 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -3,28 +3,48 @@ // #include "app.hpp" +#include "util.hpp" +#include "json_helper.hpp" +#include "wayland.hpp" + +#include namespace wm { App::App() - : api{this} + : api{this}, display{}, controller{} {} - result App::API::register_surface(uint32_t appid, + binding_api::result_type binding_api::register_surface(uint32_t appid, uint32_t surfid) { - return Err("not implemented"); + logdebug("%s appid %u surfid %u", __func__, appid, surfid); + if (appid > 0xff) { + return Err("invalid appid"); + } + + if (surfid > 0xffff) { + return Err("invalid surfaceid"); + } + + return Ok(json_object_new_int((appid << 16) + surfid)); } - result App::API::debug_layers() const { - return Err("not implemented"); + binding_api::result_type binding_api::debug_layers() { + logdebug("%s", __func__); + return Ok(to_json(this->app->controller->lprops)); } - result App::API::debug_surfaces() const { - return Err("not implemented"); + binding_api::result_type binding_api::debug_surfaces() { + logdebug("%s", __func__); + return Ok(to_json(this->app->controller->sprops)); } - result App::API::debug_status() const { - return Err("not implemented"); + 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); } } // namespace wm \ No newline at end of file diff --git a/src/app.hpp b/src/app.hpp index a3108b3..397548a 100644 --- a/src/app.hpp +++ b/src/app.hpp @@ -5,44 +5,24 @@ #ifndef TMCAGLWM_APP_HPP #define TMCAGLWM_APP_HPP -#include -#include +#include -namespace wm { - - using std::experimental::optional; - using std::experimental::nullopt; - - template - struct result { - optional e; - optional t; - - bool is_ok() const { return this->t != nullopt; } - bool is_err() const { return this->e != nullopt; } - T unwrap() { return this->t.value(); } - }; +#include "result.hpp" +#include "afb_api.hpp" - template - struct result Err(E e) { return result{e, nullopt}; } +namespace wl { + struct display; +} +namespace genivi { + struct controller; +} - template - struct result Ok(T t) { return result{nullopt, t}; } - - using json = nlohmann::json; +namespace wm { struct App { - struct API { - struct App *app; - - result debug_status() const; - result debug_layers() const; - result debug_surfaces() const; - - result register_surface(uint32_t appid, uint32_t surfid); - }; - - struct API api; + struct binding_api api; + struct wl::display *display; + struct genivi::controller *controller; App(); }; diff --git a/src/main.cpp b/src/main.cpp index 5c9df33..23b1b4d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,7 +16,6 @@ struct afb_instance { std::unique_ptr display; std::unique_ptr controller; std::vector> outputs; - wm::App app; afb_instance() : display{new wl::display}, controller{nullptr}, outputs{}, app{} {} @@ -57,6 +56,10 @@ int afb_instance::init() { // Third level objects this->display->roundtrip(); + // Init the app's members + this->app.display = this->display.get(); + this->app.controller = this->controller.get(); + return 0; } @@ -202,98 +205,9 @@ int binding_init() noexcept { return -1; } -// _ _ _ _ ____ -// __| | ___| |__ _ _ __ _ ___| |_ __ _| |_ _ _ ___ / /\ \ -// / _` |/ _ \ '_ \| | | |/ _` | / __| __/ _` | __| | | / __| | | | -// | (_| | __/ |_) | |_| | (_| | \__ \ || (_| | |_| |_| \__ \ | | | -// \__,_|\___|_.__/ \__,_|\__, |___|___/\__\__,_|\__|\__,_|___/ | | | -// |___/_____| \_\/_/ -void debug_status(struct afb_req req) { - // Quick and dirty, dump current surfaces and layers - AFB_REQ_DEBUG(req, "status"); - - // auto r = g_afb_instance->app.api.debug_status(); - // if (r.is_err()) { - // afb_req_fail(req, "failed", r.e.value()); - // return; - // } - - auto o = json_object_new_object(); - json_object_object_add(o, "surfaces", - to_json(g_afb_instance->controller->sprops)); - json_object_object_add(o, "layers", to_json(g_afb_instance->controller->lprops)); -// json_object_object_add(o, "screens", -// to_json(g_afb_instance->controller->screens)); - - afb_req_success(req, o, "status"); -} - -void debug_surfaces(afb_req req) { - afb_req_success(req, to_json(g_afb_instance->controller->sprops), "surfaces"); -} - -void debug_layers(afb_req req) { - afb_req_success(req, to_json(g_afb_instance->controller->lprops), "layers"); -} - -// Dummy register_surface implementation -void register_surface(afb_req req) { - AFB_DEBUG("register_surface"); - - auto jo = afb_req_json(req); - json_object *jappid; - if (! json_object_object_get_ex(jo, "appid", &jappid)) { - afb_req_fail(req, "failed", "register_surface needs 'appid' integer argument"); - return; - } - - json_object *jsurfid; - if (! json_object_object_get_ex(jo, "surfaceid", &jsurfid)) { - afb_req_fail(req, "failed", "register_surface needs 'surfaceid' integer argument"); - return; - } - - uint32_t appid = json_object_get_int(jappid); - uint32_t surfid = json_object_get_int(jsurfid); - - if (appid > 0xff) { - afb_req_fail(req, "failed", "invalid appid"); - return; - } - - if (surfid > 0xffff) { - afb_req_fail(req, "failed", "invalid surfaceid"); - return; - } - - lognotice("register_surface, got appid %d and surfaceid %d", appid, surfid); - - afb_req_success(req, json_object_new_int((appid << 16) + surfid), "success"); -} - -#define WRAP(F) \ - [](afb_req req) noexcept { \ - if (g_afb_instance == nullptr) { \ - afb_req_fail(req, "failed", \ - "Binding not initialized, did the compositor die?"); \ - return; \ - } \ - try { \ - F(req); \ - } catch (std::exception & e) { \ - afb_req_fail_f(req, "failed", "Uncaught exception: %s", e.what()); \ - } \ - } - -const struct afb_verb_v2 verbs[] = { - {"debug::status", WRAP(debug_status), NULL, NULL, AFB_SESSION_NONE_V2}, - {"debug::layers", WRAP(debug_layers), NULL, NULL, AFB_SESSION_NONE_V2}, - {"debug::surfaces", WRAP(debug_surfaces), NULL, NULL, AFB_SESSION_NONE_V2}, +} // namespace - {"register_surface", WRAP(register_surface), NULL, NULL, AFB_SESSION_NONE_V2}, - {} -}; -} // namespace +#include "afb_binding.inl" extern "C" const struct afb_binding_v2 afbBindingV2 = { - "winman", NULL, NULL, verbs, NULL, binding_init, NULL, 1}; + "winman", NULL, NULL, winman_verbs, NULL, binding_init, NULL, 1}; diff --git a/src/result.hpp b/src/result.hpp new file mode 100644 index 0000000..fe9956b --- /dev/null +++ b/src/result.hpp @@ -0,0 +1,38 @@ +// +// Created by mfritzsc on 7/12/17. +// + +#ifndef TMCAGLWM_RESULT_HPP +#define TMCAGLWM_RESULT_HPP + +#include + +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 + struct result { + char const *e; + optional t; + + bool is_ok() const { return this->t != nullopt; } + bool is_err() const { return this->e != nullptr; } + + T unwrap() { return this->t.value(); } + + char const *unwrap_err() { return this->e; } + }; + + template + struct result Err(char const *e) { return result{e, nullopt}; } + + template + struct result Ok(T t) { return result{nullptr, t}; } + +} // namespace wm + +#endif //TMCAGLWM_RESULT_HPP