1 /** \file   uinetplay.c
2  * \brief   UI controls for netplay
3  *
4  * \todo    This is a copy of the old Gtk2 code and should die in a fire. I'm
5  *          working on a new implementation in proper Gtk3 -- compyx
6  *
7  * \author  pottendo <pottendo@gmx.net>
8  */
9 
10 /*
11  * This file is part of VICE, the Versatile Commodore Emulator.
12  * See README for copyright notice.
13  *
14  *  This program is free software; you can redistribute it and/or modify
15  *  it under the terms of the GNU General Public License as published by
16  *  the Free Software Foundation; either version 2 of the License, or
17  *  (at your option) any later version.
18  *
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program; if not, write to the Free Software
26  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27  *  02111-1307  USA.
28  *
29  */
30 #include "vice.h"
31 
32 #include <stdio.h>
33 #include <string.h>
34 
35 #ifdef HAVE_NETWORK
36 
37 #include "lib.h"
38 #include "log.h"
39 #include "network.h"
40 #include "resources.h"
41 #include "ui.h"
42 #include "uiapi.h"
43 #include "uinetplay.h"
44 #include "util.h"
45 
46 static GtkWidget *netplay_dialog, *current_mode, *dcb, *ctrls, *np_server, *np_server_bind, *np_port;
47 static log_t np_log = LOG_ERR;
48 
49 typedef struct np_control_s {
50     char *name;
51     GtkWidget *s_cb;
52     GtkWidget *c_cb;
53     unsigned int s_mask;
54     unsigned int c_mask;
55 } np_control_t;
56 
57 #define NR_NPCONROLS 5
58 static np_control_t np_controls[] = {
59     { "Keyboard", NULL, NULL,
60       NETWORK_CONTROL_KEYB,
61       NETWORK_CONTROL_KEYB << NETWORK_CONTROL_CLIENTOFFSET },
62     { "Joystick 1", NULL, NULL,
63       NETWORK_CONTROL_JOY1,
64       NETWORK_CONTROL_JOY1 << NETWORK_CONTROL_CLIENTOFFSET },
65     { "Joystick 2", NULL, NULL,
66       NETWORK_CONTROL_JOY2,
67       NETWORK_CONTROL_JOY2 << NETWORK_CONTROL_CLIENTOFFSET },
68     { "Devices", NULL, NULL,
69       NETWORK_CONTROL_DEVC,
70       NETWORK_CONTROL_DEVC << NETWORK_CONTROL_CLIENTOFFSET },
71     { "Settings", NULL, NULL,
72       NETWORK_CONTROL_RSRC,
73       NETWORK_CONTROL_RSRC << NETWORK_CONTROL_CLIENTOFFSET },
74     { NULL, NULL, NULL, 0, 0 }
75 };
76 
netplay_update_control_res(GtkWidget * w,gpointer data)77 static void netplay_update_control_res(GtkWidget *w, gpointer data)
78 {
79     unsigned int control, mask;
80 
81     g_return_if_fail(GTK_IS_CHECK_BUTTON(w));
82     g_return_if_fail(data != 0);
83 
84     mask = *((unsigned int *)data);
85     resources_get_int("NetworkControl", (int *)&control);
86     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
87         control |= mask;
88     } else {
89         control &= ~mask;
90     }
91 
92     resources_set_int("NetworkControl", (int)control);
93 }
94 
netplay_update_control_gui(void)95 static void netplay_update_control_gui(void)
96 {
97     int i;
98     unsigned int control;
99 
100     resources_get_int("NetworkControl", (int *)&control);
101     for (i = 0; i < NR_NPCONROLS; i++)
102     {
103         if (control & np_controls[i].s_mask) {
104             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(np_controls[i].s_cb), TRUE);
105         }
106         if (control & np_controls[i].c_mask) {
107             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(np_controls[i].c_cb), TRUE);
108         }
109     }
110 }
111 
netplay_update_resources(void)112 static void netplay_update_resources(void)
113 {
114     const gchar *server_name;
115     const gchar *server_bind_address;
116     char p[256];
117     long port;
118 
119     strncpy(p, gtk_entry_get_text(GTK_ENTRY(np_port)), sizeof(p) - 1);
120     server_name = gtk_entry_get_text(GTK_ENTRY(np_server));
121     server_bind_address = gtk_entry_get_text(GTK_ENTRY(np_server_bind));
122     util_string_to_long(p, NULL, 10, &port);
123     if (port < 1 || port > 0xFFFF) {
124         ui_error("Invalid port number");
125         return;
126     }
127     resources_set_int("NetworkServerPort", (int)port);
128     resources_set_string("NetworkServerName", server_name);
129     resources_set_string("NetworkServerBindAddress", server_bind_address);
130 }
131 
netplay_update_status(void)132 static void netplay_update_status(void)
133 {
134     gchar *text = NULL;
135     const char *server_name;
136     const char *server_bind_address;
137     int port;
138     char st[256];
139 
140     switch(network_get_mode()) {
141         case NETWORK_IDLE:
142             gtk_widget_set_sensitive(GTK_WIDGET(dcb), FALSE);
143             gtk_widget_set_sensitive(GTK_WIDGET(ctrls), TRUE);
144             text = "Idle";
145             break;
146         case NETWORK_SERVER:
147             gtk_widget_set_sensitive(GTK_WIDGET(dcb), TRUE);
148             gtk_widget_set_sensitive(GTK_WIDGET(ctrls), FALSE);
149             text = "Server listening";
150             break;
151         case NETWORK_SERVER_CONNECTED:
152             gtk_widget_set_sensitive(GTK_WIDGET(dcb), TRUE);
153             gtk_widget_set_sensitive(GTK_WIDGET(ctrls), FALSE);
154             text = "Connected server";
155             break;
156         case NETWORK_CLIENT:
157             gtk_widget_set_sensitive(GTK_WIDGET(dcb), TRUE);
158             gtk_widget_set_sensitive(GTK_WIDGET(ctrls), FALSE);
159             text = "Connected client";
160             break;
161         default:
162             break;
163     }
164     gtk_label_set_text(GTK_LABEL(current_mode), text);
165 
166     resources_get_int("NetworkServerPort", &port);
167     resources_get_string("NetworkServerName", &server_name);
168     resources_get_string("NetworkServerBindAddress", &server_bind_address);
169     snprintf(st, 256, "%d", port);
170     gtk_entry_set_text(GTK_ENTRY(np_port), st);
171     if (server_name[0] == 0) {
172         server_name = "127.0.0.1";
173     }
174     gtk_entry_set_text(GTK_ENTRY(np_server), server_name);
175 
176     if (server_bind_address[0] == 0) {
177         server_bind_address = "127.0.0.1";
178     }
179     gtk_entry_set_text(GTK_ENTRY(np_server_bind), server_bind_address);
180 
181     log_message(np_log, "Status: %s, Server: %s, Port: %d; server bind address: %s", text, server_name, port, server_bind_address);
182     netplay_update_control_gui();
183 }
184 
netplay_start_server(GtkWidget * w,gpointer data)185 static void netplay_start_server(GtkWidget *w, gpointer data)
186 {
187     netplay_update_resources();
188     if (network_start_server() < 0) {
189         ui_error("Couldn't start netplay server.");
190     }
191     netplay_update_status();
192     gtk_dialog_response(GTK_DIALOG(netplay_dialog), GTK_RESPONSE_CANCEL);
193 }
194 
netplay_connect(GtkWidget * w,gpointer data)195 static void netplay_connect(GtkWidget *w, gpointer data)
196 {
197     netplay_update_resources();
198     if (network_connect_client() < 0) {
199         ui_error("Couldn't connect client.");
200     }
201     netplay_update_status();
202     gtk_dialog_response(GTK_DIALOG(netplay_dialog), GTK_RESPONSE_CANCEL);
203 }
204 
netplay_disconnect(GtkWidget * w,gpointer data)205 static void netplay_disconnect(GtkWidget *w, gpointer data)
206 {
207     netplay_update_resources();
208     network_disconnect();
209     netplay_update_status();
210     gtk_dialog_response(GTK_DIALOG(netplay_dialog), GTK_RESPONSE_CANCEL);
211 }
212 
build_netplay_dialog(void)213 static GtkWidget *build_netplay_dialog(void)
214 {
215     GtkWidget *d, *f, *b, *hb, *rb, *l, *entry, *h, *v;
216     char *unknown = util_concat("<", "Unknown", ">", NULL);
217     char *connect_to = util_concat("Connect to", " ", NULL);
218     char *current_mode_text = util_concat("Current mode", ": ", NULL);
219     char *ip = util_concat("IP", ": ", NULL);
220     char *port = util_concat("Port", ": ", NULL);
221     char *padding = util_concat("      ", NULL);
222 
223     d = gtk_dialog_new_with_buttons("Netplay", NULL, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, NULL, GTK_RESPONSE_CANCEL, NULL);
224     gtk_window_set_resizable(GTK_WINDOW(d), FALSE);
225 
226     f = gtk_frame_new("Netplay");
227 
228     h = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
229     ctrls = b = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
230 
231     hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
232 
233     /* start row */
234     gtk_box_pack_start(GTK_BOX(b), hb, FALSE, FALSE, 5);
235     gtk_widget_show(hb);
236     hb = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
237 
238     /* button "start server" */
239     rb = gtk_button_new_with_label("Start server");
240     gtk_box_pack_start(GTK_BOX(hb), rb, FALSE, FALSE, 10);
241     g_signal_connect(G_OBJECT(rb), "clicked", G_CALLBACK(netplay_start_server), rb);
242     gtk_widget_set_can_focus(rb, 0);
243     gtk_widget_show(rb);
244 
245     /* label "IP" */
246     l = gtk_label_new(ip);
247     gtk_container_add(GTK_CONTAINER(hb), l);
248     gtk_widget_show(l);
249     /*lib_free(ip);*/
250 
251     /* entry IP server bind address */
252     np_server_bind = entry = gtk_entry_new();
253     gtk_box_pack_start(GTK_BOX(hb), entry, FALSE, FALSE, 10);
254     gtk_widget_set_size_request(entry, 100, -1);
255     gtk_widget_show(entry);
256 
257     /* label "Port" */
258     l = gtk_label_new(port);
259     gtk_container_add(GTK_CONTAINER(hb), l);
260     gtk_widget_show(l);
261     lib_free(port);
262 
263     /* entry port */
264     np_port = entry = gtk_entry_new();
265     gtk_box_pack_start(GTK_BOX(hb), entry, FALSE, FALSE, 10);
266     gtk_widget_set_size_request(entry, 50, -1);
267     gtk_widget_show(entry);
268 
269     /* start row */
270     gtk_box_pack_start(GTK_BOX(b), hb, FALSE, FALSE, 10);
271     gtk_widget_show(hb);
272     hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
273 
274     /* button "connect to server" */
275     rb = gtk_button_new_with_label(connect_to);
276     gtk_box_pack_start(GTK_BOX(hb), rb, FALSE, FALSE, 10);
277     g_signal_connect(G_OBJECT(rb), "clicked", G_CALLBACK(netplay_connect), rb);
278     gtk_widget_set_can_focus(rb, 0);
279     gtk_widget_show(rb);
280     lib_free(connect_to);
281 
282     /* label "IP" */
283     l = gtk_label_new(ip);
284     gtk_container_add(GTK_CONTAINER(hb), l);
285     gtk_widget_show(l);
286     lib_free(ip);
287 
288     /* entry "remote IP" */
289     np_server = entry = gtk_entry_new();
290     gtk_box_pack_start(GTK_BOX(hb), entry, FALSE, FALSE, 10);
291     gtk_widget_set_size_request(entry, 100, -1);
292     gtk_widget_show(entry);
293 
294     /* padding label */
295     l = gtk_label_new(padding);
296     gtk_container_add(GTK_CONTAINER(hb), l);
297     gtk_widget_show(l);
298 
299     l = gtk_label_new(padding);
300     gtk_container_add(GTK_CONTAINER(hb), l);
301     gtk_widget_set_size_request(l, 50, -1);
302     gtk_widget_show(l);
303     lib_free(padding);
304 
305     /* start row */
306     gtk_box_pack_start(GTK_BOX(b), hb, FALSE, FALSE, 5);
307     gtk_widget_show(hb);
308     hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
309 
310     /* start row */
311     gtk_box_pack_start(GTK_BOX(b), hb, FALSE, FALSE, 5);
312     gtk_widget_show(hb);
313 
314     gtk_container_add(GTK_CONTAINER(f), h);
315     gtk_widget_show(h);
316     gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(d))), f, TRUE, TRUE, 10);
317     gtk_widget_show(f);
318     gtk_box_pack_start(GTK_BOX(h), b, FALSE, FALSE, 5);
319     gtk_widget_show(b);
320 
321     /* Control widgets */
322     {
323         GtkWidget *cf, *tmp, *table;
324         int i;
325 
326         cf = gtk_frame_new("Control");
327         h = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
328         v = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
329         gtk_container_add(GTK_CONTAINER(h), v);
330         gtk_widget_show(v);
331         gtk_container_add(GTK_CONTAINER(cf), h);
332         gtk_widget_show(h);
333         gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(d))), cf, FALSE, FALSE, 10);
334         gtk_widget_show(cf);
335 
336         /* content of the "control" frame */
337         table = gtk_grid_new();
338         gtk_grid_set_row_spacing(GTK_GRID(table), 5);
339         gtk_grid_set_column_spacing(GTK_GRID(table), 5);
340         tmp = gtk_label_new("Server");
341         gtk_grid_attach(GTK_GRID(table), tmp, 1, 0, 1, 1);
342         gtk_widget_show(tmp);
343         tmp = gtk_label_new("Client");
344         gtk_grid_attach(GTK_GRID(table), tmp, 2, 0, 1, 1);
345         gtk_widget_show(tmp);
346 
347         /*gtk_table_set_homogeneous(table, TRUE);*/
348 
349         for (i = 0; i < NR_NPCONROLS; i++) {
350             tmp = gtk_label_new(np_controls[i].name);
351             gtk_grid_attach(GTK_GRID(table), tmp, 0, 1 + i, 1, 1);
352             gtk_widget_show(tmp);
353             np_controls[i].s_cb = gtk_check_button_new();
354             gtk_grid_attach(GTK_GRID(table), np_controls[i].s_cb, 1, 1 + i, 1, 1);
355             g_signal_connect(G_OBJECT(np_controls[i].s_cb), "toggled", G_CALLBACK(netplay_update_control_res), (gpointer)&np_controls[i].s_mask);
356             gtk_widget_show(np_controls[i].s_cb);
357             np_controls[i].c_cb = gtk_check_button_new();
358             gtk_grid_attach(GTK_GRID(table), np_controls[i].c_cb, 2, 1 + i, 1, 1);
359             g_signal_connect(G_OBJECT(np_controls[i].c_cb), "toggled", G_CALLBACK(netplay_update_control_res), (gpointer)&np_controls[i].c_mask);
360             gtk_widget_show(np_controls[i].c_cb);
361         }
362         gtk_box_pack_start(GTK_BOX(v), table, FALSE, FALSE, 10);
363         gtk_widget_show(table);
364     }
365 
366     /* disconnect button */
367     dcb = rb = gtk_button_new_with_label("Disconnect");
368     gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(d))), rb, FALSE, FALSE, 10);
369     g_signal_connect(G_OBJECT(rb), "clicked", G_CALLBACK(netplay_disconnect), rb);
370     gtk_widget_set_can_focus(rb, 0);
371     gtk_widget_show(rb);
372 
373     h = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
374     /* label "current mode */
375     l = gtk_label_new(current_mode_text);
376     gtk_container_add(GTK_CONTAINER(h), l);
377     gtk_widget_show(l);
378     /* actual label used for the status */
379     current_mode = gtk_label_new(unknown);
380     lib_free(unknown);
381     lib_free(current_mode_text);
382     gtk_container_add(GTK_CONTAINER(h), current_mode);
383     gtk_widget_show(current_mode);
384 
385     gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(d))), h, FALSE, FALSE, 10);
386     gtk_widget_show(h);
387 
388     netplay_update_status();
389 
390     /* gtk_dialog_close_hides(GTK_DIALOG(d), TRUE); */
391     return d;
392 }
393 
ui_netplay_dialog(GtkWidget * widget,gpointer data)394 void ui_netplay_dialog(GtkWidget *widget, gpointer data)
395 {
396     if (netplay_dialog) {
397         gdk_window_show(gtk_widget_get_window(netplay_dialog));
398         gdk_window_raise(gtk_widget_get_window(netplay_dialog));
399         gtk_widget_show(netplay_dialog);
400         netplay_update_status();
401     } else {
402         np_log = log_open("Netplay");
403         netplay_dialog = build_netplay_dialog();
404         g_signal_connect(G_OBJECT(netplay_dialog), "destroy", G_CALLBACK(gtk_widget_destroyed), &netplay_dialog);
405     }
406     /* FIXME: the old GTK2 code uses ui_popup before and ui_popdown after, this
407               would block the emulator window from input, pause the emulation etc */
408     (void) gtk_dialog_run(GTK_DIALOG(netplay_dialog));
409 }
410 
411 #endif /* HAVE_NETWORK */
412