1 /** \file   uisnapshot.c
2  * \brief   Snapshot dialogs and menu item handlers
3  *
4  * \author  Bas Wassink <b.wassink@ziggo.nl>
5  */
6 
7 /*
8  * This file is part of VICE, the Versatile Commodore Emulator.
9  * See README for copyright notice.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24  *  02111-1307  USA.
25  *
26  */
27 
28 #include "vice.h"
29 
30 #include <gtk/gtk.h>
31 #include <stdlib.h>
32 
33 #include "lib.h"
34 #include "util.h"
35 #include "archdep.h"
36 #include "basewidgets.h"
37 #include "widgethelpers.h"
38 #include "debug_gtk3.h"
39 #include "machine.h"
40 #include "resources.h"
41 #include "filechooserhelpers.h"
42 #include "openfiledialog.h"
43 #include "savefiledialog.h"
44 #include "selectdirectorydialog.h"
45 #include "basedialogs.h"
46 #include "interrupt.h"
47 #include "vsync.h"
48 #include "snapshot.h"
49 #include "vice-event.h"
50 #include "uistatusbar.h"
51 #include "ui.h"
52 
53 #include "uisnapshot.h"
54 
55 
56 /*****************************************************************************
57  *                              Helper functions                             *
58  ****************************************************************************/
59 
60 
61 /** \brief  Construct filename for quickload/quicksave snapshots
62  *
63  * \return  filename for the quickload/save file, heap-allocated by VICE, so
64  *          free after use with lib_free()
65  */
quicksnap_filename(void)66 static char *quicksnap_filename(void)
67 {
68     char *fname;
69     const char *mname;
70     char *cfg;
71 
72     mname = machine_class == VICE_MACHINE_C64SC ? "c64sc" : machine_name;
73     cfg = archdep_user_config_path();
74     fname = util_concat(cfg, "/", mname, ".vsf", NULL);
75 #if 0
76     lib_free(cfg);
77 #endif
78     debug_gtk3("quicksnap_filename = %s.", fname);
79     return fname;
80 }
81 
82 
83 /** \brief  Show dialog to save a snapshot
84  */
save_snapshot_dialog(void)85 static void save_snapshot_dialog(void)
86 {
87     GtkWidget *dialog;
88     GtkWidget *extra;
89     GtkWidget *roms_widget;
90     GtkWidget *disks_widget;
91     gint response_id;
92     int save_roms;
93     int save_disks;
94 
95     dialog = gtk_file_chooser_dialog_new("Save snapshot file",
96             ui_get_active_window(),
97             GTK_FILE_CHOOSER_ACTION_SAVE,
98             "Save", GTK_RESPONSE_ACCEPT,
99             "Cancel", GTK_RESPONSE_CANCEL,
100             NULL);
101 
102     gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),
103             create_file_chooser_filter(file_chooser_filter_snapshot, FALSE));
104 
105     /* create extras widget */
106     extra = gtk_grid_new();
107     gtk_grid_set_column_spacing(GTK_GRID(extra), 16);
108 
109     disks_widget = gtk_check_button_new_with_label("Save attached disks");
110     roms_widget = gtk_check_button_new_with_label("Save attached ROMs");
111     gtk_grid_attach(GTK_GRID(extra), disks_widget, 0, 0, 1, 1);
112     gtk_grid_attach(GTK_GRID(extra), roms_widget, 1, 0, 1, 1);
113     gtk_widget_show_all(extra);
114 
115     gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), extra);
116 
117     response_id = gtk_dialog_run(GTK_DIALOG(dialog));
118     save_roms = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(roms_widget));
119     save_disks = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(disks_widget));
120 
121     debug_gtk3("response_id = %d.", response_id);
122     debug_gtk3("save disks = %s.", save_disks ? "YES" : "NO");
123     debug_gtk3("save ROMs = %s.", save_roms ? "YES" : "NO");
124 
125     if (response_id == GTK_RESPONSE_ACCEPT) {
126         gchar *filename;
127 
128         filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
129         if (filename != NULL) {
130             char *fname_copy;
131             char buffer[1024];
132 
133             fname_copy = util_add_extension_const(filename, "vsf");
134 
135             if (machine_write_snapshot(fname_copy, save_roms, save_disks, 0) < 0) {
136                 snapshot_display_error();
137                 g_snprintf(buffer, 1024, "Failed to save snapshot '%s'",
138                         fname_copy);
139             } else {
140                 debug_gtk3("Wrote snapshot file '%s'.", fname_copy);
141                 g_snprintf(buffer, 1024, "Saved snapshot '%s'", fname_copy);
142             }
143             lib_free(fname_copy);
144             g_free(filename);
145         }
146     }
147     gtk_widget_destroy(dialog);
148 }
149 
150 
151 /*****************************************************************************
152  *                              CPU trap handlers                            *
153  ****************************************************************************/
154 
155 
156 /** \brief  CPU trap handler for the load snapshot dialog
157  *
158  * \param[in]   addr    memory address (unused)
159  * \param[in]   data    unused
160  */
load_snapshot_trap(uint16_t addr,void * data)161 static void load_snapshot_trap(uint16_t addr, void *data)
162 {
163     const char *filters[] = { "*.vsf", NULL };
164     gchar *filename;
165     gchar buffer[1024];
166 
167     vsync_suspend_speed_eval();
168 
169     filename = vice_gtk3_open_file_dialog("Open snapshot file",
170             "Snapshot files", filters, NULL);
171     if (filename != NULL) {
172         /* load snapshot */
173         if (machine_read_snapshot(filename, 0) < 0) {
174             snapshot_display_error();
175             g_snprintf(buffer, 1024, "Failed to load snapshot '%s'", filename);
176         } else {
177             g_snprintf(buffer, 1024, "Loaded snapshot '%s'", filename);
178         }
179         g_free(filename);
180     }
181 }
182 
183 
184 /** \brief  CPU trap handler to trigger the Save dialog
185  *
186  * \param[in]   addr    memory address (unused)
187  * \param[in]   data    unused
188  */
save_snapshot_trap(uint16_t addr,void * data)189 static void save_snapshot_trap(uint16_t addr, void *data)
190 {
191     vsync_suspend_speed_eval();
192     save_snapshot_dialog();
193 }
194 
195 
196 /** \brief  CPU trap handler for the QuickLoad snapshot menu item
197  *
198  * \param[in]   addr    memory address (unused)
199  * \param[in]   data    quickload snapshot filename
200  */
quickload_snapshot_trap(uint16_t addr,void * data)201 static void quickload_snapshot_trap(uint16_t addr, void *data)
202 {
203     char *filename = (char *)data;
204 
205     vsync_suspend_speed_eval();
206 
207     debug_gtk3("Quickloading file '%s'.", filename);
208     if (machine_read_snapshot(filename, 0) < 0) {
209         snapshot_display_error();
210     }
211     lib_free(filename);
212 }
213 
214 
215 /** \brief  CPU trap handler for the QuickSave snapshot menu item
216  *
217  * \param[in]   addr    memory address (unused)
218  * \param[in]   data    quicksave snapshot filename
219  */
quicksave_snapshot_trap(uint16_t addr,void * data)220 static void quicksave_snapshot_trap(uint16_t addr, void *data)
221 {
222     char *filename = (char *)data;
223 
224     vsync_suspend_speed_eval();
225 
226     debug_gtk3("Quicksaving file '%s'.", filename);
227     if (machine_write_snapshot(filename, TRUE, TRUE, 0) < 0) {
228         snapshot_display_error();
229     }
230     lib_free(filename);
231 }
232 
233 
234 /*****************************************************************************
235  *                              Public functions                             *
236  ****************************************************************************/
237 
238 
239 /** \brief  Display UI to load a snapshot file
240  *
241  * \param[in]   parent      parent widget
242  * \param[in]   user_data   unused
243  */
uisnapshot_open_file(GtkWidget * parent,gpointer user_data)244 void uisnapshot_open_file(GtkWidget *parent, gpointer user_data)
245 {
246     if (!ui_emulation_is_paused()) {
247         interrupt_maincpu_trigger_trap(load_snapshot_trap, NULL);
248     } else {
249         load_snapshot_trap(0, NULL);
250     }
251 }
252 
253 
254 /** \brief  Display UI to save a snapshot file
255  *
256  * \param[in]   parent      parent widget
257  * \param[in]   user_data   unused
258  */
uisnapshot_save_file(GtkWidget * parent,gpointer user_data)259 void uisnapshot_save_file(GtkWidget *parent, gpointer user_data)
260 {
261     if (!ui_emulation_is_paused()) {
262         interrupt_maincpu_trigger_trap(save_snapshot_trap, NULL);
263     } else {
264         save_snapshot_trap(0, NULL);
265     }
266 }
267 
268 
269 /** \brief  Gtk event handler for the QuickLoad menu item
270  *
271  * \param[in]   parent      parent widget
272  * \param[in]   user_data   unused
273  */
uisnapshot_quickload_snapshot(GtkWidget * parent,gpointer user_data)274 void uisnapshot_quickload_snapshot(GtkWidget *parent, gpointer user_data)
275 {
276     char *fname = quicksnap_filename();
277 
278     interrupt_maincpu_trigger_trap(quickload_snapshot_trap, (void *)fname);
279 }
280 
281 
282 /** \brief  Gtk event handler for the QuickSave menu item
283  *
284  * \param[in]   parent      parent widget
285  * \param[in]   user_data   unused
286  */
uisnapshot_quicksave_snapshot(GtkWidget * parent,gpointer user_data)287 void uisnapshot_quicksave_snapshot(GtkWidget *parent, gpointer user_data)
288 {
289     char *fname = quicksnap_filename();
290 
291     interrupt_maincpu_trigger_trap(quicksave_snapshot_trap, (void *)fname);
292 }
293 
294 
295 #if 0
296 /** \brief  Gtk event handler for the "Select history directory" menu item
297  *
298  * \param[in]   parent      parent widget
299  * \param[in]   user_data   unused
300  */
301 void uisnapshot_history_select_dir(GtkWidget *parent, gpointer user_data)
302 {
303     char *filename;
304     const char *current;
305 
306     if (resources_get_string("EventSnapshotDir", &current) < 0) {
307         debug_gtk3("failed to get current history directory, using NULL.");
308         current = NULL;
309     }
310 
311     filename = vice_gtk3_select_directory_dialog("Select history directory",
312             NULL, TRUE, current);
313     if (filename != NULL) {
314         debug_gtk3("Setting EventSnapshotDir to '%s'.", filename);
315         if (resources_set_string("EventSnapshotDir", filename) < 0) {
316             vice_gtk3_message_error("VICE core error",
317                     "Failed to set history directory.");
318         }
319         g_free(filename);
320     }
321 }
322 #endif
323 
324 
325 /** \brief  Gtk event handler for the "Start recording events" menu item
326  *
327  * \param[in]   parent      parent widget
328  * \param[in]   user_data   unused
329  */
uisnapshot_history_record_start(GtkWidget * parent,gpointer user_data)330 void uisnapshot_history_record_start(GtkWidget *parent, gpointer user_data)
331 {
332     event_record_start();
333 }
334 
335 
336 /** \brief  Gtk event handler for the "Stop recording events" menu item
337  *
338  * \param[in]   parent      parent widget
339  * \param[in]   user_data   unused
340  */
uisnapshot_history_record_stop(GtkWidget * parent,gpointer user_data)341 void uisnapshot_history_record_stop(GtkWidget *parent, gpointer user_data)
342 {
343     event_record_stop();
344 }
345 
346 
347 /** \brief  Gtk event handler for the "Start playing back events" menu item
348  *
349  * \param[in]   parent      parent widget
350  * \param[in]   user_data   unused
351  */
uisnapshot_history_playback_start(GtkWidget * parent,gpointer user_data)352 void uisnapshot_history_playback_start(GtkWidget *parent, gpointer user_data)
353 {
354     event_playback_start();
355 }
356 
357 
358 
359 /** \brief  Gtk event handler for the "Stop playing back events" menu item
360  *
361  * \param[in]   parent      parent widget
362  * \param[in]   user_data   unused
363  */
uisnapshot_history_playback_stop(GtkWidget * parent,gpointer user_data)364 void uisnapshot_history_playback_stop(GtkWidget *parent, gpointer user_data)
365 {
366     event_playback_stop();
367 }
368 
369 
370 /** \brief  Gtk event handler for the "Set recording milestone" menu item
371  *
372  * \param[in]   parent      parent widget
373  * \param[in]   user_data   unused
374  */
uisnapshot_history_milestone_set(GtkWidget * parent,gpointer user_data)375 void uisnapshot_history_milestone_set(GtkWidget *parent, gpointer user_data)
376 {
377     event_record_set_milestone();
378 }
379 
380 
381 /** \brief  Gtk event handler for the "Return to milestone" menu item
382  *
383  * \param[in]   parent      parent widget
384  * \param[in]   user_data   unused
385  */
uisnapshot_history_milestone_reset(GtkWidget * parent,gpointer user_data)386 void uisnapshot_history_milestone_reset(GtkWidget *parent, gpointer user_data)
387 {
388     event_record_reset_milestone();
389 }
390