BuildRequires: pkgconfig(stt-engine)
BuildRequires: pkgconfig(tts-engine)
BuildRequires: pkgconfig(chromium-efl)
+BuildRequires: pkgconfig(libsessiond)
%if "%{profile}" == "tv"
BuildRequires: pkgconfig(trustzone-nwd)
%else
BuildRequires: pkgconfig(stt-engine)
BuildRequires: pkgconfig(tts-engine)
BuildRequires: pkgconfig(chromium-efl)
+BuildRequires: pkgconfig(libsessiond)
%if "%{profile}" == "tv"
BuildRequires: pkgconfig(trustzone-nwd)
%else
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tizen.NUI.WindowSystem.Shell
+{
+ internal static partial class Interop
+ {
+ internal static partial class TaskbarService
+ {
+ const string lib = "libtzsh_taskbar_service.so.0";
+
+ [global::System.Runtime.InteropServices.DllImport(lib, EntryPoint = "tzsh_taskbar_service_create")]
+ internal static extern IntPtr Create(IntPtr tzsh, IntPtr win);
+
+ [global::System.Runtime.InteropServices.DllImport(lib, EntryPoint = "tzsh_taskbar_service_destroy")]
+ internal static extern int Destroy(IntPtr taskbarService);
+
+ [global::System.Runtime.InteropServices.DllImport(lib, EntryPoint = "tzsh_taskbar_service_place_type_set")]
+ internal static extern int SetPlaceType(IntPtr taskbarService, int placeType);
+
+ [global::System.Runtime.InteropServices.DllImport(lib, EntryPoint = "tzsh_taskbar_service_size_set")]
+ internal static extern int SetSize(IntPtr taskbarService, uint width, uint height);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.ComponentModel;
+
+namespace Tizen.NUI.WindowSystem.Shell
+{
+ /// <summary>
+ /// Class for the Tizen taskbar service.
+ /// </summary>
+ /// This class is need to be hidden as inhouse API.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class TaskbarService : IDisposable
+ {
+ private TizenShell _tzsh;
+ private IntPtr _taskbarService;
+ private int _tzshWin;
+ private bool disposed = false;
+ private bool isDisposeQueued = false;
+
+ /// <summary>
+ /// Enumeration for placed type of taskbar service window.
+ /// </summary>
+ public enum PlaceType
+ {
+ /// <summary>
+ /// Place to Bottom of Screen. Default type.
+ /// </summary>
+ Bottom = 0x0,
+ /// <summary>
+ /// Place to Top of Screen.
+ /// </summary>
+ Top = 0x1,
+ /// <summary>
+ /// Place to Left Side of Screen.
+ /// </summary>
+ Left = 0x2,
+ /// <summary>
+ /// Place to Right Side of Screen.
+ /// </summary>
+ Right = 0x3,
+ }
+
+ /// <summary>
+ /// Creates a new Taskbar Service handle.
+ /// </summary>
+ /// <param name="tzShell">The TizenShell instance.</param>
+ /// <param name="win">The window to provide service of the taskbar.</param>
+ /// <param name="type">The type to be placed on the screen.</param>
+ /// <exception cref="ArgumentException">Thrown when failed of invalid argument.</exception>
+ /// <exception cref="ArgumentNullException">Thrown when a argument is null.</exception>
+ public TaskbarService(TizenShell tzShell, Window win, PlaceType type = PlaceType.Bottom)
+ {
+ if (tzShell == null)
+ {
+ throw new ArgumentNullException(nameof(tzShell));
+ }
+ if (tzShell.GetNativeHandle() == IntPtr.Zero)
+ {
+ throw new ArgumentException("tzShell is not initialized.");
+ }
+ if (win == null)
+ {
+ throw new ArgumentNullException(nameof(win));
+ }
+
+ _tzsh = tzShell;
+ _tzshWin = win.GetNativeId();
+ _taskbarService = Interop.TaskbarService.Create(_tzsh.GetNativeHandle(), (IntPtr)_tzshWin);
+ if (_taskbarService == IntPtr.Zero)
+ {
+ int err = Tizen.Internals.Errors.ErrorFacts.GetLastResult();
+ _tzsh.ErrorCodeThrow(err);
+ }
+
+ Interop.TaskbarService.SetPlaceType(_taskbarService, (int)type);
+ }
+
+ /// <summary>
+ /// Destructor.
+ /// </summary>
+ ~TaskbarService()
+ {
+ if (!isDisposeQueued)
+ {
+ isDisposeQueued = true;
+ DisposeQueue.Instance.Add(this);
+ }
+ }
+
+ /// <summary>
+ /// Dispose.
+ /// </summary>
+ public void Dispose()
+ {
+ if (isDisposeQueued)
+ {
+ Dispose(DisposeTypes.Implicit);
+ }
+ else
+ {
+ Dispose(DisposeTypes.Explicit);
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ /// <inheritdoc/>
+ protected virtual void Dispose(DisposeTypes type)
+ {
+ if (!disposed)
+ {
+ if (_taskbarService != IntPtr.Zero)
+ {
+ int res = Interop.TaskbarService.Destroy(_taskbarService);
+ _taskbarService = IntPtr.Zero;
+ }
+ disposed = true;
+ }
+ }
+
+ /// <summary>
+ /// Set the current place type.
+ /// The window manager can use this to determine the geometry of another applications.
+ /// </summary>
+ /// <param name="type">The type of placement, enumeration for the place type.</param>
+ /// <exception cref="ArgumentException">Thrown when failed of invalid argument.</exception>
+ public void SetPlaceType(PlaceType type)
+ {
+ int res;
+
+ res = Interop.TaskbarService.SetPlaceType(_taskbarService, (int)type);
+ _tzsh.ErrorCodeThrow(res);
+ }
+
+ /// <summary>
+ /// Set the size of the taskbar.
+ /// This may be different from the actual size. The window manager can use this to
+ /// determine the geometry of another applications.
+ /// </summary>
+ /// <param name="width">The width of the taskbar area.</param>
+ /// <param name="height">The height of the taskbar area.</param>
+ /// <exception cref="ArgumentException">Thrown when failed of invalid argument.</exception>
+ public void SetSize(uint width, uint height)
+ {
+ int res;
+
+ res = Interop.TaskbarService.SetSize(_taskbarService, width, height);
+ _tzsh.ErrorCodeThrow(res);
+ }
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the License);
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an AS IS BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+internal static partial class Interop
+{
+ internal static partial class Libraries
+ {
+ internal const string Session = "libsessiond.so.0";
+
+ internal const string Libc = "libc.so.6";
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the License);
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an AS IS BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.Text;
+using Tizen.System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Session
+ {
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void SubsessionReplyCallback(int result, IntPtr cb_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void SubsessionEventCallback(IntPtr info, IntPtr cb_data);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_add_user", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionAddUser(int session_uid, string user, SubsessionReplyCallback cb, IntPtr data);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_remove_user", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionRemoveUser(int session_uid, string user, SubsessionReplyCallback cb, IntPtr data);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_switch_user", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionSwitchUser(int session_uid, string next_user, SubsessionReplyCallback cb, IntPtr data);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_get_user_list", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionGetUserList(int session_uid, out IntPtr list, out int user_count);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_free_user_list", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void SubsessionFreeUserList(IntPtr list);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_get_current_user", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionGetCurrentUser(int session_uid, StringBuilder user);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_register_event_callback", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubesssionRegisterEventCallback(int session_uid, SessionEventType event_bits, SubsessionEventCallback cb, IntPtr data);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_unregister_event_callback", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubesssionUnregisterEventCallback(int session_uid, SessionEventType event_bits);
+
+ [DllImport(Libraries.Session, EntryPoint = "subsession_event_wait_done", CallingConvention = CallingConvention.Cdecl)]
+ public static extern SessionError SubsessionEventWaitDone(SubsessionEventInfoNative info);
+
+ [DllImport(Libraries.Libc, EntryPoint = "getuid", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int GetUID();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tizen.System
+{
+ /// <summary>
+ /// Provides methods to manage subsession users. Allows to register for events triggered by operations on subsession users.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public sealed class Session
+ {
+ /// <summary>
+ /// UID of current system user.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static readonly int CurrentUID;
+
+ /// <summary>
+ /// Maximum length of any given user ID.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public const int MaxUserLength = 20;
+
+ /// <summary>
+ /// Special subsession ID, which is always present and does not represent any user.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public const string EmptyUser = "";
+
+ private static ConcurrentDictionary<int, Session> s_sessionInstances = new ConcurrentDictionary<int, Session>();
+
+ private readonly object _replyLock = new object();
+
+ private IDictionary<int, Interop.Session.SubsessionReplyCallback> _replyMap = new ConcurrentDictionary<int, Interop.Session.SubsessionReplyCallback>();
+
+ private int _replyID = 0;
+
+ private delegate void EventDelegate(IntPtr infoNative, IntPtr data);
+
+ static Session()
+ {
+ CurrentUID = Interop.Session.GetUID();
+ }
+
+ private Session(int sessionUID)
+ {
+ SessionUID = sessionUID;
+ }
+
+ /// <summary>
+ /// Gets a Session object instance for a given sessionUID.
+ /// </summary>
+ /// <param name="sessionUID">Session UID of a requested Session object instance</param>
+ /// <returns>Returns requested Session object</returns>
+ /// <remarks>
+ /// To ensure thread safety, expilicit creation of Session object is not allowed.
+ /// </remarks>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static Session GetInstance(int sessionUID)
+ {
+ if (!s_sessionInstances.ContainsKey(sessionUID))
+ s_sessionInstances.TryAdd(sessionUID, new Session(sessionUID));
+ return s_sessionInstances[sessionUID];
+ }
+
+ /// <summary>
+ /// Gets session UID of this session object.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public int SessionUID { get; private set; }
+
+ /// <summary>
+ /// Gets a list of all availible subsession user IDs for this session.
+ /// </summary>
+ /// <remarks>
+ /// The list of users depends on whether the session UID for this session object exists or not. If it
+ /// doesn't, the user list is empty (in particular this is not an error).
+ /// However if the session UID exists, the user list will contain the subsession
+ /// IDs (if they exist), but also the default value, which is "" (empty string, see EmptyUser field).
+ /// This doesn't mean that "" is a subsession ID in the same way as others; it is just a marker meaning that no subsession is
+ /// enabled.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public IReadOnlyList<string> GetUsers()
+ {
+
+ IntPtr ptr;
+ int count;
+
+ SessionError ret = Interop.Session.SubsessionGetUserList(SessionUID, out ptr, out count);
+ CheckError(ret, "Interop failed to get user list");
+
+ string[] users;
+ IntPtrToStringArray(ptr, count, out users);
+ Interop.Session.SubsessionFreeUserList(ptr);
+
+ return new List<string>(users);
+ }
+
+ /// <summary>
+ /// Gets a currently active subession user ID for this session.
+ /// </summary>
+ /// <remarks>
+ /// When no subsession is enabled, "" (empty string, see EmptyUser field) is returned.
+ /// This doesn't mean that "" is a subsession ID in the same way as others; it is just a marker meaning
+ /// that no subsession is enabled.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string GetCurrentUser()
+ {
+ StringBuilder user = new StringBuilder(MaxUserLength);
+ SessionError ret = Interop.Session.SubsessionGetCurrentUser(SessionUID, user);
+ CheckError(ret, "Interop failed to get current subsession user");
+
+ return user.ToString();
+ }
+
+ /// <summary>
+ /// Request new subsession to be created.
+ /// </summary>
+ /// <param name="userName">Subesssion user ID to be created</param>
+ /// <remarks>
+ /// Subsession ID must not start with a dot or have slashes.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid, or user ID is not a valid subession ID</exception>
+ /// <exception cref="InvalidOperationException"> Provided subsession user ID already exists</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Task SubsessionAddUserAsync(string userName)
+ {
+ var task = new TaskCompletionSource<bool>();
+ int taskID = 0;
+
+ lock (_replyLock)
+ {
+ taskID = _replyID++;
+ }
+
+ _replyMap[taskID] = (int result, IntPtr data) =>
+ {
+ try
+ {
+ CheckError((SessionError)result, "Interop failed to complete adding a new subsession user");
+ task.SetResult(true);
+ }
+ catch (Exception exception)
+ {
+ task.SetException(exception);
+ }
+ finally
+ {
+ _replyMap.Remove((int)data);
+ }
+ };
+
+ SessionError ret = Interop.Session.SubsessionAddUser(SessionUID, userName, _replyMap[taskID], (IntPtr)taskID);
+ CheckError(ret, "Interop failed to register a reply for adding a user");
+ return task.Task;
+ }
+
+ /// <summary>
+ /// Request an existing subsession to be removed.
+ /// </summary>
+ /// <param name="userName">Existing subesssion user ID to be removed</param>
+ /// <remarks>
+ /// Subsession ID must not start with a dot or have slashes.
+ /// Only inactive session ID can be removed. In order remove currently used session ID first switch to special
+ /// session ID "" (empty string, see EmptyUser), and only after switch completes, remove previously active session ID.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid, or user ID is not a valid subession ID</exception>
+ /// <exception cref="InvalidOperationException">Provided subsession user ID does not exist</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Task SubsessionRemoveUserAsync(string userName)
+ {
+ var task = new TaskCompletionSource<bool>();
+ int taskID = 0;
+
+ lock (_replyLock)
+ {
+ taskID = _replyID++;
+ }
+
+ _replyMap[taskID] = (int result, IntPtr data) =>
+ {
+ try
+ {
+ CheckError((SessionError)result, "Interop failed to remove a subsession user");
+ task.SetResult(true);
+ }
+ catch (Exception exception)
+ {
+ task.SetException(exception);
+ }
+ finally
+ {
+ _replyMap.Remove((int)data);
+ }
+ };
+
+ SessionError ret = Interop.Session.SubsessionRemoveUser(SessionUID, userName, _replyMap[taskID], (IntPtr)taskID);
+ CheckError(ret, "Interop failed to register a reply for removing a user");
+ return task.Task;
+ }
+
+ /// <summary>
+ /// Request a subession to become currently active.
+ /// </summary>
+ /// <param name="userName">Existing subesssion user ID to be set as active</param>
+ /// <remarks>
+ /// Subsession ID must not start with a dot or have slashes.
+ /// Special subsession ID "" (empty string, see EmptyUser) can be switched to, when it's required to deactivate
+ /// current subsession (this step is needed when current session is to be removed).
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid, or user ID is not a valid subession ID</exception>
+ /// <exception cref="InvalidOperationException">Provided subsession user ID to switch to does not exist</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Task SubsessionSwitchUserAsync(string userName)
+ {
+ var task = new TaskCompletionSource<bool>();
+ int taskID = 0;
+
+ lock (_replyLock)
+ {
+ taskID = _replyID++;
+ }
+
+ _replyMap[taskID] = (int result, IntPtr data) =>
+ {
+ try
+ {
+ CheckError((SessionError)result, "Interop failed to switch to a different subsession user");
+ task.SetResult(true);
+ }
+ catch (Exception exception)
+ {
+ task.SetException(exception);
+ }
+ finally
+ {
+ _replyMap.Remove((int)data);
+ }
+ };
+
+ SessionError ret = Interop.Session.SubsessionSwitchUser(SessionUID, userName, _replyMap[taskID], (IntPtr)taskID);
+ CheckError(ret, "Interop failed to register a reply for switching a user");
+ return task.Task;
+ }
+
+ /// <summary>
+ /// Mark event as completed.
+ /// </summary>
+ /// <param name="subsessionEventArgs">Event argument of the event (obtained from said event)</param>
+ /// <remarks>
+ /// This method is assumed to be called from an event handler. You can only mark an event as completed
+ /// if you registered for in in the same process.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void SubsessionEventMarkAsDone(SubsessionEventArgs subsessionEventArgs)
+ {
+ SessionError ret = Interop.Session.SubsessionEventWaitDone(subsessionEventArgs.SessionInfo);
+ CheckError(ret, $"Interop failed to mark this client's event (of type {subsessionEventArgs.SessionInfo.eventType}) as finished");
+ }
+
+ private void OnAddUserWait(IntPtr infoNative, IntPtr data)
+ {
+ _addUserWaitHandler?.Invoke(this, new AddUserEventArgs(infoNative));
+ }
+
+ private Interop.Session.SubsessionEventCallback _addUserWaitCB = null;
+
+ private event EventHandler<AddUserEventArgs> _addUserWaitHandler = null;
+
+ private readonly object _addUserWaitLock = new object();
+
+ /// <summary>
+ /// Event to be invoked when a new subession user is successfully added to this session.
+ /// </summary>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<AddUserEventArgs> AddUserWait
+ {
+ add
+ {
+ lock (_addUserWaitLock)
+ {
+ if (_addUserWaitHandler == null)
+ RegisterCallbackForEvent(SessionEventType.AddUserWait, ref _addUserWaitCB, OnAddUserWait);
+ _addUserWaitHandler += value;
+ }
+ }
+ remove
+ {
+ lock (_addUserWaitLock)
+ {
+ _addUserWaitHandler -= value;
+ if (_addUserWaitHandler == null)
+ UnregisterCallbackForEvent(SessionEventType.AddUserWait, ref _addUserWaitCB);
+ }
+ }
+ }
+
+ private void OnRemoveUserWait(IntPtr infoNative, IntPtr data)
+ {
+ _removeUserWaitHandler?.Invoke(this, new RemoveUserEventArgs(infoNative));
+ }
+
+ private Interop.Session.SubsessionEventCallback _removeUserWaitCB = null;
+
+ private event EventHandler<RemoveUserEventArgs> _removeUserWaitHandler = null;
+
+ private readonly object _removeUserWaitLock = new object();
+
+ /// <summary>
+ /// Event to be invoked when a subession user is successfully removed from this session.
+ /// </summary>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<RemoveUserEventArgs> RemoveUserWait
+ {
+ add
+ {
+ lock (_removeUserWaitLock)
+ {
+ if (_removeUserWaitHandler == null)
+ RegisterCallbackForEvent(SessionEventType.RemoveUserWait, ref _removeUserWaitCB, OnRemoveUserWait);
+ _removeUserWaitHandler += value;
+ }
+ }
+ remove
+ {
+ lock (_removeUserWaitLock)
+ {
+ _removeUserWaitHandler -= value;
+ if (_removeUserWaitHandler == null)
+ UnregisterCallbackForEvent(SessionEventType.RemoveUserWait, ref _removeUserWaitCB);
+ }
+ }
+ }
+
+ private void OnSwitchUserWait(IntPtr infoNative, IntPtr data)
+ {
+ _switchUserWaitHandler?.Invoke(this, new SwitchUserWaitEventArgs(infoNative));
+ }
+
+ private Interop.Session.SubsessionEventCallback _switchUserWaitCB = null;
+
+ private event EventHandler<SwitchUserWaitEventArgs> _switchUserWaitHandler = null;
+
+ private readonly object _switchUserWaitLock = new object();
+
+ /// <summary>
+ /// Event to be invoked when an existing subession user has begun switching to an active state.
+ /// </summary>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<SwitchUserWaitEventArgs> SwitchUserWait
+ {
+ add
+ {
+ lock (_switchUserWaitLock)
+ {
+ if (_switchUserWaitHandler == null)
+ RegisterCallbackForEvent(SessionEventType.SwitchUserWait, ref _switchUserWaitCB, OnSwitchUserWait);
+ _switchUserWaitHandler += value;
+ }
+ }
+ remove
+ {
+ lock (_switchUserWaitLock)
+ {
+ _switchUserWaitHandler -= value;
+ if (_switchUserWaitHandler == null)
+ UnregisterCallbackForEvent(SessionEventType.SwitchUserWait, ref _switchUserWaitCB);
+ }
+ }
+ }
+
+ private void OnSwitchUserCompletion(IntPtr infoNative, IntPtr data)
+ {
+ _switchUserCompletionHandler?.Invoke(this, new SwitchUserCompletionEventArgs(infoNative));
+ }
+
+ private Interop.Session.SubsessionEventCallback _switchUserCompletionCB = null;
+
+ private event EventHandler<SwitchUserCompletionEventArgs> _switchUserCompletionHandler = null;
+
+ private readonly object _switchUserCompletionLock = new object();
+
+ /// <summary>
+ /// Event to be invoked when an existing subession user is successfully switched to an acvite state.
+ /// </summary>
+ /// <exception cref="ArgumentException">Session UID of this object is invalid</exception>
+ /// <exception cref="OutOfMemoryException">Out of memory</exception>
+ /// <exception cref="IOException">Internal error</exception>
+ /// <exception cref="UnauthorizedAccessException">Not permitted</exception>
+ /// <exception cref="NotSupportedException">Not supported</exception>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<SwitchUserCompletionEventArgs> SwitchUserCompleted
+ {
+ add
+ {
+ lock (_switchUserCompletionLock)
+ {
+ if (_switchUserCompletionHandler == null)
+ RegisterCallbackForEvent(SessionEventType.SwitchUserCompletion, ref _switchUserCompletionCB, OnSwitchUserCompletion);
+ _switchUserCompletionHandler += value;
+ }
+ }
+ remove
+ {
+ lock (_switchUserCompletionLock)
+ {
+ _switchUserCompletionHandler -= value;
+ if (_switchUserCompletionHandler == null)
+ UnregisterCallbackForEvent(SessionEventType.SwitchUserCompletion, ref _switchUserCompletionCB);
+ }
+ }
+ }
+
+ private void CheckError(SessionError ret, string msg)
+ {
+ if (ret == SessionError.None)
+ return;
+
+ Log.Error(SessionErrorFactory.LogTag, msg);
+ Exception ex = SessionErrorFactory.CreateException(ret);
+ if (ex == null)
+ {
+ Log.Error(SessionErrorFactory.LogTag,
+ "Unexpected exception type for SessionError: " + Enum.GetName(typeof(SessionError), ret));
+ throw new InvalidOperationException("Unrecognized error");
+ }
+ throw ex;
+ }
+
+ static void IntPtrToStringArray(IntPtr unmanagedArray, int size, out string[] managedArray)
+ {
+ managedArray = new string[size];
+ var curr = unmanagedArray;
+
+ for (int iterator = 0; iterator < size; iterator++)
+ {
+ managedArray[iterator] = Marshal.PtrToStringAnsi(curr, 20);
+ curr = IntPtr.Add(curr, 20);
+ }
+ }
+
+ private void RegisterCallbackForEvent(SessionEventType eventType, ref Interop.Session.SubsessionEventCallback eventCallback,
+ EventDelegate delegateToSet)
+ {
+ eventCallback = new Interop.Session.SubsessionEventCallback(delegateToSet);
+ SessionError ret = Interop.Session.SubesssionRegisterEventCallback(SessionUID, eventType,
+ eventCallback, IntPtr.Zero);
+ CheckError(ret, $"Interop failed to register a callback for an event of type {eventType}");
+ }
+
+ private void UnregisterCallbackForEvent(SessionEventType eventType, ref Interop.Session.SubsessionEventCallback eventCallback)
+ {
+ SessionError ret = Interop.Session.SubesssionUnregisterEventCallback(SessionUID, eventType);
+ CheckError(ret, $"Interop failed to unregister a callback for an event of type {eventType}");
+ eventCallback = null;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.ComponentModel;
+
+namespace Tizen.System
+{
+ [Flags]
+ internal enum SessionEventType : int
+ {
+ AddUserWait = 1 << 0,
+ RemoveUserWait = 1 << 1,
+ SwitchUserWait = 1 << 2,
+ SwitchUserCompletion = 1 << 3,
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the License);
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an AS IS BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.IO;
+using Tizen.Internals.Errors;
+
+namespace Tizen.System
+{
+ internal enum SessionError
+ {
+ None = ErrorCode.None,
+ InvalidParameter = ErrorCode.InvalidParameter,
+ Io = ErrorCode.IoError,
+ OutOfMemory = ErrorCode.OutOfMemory,
+ AlreadyExists = ErrorCode.FileExists,
+ NotAvailible = ErrorCode.NoSuchDevice,
+ ResourceBusy = ErrorCode.ResourceBusy,
+ PermissionDenied = ErrorCode.PermissionDenied,
+ NotSupported = ErrorCode.NotSupported,
+ }
+
+ internal static class SessionErrorFactory
+ {
+ internal const string LogTag = "Tizen.System.Session";
+
+ internal static Exception CreateException(SessionError err)
+ {
+ SessionError error = (SessionError)err;
+ if (error == SessionError.InvalidParameter)
+ {
+ return new ArgumentException("Invalid parameter");
+ }
+ else if (error == SessionError.Io)
+ {
+ return new IOException("I/O error");
+ }
+ else if (error == SessionError.OutOfMemory)
+ {
+ return new InvalidOperationException("Out of memory");
+ }
+ else if (error == SessionError.AlreadyExists)
+ {
+ return new InvalidOperationException("Already exists");
+ }
+ else if (error == SessionError.NotAvailible)
+ {
+ return new InvalidOperationException("Not availible");
+ }
+ else if (error == SessionError.ResourceBusy)
+ {
+ return new InvalidOperationException("Resource busy");
+ }
+ else if (error == SessionError.PermissionDenied)
+ {
+ return new UnauthorizedAccessException("Permission denied");
+ }
+ else if (error == SessionError.NotSupported)
+ {
+ return new NotSupportedException("Not supported");
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* Licensed under the Apache License, Version 2.0 (the License);
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an AS IS BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+
+namespace Tizen.System
+{
+ /// <summary>
+ /// A generic subession event argument type.
+ /// </summary>
+ /// <remarks>
+ /// Use this as an argument type for generic subession event handlers.
+ /// You can check the event type that was invoked by checking a type of event arguments
+ /// during runtime - they all derive from this base class.
+ /// </remarks>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public abstract class SubsessionEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Session UID of the session invoking the event
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public int SessionUID { get; internal set; }
+
+ internal SubsessionEventInfoNative SessionInfo { get; set; }
+
+ internal SubsessionEventArgs(IntPtr infoNativePtr)
+ {
+ SessionInfo = (SubsessionEventInfoNative)Marshal.PtrToStructure(infoNativePtr, typeof(SubsessionEventInfoNative));
+ SessionUID = SessionInfo.sessionUID;
+ }
+ }
+
+ /// <summary>
+ /// An event arguemnt type for AddUserWait event type
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class AddUserEventArgs : SubsessionEventArgs
+ {
+ /// <summary>
+ /// Added subsession user ID
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string UserName { get; internal set; }
+
+ internal AddUserEventArgs(IntPtr infoNativePtr)
+ : base(infoNativePtr)
+ {
+ UserName = Marshal.PtrToStringAnsi(IntPtr.Add(infoNativePtr, 8), 20);
+ }
+ }
+
+ /// <summary>
+ /// An event arguemnt type for RemoveUserWait event type
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class RemoveUserEventArgs : SubsessionEventArgs
+ {
+ /// <summary>
+ /// Removed subsession user ID
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string UserName { get; internal set; }
+
+ internal RemoveUserEventArgs(IntPtr infoNativePtr)
+ : base(infoNativePtr)
+ {
+ UserName = Marshal.PtrToStringAnsi(IntPtr.Add(infoNativePtr, 8), 20);
+ }
+ }
+
+ /// <summary>
+ /// A generic base class for Switch event types
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public abstract class SwitchUserEventArgs : SubsessionEventArgs
+ {
+ /// <summary>
+ /// ID of this switch operation
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public long SwitchID { get; internal set; }
+
+ /// <summary>
+ /// Active subsession user ID before this switch operation
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string UserNamePrev { get; internal set; }
+
+ /// <summary>
+ /// Active subsession ID after this switch operation
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string UserNameNext { get; internal set; }
+
+ internal SwitchUserEventArgs(IntPtr infoNativePtr)
+ : base(infoNativePtr)
+ {
+ SwitchID = SessionInfo.switchID;
+ UserNamePrev = Marshal.PtrToStringAnsi(IntPtr.Add(infoNativePtr, 16), 20);
+ UserNameNext = Marshal.PtrToStringAnsi(IntPtr.Add(infoNativePtr, 36), 20);
+ }
+ }
+
+ /// <summary>
+ /// An event arguemnt type for SwitchUserWait event type
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class SwitchUserWaitEventArgs : SwitchUserEventArgs
+ {
+ internal SwitchUserWaitEventArgs(IntPtr infoNativePtr) : base(infoNativePtr) { }
+ }
+
+ /// <summary>
+ /// An event arguemnt type for SwitchUserCompleted event type
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class SwitchUserCompletionEventArgs : SwitchUserEventArgs
+ {
+ internal SwitchUserCompletionEventArgs(IntPtr infoNativePtr) : base(infoNativePtr) { }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Text;
+using Tizen.Internals;
+
+namespace Tizen.System
+{
+ [NativeStruct("subsession_event_info", Include="sessiond.h", PkgConfig="libsessiond")]
+ [StructLayout(LayoutKind.Explicit)]
+ internal struct SubsessionEventInfoNative
+ {
+ [FieldOffset(0)]
+ public SessionEventType eventType;
+ [FieldOffset(4)]
+ public int sessionUID;
+
+ [FieldOffset(8)]
+ public Int64 switchID;
+
+ /// The following 4 fields are here just for the record and for the NativeStruct validation
+ /// which is performed as one of the steps during build with GBS.
+ /// However, we've verified that representing the whole structure as IntPtr and accessing
+ /// individual string fields with PtrToStructure with and PtrToStringAnsi + IntPtr.Add is
+ /// the only way to make it work. That's why we do not use these fields and they shouldn't
+ /// be accessed directly.
+ [FieldOffset(8)]
+ private IntPtr AddUserPtr;
+ [FieldOffset(8)]
+ private IntPtr RemoveUserPtr;
+ [FieldOffset(16)]
+ private IntPtr PrevUserPtr;
+ [FieldOffset(36)]
+ private IntPtr NextUserPtr;
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Tizen\Tizen.csproj" />
+ <ProjectReference Include="..\Tizen.Log\Tizen.Log.csproj" />
+ </ItemGroup>
+
+</Project>