gir.core icon indicating copy to clipboard operation
gir.core copied to clipboard

GirCore.Notify libnotify and Sample

Open czirok opened this issue 7 months ago • 9 comments

Hello

Sample https://github.com/czirok/gir.core.samples/tree/main/NotifyAdw Temporarily, the full Notify bindings are included under the Libs folder.

Gnome flatpak SDK

Notify-0.7.gir included.

Support all platform

https://formulae.brew.sh/formula/libnotify#default https://packages.msys2.org/base/mingw-w64-libnotify

Problem

Namespace Notify conflict with GObject.Notify.

What is the rule for namespace collision?

Currently, I have added a dotless version in the example. "Notify07"

Fixer

src/Generation/Generator/Fixer/Namespace.cs (Modified)

namespace Generator.Fixer;

public static class Namespace
{
    private static readonly List<Fixer<GirModel.Namespace>> Fixers = new()
    {
        new DisableRecordsConflictingWithGObjectsFixer(),
        new NamespaceNameConflictFixer(),
    };

    public static void Fixup(GirModel.Namespace ns)
    {
        foreach (var fixer in Fixers)
            fixer.Fixup(ns);
    }
}

src/Generation/Generator/Fixer/Namespace/NamespaceNameConflictFixer.cs (New)

namespace Generator.Fixer.Ns;

internal class NamespaceNameConflictFixer : Fixer<GirModel.Namespace>
{
    public void Fixup(GirModel.Namespace ns)
    {
        IEnumerable<string> classMethodNames = ns.Classes
            .SelectMany(AllMethods)
            .Select(Method.GetPublicName);

        if (classMethodNames.Contains(ns.Name))
        {
            var name = Model.Namespace.GetPublicName(ns);
            var fixedName = $"{name}{ns.Version.DotlessVersion()}";
            Model.Namespace.SetPublicName(ns, fixedName);
            Log.Information($"Namespace '{ns.Name}' renamed to '{Model.Namespace.GetPublicName(ns)}' due to conflict with method name in the same namespace.");
        }
    }

    public static IEnumerable<GirModel.Method> AllMethods(GirModel.Class cls)
    {
        var methods = new HashSet<GirModel.Method>();

        foreach (var method in cls.Methods)
            methods.Add(method);

        if (cls.Parent is not null)
            foreach (var method in AllMethods(cls.Parent))
                methods.Add(method);

        foreach (var @interface in cls.Implements)
            foreach (var method in Interface.AllMethods(@interface))
                methods.Add(method);

        return methods;
    }
}

public static class VersionExtensions
{
    public static string DotlessVersion(this string version)
        => version.Replace(".", "");
}

src/Generation/Generator/Model/Namespace.cs (Modified)

namespace Generator.Model;

internal static class Namespace
{
    private static readonly Dictionary<string, string> FixedNames = new();

    public static void SetPublicName(GirModel.Namespace @namespace, string name)
    {
        lock (FixedNames)
        {
            FixedNames[GetCanonicalName(@namespace)] = name;
        }
    }

    public static string GetPublicName(GirModel.Namespace @namespace)
    {
        return FixedNames.TryGetValue(GetCanonicalName(@namespace), out var value)
            ? value
            : @namespace.Name.ToPascalCase().EscapeIdentifier();
    }

    public static string GetCanonicalName(GirModel.Namespace @namespace)
        => $"{@namespace.Name}-{@namespace.Version}";

    public static string GetInternalName(GirModel.Namespace @namespace)
        => $"{GetPublicName(@namespace)}.Internal";
}

src/Generation/Generator/Renderer/Public/Constructor/Converter/Class.cs (Fix)

        return cls.Fundamental
            ? $"new {cls.Name}({fromVariableName})"
-            : $"{cls.Namespace.Name}.{cls.Name}.CreateIntern({fromVariableName}, {ownedRef.ToString().ToLower()})";
+           : $"{Model.Namespace.GetPublicName(cls.Namespace)}.{cls.Name}.CreateIntern({fromVariableName}, {ownedRef.ToString().ToLower()})";

czirok avatar May 13 '25 19:05 czirok

Namespace Notify conflict with GObject.Notify.

Can you give concrete symbols / class names that do conflict? Currently I can't find a potential conflict? Afaik there is no class Notify?

badcel avatar May 13 '25 20:05 badcel

Yes, normal, (without NamespaceNameConflictFixer) the generator in Notify project generate like this:


using System;
using System.Linq;
using GObject;
using System.Runtime.InteropServices;

#nullable enable

namespace Notify;

// AUTOGENERATED FILE - DO NOT MODIFY

public partial class Notification
{
    

public static Notification New(string summary, string? body, string? icon)
{
    using var summaryNative = GLib.Internal.NonNullableUtf8StringOwnedHandle.Create(summary);
using var bodyNative = GLib.Internal.NullableUtf8StringOwnedHandle.Create(body);
using var iconNative = GLib.Internal.NullableUtf8StringOwnedHandle.Create(icon);
    var notificationHandle = Internal.Notification.New(summaryNative, bodyNative, iconNative);
return Notify.Notification.CreateIntern(notificationHandle, true);
}
}

Namespace "Notify", and the GObject.Notify is a method in Object.Methods.Generated.cs L147:

public void Notify(string propertyName)
{
    using var propertyNameNative = GLib.Internal.NonNullableUtf8StringOwnedHandle.Create(propertyName);
    GObject.Internal.Object.Notify(this.Handle.DangerousGetHandle(), propertyNameNative);
}

And this is the problem.

Image Image

czirok avatar May 13 '25 21:05 czirok

According to the docs for cs0119 we should be able to fix this issue without renaming but by fully qualifying the call.

Perhaps it must be global::Notify.Notification...

You can try this manually. If it works the generator must be updated to generate the fully qualified path to the CreateIntern method.

badcel avatar May 14 '25 04:05 badcel

Perhaps it must be global::Notify.Notification... You can try this manually. If it works the generator must be updated to generate the fully qualified path to the CreateIntern method.

This is not enough unfortunately.

First I tested "global::". But in several files

  • *.Functions.Generated.cs
  • *.Methods.Generated.cs
  • *.Properties.Generated.cs ... generate Notify.Internal.AnythingHere like:
public Notification(params GObject.ConstructArgument[] constructArguments) : this(global::Notify.Internal.NotificationHandle.For<Notification>(constructArguments)) { }

or

global::Notify.Internal.Notification.SetAppName(base.Handle.DangerousGetHandle(), appNameNative);

For this I modified in the gir.core/src/Generation/Generator/Model/Namespace.cs file:

public static string GetInternalName(GirModel.Namespace @namespace)
=> $"global::{GetPublicName(@namespace)}.Internal";

But this, in 55 files, 110 references. This will mess up all Internal namespaces:

namespace global::GLib.Internal

czirok avatar May 14 '25 06:05 czirok

Would you create a PR? Then I can take a more detailed look at it.

Please try to keep the changes at a minimum so it is easy and fast to review.

badcel avatar May 14 '25 16:05 badcel

@badcel Which version should I create a PR from?

czirok avatar May 16 '25 18:05 czirok

You can simply create the PR to add libnotify to gir.core. Then the error should appear, right? Afterwards I can check in detail what to do with the issue and if it is fixed we can merge the PR.

On another note: Do you plan to add librsvg to gir.core like you did for libsecret?

badcel avatar May 16 '25 19:05 badcel

You can simply create the PR to add libnotify to gir.core. Then the error should appear, right? Afterwards I can check in detail what to do with the issue and if it is fixed we can merge the PR.

OK.

On another note: Do you plan to add librsvg to gir.core like you did for libsecret?

Yes, yes, yes, sorry, I'm just a little busy. I'll have time for it on Tuesday. Sorry.

czirok avatar May 16 '25 19:05 czirok

Please, please no pressure and please don't feel pushed by me. I just wanted to confirm you are still interested.

I'm currently pretty occupied, too. This is still a hobby. Additionally there is still some work left before the initial 0.7.0-preview.1 release.

If the code does not make it, it will be in the next release. Perhaps I could even make a separate release for you if you miss the release of the upcoming preview. It is really no big issue for me.

Regarding the sample repository: If the new nugets are published I think I can create a sample repo in the GirCore org and give you push permission.

But like I said, really don't feel pushed. I don't want anyone to feel uncomfortable because of some open source contribution.

badcel avatar May 16 '25 19:05 badcel

You can simply create the PR to add libnotify to gir.core. Then the error should appear, right? Afterwards I can check in detail what to do with the issue and if it is fixed we can merge the PR.

On another note: Do you plan to add librsvg to gir.core like you did for libsecret?

I uploaded the libnotify diff here:

https://github.com/czirok/gir.core/commit/d047c93ee2f173ea3fc19794fae11d3d68ca1076

If you have any questions or requests, just let me know.

czirok avatar Jul 14 '25 12:07 czirok

I will add Notify-0.7.gir and try to see what I can do to fix the issue beforehand. Afterwards you can create the PR to add the nuget to GirCore.

badcel avatar Jul 16 '25 20:07 badcel

Do you think it makes sense to add libnotify? The homepage states:

For GLib based applications the GNotification API should be used instead.

As all GirCore based apps are using GLib I think we do not really need this package at all? I just saw that the windows version of libnotify is still on version 0.7.x instead of 0.8.x which looks pretty unmaintained as version 0.8 was released 3 years ago.

badcel avatar Jul 16 '25 20:07 badcel

This?

https://github.com/gircore/gir.core/blob/main/src/Samples/Gio-2.0/DBus/SendNotification.cs

czirok avatar Jul 16 '25 20:07 czirok

No, this is a DBus sample on how to invoke notifications via DBus. I needed a simple sample to demonstrate DBus and choose the notification endpoint. DBus is a framework for inter process communication. You can control a lot of services with it. You "just" need to find the appropriate docs.

Please read the Notification docs. There it says to create a Notification (GirCore Notification.New) and pass this Notification on to Application.SendNotification (GirCore Application.SendNotification).

Internally this will call the DBus APIs automatically for you. If you want to experiment with this API a bit: A simple sample in the kind of the DBus one would be very welcome.

badcel avatar Jul 16 '25 20:07 badcel