chiark / gitweb /
Merge from disorder.userman
[disorder] / disobedience / client.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2006, 2007, 2008 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   if(!login_window)
72     disorder_eclient_polled(esource->client, mode);
73   return TRUE;                          /* ??? not documented */
74 }
75
76 /** @brief Table of callbacks for GSource subclass */
77 static const GSourceFuncs sourcefuncs = {
78   gtkclient_prepare,
79   gtkclient_check,
80   gtkclient_dispatch,
81   0,
82   0,
83   0,
84 };
85
86 /** @brief Tell the mainloop what we need */
87 static void gtkclient_poll(void *u,
88                            disorder_eclient attribute((unused)) *c,
89                            int fd, unsigned mode) {
90   struct eclient_source *esource = u;
91   GSource *source = u;
92
93   D(("gtkclient_poll fd=%d mode=%x", fd, mode));
94   /* deconfigure the source if currently configured */
95   if(esource->pollfd.fd != -1) {
96     D(("calling g_source_remove_poll"));
97     g_source_remove_poll(source, &esource->pollfd);
98     esource->pollfd.fd = -1;
99     esource->pollfd.events = 0;
100   }
101   /* install new settings */
102   if(fd != -1 && mode) {
103     esource->pollfd.fd = fd;
104     esource->pollfd.events = 0;
105     if(mode & DISORDER_POLL_READ)
106       esource->pollfd.events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
107     if(mode & DISORDER_POLL_WRITE)
108       esource->pollfd.events |= G_IO_OUT | G_IO_ERR;
109     /* reconfigure the source */
110     D(("calling g_source_add_poll"));
111     g_source_add_poll(source, &esource->pollfd);
112   }
113 }
114
115 /** @brief Report a communication-level error
116  *
117  * Any operations still outstanding are automatically replied by the underlying
118  * @ref lib/eclient.c code.
119  */
120 static void gtkclient_comms_error(void attribute((unused)) *u,
121                                   const char *msg) {
122   D(("gtkclient_comms_error %s", msg));
123   menu_update(-1);
124   gtk_label_set_text(GTK_LABEL(report_label), msg);
125 }
126
127 /** @brief Report a protocol-level error
128  *
129  * The error will not be retried.  We offer a callback to the submitter of the
130  * original command and if none is supplied we pop up an error box.
131  */
132 static void gtkclient_protocol_error(void attribute((unused)) *u,
133                                      void *v,
134                                      int code,
135                                      const char *msg) {
136   struct callbackdata *cbd = v;
137
138   D(("gtkclient_protocol_error %s", msg));
139   if(cbd && cbd->onerror)
140     cbd->onerror(cbd, code, msg);
141   else
142     popup_protocol_error(code, msg);
143 }
144
145 /** @brief Report callback from eclient */
146 static void gtkclient_report(void attribute((unused)) *u,
147                              const char *msg) {
148   if(!msg)
149     /* We're idle - clear the report line */
150     gtk_label_set_text(GTK_LABEL(report_label), "");
151   menu_update(-1);
152 }
153
154 /** @brief Repoort an unhandled protocol-level error to the user */
155 void popup_protocol_error(int attribute((unused)) code,
156                           const char *msg) {
157   gtk_label_set_text(GTK_LABEL(report_label), msg);
158   popup_msg(GTK_MESSAGE_ERROR, msg);
159 }
160
161 /** @brief Table of eclient callbacks */
162 static const disorder_eclient_callbacks gtkclient_callbacks = {
163   gtkclient_comms_error,
164   gtkclient_protocol_error,
165   gtkclient_poll,
166   gtkclient_report
167 };
168
169 /** @brief Create a @ref disorder_eclient using the GLib main loop */
170 disorder_eclient *gtkclient(void) {
171   GSource *source;
172   struct eclient_source *esource;
173
174   D(("gtkclient"));
175   source = g_source_new((GSourceFuncs *)&sourcefuncs,
176                         sizeof (struct eclient_source));
177   esource = (struct eclient_source *)source;
178   esource->pollfd.fd = -1;
179   esource->client = disorder_eclient_new(&gtkclient_callbacks, source);
180   if(!esource->client) {
181     g_source_destroy(source);
182     return 0;
183   }
184   g_source_attach(source, 0);
185   return esource->client;
186 }
187
188 /*
189 Local Variables:
190 c-basic-offset:2
191 comment-column:40
192 fill-column:79
193 indent-tabs-mode:nil
194 End:
195 */