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