2019-07-04 17:14:17 +02:00
|
|
|
using Ryujinx.Common;
|
2019-06-17 01:08:32 +02:00
|
|
|
using Ryujinx.HLE.HOS.Ipc;
|
2019-06-27 14:05:30 +02:00
|
|
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
|
|
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
2019-06-17 01:08:32 +02:00
|
|
|
using Ryujinx.HLE.Utilities;
|
2019-06-27 14:05:30 +02:00
|
|
|
using System;
|
2019-06-17 01:08:32 +02:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
2019-07-04 17:14:17 +02:00
|
|
|
using static Ryujinx.HLE.HOS.ErrorCode;
|
|
|
|
|
2019-06-17 01:08:32 +02:00
|
|
|
namespace Ryujinx.HLE.HOS.Services.Friend
|
|
|
|
{
|
2019-07-04 17:14:17 +02:00
|
|
|
class INotificationService : IpcService, IDisposable
|
2019-06-17 01:08:32 +02:00
|
|
|
{
|
2019-07-04 17:14:17 +02:00
|
|
|
private readonly UInt128 _userId;
|
|
|
|
private readonly FriendServicePermissionLevel _permissionLevel;
|
|
|
|
|
|
|
|
private readonly object _lock = new object();
|
2019-06-17 01:08:32 +02:00
|
|
|
|
2019-06-27 14:05:30 +02:00
|
|
|
private KEvent _notificationEvent;
|
|
|
|
private int _notificationEventHandle = 0;
|
|
|
|
|
2019-07-04 17:14:17 +02:00
|
|
|
private LinkedList<NotificationInfo> _notifications;
|
|
|
|
|
|
|
|
private bool _hasNewFriendRequest;
|
|
|
|
private bool _hasFriendListUpdate;
|
|
|
|
|
|
|
|
public INotificationService(ServiceCtx context, UInt128 userId, FriendServicePermissionLevel permissionLevel)
|
2019-06-17 01:08:32 +02:00
|
|
|
{
|
2019-07-04 17:14:17 +02:00
|
|
|
_userId = userId;
|
|
|
|
_permissionLevel = permissionLevel;
|
|
|
|
_notifications = new LinkedList<NotificationInfo>();
|
|
|
|
_notificationEvent = new KEvent(context.Device.System);
|
|
|
|
|
|
|
|
_hasNewFriendRequest = false;
|
|
|
|
_hasFriendListUpdate = false;
|
|
|
|
|
|
|
|
NotificationEventHandler.Instance.RegisterNotificationService(this);
|
2019-06-17 01:08:32 +02:00
|
|
|
}
|
2019-06-27 14:05:30 +02:00
|
|
|
|
2019-07-12 03:13:43 +02:00
|
|
|
[Command(0)] //2.0.0+
|
2019-07-04 17:14:17 +02:00
|
|
|
// nn::friends::detail::ipc::INotificationService::GetEvent() -> handle<copy>
|
2019-06-27 14:05:30 +02:00
|
|
|
public long GetEvent(ServiceCtx context)
|
|
|
|
{
|
|
|
|
if (_notificationEventHandle == 0)
|
|
|
|
{
|
|
|
|
if (context.Process.HandleTable.GenerateHandle(_notificationEvent.ReadableEvent, out _notificationEventHandle) != KernelResult.Success)
|
|
|
|
{
|
|
|
|
throw new InvalidOperationException("Out of handles!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_notificationEventHandle);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-07-04 17:14:17 +02:00
|
|
|
|
2019-07-12 03:13:43 +02:00
|
|
|
[Command(1)] //2.0.0+
|
2019-07-04 17:14:17 +02:00
|
|
|
// nn::friends::detail::ipc::INotificationService::Clear()
|
|
|
|
public long Clear(ServiceCtx context)
|
|
|
|
{
|
|
|
|
lock (_lock)
|
|
|
|
{
|
|
|
|
_hasNewFriendRequest = false;
|
|
|
|
_hasFriendListUpdate = false;
|
|
|
|
|
|
|
|
_notifications.Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-12 03:13:43 +02:00
|
|
|
[Command(2)] // 2.0.0+
|
2019-07-04 17:14:17 +02:00
|
|
|
// nn::friends::detail::ipc::INotificationService::Pop() -> nn::friends::detail::ipc::SizedNotificationInfo
|
|
|
|
public long Pop(ServiceCtx context)
|
|
|
|
{
|
|
|
|
lock (_lock)
|
|
|
|
{
|
|
|
|
if (_notifications.Count >= 1)
|
|
|
|
{
|
|
|
|
NotificationInfo notificationInfo = _notifications.First.Value;
|
|
|
|
_notifications.RemoveFirst();
|
|
|
|
|
|
|
|
if (notificationInfo.Type == NotificationEventType.FriendListUpdate)
|
|
|
|
{
|
|
|
|
_hasFriendListUpdate = false;
|
|
|
|
}
|
|
|
|
else if (notificationInfo.Type == NotificationEventType.NewFriendRequest)
|
|
|
|
{
|
|
|
|
_hasNewFriendRequest = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
context.ResponseData.WriteStruct(notificationInfo);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return MakeError(ErrorModule.Friends, FriendError.NotificationQueueEmpty);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void SignalFriendListUpdate(UInt128 targetId)
|
|
|
|
{
|
|
|
|
lock (_lock)
|
|
|
|
{
|
|
|
|
if (_userId == targetId)
|
|
|
|
{
|
|
|
|
if (!_hasFriendListUpdate)
|
|
|
|
{
|
|
|
|
NotificationInfo friendListNotification = new NotificationInfo();
|
|
|
|
|
|
|
|
if (_notifications.Count != 0)
|
|
|
|
{
|
|
|
|
friendListNotification = _notifications.First.Value;
|
|
|
|
_notifications.RemoveFirst();
|
|
|
|
}
|
|
|
|
|
|
|
|
friendListNotification.Type = NotificationEventType.FriendListUpdate;
|
|
|
|
_hasFriendListUpdate = true;
|
|
|
|
|
|
|
|
if (_hasNewFriendRequest)
|
|
|
|
{
|
|
|
|
NotificationInfo newFriendRequestNotification = new NotificationInfo();
|
|
|
|
|
|
|
|
if (_notifications.Count != 0)
|
|
|
|
{
|
|
|
|
newFriendRequestNotification = _notifications.First.Value;
|
|
|
|
_notifications.RemoveFirst();
|
|
|
|
}
|
|
|
|
|
|
|
|
newFriendRequestNotification.Type = NotificationEventType.NewFriendRequest;
|
|
|
|
_notifications.AddFirst(newFriendRequestNotification);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We defer this to make sure we are on top of the queue.
|
|
|
|
_notifications.AddFirst(friendListNotification);
|
|
|
|
}
|
|
|
|
|
|
|
|
_notificationEvent.ReadableEvent.Signal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void SignalNewFriendRequest(UInt128 targetId)
|
|
|
|
{
|
|
|
|
lock (_lock)
|
|
|
|
{
|
|
|
|
if ((_permissionLevel & FriendServicePermissionLevel.OverlayMask) != 0 && _userId == targetId)
|
|
|
|
{
|
|
|
|
if (!_hasNewFriendRequest)
|
|
|
|
{
|
|
|
|
if (_notifications.Count == 100)
|
|
|
|
{
|
|
|
|
SignalFriendListUpdate(targetId);
|
|
|
|
}
|
|
|
|
|
|
|
|
NotificationInfo newFriendRequestNotification = new NotificationInfo
|
|
|
|
{
|
|
|
|
Type = NotificationEventType.NewFriendRequest
|
|
|
|
};
|
|
|
|
|
|
|
|
_notifications.AddLast(newFriendRequestNotification);
|
|
|
|
_hasNewFriendRequest = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_notificationEvent.ReadableEvent.Signal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
NotificationEventHandler.Instance.UnregisterNotificationService(this);
|
|
|
|
}
|
2019-06-17 01:08:32 +02:00
|
|
|
}
|
|
|
|
}
|