From c4cfd57d3d6e0532c3b42fee2e98eff6644dd32e Mon Sep 17 00:00:00 2001 From: Dan Barber Date: Sat, 26 Jan 2019 19:25:45 -0500 Subject: [PATCH] MPDClient correctly sequences commands In order to avoid getting disconnected from the server, it is necessary to send an "idle" command once you are connected. This tells the server that the client is going to wait for a status change message from the server. In the meantime if the client needs to send another command, it must first send "noidle", then send the command, then send "idle" once more. If the command results in a status change, the "idle" will return immediately, which must be dealt with and then the "idle" command must be sent once more. Phew! --- Persephone/MPDClient.swift | 67 +++++++++++++++++++++++++++---- Persephone/WindowController.swift | 10 +++-- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/Persephone/MPDClient.swift b/Persephone/MPDClient.swift index 1e09dbf..cb2f9d1 100644 --- a/Persephone/MPDClient.swift +++ b/Persephone/MPDClient.swift @@ -12,40 +12,93 @@ import mpdclient class MPDClient { let HOST = "localhost" let PORT: UInt32 = 6600 + private let connection: OpaquePointer private var status: OpaquePointer? + private let commandQueue = DispatchQueue(label: "commandQueue") + private var commandQueued = false + + enum TransportCommand { + case prevTrack, nextTrack, playPause, stop + } + init?() { guard let connection = mpd_connection_new(HOST, PORT, 0) else { return nil } - mpd_connection_set_keepalive(connection, true) + guard let status = mpd_run_status(connection) + else { return nil } self.connection = connection + self.status = status } deinit { + mpd_status_free(status) mpd_connection_free(connection) } - func getStatus() { - status = mpd_status_begin() + func fetchStatus() { + status = mpd_run_status(connection) + idle() + } + + func getState() { + print(mpd_status_get_state(status)) + idle() } func playPause() { - mpd_run_toggle_pause(connection) + queueCommand(command: .playPause) } func stop() { - mpd_run_stop(connection) + queueCommand(command: .stop) } func prevTrack() { - mpd_run_previous(connection) + queueCommand(command: .prevTrack) } func nextTrack() { - mpd_run_next(connection) + queueCommand(command: .nextTrack) + } + + func queueCommand(command: TransportCommand) { + commandQueued = true + noIdle() + commandQueue.async { [unowned self] in + self.sendCommand(command: command) + self.commandQueued = false + } + idle() + } + + func sendCommand(command: TransportCommand) { + switch command { + case .prevTrack: + mpd_run_previous(connection) + case .nextTrack: + mpd_run_next(connection) + case .stop: + mpd_run_stop(connection) + case .playPause: + mpd_run_toggle_pause(connection) + } + } + + func noIdle() { + mpd_send_noidle(connection) + } + + func idle() { + commandQueue.async { [unowned self] in + mpd_send_idle(self.connection) + mpd_recv_idle(self.connection, true) + + if !self.commandQueued { self.idle() } + } } func getLastErrorMessage() -> String! { diff --git a/Persephone/WindowController.swift b/Persephone/WindowController.swift index 81dc51d..70a6ba3 100644 --- a/Persephone/WindowController.swift +++ b/Persephone/WindowController.swift @@ -20,8 +20,14 @@ class WindowController: NSWindowController { override func windowDidLoad() { super.windowDidLoad() window?.titleVisibility = .hidden + + mpdInit() + } + + func mpdInit() { mpdClient = MPDClient() - mpdClient?.getStatus() + mpdClient?.idle() + // let state = mpdClient?.getState() } @IBAction func handleTransportControl(_ sender: NSSegmentedControl) { @@ -37,8 +43,6 @@ class WindowController: NSWindowController { mpdClient?.stop() case .nextTrack: mpdClient?.nextTrack() - default: - break } } }