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