mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-13 12:23:38 +01:00
feat(komorebic): pan the Scrolling layout with the fingers on a touchpad
komorebic pan --stream reads the gestures of fingers on a touchpad from standard input, as whkd writes them for a swipe of three or four fingers (docs/gesture-stream.md in its repository): a line for each begin, move, end and cancel, with the distances in centimetres, and any field or message that it does not know ignored. The row follows the fingers while they are down, and when they lift it comes to rest with a column against an edge of the screen, going at the speed that the times of the moves say that the fingers were. Focus goes to the nearest column in view if the focused one is not. A gesture that is given up, or that is not finished when the stream stops, puts the row back to what it was. komorebic works out nothing but what it is asked to: --sensitivity multiplies the distances, --reversed flips the way that the row goes, and --paging takes a step for every two centimetres that the fingers go, as komorebic pan does, and not move the row with them. Moves that wait while one is told are added up, as whkd says that they may be. A gesture is told to komorebi on one connection, which it handles in order, as messages that are sent on a connection each are not. With ghost movement the pictures that cover the windows near the screen are taken when the fingers go down, a move of the fingers sets them where the row puts the windows and does nothing else, and when the fingers lift they glide to where it comes to rest, over the time that an animation takes, and the windows are shown. Without it the workspace is laid out a step at a time, as it is for a drag. Ten centimetres of the fingers move the row the width of the screen. The golden transcript of whkd and the swipes of its demo are fixtures of the tests that read the stream.
This commit is contained in:
1 parent
a63057c232
commit
a65b5c231e
15 files changed
+2001
-11
No files matched your search
@@ -0,0 +1,5 @@
|
||||
begin fingers=3 x=4.000 y=2.000 w=10.500 h=7.200 t=118337
|
||||
move dx=0.420 dy=-0.030 x=4.420 y=1.970 t=118345
|
||||
move dx=0.510 dy=-0.020 x=4.930 y=1.950 t=118353
|
||||
move dx=0.470 dy=0.000 x=5.400 y=1.950 t=118361
|
||||
end x=5.400 y=1.950 t=118369
|
||||
@@ -0,0 +1,18 @@
|
||||
begin fingers=3 x=5.000 y=3.000 w=11.700 h=7.500 t=100048
|
||||
move dx=0.267 dy=0.000 x=5.267 y=3.000 t=100088
|
||||
move dx=0.267 dy=0.000 x=5.533 y=3.000 t=100096
|
||||
move dx=0.267 dy=0.000 x=5.800 y=3.000 t=100104
|
||||
move dx=0.267 dy=0.000 x=6.067 y=3.000 t=100112
|
||||
move dx=0.267 dy=0.000 x=6.333 y=3.000 t=100120
|
||||
move dx=0.267 dy=0.000 x=6.600 y=3.000 t=100128
|
||||
swipe swipe3right
|
||||
move dx=0.267 dy=0.000 x=6.867 y=3.000 t=100136
|
||||
move dx=0.267 dy=0.000 x=7.133 y=3.000 t=100144
|
||||
move dx=0.267 dy=0.000 x=7.400 y=3.000 t=100152
|
||||
move dx=0.267 dy=0.000 x=7.667 y=3.000 t=100160
|
||||
move dx=0.267 dy=0.000 x=7.933 y=3.000 t=100168
|
||||
move dx=0.267 dy=0.000 x=8.200 y=3.000 t=100176
|
||||
move dx=0.267 dy=0.000 x=8.467 y=3.000 t=100184
|
||||
move dx=0.267 dy=0.000 x=8.733 y=3.000 t=100192
|
||||
move dx=0.267 dy=0.000 x=9.000 y=3.000 t=100200
|
||||
end x=9.000 y=3.000 t=100208
|
||||
@@ -0,0 +1,218 @@
|
||||
begin fingers=3 x=5.000 y=3.000 w=11.700 h=7.500 t=100048
|
||||
move dx=0.007 dy=0.000 x=5.015 y=3.001 t=100110
|
||||
move dx=0.008 dy=0.000 x=5.023 y=3.001 t=100119
|
||||
move dx=0.010 dy=0.001 x=5.033 y=3.002 t=100129
|
||||
move dx=0.012 dy=0.001 x=5.045 y=3.003 t=100136
|
||||
move dx=0.014 dy=0.001 x=5.059 y=3.004 t=100142
|
||||
move dx=0.015 dy=0.001 x=5.074 y=3.004 t=100153
|
||||
move dx=0.017 dy=0.001 x=5.091 y=3.005 t=100160
|
||||
move dx=0.018 dy=0.001 x=5.109 y=3.007 t=100168
|
||||
move dx=0.020 dy=0.001 x=5.129 y=3.008 t=100174
|
||||
move dx=0.022 dy=0.001 x=5.151 y=3.009 t=100184
|
||||
move dx=0.023 dy=0.001 x=5.174 y=3.010 t=100193
|
||||
move dx=0.025 dy=0.001 x=5.199 y=3.012 t=100199
|
||||
move dx=0.026 dy=0.002 x=5.225 y=3.013 t=100207
|
||||
move dx=0.027 dy=0.002 x=5.252 y=3.015 t=100217
|
||||
move dx=0.029 dy=0.002 x=5.281 y=3.017 t=100223
|
||||
move dx=0.030 dy=0.002 x=5.311 y=3.019 t=100231
|
||||
move dx=0.032 dy=0.002 x=5.343 y=3.021 t=100240
|
||||
move dx=0.033 dy=0.002 x=5.376 y=3.023 t=100248
|
||||
move dx=0.034 dy=0.002 x=5.410 y=3.025 t=100256
|
||||
move dx=0.035 dy=0.002 x=5.446 y=3.027 t=100262
|
||||
move dx=0.037 dy=0.002 x=5.482 y=3.029 t=100272
|
||||
move dx=0.038 dy=0.002 x=5.520 y=3.031 t=100281
|
||||
move dx=0.039 dy=0.002 x=5.559 y=3.034 t=100287
|
||||
move dx=0.040 dy=0.002 x=5.599 y=3.036 t=100294
|
||||
move dx=0.041 dy=0.002 x=5.640 y=3.038 t=100303
|
||||
move dx=0.042 dy=0.003 x=5.682 y=3.041 t=100312
|
||||
move dx=0.043 dy=0.003 x=5.726 y=3.044 t=100318
|
||||
move dx=0.044 dy=0.003 x=5.770 y=3.046 t=100327
|
||||
move dx=0.045 dy=0.003 x=5.815 y=3.049 t=100334
|
||||
move dx=0.046 dy=0.003 x=5.861 y=3.052 t=100342
|
||||
move dx=0.047 dy=0.003 x=5.909 y=3.055 t=100351
|
||||
move dx=0.048 dy=0.003 x=5.956 y=3.057 t=100360
|
||||
move dx=0.049 dy=0.003 x=6.005 y=3.060 t=100369
|
||||
move dx=0.050 dy=0.003 x=6.055 y=3.063 t=100375
|
||||
move dx=0.050 dy=0.003 x=6.105 y=3.066 t=100383
|
||||
move dx=0.051 dy=0.003 x=6.156 y=3.069 t=100390
|
||||
move dx=0.052 dy=0.003 x=6.208 y=3.072 t=100400
|
||||
move dx=0.053 dy=0.003 x=6.261 y=3.076 t=100406
|
||||
move dx=0.053 dy=0.003 x=6.314 y=3.079 t=100414
|
||||
move dx=0.054 dy=0.003 x=6.368 y=3.082 t=100423
|
||||
move dx=0.054 dy=0.003 x=6.422 y=3.085 t=100431
|
||||
move dx=0.055 dy=0.003 x=6.477 y=3.089 t=100439
|
||||
move dx=0.056 dy=0.003 x=6.533 y=3.092 t=100448
|
||||
swipe swipe3right
|
||||
move dx=0.056 dy=0.003 x=6.589 y=3.095 t=100454
|
||||
move dx=0.057 dy=0.003 x=6.646 y=3.099 t=100464
|
||||
move dx=0.057 dy=0.003 x=6.703 y=3.102 t=100472
|
||||
move dx=0.057 dy=0.003 x=6.760 y=3.106 t=100481
|
||||
move dx=0.058 dy=0.003 x=6.818 y=3.109 t=100487
|
||||
move dx=0.058 dy=0.003 x=6.876 y=3.113 t=100497
|
||||
move dx=0.058 dy=0.004 x=6.934 y=3.116 t=100504
|
||||
move dx=0.059 dy=0.004 x=6.993 y=3.120 t=100511
|
||||
move dx=0.059 dy=0.004 x=7.052 y=3.123 t=100520
|
||||
move dx=0.059 dy=0.004 x=7.111 y=3.127 t=100527
|
||||
move dx=0.059 dy=0.004 x=7.171 y=3.130 t=100535
|
||||
move dx=0.060 dy=0.004 x=7.230 y=3.134 t=100543
|
||||
move dx=0.060 dy=0.004 x=7.290 y=3.137 t=100552
|
||||
move dx=0.060 dy=0.004 x=7.350 y=3.141 t=100561
|
||||
move dx=0.060 dy=0.004 x=7.410 y=3.145 t=100567
|
||||
move dx=0.060 dy=0.004 x=7.470 y=3.148 t=100576
|
||||
move dx=0.060 dy=0.004 x=7.530 y=3.152 t=100584
|
||||
move dx=0.060 dy=0.004 x=7.590 y=3.155 t=100591
|
||||
move dx=0.060 dy=0.004 x=7.650 y=3.159 t=100600
|
||||
move dx=0.060 dy=0.004 x=7.710 y=3.163 t=100609
|
||||
move dx=0.060 dy=0.004 x=7.770 y=3.166 t=100615
|
||||
move dx=0.060 dy=0.004 x=7.829 y=3.170 t=100624
|
||||
move dx=0.059 dy=0.004 x=7.889 y=3.173 t=100632
|
||||
move dx=0.059 dy=0.004 x=7.948 y=3.177 t=100640
|
||||
move dx=0.059 dy=0.004 x=8.007 y=3.180 t=100648
|
||||
move dx=0.059 dy=0.004 x=8.066 y=3.184 t=100654
|
||||
move dx=0.058 dy=0.004 x=8.124 y=3.187 t=100664
|
||||
move dx=0.058 dy=0.003 x=8.182 y=3.191 t=100672
|
||||
move dx=0.058 dy=0.003 x=8.240 y=3.194 t=100680
|
||||
move dx=0.057 dy=0.003 x=8.297 y=3.198 t=100688
|
||||
move dx=0.057 dy=0.003 x=8.354 y=3.201 t=100694
|
||||
move dx=0.057 dy=0.003 x=8.411 y=3.205 t=100703
|
||||
move dx=0.056 dy=0.003 x=8.467 y=3.208 t=100712
|
||||
move dx=0.056 dy=0.003 x=8.523 y=3.211 t=100718
|
||||
move dx=0.055 dy=0.003 x=8.578 y=3.215 t=100728
|
||||
move dx=0.054 dy=0.003 x=8.632 y=3.218 t=100735
|
||||
move dx=0.054 dy=0.003 x=8.686 y=3.221 t=100744
|
||||
move dx=0.053 dy=0.003 x=8.739 y=3.224 t=100752
|
||||
move dx=0.053 dy=0.003 x=8.792 y=3.228 t=100760
|
||||
move dx=0.052 dy=0.003 x=8.844 y=3.231 t=100769
|
||||
move dx=0.051 dy=0.003 x=8.895 y=3.234 t=100774
|
||||
move dx=0.050 dy=0.003 x=8.945 y=3.237 t=100785
|
||||
move dx=0.050 dy=0.003 x=8.995 y=3.240 t=100792
|
||||
move dx=0.049 dy=0.003 x=9.044 y=3.243 t=100799
|
||||
move dx=0.048 dy=0.003 x=9.091 y=3.245 t=100809
|
||||
move dx=0.047 dy=0.003 x=9.139 y=3.248 t=100815
|
||||
move dx=0.046 dy=0.003 x=9.185 y=3.251 t=100824
|
||||
move dx=0.045 dy=0.003 x=9.230 y=3.254 t=100832
|
||||
move dx=0.044 dy=0.003 x=9.274 y=3.256 t=100839
|
||||
move dx=0.043 dy=0.003 x=9.318 y=3.259 t=100846
|
||||
move dx=0.042 dy=0.003 x=9.360 y=3.262 t=100856
|
||||
move dx=0.041 dy=0.002 x=9.401 y=3.264 t=100863
|
||||
move dx=0.040 dy=0.002 x=9.441 y=3.266 t=100873
|
||||
move dx=0.039 dy=0.002 x=9.480 y=3.269 t=100880
|
||||
move dx=0.038 dy=0.002 x=9.518 y=3.271 t=100887
|
||||
move dx=0.037 dy=0.002 x=9.554 y=3.273 t=100897
|
||||
move dx=0.035 dy=0.002 x=9.590 y=3.275 t=100904
|
||||
move dx=0.034 dy=0.002 x=9.624 y=3.277 t=100912
|
||||
move dx=0.033 dy=0.002 x=9.657 y=3.279 t=100918
|
||||
move dx=0.032 dy=0.002 x=9.689 y=3.281 t=100929
|
||||
move dx=0.030 dy=0.002 x=9.719 y=3.283 t=100935
|
||||
move dx=0.029 dy=0.002 x=9.748 y=3.285 t=100944
|
||||
move dx=0.027 dy=0.002 x=9.775 y=3.287 t=100952
|
||||
move dx=0.026 dy=0.002 x=9.801 y=3.288 t=100959
|
||||
move dx=0.025 dy=0.001 x=9.826 y=3.290 t=100968
|
||||
move dx=0.023 dy=0.001 x=9.849 y=3.291 t=100974
|
||||
move dx=0.022 dy=0.001 x=9.871 y=3.292 t=100983
|
||||
move dx=0.020 dy=0.001 x=9.891 y=3.293 t=100991
|
||||
move dx=0.018 dy=0.001 x=9.909 y=3.295 t=101001
|
||||
move dx=0.017 dy=0.001 x=9.926 y=3.296 t=101008
|
||||
move dx=0.015 dy=0.001 x=9.941 y=3.296 t=101016
|
||||
move dx=0.014 dy=0.001 x=9.955 y=3.297 t=101023
|
||||
move dx=0.012 dy=0.001 x=9.967 y=3.298 t=101033
|
||||
move dx=0.010 dy=0.001 x=9.977 y=3.299 t=101039
|
||||
move dx=0.008 dy=0.000 x=9.985 y=3.299 t=101048
|
||||
move dx=0.007 dy=0.000 x=9.991 y=3.299 t=101056
|
||||
move dx=-0.005 dy=-0.001 x=9.991 y=3.299 t=101105
|
||||
move dx=-0.007 dy=-0.001 x=9.984 y=3.298 t=101110
|
||||
move dx=-0.009 dy=-0.001 x=9.975 y=3.297 t=101121
|
||||
move dx=-0.011 dy=-0.001 x=9.964 y=3.296 t=101126
|
||||
move dx=-0.013 dy=-0.001 x=9.951 y=3.295 t=101135
|
||||
move dx=-0.014 dy=-0.001 x=9.937 y=3.294 t=101143
|
||||
move dx=-0.016 dy=-0.002 x=9.921 y=3.292 t=101151
|
||||
move dx=-0.018 dy=-0.002 x=9.903 y=3.290 t=101159
|
||||
move dx=-0.019 dy=-0.002 x=9.884 y=3.288 t=101168
|
||||
move dx=-0.021 dy=-0.002 x=9.863 y=3.286 t=101177
|
||||
move dx=-0.022 dy=-0.002 x=9.841 y=3.284 t=101182
|
||||
move dx=-0.024 dy=-0.002 x=9.817 y=3.282 t=101192
|
||||
move dx=-0.025 dy=-0.002 x=9.792 y=3.279 t=101198
|
||||
move dx=-0.026 dy=-0.003 x=9.766 y=3.277 t=101208
|
||||
move dx=-0.027 dy=-0.003 x=9.738 y=3.274 t=101216
|
||||
move dx=-0.029 dy=-0.003 x=9.710 y=3.271 t=101222
|
||||
move dx=-0.030 dy=-0.003 x=9.680 y=3.268 t=101231
|
||||
move dx=-0.031 dy=-0.003 x=9.649 y=3.265 t=101240
|
||||
move dx=-0.032 dy=-0.003 x=9.617 y=3.262 t=101247
|
||||
move dx=-0.033 dy=-0.003 x=9.585 y=3.258 t=101254
|
||||
move dx=-0.034 dy=-0.003 x=9.551 y=3.255 t=101265
|
||||
move dx=-0.034 dy=-0.003 x=9.517 y=3.252 t=101273
|
||||
move dx=-0.035 dy=-0.004 x=9.481 y=3.248 t=101280
|
||||
move dx=-0.036 dy=-0.004 x=9.446 y=3.245 t=101287
|
||||
move dx=-0.037 dy=-0.004 x=9.409 y=3.241 t=101295
|
||||
move dx=-0.037 dy=-0.004 x=9.372 y=3.237 t=101304
|
||||
move dx=-0.038 dy=-0.004 x=9.334 y=3.233 t=101313
|
||||
move dx=-0.038 dy=-0.004 x=9.296 y=3.230 t=101319
|
||||
move dx=-0.039 dy=-0.004 x=9.257 y=3.226 t=101328
|
||||
move dx=-0.039 dy=-0.004 x=9.218 y=3.222 t=101335
|
||||
move dx=-0.039 dy=-0.004 x=9.179 y=3.218 t=101342
|
||||
move dx=-0.040 dy=-0.004 x=9.140 y=3.214 t=101353
|
||||
move dx=-0.040 dy=-0.004 x=9.100 y=3.210 t=101359
|
||||
move dx=-0.040 dy=-0.004 x=9.060 y=3.206 t=101367
|
||||
move dx=-0.040 dy=-0.004 x=9.020 y=3.202 t=101375
|
||||
move dx=-0.040 dy=-0.004 x=8.980 y=3.198 t=101384
|
||||
move dx=-0.040 dy=-0.004 x=8.940 y=3.194 t=101390
|
||||
move dx=-0.040 dy=-0.004 x=8.900 y=3.190 t=101399
|
||||
move dx=-0.040 dy=-0.004 x=8.860 y=3.186 t=101408
|
||||
move dx=-0.040 dy=-0.004 x=8.821 y=3.182 t=101417
|
||||
move dx=-0.039 dy=-0.004 x=8.782 y=3.178 t=101425
|
||||
move dx=-0.039 dy=-0.004 x=8.743 y=3.174 t=101430
|
||||
move dx=-0.039 dy=-0.004 x=8.704 y=3.170 t=101441
|
||||
move dx=-0.038 dy=-0.004 x=8.666 y=3.167 t=101448
|
||||
move dx=-0.038 dy=-0.004 x=8.628 y=3.163 t=101457
|
||||
move dx=-0.037 dy=-0.004 x=8.591 y=3.159 t=101465
|
||||
move dx=-0.037 dy=-0.004 x=8.554 y=3.155 t=101470
|
||||
move dx=-0.036 dy=-0.004 x=8.519 y=3.152 t=101479
|
||||
move dx=-0.035 dy=-0.004 x=8.483 y=3.148 t=101488
|
||||
move dx=-0.034 dy=-0.003 x=8.449 y=3.145 t=101495
|
||||
move dx=-0.034 dy=-0.003 x=8.415 y=3.142 t=101505
|
||||
move dx=-0.033 dy=-0.003 x=8.383 y=3.138 t=101511
|
||||
move dx=-0.032 dy=-0.003 x=8.351 y=3.135 t=101520
|
||||
move dx=-0.031 dy=-0.003 x=8.320 y=3.132 t=101526
|
||||
move dx=-0.030 dy=-0.003 x=8.290 y=3.129 t=101537
|
||||
move dx=-0.029 dy=-0.003 x=8.262 y=3.126 t=101545
|
||||
move dx=-0.027 dy=-0.003 x=8.234 y=3.123 t=101553
|
||||
move dx=-0.026 dy=-0.003 x=8.208 y=3.121 t=101559
|
||||
move dx=-0.025 dy=-0.002 x=8.183 y=3.118 t=101567
|
||||
move dx=-0.024 dy=-0.002 x=8.159 y=3.116 t=101577
|
||||
move dx=-0.022 dy=-0.002 x=8.137 y=3.114 t=101584
|
||||
move dx=-0.021 dy=-0.002 x=8.116 y=3.112 t=101591
|
||||
move dx=-0.019 dy=-0.002 x=8.097 y=3.110 t=101600
|
||||
move dx=-0.018 dy=-0.002 x=8.079 y=3.108 t=101606
|
||||
move dx=-0.016 dy=-0.002 x=8.063 y=3.106 t=101615
|
||||
move dx=-0.014 dy=-0.001 x=8.049 y=3.105 t=101624
|
||||
move dx=-0.013 dy=-0.001 x=8.036 y=3.104 t=101633
|
||||
move dx=-0.011 dy=-0.001 x=8.025 y=3.103 t=101640
|
||||
move dx=-0.009 dy=-0.001 x=8.016 y=3.102 t=101647
|
||||
move dx=-0.007 dy=-0.001 x=8.009 y=3.101 t=101656
|
||||
move dx=-0.005 dy=-0.001 x=8.004 y=3.100 t=101663
|
||||
move dx=0.128 dy=0.000 x=8.128 y=3.100 t=101688
|
||||
move dx=0.128 dy=0.000 x=8.256 y=3.100 t=101696
|
||||
move dx=0.128 dy=0.000 x=8.384 y=3.100 t=101705
|
||||
move dx=0.128 dy=0.000 x=8.512 y=3.100 t=101713
|
||||
move dx=0.128 dy=0.000 x=8.640 y=3.100 t=101721
|
||||
move dx=0.128 dy=0.000 x=8.768 y=3.100 t=101728
|
||||
move dx=0.128 dy=0.000 x=8.896 y=3.100 t=101735
|
||||
move dx=0.128 dy=0.000 x=9.024 y=3.100 t=101743
|
||||
move dx=0.128 dy=0.000 x=9.152 y=3.100 t=101751
|
||||
move dx=0.128 dy=0.000 x=9.280 y=3.100 t=101759
|
||||
move dx=0.128 dy=0.000 x=9.408 y=3.100 t=101769
|
||||
move dx=0.128 dy=0.000 x=9.536 y=3.100 t=101774
|
||||
move dx=0.128 dy=0.000 x=9.664 y=3.100 t=101783
|
||||
move dx=0.128 dy=0.000 x=9.792 y=3.100 t=101792
|
||||
move dx=0.128 dy=0.000 x=9.920 y=3.100 t=101800
|
||||
move dx=0.128 dy=0.000 x=10.048 y=3.100 t=101809
|
||||
move dx=0.128 dy=0.000 x=10.176 y=3.100 t=101814
|
||||
move dx=0.128 dy=0.000 x=10.304 y=3.100 t=101822
|
||||
move dx=0.128 dy=0.000 x=10.432 y=3.100 t=101831
|
||||
move dx=0.128 dy=0.000 x=10.560 y=3.100 t=101841
|
||||
move dx=0.128 dy=0.000 x=10.688 y=3.100 t=101847
|
||||
move dx=0.128 dy=0.000 x=10.816 y=3.100 t=101856
|
||||
move dx=0.128 dy=0.000 x=10.944 y=3.100 t=101864
|
||||
move dx=0.128 dy=0.000 x=11.072 y=3.100 t=101872
|
||||
move dx=0.128 dy=0.000 x=11.200 y=3.100 t=101880
|
||||
end x=11.200 y=3.100 t=101888
|
||||
@@ -0,0 +1,67 @@
|
||||
begin fingers=3 x=5.000 y=3.000 w=11.700 h=7.500 t=100048
|
||||
move dx=0.006 dy=0.001 x=5.020 y=3.002 t=100128
|
||||
move dx=0.007 dy=0.001 x=5.027 y=3.002 t=100136
|
||||
move dx=0.008 dy=0.001 x=5.035 y=3.003 t=100144
|
||||
move dx=0.009 dy=0.001 x=5.044 y=3.004 t=100152
|
||||
move dx=0.010 dy=0.001 x=5.053 y=3.005 t=100160
|
||||
move dx=0.011 dy=0.001 x=5.064 y=3.006 t=100168
|
||||
move dx=0.011 dy=0.001 x=5.075 y=3.007 t=100176
|
||||
move dx=0.012 dy=0.001 x=5.088 y=3.008 t=100184
|
||||
move dx=0.013 dy=0.001 x=5.101 y=3.009 t=100192
|
||||
move dx=0.014 dy=0.001 x=5.114 y=3.010 t=100200
|
||||
move dx=0.014 dy=0.001 x=5.129 y=3.012 t=100208
|
||||
move dx=0.015 dy=0.001 x=5.144 y=3.013 t=100216
|
||||
move dx=0.016 dy=0.001 x=5.160 y=3.015 t=100224
|
||||
move dx=0.016 dy=0.001 x=5.176 y=3.016 t=100232
|
||||
move dx=0.017 dy=0.002 x=5.193 y=3.018 t=100240
|
||||
move dx=0.017 dy=0.002 x=5.210 y=3.019 t=100248
|
||||
move dx=0.018 dy=0.002 x=5.228 y=3.021 t=100256
|
||||
move dx=0.018 dy=0.002 x=5.247 y=3.022 t=100264
|
||||
move dx=0.019 dy=0.002 x=5.266 y=3.024 t=100272
|
||||
move dx=0.019 dy=0.002 x=5.285 y=3.026 t=100280
|
||||
move dx=0.020 dy=0.002 x=5.305 y=3.028 t=100288
|
||||
move dx=0.020 dy=0.002 x=5.325 y=3.030 t=100296
|
||||
move dx=0.020 dy=0.002 x=5.345 y=3.031 t=100304
|
||||
move dx=0.021 dy=0.002 x=5.366 y=3.033 t=100312
|
||||
move dx=0.021 dy=0.002 x=5.387 y=3.035 t=100320
|
||||
move dx=0.021 dy=0.002 x=5.408 y=3.037 t=100328
|
||||
move dx=0.021 dy=0.002 x=5.430 y=3.039 t=100336
|
||||
move dx=0.022 dy=0.002 x=5.451 y=3.041 t=100344
|
||||
move dx=0.022 dy=0.002 x=5.473 y=3.043 t=100352
|
||||
move dx=0.022 dy=0.002 x=5.495 y=3.045 t=100360
|
||||
move dx=0.022 dy=0.002 x=5.517 y=3.047 t=100368
|
||||
move dx=0.022 dy=0.002 x=5.539 y=3.049 t=100376
|
||||
move dx=0.022 dy=0.002 x=5.561 y=3.051 t=100384
|
||||
move dx=0.022 dy=0.002 x=5.583 y=3.053 t=100392
|
||||
move dx=0.022 dy=0.002 x=5.605 y=3.055 t=100400
|
||||
move dx=0.022 dy=0.002 x=5.627 y=3.057 t=100408
|
||||
move dx=0.022 dy=0.002 x=5.649 y=3.059 t=100416
|
||||
move dx=0.022 dy=0.002 x=5.670 y=3.061 t=100424
|
||||
move dx=0.021 dy=0.002 x=5.692 y=3.063 t=100432
|
||||
move dx=0.021 dy=0.002 x=5.713 y=3.065 t=100440
|
||||
move dx=0.021 dy=0.002 x=5.734 y=3.067 t=100448
|
||||
move dx=0.021 dy=0.002 x=5.755 y=3.069 t=100456
|
||||
move dx=0.020 dy=0.002 x=5.775 y=3.070 t=100464
|
||||
move dx=0.020 dy=0.002 x=5.795 y=3.072 t=100472
|
||||
move dx=0.020 dy=0.002 x=5.815 y=3.074 t=100480
|
||||
move dx=0.019 dy=0.002 x=5.834 y=3.076 t=100488
|
||||
move dx=0.019 dy=0.002 x=5.853 y=3.078 t=100496
|
||||
move dx=0.018 dy=0.002 x=5.872 y=3.079 t=100504
|
||||
move dx=0.018 dy=0.002 x=5.890 y=3.081 t=100512
|
||||
move dx=0.017 dy=0.002 x=5.907 y=3.082 t=100520
|
||||
move dx=0.017 dy=0.002 x=5.924 y=3.084 t=100528
|
||||
move dx=0.016 dy=0.001 x=5.940 y=3.085 t=100536
|
||||
move dx=0.016 dy=0.001 x=5.956 y=3.087 t=100544
|
||||
move dx=0.015 dy=0.001 x=5.971 y=3.088 t=100552
|
||||
move dx=0.014 dy=0.001 x=5.986 y=3.090 t=100560
|
||||
move dx=0.014 dy=0.001 x=5.999 y=3.091 t=100568
|
||||
move dx=0.013 dy=0.001 x=6.012 y=3.092 t=100576
|
||||
move dx=0.012 dy=0.001 x=6.025 y=3.093 t=100584
|
||||
move dx=0.011 dy=0.001 x=6.036 y=3.094 t=100592
|
||||
move dx=0.011 dy=0.001 x=6.047 y=3.095 t=100600
|
||||
move dx=0.010 dy=0.001 x=6.056 y=3.096 t=100608
|
||||
move dx=0.009 dy=0.001 x=6.065 y=3.097 t=100616
|
||||
move dx=0.008 dy=0.001 x=6.073 y=3.098 t=100624
|
||||
move dx=0.007 dy=0.001 x=6.080 y=3.098 t=100632
|
||||
move dx=0.006 dy=0.001 x=6.086 y=3.099 t=100640
|
||||
end x=6.100 y=3.100 t=100688
|
||||
+49
-5
@@ -6,6 +6,8 @@ use chrono::Utc;
|
||||
use komorebi_client::PathExt;
|
||||
use komorebi_client::replace_env_in_path;
|
||||
use komorebi_client::splash;
|
||||
mod pan;
|
||||
|
||||
use std::fs::File;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io;
|
||||
@@ -1013,8 +1015,27 @@ struct ScrollingLayoutColumns {
|
||||
#[derive(Parser)]
|
||||
struct Pan {
|
||||
/// The way that the view goes
|
||||
#[clap(value_enum)]
|
||||
direction: PanDirection,
|
||||
#[clap(
|
||||
value_enum,
|
||||
required_unless_present = "stream",
|
||||
conflicts_with = "stream"
|
||||
)]
|
||||
direction: Option<PanDirection>,
|
||||
/// Read the gestures of fingers on a touchpad from standard input, as whkd writes them for
|
||||
/// a swipe of three or four fingers, and pan with them
|
||||
#[clap(long)]
|
||||
stream: bool,
|
||||
/// What the distances of a gesture are multiplied by, which is 1 if it is left out, with
|
||||
/// --stream
|
||||
#[clap(long)]
|
||||
sensitivity: Option<f64>,
|
||||
/// Go the other way to the fingers, with --stream
|
||||
#[clap(long)]
|
||||
reversed: bool,
|
||||
/// Take a step for every two centimetres that the fingers go, and not move the view with
|
||||
/// them, with --stream
|
||||
#[clap(long)]
|
||||
paging: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
@@ -1310,8 +1331,8 @@ enum SubCommand {
|
||||
/// Stack the windows of the focused column of the Scrolling layout, or spread the focused
|
||||
/// stack out into a column with a window in each slot
|
||||
ToggleColumnStack,
|
||||
/// Move the view of the Scrolling layout a column to the left or the right, which does
|
||||
/// nothing in any other layout
|
||||
/// Move the view of the Scrolling layout a column to the left or the right, or with the
|
||||
/// fingers on a touchpad, which does nothing in any other layout
|
||||
#[clap(arg_required_else_help = true)]
|
||||
Pan(Pan),
|
||||
/// Set the layout column and row ratios for the focused workspace
|
||||
@@ -3005,7 +3026,30 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
|
||||
send_message(&SocketMessage::ToggleColumnStack)?;
|
||||
}
|
||||
SubCommand::Pan(args) => {
|
||||
send_message(&SocketMessage::Pan(args.direction))?;
|
||||
// a flag that has a default counts as given, so what only goes with --stream
|
||||
// cannot be left to the parser to say
|
||||
if !args.stream && (args.sensitivity.is_some() || args.reversed || args.paging) {
|
||||
Opts::command()
|
||||
.error(
|
||||
clap::error::ErrorKind::ArgumentConflict,
|
||||
"--sensitivity, --reversed and --paging can only be used with --stream",
|
||||
)
|
||||
.exit();
|
||||
}
|
||||
|
||||
if args.stream {
|
||||
pan::run(
|
||||
BufReader::new(io::stdin()),
|
||||
pan::Options {
|
||||
sensitivity: args.sensitivity.unwrap_or(1.0),
|
||||
reversed: args.reversed,
|
||||
paging: args.paging,
|
||||
},
|
||||
&mut pan::Socket::new(komorebi_client::Connection::open),
|
||||
)?;
|
||||
} else if let Some(direction) = args.direction {
|
||||
send_message(&SocketMessage::Pan(direction))?;
|
||||
}
|
||||
}
|
||||
SubCommand::LayoutRatios(args) => {
|
||||
if args.columns.is_none() && args.rows.is_none() {
|
||||
|
||||
@@ -0,0 +1,809 @@
|
||||
//! Reading the stream of gestures that `komorebic pan --stream` is given, which whkd writes for a
|
||||
//! swipe of three or four fingers on a touchpad, and telling komorebi what the fingers do.
|
||||
//!
|
||||
//! The format is whkd's, and is written down in `docs/gesture-stream.md` in its repository: one
|
||||
//! message to a line, a name and then `key=value` fields in any order, the distances in
|
||||
//! centimetres. A field that is not known is ignored, and so is a message that is not known.
|
||||
|
||||
use komorebi_client::PanDirection;
|
||||
use komorebi_client::PanGesture;
|
||||
use komorebi_client::SocketMessage;
|
||||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
use std::io::BufRead;
|
||||
use std::sync::mpsc;
|
||||
|
||||
/// How far the fingers go with `--paging` for each step, in centimetres
|
||||
pub const PAGE_CM: f64 = 2.0;
|
||||
|
||||
/// One thing that the fingers do
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Message {
|
||||
Begin,
|
||||
Move { dx: f64, dy: f64, t: f64 },
|
||||
End,
|
||||
Cancel,
|
||||
}
|
||||
|
||||
/// What a line of the stream comes to
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Line {
|
||||
Message(Message),
|
||||
/// Nothing to do: a line with nothing in it, or a comment, or a message that is not known
|
||||
Ignored,
|
||||
/// A message that is known, and is not what it should be
|
||||
Bad(String),
|
||||
}
|
||||
|
||||
/// What a line of the stream says
|
||||
#[must_use]
|
||||
pub fn parse_line(line: &str) -> Line {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
return Line::Ignored;
|
||||
}
|
||||
|
||||
let mut words = line.split_whitespace();
|
||||
let Some(name) = words.next() else {
|
||||
return Line::Ignored;
|
||||
};
|
||||
|
||||
let fields: Vec<(&str, &str)> = words.filter_map(|word| word.split_once('=')).collect();
|
||||
let number = |key: &str| -> Option<Result<f64, String>> {
|
||||
fields
|
||||
.iter()
|
||||
.find(|(name, _)| *name == key)
|
||||
.map(|(_, value)| {
|
||||
value
|
||||
.parse::<f64>()
|
||||
.ok()
|
||||
.filter(|number| number.is_finite())
|
||||
.ok_or_else(|| format!("{key}={value} is not a number"))
|
||||
})
|
||||
};
|
||||
|
||||
match name {
|
||||
"begin" => Line::Message(Message::Begin),
|
||||
"end" => Line::Message(Message::End),
|
||||
"cancel" => Line::Message(Message::Cancel),
|
||||
"move" => {
|
||||
let dx = match number("dx") {
|
||||
Some(Ok(dx)) => dx,
|
||||
Some(Err(error)) => return Line::Bad(error),
|
||||
None => return Line::Bad(String::from("a move has no dx")),
|
||||
};
|
||||
let t = match number("t") {
|
||||
Some(Ok(t)) => t,
|
||||
Some(Err(error)) => return Line::Bad(error),
|
||||
None => return Line::Bad(String::from("a move has no t")),
|
||||
};
|
||||
let dy = match number("dy") {
|
||||
Some(Ok(dy)) => dy,
|
||||
Some(Err(error)) => return Line::Bad(error),
|
||||
None => 0.0,
|
||||
};
|
||||
|
||||
Line::Message(Message::Move { dx, dy, t })
|
||||
}
|
||||
_ => Line::Ignored,
|
||||
}
|
||||
}
|
||||
|
||||
/// What `--stream` is asked to do with the distances
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Options {
|
||||
/// What the distances are multiplied by
|
||||
pub sensitivity: f64,
|
||||
/// The strip goes the other way to the fingers
|
||||
pub reversed: bool,
|
||||
/// One step for each [`PAGE_CM`] that the fingers go, and not the strip going with them
|
||||
pub paging: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sensitivity: 1.0,
|
||||
reversed: false,
|
||||
paging: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Works out what komorebi is told for what the fingers do
|
||||
#[derive(Debug)]
|
||||
pub struct Forwarder {
|
||||
options: Options,
|
||||
/// Whether a gesture is going on
|
||||
active: bool,
|
||||
/// How far the fingers have gone since the last step, with paging
|
||||
page: f64,
|
||||
}
|
||||
|
||||
impl Forwarder {
|
||||
#[must_use]
|
||||
pub const fn new(options: Options) -> Self {
|
||||
Self {
|
||||
options,
|
||||
active: false,
|
||||
page: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// What komorebi is told for this. Fingers that move when there was no beginning, and an end
|
||||
/// or a cancel for a gesture that is not going on, are not anything.
|
||||
pub fn handle(&mut self, message: Message) -> Vec<SocketMessage> {
|
||||
let gesture = |gesture| vec![SocketMessage::PanGesture(gesture)];
|
||||
|
||||
match message {
|
||||
Message::Begin => {
|
||||
self.active = true;
|
||||
self.page = 0.0;
|
||||
|
||||
if self.options.paging {
|
||||
Vec::new()
|
||||
} else {
|
||||
gesture(PanGesture::Begin)
|
||||
}
|
||||
}
|
||||
Message::Move { dx, dy, t } if self.active => {
|
||||
let sign = if self.options.reversed { -1.0 } else { 1.0 };
|
||||
let dx = dx * self.options.sensitivity * sign;
|
||||
let dy = dy * self.options.sensitivity;
|
||||
|
||||
if self.options.paging {
|
||||
self.page += dx;
|
||||
self.pages()
|
||||
} else {
|
||||
gesture(PanGesture::Move { dx, dy, t })
|
||||
}
|
||||
}
|
||||
Message::End if self.active => {
|
||||
self.active = false;
|
||||
|
||||
if self.options.paging {
|
||||
Vec::new()
|
||||
} else {
|
||||
gesture(PanGesture::End)
|
||||
}
|
||||
}
|
||||
Message::Cancel if self.active => {
|
||||
self.active = false;
|
||||
|
||||
if self.options.paging {
|
||||
Vec::new()
|
||||
} else {
|
||||
gesture(PanGesture::Cancel)
|
||||
}
|
||||
}
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The stream is over: a gesture that is still going on was not finished, so it is given up
|
||||
pub fn finish(&mut self) -> Vec<SocketMessage> {
|
||||
let unfinished = self.active && !self.options.paging;
|
||||
self.active = false;
|
||||
|
||||
if unfinished {
|
||||
vec![SocketMessage::PanGesture(PanGesture::Cancel)]
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// The steps that what the fingers have gone since the last one comes to. Fingers that go to
|
||||
/// the right take the strip with them, which is to look to the left.
|
||||
fn pages(&mut self) -> Vec<SocketMessage> {
|
||||
let mut steps = Vec::new();
|
||||
|
||||
while self.page.abs() >= PAGE_CM {
|
||||
let direction = if self.page > 0.0 {
|
||||
PanDirection::Left
|
||||
} else {
|
||||
PanDirection::Right
|
||||
};
|
||||
|
||||
self.page -= PAGE_CM.copysign(self.page);
|
||||
steps.push(SocketMessage::Pan(direction));
|
||||
}
|
||||
|
||||
steps
|
||||
}
|
||||
}
|
||||
|
||||
/// The next message to handle, with the moves that follow it added to it if it is one: when
|
||||
/// the stream comes faster than it can be handled, the distances add up and the time is the
|
||||
/// last one, as whkd says that they may.
|
||||
pub fn next_merged(queue: &mut VecDeque<Message>) -> Option<Message> {
|
||||
let first = queue.pop_front()?;
|
||||
let Message::Move {
|
||||
mut dx,
|
||||
mut dy,
|
||||
mut t,
|
||||
} = first
|
||||
else {
|
||||
return Some(first);
|
||||
};
|
||||
|
||||
while let Some(Message::Move {
|
||||
dx: more_dx,
|
||||
dy: more_dy,
|
||||
t: later,
|
||||
}) = queue.front().copied()
|
||||
{
|
||||
dx += more_dx;
|
||||
dy += more_dy;
|
||||
t = later;
|
||||
queue.pop_front();
|
||||
}
|
||||
|
||||
Some(Message::Move { dx, dy, t })
|
||||
}
|
||||
|
||||
/// Where komorebi is told what the fingers do
|
||||
pub trait Sink {
|
||||
fn send(&mut self, message: &SocketMessage) -> io::Result<()>;
|
||||
|
||||
/// The gesture that was being told is over
|
||||
fn gesture_over(&mut self);
|
||||
}
|
||||
|
||||
/// A way of telling komorebi things, in order
|
||||
pub trait Link {
|
||||
fn send(&mut self, message: &SocketMessage) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl Link for komorebi_client::Connection {
|
||||
fn send(&mut self, message: &SocketMessage) -> io::Result<()> {
|
||||
komorebi_client::Connection::send(self, message)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tells komorebi what the fingers do over one connection for each gesture. Messages that are
|
||||
/// sent on a connection each are not handled in the order that they were sent, so a gesture is
|
||||
/// told on one, which is made when it is first needed and let go of when the gesture is over.
|
||||
/// If it cannot be written to the connection is made again once, as komorebi lets go of one
|
||||
/// that nothing has been written to for a while.
|
||||
pub struct Socket<L, F> {
|
||||
open: F,
|
||||
link: Option<L>,
|
||||
}
|
||||
|
||||
impl<L: Link, F: FnMut() -> io::Result<L>> Socket<L, F> {
|
||||
pub const fn new(open: F) -> Self {
|
||||
Self { open, link: None }
|
||||
}
|
||||
|
||||
fn try_send(&mut self, message: &SocketMessage) -> io::Result<()> {
|
||||
if self.link.is_none() {
|
||||
self.link = Some((self.open)()?);
|
||||
}
|
||||
|
||||
match self.link.as_mut() {
|
||||
Some(link) => link.send(message),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Link, F: FnMut() -> io::Result<L>> Sink for Socket<L, F> {
|
||||
fn send(&mut self, message: &SocketMessage) -> io::Result<()> {
|
||||
if self.try_send(message).is_err() {
|
||||
self.link = None;
|
||||
return self.try_send(message);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gesture_over(&mut self) {
|
||||
self.link = None;
|
||||
}
|
||||
}
|
||||
|
||||
/// Read gestures from `input` until it closes and tell komorebi what the fingers do with `sink`
|
||||
pub fn run(
|
||||
input: impl BufRead + Send + 'static,
|
||||
options: Options,
|
||||
sink: &mut impl Sink,
|
||||
) -> io::Result<()> {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
for line in input.lines() {
|
||||
let Ok(line) = line else {
|
||||
break;
|
||||
};
|
||||
|
||||
match parse_line(&line) {
|
||||
Line::Message(message) => {
|
||||
if sender.send(message).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Line::Ignored => {}
|
||||
Line::Bad(why) => eprintln!("pan: {why}: {line}"),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut forwarder = Forwarder::new(options);
|
||||
let mut queue = VecDeque::new();
|
||||
|
||||
while let Ok(first) = receiver.recv() {
|
||||
queue.push_back(first);
|
||||
queue.extend(receiver.try_iter());
|
||||
|
||||
while let Some(message) = next_merged(&mut queue) {
|
||||
let over = matches!(message, Message::End | Message::Cancel);
|
||||
|
||||
for told in forwarder.handle(message) {
|
||||
sink.send(&told)?;
|
||||
}
|
||||
|
||||
if over {
|
||||
sink.gesture_over();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for told in forwarder.finish() {
|
||||
sink.send(&told)?;
|
||||
}
|
||||
sink.gesture_over();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const TRANSCRIPT: &str = include_str!("../fixtures/gesture-stream.transcript.txt");
|
||||
|
||||
fn gestures(told: &[SocketMessage]) -> Vec<PanGesture> {
|
||||
told.iter()
|
||||
.filter_map(|message| match message {
|
||||
SocketMessage::PanGesture(gesture) => Some(*gesture),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn messages(text: &str) -> Vec<Message> {
|
||||
text.lines()
|
||||
.filter_map(|line| match parse_line(line) {
|
||||
Line::Message(message) => Some(message),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// What is told, and how many gestures have been over
|
||||
#[derive(Default)]
|
||||
struct Recorder {
|
||||
told: Vec<SocketMessage>,
|
||||
over: usize,
|
||||
}
|
||||
|
||||
impl Sink for Recorder {
|
||||
fn send(&mut self, message: &SocketMessage) -> io::Result<()> {
|
||||
self.told.push(message.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gesture_over(&mut self) {
|
||||
self.over += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// A link that remembers what it was written, and whose connections are numbered
|
||||
struct Wire {
|
||||
connection: usize,
|
||||
written: Written,
|
||||
fail_next: Failing,
|
||||
}
|
||||
|
||||
impl Link for Wire {
|
||||
fn send(&mut self, message: &SocketMessage) -> io::Result<()> {
|
||||
if self.fail_next.replace(false) {
|
||||
return Err(io::Error::new(io::ErrorKind::BrokenPipe, "let go of"));
|
||||
}
|
||||
|
||||
self.written
|
||||
.borrow_mut()
|
||||
.push((self.connection, message.clone()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
type Written = std::rc::Rc<std::cell::RefCell<Vec<(usize, SocketMessage)>>>;
|
||||
|
||||
type Failing = std::rc::Rc<std::cell::Cell<bool>>;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn wire() -> (
|
||||
Socket<Wire, impl FnMut() -> io::Result<Wire>>,
|
||||
Written,
|
||||
Failing,
|
||||
) {
|
||||
let written = std::rc::Rc::new(std::cell::RefCell::new(Vec::new()));
|
||||
let fail_next = std::rc::Rc::new(std::cell::Cell::new(false));
|
||||
let mut made = 0;
|
||||
|
||||
let socket = Socket::new({
|
||||
let written = written.clone();
|
||||
let fail_next = fail_next.clone();
|
||||
move || {
|
||||
made += 1;
|
||||
Ok(Wire {
|
||||
connection: made,
|
||||
written: written.clone(),
|
||||
fail_next: fail_next.clone(),
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
(socket, written, fail_next)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_gesture_is_told_on_one_connection_and_the_next_has_its_own() {
|
||||
let (mut socket, written, _) = wire();
|
||||
let message = |gesture| SocketMessage::PanGesture(gesture);
|
||||
let moved = PanGesture::Move {
|
||||
dx: 1.0,
|
||||
dy: 0.0,
|
||||
t: 1.0,
|
||||
};
|
||||
|
||||
socket.send(&message(PanGesture::Begin)).unwrap();
|
||||
socket.send(&message(moved)).unwrap();
|
||||
socket.send(&message(moved)).unwrap();
|
||||
socket.send(&message(PanGesture::End)).unwrap();
|
||||
socket.gesture_over();
|
||||
socket.send(&message(PanGesture::Begin)).unwrap();
|
||||
|
||||
let connections: Vec<_> = written.borrow().iter().map(|(number, _)| *number).collect();
|
||||
assert_eq!(connections, vec![1, 1, 1, 1, 2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_connection_that_komorebi_let_go_of_is_made_again_once() {
|
||||
let (mut socket, written, fail_next) = wire();
|
||||
let message = SocketMessage::PanGesture(PanGesture::Begin);
|
||||
|
||||
socket.send(&message).unwrap();
|
||||
fail_next.set(true);
|
||||
socket.send(&message).unwrap();
|
||||
|
||||
let connections: Vec<_> = written.borrow().iter().map(|(number, _)| *number).collect();
|
||||
assert_eq!(
|
||||
connections,
|
||||
vec![1, 2],
|
||||
"the second was written on a new one"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_message_that_cannot_be_written_twice_is_an_error() {
|
||||
let mut socket = Socket::new(|| -> io::Result<Wire> {
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "no komorebi"))
|
||||
});
|
||||
|
||||
assert!(
|
||||
socket
|
||||
.send(&SocketMessage::PanGesture(PanGesture::Begin))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn what_is_told_ends_each_gesture_that_ends_and_the_stream_that_stops() {
|
||||
let input = b"begin\nmove dx=1 t=1\nend\nbegin\nmove dx=1 t=2\n".to_vec();
|
||||
let mut sink = Recorder::default();
|
||||
|
||||
run(io::Cursor::new(input), Options::default(), &mut sink).unwrap();
|
||||
|
||||
// the first ended, and the second was given up when the stream stopped
|
||||
assert_eq!(sink.over, 2);
|
||||
assert_eq!(gestures(&sink.told).last(), Some(&PanGesture::Cancel));
|
||||
}
|
||||
|
||||
fn step(dx: f64) -> Message {
|
||||
Message::Move {
|
||||
dx,
|
||||
dy: 0.0,
|
||||
t: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn what_whkd_writes_is_read_as_it_was_written() {
|
||||
assert_eq!(
|
||||
messages(TRANSCRIPT),
|
||||
vec![
|
||||
Message::Begin,
|
||||
Message::Move {
|
||||
dx: 0.42,
|
||||
dy: -0.03,
|
||||
t: 118_345.0
|
||||
},
|
||||
Message::Move {
|
||||
dx: 0.51,
|
||||
dy: -0.02,
|
||||
t: 118_353.0
|
||||
},
|
||||
Message::Move {
|
||||
dx: 0.47,
|
||||
dy: 0.0,
|
||||
t: 118_361.0
|
||||
},
|
||||
Message::End,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// What whkd writes for a swipe of three fingers that goes on for about two seconds, a
|
||||
/// flick, and one that eases to a stop: `whkd touchpad --demo <name> --instant`
|
||||
const LONG: &str = include_str!("../fixtures/pan3-long.transcript.txt");
|
||||
const FLICK: &str = include_str!("../fixtures/pan3-flick.transcript.txt");
|
||||
const SLOW: &str = include_str!("../fixtures/pan3-slow.transcript.txt");
|
||||
|
||||
/// How many lines of a transcript are not part of the stream: whkd's demo says which swipe
|
||||
/// it saw, as `swipe swipe3right`, among them, and nothing says that the program that it
|
||||
/// runs is told it, so it is ignored like any other message that is not known
|
||||
fn others(transcript: &str) -> usize {
|
||||
transcript
|
||||
.lines()
|
||||
.filter(|line| line.starts_with("swipe "))
|
||||
.count()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_line_that_whkd_writes_for_a_swipe_is_read_and_nothing_is_said_to_be_wrong() {
|
||||
for (transcript, lines) in [(LONG, 218), (FLICK, 18), (SLOW, 67)] {
|
||||
assert_eq!(transcript.lines().count(), lines);
|
||||
|
||||
for line in transcript.lines() {
|
||||
match parse_line(line) {
|
||||
Line::Message(_) => {}
|
||||
Line::Ignored => assert!(line.starts_with("swipe "), "ignored: {line}"),
|
||||
Line::Bad(why) => panic!("{why}: {line}"),
|
||||
}
|
||||
}
|
||||
|
||||
let messages = messages(transcript);
|
||||
assert_eq!(messages.first(), Some(&Message::Begin));
|
||||
assert_eq!(messages.last(), Some(&Message::End));
|
||||
assert_eq!(messages.len(), lines - others(transcript));
|
||||
}
|
||||
|
||||
assert_eq!(others(LONG) + others(FLICK) + others(SLOW), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_swipe_is_told_to_komorebi_move_for_move() {
|
||||
for transcript in [LONG, FLICK, SLOW] {
|
||||
let mut forwarder = Forwarder::new(Options::default());
|
||||
let told: Vec<_> = messages(transcript)
|
||||
.into_iter()
|
||||
.flat_map(|message| forwarder.handle(message))
|
||||
.collect();
|
||||
|
||||
let gestures = gestures(&told);
|
||||
assert_eq!(
|
||||
gestures.len(),
|
||||
transcript.lines().count() - others(transcript)
|
||||
);
|
||||
assert_eq!(gestures.first(), Some(&PanGesture::Begin));
|
||||
assert_eq!(gestures.last(), Some(&PanGesture::End));
|
||||
|
||||
// the distances add up to how far the fingers went across the touchpad
|
||||
let across: f64 = gestures
|
||||
.iter()
|
||||
.filter_map(|gesture| match gesture {
|
||||
PanGesture::Move { dx, .. } => Some(*dx),
|
||||
_ => None,
|
||||
})
|
||||
.sum();
|
||||
assert!(across > 1.0, "{across}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_field_that_is_not_known_is_not_anything_and_nor_is_a_message() {
|
||||
// x, y, w, h, and a field of the future, which whkd says to ignore without a word
|
||||
assert_eq!(
|
||||
parse_line("move dx=1.5 dy=0.25 x=4.420 y=1.970 t=10 pressure=0.7"),
|
||||
Line::Message(Message::Move {
|
||||
dx: 1.5,
|
||||
dy: 0.25,
|
||||
t: 10.0
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
parse_line("begin fingers=3 x=4.000 y=2.000 w=10.500 h=7.200 t=118337"),
|
||||
Line::Message(Message::Begin)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_line("end x=5.4 y=1.95 t=118369"),
|
||||
Line::Message(Message::End)
|
||||
);
|
||||
assert_eq!(parse_line("pinch scale=1.1"), Line::Ignored);
|
||||
assert_eq!(parse_line(""), Line::Ignored);
|
||||
assert_eq!(parse_line(" "), Line::Ignored);
|
||||
assert_eq!(parse_line("# a comment"), Line::Ignored);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_move_that_is_not_what_it_should_be_is_said_to_be() {
|
||||
assert!(matches!(parse_line("move dy=1 t=3"), Line::Bad(_)), "no dx");
|
||||
assert!(matches!(parse_line("move dx=1 dy=1"), Line::Bad(_)), "no t");
|
||||
assert!(matches!(parse_line("move dx=wide t=3"), Line::Bad(_)));
|
||||
assert!(matches!(parse_line("move dx=NaN t=3"), Line::Bad(_)));
|
||||
assert_eq!(
|
||||
parse_line("move dx=2 t=3"),
|
||||
Line::Message(Message::Move {
|
||||
dx: 2.0,
|
||||
dy: 0.0,
|
||||
t: 3.0
|
||||
}),
|
||||
"dy is not needed"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_strip_goes_with_the_fingers_and_the_times_are_told_as_they_are() {
|
||||
let mut forwarder = Forwarder::new(Options::default());
|
||||
let told: Vec<_> = messages(TRANSCRIPT)
|
||||
.into_iter()
|
||||
.flat_map(|message| forwarder.handle(message))
|
||||
.collect();
|
||||
|
||||
let gestures = gestures(&told);
|
||||
assert_eq!(gestures.len(), 5);
|
||||
assert_eq!(gestures[0], PanGesture::Begin);
|
||||
assert_eq!(
|
||||
gestures[1],
|
||||
PanGesture::Move {
|
||||
dx: 0.42,
|
||||
dy: -0.03,
|
||||
t: 118_345.0
|
||||
}
|
||||
);
|
||||
assert_eq!(gestures[4], PanGesture::End);
|
||||
assert!(
|
||||
forwarder.finish().is_empty(),
|
||||
"it ended, so nothing is left"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sensitivity_scales_the_distances_and_reversing_flips_the_way_it_goes() {
|
||||
let mut forwarder = Forwarder::new(Options {
|
||||
sensitivity: 2.0,
|
||||
reversed: true,
|
||||
paging: false,
|
||||
});
|
||||
forwarder.handle(Message::Begin);
|
||||
|
||||
let told = forwarder.handle(Message::Move {
|
||||
dx: 1.0,
|
||||
dy: 0.5,
|
||||
t: 1.0,
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
gestures(&told),
|
||||
vec![PanGesture::Move {
|
||||
dx: -2.0,
|
||||
dy: 1.0,
|
||||
t: 1.0
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn what_has_no_beginning_is_not_anything() {
|
||||
let mut forwarder = Forwarder::new(Options::default());
|
||||
|
||||
assert!(forwarder.handle(step(1.0)).is_empty());
|
||||
assert!(forwarder.handle(Message::End).is_empty());
|
||||
assert!(forwarder.handle(Message::Cancel).is_empty());
|
||||
assert!(forwarder.finish().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_stream_that_stops_in_the_middle_of_a_gesture_gives_it_up() {
|
||||
let mut forwarder = Forwarder::new(Options::default());
|
||||
forwarder.handle(Message::Begin);
|
||||
forwarder.handle(step(1.0));
|
||||
|
||||
assert_eq!(gestures(&forwarder.finish()), vec![PanGesture::Cancel]);
|
||||
assert!(forwarder.finish().is_empty(), "once");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fingers_that_go_far_enough_take_a_step_each_with_paging() {
|
||||
let mut forwarder = Forwarder::new(Options {
|
||||
paging: true,
|
||||
..Options::default()
|
||||
});
|
||||
|
||||
assert!(forwarder.handle(Message::Begin).is_empty());
|
||||
assert!(forwarder.handle(step(1.5)).is_empty(), "not far enough");
|
||||
|
||||
// 1.5 and 0.7 is 2.2: a step to the left, as the strip goes right with the fingers
|
||||
let told = forwarder.handle(step(0.7));
|
||||
assert!(matches!(
|
||||
told.as_slice(),
|
||||
[SocketMessage::Pan(PanDirection::Left)]
|
||||
));
|
||||
|
||||
// 0.2 is left over, and with 4.0 more it is two steps
|
||||
let told = forwarder.handle(step(4.0));
|
||||
assert_eq!(told.len(), 2);
|
||||
|
||||
// and the other way: 0.2 and 8.5 back is 8.3, which is four
|
||||
let told = forwarder.handle(step(-8.5));
|
||||
assert_eq!(told.len(), 4);
|
||||
assert!(
|
||||
told.iter()
|
||||
.all(|message| matches!(message, SocketMessage::Pan(PanDirection::Right)))
|
||||
);
|
||||
|
||||
assert!(forwarder.handle(Message::End).is_empty());
|
||||
assert!(forwarder.finish().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn moves_that_wait_are_added_up_and_nothing_else_is_put_out_of_its_place() {
|
||||
let mut queue: VecDeque<Message> = [
|
||||
Message::Begin,
|
||||
Message::Move {
|
||||
dx: 1.0,
|
||||
dy: 0.5,
|
||||
t: 1.0,
|
||||
},
|
||||
Message::Move {
|
||||
dx: 2.0,
|
||||
dy: 0.25,
|
||||
t: 2.0,
|
||||
},
|
||||
Message::Move {
|
||||
dx: 4.0,
|
||||
dy: 0.0,
|
||||
t: 3.0,
|
||||
},
|
||||
Message::End,
|
||||
]
|
||||
.into();
|
||||
|
||||
assert_eq!(next_merged(&mut queue), Some(Message::Begin));
|
||||
assert_eq!(
|
||||
next_merged(&mut queue),
|
||||
Some(Message::Move {
|
||||
dx: 7.0,
|
||||
dy: 0.75,
|
||||
t: 3.0
|
||||
})
|
||||
);
|
||||
assert_eq!(next_merged(&mut queue), Some(Message::End));
|
||||
assert_eq!(next_merged(&mut queue), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_stream_is_told_to_komorebi_in_order_and_given_up_if_it_stops() {
|
||||
let input = b"begin fingers=3\nmove dx=1.0 dy=0 t=1\nmove dx=2.0 dy=0 t=2\n".to_vec();
|
||||
let mut sink = Recorder::default();
|
||||
|
||||
run(io::Cursor::new(input), Options::default(), &mut sink).unwrap();
|
||||
|
||||
let gestures = gestures(&sink.told);
|
||||
assert_eq!(gestures[0], PanGesture::Begin);
|
||||
assert!(matches!(gestures[1], PanGesture::Move { .. }));
|
||||
assert_eq!(gestures.last(), Some(&PanGesture::Cancel));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user