1 /* Atomix -- a little puzzle game about atoms and molecules.
2  * Copyright (C) 1999-2001 Jens Finke
3  * Copyright (C) 2005 Guilherme de S. Pastore
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include "config.h"
21 
22 #include "board-gtk.h"
23 #include "playfield.h"
24 #include "main.h"
25 #include "goal.h"
26 #include "level.h"
27 #include "goal-view.h"
28 #include "clock.h"
29 #include "undo.h"
30 #include <libgnome-games-support.h>
31 
32 AtomixApp *app;
33 
34 typedef enum
35   {
36     GAME_ACTION_NEW,
37     GAME_ACTION_END,
38     GAME_ACTION_PAUSE,
39     GAME_ACTION_CONTINUE,
40     GAME_ACTION_SKIP,
41     GAME_ACTION_UNDO,
42     GAME_ACTION_FINISHED,
43     GAME_ACTION_RESTART,
44   } GameAction;
45 
46 static void controller_handle_action (GameAction action);
47 static void set_game_not_running_state (void);
48 static gboolean set_next_level (void);
49 static void setup_level (void);
50 static void level_cleanup_view (void);
51 static void atomix_exit (void);
52 static void game_init (void);
53 static void update_statistics (void);
54 static void view_congratulations (void);
55 static void calculate_score (void);
56 
57 /* ===============================================================
58 
59              Menu callback  functions
60 
61 -------------------------------------------------------------- */
verb_GameNew_cb(GSimpleAction * action,GVariant * variant,gpointer data)62 static void verb_GameNew_cb (GSimpleAction *action, GVariant *variant, gpointer data)
63 {
64   controller_handle_action (GAME_ACTION_NEW);
65 }
66 
verb_GameEnd_cb(GSimpleAction * action,GVariant * variant,gpointer data)67 static void verb_GameEnd_cb (GSimpleAction *action, GVariant *variant, gpointer data)
68 {
69   controller_handle_action (GAME_ACTION_END);
70 }
71 
verb_GameSkip_cb(GSimpleAction * action,GVariant * variant,gpointer data)72 static void verb_GameSkip_cb (GSimpleAction *action, GVariant *variant, gpointer data)
73 {
74   controller_handle_action (GAME_ACTION_SKIP);
75 }
76 
verb_GameReset_cb(GSimpleAction * action,GVariant * variant,gpointer data)77 static void verb_GameReset_cb (GSimpleAction *action, GVariant *variant, gpointer data)
78 {
79   controller_handle_action (GAME_ACTION_RESTART);
80 }
81 
verb_GamePause_cb(GSimpleAction * action,GVariant * variant,gpointer data)82 static void verb_GamePause_cb (GSimpleAction *action, GVariant *variant, gpointer data)
83 {
84   if (app->state != GAME_STATE_PAUSED) {
85     controller_handle_action (GAME_ACTION_PAUSE);
86   } else {
87     controller_handle_action (GAME_ACTION_CONTINUE);
88   }
89 }
90 
verb_GameUndo_cb(GSimpleAction * action,GVariant * variant,gpointer data)91 static void verb_GameUndo_cb (GSimpleAction *action, GVariant *variant, gpointer data)
92 {
93   controller_handle_action (GAME_ACTION_UNDO);
94 }
95 
verb_GameExit_cb(GSimpleAction * action,GVariant * variant,gpointer data)96 static void verb_GameExit_cb (GSimpleAction *action, GVariant *variant, gpointer data)
97 {
98   atomix_exit ();
99 }
100 
verb_HelpAbout_cb(GSimpleAction * action,GVariant * variant,gpointer data)101 static void verb_HelpAbout_cb (GSimpleAction *action, GVariant *variant, gpointer data)
102 {
103   const char *authors[] =
104     {
105       "Guilherme de S. Pastore <gpastore@gnome.org>",
106       "Jens Finke <jens@triq.net>",
107       "Robert Roth <robert.roth.off@gmail.com>",
108       NULL
109     };
110 
111   const char *artists[] =
112     {
113       "Jakub Steiner <jimmac@ximian.com>",
114       NULL
115     };
116 
117 
118   gtk_show_about_dialog(GTK_WINDOW(app->mainwin),
119                         "program-name", _("Atomix"),
120                         "logo-icon-name", "atomix",
121                         "version", VERSION,
122                         "comments", _("A puzzle game about atoms and molecules"),
123                         "website", "https://wiki.gnome.org/Apps/Atomix",
124                         "authors", authors,
125                         "artists", artists,
126                         "translator_credits", _("translator-credits"),
127                         NULL);
128 }
129 
on_app_destroy_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)130 static gboolean on_app_destroy_event (GtkWidget *widget, GdkEvent *event,
131 				      gpointer user_data)
132 {
133   atomix_exit ();
134 
135   return TRUE;
136 }
137 
138 /* ===============================================================
139 
140              Game steering functions
141 
142 -------------------------------------------------------------- */
143 
controller_handle_action(GameAction action)144 static void controller_handle_action (GameAction action)
145 {
146   switch (app->state)
147     {
148     case GAME_STATE_NOT_RUNNING:
149       if (action == GAME_ACTION_NEW && set_next_level ())
150 	      {
151 	        app->level_no = 1;
152 	        app->score = 0;
153 	        setup_level ();
154 	        app->state = GAME_STATE_RUNNING_UNMOVED;
155 	      }
156       break;
157 
158     case GAME_STATE_RUNNING_UNMOVED:
159     case GAME_STATE_RUNNING:
160       switch (action)
161 	    {
162 	      case GAME_ACTION_END:
163 	        level_cleanup_view ();
164 	        set_game_not_running_state ();
165 	        break;
166 
167 	      case GAME_ACTION_PAUSE:
168 	        clock_stop (CLOCK (app->clock));
169 	        board_gtk_hide ();
170 	        app->state = GAME_STATE_PAUSED;
171 	        break;
172 
173 	      case GAME_ACTION_SKIP:
174 	        level_cleanup_view ();
175 
176 	        if (set_next_level ())
177 	          setup_level ();
178 	        else
179 	          set_game_not_running_state ();
180 
181 	        break;
182 
183 	      case GAME_ACTION_FINISHED:
184 	        calculate_score ();
185 	        if (level_manager_is_last_level (app->lm, app->level))
186 	          {
187 	            view_congratulations ();
188 	            level_cleanup_view ();
189 	            set_game_not_running_state ();
190 	          }
191 	        else
192 	          {
193 	            level_cleanup_view ();
194 	            set_next_level ();
195 	            setup_level ();
196 	          }
197 	        break;
198 
199 	      case GAME_ACTION_RESTART:
200 	        g_assert (app->state != GAME_STATE_RUNNING_UNMOVED);
201 
202 	        level_cleanup_view ();
203 	        setup_level ();
204 	        break;
205 
206 	      case GAME_ACTION_UNDO:
207 	        g_assert (app->state != GAME_STATE_RUNNING_UNMOVED);
208 
209 	        board_gtk_undo_move ();
210 	        break;
211 
212 	      case GAME_ACTION_NEW:
213 	      case GAME_ACTION_CONTINUE:
214 	      default:
215 	        break;
216 	    }
217       break;
218 
219     case GAME_STATE_PAUSED:
220       if (action == GAME_ACTION_CONTINUE)
221 	      {
222 	        clock_resume (CLOCK(app->clock));
223 	        board_gtk_show ();
224 	        app->state = (undo_exists())?GAME_STATE_RUNNING:GAME_STATE_RUNNING_UNMOVED;
225 	      }
226       break;
227     default:
228       g_assert_not_reached ();
229   }
230 
231   update_menu_item_state ();
232   update_statistics ();
233   gtk_widget_grab_focus (GTK_WIDGET (app->fi_matrix));
234 }
235 
236 static void
add_score_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)237 add_score_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
238 {
239   GamesScoresContext *context = GAMES_SCORES_CONTEXT (source_object);
240   GError *error = NULL;
241 
242   games_scores_context_add_score_finish (context, res, &error);
243   if (error != NULL) {
244     g_warning ("Failed to add score: %s", error->message);
245     g_error_free (error);
246   }
247 }
248 
249 
set_game_not_running_state(void)250 static void set_game_not_running_state (void)
251 {
252   board_gtk_show_logo (TRUE);
253 
254   if (app->level)
255     g_object_unref (app->level);
256 
257   if (app->goal)
258     g_object_unref (app->goal);
259 
260   if (app->score > 0) {
261     games_scores_context_add_score (app->high_scores,
262                                     app->score,
263                                     app->current_category,
264                                     NULL,
265                                     add_score_cb,
266                                     NULL);
267   }
268 
269   app->level = NULL;
270   app->goal = NULL;
271   app->score = 0;
272   app->state = GAME_STATE_NOT_RUNNING;
273 }
274 
set_next_level(void)275 static gboolean set_next_level (void)
276 {
277   Level *next_level;
278   PlayField *goal_pf;
279 
280   next_level = level_manager_get_next_level (app->lm, app->level);
281 
282   if (app->level)
283     {
284       g_object_unref (app->level);
285     }
286   app->level = NULL;
287 
288   if (app->goal)
289     {
290       g_object_unref (app->goal);
291     }
292   app->goal = NULL;
293 
294   if (next_level == NULL)
295     {
296       return FALSE;
297     }
298 
299   app->level = next_level;
300   app->level_no++;
301   goal_pf = level_get_goal (app->level);
302   app->goal = goal_new (goal_pf);
303 
304   g_object_unref (goal_pf);
305 
306   return TRUE;
307 }
308 
setup_level(void)309 static void setup_level (void)
310 {
311   PlayField *env_pf;
312   PlayField *sce_pf;
313 
314   g_return_if_fail (app != NULL);
315 
316   if (app->level == NULL)
317     return;
318 
319   g_return_if_fail (app->goal != NULL);
320 
321   /* init board */
322   env_pf = level_get_environment (app->level);
323   sce_pf = level_get_scenario (app->level);
324   board_gtk_show_logo (FALSE);
325   board_gtk_init_level (env_pf, sce_pf, app->goal);
326 
327   /* init goal */
328   goal_view_render (app->goal);
329 
330   /* init clock */
331   clock_reset (CLOCK(app->clock));
332   clock_start (CLOCK(app->clock));
333 
334   g_object_unref (env_pf);
335   g_object_unref (sce_pf);
336 }
337 
level_cleanup_view(void)338 static void level_cleanup_view (void)
339 {
340   board_gtk_clear ();
341   goal_view_clear ();
342 
343   clock_stop (CLOCK(app->clock));
344 }
345 
atomix_exit(void)346 static void atomix_exit (void)
347 {
348 #ifdef DEBUG
349   g_message ("Destroy application");
350 #endif
351 
352   g_return_if_fail (app != NULL);
353 
354   if (app->state != GAME_STATE_NOT_RUNNING)
355     set_game_not_running_state ();
356 
357   board_gtk_destroy ();
358 
359   if (app->level)
360     g_object_unref (app->level);
361 
362   if (app->lm)
363     g_object_unref (app->lm);
364 
365   if (app->theme)
366     g_object_unref (app->theme);
367 
368   if (app->tm)
369     g_object_unref (app->tm);
370 
371   /* quit application */
372   gtk_widget_destroy (app->mainwin);
373 
374 }
375 
game_init(void)376 static void game_init (void)
377 {
378   g_return_if_fail (app != NULL);
379 
380   /* init theme manager */
381   app->tm = theme_manager_new ();
382   theme_manager_init_themes (app->tm);
383   app->theme = theme_manager_get_theme (app->tm, "default");
384   g_assert (app->theme != NULL);
385 
386   /* init level manager */
387   app->lm = level_manager_new ();
388   level_manager_init_levels (app->lm);
389 
390   /* init level statistics */
391   app->level = NULL;
392   app->level_no = 0;
393   app->score = 0;
394   clock_set_format (CLOCK(app->clock), "%M:%S");
395   clock_reset (CLOCK(app->clock));
396 
397   /* init the board */
398   board_gtk_init (app->theme, GTK_FIXED (app->fi_matrix));
399 
400   /* init goal */
401   goal_view_init (app->theme, GTK_FIXED (app->fi_goal));
402 
403   /* update user visible information */
404   app->state = GAME_STATE_NOT_RUNNING;
405   update_menu_item_state ();
406   update_statistics ();
407 
408   gtk_widget_grab_focus (GTK_WIDGET (app->fi_matrix));
409 }
410 
game_level_finished(void)411 void game_level_finished (void)
412 {
413   controller_handle_action (GAME_ACTION_FINISHED);
414 }
415 
calculate_score(void)416 static void calculate_score (void)
417 {
418   gint seconds = clock_get_elapsed (CLOCK (app->clock));
419 
420   if (seconds > 300)
421     return;
422 
423   if (app->score == 0)
424     app->score = 300 - seconds;
425   else
426     app->score = app->score * (2 - (seconds / 300));
427 }
428 
view_congratulations(void)429 static void view_congratulations (void)
430 {
431   GtkWidget *dlg;
432   dlg = gtk_message_dialog_new (GTK_WINDOW (app->mainwin),
433 				GTK_DIALOG_MODAL,
434 				GTK_MESSAGE_INFO,
435 				GTK_BUTTONS_CLOSE,
436 				"%s",
437 				_("Congratulations! You have finished all Atomix levels."));
438   gtk_dialog_run (GTK_DIALOG (dlg));
439   gtk_widget_destroy (GTK_WIDGET (dlg));
440 }
441 
442 /* ===============================================================
443 
444              UI update functions
445 
446 -------------------------------------------------------------- */
update_statistics(void)447 static void update_statistics (void)
448 {
449   gchar *str_buffer;
450 
451   g_return_if_fail (app != NULL);
452 
453   if (app->state == GAME_STATE_NOT_RUNNING)
454     {
455       /* don't show anything */
456       gtk_label_set_text (GTK_LABEL (app->lb_level), "");
457       gtk_label_set_text (GTK_LABEL (app->lb_name), "");
458       gtk_label_set_text (GTK_LABEL (app->lb_formula), "");
459       gtk_label_set_text (GTK_LABEL (app->lb_score), "");
460       gtk_widget_hide (GTK_WIDGET (app->clock));
461     }
462   else
463     {
464       /* set level number */
465       str_buffer = g_new0 (gchar, 10);
466       g_snprintf (str_buffer, 10, "%i", app->level_no);
467       gtk_label_set_text (GTK_LABEL (app->lb_level), str_buffer);
468 
469       /* set levelname */
470       gtk_label_set_text (GTK_LABEL (app->lb_name),
471 			  _(level_get_name (app->level)));
472 
473       /* set the formula of the compound */
474       gtk_label_set_markup (GTK_LABEL (app->lb_formula),
475 			    level_get_formula (app->level));
476 
477       /* set score */
478       g_snprintf (str_buffer, 10, "%i", app->score);
479       gtk_label_set_text (GTK_LABEL (app->lb_score), str_buffer);
480 
481       /* show clock */
482       gtk_widget_show (GTK_WIDGET (app->clock));
483 
484       g_free (str_buffer);
485     }
486 }
487 
488 typedef struct
489 {
490   const gchar *cmd;
491   gboolean enabled;
492 } CmdEnable;
493 
494 
495 static const GActionEntry app_entries[] =
496   {
497     { "GameAbout", verb_HelpAbout_cb, NULL, NULL, NULL},
498     { "GameQuit", verb_GameExit_cb, NULL, NULL, NULL}
499   };
500 
501 static const GActionEntry win_entries[] =
502   {
503     { "GameNew", verb_GameNew_cb, NULL, NULL, NULL},
504     { "GameEnd", verb_GameEnd_cb, NULL, NULL, NULL},
505     { "LevelSkip", verb_GameSkip_cb, NULL, NULL, NULL},
506     { "LevelReset", verb_GameReset_cb, NULL, NULL, NULL},
507     { "GameUndo", verb_GameUndo_cb, NULL, NULL, NULL},
508     { "GamePause", verb_GamePause_cb, NULL, NULL, NULL},
509   };
510 
511 static const CmdEnable not_running[] =
512   {
513     { "GameNew",      TRUE  },
514     { "GameEnd",      FALSE },
515     { "LevelSkip",    FALSE },
516     { "LevelReset",   FALSE },
517     { "GameUndo",     FALSE },
518     { "GamePause",    FALSE },
519     { NULL, FALSE }
520   };
521 
522 static const CmdEnable running_unmoved[] =
523   {
524     { "GameNew",      FALSE },
525     { "GameEnd",      TRUE  },
526     { "LevelSkip",    TRUE  },
527     { "LevelReset",   FALSE },
528     { "GameUndo",     FALSE },
529     { "GamePause",    TRUE  },
530     { NULL, FALSE }
531 };
532 
533 static const CmdEnable running[] =
534   {
535     { "GameNew",      FALSE },
536     { "GameEnd",      TRUE  },
537     { "LevelSkip",    TRUE  },
538     { "LevelReset",   TRUE  },
539     { "GameUndo",     TRUE  },
540     { "GamePause",    TRUE  },
541     { NULL, FALSE }
542 };
543 
544 static const CmdEnable paused[] =
545   {
546     { "GameNew",      FALSE },
547     { "GameEnd",      FALSE },
548     { "LevelSkip",    FALSE },
549     { "LevelReset",   FALSE },
550     { "GameUndo",     FALSE },
551     { "GamePause",    TRUE  },
552     { NULL, FALSE }
553 };
554 
555 static const CmdEnable *state_sensitivity[] =
556   {
557     not_running, running_unmoved, running, paused
558   };
559 
update_menu_item_state(void)560 void update_menu_item_state (void)
561 {
562   gint i;
563   const CmdEnable *cmd_list = state_sensitivity[app->state];
564   GAction *action;
565 
566   for (i = 0; cmd_list[i].cmd != NULL; i++)
567     {
568       action = g_action_map_lookup_action (G_ACTION_MAP (app->mainwin), cmd_list[i].cmd);
569       g_simple_action_set_enabled (G_SIMPLE_ACTION (action), cmd_list[i].enabled);
570     }
571 }
572 
get_scores_category(const gchar * category_key,void * user_data)573 static GamesScoresCategory* get_scores_category(const gchar* category_key, void* user_data)
574 {
575   return app->current_category;
576 }
577 /* ===============================================================
578 
579              GUI creation  functions
580 
581 -------------------------------------------------------------- */
create_gui(GApplication * app_instance)582 static AtomixApp *create_gui (GApplication *app_instance)
583 {
584   gchar *ui_path;
585   GtkBuilder *builder;
586   GtkWidget *stats_grid;
587   GtkWidget *time_label;
588   GtkWidget *headerbar;
589   GtkWidget *primary_menu_button;
590 
591   app = g_new0 (AtomixApp, 1);
592   app->level = NULL;
593   app->app_instance = app_instance;
594 
595   builder = gtk_builder_new ();
596 
597   ui_path = g_build_filename (PKGDATADIR, "ui", "interface.ui", NULL);
598   gtk_builder_add_from_file (builder, ui_path, NULL);
599   g_free (ui_path);
600 
601   ui_path = g_build_filename (PKGDATADIR, "ui", "menu.ui", NULL);
602   gtk_builder_add_from_file (builder, ui_path, NULL);
603   g_free (ui_path);
604 
605   app->mainwin = GTK_WIDGET (gtk_builder_get_object (builder, "mainwin"));
606 
607   app->current_category = games_scores_category_new ("atomix", "Best scores");
608   app->high_scores = games_scores_context_new("atomix", "Best scores",
609                                                 GTK_WINDOW (app->mainwin),
610                                                 get_scores_category,
611                                                 NULL, GAMES_SCORES_STYLE_POINTS_GREATER_IS_BETTER);
612 
613   g_signal_connect (G_OBJECT (app->mainwin), "delete_event",
614                     (GCallback) on_app_destroy_event, app);
615 
616   /* create canvas widgets */
617   app->fi_matrix = GTK_WIDGET (gtk_builder_get_object (builder, "game_fixed"));
618   app->fi_goal = GTK_WIDGET (gtk_builder_get_object (builder, "preview_fixed"));
619 
620   stats_grid = GTK_WIDGET (gtk_builder_get_object (builder, "stats_grid"));
621   time_label = GTK_WIDGET (gtk_builder_get_object (builder, "time_label"));
622   app->clock = clock_new ();
623   gtk_grid_attach_next_to (GTK_GRID (stats_grid), app->clock,
624                            time_label, GTK_POS_RIGHT, 1, 1);
625 
626   app->lb_level = GTK_WIDGET (gtk_builder_get_object (builder, "level_value"));
627   app->lb_name = GTK_WIDGET (gtk_builder_get_object (builder, "molecule_value"));
628   app->lb_formula = GTK_WIDGET (gtk_builder_get_object (builder, "formula_value"));
629   app->lb_score = GTK_WIDGET (gtk_builder_get_object (builder, "score_value"));
630 
631   gtk_widget_show_all (GTK_WIDGET (app->mainwin));
632 
633   headerbar = GTK_WIDGET (gtk_builder_get_object(builder, "headerbar"));
634   gtk_application_add_window (GTK_APPLICATION (app->app_instance), GTK_WINDOW (app->mainwin));
635 
636   const char *new_game_accels[] = {"<Primary>N", NULL};
637   gtk_application_set_accels_for_action (GTK_APPLICATION (app->app_instance), "win.GameNew", new_game_accels);
638   const char *pause_game_accels[] = {"<Primary>P", NULL};
639   gtk_application_set_accels_for_action (GTK_APPLICATION (app->app_instance), "win.GamePause", pause_game_accels);
640   const char *skip_level_accels[] = {"<Primary>S", NULL};
641   gtk_application_set_accels_for_action (GTK_APPLICATION (app->app_instance), "win.LevelSkip", skip_level_accels);
642   const char *undo_move_accels[] = {"<Primary>Z", NULL};
643   gtk_application_set_accels_for_action (GTK_APPLICATION (app->app_instance), "win.GameUndo", undo_move_accels);
644 
645   gtk_window_set_titlebar (GTK_WINDOW (app->mainwin), headerbar);
646   GMenu * menu = G_MENU (gtk_builder_get_object (builder, "primary-menu"));
647 
648   primary_menu_button = GTK_WIDGET (gtk_builder_get_object (builder, "primary-menu-button"));
649   gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (primary_menu_button), G_MENU_MODEL (menu));
650 
651   g_object_unref (builder);
652 
653   return app;
654 }
655 
656 static void
app_activate(GApplication * app_instance,gpointer user_data)657 app_activate (GApplication *app_instance, gpointer user_data)
658 {
659 
660   if (app == NULL) {
661     /* make a few initalisations here */
662     g_action_map_add_action_entries (G_ACTION_MAP (app_instance), app_entries, G_N_ELEMENTS (app_entries), app_instance);
663 
664     app = create_gui (app_instance);
665 
666     g_action_map_add_action_entries (G_ACTION_MAP (app->mainwin), win_entries, G_N_ELEMENTS (win_entries), app_instance);
667 
668     game_init ();
669     gtk_widget_set_size_request (GTK_WIDGET (app->mainwin), 678, 520);
670 
671     //gtk_window_set_resizable (GTK_WINDOW (app->mainwin), FALSE);
672     gtk_widget_show (app->mainwin);
673   } else {
674     gtk_window_present (GTK_WINDOW (app->mainwin));
675   }
676 }
677 
main(int argc,char * argv[])678 int main (int argc, char *argv[])
679 {
680   GtkApplication *gtk_app;
681 
682   int status;
683 
684   g_set_application_name (_("Atomix"));
685 
686   bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
687   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
688   textdomain (GETTEXT_PACKAGE);
689 
690   gtk_app = gtk_application_new ("org.gnome.atomix", G_APPLICATION_FLAGS_NONE);
691   g_signal_connect (gtk_app, "activate", G_CALLBACK (app_activate), NULL);
692 
693   status = g_application_run (G_APPLICATION (gtk_app), argc, argv);
694   g_object_unref (gtk_app);
695 
696   return status;
697 }
698