[gnome-shell] extensions-tool: Implement create command



commit 0b1e29e5e36b6021210136765e08a073b2e9652a
Author: Florian Müllner <fmuellner gnome org>
Date:   Mon Aug 27 03:24:10 2018 +0200

    extensions-tool: Implement create command
    
    This implements more functionality of the existing tool and, as
    'reload' is an unreliable feature that doesn't work more often
    than not, the last bit that we will replicate.
    
    The command follows the original for the most part, with the most
    important difference being the installed template, which doesn't
    provide any sample functionality and uses modern JS syntax.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/issues/1234

 src/extensions-tool/command-create.c               | 256 +++++++++++++++++++++
 src/extensions-tool/commands.h                     |   1 +
 src/extensions-tool/common.h                       |   3 +-
 .../gnome-extensions-tool.gresource.xml            |   7 +
 src/extensions-tool/main.c                         |  16 ++
 src/extensions-tool/meson-src.build                |  11 +-
 src/extensions-tool/meson.build                    |  10 +-
 src/extensions-tool/template/extension.js          |  34 +++
 src/extensions-tool/template/stylesheet.css        |   1 +
 9 files changed, 334 insertions(+), 5 deletions(-)
---
diff --git a/src/extensions-tool/command-create.c b/src/extensions-tool/command-create.c
new file mode 100644
index 0000000000..5c43c68b9f
--- /dev/null
+++ b/src/extensions-tool/command-create.c
@@ -0,0 +1,256 @@
+/* command-create.c
+ *
+ * Copyright 2018 Florian Müllner <fmuellner gnome org>
+ *
+ * This program 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gio/gunixinputstream.h>
+
+#include "commands.h"
+#include "common.h"
+
+static char *
+get_shell_version (GError **error)
+{
+  g_autoptr (GDBusProxy) proxy = NULL;
+  g_autoptr (GVariant) variant = NULL;
+  g_auto (GStrv) split_version = NULL;
+
+  proxy = get_shell_proxy (error);
+  if (proxy == NULL)
+    return NULL;
+
+  variant = g_dbus_proxy_get_cached_property (proxy, "ShellVersion");
+  if (variant == NULL)
+    return NULL;
+
+  split_version = g_strsplit (g_variant_get_string (variant, NULL), ".", 3);
+  if (g_ascii_strtoll (split_version[1], NULL, 10) % 2 == 0)
+    g_clear_pointer (&split_version[2], g_free);
+
+  return g_strjoinv (".", split_version);
+}
+
+static gboolean
+create_metadata (GFile       *target_dir,
+                 const char  *uuid,
+                 const char  *name,
+                 const char  *description,
+                 GError     **error)
+{
+  g_autoptr (GFile) target = NULL;
+  g_autoptr (GString) json = NULL;
+  g_autofree char *version = NULL;
+
+  version = get_shell_version (error);
+  if (version == NULL)
+    return FALSE;
+
+  json = g_string_new ("{\n");
+
+  g_string_append_printf (json, "  \"name\": \"%s\",\n", name);
+  g_string_append_printf (json, "  \"description\": \"%s\",\n", description);
+  g_string_append_printf (json, "  \"uuid\": \"%s\",\n", uuid);
+  g_string_append_printf (json, "  \"shell-version\": [\n");
+  g_string_append_printf (json, "    \"%s\"\n", version);
+  g_string_append_printf (json, "  ]\n}\n");
+
+  target = g_file_get_child (target_dir, "metadata.json");
+  return g_file_replace_contents (target,
+                                  json->str,
+                                  json->len,
+                                  NULL,
+                                  FALSE,
+                                  0,
+                                  NULL,
+                                  NULL,
+                                  error);
+}
+
+
+#define TEMPLATE_PATH "/org/gnome/extensions-tool/template"
+static gboolean
+copy_extension_template (GFile *target_dir, GError **error)
+{
+  g_auto (GStrv) templates;
+  char **s;
+
+  templates = g_resources_enumerate_children (TEMPLATE_PATH, 0, NULL);
+  for (s = templates; *s; s++)
+    {
+      g_autoptr (GFile) target = NULL;
+      g_autoptr (GFile) source = NULL;
+      g_autofree char *uri = NULL;
+
+      uri = g_strdup_printf ("resource://%s/%s", TEMPLATE_PATH, *s);
+      source = g_file_new_for_uri (uri);
+      target = g_file_get_child (target_dir, *s);
+
+      if (!g_file_copy (source, target, G_FILE_COPY_TARGET_DEFAULT_PERMS, NULL, NULL, NULL, error))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+launch_extension_source (GFile *dir, GError **error)
+{
+  g_autoptr (GFile) main_source = NULL;
+  g_autoptr (GAppInfo) handler = NULL;
+  GList l;
+
+  main_source = g_file_get_child (dir, "extension.js");
+  handler = g_file_query_default_handler (main_source, NULL, error);
+  if (handler == NULL)
+    return FALSE;
+
+  l.data = main_source;
+  l.next = l.prev = NULL;
+
+  return g_app_info_launch (handler, &l, NULL, error);
+}
+
+static gboolean
+create_extension (const char *uuid, const char *name, const char *description)
+{
+  g_autoptr (GFile) dir = NULL;
+  g_autoptr (GError) error = NULL;
+
+  dir = g_file_new_build_filename (g_get_user_data_dir (),
+                                   "gnome-shell",
+                                   "extensions",
+                                   uuid,
+                                   NULL);
+
+  if (!g_file_make_directory_with_parents (dir, NULL, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      return FALSE;
+    }
+
+  if (!create_metadata (dir, uuid, name, description, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      return FALSE;
+    }
+
+  if (!copy_extension_template (dir, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      return FALSE;
+    }
+
+  if (!launch_extension_source (dir, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+prompt_metadata (char **uuid, char **name, char **description)
+{
+  g_autoptr (GInputStream) stdin = NULL;
+  g_autoptr (GDataInputStream) istream = NULL;
+
+  stdin = g_unix_input_stream_new (0, FALSE);
+  istream = g_data_input_stream_new (stdin);
+
+  if (name != NULL)
+    {
+      char *line;
+
+      g_print (
+        _("Name should be a very short (ideally descriptive) string.\n"
+          "Examples are: %s"),
+        "“Click To Focus”, “Adblock”, “Shell Window Shrinker”\n");
+      g_print ("%s: ", _("Name"));
+
+      line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL);
+      *name = g_strdelimit (line, "\n", '\0');
+    }
+
+  if (description != NULL)
+    {
+      char *line;
+
+      g_print (
+        _("Description is a single-sentence explanation of what your extension does.\n"
+          "Examples are: %s"),
+        "“Make windows visible on click”, “Block advertisement popups”, “Animate windows shrinking on 
minimize”\n");
+      g_print ("%s: ", _("Description"));
+
+      line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL);
+      *description = g_strdelimit (line, "\n", '\0');
+    }
+
+  if (uuid != NULL)
+    {
+      char *line;
+
+      g_print (
+        _("UUID is a globally-unique identifier for your extension.\n"
+          "This should be in the format of an email address (clicktofocus janedoe example com)\n"));
+      g_print ("UUID: ");
+
+      line = g_data_input_stream_read_line_utf8 (istream, NULL, NULL, NULL);
+      *uuid = g_strdelimit (line, "\n", '\0');
+    }
+}
+
+int
+handle_create (int argc, char *argv[], gboolean do_help)
+{
+  g_autoptr (GOptionContext) context = NULL;
+  g_autoptr (GError) error = NULL;
+  g_autofree char *name = NULL;
+  g_autofree char *description = NULL;
+  g_autofree char *uuid = NULL;
+
+  g_set_prgname ("gnome-extensions create");
+
+  context = g_option_context_new (NULL);
+  g_option_context_set_help_enabled (context, FALSE);
+  g_option_context_set_summary (context, _("Create a new extension"));
+
+  if (do_help)
+    {
+      show_help (context, NULL);
+      return 0;
+    }
+
+  if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+      show_help (context, error->message);
+      return 1;
+    }
+
+  if (argc > 1)
+    {
+      show_help (context, _("Unknown arguments"));
+      return 1;
+    }
+
+  prompt_metadata (&uuid, &name, &description);
+
+  return create_extension (uuid, name, description) ? 0 : 2;
+}
diff --git a/src/extensions-tool/commands.h b/src/extensions-tool/commands.h
index c0b0ba18cc..f2b4c712f0 100644
--- a/src/extensions-tool/commands.h
+++ b/src/extensions-tool/commands.h
@@ -26,5 +26,6 @@ G_BEGIN_DECLS
 
 int handle_enable     (int argc, char *argv[], gboolean do_help);
 int handle_disable    (int argc, char *argv[], gboolean do_help);
+int handle_create     (int argc, char *argv[], gboolean do_help);
 
 G_END_DECLS
diff --git a/src/extensions-tool/common.h b/src/extensions-tool/common.h
index c3a243c929..1c8b4cdd24 100644
--- a/src/extensions-tool/common.h
+++ b/src/extensions-tool/common.h
@@ -20,13 +20,14 @@
 
 #pragma once
 
-#include <glib.h>
+#include <gio/gio.h>
 
 G_BEGIN_DECLS
 
 void show_help (GOptionContext *context,
                 const char     *message);
 
+GDBusProxy *get_shell_proxy (GError **error);
 GSettings  *get_shell_settings (void);
 
 G_END_DECLS
diff --git a/src/extensions-tool/gnome-extensions-tool.gresource.xml 
b/src/extensions-tool/gnome-extensions-tool.gresource.xml
new file mode 100644
index 0000000000..2aad7b1ce4
--- /dev/null
+++ b/src/extensions-tool/gnome-extensions-tool.gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/extensions-tool">
+    <file>template/extension.js</file>
+    <file>template/stylesheet.css</file>
+  </gresource>
+</gresources>
diff --git a/src/extensions-tool/main.c b/src/extensions-tool/main.c
index 81c72a3ac0..19c331112b 100644
--- a/src/extensions-tool/main.c
+++ b/src/extensions-tool/main.c
@@ -38,6 +38,19 @@ show_help (GOptionContext *context, const char *message)
   g_printerr ("%s", help);
 }
 
+GDBusProxy *
+get_shell_proxy (GError **error)
+{
+  return g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+                                        G_DBUS_PROXY_FLAGS_NONE,
+                                        NULL,
+                                        "org.gnome.Shell",
+                                        "/org/gnome/Shell",
+                                        "org.gnome.Shell.Extensions",
+                                        NULL,
+                                        error);
+}
+
 GSettings *
 get_shell_settings (void)
 {
@@ -87,6 +100,7 @@ usage (void)
   g_printerr ("  version   %s\n", _("Print version"));
   g_printerr ("  enable    %s\n", _("Enable extension"));
   g_printerr ("  disable   %s\n", _("Disable extension"));
+  g_printerr ("  create    %s\n", _("Create extension"));
   g_printerr ("\n");
   g_printerr (_("Use %s to get detailed help.\n"), "“gnome-extensions help COMMAND”");
 }
@@ -144,6 +158,8 @@ main (int argc, char *argv[])
     return handle_enable (argc, argv, do_help);
   else if (g_str_equal (command, "disable"))
     return handle_disable (argc, argv, do_help);
+  else if (g_str_equal (command, "create"))
+    return handle_create (argc, argv, do_help);
   else
     usage ();
 
diff --git a/src/extensions-tool/meson-src.build b/src/extensions-tool/meson-src.build
index fb3ab04d17..d11a75ba2d 100644
--- a/src/extensions-tool/meson-src.build
+++ b/src/extensions-tool/meson-src.build
@@ -1,12 +1,19 @@
 sources = [
   'commands.h',
+  'command-create.c',
   'command-disable.c',
   'command-enable.c',
   'common.h',
   'main.c'
 ]
+
+resources = gnome.compile_resources('resources',
+  'gnome-extensions-tool.gresource.xml',
+  source_dir: '.'
+)
+
 executable('gnome-extensions',
-  sources,
-  dependencies: gio_dep,
+  sources, resources,
+  dependencies: [gio_dep, gio_unix_dep],
   install: true
 )
diff --git a/src/extensions-tool/meson.build b/src/extensions-tool/meson.build
index 2e03539c4b..35d5245b5e 100644
--- a/src/extensions-tool/meson.build
+++ b/src/extensions-tool/meson.build
@@ -9,13 +9,19 @@ configure_file(
 )
 
 sources = [
+  'command-create.c',
   'command-disable.c',
   'command-enable.c',
   'main.c'
 ]
 
+resources = gnome.compile_resources('resources',
+  'gnome-extensions-tool.gresource.xml',
+  source_dir: '.'
+)
+
 executable('gnome-extensions',
-  sources,
-  dependencies: gio_dep,
+  sources, resources,
+  dependencies: [gio_dep, gio_unix_dep],
   install: true
 )
diff --git a/src/extensions-tool/template/extension.js b/src/extensions-tool/template/extension.js
new file mode 100644
index 0000000000..64857afb7e
--- /dev/null
+++ b/src/extensions-tool/template/extension.js
@@ -0,0 +1,34 @@
+/* extension.js
+ *
+ * This program 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.
+ *
+ * This program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* exported init */
+
+class Extension {
+    constructor() {
+    }
+
+    enable() {
+    }
+
+    disable() {
+    }
+}
+
+function init() {
+    return new Extension();
+}
diff --git a/src/extensions-tool/template/stylesheet.css b/src/extensions-tool/template/stylesheet.css
new file mode 100644
index 0000000000..37b93f2191
--- /dev/null
+++ b/src/extensions-tool/template/stylesheet.css
@@ -0,0 +1 @@
+/* Add your custom extension styling here */


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