chiark / gitweb /
progress popup is a transient of the main window
[disorder] / disobedience / client.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2006, 2007 Richard Kettlewell
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; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20 /** @file disobedience/client.c
21  * @brief GLIB integration for @ref lib/eclient.c client
22  */
23
24 #include "disobedience.h"
25
26 /** @brief GSource subclass for disorder_eclient */
27 struct eclient_source {
28   GSource gsource;
29   disorder_eclient *client;
30   time_t last_poll;
31   GPollFD pollfd;
32 };
33
34 /** @brief Called before FDs are polled to choose a timeout.
35  *
36  * We ask for a 3s timeout and every 10s or so we force a dispatch. 
37  */
38 static gboolean gtkclient_prepare(GSource *source,
39                                   gint *timeout) {
40   const struct eclient_source *esource = (struct eclient_source *)source;
41   D(("gtkclient_prepare"));
42   if(time(0) > esource->last_poll + 10)
43     return TRUE;                /* timed out */
44   *timeout = 3000/*milliseconds*/;
45   return FALSE;                 /* please poll */
46 }
47
48 /** @brief Check whether we're ready to dispatch */
49 static gboolean gtkclient_check(GSource *source) {
50   const struct eclient_source *esource = (struct eclient_source *)source;
51   D(("gtkclient_check fd=%d events=%x revents=%x",
52      esource->pollfd.fd, esource->pollfd.events, esource->pollfd.revents));
53   return esource->pollfd.fd != -1 && esource->pollfd.revents != 0;
54 }
55
56 /** @brief Called after poll() (or otherwise) to dispatch an event */
57 static gboolean gtkclient_dispatch(GSource *source,
58                                    GSourceFunc attribute((unused)) callback,
59                                    gpointer attribute((unused)) user_data) {
60   struct eclient_source *esource = (struct eclient_source *)source;
61   unsigned revents,  mode;
62
63   D(("gtkclient_dispatch"));
64   revents = esource->pollfd.revents & esource->pollfd.events;
65   mode = 0;
66   if(revents & (G_IO_IN|G_IO_HUP|G_IO_ERR))
67     mode |= DISORDER_POLL_READ;
68   if(revents & (G_IO_OUT|G_IO_HUP|G_IO_ERR))
69     mode |= DISORDER_POLL_WRITE;
70   time(&esource->last_poll);
71   disorder_eclient_polled(esource->client, mode);
72   return TRUE;                          /* ??? not documented */
73 }
74
75 /** @brief Table of callbacks for GSource subclass */
76 static const GSourceFuncs sourcefuncs = {
77   gtkclient_prepare,
78   gtkclient_check,
79   gtkclient_dispatch,
80   0,
81   0,
82   0,
83 };
84
85 /** @brief Tell the mainloop what we need */
86 static void gtkclient_poll(void *u,
87                            disorder_eclient attribute((unused)) *c,
88                            int fd, unsigned mode) {
89   struct eclient_source *esource = u;
90   GSource *source = u;
91
92   D(("gtkclient_poll fd=%d mode=%x", fd, mode));
93   /* deconfigure the source if currently configured */
94   if(esource->pollfd.fd != -1) {
95     D(("calling g_source_remove_poll"));
96     g_source_remove_poll(source, &esource->pollfd);
97     esource->pollfd.fd = -1;
98     esource->pollfd.events = 0;
99   }
100   /* install new settings */
101   if(fd != -1 && mode) {
102     esource->pollfd.fd = fd;
103     esource->pollfd.events = 0;
104     if(mode & DISORDER_POLL_READ)
105       esource->pollfd.events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
106     if(mode & DISORDER_POLL_WRITE)
107       esource->pollfd.events |= G_IO_OUT | G_IO_ERR;
108     /* reconfigure the source */
109     D(("calling g_source_add_poll"));
110     g_source_add_poll(source, &esource->pollfd);
111   }
112 }
113
114 /** @brief Report a communication-level error
115  *
116  * Any operations still outstanding are automatically replied by the underlying
117  * @ref lib/eclient.c code.
118  */
119 static void gtkclient_comms_error(void attribute((unused)) *u,
120                                   const char *msg) {
121   D(("gtkclient_comms_error %s", msg));
122   menu_update(-1);
123   gtk_label_set_text(GTK_LABEL(report_label), msg);
124 }
125
126 /** @brief Report a protocol-level error
127  *
128  * The error will not be retried.  We offer a callback to the submitter of the
129  * original command and if none is supplied we pop up an error box.
130  */
131 static void gtkclient_protocol_error(void attribute((unused)) *u,
132                                      void *v,
133                                      int code,
134                                      const char *msg) {
135   struct callbackdata *cbd = v;
136
137   D(("gtkclient_protocol_error %s", msg));
138   if(cbd && cbd->onerror)
139     cbd->onerror(cbd, code, msg);
140   else
141     popup_protocol_error(code, msg);
142 }
143
144 /** @brief Report callback from eclient */
145 static void gtkclient_report(void attribute((unused)) *u,
146                              const char *msg) {
147   if(!msg)
148     /* We're idle - clear the report line */
149     gtk_label_set_text(GTK_LABEL(report_label), "");
150   menu_update(-1);
151 }
152
153 /** @brief Repoort an unhandled protocol-level error to the user */
154 void popup_protocol_error(int attribute((unused)) code,
155                           const char *msg) {
156   gtk_label_set_text(GTK_LABEL(report_label), msg);
157   popup_msg(GTK_MESSAGE_ERROR, msg);
158 }
159
160 /** @brief Table of eclient callbacks */
161 static const disorder_eclient_callbacks gtkclient_callbacks = {
162   gtkclient_comms_error,
163   gtkclient_protocol_error,
164   gtkclient_poll,
165   gtkclient_report
166 };
167
168 /** @brief Create a @ref disorder_eclient using the GLib main loop */
169 disorder_eclient *gtkclient(void) {
170   GSource *source;
171   struct eclient_source *esource;
172
173   D(("gtkclient"));
174   source = g_source_new((GSourceFuncs *)&sourcefuncs,
175                         sizeof (struct eclient_source));
176   esource = (struct eclient_source *)source;
177   esource->pollfd.fd = -1;
178   esource->client = disorder_eclient_new(&gtkclient_callbacks, source);
179   if(!esource->client) {
180     g_source_destroy(source);
181     return 0;
182   }
183   g_source_attach(source, 0);
184   return esource->client;
185 }
186
187 /*
188 Local Variables:
189 c-basic-offset:2
190 comment-column:40
191 fill-column:79
192 indent-tabs-mode:nil
193 End:
194 */