1/*
2   This file is part of GNOME 2048.
3
4   Copyright (C) 2014-2015 Juan R. García Blanco <juanrgar@gmail.com>
5   Copyright (C) 2016-2019 Arnaud Bonatti <arnaud.bonatti@gmail.com>
6
7   GNOME 2048 is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   GNOME 2048 is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with GNOME 2048.  If not, see <https://www.gnu.org/licenses/>.
19*/
20
21using Gtk;
22
23private class TwentyFortyEight : Gtk.Application
24{
25    private GameWindow _window;
26
27    private static bool show_version;
28    private static string? size = null;
29    private static string? cli = null;
30    private uint8 cols = 0;
31    private uint8 rows = 0;
32
33    private const OptionEntry [] option_entries =
34    {
35        /* Translators: command-line option description, see 'gnome-2048 --help' */
36        { "cli", 0,         OptionFlags.OPTIONAL_ARG, OptionArg.CALLBACK, (void*) _cli, N_("Play in the terminal (see “--cli=help”)"),
37
38        /* Translators: in the command-line options description, text to indicate the user should give a command after '--cli' for playing in the terminal, see 'gnome-2048 --help' */
39                                                                                        N_("COMMAND") },
40
41        /* Translators: command-line option description, see 'gnome-2048 --help' */
42        { "size", 's',      OptionFlags.NONE, OptionArg.STRING, ref size,               N_("Start new game of given size"),
43
44        /* Translators: in the command-line options description, text to indicate the user should specify a size after '--size', see 'gnome-2048 --help' */
45                                                                                        N_("SIZE") },
46
47        /* Translators: command-line option description, see 'gnome-2048 --help' */
48        { "version", 'v',   OptionFlags.NONE, OptionArg.NONE,   ref show_version,       N_("Print release version and exit"), null },
49        {}
50    };
51
52    private bool _cli (string? option_name, string? val)
53    {
54        cli = option_name == null ? "" : (!) option_name;  // TODO report bug: should probably be val...
55        return true;
56    }
57
58    private const GLib.ActionEntry [] action_entries =
59    {
60        { "quit", quit_cb }
61    };
62
63    private static int main (string [] args)
64    {
65        Intl.setlocale (LocaleCategory.ALL, "");
66        Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
67        Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
68        Intl.textdomain (GETTEXT_PACKAGE);
69
70        OptionContext context = new OptionContext ("");
71        context.add_main_entries (option_entries, GETTEXT_PACKAGE);
72
73        context.add_group (get_option_group (true));
74        context.add_group (Clutter.get_option_group_without_init ());
75
76        try {
77            context.parse (ref args);
78        } catch (Error e) {
79            stderr.printf ("%s\n", e.message);
80            return Posix.EXIT_FAILURE;
81        }
82
83        const string application_name = "org.gnome.TwentyFortyEight";
84        Environment.set_application_name (application_name);
85        Environment.set_prgname ("org.gnome.TwentyFortyEight");
86        Window.set_default_icon_name ("org.gnome.TwentyFortyEight");
87
88        try {
89            GtkClutter.init_with_args (ref args, "", new OptionEntry[0], null);
90        } catch (Error e) {
91            MessageDialog dialog = new MessageDialog (null,
92                                                      DialogFlags.MODAL,
93                                                      MessageType.ERROR,
94                                                      ButtonsType.NONE,
95                                                      "Unable to initialize Clutter:\n%s", e.message);
96            dialog.set_title (application_name);
97            dialog.run ();
98            dialog.destroy ();
99            return Posix.EXIT_FAILURE;
100        }
101
102        TwentyFortyEight app = new TwentyFortyEight ();
103        return app.run (args);
104    }
105
106    private TwentyFortyEight ()
107    {
108        Object (application_id: "org.gnome.TwentyFortyEight", flags: ApplicationFlags.FLAGS_NONE);
109    }
110
111    protected override int handle_local_options (GLib.VariantDict options)  // options will be empty, we used a custom OptionContext
112    {
113        if (show_version)
114        {
115            /* NOTE: Is not translated so can be easily parsed */
116            stdout.printf ("%1$s %2$s\n", "gnome-2048", VERSION);
117            return Posix.EXIT_SUCCESS;
118        }
119
120        if (size != null && !CLI.parse_size ((!) size, out cols, out rows))
121        {
122            /* Translators: command-line error message, displayed for an incorrect game size request; try 'gnome-2048 -s 0' */
123            stderr.printf ("%s\n", _("Failed to parse size. Size must be between 2 and 9, or in the form 2x3."));
124            return Posix.EXIT_FAILURE;
125        }
126
127        if (cli != null)
128        {
129            if ((!) cli == "help" || (!) cli == "HELP")
130            {
131                string help_string = ""
132                    + "\n" + "To play GNOME 2048 in command-line:"
133                    + "\n" + "  --cli         " + "Display current game. Alias: “status” or “show”."
134                    + "\n" + "  --cli new     " + "Start a new game; for changing size, use --size."
135                    + "\n"
136                    + "\n" + "  --cli up      " + "Move tiles up.    Alias: “u”."
137                    + "\n" + "  --cli down    " + "Move tiles down.  Alias: “d”."
138                    + "\n" + "  --cli left    " + "Move tiles left.  Alias: “l”."
139                    + "\n" + "  --cli right   " + "Move tiles right. Alias: “r”."
140                    + "\n\n";
141                stdout.printf (help_string);
142                return Posix.EXIT_SUCCESS;
143            }
144            return CLI.play_cli ((!) cli, "org.gnome.TwentyFortyEight", ref cols, ref rows);
145        }
146
147        /* Activate */
148        return -1;
149    }
150
151    protected override void startup ()
152    {
153        base.startup ();
154
155        add_action_entries (action_entries, this);
156
157        _window = new GameWindow (this, cols, rows);
158
159        set_accels_for_action ("ui.toggle-new-game",    {        "<Primary>n"       });
160        set_accels_for_action ("ui.new-game",           { "<Shift><Primary>n"       });
161        set_accels_for_action ("app.quit",              {        "<Primary>q"       });
162        set_accels_for_action ("ui.undo",               {        "<Primary>z"       });
163        set_accels_for_action ("ui.about",              {          "<Shift>F1",
164                                                          "<Shift><Primary>F1"      }); // as usual, this second shortcut does not work
165        set_accels_for_action ("win.show-help-overlay", {                 "F1",
166                                                                 "<Primary>F1",
167                                                                 "<Primary>question",
168                                                          "<Shift><Primary>question"});
169        set_accels_for_action ("ui.toggle-hamburger",   {                 "F10",
170                                                                          "Menu"    });
171    }
172
173    protected override void activate ()
174    {
175        _window.present ();
176    }
177
178    private void quit_cb (/* SimpleAction action, Variant? variant */)
179    {
180        _window.destroy ();
181    }
182}
183