chiark / gitweb /
Loosen playlist command rights.
[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   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   gtk_label_set_text(GTK_LABEL(report_label), msg);
123 }
124
125 /** @brief Report a protocol-level error
126  *
127  * The error will not be retried.  We offer a callback to the submitter of the
128  * original command and if none is supplied we drop the error message in the
129  * status bar.
130  */
131 static void gtkclient_protocol_error(void attribute((unused)) *u,
132                                      void attribute((unused)) *v,
133                                      int attribute((unused)) code,
134                                      const char *msg) {
135   D(("gtkclient_protocol_error %s", msg));
136   gtk_label_set_text(GTK_LABEL(report_label), msg);
137 }
138
139 /** @brief Report callback from eclient */
140 static void gtkclient_report(void attribute((unused)) *u,
141                              const char *msg) {
142   if(!msg)
143     /* We're idle - clear the report line */
144     gtk_label_set_text(GTK_LABEL(report_label), "");
145 }
146
147 /** @brief Repoort an unhandled protocol-level error to the user */
148 void popup_protocol_error(int attribute((unused)) code,
149                           const char *msg) {
150   gtk_label_set_text(GTK_LABEL(report_label), msg);
151   popup_msg(GTK_MESSAGE_ERROR, msg);
152 }
153
154 /** @brief Table of eclient callbacks */
155 static const disorder_eclient_callbacks gtkclient_callbacks = {
156   gtkclient_comms_error,
157   gtkclient_protocol_error,
158   gtkclient_poll,
159   gtkclient_report
160 };
161
162 /** @brief Create a @ref disorder_eclient using the GLib main loop */
163 disorder_eclient *gtkclient(void) {
164   GSource *source;
165   struct eclient_source *esource;
166
167   D(("gtkclient"));
168   source = g_source_new((GSourceFuncs *)&sourcefuncs,
169                         sizeof (struct eclient_source));
170   esource = (struct eclient_source *)source;
171   esource->pollfd.fd = -1;
172   esource->client = disorder_eclient_new(&gtkclient_callbacks, source);
173   if(!esource->client) {
174     g_source_destroy(source);
175     return 0;
176   }
177   g_source_attach(source, 0);
178   return esource->client;
179 }
180
181 /*
182 Local Variables:
183 c-basic-offset:2
184 comment-column:40
185 fill-column:79
186 indent-tabs-mode:nil
187 End:
188 */