1 /** \file   uicommands.c
2  * \brief   Simple commands triggered from the menu
3  *
4  * \author  Bas Wassink <b.wassink@ziggo.nl>
5  *
6  */
7 
8 /*
9  * $VICERES WarpMode        all
10  * $VICERES JoyDevice1      -vsid
11  * $VICERES JoyDevice2      -vsid
12  * $VICERES JoyDevice3      -vsid
13  * $VICERES JoyDevice4      -vsid
14  */
15 
16 /*
17  * This file is part of VICE, the Versatile Commodore Emulator.
18  * See README for copyright notice.
19  *
20  *  This program is free software; you can redistribute it and/or modify
21  *  it under the terms of the GNU General Public License as published by
22  *  the Free Software Foundation; either version 2 of the License, or
23  *  (at your option) any later version.
24  *
25  *  This program is distributed in the hope that it will be useful,
26  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
27  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28  *  GNU General Public License for more details.
29  *
30  *  You should have received a copy of the GNU General Public License
31  *  along with this program; if not, write to the Free Software
32  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
33  *  02111-1307  USA.
34  *
35  */
36 
37 #include "vice.h"
38 
39 #include <stdio.h>
40 #include <string.h>
41 #include <gtk/gtk.h>
42 
43 #include "archdep.h"
44 #include "resources.h"
45 #include "debug_gtk3.h"
46 #include "basedialogs.h"
47 #include "drive.h"
48 #include "log.h"
49 #include "machine.h"
50 #include "util.h"
51 #include "vsync.h"
52 
53 #if 0
54 #ifdef WIN32_COMPILE
55 # include <windows.h>
56 #endif
57 #endif
58 
59 #include "ui.h"
60 #include "uicommands.h"
61 #include "uimachinewindow.h"
62 
63 
64 /** \brief  Swap joysticks
65  *
66  * \param[in]   widget      widget triggering the event (invalid)
67  * \param[in]   user_data   extra data for event (unused)
68  *
69  * \return  TRUE
70  */
ui_swap_joysticks_callback(GtkWidget * widget,gpointer user_data)71 gboolean ui_swap_joysticks_callback(GtkWidget *widget, gpointer user_data)
72 {
73     int joy1;
74     int joy2;
75 
76     resources_get_int("JoyDevice1", &joy1);
77     resources_get_int("JoyDevice2", &joy2);
78     resources_set_int("JoyDevice1", joy2);
79     resources_set_int("JoyDevice2", joy1);
80 
81     return TRUE;
82 }
83 
84 
85 /** \brief  Swap userport joysticks
86  *
87  * \param[in]   widget      widget triggering the event (invalid)
88  * \param[in]   user_data   extra data for event (unused)
89  *
90  * \return  TRUE
91  */
ui_swap_userport_joysticks_callback(GtkWidget * widget,gpointer user_data)92 gboolean ui_swap_userport_joysticks_callback(GtkWidget *widget,
93                                              gpointer user_data)
94 {
95     int joy3;
96     int joy4;
97 
98     resources_get_int("JoyDevice3", &joy3);
99     resources_get_int("JoyDevice4", &joy4);
100     resources_set_int("JoyDevice3", joy4);
101     resources_set_int("JoyDevice4", joy3);
102 
103     return TRUE;
104 }
105 
106 
107 /** \brief  Callback for the soft/hard reset items
108  *
109  * \param[in]   widget      menu item triggering the event (unused)
110  * \param[in]   user_data   MACHINE_RESET_MODE_SOFT/MACHINE_RESET_MODE_HARD
111  */
ui_machine_reset_callback(GtkWidget * widget,gpointer user_data)112 void ui_machine_reset_callback(GtkWidget *widget, gpointer user_data)
113 {
114     vsync_suspend_speed_eval();
115     machine_trigger_reset(GPOINTER_TO_INT(user_data));
116 }
117 
118 
119 /** \brief  Callback for the drive reset items
120  *
121  * \param[in]   widget      menu item triggering the event (unused)
122  * \param[in]   user_data   drive unit number (8-11) (int)
123  */
ui_drive_reset_callback(GtkWidget * widget,gpointer user_data)124 void ui_drive_reset_callback(GtkWidget *widget, gpointer user_data)
125 {
126     vsync_suspend_speed_eval();
127     drive_cpu_trigger_reset(GPOINTER_TO_INT(user_data) - 8);
128 }
129 
130 
131 /** \brief  Ask the user to confirm to exit the emulator if ConfirmOnExit is set
132  *
133  * \return  TRUE if the emulator should be exited, FALSE if not
134  */
confirm_exit(void)135 static gboolean confirm_exit(void)
136 {
137     int confirm;
138 
139     resources_get_int("ConfirmOnExit", &confirm);
140     if (!confirm) {
141         return TRUE;
142     }
143 
144     if (vice_gtk3_message_confirm("Exit VICE",
145                                   "Do you really wish to exit VICE?")) {
146         return TRUE;
147     }
148     ui_set_ignore_mouse_hide(FALSE);
149     return FALSE;
150 }
151 
152 
153 /** \brief  Callback for the File->Exit menu item
154  *
155  * \param[in]   widget      menu item triggering the event (unused)
156  * \param[in]   user_data   unused
157  */
ui_close_callback(GtkWidget * widget,gpointer user_data)158 void ui_close_callback(GtkWidget *widget, gpointer user_data)
159 {
160     if (confirm_exit()) {
161         ui_exit();
162     }
163 }
164 
165 
166 /** \brief  Handler for the "delete-event" of a main window
167  *
168  * \param[in]   widget      window triggering the event
169  * \param[in]   event       event details (unused)
170  * \param[in]   user_data   extra data for the event (unused)
171  *
172  * \return  TRUE, if the function returns at all
173  */
ui_main_window_delete_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)174 gboolean ui_main_window_delete_event(GtkWidget *widget, GdkEvent *event,
175                                      gpointer user_data)
176 {
177     if (confirm_exit()) {
178         /* if we reach this point, the function doesn't return */
179         ui_exit();
180     }
181     return TRUE;
182 }
183 
184 
185 /** \brief  Callback for the "destroy" event of a main window
186  *
187  * \param[in]   widget      widget triggering the event
188  * \param[in]   user_data   extra data for the callback (unused)
189  */
ui_main_window_destroy_callback(GtkWidget * widget,gpointer user_data)190 void ui_main_window_destroy_callback(GtkWidget *widget, gpointer user_data)
191 {
192     GtkWidget *grid;
193 
194     debug_gtk3("WINDOW DESTROY called on %p.", widget);
195 
196     /*
197      * This should not be needed, destroying a GtkWindow should trigger
198      * destruction of all widgets it contains.
199      */
200     debug_gtk3("Manually calling destroy() on the CRT widgets. This should not"
201             " be necesarry, but right now it is.");
202     grid = gtk_bin_get_child(GTK_BIN(widget));
203     if (grid != NULL) {
204         GtkWidget *crt = gtk_grid_get_child_at(GTK_GRID(grid), 0, 2);
205         if (crt != NULL) {
206             gtk_widget_destroy(crt);
207         }
208     }
209 }
210 
211 
212 /** \brief  Toggle boolean resource from the menu
213  *
214  * Toggles \a resource when a valid.
215  *
216  * \param[in]   widget      menu item triggering the event
217  * \param[in]   resource    resource name
218  *
219  * \return  TRUE if succesful, FALSE otherwise
220  */
ui_toggle_resource(GtkWidget * widget,gpointer resource)221 gboolean ui_toggle_resource(GtkWidget *widget, gpointer resource)
222 {
223     const char *res = (const char *)resource;
224 
225     if (res != NULL) {
226         int new_state;
227 
228         /* attempt to toggle resource */
229         if (resources_toggle(res, &new_state) < 0) {
230             debug_gtk3("toggling resource %s failed.", res);
231             return FALSE;
232         }
233         debug_gtk3("resource %s toggled to %s.",
234                    res, new_state ? "True" : "False");
235         return TRUE;
236     }
237     return FALSE;
238 }
239 
240 
241 /** \brief  Open the Manual
242  */
ui_open_manual_callback(GtkWidget * widget,gpointer user_data)243 void ui_open_manual_callback(GtkWidget *widget, gpointer user_data)
244 {
245     GError *error = NULL;
246     gboolean res;
247     char *uri;
248     const char *path;
249     gchar *final_uri;
250 
251     /*
252      * Gget arch-dependent documentation dir (doesn't contain the HTML docs
253      * on Windows, but that's an other issue to fix.
254      */
255     path = archdep_get_vice_docsdir();
256 
257     /* first try opening the pdf */
258     uri = archdep_join_paths(path, "vice.pdf", NULL);
259 
260     debug_gtk3("URI before GTK3: %s", uri);
261 
262     /*
263      * This should not be used, but rather a helper tool provided by Gtk:
264      * gspawn-winXX-helper-console.exe needs to be installed by the bindist
265      * script.
266      */
267 #if 0
268 #ifdef WIN32_COMPILE
269     /* Windows: the Gtk/GLib stuff fails whatever I do, so let's use actual
270      *          Windows code. --compyx
271      */
272     ShellExecuteA(NULL, "open", uri, NULL, NULL, SW_SHOW);
273     /* that's right: no error checking and no fallback to HTML */
274     return;
275 #endif
276 #endif
277 
278     final_uri = g_filename_to_uri(uri, NULL, &error);
279     debug_gtk3("final URI (pdf): %s", final_uri);
280     if (final_uri == NULL) {
281         /*
282          * This is a fatal error, if a proper URI can't be built something is
283          * wrong and should be looked at. This is different from failing to
284          * load the PDF or not having a program to show the PDF
285          */
286         log_error(LOG_ERR, "failed to construct a proper URI from '%s',"
287                 " not trying the HTML fallback, this is an error that"
288                 " should not happen.",
289                 uri);
290         g_clear_error(&error);
291         lib_free(uri);
292         lib_free(path);
293         return;
294     }
295 
296     debug_gtk3("pdf uri: '%s'.", final_uri);
297     res = gtk_show_uri_on_window(NULL, final_uri, GDK_CURRENT_TIME, &error);
298     if (!res) {
299         vice_gtk3_message_error(
300                 "Failed to load PDF: %s.",
301                 error != NULL ? error->message : "<no message>");
302     }
303     lib_free(uri);
304     g_free(final_uri);
305     g_clear_error(&error);
306     if (res) {
307         /* We succesfully managed to open the PDF application, but there's no
308          * way to determine if actually loading the PDF in that application
309          * worked. So we simply exit here to avoid also opening a HTML browser
310          * which on Windows at least seems to completely ignore the default and
311          * always starts fucking Internet Explorer.
312          *
313          * Also how do we close the PDF application if we could determine it
314          * failed to load the PDF? We don't get any reference to the application
315          * to be able to terminate it. Gtk3 is awesome!
316          *
317          * -- compyx
318          */
319         lib_free(path);
320         return;
321     }
322 
323     /* try opening the html doc */
324 #if defined(WIN32_COMPILE)
325     /* HACK: on windows the html files are in a separate directory */
326     uri = archdep_join_paths(path, "..", "html", "vice_toc.html", NULL);
327 #else
328     uri = archdep_join_paths(path, "vice_toc.html", NULL);
329 #endif
330 
331     final_uri = g_filename_to_uri(uri, NULL, &error);
332     if (final_uri == NULL) {
333         /*
334          * This is a fatal error, if a proper URI can't be built something is
335          * wrong and should be looked at. This is different from failing to
336          * load the PDF or not having a program to show the PDF
337          */
338         log_error(LOG_ERR,
339                 "failed to construct a proper URI from '%s',"
340                 " this is an error that should not happen.",
341                 uri);
342         g_free(final_uri);
343         lib_free(uri);
344         lib_free(path);
345         return;
346     }
347 
348     /*
349      * On Windows this does not respect the user's preferred browser. That is,
350      * it didn't respect my Firefox but decided to use Internet Explorer,
351      * which is an unspeakable act of cruelty.
352      */
353     debug_gtk3("html uri: '%s'.", final_uri);
354     res = gtk_show_uri_on_window(NULL, final_uri, GDK_CURRENT_TIME, &error);
355     if (!res && error != NULL) {
356         vice_gtk3_message_error("Failed to show URI", error->message);
357     }
358     lib_free(uri);
359     g_free(final_uri);
360     g_clear_error(&error);
361     lib_free(path);
362 }
363