Release: 1.1
[udpgate.git] / src / ui-gnome.c
1 /* $Id$
2  * Gnome user interface
3  * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "ui-gnome.h"   /* self */
23 #include <glib/gmessages.h>
24 #include "main.h"
25 #include <libgnomeui/gnome-app.h>
26 #include <sys/types.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <libgnomeui/gnome-appbar.h>
31 #include <gtk/gtkbutton.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <gtk/gtkentry.h>
35 #include <gtk/gtktogglebutton.h>
36 #include <string.h>
37 #include <gtk/gtkmain.h>
38 #include <libgnomeui/gnome-app-util.h>
39 #include <gtk/gtklabel.h>
40 #include <gtk/gtkcheckbutton.h>
41
42 #include "network.h"
43 #include "packet.h"
44 #include "static-startup.h"
45
46
47 /* Config: */
48 #define DAEMON_CHECK_INTERVAL_MS 100
49 #define EXTERNAL_STARTUP_CHECK_INTERVAL_MS 1000
50 #define PORT_RANGE_BEGIN 2048
51 #define PORT_RANGE_END 10240
52 #define UI_GNOME_PROBE_TIMEOUT_SEC 10
53
54
55 static GnomeApp *App;
56 static GtkButton *ButtonStart;
57 static GtkButton *ButtonStop;
58 static GtkHBox *PortHBox;
59 static GnomeAppBar *AppBar;
60 static GtkEntry *PortEntry;
61 static GtkEntry *HostIPEntry;
62 static GtkLabel *AutostartLabel;
63 static GtkCheckButton *AutostartCheckButton;
64
65
66 static void state_start_stop(void)
67 {
68 pid_t daemon_pid;
69 gboolean daemon_running;
70 static gboolean last_daemon_running,last_daemon_running_set=FALSE;
71
72         daemon_pid=is_daemon_running();
73         daemon_running=((pid_t)-1!=daemon_pid);
74
75         /* Cache the result; maybe not needed. */
76         if (last_daemon_running_set && last_daemon_running==daemon_running)
77                 return;
78         last_daemon_running=daemon_running;
79         last_daemon_running_set=TRUE;
80
81         gtk_widget_set_sensitive(GTK_WIDGET(ButtonStart),!daemon_running);
82         gtk_widget_set_sensitive(GTK_WIDGET(ButtonStop) , daemon_running);
83         gtk_widget_set_sensitive(GTK_WIDGET(PortHBox)   ,!daemon_running);
84         if (daemon_running)
85                 gnome_appbar_set_status(AppBar,
86                                 udpgate_printf_alloca(_("udpgate daemon running as PID %d."),(int)daemon_pid));
87         else
88                 gnome_appbar_set_status(AppBar,_("No udpgate daemon currently running."));
89 }
90
91 static gboolean daemon_check_timeout_func(gpointer data /* unused */)
92 {
93         if (!App)       /* Quitting? */
94                 return FALSE;   /* stop running */
95
96         state_start_stop();
97         return TRUE;    /* continue running */
98 }
99
100 static gboolean external_startup_check_timeout_func(gpointer data /* unused */)
101 {
102 gboolean state_startup_is_on;
103
104         if (!App)       /* Quitting? */
105                 return FALSE;   /* stop running */
106
107         if (static_startup_query(&state_startup_is_on))
108                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(AutostartCheckButton),state_startup_is_on);
109         return TRUE;    /* continue running */
110 }
111
112 static void buttonstart(void)
113 {
114         if (!optarg_port_set_string(gtk_entry_get_text(PortEntry)))
115                 return;
116         network_start(optarg_port);
117 }
118
119 void on_PortButtonRandom_clicked(GtkButton *button,gpointer user_data)
120 {
121         g_return_if_fail(GTK_IS_BUTTON(button));
122
123         state_start_stop();
124         if ((pid_t)-1!=is_daemon_running())
125                 return;
126         gtk_entry_set_text(PortEntry,
127                         udpgate_printf_alloca("%d",(int)g_random_int_range(PORT_RANGE_BEGIN,PORT_RANGE_END)));
128         buttonstart();
129 }
130
131 void on_AutostartCheckButton_toggled(GtkToggleButton *togglebutton,gpointer user_data)
132 {
133 static gint inside=0;
134
135         g_return_if_fail(GTK_IS_TOGGLE_BUTTON(togglebutton));
136
137         /* Avoid reentrancy to prevent segfault during failed registration.
138          * FIXME: Who knows why? Some forbidden GTK recursion occurs.
139          */
140         g_assert(inside>=0);
141         if (inside)
142                 return;
143         inside++;
144
145         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(AutostartCheckButton)))
146                 static_startup_on();
147         else
148                 static_startup_off();
149         external_startup_check_timeout_func(NULL);      /* data; unused */
150
151         g_assert(inside==1);
152         inside--;
153 }
154
155 void on_ButtonStart_clicked(GtkButton *button,gpointer user_data)
156 {
157         g_return_if_fail(GTK_IS_BUTTON(button));
158
159         buttonstart();
160 }
161
162 void on_ButtonStop_clicked(GtkButton *button,gpointer user_data)
163 {
164         g_return_if_fail(GTK_IS_BUTTON(button));
165
166         network_stop();
167 }
168
169 void on_ButtonHide_clicked(GtkButton *button,gpointer user_data)
170 {
171 GnomeApp *App_local;
172
173         g_return_if_fail(GTK_IS_BUTTON(button));
174
175         /* update config file */
176         optarg_port_set_string(gtk_entry_get_text(PortEntry));
177
178         /* Do not: gtk_main_quit();
179          * as 'App' widget will quit our gtk_main() automatically.
180          */
181         /* Do not: gtk_widget_destroy(App); App=NULL;
182          * as it would race with g_timeout_add()ed function which check
183          * 'if (!App)' first and expect there fully valid tree afterwards.
184          */
185         App_local=App;
186         App=NULL;
187         gtk_widget_destroy(GTK_WIDGET(App_local));
188 }
189
190 static void ui_gnome_network_notify_hostip(guint32 hostip_guint32)
191 {
192         if (!App)       /* Quitting? */
193                 return;
194
195         if (!hostip_guint32) {
196 pid_t daemon_pid;
197
198                 daemon_pid=is_daemon_running();
199                 if ((pid_t)-1==daemon_pid)
200                         gtk_entry_set_text(HostIPEntry,_("(unknown; Start the daemon)"));
201                 else if (getpid()==daemon_pid)
202                         gtk_entry_set_text(HostIPEntry,_("(unknown; detecting...)"));
203                 else
204                         gtk_entry_set_text(HostIPEntry,_("(unknown; Kill the daemon and start your own)"));
205                 }
206         else {
207                 gtk_entry_set_text(HostIPEntry,HOSTIP_GUINT32_TO_STRING(hostip_guint32));
208                 }
209 }
210
211 static guint ui_gnome_g_log_handler_handler_id;
212 static void ui_gnome_g_log_handler(const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
213 {
214 GtkWidget *dialog;
215
216         if (!App)       /* Quitting? */
217                 return;
218
219         /**/ if (log_level & G_LOG_LEVEL_ERROR)
220                 dialog=gnome_app_error(App,message);
221         else if (log_level & (G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING))
222                 dialog=gnome_app_warning(App,message);
223         else
224                 dialog=gnome_app_message(App,message);
225
226         /* Do not set it modal as ... who knows. At least in the fully static
227          * build during on_AutostartCheckButton_toggled() we get reported
228          * the messages twice and application immediately gtk_main_quit()
229          * even the second (toplevel) gtk_main().
230          */
231 #if 0
232         gtk_window_set_modal(GTK_WINDOW(dialog),TRUE);
233         g_signal_connect((gpointer)dialog,"close",G_CALLBACK(gtk_main_quit),NULL);
234         gtk_main();
235         /* 'dialog' gets destroyed automatically */
236 #endif
237 }
238
239 static void ui_gnome_interactive(void)
240 {
241         probe_timeout_sec_max=UI_GNOME_PROBE_TIMEOUT_SEC;
242         gtk_main();
243         network_notify_hostip=NULL;
244         g_log_remove_handler(
245                         G_LOG_DOMAIN,   /* log_domain; "Captive" */
246                         ui_gnome_g_log_handler_handler_id);     /* handler_id */
247         network_detach();
248 }
249
250 /* of "ui-gnome-interface.h": */
251 GtkWidget *create_App(void);
252 /* of "ui-gnome-support.h": */
253 GtkWidget *lookup_widget(GtkWidget *widget,const gchar *widget_name);
254
255 gboolean ui_gnome_init(void)
256 {
257         App=GNOME_APP(create_App());
258
259         ButtonStart=GTK_BUTTON(lookup_widget(GTK_WIDGET(App),"ButtonStart"));
260         ButtonStop=GTK_BUTTON(lookup_widget(GTK_WIDGET(App),"ButtonStop"));
261         PortHBox=GTK_HBOX(lookup_widget(GTK_WIDGET(App),"PortHBox"));
262         AppBar=GNOME_APPBAR(lookup_widget(GTK_WIDGET(App),"AppBar"));
263         PortEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"PortEntry"));
264         HostIPEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"HostIPEntry"));
265         AutostartLabel=GTK_LABEL(lookup_widget(GTK_WIDGET(App),"AutostartLabel"));
266         AutostartCheckButton=GTK_CHECK_BUTTON(lookup_widget(GTK_WIDGET(App),"AutostartCheckButton"));
267
268         /* ui_gnome_g_log_handler() needs 'App'. */
269         ui_gnome_g_log_handler_handler_id=g_log_set_handler(
270                         G_LOG_DOMAIN,   /* log_domain; "Captive" */
271                         (G_LOG_LEVEL_MASK|G_LOG_FLAG_FATAL)&~(0
272                                         |G_LOG_LEVEL_MESSAGE
273                                         |G_LOG_LEVEL_INFO
274                                         |G_LOG_LEVEL_DEBUG),    /* log_levels */
275                         ui_gnome_g_log_handler, /* log_func */
276                         NULL);  /* user_data */
277
278         ui_gnome_network_notify_hostip(0);
279         gtk_entry_set_text(PortEntry,udpgate_printf_alloca("%d",(int)optarg_port));
280         if (!static_startup_supported()) {
281                 gtk_widget_set_sensitive(GTK_WIDGET(AutostartLabel),FALSE);
282                 gtk_widget_set_sensitive(GTK_WIDGET(AutostartCheckButton),FALSE);
283                 }
284         if (!static_startup_query(NULL))
285                 gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(AutostartCheckButton),TRUE);
286         daemon_check_timeout_func(NULL);        /* data; unused */
287         external_startup_check_timeout_func(NULL);      /* data; unused */
288
289         gtk_widget_show_all(GTK_WIDGET(App));
290         g_timeout_add(
291                         DAEMON_CHECK_INTERVAL_MS,       /* interval */
292                         daemon_check_timeout_func,      /* function */
293                         NULL);  /* data; unused */
294         g_timeout_add(
295                         EXTERNAL_STARTUP_CHECK_INTERVAL_MS,     /* interval */
296                         external_startup_check_timeout_func,    /* function */
297                         NULL);  /* data; unused */
298
299         network_notify_hostip=ui_gnome_network_notify_hostip;
300
301         ui_interactive=ui_gnome_interactive;
302
303         return TRUE;
304 }