From 276542ea98cf6f6693b68c28a847b675f5e84391 Mon Sep 17 00:00:00 2001
From: Brad Parker <bparker@reach3d.net>
Date: Mon, 5 Sep 2016 01:04:15 -0400
Subject: [PATCH] more menu popup refactor, add back working confirm_on_exit

---
 command.c                   | 48 +++++++++++++++-----------
 command.h                   |  1 +
 config.def.h                |  2 ++
 configuration.c             |  1 +
 configuration.h             |  1 +
 intl/msg_hash_us.c          |  7 ++++
 menu/cbs/menu_cbs_ok.c      | 18 +++++-----
 menu/drivers/menu_generic.c | 31 +++++++++++++++--
 menu/menu_displaylist.c     |  3 ++
 menu/menu_driver.c          |  4 +--
 menu/menu_popup.c           | 67 ++++++++++++++++++++++++++++---------
 menu/menu_popup.h           | 42 +++++++++++++++--------
 menu/menu_setting.c         | 16 +++++++++
 msg_hash.h                  |  3 ++
 retroarch.cfg               |  3 ++
 runloop.c                   | 29 +++++++++++++++-
 runloop.h                   |  3 ++
 17 files changed, 217 insertions(+), 62 deletions(-)

diff --git a/command.c b/command.c
index 04118a1728..f29f2a76e6 100644
--- a/command.c
+++ b/command.c
@@ -73,6 +73,7 @@
 #include "menu/menu_content.h"
 #include "menu/menu_display.h"
 #include "menu/menu_shader.h"
+#include "menu/menu_popup.h"
 #endif
 
 #ifdef HAVE_NETPLAY
@@ -1775,6 +1776,27 @@ static void command_event_main_state(unsigned cmd)
    RARCH_LOG("%s\n", msg);
 }
 
+void handle_quit_event()
+{
+#ifdef HAVE_MENU
+   if (menu_popup_is_active())
+      return;
+#endif
+
+   command_event(CMD_EVENT_AUTOSAVE_STATE, NULL);
+   command_event(CMD_EVENT_DISABLE_OVERRIDES, NULL);
+   command_event(CMD_EVENT_RESTORE_DEFAULT_SHADER_PRESET, NULL);
+
+#ifdef HAVE_DYNAMIC
+   command_event(CMD_EVENT_LOAD_CORE_DEINIT, NULL);
+#endif
+
+   runloop_ctl(RUNLOOP_CTL_SET_SHUTDOWN, NULL);
+#ifdef HAVE_MENU
+   rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL);
+#endif
+}
+
 /**
  * command_event:
  * @cmd                  : Event command index.
@@ -1954,14 +1976,10 @@ bool command_event(enum event_command cmd, void *data)
             return false;
          break;
       case CMD_EVENT_UNLOAD_CORE:
-      case CMD_EVENT_QUIT:
          command_event(CMD_EVENT_AUTOSAVE_STATE, NULL);
          command_event(CMD_EVENT_DISABLE_OVERRIDES, NULL);
          command_event(CMD_EVENT_RESTORE_DEFAULT_SHADER_PRESET, NULL);
 
-         switch (cmd)
-         {
-            case CMD_EVENT_UNLOAD_CORE:
                if (content_is_inited())
                   if (!task_push_content_load_default(
                            NULL, NULL,
@@ -1975,25 +1993,17 @@ bool command_event(enum event_command cmd, void *data)
                core_unload();
 #endif
                break;
-            default:
+      case CMD_EVENT_QUIT_CONFIRM:
+         handle_quit_event();
                break;
-         }
-
-#ifdef HAVE_DYNAMIC
-         command_event(CMD_EVENT_LOAD_CORE_DEINIT, NULL);
-#endif
-
-         switch (cmd)
-         {
             case CMD_EVENT_QUIT:
-               runloop_ctl(RUNLOOP_CTL_SET_SHUTDOWN, NULL);
 #ifdef HAVE_MENU
-               rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL);
+         if (settings && settings->confirm_on_exit &&
+                !menu_popup_is_active() && !runloop_is_quit_confirm())
+            menu_popup_show_message(MENU_POPUP_QUIT_CONFIRM, MENU_ENUM_LABEL_CONFIRM_ON_EXIT);
+               break;
 #endif
-               break;
-            default:
-               break;
-         }
+               handle_quit_event();
          break;
       case CMD_EVENT_CHEEVOS_HARDCORE_MODE_TOGGLE:
 #ifdef HAVE_CHEEVOS
diff --git a/command.h b/command.h
index 0771f4fc33..55728fbeb2 100644
--- a/command.h
+++ b/command.h
@@ -59,6 +59,7 @@ enum event_command
    CMD_EVENT_TAKE_SCREENSHOT,
    /* Quits RetroArch. */
    CMD_EVENT_QUIT,
+   CMD_EVENT_QUIT_CONFIRM,
    /* Reinitialize all drivers. */
    CMD_EVENT_REINIT,
    /* Toggles cheevos hardcore mode. */
diff --git a/config.def.h b/config.def.h
index 0ea69afb77..60d7b94f25 100644
--- a/config.def.h
+++ b/config.def.h
@@ -511,6 +511,8 @@ static unsigned aspect_ratio_idx = ASPECT_RATIO_CORE;
 /* Save configuration file on exit. */
 static bool config_save_on_exit = true;
 
+static bool confirm_on_exit = false;
+
 static bool show_hidden_files = true;
 
 static const bool overlay_hide_in_menu = true;
diff --git a/configuration.c b/configuration.c
index 3cc6ab5faf..da13e689fd 100644
--- a/configuration.c
+++ b/configuration.c
@@ -796,6 +796,7 @@ static int populate_settings_bool(settings_t *settings, struct config_bool_setti
    SETTING_BOOL("sort_savefiles_enable",        &settings->sort_savefiles_enable, true, default_sort_savefiles_enable, false);
    SETTING_BOOL("sort_savestates_enable",       &settings->sort_savestates_enable, true, default_sort_savestates_enable, false);
    SETTING_BOOL("config_save_on_exit",          &settings->config_save_on_exit, true, config_save_on_exit, false);
+   SETTING_BOOL("confirm_on_exit",              &settings->confirm_on_exit, true, confirm_on_exit, false);
    SETTING_BOOL("show_hidden_files",            &settings->show_hidden_files, true, show_hidden_files, false);
    SETTING_BOOL("input_autodetect_enable",      &settings->input.autodetect_enable, true, input_autodetect_enable, false);
    SETTING_BOOL("audio_rate_control",           &settings->audio.rate_control, true, rate_control, false);
diff --git a/configuration.h b/configuration.h
index dca3235b5a..cdb2c4f979 100644
--- a/configuration.h
+++ b/configuration.h
@@ -446,6 +446,7 @@ typedef struct settings
 #endif
 
    bool config_save_on_exit;
+   bool confirm_on_exit;
    bool show_hidden_files;
 
 #ifdef HAVE_LAKKA
diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c
index 7b83d9b9ba..27be847065 100644
--- a/intl/msg_hash_us.c
+++ b/intl/msg_hash_us.c
@@ -991,6 +991,9 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len)
 #endif
                );
          break;
+      case MENU_ENUM_LABEL_CONFIRM_ON_EXIT:
+         snprintf(s, len, "Are you sure you want to quit?");
+         break;
       case MENU_ENUM_LABEL_SHOW_HIDDEN_FILES:
          snprintf(s, len, "Show hidden files\n"
                "and folders.");
@@ -2527,6 +2530,8 @@ static const char *menu_hash_to_str_us_label_enum(enum msg_hash_enums msg)
          return "auto_overrides_enable";
       case MENU_ENUM_LABEL_CONFIG_SAVE_ON_EXIT:
          return "config_save_on_exit";
+      case MENU_ENUM_LABEL_CONFIRM_ON_EXIT:
+         return "confirm_on_exit";
       case MENU_ENUM_LABEL_SHOW_HIDDEN_FILES:
          return "show_hidden_files";
       case MENU_ENUM_LABEL_VIDEO_SMOOTH:
@@ -3854,6 +3859,8 @@ const char *msg_hash_to_str_us(enum msg_hash_enums msg)
          return "Load Override Files Automatically";
       case MENU_ENUM_LABEL_VALUE_CONFIG_SAVE_ON_EXIT:
          return "Save Configuration On Exit";
+      case MENU_ENUM_LABEL_VALUE_CONFIRM_ON_EXIT:
+         return "Ask For Confirmation On Exit";
       case MENU_ENUM_LABEL_VALUE_SHOW_HIDDEN_FILES:
          return "Show Hidden Files and Folders";
       case MENU_ENUM_LABEL_VALUE_VIDEO_SMOOTH:
diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c
index ddef65045b..fe6f4f753b 100644
--- a/menu/cbs/menu_cbs_ok.c
+++ b/menu/cbs/menu_cbs_ok.c
@@ -1442,7 +1442,7 @@ static int action_ok_shader_pass_load(const char *path,
 
 static int  generic_action_ok_help(const char *path,
       const char *label, unsigned type, size_t idx, size_t entry_idx,
-      enum msg_hash_enums id, enum menu_help_type id2)
+      enum msg_hash_enums id, enum menu_popup_type id2)
 {
    const char               *lbl  = msg_hash_to_str(id);
 
@@ -1457,7 +1457,7 @@ static int action_ok_cheevos(const char *path,
 
    return generic_action_ok_help(path, label, new_id, idx, entry_idx,
          MENU_ENUM_LABEL_CHEEVOS_DESCRIPTION,
-         MENU_HELP_CHEEVOS_DESCRIPTION);
+         MENU_POPUP_HELP_CHEEVOS_DESCRIPTION);
 }
 
 static int action_ok_cheat(const char *path,
@@ -3146,35 +3146,35 @@ static int action_ok_help_audio_video_troubleshooting(const char *path,
 {
    return generic_action_ok_help(path, label, type, idx, entry_idx,
          MENU_ENUM_LABEL_HELP_AUDIO_VIDEO_TROUBLESHOOTING,
-         MENU_HELP_AUDIO_VIDEO_TROUBLESHOOTING);
+         MENU_POPUP_HELP_AUDIO_VIDEO_TROUBLESHOOTING);
 }
 
 static int action_ok_help(const char *path,
       const char *label, unsigned type, size_t idx, size_t entry_idx)
 {
    return generic_action_ok_help(path, label, type, idx, entry_idx,
-         MENU_ENUM_LABEL_HELP, MENU_HELP_WELCOME);
+         MENU_ENUM_LABEL_HELP, MENU_POPUP_WELCOME);
 }
 
 static int action_ok_help_controls(const char *path,
       const char *label, unsigned type, size_t idx, size_t entry_idx)
 {
    return generic_action_ok_help(path, label, type, idx, entry_idx,
-         MENU_ENUM_LABEL_HELP_CONTROLS, MENU_HELP_CONTROLS);
+         MENU_ENUM_LABEL_HELP_CONTROLS, MENU_POPUP_HELP_CONTROLS);
 }
 
 static int action_ok_help_what_is_a_core(const char *path,
       const char *label, unsigned type, size_t idx, size_t entry_idx)
 {
    return generic_action_ok_help(path, label, type, idx, entry_idx,
-         MENU_ENUM_LABEL_HELP_WHAT_IS_A_CORE, MENU_HELP_WHAT_IS_A_CORE);
+         MENU_ENUM_LABEL_HELP_WHAT_IS_A_CORE, MENU_POPUP_HELP_WHAT_IS_A_CORE);
 }
 
 static int action_ok_help_scanning_content(const char *path,
       const char *label, unsigned type, size_t idx, size_t entry_idx)
 {
    return generic_action_ok_help(path, label, type, idx, entry_idx,
-         MENU_ENUM_LABEL_HELP_SCANNING_CONTENT, MENU_HELP_SCANNING_CONTENT);
+         MENU_ENUM_LABEL_HELP_SCANNING_CONTENT, MENU_POPUP_HELP_SCANNING_CONTENT);
 }
 
 static int action_ok_help_change_virtual_gamepad(const char *path,
@@ -3182,14 +3182,14 @@ static int action_ok_help_change_virtual_gamepad(const char *path,
 {
    return generic_action_ok_help(path, label, type, idx, entry_idx,
          MENU_ENUM_LABEL_HELP_CHANGE_VIRTUAL_GAMEPAD,
-         MENU_HELP_CHANGE_VIRTUAL_GAMEPAD);
+         MENU_POPUP_HELP_CHANGE_VIRTUAL_GAMEPAD);
 }
 
 static int action_ok_help_load_content(const char *path,
       const char *label, unsigned type, size_t idx, size_t entry_idx)
 {
    return generic_action_ok_help(path, label, type, idx, entry_idx,
-         MENU_ENUM_LABEL_HELP_LOADING_CONTENT, MENU_HELP_LOADING_CONTENT);
+         MENU_ENUM_LABEL_HELP_LOADING_CONTENT, MENU_POPUP_HELP_LOADING_CONTENT);
 }
 
 static int action_ok_video_resolution(const char *path,
diff --git a/menu/drivers/menu_generic.c b/menu/drivers/menu_generic.c
index 684e1d3b7a..50004f2909 100644
--- a/menu/drivers/menu_generic.c
+++ b/menu/drivers/menu_generic.c
@@ -31,6 +31,9 @@
 #include "../../performance_counters.h"
 
 #include "../../verbosity.h"
+#include "../../runloop.h"
+#include "../../content.h"
+#include "../../retroarch.h"
 
 static enum action_iterate_type action_iterate_type(uint32_t hash)
 {
@@ -100,12 +103,35 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action)
    switch (iterate_type)
    {
       case ITERATE_TYPE_HELP:
-         ret = menu_popup_iterate_help(
+         ret = menu_popup_iterate(
                menu->menu_state.msg, sizeof(menu->menu_state.msg), label);
          BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX);
          BIT64_SET(menu->state, MENU_STATE_POST_ITERATE);
-         if (ret == 1 || action == MENU_ACTION_OK || action == MENU_ACTION_CANCEL)
+         if (ret == 1 || action == MENU_ACTION_OK)
+         {
             BIT64_SET(menu->state, MENU_STATE_POP_STACK);
+            menu_popup_set_active(false);
+
+            if (menu_popup_get_current_type() == MENU_POPUP_QUIT_CONFIRM)
+            {
+               runloop_set_quit_confirm(true);
+               command_event(CMD_EVENT_QUIT_CONFIRM, NULL);
+            }
+         }
+
+         if (action == MENU_ACTION_CANCEL)
+         {
+            BIT64_SET(menu->state, MENU_STATE_POP_STACK);
+            menu_popup_set_active(false);
+
+            if (menu_popup_get_current_type() == MENU_POPUP_QUIT_CONFIRM)
+            {
+               runloop_set_quit_confirm(false);
+
+               if (content_is_inited())
+                  rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL);
+            }
+         }
          break;
       case ITERATE_TYPE_BIND:
          {
@@ -214,6 +240,7 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action)
          BIT64_SET(menu->state, MENU_STATE_POST_ITERATE);
          if (action == MENU_ACTION_OK || action == MENU_ACTION_CANCEL)
             BIT64_SET(menu->state, MENU_STATE_POP_STACK);
+            menu_popup_set_active(false);
          break;
       case ITERATE_TYPE_DEFAULT:
          /* FIXME: Crappy hack, needed for mouse controls 
diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c
index 3b2362f983..a3572bb19a 100644
--- a/menu/menu_displaylist.c
+++ b/menu/menu_displaylist.c
@@ -4338,6 +4338,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
          menu_displaylist_parse_settings_enum(menu, info,
                MENU_ENUM_LABEL_CONFIG_SAVE_ON_EXIT,
                PARSE_ONLY_BOOL, false);
+         menu_displaylist_parse_settings_enum(menu, info,
+               MENU_ENUM_LABEL_CONFIRM_ON_EXIT,
+               PARSE_ONLY_BOOL, false);
          menu_displaylist_parse_settings_enum(menu, info,
                MENU_ENUM_LABEL_CORE_SPECIFIC_CONFIG,
                PARSE_ONLY_BOOL, false);
diff --git a/menu/menu_driver.c b/menu/menu_driver.c
index 08dff17096..13fb9ebc18 100644
--- a/menu/menu_driver.c
+++ b/menu/menu_driver.c
@@ -172,7 +172,7 @@ static bool menu_init(menu_handle_t *menu_data)
 
    if (settings->menu_show_start_screen)
    {
-      menu_popup_push_pending(true, MENU_HELP_WELCOME);
+      menu_popup_push_pending(true, MENU_POPUP_WELCOME);
       settings->menu_show_start_screen   = false;
       command_event(CMD_EVENT_MENU_SAVE_CURRENT_CONFIG, NULL);
    }
@@ -188,7 +188,7 @@ static bool menu_init(menu_handle_t *menu_data)
 #endif
       )
    {
-      menu_popup_push_pending(true, MENU_HELP_EXTRACT);
+      menu_popup_push_pending(true, MENU_POPUP_HELP_EXTRACT);
 #ifdef HAVE_ZLIB
       task_push_decompress(settings->path.bundle_assets_src, 
             settings->path.bundle_assets_dst,
diff --git a/menu/menu_popup.c b/menu/menu_popup.c
index 0bb17f68f9..ca43cacbdb 100644
--- a/menu/menu_popup.c
+++ b/menu/menu_popup.c
@@ -19,6 +19,7 @@
 #include "menu_driver.h"
 #include "menu_popup.h"
 
+#include "../retroarch.h"
 #include "../configuration.h"
 #ifdef HAVE_CHEEVOS
 #include "../cheevos.h"
@@ -27,10 +28,12 @@
 #include "../input/input_config.h"
 
 static bool                menu_popup_pending_push   = false;
+static bool                 menu_popup_active       = false;
 static unsigned            menu_popup_current_id     = 0;
-static enum menu_help_type menu_popup_current_type   = MENU_HELP_NONE;
+static enum menu_popup_type menu_popup_current_type = MENU_POPUP_NONE;
+static enum msg_hash_enums  menu_popup_current_msg  = MSG_UNKNOWN;
 
-int menu_popup_iterate_help(char *s, size_t len, const char *label)
+int menu_popup_iterate(char *s, size_t len, const char *label)
 {
 #ifdef HAVE_CHEEVOS
    cheevos_ctx_desc_t desc_info;
@@ -40,7 +43,7 @@ int menu_popup_iterate_help(char *s, size_t len, const char *label)
 
    switch (menu_popup_current_type)
    {
-      case MENU_HELP_WELCOME:
+      case MENU_POPUP_WELCOME:
          {
             static int64_t timeout_end;
             int64_t timeout;
@@ -71,7 +74,7 @@ int menu_popup_iterate_help(char *s, size_t len, const char *label)
             }
          }
          break;
-      case MENU_HELP_CONTROLS:
+      case MENU_POPUP_HELP_CONTROLS:
          {
             unsigned i;
             char s2[PATH_MAX_LENGTH] = {0};
@@ -169,7 +172,7 @@ int menu_popup_iterate_help(char *s, size_t len, const char *label)
          break;
          
 #ifdef HAVE_CHEEVOS
-      case MENU_HELP_CHEEVOS_DESCRIPTION:
+      case MENU_POPUP_HELP_CHEEVOS_DESCRIPTION:
          desc_info.idx = menu_popup_current_id;
          desc_info.s   = s;
          desc_info.len = len;
@@ -177,29 +180,29 @@ int menu_popup_iterate_help(char *s, size_t len, const char *label)
          break;
 #endif
 
-      case MENU_HELP_WHAT_IS_A_CORE:
+      case MENU_POPUP_HELP_WHAT_IS_A_CORE:
          menu_hash_get_help_enum(MENU_ENUM_LABEL_VALUE_WHAT_IS_A_CORE_DESC,
                s, len);
          break;
-      case MENU_HELP_LOADING_CONTENT:
+      case MENU_POPUP_HELP_LOADING_CONTENT:
          menu_hash_get_help_enum(MENU_ENUM_LABEL_LOAD_CONTENT_LIST,
                s, len);
          break;
-      case MENU_HELP_CHANGE_VIRTUAL_GAMEPAD:
+      case MENU_POPUP_HELP_CHANGE_VIRTUAL_GAMEPAD:
          menu_hash_get_help_enum(
                MENU_ENUM_LABEL_VALUE_HELP_CHANGE_VIRTUAL_GAMEPAD_DESC,
                s, len);
          break;
-      case MENU_HELP_AUDIO_VIDEO_TROUBLESHOOTING:
+      case MENU_POPUP_HELP_AUDIO_VIDEO_TROUBLESHOOTING:
          menu_hash_get_help_enum(
                MENU_ENUM_LABEL_VALUE_HELP_AUDIO_VIDEO_TROUBLESHOOTING_DESC,
                s, len);
          break;
-      case MENU_HELP_SCANNING_CONTENT:
+      case MENU_POPUP_HELP_SCANNING_CONTENT:
          menu_hash_get_help_enum(MENU_ENUM_LABEL_VALUE_HELP_SCANNING_CONTENT_DESC,
                s, len);
          break;
-      case MENU_HELP_EXTRACT:
+      case MENU_POPUP_HELP_EXTRACT:
          menu_hash_get_help_enum(MENU_ENUM_LABEL_VALUE_EXTRACTING_PLEASE_WAIT,
                s, len);
 
@@ -209,14 +212,22 @@ int menu_popup_iterate_help(char *s, size_t len, const char *label)
             do_exit                   = true;
          }
          break;
-      case MENU_HELP_NONE:
+      case MENU_POPUP_QUIT_CONFIRM:
+      case MENU_POPUP_INFORMATION:
+      case MENU_POPUP_QUESTION:
+      case MENU_POPUP_WARNING:
+      case MENU_POPUP_ERROR:
+         menu_hash_get_help_enum(menu_popup_current_msg,
+               s, len);
+         break;
+      case MENU_POPUP_NONE:
       default:
          break;
    }
 
    if (do_exit)
    {
-      menu_popup_current_type = MENU_HELP_NONE;
+      menu_popup_current_type = MENU_POPUP_NONE;
       return 1;
    }
 
@@ -233,10 +244,11 @@ void menu_popup_unset_pending_push(void)
    menu_popup_pending_push = false;
 }
 
-void menu_popup_push_pending(bool push, enum menu_help_type type)
+void menu_popup_push_pending(bool push, enum menu_popup_type type)
 {
    menu_popup_pending_push = push;
    menu_popup_current_type = type;
+   menu_popup_active = true;
 }
 
 void menu_popup_push(void)
@@ -259,5 +271,30 @@ void menu_popup_reset(void)
 {
    menu_popup_pending_push = false;
    menu_popup_current_id   = 0;
-   menu_popup_current_type = MENU_HELP_NONE;
+   menu_popup_current_type = MENU_POPUP_NONE;
+   menu_popup_current_msg  = MSG_UNKNOWN;
+}
+
+void menu_popup_show_message(
+      enum menu_popup_type type, enum msg_hash_enums msg)
+{
+   menu_popup_current_msg = msg;
+
+   menu_popup_push_pending(true, type);
+   menu_popup_push();
+}
+
+bool menu_popup_is_active(void)
+{
+   return menu_popup_active;
+}
+
+void menu_popup_set_active(bool on)
+{
+   menu_popup_active = on;
+}
+
+enum menu_popup_type menu_popup_get_current_type(void)
+{
+   return menu_popup_current_type;
 }
diff --git a/menu/menu_popup.h b/menu/menu_popup.h
index ee1158730e..595eaf791c 100644
--- a/menu/menu_popup.h
+++ b/menu/menu_popup.h
@@ -24,27 +24,32 @@
 
 #include <retro_common_api.h>
 
-enum menu_help_type
+enum menu_popup_type
 {
-   MENU_HELP_NONE       = 0,
-   MENU_HELP_WELCOME,
-   MENU_HELP_EXTRACT,
-   MENU_HELP_CONTROLS,
-   MENU_HELP_CHEEVOS_DESCRIPTION,
-   MENU_HELP_LOADING_CONTENT,
-   MENU_HELP_WHAT_IS_A_CORE,
-   MENU_HELP_CHANGE_VIRTUAL_GAMEPAD,
-   MENU_HELP_AUDIO_VIDEO_TROUBLESHOOTING,
-   MENU_HELP_SCANNING_CONTENT,
-   MENU_HELP_LAST
+   MENU_POPUP_NONE = 0,
+   MENU_POPUP_WELCOME,
+   MENU_POPUP_HELP_EXTRACT,
+   MENU_POPUP_HELP_CONTROLS,
+   MENU_POPUP_HELP_CHEEVOS_DESCRIPTION,
+   MENU_POPUP_HELP_LOADING_CONTENT,
+   MENU_POPUP_HELP_WHAT_IS_A_CORE,
+   MENU_POPUP_HELP_CHANGE_VIRTUAL_GAMEPAD,
+   MENU_POPUP_HELP_AUDIO_VIDEO_TROUBLESHOOTING,
+   MENU_POPUP_HELP_SCANNING_CONTENT,
+   MENU_POPUP_QUIT_CONFIRM,
+   MENU_POPUP_INFORMATION,
+   MENU_POPUP_QUESTION,
+   MENU_POPUP_WARNING,
+   MENU_POPUP_ERROR,
+   MENU_POPUP_LAST,
 };
 
 RETRO_BEGIN_DECLS
 
 void menu_popup_push_pending(
-      bool push, enum menu_help_type type);
+      bool push, enum menu_popup_type type);
 
-int menu_popup_iterate_help( 
+int menu_popup_iterate(
       char *s, size_t len, const char *label);
 
 void menu_popup_unset_pending_push(void);
@@ -55,6 +60,15 @@ void menu_popup_push(void);
 
 void menu_popup_reset(void);
 
+void menu_popup_show_message(
+      enum menu_popup_type type, enum msg_hash_enums msg);
+
+bool menu_popup_is_active(void);
+
+void menu_popup_set_active(bool on);
+
+enum menu_popup_type menu_popup_get_current_type(void);
+
 RETRO_END_DECLS
 
 #endif
diff --git a/menu/menu_setting.c b/menu/menu_setting.c
index f810be7a67..122f01822a 100644
--- a/menu/menu_setting.c
+++ b/menu/menu_setting.c
@@ -2898,6 +2898,22 @@ static bool setting_append_list(
                SD_FLAG_NONE);
          menu_settings_list_current_add_enum_idx(list, list_info, MENU_ENUM_LABEL_CONFIG_SAVE_ON_EXIT);
 
+         CONFIG_BOOL(
+               list, list_info,
+               &settings->confirm_on_exit,
+               msg_hash_to_str(MENU_ENUM_LABEL_CONFIRM_ON_EXIT),
+               msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONFIRM_ON_EXIT),
+               confirm_on_exit,
+               msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF),
+               msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON),
+               &group_info,
+               &subgroup_info,
+               parent_group,
+               general_write_handler,
+               general_read_handler,
+               SD_FLAG_NONE);
+         menu_settings_list_current_add_enum_idx(list, list_info, MENU_ENUM_LABEL_CONFIRM_ON_EXIT);
+
          CONFIG_BOOL(
                list, list_info,
                &settings->show_hidden_files,
diff --git a/msg_hash.h b/msg_hash.h
index 9d2e114945..2a480e035e 100644
--- a/msg_hash.h
+++ b/msg_hash.h
@@ -1149,6 +1149,9 @@ enum msg_hash_enums
    MENU_ENUM_LABEL_CONFIG_SAVE_ON_EXIT,
    MENU_ENUM_LABEL_VALUE_CONFIG_SAVE_ON_EXIT,
 
+   MENU_ENUM_LABEL_CONFIRM_ON_EXIT,
+   MENU_ENUM_LABEL_VALUE_CONFIRM_ON_EXIT,
+
    MENU_ENUM_LABEL_SHOW_HIDDEN_FILES,
    MENU_ENUM_LABEL_VALUE_SHOW_HIDDEN_FILES,
 
diff --git a/retroarch.cfg b/retroarch.cfg
index 1dc3230a1f..8b57dc2b9c 100644
--- a/retroarch.cfg
+++ b/retroarch.cfg
@@ -119,6 +119,9 @@
 # Overwrites the config. #include's and comments are not preserved.
 # config_save_on_exit = true
 
+# Ask for confirmation on exit.
+# confirm_on_exit = false
+
 # Load up a specific config file based on the core being used.
 # core_specific_config = false
 
diff --git a/runloop.c b/runloop.c
index 957d7b4dc9..8c4f9f90cf 100644
--- a/runloop.c
+++ b/runloop.c
@@ -62,6 +62,7 @@
 
 #ifdef HAVE_MENU
 #include "menu/menu_driver.h"
+#include "menu/menu_popup.h"
 #endif
 
 #ifdef HAVE_NETPLAY
@@ -118,6 +119,7 @@ static bool runloop_set_frame_limit              = false;
 static bool runloop_paused                       = false;
 static bool runloop_idle                         = false;
 static bool runloop_exec                         = false;
+static bool runloop_quit_confirm                 = false;
 static bool runloop_slowmotion                   = false;
 static bool runloop_shutdown_initiated           = false;
 static bool runloop_core_shutdown_initiated      = false;
@@ -1320,6 +1322,16 @@ static int runloop_iterate_time_to_exit_load_dummy(void)
    return 1;
 }
 
+bool runloop_is_quit_confirm(void)
+{
+   return runloop_quit_confirm;
+}
+
+void runloop_set_quit_confirm(bool on)
+{
+   runloop_quit_confirm = on;
+}
+
 /* Time to exit out of the main loop?
  * Reasons for exiting:
  * a) Shutdown environment callback was invoked.
@@ -1330,6 +1342,7 @@ static int runloop_iterate_time_to_exit_load_dummy(void)
  */
 static INLINE int runloop_iterate_time_to_exit(bool quit_key_pressed)
 {
+   settings_t *settings = config_get_ptr();
    bool time_to_exit             = runloop_ctl(RUNLOOP_CTL_IS_SHUTDOWN, NULL);
    time_to_exit                  = time_to_exit || quit_key_pressed;
    time_to_exit                  = time_to_exit || !video_driver_is_alive();
@@ -1340,6 +1353,21 @@ static INLINE int runloop_iterate_time_to_exit(bool quit_key_pressed)
    if (!time_to_exit)
       return 1;
 
+#ifdef HAVE_MENU
+   if (!runloop_quit_confirm && menu_popup_is_active())
+      return 1;
+
+   if (settings && settings->confirm_on_exit &&
+          !runloop_quit_confirm)
+   {
+      if (content_is_inited())
+         rarch_ctl(RARCH_CTL_MENU_RUNNING, NULL);
+
+      menu_popup_show_message(MENU_POPUP_QUIT_CONFIRM, MENU_ENUM_LABEL_CONFIRM_ON_EXIT);
+      return 1;
+   }
+#endif
+
    if (runloop_ctl(RUNLOOP_CTL_IS_EXEC, NULL))
       runloop_ctl(RUNLOOP_CTL_UNSET_EXEC, NULL);
 
@@ -1509,7 +1537,6 @@ int runloop_iterate(unsigned *sleep_ms)
       return -1;
    }
 
-
 #ifdef HAVE_MENU
    if (menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL))
    {
diff --git a/runloop.h b/runloop.h
index c2c7625236..0774f55a9f 100644
--- a/runloop.h
+++ b/runloop.h
@@ -302,6 +302,9 @@ void runloop_msg_queue_push(const char *msg, unsigned prio,
 
 char* runloop_msg_queue_pull(void);
 
+bool runloop_is_quit_confirm(void);
+void runloop_set_quit_confirm(bool on);
+
 bool runloop_ctl(enum runloop_ctl_state state, void *data);
 
 RETRO_END_DECLS