Example #1
0
        private static void WriteProtocol(XContainer doc, CSharpWriter w)
        {
            // for writing the high level API
            var hlw = new CSharpWriter();

            var protocolElement = doc.Element(ProtocolElement);

            if (protocolElement == null)
            {
                throw new Exception("No protocol element found!");
            }

            var protocolName = protocolElement.Attribute(NameAttrib).Value;

            w.Line($"// Protocol: {protocolName}");
            w.Line();

            w.Line("using System;");
            w.Line("using System.Runtime.InteropServices;");
            w.Line();

            hlw.Line("namespace OpenWindow.Backends.Wayland.Managed");
            hlw.OpenBlock();

            w.Line("namespace OpenWindow.Backends.Wayland");
            w.OpenBlock();

            var interfaceElements = protocolElement.Elements(InterfaceElement);
            var ifaces            = interfaceElements.Select(e => new Interface(e)).ToArray();
            var messages          = ifaces.SelectMany(iface => iface.Requests.Concat(iface.Events)).ToArray();
            var ifaceCount        = ifaces.Length;
            var msgCount          = messages.Length;

            _protocolClassName = Util.ToPascalCase(protocolName) + "Bindings";

            w.Line($"internal static unsafe partial class {_protocolClassName}");
            w.OpenBlock();

            w.Line("private static bool _loaded;");
            w.Line();
            w.Line("public static wl_interface* Interfaces;");
            w.Line("private static wl_message* _messages;");
            w.Line("private static wl_interface** _signatureTypes;");
            w.Line();
            w.Line($"private static readonly int InterfaceCount = {ifaceCount};");
            w.Line($"private static readonly int MessageCount = {msgCount};");

            w.Line();

            foreach (var iface in ifaces)
            {
                w.Line($"public const string {iface.RawName}_name = \"{iface.RawName}\";");
            }

            w.Line();

            w.Line("public static void Load()");
            w.OpenBlock();
            w.Line("if (_loaded)");
            w.LineIndented("return;");
            w.Line("_loaded = true;");
            w.Line();

            w.Line("Interfaces = (wl_interface*) Marshal.AllocHGlobal(sizeof(wl_interface) * InterfaceCount);");
            w.Line("_messages = (wl_message*) Marshal.AllocHGlobal(sizeof(wl_message) * MessageCount);");

            w.Line();

            w.Line();

            for (var i = 0; i < ifaces.Length; i++)
            {
                var iface = ifaces[i];
                w.Line($"Util.CreateInterface(&Interfaces[{i}], \"{iface.RawName}\", {iface.Version}, {iface.Requests.Length}, {iface.Events.Length});");
            }

            w.Line();

            var signatureTypes = new List <string>();
            var typeMap        = new List <(string TypeStr, int Index)>();

            var typeMapIndex = 0;

            // handle messages with the most types first so we don't create duplicates
            foreach (var message in messages.OrderByDescending(m => m.SigTypes.Length))
            {
                var typesIndex = 0;
                var typeStr    = ';' + string.Join(";", message.SigTypes) + ';';
                var match      = typeMap.Where(t => t.TypeStr.Contains(typeStr));
                if (match.Any())
                {
                    var m      = match.First();
                    var offset = m.TypeStr.Take(m.TypeStr.IndexOf(typeStr)).Count(c => c == ';');
                    typeMap.Add((typeStr, m.Index + offset));
                    typesIndex = m.Index + offset;
                }
                else
                {
                    typeMap.Add((typeStr, typeMapIndex));
                    typesIndex = typeMapIndex;

                    foreach (var type in message.SigTypes)
                    {
                        var sigTypeStr = type == string.Empty ? null : type;
                        signatureTypes.Add(sigTypeStr);
                        typeMapIndex++;
                    }
                }

                message.SigTypesIndex = typesIndex;
            }

            w.Line($"_signatureTypes = (wl_interface**) Marshal.AllocHGlobal(sizeof(void*) * {signatureTypes.Count});");
            for (int i = 0; i < signatureTypes.Count; i++)
            {
                var stype    = signatureTypes[i];
                var stypeStr = stype == null ? "null" : stype + ".Interface";
                w.Line($"_signatureTypes[{i}] = {stypeStr};");
            }

            w.Line();

            for (var i = 0; i < messages.Length; i++)
            {
                var message = messages[i];
                w.Line($"Util.CreateMessage(&_messages[{i}], \"{message.RawName}\", \"{message.Signature}\", &_signatureTypes[{message.SigTypesIndex}]);");
            }

            w.Line();

            var msgIndex = 0;

            for (var i = 0; i < ifaces.Length; i++)
            {
                var iface   = ifaces[i];
                var reqInit = iface.Requests.Length == 0 ? "null" : $"&_messages[{msgIndex}]";
                w.Line($"Interfaces[{i}].Requests = {reqInit};");
                msgIndex += iface.Requests.Length;

                var evInit = iface.Events.Length == 0 ? "null" : $"&_messages[{msgIndex}]";
                w.Line($"Interfaces[{i}].Events = {evInit};");
                msgIndex += iface.Events.Length;
            }

            w.CloseBlock();
            w.Line();

            w.Line("public static void Unload()");
            w.OpenBlock();
            w.Line("if (!_loaded)");
            w.LineIndented("return;");
            w.Line("_loaded = false;");
            w.Line();

            w.Line("for (var i = 0; i < InterfaceCount; i++)");
            w.LineIndented($"Marshal.FreeHGlobal((IntPtr) Interfaces[i].Name);");

            w.Line();

            w.Line("for (var i = 0; i < MessageCount; i++)");
            w.OpenBlock();
            w.Line("Marshal.FreeHGlobal((IntPtr) _messages[i].Name);");
            w.Line("Marshal.FreeHGlobal((IntPtr) _messages[i].Signature);");
            w.CloseBlock();

            w.Line();
            w.Line("Marshal.FreeHGlobal((IntPtr) _messages);");
            w.Line("Marshal.FreeHGlobal((IntPtr) _signatureTypes);");
            w.Line("Marshal.FreeHGlobal((IntPtr) Interfaces);");
            w.CloseBlock();

            w.Line();

            foreach (var iface in ifaces)
            {
                hlw.Line($"internal unsafe partial struct {iface.ClsName}");
                hlw.OpenBlock();
                hlw.Line($"public static IntPtr Interface => (IntPtr) {iface.RawName}.Interface;");
                hlw.Line($"public static {iface.ClsName} Null => new {iface.ClsName}();");
                hlw.Line($"public readonly {iface.RawName}* Pointer;");
                hlw.Line("public bool IsNull => Pointer == null;");
                if (iface.Events.Length != 0)
                {
                    // cached delegates to prevent GC
                    foreach (var ev in iface.Events)
                    {
                        hlw.Line($"private {_protocolClassName}.{GetDelegateName(iface, ev)} _{ev.RawName};");
                    }
                    // unmanaged listener, needs to be freed by users
                    hlw.Line($"private {_protocolClassName}.{iface.RawName}_listener* _listener;");
                }
                // we need to initialize all fields in the ctor
                var eventInitializer = iface.Events.Length == 0 ? "" :
                                       " _listener = null; " + string.Join(' ', iface.Events.Select(ev => "_" + ev.RawName + " = null;"));
                hlw.Line($"public {iface.ClsName}({iface.RawName}* ptr) {{ Pointer = ptr;{eventInitializer} }}");
                hlw.Line($"public static implicit operator {iface.ClsName}({iface.RawName}* ptr) => new {iface.ClsName}(ptr);");
                hlw.Line($"public static explicit operator {iface.ClsName}(wl_proxy* ptr) => new {iface.ClsName}(({iface.RawName}*) ptr);");

                WriteRequests(iface, w, hlw);
                WriteEvents(iface, w, hlw);

                if (!iface.Requests.Any(m => "destructor".Equals(m.Type)))
                {
                    hlw.Line("public void Destroy() { if (!IsNull) WaylandClient.wl_proxy_destroy((wl_proxy*) Pointer); }");
                }

                hlw.CloseBlock();
            }

            w.CloseBlock(); // class {ProtocolName}Bindings*/

            w.Line();

            // dummy structs for typed pointers
            for (var i = 0; i < ifaces.Length; i++)
            {
                var iface = ifaces[i];
                WriteDescription(iface.Element, w);
                w.Line($"internal struct {iface.RawName} {{ public static unsafe wl_interface* Interface => &{_protocolClassName}.Interfaces[{i}]; }}");
            }

            w.Line();

            foreach (var iface in ifaces)
            {
                foreach (var e in iface.Enums)
                {
                    w.Line();
                    WriteEnum(iface, e, w);
                }
            }

            w.CloseBlock();   // namespace
            hlw.CloseBlock(); // namespace

            w.Line();
            w.Write(hlw);
        }