1 /* ncmpc (Ncurses MPD Client)
2 (c) 2004-2020 The Music Player Daemon Project
3 Project homepage: http://musicpd.org
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 - Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
20 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "gidle.hxx"
30
31 #include <mpd/async.h>
32 #include <mpd/parser.h>
33
34 #include <assert.h>
35 #include <string.h>
36
MpdIdleSource(EventLoop & event_loop,struct mpd_connection & _connection,MpdIdleHandler & _handler)37 MpdIdleSource::MpdIdleSource(EventLoop &event_loop,
38 struct mpd_connection &_connection,
39 MpdIdleHandler &_handler) noexcept
40 :connection(&_connection),
41 async(mpd_connection_get_async(connection)),
42 parser(mpd_parser_new()),
43 event(event_loop, BIND_THIS_METHOD(OnSocketReady),
44 SocketDescriptor(mpd_async_get_fd(async))),
45 handler(_handler)
46 {
47 /* TODO check parser!=nullptr */
48 }
49
~MpdIdleSource()50 MpdIdleSource::~MpdIdleSource() noexcept
51 {
52 mpd_parser_free(parser);
53
54 }
55
56 void
InvokeAsyncError()57 MpdIdleSource::InvokeAsyncError() noexcept
58 {
59 InvokeError(mpd_async_get_error(async),
60 (enum mpd_server_error)0,
61 mpd_async_get_error_message(async));
62 }
63
64 bool
Feed(char * line)65 MpdIdleSource::Feed(char *line) noexcept
66 {
67 enum mpd_parser_result result;
68
69 result = mpd_parser_feed(parser, line);
70 switch (result) {
71 case MPD_PARSER_MALFORMED:
72 event.Cancel();
73 io_events = 0;
74
75 InvokeError(MPD_ERROR_MALFORMED,
76 (enum mpd_server_error)0,
77 "Malformed MPD response");
78 return false;
79
80 case MPD_PARSER_SUCCESS:
81 event.Cancel();
82 io_events = 0;
83
84 InvokeCallback();
85 return false;
86
87 case MPD_PARSER_ERROR:
88 event.Cancel();
89 io_events = 0;
90
91 InvokeError(MPD_ERROR_SERVER,
92 mpd_parser_get_server_error(parser),
93 mpd_parser_get_message(parser));
94 return false;
95
96 case MPD_PARSER_PAIR:
97 if (strcmp(mpd_parser_get_name(parser),
98 "changed") == 0)
99 idle_events |=
100 mpd_idle_name_parse(mpd_parser_get_value(parser));
101
102 break;
103 }
104
105 return true;
106 }
107
108 bool
Receive()109 MpdIdleSource::Receive() noexcept
110 {
111 char *line;
112 while ((line = mpd_async_recv_line(async)) != nullptr) {
113 if (!Feed(line))
114 return false;
115 }
116
117 if (mpd_async_get_error(async) != MPD_ERROR_SUCCESS) {
118 event.Cancel();
119 io_events = 0;
120
121 InvokeAsyncError();
122 return false;
123 }
124
125 return true;
126 }
127
128 void
OnSocketReady(unsigned flags)129 MpdIdleSource::OnSocketReady(unsigned flags) noexcept
130 {
131 unsigned events = 0;
132 if (flags & SocketEvent::READ)
133 events |= MPD_ASYNC_EVENT_READ;
134 if (flags & SocketEvent::WRITE)
135 events |= MPD_ASYNC_EVENT_WRITE;
136
137 if (!mpd_async_io(async, (enum mpd_async_event)events)) {
138 event.Cancel();
139 io_events = 0;
140
141 InvokeAsyncError();
142 return;
143 }
144
145 if (flags & SocketEvent::READ)
146 if (!Receive())
147 return;
148
149 UpdateSocket();
150 }
151
152 void
UpdateSocket()153 MpdIdleSource::UpdateSocket() noexcept
154 {
155 enum mpd_async_event events = mpd_async_events(async);
156 if (events == io_events)
157 return;
158
159 unsigned flags = 0;
160 if (events & MPD_ASYNC_EVENT_READ)
161 flags |= SocketEvent::READ;
162
163 if (events & MPD_ASYNC_EVENT_WRITE)
164 flags |= SocketEvent::WRITE;
165
166 event.Schedule(flags);
167
168 io_events = events;
169 }
170
171 bool
Enter()172 MpdIdleSource::Enter() noexcept
173 {
174 assert(io_events == 0);
175
176 idle_events = 0;
177
178 if (!mpd_async_send_command(async, "idle", nullptr)) {
179 InvokeAsyncError();
180 return false;
181 }
182
183 UpdateSocket();
184 return true;
185 }
186
187 void
Leave()188 MpdIdleSource::Leave() noexcept
189 {
190 if (io_events == 0)
191 /* already left, callback was invoked */
192 return;
193
194 event.Cancel();
195 io_events = 0;
196
197 enum mpd_idle events = idle_events == 0
198 ? mpd_run_noidle(connection)
199 : mpd_recv_idle(connection, false);
200
201 if (events == 0 &&
202 mpd_connection_get_error(connection) != MPD_ERROR_SUCCESS) {
203 enum mpd_error error =
204 mpd_connection_get_error(connection);
205 enum mpd_server_error server_error =
206 error == MPD_ERROR_SERVER
207 ? mpd_connection_get_server_error(connection)
208 : (enum mpd_server_error)0;
209
210 InvokeError(error, server_error,
211 mpd_connection_get_error_message(connection));
212 return;
213 }
214
215 idle_events |= events;
216 InvokeCallback();
217 }
218