1 /** \file   settings_ethernet.c
2  * \brief   GTK3 ethernet settings widget
3  *
4  * \author  Bas Wassink <b.wassink@ziggo.nl>
5  */
6 
7 /*
8  * $VICERES ETHERNET_INTERFACE  x64 x64sc xscpu64 x128 xvic
9  */
10 
11 /*
12  * This file is part of VICE, the Versatile Commodore Emulator.
13  * See README for copyright notice.
14  *
15  *  This program is free software; you can redistribute it and/or modify
16  *  it under the terms of the GNU General Public License as published by
17  *  the Free Software Foundation; either version 2 of the License, or
18  *  (at your option) any later version.
19  *
20  *  This program is distributed in the hope that it will be useful,
21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  *  GNU General Public License for more details.
24  *
25  *  You should have received a copy of the GNU General Public License
26  *  along with this program; if not, write to the Free Software
27  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28  *  02111-1307  USA.
29  *
30  */
31 
32 #include "vice.h"
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include <gtk/gtk.h>
37 #include <stdbool.h>
38 
39 #include "vice_gtk3.h"
40 #include "resources.h"
41 #include "lib.h"
42 #include "log.h"
43 #include "machine.h"
44 #ifdef HAVE_RAWNET
45 # include "rawnet.h"
46 #endif
47 #include "archdep_defs.h"
48 #include "uisettings.h"
49 #include "archdep_ethernet_available.h"
50 
51 
52 #include "settings_ethernet.h"
53 
54 
55 #ifdef HAVE_RAWNET
56 static void clean_iface_list(void);
57 #endif
58 
59 
60 
61 /** \brief  Handler for the 'destroy' event of the main widget
62  *
63  * \param[in]   widget  main widget (grid)
64  * \param[in]   data    extra event data (unused)
65  */
on_settings_ethernet_destroy(GtkWidget * widget,gpointer data)66 static void on_settings_ethernet_destroy(GtkWidget *widget, gpointer data)
67 {
68 #ifdef HAVE_RAWNET
69     clean_iface_list();
70 #endif
71 }
72 
73 
74 
75 #ifdef HAVE_RAWNET
76 
77 /** \brief  List of available interfaces
78  *
79  * This list is dynamically generated and destroyed when the main widget
80  * is destroyed.
81  */
82 static vice_gtk3_combo_entry_str_t *iface_list;
83 
84 
85 /** \brief  Build interface list for the combo box
86  *
87  * \return  bool
88  */
build_iface_list(void)89 static gboolean build_iface_list(void)
90 {
91     int num = 0;
92     char *if_name;
93     char *if_desc;
94 
95     /* get number of adapters */
96     if (!rawnet_enumadapter_open()) {
97         return FALSE;
98     }
99     while (rawnet_enumadapter(&if_name, &if_desc)) {
100         lib_free(if_name);
101         if (if_desc != NULL) {
102             lib_free(if_desc);
103         }
104         num++;
105     }
106     rawnet_enumadapter_close();
107 
108     /* allocate memory for list */
109     iface_list = lib_malloc((size_t)(num + 1) * sizeof *iface_list);
110 
111     /* now add the list items */
112     if (!rawnet_enumadapter_open()) {
113         lib_free(iface_list);
114         iface_list = NULL;
115         return FALSE;
116     }
117 
118     num = 0;
119     while (rawnet_enumadapter(&if_name, &if_desc)) {
120         iface_list[num].id = lib_strdup(if_name);
121         /*
122          * On Windows, the description string seems to be always present, on
123          * Unix this isn't the case and NULL can be returned.
124          */
125         if (if_desc == NULL) {
126             iface_list[num].name = lib_strdup(if_name);
127         } else {
128             iface_list[num].name = lib_msprintf("%s (%s)", if_name, if_desc);
129         }
130         lib_free(if_name);
131         if (if_desc != NULL) {
132             lib_free(if_desc);
133         }
134 
135         num++;
136     }
137     iface_list[num].id = NULL;
138     iface_list[num].name = NULL;
139     rawnet_enumadapter_close();
140     return TRUE;
141 }
142 
143 
144 /** \brief  Free memory used by the interface list
145  */
clean_iface_list(void)146 static void clean_iface_list(void)
147 {
148     if (iface_list != NULL) {
149         int num = 0;
150         while (iface_list[num].id != NULL) {
151             lib_free(iface_list[num].id);
152             lib_free(iface_list[num].name);
153             num++;
154         }
155         lib_free(iface_list);
156         iface_list = NULL;
157     }
158 }
159 
160 
161 
162 /** \brief  Create combo box to select the ethernet interface
163  *
164  * \return  GtkComboBoxText
165  */
create_device_combo(void)166 static GtkWidget *create_device_combo(void)
167 {
168     GtkWidget *combo;
169 
170     if (build_iface_list()) {
171         combo = vice_gtk3_resource_combo_box_str_new("ETHERNET_INTERFACE",
172                 iface_list);
173     } else {
174         combo = gtk_combo_box_text_new();
175     }
176 
177     return combo;
178 }
179 #endif
180 
181 
182 /** \brief  Create Ethernet settings widget for the settings UI
183  *
184  * \param[in]   parent  parent widget (ignored)
185  *
186  * \return  GtkGrid
187  */
settings_ethernet_widget_create(GtkWidget * parent)188 GtkWidget *settings_ethernet_widget_create(GtkWidget *parent)
189 {
190     GtkWidget *grid;
191     GtkWidget *label;
192     char *text;
193 #ifdef HAVE_RAWNET
194     GtkWidget *combo;
195     bool available = archdep_ethernet_available();
196     debug_gtk3("Ethernet available = %s\n", available ? "True" : "False");
197 #endif
198 
199     grid = vice_gtk3_grid_new_spaced(VICE_GTK3_DEFAULT, VICE_GTK3_DEFAULT);
200 
201     switch (machine_class) {
202         case VICE_MACHINE_C64DTV:   /* fall through */
203         case VICE_MACHINE_PLUS4:    /* fall through */
204         case VICE_MACHINE_PET:      /* fall through */
205         case VICE_MACHINE_CBM5x0:   /* fall through */
206         case VICE_MACHINE_CBM6x0:   /* fall through */
207         case VICE_MACHINE_VSID:
208 
209             text = lib_msprintf(
210                     "<b>Error</b>: Ethernet not supported for <b>%s</b>, "
211                     "please fix the code that calls this code!",
212                     machine_name);
213             label = gtk_label_new(NULL);
214             gtk_label_set_markup(GTK_LABEL(label), text);
215             gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
216             gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
217             lib_free(text);
218             gtk_widget_show_all(grid);
219             return grid;
220         default:
221             break;
222     }
223 
224 #ifdef HAVE_RAWNET
225     label = gtk_label_new("Ethernet device");
226     gtk_widget_set_halign(label, GTK_ALIGN_START);
227 
228     combo = create_device_combo();
229     gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
230     gtk_grid_attach(GTK_GRID(grid), combo, 1, 0, 1, 1);
231 
232     if (!available) {
233         gtk_widget_set_sensitive(combo, FALSE);
234         label = gtk_label_new(NULL);
235 # ifdef ARCHDEP_OS_UNIX
236         gtk_label_set_markup(GTK_LABEL(label),
237                 "<i>VICE needs to be run as <tt>root</tt> to be able to use ethernet emulation.</i>");
238 # elif defined(ARCHDEP_OS_WINDOWS)
239         gtk_label_set_markup(GTK_LABEL(label),
240                 "<i><tt>wpcap.dll</tt> not found, please install WinPCAP to use ethernet emulation.</i>");
241 # else
242         gtk_label_set_markup(GTK_LABEL(label),
243                 "<i>Ethernet emulation disabled due to unsupported OS.</i>");
244 # endif
245         g_object_set(label, "margin-left", 16, NULL);
246         gtk_widget_set_halign(label, GTK_ALIGN_START);
247         gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 2, 1);
248     }
249 
250 
251 
252 
253 #else
254     label = gtk_label_new("Ethernet not supported, please compile with "
255             "--enable-ethernet.");
256     gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
257 #endif
258 
259     g_signal_connect(grid, "destroy", G_CALLBACK(on_settings_ethernet_destroy),
260             NULL);
261 
262     gtk_widget_show_all(grid);
263     return grid;
264 }
265