From: Haesu Gwon Date: Wed, 11 Jun 2025 08:20:51 +0000 (+0900) Subject: [ScreenMirroring] Add Uibc APIs (#6964) X-Git-Tag: submit/tizen/20250611.150922~1^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=004e1e4c9613ee350c559d21e0634030032fb63b;p=platform%2Fcore%2Fcsapi%2Ftizenfx.git [ScreenMirroring] Add Uibc APIs (#6964) * [ScreenMirroring] Add Uibc APIs --- diff --git a/src/Tizen.Multimedia.Remoting/Interop/Interop.ScreenMirroring.cs b/src/Tizen.Multimedia.Remoting/Interop/Interop.ScreenMirroring.cs index 9357aceaf..d54034313 100755 --- a/src/Tizen.Multimedia.Remoting/Interop/Interop.ScreenMirroring.cs +++ b/src/Tizen.Multimedia.Remoting/Interop/Interop.ScreenMirroring.cs @@ -17,6 +17,7 @@ using System; using System.Drawing; using System.Runtime.InteropServices; +using Tizen.Internals; using Tizen.Multimedia; using Tizen.Multimedia.Remoting; @@ -28,6 +29,12 @@ internal static partial class Interop internal delegate void StateChangedCallback(ScreenMirroringErrorCode error, ScreenMirroringState state, IntPtr userData); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void SrcDisplayOrientationReceivedCallback(ScreenMirroringDisplayOrientation orientation, IntPtr userData); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void UibcInfoReceivedCallback(ScreenMirroringErrorCode error, IntPtr info, IntPtr userData); + [DllImport(Libraries.ScreenMirroring, EntryPoint = "scmirroring_sink_create")] internal static extern ScreenMirroringErrorCode Create(out IntPtr handle); @@ -113,5 +120,65 @@ internal static partial class Interop [DllImport(Libraries.ScreenMirroring, EntryPoint = "scmirroring_sink_get_negotiated_audio_bitwidth")] internal static extern ScreenMirroringErrorCode GetNegotiatedAudioBitwidth(ref IntPtr handle, out int bitwidth); + + // UIBC + [DllImport(Libraries.ScreenMirroring, EntryPoint = "scmirroring_sink_set_src_display_orientation_notify_cb")] + internal static extern ScreenMirroringErrorCode SetSrcDisplayOrientationChangedCb(IntPtr handle, + SrcDisplayOrientationReceivedCallback callback, IntPtr userData = default); + + [DllImport(Libraries.ScreenMirroring, EntryPoint = "scmirroring_sink_set_uibc_info_received_cb")] + internal static extern ScreenMirroringErrorCode SetUibcInfoReceivedCb(IntPtr handle, + UibcInfoReceivedCallback callback, IntPtr userData = default); + + [DllImport(Libraries.ScreenMirroring, EntryPoint = "scmirroring_sink_set_window_size")] + internal static extern ScreenMirroringErrorCode SetWindowSize(IntPtr handle, int width, int height); + + [DllImport(Libraries.ScreenMirroring, EntryPoint = "scmirroring_sink_enable_uibc")] + internal static extern ScreenMirroringErrorCode EnableUibc(IntPtr handle, ScreenMirroringCaptureMode mode); + + [DllImport(Libraries.ScreenMirroring, EntryPoint = "scmirroring_sink_send_generic_mouse_event")] + internal static extern ScreenMirroringErrorCode SendGenericMouseEvent(IntPtr handle, IntPtr uibcEvent); + + [DllImport(Libraries.ScreenMirroring, EntryPoint = "scmirroring_sink_send_generic_key_event")] + internal static extern ScreenMirroringErrorCode SendGenericKeyEvent(IntPtr handle, ScreenMirroringKeyEventType type, ushort keyCode1, ushort keyCode2); + + [NativeStruct("scmirroring_uibc_input_s", Include = "scmirroring_type_internal.h", PkgConfig = "capi-media-screen-mirroring")] + [StructLayout(LayoutKind.Sequential)] + internal struct UibcInput + { + internal ScreenMirroringUibcInputType type; + internal ScreenMirroringUibcInputPath path; + } + + [NativeStruct("scmirroring_uibc_info_s", Include = "scmirroring_type_internal.h", PkgConfig = "capi-media-screen-mirroring")] + [StructLayout(LayoutKind.Sequential)] + internal struct UibcInfo + { + internal string Ip; + internal uint Port; + internal uint GenCapability; + internal int Width; + internal int Height; + internal IntPtr hidcCapsList; + internal uint hidcCapsSize; + } + + [NativeStruct("scmirroring_uibc_mouse_s", Include = "scmirroring_type_internal.h", PkgConfig = "capi-media-screen-mirroring")] + [StructLayout(LayoutKind.Sequential)] + internal struct UibcMouse + { + internal ushort id; + internal ushort x; + internal ushort y; + } + + [NativeStruct("scmirroring_uibc_mouse_event_s", Include = "scmirroring_type_internal.h", PkgConfig = "capi-media-screen-mirroring")] + [StructLayout(LayoutKind.Sequential)] + internal struct UibcMouseEvent + { + internal int size; + internal ScreenMirroringMouseEventType type; + internal IntPtr uibcMouse; + } } } diff --git a/src/Tizen.Multimedia.Remoting/ScreenMirroring/EventArgs.cs b/src/Tizen.Multimedia.Remoting/ScreenMirroring/EventArgs.cs index ea931b54d..8b69b49ea 100644 --- a/src/Tizen.Multimedia.Remoting/ScreenMirroring/EventArgs.cs +++ b/src/Tizen.Multimedia.Remoting/ScreenMirroring/EventArgs.cs @@ -15,6 +15,11 @@ */ using System; +using System.ComponentModel; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.InteropServices; +using Native = Interop.ScreenMirroring; namespace Tizen.Multimedia.Remoting { @@ -53,4 +58,106 @@ namespace Tizen.Multimedia.Remoting /// 4 public ScreenMirroringError Error { get; } } -} \ No newline at end of file + + /// + /// Provides data for the event. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public class ScreenMirroringDisplayOrientationChangedEventArgs : EventArgs + { + internal ScreenMirroringDisplayOrientationChangedEventArgs(ScreenMirroringDisplayOrientation orientation) + { + Orientation = orientation; + } + + /// + /// Gets the display orientation of source. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ScreenMirroringDisplayOrientation Orientation { get; } + } + + /// + /// Provides data for the event. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public class ScreenMirroringUibcInfoReceivedEventArgs : EventArgs + { + internal ScreenMirroringUibcInfoReceivedEventArgs(ScreenMirroringError error, IntPtr uibcInfo) + { + var unmanagedStruct = Marshal.PtrToStructure(uibcInfo); + + Error = error; + Ip = unmanagedStruct.Ip; + Port = unmanagedStruct.Port; + GenCapability = unmanagedStruct.GenCapability; + Resolution = new Size(unmanagedStruct.Width, unmanagedStruct.Height); + HidcCapabilities = GetUibcInputs(unmanagedStruct.hidcCapsList, unmanagedStruct.hidcCapsSize); + } + + private ReadOnlyCollection GetUibcInputs(IntPtr unmanagedIntPtr, uint hidcCapsSize) + { + var size = Marshal.SizeOf(typeof(Native.UibcInput)); + var unmanagedStruct = new Native.UibcInput[hidcCapsSize]; + IntPtr unmanagedUibcInput; + + for (int i = 0; i < hidcCapsSize; i++) + { + if (IntPtr.Size == 4) + { + unmanagedUibcInput = new IntPtr(unmanagedIntPtr.ToInt32() + i * size); + } + else + { + unmanagedUibcInput = new IntPtr(unmanagedIntPtr.ToInt64() + i * size); + } + + unmanagedStruct[i] = Marshal.PtrToStructure(unmanagedUibcInput); + } + + var hidcList = new List(); + for (int i = 0; i < hidcCapsSize; i++) + { + hidcList.Add(new UibcInputs(unmanagedStruct[i].type, unmanagedStruct[i].path)); + } + + return new ReadOnlyCollection(hidcList); + } + + /// + /// Gets the error that occurred. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ScreenMirroringError Error { get; } + + /// + /// Gets the IP address + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public string Ip { get; } + + /// + /// Gets the port number + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public uint Port { get; } + + /// + /// Gets the gen capability + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public uint GenCapability { get; } + + /// + /// Gets the resolution of window + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public Size Resolution { get; } + + /// + /// Gets the HIDC(Human Interface Device Command) capabilities + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public ReadOnlyCollection HidcCapabilities { get; } + } +} diff --git a/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroring.cs b/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroring.cs index 7bfa7efd3..9e47902f1 100644 --- a/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroring.cs +++ b/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroring.cs @@ -15,10 +15,17 @@ */ using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Linq; +using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; +using Microsoft.VisualBasic; using Native = Interop.ScreenMirroring; namespace Tizen.Multimedia.Remoting @@ -41,6 +48,10 @@ namespace Tizen.Multimedia.Remoting private bool _disposed = false; + private Native.StateChangedCallback _stateChangedCallback; + private Native.SrcDisplayOrientationReceivedCallback _displayOrientationReceivedCallback; + private Native.UibcInfoReceivedCallback _uibcInfoReceivedCallback; + internal IntPtr Handle { get @@ -78,6 +89,8 @@ namespace Tizen.Multimedia.Remoting VideoInfo = new ScreenMirroringVideoInfo(this); RegisterStateChangedEvent(); + RegisterDisplayOrientationChangedEvent(); + RegisterUibcInfoReceivedEvent(); } /// @@ -100,6 +113,18 @@ namespace Tizen.Multimedia.Remoting /// 4 public event EventHandler ErrorOccurred; + /// + /// Occurs when the display orientation of source is changed. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler DisplayOrientationChanged; + + /// + /// Occurs when the UIBC information is received from source. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler UibcInfoReceived; + #region Display support private Display _display; @@ -455,6 +480,102 @@ namespace Tizen.Multimedia.Remoting DetachDisplay(); } + /// + /// Sets the information for UIBC(User Input Back Channel). + /// + /// + /// The current state is not in the valid.
+ /// -or-
+ /// An internal error occurs. + ///
+ /// The has already been disposed. + [EditorBrowsable(EditorBrowsableState.Never)] + public void SetUibcInformation(Size windowSize, ScreenMirroringCaptureMode mode) + { + ValidateState(ScreenMirroringState.Idle); + + Native.SetWindowSize(Handle, windowSize.Width, windowSize.Height).ThrowIfError("Failed to set uibc window size"); + Native.EnableUibc(Handle, mode).ThrowIfError("Failed to set uibc capture mode"); + } + + /// + /// Sends mouse event for UIBC(User Input Back Channel). + /// + /// + /// The current state is not in the valid.
+ /// -or-
+ /// An internal error occurs. + ///
+ /// is null. + /// The has already been disposed. + [EditorBrowsable(EditorBrowsableState.Never)] + public void SendGenericMouseEvent(IEnumerable uibcMouseInfos, ScreenMirroringMouseEventType type) + { + ValidateState(ScreenMirroringState.Connected); + + if (!uibcMouseInfos.Any()) + { + throw new ArgumentNullException(nameof(uibcMouseInfos)); + } + + var uibcMouseInfosSize = uibcMouseInfos.Count(); + var uibcMouse = new Native.UibcMouse[uibcMouseInfosSize]; + int i = 0; + IntPtr unmanagedUibcMousePtr; + + foreach (var uibcMouseInfo in uibcMouseInfos) + { + uibcMouse[i].id = uibcMouseInfo.Id; + uibcMouse[i].x = uibcMouseInfo.X; + uibcMouse[i++].y = uibcMouseInfo.Y; + } + + var size = Marshal.SizeOf(typeof(Native.UibcMouse)); + IntPtr unmanagedUibcMouse = Marshal.AllocHGlobal(size * uibcMouseInfosSize); + for (i = 0; i < uibcMouseInfosSize; i++) + { + if (IntPtr.Size == 4) + { + unmanagedUibcMousePtr = new IntPtr(unmanagedUibcMouse.ToInt32() + i * size); + } + else + { + unmanagedUibcMousePtr = new IntPtr(unmanagedUibcMouse.ToInt64() + i * size); + } + Marshal.StructureToPtr(uibcMouse[i], unmanagedUibcMousePtr, false); + } + + Native.UibcMouseEvent uibcObject; + uibcObject.size = uibcMouseInfosSize; + uibcObject.type = type; + uibcObject.uibcMouse = unmanagedUibcMouse; + + var unmanagedUibcObject = Marshal.AllocHGlobal(Marshal.SizeOf(uibcObject)); + Marshal.StructureToPtr(uibcObject, unmanagedUibcObject, false); + + try + { + Native.SendGenericMouseEvent(Handle, unmanagedUibcObject).ThrowIfError("Failed to send generic mouse event"); + } + finally + { + Marshal.FreeHGlobal(unmanagedUibcMouse); + Marshal.FreeHGlobal(unmanagedUibcObject); + } + } + + /// + /// Sends key event for UIBC(User Input Back Channel). + /// + /// The has already been disposed. + [EditorBrowsable(EditorBrowsableState.Never)] + public void SendGenericKeyEvent(ScreenMirroringKeyEventType type, ushort keyCode1, ushort keyCode2) + { + ValidateState(ScreenMirroringState.Connected); + + Native.SendGenericKeyEvent(Handle, type, keyCode1, keyCode2).ThrowIfError("Failed to send generic key event"); + } + private void ThrowIfDisposed() { if (_disposed) @@ -618,8 +739,6 @@ namespace Tizen.Multimedia.Remoting } } - private Native.StateChangedCallback _stateChangedCallback; - private void RegisterStateChangedEvent() { _stateChangedCallback = (error, state, _) => @@ -645,6 +764,28 @@ namespace Tizen.Multimedia.Remoting ThrowIfError("Failed to initialize StateChanged event."); } + private void RegisterDisplayOrientationChangedEvent() + { + _displayOrientationReceivedCallback = (orientation, _) => + { + DisplayOrientationChanged?.Invoke(this, new ScreenMirroringDisplayOrientationChangedEventArgs(orientation)); + }; + + Native.SetSrcDisplayOrientationChangedCb(Handle, _displayOrientationReceivedCallback). + ThrowIfError("Failed to initialize DisplayOrientationChanged event."); + } + + private void RegisterUibcInfoReceivedEvent() + { + _uibcInfoReceivedCallback = (error, uibcInfo, _) => + { + UibcInfoReceived?.Invoke(this, new ScreenMirroringUibcInfoReceivedEventArgs(error.ToCsharp(), uibcInfo)); + }; + + Native.SetUibcInfoReceivedCb(Handle, _uibcInfoReceivedCallback). + ThrowIfError("Failed to initialize UibcInfoReceived event."); + } + private void ValidateState(params ScreenMirroringState[] required) { Debug.Assert(required.Length > 0); @@ -658,10 +799,9 @@ namespace Tizen.Multimedia.Remoting if (!required.Contains(curState)) { throw new InvalidOperationException($"The screen mirroring is not in a valid state. " + - $"Current State : { curState }, Valid State : { string.Join(", ", required) }."); + $"Current State : {curState}, Valid State : {string.Join(", ", required)}."); } } - } internal class AtomicState diff --git a/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroringEnums.cs b/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroringEnums.cs index b8ea52bdd..be3f3c2f2 100644 --- a/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroringEnums.cs +++ b/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroringEnums.cs @@ -15,6 +15,7 @@ */ using System; +using System.ComponentModel; namespace Tizen.Multimedia.Remoting { @@ -209,4 +210,247 @@ namespace Tizen.Multimedia.Remoting ///
Mobile } + + /// + /// Specifies the UIBC(User Input Back Channel) display orientation for . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public enum ScreenMirroringDisplayOrientation + { + /// + /// None + /// + None, + + /// + /// Portrait display + /// + PortraitDisplay, + + /// + /// Portrait + /// + Portrait, + + /// + /// Landscape + /// + Landscape + } + + /// + /// Specifies the UIBC(User Input Back Channel) input type for . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public enum ScreenMirroringUibcInputType + { + /// + /// Unknown + /// + Unknown, + + /// + /// Keyboard + /// + Keyboard = (1 << 0), + + /// + /// Mouse + /// + Mouse = (1 << 1), + + /// + /// Single touch + /// + SingleTouch = (1 << 2), + + /// + /// Multi touch + /// + MultiTouch = (1 << 3), + + /// + /// Joystick + /// + Joystick = (1 << 4), + + /// + /// Camera + /// + Camera = (1 << 5), + + /// + /// Gesture + /// + Gesture = (1 << 6), + + /// + /// Remote control + /// + RemoteControl = (1 << 7), + } + + /// + /// Specifies the UIBC(User Input Back Channel) input path for . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public enum ScreenMirroringUibcInputPath + { + /// + /// Unknown + /// + Unknown, + + /// + /// Infrared + /// + Infrared = (1 << 0), + + /// + /// Usb + /// + Usb = (1 << 1), + + /// + /// Bluetooth + /// + Bt = (1 << 2), + + /// + /// Zigbee + /// + Zigbee = (1 << 3), + + /// + /// Wifi + /// + Wifi = (1 << 4), + + /// + /// Nosp + /// + Nosp = (1 << 5), + } + + /// + /// Specifies the UIBC(User Input Back Channel) capture mode for . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public enum ScreenMirroringCaptureMode + { + /// + /// Application + /// + Application, + + /// + /// Raw interface + /// + RawInterface + } + + /// + /// Specifies the UIBC(User Input Back Channel) mouse event type for . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public enum ScreenMirroringMouseEventType + { + /// + /// Left button is pressed + /// + LeftButtonPressed, + + /// + /// Left button is released + /// + LeftButtonReleased, + + /// + /// Mouse is moved + /// + Move + } + + /// + /// Specifies the UIBC(User Input Back Channel) key event type for . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public enum ScreenMirroringKeyEventType + { + /// + /// Key is pressed + /// + KeyPressed, + + /// + /// Key is released + /// + KeyReleased + } + + /// + /// Specifies the UIBC(User Input Back Channel) inputs for . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct UibcInputs + { + internal UibcInputs(ScreenMirroringUibcInputType type, ScreenMirroringUibcInputPath path) + { + Type = type; + Path = path; + } + + /// + /// Gets the type of UIBC input. + /// + /// The UIBC input type + [EditorBrowsable(EditorBrowsableState.Never)] + public ScreenMirroringUibcInputType Type { get; } + + /// + /// Gets the path of UIBC input. + /// + /// The UIBC input path + [EditorBrowsable(EditorBrowsableState.Never)] + public ScreenMirroringUibcInputPath Path { get; } + } + + /// + /// Specifies the UIBC(User Input Back Channel) mount event. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public struct UibcMouseInfo + { + /// + /// Initializes a new instance of the class. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public UibcMouseInfo(ushort id, ushort x, ushort y) + { + Id = id; + X = x; + Y = y; + } + + /// + /// Gets the ID for pointer. + /// + /// The UIBC input type + [EditorBrowsable(EditorBrowsableState.Never)] + public ushort Id { get; set; } + + /// + /// Gets the X coordinates of mouse. + /// + /// The X coordinates + [EditorBrowsable(EditorBrowsableState.Never)] + public ushort X { get; set; } + + /// + /// Gets the Y coordinates of mouse. + /// + /// The Y coordinates + [EditorBrowsable(EditorBrowsableState.Never)] + public ushort Y { get; set; } + } } diff --git a/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroringError.cs b/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroringError.cs index e00302a62..6b58febc4 100644 --- a/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroringError.cs +++ b/src/Tizen.Multimedia.Remoting/ScreenMirroring/ScreenMirroringError.cs @@ -72,6 +72,18 @@ namespace Tizen.Multimedia.Remoting return new InvalidOperationException($"Unknown error : {err.ToString()}."); } } + + internal static ScreenMirroringError ToCsharp(this ScreenMirroringErrorCode err) + { + switch (err) + { + case ScreenMirroringErrorCode.InvalidOperation: + return ScreenMirroringError.InvalidOperation; + + default: + throw new InvalidOperationException($"Unknown error : {err.ToString()}."); + } + } } }