# Any comment on this task ("click" or "double click")?

**URL:** https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127
**Category:** General
**Created:** [August 29, 2024, 8:07am UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127 "2024-08-29T08:07:59Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![reinert](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/r/46a35a/32.png) [@reinert](https://forum.ada-lang.io/u/reinert)
#### Post date: [August 29, 2024, 8:08am UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127/1 "2024-08-29T08:08:00Z")

</div>

Hello,

I explored the possibility of making my program distinguish between a ‘click’ and a ‘double-click.’ For example, if the user presses the ‘i’ key only once, the program does ‘this’ (p1), and if the user presses ‘i’ repeatedly in quick succession, the program does ‘that’ (p2). Note that the program must wait a bit to see if another “i” comes after first receiving one. I ended up with this task:

task body task\_react1\_t is  
t : constant ada.real\_time.time := ada.real\_time.clock;  
i : integer := 0;  
p1b,p2b : Procedure\_Access;  
begin  
while i \< 2 and ada.real\_time.clock \< t + dclick1 loop  
select  
accept key1(p1,p2 : Procedure\_Access) do  
i := @ + 1;  
p1b := p1;  
p2b := p2;  
end;  
else  
null;  
end select;  
end loop;  
– Conclusion:  
case i is  
when 1 =\> p1b.all;  
when 2 =\> p2b.all;  
when others =\> raise error with "In task\_react1\_t, i: " & i’Image;  
end case;  
abort task\_react1\_t;  
end task\_react1\_t;

I am just smart enough to understand that I am not so smart. Hence, some comments would be nice.

reinert

---

<div class="post-metadata">

### Author: ![simonjwright](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/simonjwright/32/11_2.png) [@simonjwright](https://forum.ada-lang.io/u/simonjwright)
#### Post date: [August 29, 2024, 2:45pm UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127/2 "2024-08-29T14:45:35Z")

</div>

@reinert, could you edit your code to insert three backticks (“```”) before and after it? that will retain the formatting. As it is, it’s hard to see what’s going on.

I think you’re starting a new instance of the task every time a first-key in a potential key-sequence is pressed. Most people would prefer to have a permanent task, with a loop inside it.

You don’t need to abort the task at the end, just let it drop through.

---

<div class="post-metadata">

### Author: ![reinert](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/r/46a35a/32.png) [@reinert](https://forum.ada-lang.io/u/reinert)
#### Post date: [August 29, 2024, 7:22pm UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127/3 "2024-08-29T19:22:17Z")

</div>

Here I try you first advice:

```ada
   task body task_react1_t is
     t : constant ada.real_time.time := ada.real_time.clock;
     i : integer := 0;
     p1b,p2b : Procedure_Access;
   begin
     while i < 2 and ada.real_time.clock < t + dclick1 loop
       select
          accept key1(p1,p2 : Procedure_Access) do
             i := @ + 1;
             p1b := p1;
             p2b := p2;
          end;
       else
          null;
       end select;
     end loop;
-- Conclusion:
     case i is
       when 1 => p1b.all;
       when 2 => p2b.all;
       when others => raise error with "In task_react1_t, i: " & i'Image;
     end case;
     abort task_react1_t;
   end task_react1_t;

```

You have resolved one of my frustrations 🙂

I’ll post an update after first trying out.

reinert

---

<div class="post-metadata">

### Author: ![sidisyom](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/s/5fc32e/32.png) [@sidisyom](https://forum.ada-lang.io/u/sidisyom)
#### Post date: [September 1, 2024, 3:08pm UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127/4 "2024-09-01T15:08:50Z")

</div>

An alternative to the rendezvous option could be to use a protected object.

The specification would be something along the lines of:

```ada
with Ada.Real_Time.Timing_Events;
use Ada.Real_Time.Timing_Events;

package Keyboard_Monitors is
   
   type Typing_Pattern is (Once, Twice);

   protected type Keyboard_Monitor is
      procedure Key_Pressed;
      entry Completed(TP: out Typing_Pattern);
   private      
      procedure Event_Handler(Event: in out Timing_Event);
      Event: Timing_Event;
      First: Boolean := True;
      OK: Boolean := False;
      Outcome: Typing_Pattern := Once;
   end Keyboard_Monitor;
end Keyboard_Monitors;

```

with an implementation of:

```ada
with Ada.Real_Time;
use Ada.Real_Time; 

package body Keyboard_Monitors is
   
   Double_Click_Deadline: constant Time_Span := Milliseconds(1_000);

   protected body Keyboard_Monitor is
      
      procedure Key_Pressed is
         Cancelled: Boolean := False;
      begin
         if First then
            Cancel_Handler(Event, Cancelled);
            Outcome := Twice;
            OK := True;
         else
            First := True;
            Set_Handler(Event, Double_Click_Deadline, Event_Handler'Access);
         end if;
      end Key_Pressed;
      
      procedure Reset is
      begin
         First := False;
         OK := False;
         Outcome := Once;
      end Reset;
            
      entry Completed(TP: out Typing_Pattern) when OK is
      begin
         TP := Outcome;
         Reset;
      end Completed;
      
      procedure Event_Handler(Event: in out Timing_Event) is
      begin
         Outcome := Once;
         OK := True;
      end Event_Handler;                              
   end Keyboard_Monitor;
end Keyboard_Monitors;

```

and then in the client task (perhaps the main task)

```ada
with Keyboard_Monitors; use Keyboard_Monitors;

procedure keyboard_action is
   KM: Keyboard_Monitor;
   TP: Typing_Pattern;
begin
   loop
      KM.Completed(TP);
      case TP is
         when Once => null; -- i.e. p1b.all
         when Twice => null; -- i.e. p2b.all         
      end case;
   end loop;
end keyboard_action;

```

A bit more verbose arguably, but avoids the spin-loop and the need to create a task for a typing monitoring session.

Disclaimer: Haven’t tested that so not entirely convinced it’s race-free but assuming tasks blocked on a barrier are given precedence over the timing-event handler (which I _believe_ is the case) it should be.

---

<div class="post-metadata">

### Author: ![sidisyom](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/s/5fc32e/32.png) [@sidisyom](https://forum.ada-lang.io/u/sidisyom)
#### Post date: [September 1, 2024, 3:15pm UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127/5 "2024-09-01T15:15:15Z")

</div>

Just an observation that if re-entrant entries (and `accept` statements within the abortable section of the ATC) were somehow allowed this could be implemented somewhat more elegantly using a rendezvous + ATC pattern:

```ada
with Ada.Real_Time; use Ada.Real_Time;

procedure Typing_Counter is
         
   task T1 is
      entry S1;
   end T1;
   
   task body T1 is
      Double_Click_Deadline: constant Time_Span := Milliseconds(1_000);
   begin
      loop
         select
            accept S1 do
               select
                  delay until Clock + Double_Click_Deadline;
                  -- action for Once
               then abort
                  accept S1 do
                     -- action for Twice
                     null;
                  end S1;
               end select;
            end S1;
         else
            null;
         end select;
      end loop;
   end T1;
begin
   null;
end Typing_Counter;

```

(which of-course doesn’t compile 🙂)

---

<div class="post-metadata">

### Author: ![simonjwright](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/simonjwright/32/11_2.png) [@simonjwright](https://forum.ada-lang.io/u/simonjwright)
#### Post date: [September 1, 2024, 6:35pm UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127/6 "2024-09-01T18:35:54Z")

</div>

I think the state machine implemented in the PO is much better (especially since - at first glance - it can be implemented in Ravenscar). I might be inclined to name the states in an enumeration, especially if the SM got more complicated (triple-press?!)

---

<div class="post-metadata">

### Author: ![sidisyom](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/s/5fc32e/32.png) [@sidisyom](https://forum.ada-lang.io/u/sidisyom)
#### Post date: [September 1, 2024, 10:47pm UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127/7 "2024-09-01T22:47:11Z")

</div>

That would make more sense. The pattern enum can be extended to  
`type Typing_Pattern is (None, Once, Twice, Thrice) -- or more`

and a current state variable introduced in the private part  
`Current: Typing_Pattern := None` so that `Key_Pressed` now becomes:

```ada
procedure Key_Pressed is
      Cancelled: Boolean := False;
begin
   if Current = Typing_Pattern'Last then
      Current := Typing_Pattern'First;
   else
      Current := Typing_Pattern'Succ(Current);
   end if;
   
   Cancel_Handler(Event, Cancelled); -- Safe even when event is not set
   
   if Current = Cutoff then
      Outcome := Current;
      OK := True;
   else
      Set_Handler(Event, Double_Click_Deadline, Event_Handler'Access);           
   end if;
end Key_Pressed;

```

where `Cutoff` can be passed as a discriminant on the PO.

---

<div class="post-metadata">

### Author: ![reinert](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/r/46a35a/32.png) [@reinert](https://forum.ada-lang.io/u/reinert)
#### Post date: [September 2, 2024, 6:27am UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127/8 "2024-09-02T06:27:28Z")

</div>

I would like the alternatives are only “once” or “twice” (i.e. only two alternatives). This means that if there is a double-click, the program does not need to wait more to see if there soon comes another (third) click.

reinert

---

<div class="post-metadata">

### Author: ![simonjwright](https://forum.ada-lang.io/user_avatar/forum.ada-lang.io/simonjwright/32/11_2.png) [@simonjwright](https://forum.ada-lang.io/u/simonjwright)
#### Post date: [September 2, 2024, 7:29am UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127/9 "2024-09-02T07:29:30Z")

</div>

That’s handled in the test `if Current = Cutoff then` - where `Cutoff` would be `Twice` - I think.

Many SMs are a lot more complicated than this, so (IMO) simple is better.

---

<div class="post-metadata">

### Author: ![reinert](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/r/46a35a/32.png) [@reinert](https://forum.ada-lang.io/u/reinert)
#### Post date: [September 2, 2024, 7:20pm UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127/10 "2024-09-02T19:20:22Z")

</div>

Just a cosmetic modification of the original version (shorter):

```ada
  task body task_react1_t is
     t : constant ada.real_time.time := ada.real_time.clock;
     i : integer := 0;
     p : Procedure_Access;
   begin
     while i < 2 and ada.real_time.clock < t + dclick1 loop
       select
          accept key1(p1,p2 : Procedure_Access) do
             i := @ + 1;
             p := (if i = 1 then p1 else p2);
          end;
       else
          null;
       end select;
     end loop;
     p.all;
     abort task_react1_t;
   end task_react1_t;

```

---

<div class="post-metadata">

### Author: ![reinert](https://forum.ada-lang.io/letter_avatar_proxy/v4/letter/r/46a35a/32.png) [@reinert](https://forum.ada-lang.io/u/reinert)
#### Post date: [September 11, 2024, 10:28am UTC](https://forum.ada-lang.io/t/any-comment-on-this-task-click-or-double-click/1127/11 "2024-09-11T10:28:52Z")

</div>

Another version using protected object (pause1\_t):

```ada
   protected body pause1_t is
      entry wait1 when ok is
      begin
         null;
      end wait1;

      procedure halt1 is
      begin
         ok := false;
      end halt1;

      procedure resume1 is
      begin
         ok := true;
      end resume1;
   end pause1_t;

```

react\_pause1 : pause1\_t;

```ada
   task body task_react1_t is
     t : ada.real_time.time := ada.real_time.clock;
     i : integer := 0;
     p : Procedure_Access;
   begin
     loop
        react_pause1.wait1;
        select
           accept init1 do
              t := ada.real_time.clock;
              i := 0;
           end;
        or
           accept key1(p1,p2 : Procedure_Access) do
              i := @ + 1;
              p := (if i = 1 then p1 else p2);
           end;
        or delay 0.05;
        end select;
        if (ada.real_time.clock > t + dclick1 and i = 1) or i > 1 then
           p.all;
           react_pause1.halt1;
           i := 0;
        end if;
     end loop;
   end task_react1_t;

```
