private async Task <Message> RunLoop(CancellationToken ct) { TaskUtilities.AssertIsOnBackgroundThread(); try { _log.EnterRLoop(_rLoopDepth++); while (!ct.IsCancellationRequested) { var message = await ReceiveMessageAsync(ct); if (message == null) { return(null); } else if (message.IsResponse) { Request request; if (!_requests.TryRemove(message.RequestId, out request)) { throw ProtocolError($"Mismatched response - no request with such ID:", message); } else if (message.Name != ":" + request.MessageName.Substring(1)) { throw ProtocolError($"Mismatched response - message name does not match request '{request.MessageName}':", message); } request.Handle(this, message); continue; } try { switch (message.Name) { case "!End": message.ExpectArguments(1); await _callbacks.Shutdown(message.GetBoolean(0, "rdataSaved")); break; case "!CanceledAll": CancelAll(); break; case "?YesNoCancel": ShowDialog(message, MessageButtons.YesNoCancel, CancellationTokenSource.CreateLinkedTokenSource(ct, _cancelAllCts.Token).Token) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?YesNo": ShowDialog(message, MessageButtons.YesNo, CancellationTokenSource.CreateLinkedTokenSource(ct, _cancelAllCts.Token).Token) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?OkCancel": ShowDialog(message, MessageButtons.OKCancel, CancellationTokenSource.CreateLinkedTokenSource(ct, _cancelAllCts.Token).Token) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?>": ReadConsole(message, CancellationTokenSource.CreateLinkedTokenSource(ct, _cancelAllCts.Token).Token) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "!": case "!!": message.ExpectArguments(1); await _callbacks.WriteConsoleEx( message.GetString(0, "buf", allowNull: true), message.Name.Length == 1?OutputType.Output : OutputType.Error, ct); break; case "!ShowMessage": message.ExpectArguments(1); await _callbacks.ShowMessage(message.GetString(0, "s", allowNull: true), ct); break; case "!+": await _callbacks.Busy(true, ct); break; case "!-": await _callbacks.Busy(false, ct); break; case "!SetWD": _callbacks.DirectoryChanged(); break; case "!Library": await _callbacks.ViewLibrary(ct); break; case "!ShowFile": message.ExpectArguments(3); await _callbacks.ShowFile( message.GetString(0, "file"), message.GetString(1, "tabName"), message.GetBoolean(2, "delete.file")); break; case "!View": message.ExpectArguments(2); _callbacks.ViewObject(message.GetString(0, "x"), message.GetString(1, "title")); break; case "!Plot": await _callbacks.Plot( new PlotMessage( Guid.Parse(message.GetString(0, "device_id")), Guid.Parse(message.GetString(1, "plot_id")), message.GetString(2, "file_path"), message.GetInt32(3, "device_num"), message.GetInt32(4, "active_plot_index"), message.GetInt32(5, "plot_count"), message.Blob), ct); break; case "?Locator": var locatorResult = await _callbacks.Locator(Guid.Parse(message.GetString(0, "device_id")), ct); ct.ThrowIfCancellationRequested(); await RespondAsync(message, ct, locatorResult.Clicked, locatorResult.X, locatorResult.Y); break; case "?PlotDeviceCreate": var plotDeviceResult = await _callbacks.PlotDeviceCreate(Guid.Parse(message.GetString(0, "device_id")), ct); ct.ThrowIfCancellationRequested(); await RespondAsync(message, ct, plotDeviceResult.Width, plotDeviceResult.Height, plotDeviceResult.Resolution); break; case "!PlotDeviceDestroy": await _callbacks.PlotDeviceDestroy(Guid.Parse(message.GetString(0, "device_id")), ct); break; case "!WebBrowser": await _callbacks.WebBrowser(message.GetString(0, "url"), ct); break; case "!PackagesInstalled": _callbacks.PackagesInstalled(); break; case "!PackagesRemoved": _callbacks.PackagesRemoved(); break; case "!FetchFile": var destPath = await _callbacks.SaveFileAsync(message.GetString(0, "file_path"), message.Blob); await _callbacks.WriteConsoleEx(destPath, OutputType.Error, ct); break; default: throw ProtocolError($"Unrecognized host message name:", message); } } catch (OperationCanceledException) when(!ct.IsCancellationRequested) { // Canceled via _cancelAllCts - just move onto the next message. } } } finally { _log.ExitRLoop(--_rLoopDepth); } return(null); }