private void SockProc(Socket sock) { try { HaProtoImpl.Send(sock, HaProtoImpl.Opcode.GETDB, new HaProtoImpl.GETDB()); while (true) { byte[] buf; HaProtoImpl.Opcode type = HaProtoImpl.Receive(sock, out buf); bool foo; Dispatcher.Invoke(delegate() { try { internalChanging = true; switch (type) { case HaProtoImpl.Opcode.GETDB: throw new NotSupportedException(); case HaProtoImpl.Opcode.SETDB: HaProtoImpl.SETDB setdb = HaProtoImpl.SETDB.Parse(buf); data.ServerDataSource = setdb.dataSource; BringSelectedItemIntoView(); break; case HaProtoImpl.Opcode.LIBRARY_ADD: case HaProtoImpl.Opcode.LIBRARY_REMOVE: case HaProtoImpl.Opcode.LIBRARY_RESET: // No selection eye candy for library because it's not worth the time it will take to implement HaProtoImpl.ApplyPacketToDatabase(type, buf, data.ServerDataSource, out foo); // For some reason this doesn't happen automatically and I'm too tired to search why // mediaBrowser.InvalidateProperty(MediaBrowser.SelectedDataProperty); // For some reason, .NET sucks and InvalidateProperty doesn't do what it says it does... // BindingOperations.GetBindingExpression(mediaBrowser, MediaBrowser.SelectedDataProperty).UpdateTarget(); // For some reason, .NET sucks and GetBindingExpression only works with single bindings... BindingOperations.GetMultiBindingExpression(mediaBrowser, MediaBrowser.SelectedDataProperty).UpdateTarget(); break; case HaProtoImpl.Opcode.ADD: case HaProtoImpl.Opcode.REMOVE: case HaProtoImpl.Opcode.CLEAR: case HaProtoImpl.Opcode.SETSONG: case HaProtoImpl.Opcode.ADDPL: case HaProtoImpl.Opcode.DELPL: case HaProtoImpl.Opcode.RENPL: case HaProtoImpl.Opcode.REORDER: case HaProtoImpl.Opcode.INJECT: // These opcodes might change the list selection, we need to back it up // However, only do this for small selections - if the selection is large, we would rather take the UI // inconsistency over multiple seconds of delay List <PlaylistItem> selectedItems = data.SelectedPlaylistItems.ToList(); IInputElement element = FocusManager.GetFocusedElement(this); PlaylistItem focusItem = null; if (element is FrameworkElement && ((FrameworkElement)element).DataContext is PlaylistItem) { focusItem = (PlaylistItem)((FrameworkElement)element).DataContext; } int firstIndex; if (selectedItems.Count == 0) { firstIndex = 0; } else if (selectedItems.Count < EyeCandyDisableThreshold) { firstIndex = selectedItems.Select(x => data.SelectedPlaylist.PlaylistItems.IndexOf(x)).Min(); } else { firstIndex = data.SelectedPlaylist.PlaylistItems.IndexOf(selectedItems[0]); } HaProtoImpl.ApplyPacketToDatabase(type, buf, data.ServerDataSource, out foo); // Check which items still exist List <PlaylistItem> newSelectedItems = new List <PlaylistItem>(); foreach (PlaylistItem item in selectedItems) { PlaylistItem newItem; if (data.SelectedPlaylist.PlaylistItems.FastTryGet(item.UID, out newItem)) { // We could have added item and it would work, but for extra safety lets add the new item newSelectedItems.Add(newItem); } } // Special case for deletion if (selectedItems.Count > 0 && newSelectedItems.Count == 0) { int selectedIndex; if (firstIndex < data.SelectedPlaylist.PlaylistItems.Count) { selectedIndex = firstIndex; } else if (data.SelectedPlaylist.PlaylistItems.Count > 0) { selectedIndex = data.SelectedPlaylist.PlaylistItems.Count - 1; } else { selectedIndex = -1; } if (selectedIndex == -1) { focusItem = null; } else { newSelectedItems.Add(focusItem = data.SelectedPlaylist.PlaylistItems[selectedIndex]); } } if (newSelectedItems.Count < EyeCandyDisableThreshold && !IsSelectionEqual(data.SelectedPlaylistItems, newSelectedItems)) { data.SelectedPlaylistItems.Clear(); newSelectedItems.ForEach(x => data.SelectedPlaylistItems.Add(x)); if (focusItem != null) { SetFocusItem(focusItem); } } break; case HaProtoImpl.Opcode.SETMOVE: HaProtoImpl.ApplyPacketToDatabase(type, buf, data.ServerDataSource, out foo); break; case HaProtoImpl.Opcode.SKIP: // We should not be receiving SKIP packets, the server should translate them to SETSONG throw new NotSupportedException(); case HaProtoImpl.Opcode.SETVOL: data.ServerDataSource.Volume = HaProtoImpl.SETVOL.Parse(buf).volume; break; case HaProtoImpl.Opcode.SEEK: HaProtoImpl.SEEK seek = HaProtoImpl.SEEK.Parse(buf); data.ServerDataSource.Position = seek.pos; data.ServerDataSource.Maximum = seek.max; break; case HaProtoImpl.Opcode.SETPLAYING: data.ServerDataSource.Playing = HaProtoImpl.SETPLAYING.Parse(buf).playing; break; default: throw new NotSupportedException(); } } finally { internalChanging = false; } }); } } catch (Exception e) { try { // Try to close the socket, if it's already closed than w/e sock.Shutdown(SocketShutdown.Both); sock.Close(); } catch { } try { Dispatcher.Invoke(delegate() { SetEnabled(false); }); } catch { } } }
private void Proc() { try { log(string.Format("{0} Connected", id)); while (true) { bool announceIndexChanges = false; byte[] data; HaProtoImpl.Opcode type = HaProtoImpl.Receive(s, out data); log(string.Format("{0}: {1}", id, type.ToString())); HaProtoImpl.HaProtoPacket packet; switch (type) { case HaProtoImpl.Opcode.GETDB: lock (mainForm.DataSource.Lock) { HaProtoImpl.Send(s, HaProtoImpl.Opcode.SETDB, new HaProtoImpl.SETDB() { dataSource = mainForm.DataSource }); } break; case HaProtoImpl.Opcode.SETDB: case HaProtoImpl.Opcode.LIBRARY_ADD: case HaProtoImpl.Opcode.LIBRARY_REMOVE: case HaProtoImpl.Opcode.LIBRARY_RESET: throw new NotSupportedException(); case HaProtoImpl.Opcode.ADD: case HaProtoImpl.Opcode.REMOVE: case HaProtoImpl.Opcode.CLEAR: case HaProtoImpl.Opcode.SETSONG: case HaProtoImpl.Opcode.ADDPL: case HaProtoImpl.Opcode.DELPL: case HaProtoImpl.Opcode.RENPL: case HaProtoImpl.Opcode.SETMOVE: case HaProtoImpl.Opcode.REORDER: case HaProtoImpl.Opcode.INJECT: packet = HaProtoImpl.ApplyPacketToDatabase(type, data, mainForm.DataSource, out announceIndexChanges); mainForm.BroadcastMessage(type, packet); break; case HaProtoImpl.Opcode.SKIP: long uid; lock (mainForm.DataSource.Lock) { mainForm.DataSource.CurrentItem = mainForm.Mover.Next(); uid = mainForm.DataSource.CurrentItem == null ? -1 : mainForm.DataSource.CurrentItem.UID; } mainForm.BroadcastMessage(HaProtoImpl.Opcode.SETSONG, new HaProtoImpl.SETSONG() { uid = uid }); announceIndexChanges = true; break; case HaProtoImpl.Opcode.SETVOL: HaProtoImpl.SETVOL setvol = HaProtoImpl.SETVOL.Parse(data); lock (mainForm.DataSource.Lock) { mainForm.DataSource.Volume = setvol.volume; } mainForm.SetVolume(setvol.volume); mainForm.BroadcastMessage(type, setvol, this); break; case HaProtoImpl.Opcode.SEEK: HaProtoImpl.SEEK seek = HaProtoImpl.SEEK.Parse(data); lock (mainForm.DataSource.Lock) { mainForm.DataSource.Position = seek.pos; seek.max = mainForm.DataSource.Maximum; } mainForm.SetPosition(seek.pos); mainForm.BroadcastMessage(type, seek, this); break; case HaProtoImpl.Opcode.SETPLAYING: HaProtoImpl.SETPLAYING setplaying = HaProtoImpl.SETPLAYING.Parse(data); lock (mainForm.DataSource.Lock) { mainForm.DataSource.Playing = setplaying.playing; } mainForm.SetPlaying(setplaying.playing); mainForm.BroadcastMessage(type, setplaying); break; default: throw new NotSupportedException(); } if (announceIndexChanges) { mainForm.AnnounceIndexChange(); } } } catch (Exception e) { if (e is SocketException && ((SocketException)e).ErrorCode == 0) { log(string.Format("{0} exited normally", id)); } else { log(string.Format("Exception in {0} : {1}", id, Program.GetErrorException(e))); } try { // Try to close the socket, if it's already closed then w/e s.Close(); } catch { } mainForm.OnThreadExit(this); return; } }