[gnome-subtitles: 3/3] Fix #153: Add support for Enchant 2



commit bae6f6d853c70ede461440ce8002913ea1472089
Author: Pedro Castro <pedro gnomesubtitles org>
Date:   Sun Mar 29 12:58:33 2020 +0100

    Fix #153: Add support for Enchant 2
    
    The API we use hasn't changed from ver. 1, however the config file was
    pointing specifically to ver. 1. With this change, the config file isn't
    used anymore, instead we dynamically load libenchant according to the
    following order: ver2, ver1, "any version" (libenchant.so, as a fallback
    method if the other two don't exist).

 gnome-subtitles.csproj                             |   4 +
 src/External/Enchant/Enchant.cs                    | 143 +++++++++++++++++++++
 src/External/Interop/Interop.cs                    |  55 ++++++++
 src/GnomeSubtitles/Core/SpellLanguages.cs          |  96 +++++---------
 src/GnomeSubtitles/Dialog/SetLanguagesDialog.cs    |   2 +-
 .../Execution/gnome-subtitles.exe.config           |   2 +-
 src/Makefile.am                                    |   2 +
 7 files changed, 239 insertions(+), 65 deletions(-)
---
diff --git a/gnome-subtitles.csproj b/gnome-subtitles.csproj
index 704fc74..e0cc290 100644
--- a/gnome-subtitles.csproj
+++ b/gnome-subtitles.csproj
@@ -277,12 +277,16 @@
     <Compile Include="src\External\GtkSpell\SpellChecker.cs" />
     <Compile Include="src\GnomeSubtitles\Dialog\Message\MessageDialog.cs" />
     <Compile Include="src\GnomeSubtitles\Ui\WindowState.cs" />
+    <Compile Include="src\External\Enchant\Enchant.cs" />
+    <Compile Include="src\External\Interop\Interop.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>
     <Folder Include="Properties\" />
     <Folder Include="src\SubLib\Util\" />
     <Folder Include="src\External\GtkSpell\" />
+    <Folder Include="src\External\Enchant\" />
+    <Folder Include="src\External\Interop\" />
   </ItemGroup>
   <ItemGroup>
     <None Include="data\gnome-subtitles.svg">
diff --git a/src/External/Enchant/Enchant.cs b/src/External/Enchant/Enchant.cs
new file mode 100644
index 0000000..c5ff3e1
--- /dev/null
+++ b/src/External/Enchant/Enchant.cs
@@ -0,0 +1,143 @@
+/*
+ * This file is part of Gnome Subtitles.
+ * Copyright (C) 2020 Pedro Castro
+ *
+ * Gnome Subtitles is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Gnome Subtitles is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using System;
+using System.Collections;
+using System.Runtime.InteropServices;
+
+namespace External.Enchant {
+
+public class Enchant {
+       private IntPtr lib;
+       private IntPtr broker;
+       private string version;
+       
+       /* Constants */
+       
+       private const string LIBENCHANT_2_SO = "libenchant-2.so.2"; //ver 2
+       private const string LIBENCHANT_1_SO = "libenchant.so.1"; //ver 1
+       private const string LIBENCHANT_SO = "libenchant.so"; //tentative fallback if the other two don't 
exist
+       
+
+       /* Delegates */
+       private delegate void ProviderListHandler (string providerName, string providerDesc, string 
providerFile, IntPtr userdata);
+       private delegate void LanguageListHandler (string langTag, string providerName, string providerDesc, 
string providerFile, IntPtr userdata);
+       
+       private delegate IntPtr EnchantBrokerInitDelegate ();
+       private delegate void EnchantBrokerDescribeDelegate (IntPtr broker, ProviderListHandler cb, IntPtr 
userdata);
+       private delegate void EnchantBrokerListDictsDelegate (IntPtr broker, LanguageListHandler cb, IntPtr 
userdata);
+       private delegate void EnchantBrokerFreeDelegate (IntPtr broker);
+
+       
+       /* Public members */
+       
+       public string Version {
+               get { return version; }
+       }
+       
+       public void Open () {
+               lib = OpenLib();
+               if (lib == IntPtr.Zero) {
+                       throw new Exception("Unable to find libenchant. Tried: '" + LIBENCHANT_2_SO
+                               + "', '" + LIBENCHANT_1_SO + "' and '" + LIBENCHANT_SO + "'.");
+               }
+               
+               broker = BrokerInit(lib);
+               if (broker == IntPtr.Zero) {
+                       throw new Exception("Unable to open enchant broker");
+               }
+       }
+       
+       public string[] GetProviders () {
+               return BrokerDescribe(lib, broker);
+       }
+       
+       public string[] GetLanguages () {
+               return BrokerListDicts(lib, broker);
+       }
+       
+       public int Close () {
+               BrokerFree(lib, broker);
+
+               return Interop.Interop.Close(lib);
+       }
+
+
+       /* Private methods */
+
+       private IntPtr OpenLib () {
+               //Try version 2
+               IntPtr lib = Interop.Interop.Open(LIBENCHANT_2_SO);
+               if (lib != IntPtr.Zero) {
+                       version = "2";
+                       return lib;
+               }
+               
+               //Try version 1
+               lib = Interop.Interop.Open(LIBENCHANT_1_SO);
+               if (lib != IntPtr.Zero) {
+                       version = "1";
+                       return lib;
+               }
+               
+               //Try fallback
+               lib = Interop.Interop.Open(LIBENCHANT_SO);
+               if (lib != IntPtr.Zero) {
+                       version = "unknown";
+                       return lib;
+               }
+               
+               return IntPtr.Zero;
+       }
+       
+       private IntPtr BrokerInit (IntPtr lib) {
+               IntPtr method = Interop.Interop.GetMethod(lib, "enchant_broker_init");
+               EnchantBrokerInitDelegate enchantBrokerInit = Marshal.GetDelegateForFunctionPointer(method, 
typeof(EnchantBrokerInitDelegate)) as EnchantBrokerInitDelegate;
+               return enchantBrokerInit();
+       }
+       
+       private void BrokerFree (IntPtr lib, IntPtr broker) {
+               IntPtr method = Interop.Interop.GetMethod(lib, "enchant_broker_free");
+               EnchantBrokerFreeDelegate enchantBrokerFree = Marshal.GetDelegateForFunctionPointer(method, 
typeof(EnchantBrokerFreeDelegate)) as EnchantBrokerFreeDelegate;
+               enchantBrokerFree(broker);
+       }
+
+       private string[] BrokerDescribe (IntPtr lib, IntPtr broker) {
+               IntPtr method = Interop.Interop.GetMethod(lib, "enchant_broker_describe");
+               EnchantBrokerDescribeDelegate enchantBrokerDescribe = 
Marshal.GetDelegateForFunctionPointer(method, typeof(EnchantBrokerDescribeDelegate)) as 
EnchantBrokerDescribeDelegate;
+               
+               ArrayList providers = new ArrayList();
+               enchantBrokerDescribe(broker, (providerName, providerDesc, providerFile, userdata) => 
providers.Add(providerName), IntPtr.Zero);
+               
+               return (string[])providers.ToArray(typeof(string));
+       }
+       
+       private string[] BrokerListDicts (IntPtr lib, IntPtr broker) {
+               IntPtr method = Interop.Interop.GetMethod(lib, "enchant_broker_list_dicts");
+               EnchantBrokerListDictsDelegate enchantBrokerListDicts = 
Marshal.GetDelegateForFunctionPointer(method, typeof(EnchantBrokerListDictsDelegate)) as 
EnchantBrokerListDictsDelegate;
+               
+               ArrayList languages = new ArrayList();
+               enchantBrokerListDicts (broker, (langTag, providerName, providerDesc, providerFile, userdata) 
=> languages.Add(langTag), IntPtr.Zero);
+
+               return (string[])languages.ToArray(typeof(string));
+       }
+
+}
+
+}
diff --git a/src/External/Interop/Interop.cs b/src/External/Interop/Interop.cs
new file mode 100644
index 0000000..6525800
--- /dev/null
+++ b/src/External/Interop/Interop.cs
@@ -0,0 +1,55 @@
+/*
+ * This file is part of Gnome Subtitles.
+ * Copyright (C) 2020 Pedro Castro
+ *
+ * Gnome Subtitles is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Gnome Subtitles is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace External.Interop {
+
+public class Interop {
+       
+       private const int RTLD_NOW = 2; // dlopen, see 
https://docs.oracle.com/cd/E86824_01/html/E54766/dlopen-3c.html#REFMAN3Adlopen-3c
+
+       /* Function imports */
+       
+       [DllImport("libdl")]
+       private static extern IntPtr dlopen (string filename, int flags);
+       
+       [DllImport("libdl")]
+       private static extern IntPtr dlsym (IntPtr handle, string symbol);
+       
+       [DllImport("libdl")]
+       private static extern int dlclose (IntPtr handl);
+       
+       
+       public static IntPtr Open (string filename) {
+               return dlopen(filename, RTLD_NOW);
+       }
+       
+       public static IntPtr GetMethod (IntPtr lib, string methodName) {
+               return dlsym(lib, methodName);
+       }
+       
+       public static int Close (IntPtr lib) {
+               return dlclose(lib);
+       }
+
+}
+
+}
\ No newline at end of file
diff --git a/src/GnomeSubtitles/Core/SpellLanguages.cs b/src/GnomeSubtitles/Core/SpellLanguages.cs
index 746aae2..b7b2607 100644
--- a/src/GnomeSubtitles/Core/SpellLanguages.cs
+++ b/src/GnomeSubtitles/Core/SpellLanguages.cs
@@ -20,31 +20,21 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
-using System.Runtime.InteropServices;
+using External.Enchant;
 using SubLib.Core.Domain;
 using SubLib.Util;
 
 namespace GnomeSubtitles.Core {
 
-/* Delegates */
-public delegate void LanguageListHandler (string langTag, string providerName, string providerDesc, string 
providerFile, IntPtr userdata);
-
-public delegate void ProviderListHandler (string providerName, string providerDesc, string providerFile, 
IntPtr userdata);
-
 public class SpellLanguages {
        private bool enabled = false;
-       private ArrayList providers = null;
-       private ArrayList languages = null;
+       private string[] providers = null;
+       private SpellLanguage[] languages = null;
        private int activeTextLanguageIndex = -1;
        private int activeTranslationLanguageIndex = -1;
 
-       private LanguageListHandler languageListHandler = null;
-       private ProviderListHandler providerListHandler = null;
 
        public SpellLanguages () {
-               languageListHandler = OnLanguageList;
-               providerListHandler = OnProviderList;
-               
                Init();
                GetEnabledFromConfig();
        }
@@ -57,11 +47,11 @@ public class SpellLanguages {
 
        /* Public members */
 
-       public ArrayList Providers {
+       public string[] Providers {
                get { return providers; }
        }
 
-       public ArrayList Languages {
+       public SpellLanguage[] Languages {
                get { return languages; }
        }
 
@@ -113,7 +103,7 @@ public class SpellLanguages {
        }
 
        public SpellLanguage GetLanguage (int index) {
-               if ((index < 0) || (index >= languages.Count))
+               if ((index < 0) || (index >= languages.Length))
                        return null;
                else
                        return languages[index] as SpellLanguage;
@@ -125,7 +115,7 @@ public class SpellLanguages {
        }
 
        public void SetActiveLanguageIndex (SubtitleTextType textType, int index) {
-               bool isEmpty = ((index < 0) || (index >= languages.Count));
+               bool isEmpty = ((index < 0) || (index >= languages.Length));
 
                SpellLanguage activeLanguage = null;
                if (isEmpty)
@@ -147,53 +137,43 @@ public class SpellLanguages {
                EmitLanguageChanged(textType);
        }
 
-       /* LibEnchant imports */
-
-       [DllImport ("libenchant")]
-       static extern IntPtr enchant_broker_init ();
-
-       [DllImport ("libenchant")]
-       static extern void enchant_broker_free (IntPtr broker);
-
-       [DllImport ("libenchant")]
-       static extern void enchant_broker_list_dicts (IntPtr broker, LanguageListHandler cb, IntPtr userdata);
-
-       [DllImport ("libenchant")]
-       static extern void enchant_broker_describe (IntPtr broker, ProviderListHandler cb, IntPtr userdata);
-
 
        /* Private members */
 
        private void Init () {
-               /* Providers */
-               providers = new ArrayList();
-               
-               /* Languages */
-               languages = new ArrayList();
+               GetProvidersAndLanguages();
+       
                activeTextLanguageIndex = -1;
                activeTranslationLanguageIndex = -1;
-
-               GetProvidersAndLanguages();
+               
                GetActiveLanguagesFromConfig();
        }
 
        private void GetProvidersAndLanguages () {
-               IntPtr broker = enchant_broker_init ();
-               if (broker == IntPtr.Zero)
-                       return;
-
-               enchant_broker_describe (broker, providerListHandler, IntPtr.Zero);
+               Enchant enchant = new Enchant();
 
-               enchant_broker_list_dicts (broker, languageListHandler, IntPtr.Zero);
-
-               enchant_broker_free(broker);
-                
-               languages.Sort();
-               Logger.Info("[Spellcheck] Found {0} providers: {1}", providers.Count, string.Join(",", 
providers.ToArray()));
-               Logger.Info("[Spellcheck] Found {0} languages: {1}", languages.Count, 
GetLanguageIDsAsString(languages));
+               enchant.Open();
+               Logger.Info("[Spellcheck] Found Enchant version: {0}", enchant.Version);
+               
+               providers = enchant.GetProviders();
+               Logger.Info("[Spellcheck] Found {0} providers: {1}", providers.Length, string.Join(",", 
providers));
+               
+               string[] langTags = enchant.GetLanguages();
+               ArrayList langs = new ArrayList();
+               foreach (string langTag in langTags) {
+                       SpellLanguage language = new SpellLanguage(langTag);
+                       if (!langs.Contains(language)) {
+                               langs.Add(language);
+                       }
+               }
+               langs.Sort();
+               languages = (SpellLanguage[])langs.ToArray(typeof(SpellLanguage));
+               Logger.Info("[Spellcheck] Found {0} languages: {1}", languages.Length, 
GetLanguageIDsAsString(languages));
+               
+               enchant.Close();                
        }
 
-       private string GetLanguageIDsAsString (ArrayList languages) {
+       private string GetLanguageIDsAsString (SpellLanguage[] languages) {
                List<string> ids = new List<string>();
                foreach (SpellLanguage language in languages) {
                        ids.Add(language.ID);
@@ -222,7 +202,7 @@ public class SpellLanguages {
        }
 
        private int GetLanguageIndex (string languageID) {
-               for (int index = 0 ; index < languages.Count ; index++) {
+               for (int index = 0 ; index < languages.Length ; index++) {
                        SpellLanguage language = languages[index] as SpellLanguage;
                        if (language.ID == languageID)
                                return index;
@@ -233,16 +213,6 @@ public class SpellLanguages {
 
        /* Event members */
 
-       private void OnLanguageList (string langTag, string providerName, string providerDesc, string 
providerFile, IntPtr userdata) {
-               SpellLanguage language = new SpellLanguage(langTag);
-               if (!languages.Contains(language))
-                       languages.Add(language);
-       }
-       
-       private void OnProviderList (string providerName, string providerDesc, string providerFile, IntPtr 
userdata) {
-               providers.Add(providerName);
-       }
-
        private void EmitToggleEnabled () {
        if (this.ToggleEnabled != null)
                this.ToggleEnabled();
@@ -267,4 +237,4 @@ public class SpellLanguages {
 
 }
 
-}
+}
\ No newline at end of file
diff --git a/src/GnomeSubtitles/Dialog/SetLanguagesDialog.cs b/src/GnomeSubtitles/Dialog/SetLanguagesDialog.cs
index 9a5393c..41d9a5f 100644
--- a/src/GnomeSubtitles/Dialog/SetLanguagesDialog.cs
+++ b/src/GnomeSubtitles/Dialog/SetLanguagesDialog.cs
@@ -80,7 +80,7 @@ public class SetLanguagesDialog : BaseDialog {
                
                
                /* Bottom: info message */
-               string providers = string.Join(", ", Base.SpellLanguages.Providers.ToArray());
+               string providers = string.Join(", ", Base.SpellLanguages.Providers);
                Label bottomLabel = Util.CreateLabel("<i>" + string.Format(Catalog.GetString("Use your 
distro's package manager to install additional languages (tip: search for 'spell'). Supported language packs: 
{0}."), providers) + "</i>", 0, 0);
                bottomLabel.UseMarkup = true;
                bottomLabel.Wrap = true;
diff --git a/src/GnomeSubtitles/Execution/gnome-subtitles.exe.config 
b/src/GnomeSubtitles/Execution/gnome-subtitles.exe.config
index fa663ae..4ec86f7 100644
--- a/src/GnomeSubtitles/Execution/gnome-subtitles.exe.config
+++ b/src/GnomeSubtitles/Execution/gnome-subtitles.exe.config
@@ -1,5 +1,5 @@
 <configuration>
        <dllmap dll="libgtk" target="libgtk-3.so.0" />
-       <dllmap dll="libenchant" target="libenchant.so.1" />
        <dllmap dll="libgtkspell" target="libgtkspell3-3.so.0" />
+       <dllmap dll="libdl" target="libdl.so" />
 </configuration>
diff --git a/src/Makefile.am b/src/Makefile.am
index 5113ca9..fd73c52 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,8 +19,10 @@ libgstreamer_playbin_la_LDFLAGS = -module -avoid-version
 libgstreamer_playbin_la_LIBTOOLFLAGS = --tag=disable-static 
 
 GSSOURCES = \
+       $(srcdir)/External/Enchant/*.cs \
        $(srcdir)/External/GtkSpell/*.cs \
        $(srcdir)/External/GStreamerPlaybin/*.cs \
+       $(srcdir)/External/Interop/*.cs \
        $(srcdir)/External/NCharDet/*.cs \
        $(srcdir)/GnomeSubtitles/Core/*.cs \
        $(srcdir)/GnomeSubtitles/Core/Command/*.cs \


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]