From 0b1e29e5e36b6021210136765e08a073b2e9652a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Mon, 27 Aug 2018 03:24:10 +0200 Subject: [PATCH] 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(-) create mode 100644 src/extensions-tool/command-create.c create mode 100644 src/extensions-tool/gnome-extensions-tool.gresource.xml create mode 100644 src/extensions-tool/template/extension.js create mode 100644 src/extensions-tool/template/stylesheet.css diff --git a/src/extensions-tool/command-create.c b/src/extensions-tool/command-create.c new file mode 100644 index 000000000..5c43c68b9 --- /dev/null +++ b/src/extensions-tool/command-create.c @@ -0,0 +1,256 @@ +/* command-create.c + * + * Copyright 2018 Florian Müllner + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include + +#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 c0b0ba18c..f2b4c712f 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 c3a243c92..1c8b4cdd2 100644 --- a/src/extensions-tool/common.h +++ b/src/extensions-tool/common.h @@ -20,13 +20,14 @@ #pragma once -#include +#include 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 000000000..2aad7b1ce --- /dev/null +++ b/src/extensions-tool/gnome-extensions-tool.gresource.xml @@ -0,0 +1,7 @@ + + + + template/extension.js + template/stylesheet.css + + diff --git a/src/extensions-tool/main.c b/src/extensions-tool/main.c index 81c72a3ac..19c331112 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 fb3ab04d1..d11a75ba2 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 2e03539c4..35d5245b5 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 000000000..64857afb7 --- /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 . + * + * 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 000000000..37b93f219 --- /dev/null +++ b/src/extensions-tool/template/stylesheet.css @@ -0,0 +1 @@ +/* Add your custom extension styling here */