• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

luttje / Key2Joy / 6602669847

22 Oct 2023 08:23AM UTC coverage: 44.094% (-8.4%) from 52.519%
6602669847

push

github

web-flow
Add XInput in preparation for gamepad triggers + add xmldoc (#50)

* Add XInput in preparation for gamepad triggers + add xmldoc

* add parent-child relation between mapped options

* add remove child mappings option + remove debug context menu

* x86 > AnyCPU (gives more sensible errors in WinForms Designer) - Fixed Designer failing on MappingForm

* Fix polling gamepad

* Add gamepad input trigger and config control + Fix mapping list not updating correctly

* remove broken and quite useless test

* update readme with warning regarding #46 + update readme special thanks

* add warning on same gamepad id trigger and action

* attempt to give github actions more time before timeout (tests sometimes fail)

* use current default mapping profile from app (so we dont have to copy it to tests everytime manually)

* Add gamepad button trigger

* Add multi-property edit mode

* Show physical gamepad connection warning

* give even more time for tests so they dont fail

* Block arming mappings if simulated gamepad index collides with physical gamepad

* Cleanup + add GamePad Trigger Trigger

* Fix combined trigger remove not working

* Separate stick action and allow custom scaling

* improve stick feeling

* fix parent picker

* update default mappings

* add trigger and try work out conflicts between physical and simulated devices (no luck yet)

* Fix simulated gamepad recognized as physical

* Show gamepad devices in UI

* fix mapping form + add more multi-edit type support

* make reverse mapping tool more useful

* easily setup/update reverse mappings while creating/updating a mapping

* update readme screenshots + prefer 'Arm' terminology for enabling profile

* support more nullable types in mapping profile

* simplify groups

* add configurable grouping

* prevent wonky sorting across groups

* Add easy switch group option

* Fix being able to create corrupt profile

* fail loading ... (continued)

762 of 2383 branches covered (0.0%)

Branch coverage included in aggregate %.

3060 of 3060 new or added lines in 106 files covered. (100.0%)

3897 of 8183 relevant lines covered (47.62%)

12635.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.32
/Core/Key2Joy.Contracts/Plugins/Remoting/RemoteEventSubscriberHost.cs
1
using System;
2
using System.Diagnostics;
3
using System.IO;
4
using System.IO.Pipes;
5
using System.Threading.Tasks;
6
using System.Threading;
7
using Key2Joy.Contracts.Util;
8

9
namespace Key2Joy.Contracts.Plugins.Remoting;
10

11
public class RemoteEventSubscriberHost : IDisposable
12
{
13
    public event EventHandler Disposing;
14

15
    private readonly NamedPipeServerStream pipeStream;
16
    private bool isPipeServerStreamReady = false;
17

18
    private readonly CancellationTokenSource pipeCancellation;
19
    private static readonly TimeSpan HeartbeatWithMargin = RemoteEventSubscriber.MaxHeartbeatInterval - TimeSpan.FromSeconds(2);
1✔
20
    private static readonly TimeSpan MaxWaitForReady = TimingHelper.FromSeconds(5);
1✔
21

22
    internal RemoteEventSubscriberHost(string portName)
16✔
23
    {
24
        this.pipeStream = new NamedPipeServerStream(
16✔
25
            RemotePipe.GetAbsolutePipeName(portName),
16✔
26
            PipeDirection.InOut,
16✔
27
            1,
16✔
28
            PipeTransmissionMode.Message,
16✔
29
            PipeOptions.Asynchronous);
16✔
30

31
        this.pipeCancellation = new CancellationTokenSource();
16✔
32

33
        this.InitBackgroundEventThread();
16✔
34
        this.InitBackgroundHeartbeatThread();
16✔
35
    }
16✔
36

37
    /// <summary>
38
    /// Listens in the background for any incoming messages from the client.
39
    /// </summary>
40
    private async void InitBackgroundEventThread()
41
    {
42
        var backgroundThread = Task.Run(() =>
16✔
43
        {
16✔
44
            this.pipeStream.WaitForConnection();
16✔
45

16✔
46
            StreamReader reader = new(this.pipeStream);
16✔
47

16✔
48
            while (!this.pipeCancellation.IsCancellationRequested)
32✔
49
            {
16✔
50
                try
16✔
51
                {
16✔
52
                    var messageOrSubscriptionId = RemotePipe.ReadMessage(this.pipeStream);
32✔
53

16✔
54
                    if (string.IsNullOrEmpty(messageOrSubscriptionId))
16!
55
                    {
16✔
56
                        continue;
×
57
                    }
16✔
58

16✔
59
                    if (messageOrSubscriptionId == RemoteEventSubscriber.SignalReady)
16!
60
                    {
16✔
61
                        this.isPipeServerStreamReady = true;
16✔
62
                        continue;
16✔
63
                    }
16✔
64
                    else if (messageOrSubscriptionId == RemoteEventSubscriber.SignalExit)
×
65
                    {
16✔
66
                        this.Dispose();
×
67
                        return;
×
68
                    }
16✔
69

16✔
70
                    RemoteEventSubscriber.HandleInvoke(messageOrSubscriptionId);
×
71
                }
×
72
                catch (Exception ex)
14✔
73
                {
16✔
74
                    Output.WriteLine(ex);
14✔
75
                    Debug.WriteLine($"-------------> Exception: {ex.Message}");
16✔
76
                    return;
14✔
77
                }
16✔
78
            }
16✔
79
        });
30✔
80

81
        await backgroundThread;
16✔
82
    }
14✔
83

84
    /// <summary>
85
    /// Sends heartbeats in the background at the commonly agreed interval.
86
    /// </summary>
87
    private async void InitBackgroundHeartbeatThread()
88
    {
89
        var backgroundThread = Task.Run(async () =>
16✔
90
        {
16✔
91
            while (!this.pipeCancellation.IsCancellationRequested)
20!
92
            {
16✔
93
                await Task.Delay(HeartbeatWithMargin);
20✔
94
                if (!this.isPipeServerStreamReady)
18✔
95
                {
16✔
96
                    continue;
16✔
97
                }
16✔
98

16✔
99
                try
16✔
100
                {
16✔
101
                    RemotePipe.WriteMessage(this.pipeStream, RemoteEventSubscriber.SignalHeartbeat);
18✔
102
                }
4✔
103
                catch (Exception ex)
14✔
104
                {
16✔
105
                    Output.WriteLine(ex);
14✔
106
                    Debug.WriteLine($"-------------> Exception: {ex.Message}");
16✔
107
                    return;
14✔
108
                }
16✔
109
            }
16✔
110
        });
30✔
111

112
        await backgroundThread;
16✔
113
    }
14✔
114

115
    /// <summary>
116
    /// Stops execution until the event pipe has received the ready signal
117
    /// </summary>
118
    public void WaitForEventPipeReady()
119
    {
120
        var waitStart = DateTime.Now;
16✔
121

122
        while (!this.isPipeServerStreamReady)
11,316,614✔
123
        {
124
            Task.Delay(10);
11,316,598✔
125

126
            if ((DateTime.Now - waitStart) > MaxWaitForReady)
11,316,598!
127
            {
128
                throw new TimeoutException($"WaitForEventPipeReady timed out after {MaxWaitForReady.TotalMilliseconds}ms");
×
129
            }
130
        }
131
    }
16✔
132

133
    public void Dispose()
134
    {
135
        this.Disposing?.Invoke(this, EventArgs.Empty);
14!
136

137
        this.pipeStream?.Dispose();
14!
138

139
        this.pipeCancellation?.Cancel();
14!
140
    }
14✔
141
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc