-
Notifications
You must be signed in to change notification settings - Fork 7
/
base.util.input.bmx
executable file
·1107 lines (826 loc) · 28.8 KB
/
base.util.input.bmx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Rem
====================================================================
Input classes
====================================================================
There are 3 Managers:
TMouseManager: Mouse position, buttons ...
TKeyManager: key states ...
TKeyWrapper: managing "hold down" states for keys
====================================================================
If not otherwise stated, the following code is available under the
following licence:
LICENCE: zlib/libpng
Copyright (C) 2002-now Ronny Otto, digidea.de
This software is provided 'as-is', without any express or
implied warranty. In no event will the authors be held liable
for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it
and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
====================================================================
ENDREM
SuperStrict
Import brl.System
Import brl.PolledInput
Import Brl.vector
Import "base.util.vector.bmx"
Import "base.util.time.bmx"
Import "base.util.virtualgraphics.bmx"
Import "base.util.objectqueue.bmx"
Global MOUSEMANAGER:TMouseManager = New TMouseManager
Global KEYMANAGER:TKeyManager = New TKeyManager
Global KEYWRAPPER:TKeyWrapper = New TKeyWrapper
Const KEY_STATE_NORMAL:Int = 0 'nothing done
Const KEY_STATE_HIT:Int = 1 'once down, now up
Const KEY_STATE_DOWN:Int = 2 'down
Const KEY_STATE_UP:Int = 4 'up
Const KEY_STATE_BLOCKED:Int = 8
Const KEY_STATE_BLOCKED_TILL_RELEASE:Int = 16 'no keyhit/keydown/... until released first
For Local i:Int = 0 To 255
KEYWRAPPER.Init(i, 600, 100)
Next
AddHook(EmitEventHook, TMouseManager.InputHook,Null,0)
Rem
https://www.quirksmode.org/dom/events/click.html
https://developer.mozilla.org/en-US/docs/Web/API/Element/dblclick_event
-> they describe "doubleclick" as follow up of two clicks within
a given timeframe. So each "doubleclick" is preceeded by two clicks
Hint:
"click":
the button was down and is now released
"double click":
the button was clicked two times within a defined time
End Rem
Type TMouseManager
Field currentPos:TVec2D = New TVec2D.Init(0,0)
Field x:Int = 0.0
Field y:Int = 0.0
'amount of pixels moved (0=zero, -upwards, +downwards)
Field scrollWheelMoved:Int = 0
'time (in ms) to wait for another click to recognize doubleclicks
'so it is also the maximum time "between" that needed two clicks
Field doubleClickMaxTime:Int = 250
Field longClickMinTime:Int = 450
Field _lastPos:TVec2D = New TVec2D.Init(0,0)
Field _lastScrollWheel:Int = 0
'store the last click for each button
'so we can check if we moved "since then"
field lastClick:TMouseManagerClick[5]
field lastDoubleClick:TMouseManagerClick[5]
field lastLongClick:TMouseManagerClick[5]
field skipLongClicks:Int[] = [0,0,0,0,0]
field skipClicks:Int[] = [0,0,0,0,0]
'store up to 20 click information of all click types and all buttons
'so clicks between "updates" can still be processed one after another
'each information contains: position, click type and time of the click
Field _unhandledClicksStack:TObjectQueue
'once handled store them in there. Update() then clears this stack
'or removes the first from unhandled if none was handled that cycle
Field _handledClicksStack:TObjectQueue
Field _changedClicksUpdateCycle:Int
Field _updateCycle:Int
Field _lastMovementTime:Long
'previous position at the screen when a button was clicked, doubleclicked
'or longclicke the last time
Field _lastAnyClickPos:TVec2D[5]
'store clicks in the doubleclick timespan to identify double clicks
Field _clicksInDoubleClickTimeCount:Int[] = [0,0,0,0,0]
'down or not?
Field _down:Int[] = [0,0,0,0,0]
'time since when the button was pressed last
Field _downTime:Long[] = [0:Long,0:Long,0:Long,0:Long,0:Long]
Field _downStartPos:SVec2I[] = new SVec2I[5]
'can clicks/hits get read?
'special info read is possible (IsDown(), IsNormal())
Field _enabled:Int[] = [True, True, True, True, True]
'TOUCH emulation
Field _longClickModeEnabled:Int = False
Field _longClickLeadsToRightClick:Int = True
'skip first click (touch screens)
Field _ignoreFirstClick:Int = False
Field _hasIgnoredFirstClick:Int[] = [0,0,0,0,0] 'currently ignoring?
Field _ignoredFirstClickPos:TVec2D[] = [New TVec2D, New TVec2D, New TVec2D, New TVec2D, New TVec2D]
'distance in pixels, if mouse moved further away, the next
'click will get ignored ("tap")
Field _minSwipeDistance:Int = 10
Global evMouseDown:Int[5]
Global evMouseHits:Int[5]
Global evMousePosX:Int
Global evMousePosY:Int
Global evMouseZ:Int
Global CLICKTYPE_CLICK:Int = 1
Global CLICKTYPE_LONGCLICK:Int = 2
Global CLICKTYPE_DOUBLECLICK:Int = 3
'handle all incoming input events
Function InputHook:Object( id:Int, data:Object, context:Object )
Local ev:TEvent = TEvent(data)
If Not ev Return data
'filter by source?
'If ev.source = ... Return data
Select ev.id
Case EVENT_MOUSEDOWN
'we only handle up to the 5h mousebutton
If ev.data > 0 And ev.data <= 5
If Not evMouseDown[ev.data-1]
evMouseDown[ev.data-1] = 1
evMouseHits[ev.data-1] :+ 1
EndIf
MouseManager._HandleButtonEvent(ev.data)
EndIf
Case EVENT_MOUSEUP
'we only handle up to the 5h mousebutton
If ev.data > 0 And ev.data <= 5
evMouseDown[ev.data-1] = 0
MouseManager._HandleButtonEvent(ev.data)
EndIf
Case EVENT_MOUSEMOVE
evMousePosX = ev.x
evMousePosY = ev.y
Case EVENT_MOUSEWHEEL
evMouseZ :+ ev.data
Case EVENT_APPSUSPEND, EVENT_APPRESUME
evMouseDown = New Int[5] 'flush
evMouseHits = New Int[5] 'flush
evMouseZ = 0'flush
End Select
Return data
End Function
Method New()
_unhandledClicksStack = new TObjectQueue
_handledClicksStack = new TObjectQueue
End Method
Method _GetClickEntry:TMouseManagerClick()
If _unhandledClicksStack.IsEmpty() Then Return Null
Return TMouseManagerClick(_unhandledClicksStack.Peek())
End Method
'remove X added clicks from the stack (the "oldest" first)
Method _RemoveClickEntry:Int(clickCount:Int = 1)
'remove all
If clickCount < 0
_unhandledClicksStack.Clear()
'remove (up to) defined amount
Else
For Local i:Int = 0 Until clickCount
If _unhandledClicksStack.IsEmpty() Then exit
_unhandledClicksStack.Dequeue()
Next
EndIf
Return True
End Method
Method _RemoveClickEntries:Int(button:Int = -1, clickType:Int = -1)
local clicks:object[] = _unhandledClicksStack.ToArray()
_unhandledClicksStack.Clear()
For local c:TMouseManagerClick = EachIn clicks
if button >= 0 and c.button <> button then continue
if clickType >= 0 and c.clickType <> clickType then continue
_unhandledClicksStack.Enqueue(c)
Next
End Method
Method _AddClickEntry:TMouseManagerClick(button:Int, clickType:Int = -1, position:TVec2D, time:Long = -1)
'store for next update cycle
Local click:TMouseManagerClick = New TMouseManagerClick.Init(button, clickType, position.Copy(), time)
'print "_addClickentry: " + _updateCycle
_unhandledClicksStack.Enqueue( click )
_changedClicksUpdateCycle = _updateCycle
Select clickType
case CLICKTYPE_DOUBLECLICK
lastDoubleClick[button-1] = click
case CLICKTYPE_LONGCLICK
lastLongClick[button-1] = click
default
lastClick[button-1] = click
End Select
Return click
End Method
'return amount of managed buttons
Method GetButtonCount:Int()
Return _down.length 'ignore 0
End Method
Method SkipNextLongClick:Int(button:Int, amount:Int = 1)
skipLongClicks[button-1] = amount
End Method
Method SkipNextClick:Int(button:Int, amount:Int = 1)
skipClicks[button-1] = amount
End Method
'reset the state of the given button
Method ResetButton:Int(button:Int)
_downTime[button-1] = 0
_down[button-1] = False
'remove all potentially logged clicks for that button
ResetClicks(button, -1)
Return KEY_STATE_NORMAL
End Method
Method ResetClicks(button:Int, clickType:Int = -1)
_RemoveClickEntries(button, clickType)
End Method
Method ResetClicked(button:Int)
ResetClicks(button, CLICKTYPE_CLICK)
End Method
Method ResetDoubleClicked(button:Int)
ResetClicks(button, CLICKTYPE_DOUBLECLICK)
End Method
Method ResetLongClicked(button:Int)
ResetClicks(button, CLICKTYPE_LONGCLICK)
End Method
'remove all logged clicks older than "age" milliseconds
Method RemoveOutdatedClicks:Int(age:Int)
Local removed:Int = 0
Local t:Long = Time.GetAppTimeGone()
Local click:TMouseManagerClick = _GetClickEntry()
While click and click.t + age < t
_RemoveClickEntry()
click = _GetClickEntry()
Wend
End Method
'set the current single/double/long click of one of the mousebutton
'handled
'TODO: remove button, kept it in for compatiblity (in case we split
' it clickStack up for each button)
Method SetClickHandled:Int(button:Int)
local c:TMouseManagerClick = _GetClickEntry()
if not c then Return False
c.handled = True
_RemoveClickEntry()
'add to handled clicks
_handledClicksStack.Enqueue(c)
_changedClicksUpdateCycle = _updateCycle
Return True
End Method
'TODO: remove, kept it in for compatiblity (in case we split
' it clickStack up for each button)
Method SetLongClickHandled:Int(button:Int)
Return SetClickHandled(button)
End Method
'TODO: remove, kept it in for compatiblity (in case we split
' it clickStack up for each button)
Method SetDoubleClickHandled:Int(button:Int)
Return SetClickHandled(button)
End Method
Method Disable(button:Int)
_enabled[button-1] = False
End Method
Method Enable(button:Int)
_enabled[button-1] = True
End Method
Method IsEnabled:Int(button:Int)
Return _enabled[button-1]
End Method
'returns whether the button is in normal state
Method IsNormal:Int(button:Int)
'currently ignoring the key?
If _ignoreFirstClick And Not _hasIgnoredFirstClick[button-1] Then Return True
Return Not _down[button-1]
End Method
'returns whether the button is in down state
Method isDown:Int(button:Int)
'currently ignoring the key?
If _ignoreFirstClick And Not _hasIgnoredFirstClick[button-1] Then Return False
Return _down[button-1]
End Method
'returns whether the button got clicked, no waiting time
'is added
Method IsClicked:Int(button:Int)
If Not _enabled[button-1] Then Return False
Local c:TMouseManagerClick = _GetClickEntry()
If Not c Then Return False
Return not c.handled and c.button = button and c.clickType = CLICKTYPE_CLICK
End Method
'returns whether the button was clicked 2 times
Method IsDoubleClicked:Int(button:Int)
If Not _enabled[button-1] Then Return False
Local c:TMouseManagerClick = _GetClickEntry()
If Not c Then Return False
Return not c.handled and c.button = button and c.clickType = CLICKTYPE_DOUBLECLICK
End Method
'returns whether the button was clicked 2 times
Method IsLongClicked:Int(button:Int)
If Not _longClickModeEnabled Then Return False
If Not _enabled[button-1] Then Return False
Local c:TMouseManagerClick = _GetClickEntry()
If Not c Then Return False
Return not c.handled and c.button = button and c.clickType = CLICKTYPE_LONGCLICK
End Method
'returns whether the mouse moved between two updates
Method HasMoved:Int()
Return (0 <> GetMovedDistance())
End Method
'returns whether the mouse moved since last click of the button
Method HasMovedSinceClick:Int(button:Int)
Return (0 <> GetMovedDistanceSinceClick(button))
End Method
Method GetMovedDistance:Int()
Return Sqr((_lastPos.X - Int(x))^2 + (_lastPos.y - Int(y))^2)
End Method
Method GetLastMovedTime:Long()
Return _lastMovementTime
End Method
Method GetMovedDistanceSinceClick:Int(button:Int, clickType:Int = -1)
If clickType = -1 Then clickType = CLICKTYPE_CLICK
'fetch last click
Local c:TMouseManagerClick
Select clickType
case CLICKTYPE_DOUBLECLICK
c = lastDoubleClick[button]
case CLICKTYPE_LONGCLICK
c = lastLongClick[button]
default
c = lastClick[button]
End Select
If Not c Then Return 0
Return Sqr((c.position.x - Int(x))^2 + (c.position.y - Int(y))^2)
End Method
Method GetClickTime:Long()
Local c:TMouseManagerClick = _GetClickEntry()
If Not c Then Return 0
If c.handled Then Return 0
Return c.t
End Method
'TODO: remove button, kept it in for compatiblity (in case we split
' it clickStack up for each button)
Method GetClickPosition:TVec2D(button:int)
Local c:TMouseManagerClick = _GetClickEntry()
If Not c Then Return Null
If c.handled Then Return Null
Return c.position
End Method
Method GetDownStartPosition:SVec2I(button:Int)
if _down[button-1]
Return _downStartPos[button-1]
else
Return new SVec2I(-1,-1)
Endif
End Method
Method GetPosition:TVec2D()
Return currentPos
End Method
Method GetX:Int()
Return Int(currentPos.x)
End Method
Method GetY:Int()
Return Int(currentPos.y)
End Method
'returns how many milliseconds a button is down
Method GetDownTime:Long(button:Int)
If _downTime[button-1] > 0
Return Time.GetAppTimeGone() - _downTime[button-1]
Else
Return 0
EndIf
End Method
'returns positive or negative value describing the movement
'of the scrollwheel
Method GetScrollwheelMovement:Int()
Return scrollWheelMoved
End Method
'returns the amount of clicks
Method GetClicks:Int(button:Int, clickType:Int = -1)
Local c:TMouseManagerClick = _GetClickEntry()
If Not c Then Return 0
if button >= 0 and c.button <> button Then Return 0
if clickType >= 0 and c.clickType <> clickType Then Return 0
Return 1
End Method
'returns the amount of double clicks
Method GetDoubleClicks:Int(button:Int)
Local c:TMouseManagerClick = _GetClickEntry()
If Not c Then Return 0
if button >= 0 and c.button <> button Then Return 0
if c.clickType <> CLICKTYPE_DOUBLECLICK Then Return 0
Return 1
End Method
'returns the amount of long clicks
Method GetLongClicks:Int(button:Int)
Local c:TMouseManagerClick = _GetClickEntry()
If Not c Then Return 0
if button >= 0 and c.button <> button Then Return 0
if c.clickType <> CLICKTYPE_LONGCLICK Then Return 0
Return 1
End Method
'returns array of bools describing down state of each button
Method GetAllIsDown:Int[]()
Return [False,..
IsDown(1), ..
IsDown(2), ..
IsDown(3) ..
]
End Method
'returns array of bools describing clicked state of each button
Method GetAllIsClicked:Int[]()
Return [False,..
IsClicked(1), ..
IsClicked(2), ..
IsClicked(3) ..
]
End Method
'returns array of bools describing clicked state of each button
Method GetAllIsDoubleClicked:Int[]()
Return [False,..
IsDoubleClicked(1),..
IsDoubleClicked(2),..
IsDoubleClicked(3)..
]
End Method
'returns array of bools describing clicked state of each button
Method GetAllIsLongClicked:Int[]()
Return [False,..
IsLongClicked(1), ..
IsLongClicked(2), ..
IsLongClicked(3) ..
]
End Method
'-----
'Update the button states
Method Update:Int()
'if nobody was handling something for more than a cycle
'remove the oldest/first unhandled one
If _changedClicksUpdateCycle <> _updateCycle and _unhandledClicksStack and _unhandledClicksStack.Count() > 0
'print "updatecycle="+ _updateCycle + " Removing " + _unhandledClicksStack.Count() + " unhandled click(s). " + Millisecs()
'print "- " + TMouseManagerClick( _unhandledClicksStack.Dequeue() ).ToString()
_unhandledClicksStack.Dequeue()
EndIf
'some click got handled?
If _handledClicksStack and _handledClicksStack.Count() > 0
'print "Removing " + _handledClicksStack.Count() + " handled clicks. " + Millisecs()
_handledClicksStack.Clear()
EndIf
_updateCycle :+ 1
'SCROLLWHEEL
'by default scroll wheel did not move
scrollWheelMoved = 0
If _lastScrollWheel <> evMouseZ
scrollWheelMoved = -evMouseZ
_lastScrollWheel = -evMouseZ
evMouseZ = 0
EndIf
'POSITION
'store last position
_lastPos.SetXY(x, y)
'update current position
x = TVirtualGfx.getInstance().VMouseX()
y = TVirtualGfx.getInstance().VMouseY()
currentPos.x = x
currentPos.y = y
'MOVEMENT TIME
If HasMoved()
_lastMovementTime = Time.GetTimeGone()
EndIf
For Local i:Int = 0 Until GetButtonCount()
If _ignoreFirstClick And _lastAnyClickPos[i] And _lastAnyClickPos[i].DistanceTo(currentPos) > _minSwipeDistance
_hasIgnoredFirstClick[i] = False
EndIf
Next
End Method
'=== INTERNAL ===
'called on incoming OS mouse events
Method _HandleButtonEvent:Int(button:Int)
Local buttonIndex:Int = button - 1
'press (from "up" to "down")
If evMouseDown[buttonIndex] And Not _down[buttonIndex]
If Not _enabled[buttonIndex] Then Return False
'Print Time.GetTimeGone() + " normal => down x="+x + " y="+y
_down[buttonIndex] = True
_downTime[buttonIndex] = Time.GetAppTimeGone()
_downStartPos[buttonIndex] = new SVec2I(x, y)
EndIf
'release -> click (from down to up)
If Not evMouseDown[buttonIndex] And _down[buttonIndex]
If Not _enabled[buttonIndex]
'if not enabled we still return to "NORMAL" and are no longer
'down but do not count the hit/click
_downTime[buttonIndex] = 0
_down[buttonIndex] = False
Return False
EndIf
Local t:Long = Time.GetAppTimeGone()
Local timeSinceLastClick:Int = 0
if lastClick[buttonIndex] then timeSinceLastClick = t - lastClick[buttonIndex].t
If timeSinceLastClick < doubleClickMaxTime
_clicksInDoubleClickTimeCount[buttonIndex] :+ 1
'Print Time.GetTimeGone() + " down => up => click within doubleclick time clicks=" + (_clicksInDoubleClickTimeCount[buttonIndex])
Else
'Print Time.GetTimeGone() + " down => up => reset doubleclick time"
_clicksInDoubleClickTimeCount[buttonIndex] = 1
EndIf
Local currentPosVec:TVec2D = New TVec2D.Init(x, y)
'check for swipe
If _hasIgnoredFirstClick[buttonIndex] And _lastAnyClickPos[buttonIndex] And _lastAnyClickPos[buttonIndex].DistanceTo(currentPosVec) > _minSwipeDistance
'Print "reset " + _lastAnyClickPos[buttonIndex].DistanceTo(currentPosVec) +" > "+_minSwipeDistance
_hasIgnoredFirstClick[buttonIndex] = False
EndIf
'store last click pos - even if we ignore the effect of the click
_lastAnyClickPos[buttonIndex] = currentPosVec
'- Ignore first click?
'- Long click?
'- Normal click + Double clicks?
'ignore this potential click if it is the first one
If _ignoreFirstClick And Not _hasIgnoredFirstClick[buttonIndex]
_hasIgnoredFirstClick[buttonIndex] = True
'Print Time.GetTimeGone() + " click => ignored first click"
Else
' check for a long click
If _longClickModeEnabled And _downTime[buttonIndex] + longClickMinTime < t
if skipLongClicks[buttonIndex] > 0
skipLongClicks[buttonIndex] :- 1
else
_AddClickEntry(button, CLICKTYPE_LONGCLICK, currentPosVec.Copy(), t)
'emulating right click?
If _longClickLeadsToRightClick And button = 1
_AddClickEntry(2, CLICKTYPE_CLICK, currentPosVec.Copy(), t)
_downTime[2] = _downTime[1]
EndIf
endif
'Print Time.GetTimeGone() + " click => long clicked downTime="+_downTime[button] +" longClickMinTime="+longClickMinTime +" button="+button
' normal click + double clicks
Else
'Print Time.GetTimeGone() + " down => up => click GetClicks( " + button + ")=" + GetClicks(button) + " _clicksInDoubleClickTimeCount["+buttonIndex+"]="+_clicksInDoubleClickTimeCount[buttonIndex]
if skipClicks[buttonIndex] > 0
skipClicks[buttonIndex] :- 1
else
_AddClickEntry(button, CLICKTYPE_CLICK, currentPosVec.Copy(), t)
'double clicks (additionally to normal clicks!)
If _clicksInDoubleClickTimeCount[buttonIndex] >= 2
_AddClickEntry(button, CLICKTYPE_DOUBLECLICK, currentPosVec.Copy(), t)
_clicksInDoubleClickTimeCount[buttonIndex] :- 2
EndIf
endif
EndIf
EndIf
'reset mousedown time - no longer happening
_downTime[buttonIndex] = 0
_down[buttonIndex] = False
EndIf
End Method
EndType
'record for each single click
Type TMouseManagerClick
Field button:Int
Field clickType:Int = 1
Field position:TVec2D
Field t:Long
Field handled:Int
Field id:int
Global lastID:int = 0
Method New()
lastID :+ 1
id = lastID
End Method
Method Init:TMouseManagerClick(button:Int, clickType:Int, position:TVec2D, clickTime:Long = -1)
If clickTime = -1 Then clickTime = Time.GetAppTimeGone()
Self.button = button
Self.clickType = clickType
Self.position = position
Self.handled = False
Self.t = clickTime
Return Self
End Method
Method ToString:String()
Select clickType
case TMouseManager.CLICKTYPE_CLICK
Return "Click #" + id +". button=" + button + " clickType=" + clickType + " [singleclick] position="+position.ToString() + " t=" + t + " handled="+handled
case TMouseManager.CLICKTYPE_DOUBLECLICK
Return "Click #" + id +". button=" + button + " clickType=" + clickType + " [doubleclick] position="+position.ToString() + " t=" + t + " handled="+handled
case TMouseManager.CLICKTYPE_LONGCLICK
Return "Click #" + id +". button=" + button + " clickType=" + clickType + " [longclick] position="+position.ToString() + " t=" + t + " handled="+handled
default
Return "Click #" + id +". button=" + button + " clickType=" + clickType + " [unknown] position="+position.ToString() + " t=" + t + " handled="+handled
End Select
End Method
End Type
Type TKeyManager
'status of all keys
Field _keyStatus:Int[256]
Field _downTime:Long[256]
Field _blockKeyTime:Long[256]
Global processedAppSuspend:Int = False
'returns whether the button is in normal state
Method isNormal:Int(key:Int)
Return _keyStatus[Key] = KEY_STATE_NORMAL
End Method
'returns whether the button is currently blocked (time wise)
Method isBlocked:Int(key:Int)
'this time it is a bitmask flag (normal/hit/.. + blocked)
Return (_keyStatus[key] & KEY_STATE_BLOCKED)
End Method
Method isBlockedTillRelease:Int(key:Int)
'this time it is a bitmask flag (normal/hit/.. + blocked)
Return (_keyStatus[key] & KEY_STATE_BLOCKED_TILL_RELEASE)
End Method
'returns whether the button is in hit state
Method isHit:Int(key:Int)
Return _keyStatus[key] = KEY_STATE_HIT
End Method
'returns whether the button is in down state
Method isDown:Int(key:Int)
Return _keyStatus[key] = KEY_STATE_DOWN
End Method
'returns whether the button is in up state
Method isUp:Int(key:Int)
Return _keyStatus[key] = KEY_STATE_UP
End Method
'returns how many milliseconds a key is down
Method GetDownTime:Long(key:Int)
If _downTime[key-1] > 0
Return Time.GetAppTimeGone() - _downTime[key-1]
Else
Return 0
EndIf
End Method
'refresh all key states
Method Update:Int()
If AppSuspended()
If Not processedAppSuspend
FlushKeys()
processedAppSuspend = True
EndIf
ElseIf processedAppSuspend
processedAppSuspend = False
EndIf
Local nowTime:Long = Time.GetAppTimeGone()
For Local i:Int = 1 To 255
'ignore key if it is blocked
'or set back to "normal" afterwards
If _blockKeyTime[i] > nowTime
_keyStatus[i] :| KEY_STATE_BLOCKED
ElseIf isBlocked(i)
_downTime[i] = 0
_keyStatus[i] :& ~KEY_STATE_BLOCKED
' 'meanwhile a hit can get renewed
If _keyStatus[i] = KEY_STATE_HIT
_keyStatus[i] = KEY_STATE_NORMAL
EndIf
EndIf
'normal check
If not KeyDown(i) and isBlockedTillRelease(i)
_keyStatus[i] = KEY_STATE_NORMAL
EndIf
If _keyStatus[i] = KEY_STATE_NORMAL
If KeyDown(i)
_downTime[i] = nowTime
_keyStatus[i] = KEY_STATE_HIT
EndIf
ElseIf _keyStatus[i] = KEY_STATE_HIT
If KeyDown(i)
_keyStatus[i] = KEY_STATE_DOWN
Else
_downTime[i] = 0
_keyStatus[i] = KEY_STATE_UP
EndIf
ElseIf _keyStatus[i] = KEY_STATE_DOWN
If Not KeyDown(i)
_downTime[i] = 0
_keyStatus[i] = KEY_STATE_UP
EndIf
ElseIf _keyStatus[i] = KEY_STATE_UP
_keyStatus[i] = KEY_STATE_NORMAL
EndIf
Next
'update wrapper too
KeyWrapper.Update()
End Method
'returns the status of a key
Method getStatus:Int(key:Int)
Return _keyStatus[key]
End Method
'set a key as blocked for the given time
Method BlockKey:Int(key:Int, milliseconds:Int=0)
'time can be absolute as a key block is just for blocking a key
'which has not to be deterministic
_blockKeyTime[key] = Time.GetAppTimeGone() + milliseconds
'also add the block state to the current status
_keyStatus[key] :| KEY_STATE_BLOCKED
End Method
Method BlockKeyTillRelease:Int(key:Int)
'also add the block state to the current status
_keyStatus[key] :| KEY_STATE_BLOCKED_TILL_RELEASE
End Method
'resets the keys status
Method ResetKey:Int(key:Int)
_keyStatus[key] = KEY_STATE_UP
Return KEY_STATE_UP
End Method
EndType
Const KEYWRAP_ALLOW_HIT:Int = 1
Const KEYWRAP_ALLOW_HOLD:Int= 2
Const KEYWRAP_ALLOW_BOTH:Int= 3
Type TKeyWrapper
Field keyHoldInformation:TKeyHoldInformation[256]
Method New()
For local i:int = 0 to 255
keyHoldInformation[i] = new TKeyHoldInformation(i, 600, 100)
Next
End Method
Method Init(key:Int, firstHoldTime:Int=600, holdTime:Int=100)
keyHoldInformation[key].Init(key, firstHoldTime, holdTime)
End Method
Method Update:Int()
For local key:int = 0 To 255
keyHoldInformation[key].Update( KeyManager._keyStatus[key] )
Next
End Method
'returns if key is currently hit or "hold" (and enough holding time
'is gone)
Method IsPressed:Int(key:Int)
Return keyHoldInformation[key].IsPressed()
End Method
Method IsHit:Int(key:Int)
Return keyHoldInformation[key].IsHit()
End Method
'returns if a key is currently hold
Method IsHold:Int(key:Int)
Return keyHoldInformation[key].IsHold()
End Method
Method ResetKey(key:Int)
keyHoldInformation[key].Reset()
End Method
End Type