Skip to content
This repository was archived by the owner on Jan 30, 2025. It is now read-only.

Commit

Permalink
vm_tools: Adds sommelier shim gen files
Browse files Browse the repository at this point in the history
+ Adds scripts and template files to generate wayland shims.
* Modifies build scripts to generate and build these files

BUG=b:255439799
TEST=CQ - This is generating test infra stuff.

Cq-Depend: chromium:4369582, chrome-internal:5651758
Change-Id: Ice996b9e985d6d85a6ffc762ee9629c4e48d8fef
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/4349388
Reviewed-by: Chloe Pelling <[email protected]>
Tested-by: Justin Huang <[email protected]>
Reviewed-by: Nic Hollingum <[email protected]>
Commit-Queue: Justin Huang <[email protected]>
  • Loading branch information
Justin Huang authored and Chromeos LUCI committed Mar 27, 2023
1 parent fa86f5b commit 2528898
Show file tree
Hide file tree
Showing 7 changed files with 390 additions and 20 deletions.
47 changes: 29 additions & 18 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# found in the LICENSE file.

import("//common-mk/pkg_config.gni")
import("shim_gen.gni")
import("wayland_protocol.gni")

group("all") {
Expand Down Expand Up @@ -36,25 +37,32 @@ if (!defined(dark_frame_color)) {
dark_frame_color = "\"#323639\""
}

wayland_protocols = [
"protocol/aura-shell.xml",
"protocol/drm.xml",
"protocol/gaming-input-unstable-v2.xml",
"protocol/gtk-shell.xml",
"protocol/keyboard-extension-unstable-v1.xml",
"protocol/linux-dmabuf-unstable-v1.xml",
"protocol/linux-explicit-synchronization-unstable-v1.xml",
"protocol/pointer-constraints-unstable-v1.xml",
"protocol/relative-pointer-unstable-v1.xml",
"protocol/text-input-extension-unstable-v1.xml",
"protocol/text-input-unstable-v1.xml",
"protocol/text-input-x11-unstable-v1.xml",
"protocol/viewporter.xml",
"protocol/xdg-output-unstable-v1.xml",
"protocol/xdg-shell.xml",
]

wayland_protocol_library("sommelier-protocol") {
out_dir = "include"
sources = [
"protocol/aura-shell.xml",
"protocol/drm.xml",
"protocol/gaming-input-unstable-v2.xml",
"protocol/gtk-shell.xml",
"protocol/keyboard-extension-unstable-v1.xml",
"protocol/linux-dmabuf-unstable-v1.xml",
"protocol/linux-explicit-synchronization-unstable-v1.xml",
"protocol/pointer-constraints-unstable-v1.xml",
"protocol/relative-pointer-unstable-v1.xml",
"protocol/text-input-extension-unstable-v1.xml",
"protocol/text-input-unstable-v1.xml",
"protocol/text-input-x11-unstable-v1.xml",
"protocol/viewporter.xml",
"protocol/xdg-output-unstable-v1.xml",
"protocol/xdg-shell.xml",
]
sources = wayland_protocols
}

gen_shim("sommelier-shims") {
out_dir = "include"
sources = wayland_protocols
}

gaming_defines = [ "GAMEPAD_SUPPORT" ]
Expand Down Expand Up @@ -129,7 +137,10 @@ static_library("libsommelier") {
"xkbcommon",
] + tracing_pkg_deps
libs = [ "m" ] + tracing_libs
deps = [ ":sommelier-protocol" ] + gaming_deps
deps = [
":sommelier-protocol",
":sommelier-shims",
] + gaming_deps
}

executable("sommelier") {
Expand Down
175 changes: 175 additions & 0 deletions gen-shim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/usr/bin/env python3
# Copyright 2023 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""A C++ code generator of Wayland protocol shims."""

# pylint: enable=import-error
import os
import sys
from xml.etree import ElementTree

# pylint: disable=import-error
from jinja2 import Template


def CppTypeForWaylandEventType(xml_type_string, interface):
"""Generates the type for a wayland event argument."""
if xml_type_string == "new_id" or xml_type_string == "object":
return "struct wl_resource *"
else:
return CppTypeForWaylandType(xml_type_string, interface)


def CppTypeForWaylandType(xml_type_string, interface):
"""Generates the type for generic wayland type."""
if xml_type_string == "int" or xml_type_string == "fd":
return "int32_t"
elif xml_type_string == "uint" or xml_type_string == "new_id":
return "uint32_t"
elif xml_type_string == "fixed":
return "wl_fixed_t"
elif xml_type_string == "string":
return "const char *"
elif xml_type_string == "object":
return "struct %s *" % interface
elif xml_type_string == "array":
return "struct wl_array *"
else:
raise ValueError("Invalid Type conversion: %s" % xml_type_string)


def GetRequestReturnType(args):
"""Gets the return type of a Wayland request."""
for arg in args:
if arg.attrib["type"] == "new_id":
if "interface" in arg.attrib:
return "struct %s *" % arg.attrib["interface"]
else:
return "void *"
return "void"


def RequestXmlToJinjaInput(request):
"""Parses a <request> element into dictionary form for use of jinja template."""
method = {"name": request.attrib["name"], "args": [], "ret": ""}
method["ret"] = GetRequestReturnType(request.findall("arg"))

for arg in request.findall("arg"):
if arg.attrib["type"] == "new_id":
if not arg.attrib.get("interface"):
method["args"].append(
{
"type": "const struct wl_interface *",
"name": "interface",
}
)
method["args"].append({"type": "uint32_t", "name": "version"})
else:
method["args"].append(
{
"name": arg.attrib["name"],
"type": CppTypeForWaylandType(
arg.attrib["type"], arg.attrib.get("interface", "")
),
}
)
return method


def EventXmlToJinjaInput(event):
"""Parses an <event> element into dictionary for for use of jinja template."""
return {
"name": event.attrib["name"],
"args": [
{
"name": arg.attrib["name"],
"type": CppTypeForWaylandEventType(
arg.attrib["type"], arg.attrib.get("interface", "")
),
}
for arg in event.findall("arg")
],
}


def InterfaceXmlToJinjaInput(interface):
"""Creates an interface dict for XML interface input."""
interf = {
"name": "".join(
[i.capitalize() for i in interface.attrib["name"].split("_")]
)
+ "Shim",
"name_underscore": interface.attrib["name"],
"methods": [
RequestXmlToJinjaInput(i) for i in interface.findall("request")
],
"events": [EventXmlToJinjaInput(i) for i in interface.findall("event")],
}
return interf


def GenerateShims(in_xml, out_directory):
"""Generates shims for Wayland Protocols."""
with open(
os.path.dirname(os.path.abspath(__file__))
+ "/gen/protocol-shim.h.jinja2",
encoding="utf-8",
) as f:
shim_template = Template(f.read())
with open(
os.path.dirname(os.path.abspath(__file__))
+ "/gen/protocol-shim.cc.jinja2",
encoding="utf-8",
) as f:
shim_impl_template = Template(f.read())
with open(
os.path.dirname(os.path.abspath(__file__))
+ "/gen/mock-protocol-shim.h.jinja2",
encoding="utf-8",
) as f:
mock_template = Template(f.read())

tree = ElementTree.parse(in_xml)
root = tree.getroot()

filename = os.path.basename(in_xml).split(".")[0]

# Because some protocol files don't have the protocol name == file name, we
# have to infer the name from the file name instead (gtk-shell :eyes:)
protocol = {
"interfaces": [
InterfaceXmlToJinjaInput(i) for i in root.findall("interface")
],
"name_hyphen": filename,
"name_underscore": filename.replace("-", "_"),
}

with open(
out_directory + "/" + protocol["name_hyphen"] + "-shim.h",
"w",
encoding="utf-8",
) as f:
f.write(shim_template.render(protocol=protocol))

with open(
out_directory + "/" + protocol["name_hyphen"] + "-shim.cc",
"w",
encoding="utf-8",
) as f:
f.write(shim_impl_template.render(protocol=protocol))

with open(
out_directory + "/mock-" + protocol["name_hyphen"] + "-shim.h",
"w",
encoding="utf-8",
) as f:
f.write(mock_template.render(protocol=protocol))


if __name__ == "__main__":
source_xml = sys.argv[1]
out_dir = sys.argv[2]

GenerateShims(source_xml, out_dir)
23 changes: 23 additions & 0 deletions gen/mock-protocol-shim.h.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// NOTE: This code is automatically generated and any changes to this file will be overwritten.

#ifndef VM_TOOLS_SOMMELIER_GEN_MOCK_{{ protocol.name_underscore | upper }}_SHIM_H_
#define VM_TOOLS_SOMMELIER_GEN_MOCK_{{ protocol.name_underscore | upper }}_SHIM_H_

#include "{{ protocol.name_hyphen }}-shim.h"

{%- for interface in protocol.interfaces %}
class Mock{{interface.name}} {
public:
{%- for method in interface.methods %}
MOCK_METHOD({{ method.ret }}, {{ method.name }}, ({% for arg in method.args %}{{ arg.type }} {{ arg.name }}{% if not loop.last %},{% endif %}{% endfor %}), (override));
{% endfor -%}
{%- for event in interface.events %}
MOCK_METHOD(void, {{ event.name }}, ({% for arg in event.args %}{{ arg.type }} {{ arg.name }}{% if not loop.last %},{% endif %}{% endfor %}), (override));
{% endfor -%}
};
{% endfor -%}

#endif // VM_TOOLS_SOMMELIER_GEN_MOCK_{{ protocol.name_underscore | upper }}_SHIM_H_
44 changes: 44 additions & 0 deletions gen/protocol-shim.cc.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// NOTE: This code is automatically generated and any changes to this file will be overwritten.

#include "{{ protocol.name_hyphen }}-shim.h"

{%- for interface in protocol.interfaces %}

void {{ interface.name }}::set_user_data(struct {{interface.name_underscore}} *{{interface.name_underscore}}, void* user_data) {
{{ interface.name_underscore }}_set_user_data({{ interface.name_underscore }}, user_data);
}

void* {{ interface.name }}::get_user_data(struct {{ interface.name_underscore }} *{{interface.name_underscore}}) {
return {{ interface.name_underscore }}_get_user_data({{ interface.name_underscore }});
}

{%- for method in interface.methods %}
{{ method.ret }} {{ interface.name }}::{{method.name}}(
struct {{ interface.name_underscore }} *{{ interface.name_underscore }}{% for arg in method.args %},
{{ arg.type }} {{ arg.name }}{%endfor%}) {
{% if method.ret %}return {% endif %}{{ interface.name_underscore }}_{{ method.name }}({{ interface.name_underscore }}{% for arg in method.args %}, {{ arg.name }}{% endfor %});
}
{% endfor -%}

{%- for event in interface.events %}
void {{ interface.name}}::send_{{event.name}}(
struct wl_resource* resource{% for arg in event.args %},
{{ arg.type }} {{ arg.name }}{% endfor %}) {
{{ interface.name_underscore }}_send_{{ event.name }}(resource{% for arg in event.args %}, {{ arg.name }}{% endfor %});
}
{% endfor -%}


static {{ interface.name }}* {{ interface.name_underscore }}_singleton = nullptr;

{{ interface.name }}* {{ interface.name_underscore | lower }}_shim() {
return {{ interface.name_underscore }}_singleton;
}

void set_{{ interface.name_underscore }}_shim ({{ interface.name }}* shim) {
{{ interface.name_underscore }}_singleton = shim;
}
{% endfor -%}
47 changes: 47 additions & 0 deletions gen/protocol-shim.h.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{# NOTE: This combines both the client and server APIs together because sommelier is both -#}
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// NOTE: This code is automatically generated and any changes to this file will be overwritten.

#ifndef VM_TOOLS_SOMMELIER_GEN_{{ protocol.name_underscore | upper }}_SHIM_H_
#define VM_TOOLS_SOMMELIER_GEN_{{ protocol.name_underscore | upper }}_SHIM_H_

#include "{{ protocol.name_hyphen }}-client-protocol.h" // NOLINT(build/include_directory)
#include "{{ protocol.name_hyphen }}-server-protocol.h" // NOLINT(build/include_directory)

{# This generates a series of virtual functions which calls the underlying
wayland protocol functions. It's mostly modelled off wayland-scanner, ref:
https://chromium.googlesource.com/external/wayland/wayland/+/refs/heads/master/src/scanner.c#932 -#}
{% for interface in protocol.interfaces %}
class {{ interface.name }} {
public:
{{ interface.name }}() = default;
{{ interface.name }}({{ interface.name }}&&) = delete;
{{ interface.name }}& operator=({{ interface.name }}&&) = delete;

{# Logic comes from wayland scanner -#}
{# Stub logic -> https://chromium.googlesource.com/external/wayland/wayland/+/refs/heads/master/src/scanner.c#1007 -#}
virtual void set_user_data(struct {{ interface.name_underscore }} *{{ interface.name_underscore}}, void *user_data);

virtual void* get_user_data(struct {{ interface.name_underscore }} *{{ interface.name_underscore }});

{%- for method in interface.methods %}
virtual {{ method.ret }} {{ method.name }}(
struct {{ interface.name_underscore }} *{{ interface.name_underscore }}{% for arg in method.args %},
{{ arg.type }} {{ arg.name }}{% endfor %});
{% endfor -%}

{# Event logic -> https://chromium.googlesource.com/external/wayland/wayland/+/refs/heads/master/src/scanner.c#1074 -#}
{%- for event in interface.events %}
virtual void send_{{ event.name }}(
struct wl_resource * resource{% for arg in event.args %},
{{ arg.type }} {{arg.name}}{% endfor %});
{% endfor -%}
};

{{ interface.name }}* {{ interface.name_underscore | lower }}_shim();
void set_{{ interface.name_underscore }}_shim ({{ interface.name }}* shim);
{% endfor %}

#endif // VM_TOOLS_SOMMELIER_GEN_{{ protocol.name_underscore | upper}}_SHIM_H_
Loading

0 comments on commit 2528898

Please sign in to comment.