From d22ed691b599af56b18a0852b92253bf12f797c5 Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Wed, 2 Aug 2017 16:07:02 -0230 Subject: [PATCH] Updated PointingDevice (trakball and friends) - changed from templates back to inheritance (sorry DirtyHairy :)) - added 'tsense' commandline argument and associated UI - updated docs and screenshots for new functionality --- Changes.txt | 2 + docs/graphics/eventmapping.png | Bin 7823 -> 8495 bytes docs/graphics/eventmapping_combo.png | Bin 4129 -> 3445 bytes docs/graphics/eventmapping_devsports.png | Bin 9253 -> 8996 bytes docs/graphics/eventmapping_remap.png | Bin 6803 -> 7995 bytes docs/index.html | 1 + src/emucore/AmigaMouse.hxx | 37 +++-- src/emucore/AtariMouse.hxx | 37 +++-- src/emucore/EventHandler.cxx | 2 + src/emucore/Paddles.hxx | 8 +- src/emucore/PointingDevice.cxx | 156 ++++++++++++++++++++ src/emucore/PointingDevice.hxx | 174 +++++------------------ src/emucore/Settings.cxx | 14 +- src/emucore/TrakBall.hxx | 38 +++-- src/emucore/module.mk | 1 + src/gui/InputDialog.cxx | 38 ++++- src/gui/InputDialog.hxx | 3 + 17 files changed, 311 insertions(+), 200 deletions(-) create mode 100644 src/emucore/PointingDevice.cxx diff --git a/Changes.txt b/Changes.txt index 958d7b49a..57a72fe38 100644 --- a/Changes.txt +++ b/Changes.txt @@ -16,6 +16,8 @@ * Improved emulation of Trakball controller, eliminating bias in left/ right directions. Thanks to Thomas Jentzsch for the idea and code. + Related to this, added 'tsense' commandline argument and associated + UI item, to allow changing sensitivity of mouse trackball emulation. * Fixed an annoying bug in Linux, where Alt-Tab'ing out of a window and then back again would pass a 'Tab' key event to the app, which in diff --git a/docs/graphics/eventmapping.png b/docs/graphics/eventmapping.png index da13d048f4f90337ccd5732fb2bca66a19b92c7f..5eb1c91ae88f39345fcba717faaac68d0b81d458 100644 GIT binary patch literal 8495 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�Q=#+xeO}9$6H;p^ov@)U%)TPySln&2Es(*2(91kWZOEN_n?V938=C~9lyKm&uYXw0lu!? zMRj5G9p6ni+~$Kx-~BsOan2xUZw$jQH%6^(#WyDV^GyBlOiS$OwdX$IGP02UBB|i* zw6Qe83bNgx3h%FuE%x}MU@)iU4RYPOwSkThhDoyfT^|o?T~toCe?=5iR8Rn#=s#>w zzCQFZa+7{x8vS#-xY7a^nbb+0Om!iDJ>=rV@e6xXLKF)X6B8cN$NZVgkSlO-a5xm&1HJGp%$czmV)dAV_IeWMnk0QJVpyi3vOCYb|dC>2q57WWrWQr(8m3Y***- zw72)TNWS$%8WKmsv@kgDxwSPGu$JxGM%y>Fjgsxf#At-4x<~@OeubB>m%SNh%bwL( zy*q)3(+lMkTJ!Po1x3EQsyVm1&W4wF-!={eMXJ4Nt&1s9KXzy1XMZmxba9#JvPixy ziC-RiB%vKFz{4)ZiuUyOLLaum;FVJxHM7V$sAcEt{fN-or0SW2+I<;si$*4t20Z-*2R*kA{T7IOyIUZ4LY~%;h$n zUeBpT(x4N};m#;Nz?zUY7W{MqT(YYyRzq9c{kFCpMBqg>LqGVNHW64&j}cb#C57hE z8nZN*#go^owHb|Od?1-E&O9BM(QJbEwmkhZbITc3RTunYR1gzHIZn|I+ss3eS)~)2 zhmm6;LJL?iq<`hpuoW7b6ULW1p((7C#iAzF+h#XwriZk;hG8(+QF>s+n7>apaAhye z_4$NmrVT-9#Lj1BX>riNYf?Cp0%>+SwQv zAPbY!kZ#lla{6=F!ZkZy*Tyw{Zdc+vM(74|NVI3&(ct z_Tcoqslb)E@1me1zD68Kr^>_FPUdGBD~$2d4PMjtp`A zh(}qI)n3B_$jG&{IZxV0y#VN}zf(w=9G7QnlV~mkd&}gZEuyd)xUvASa zSl)9FbXU`V2UZaNzX!{!nW6w@Nd->}>G=#XHJ*$!?k#L>iI&`FIwl2EbZ9XR!9JYw)Z9s!a4<*ef6 z{!|!r$0B5k6rjFw@dg^$7eACLWZRP(L)1ya1sM5dM%18OO5vZrh5b_~{@ItMWxgtwR2ue;98xSoxBRal+k(%JtW$A}H8uF+fi=IJcY zBD<|y&mVlb9~$;5Y2DTTf=hB=o*d0P1&$LCu!(*CxI4|+G5`E+{ZbJ^{_O4K62{FZ zq$}?}O05Jq)J3wd4`ODjG9zSKDtj9qVJ;g|)Gu{9u3nhytT;2-LrpMGiW#=kfBh}t z*(sb&^Mn3);=QHos`=(V4h)(^Zdoc>QfR)NbIKPSxQHMUnlC2_BY{{{Osz)B#rcnnlWQXrAMAlds!dfRNtiRaMZpOxDH(= z4>OzpesD^}hQyj2XR32kIfp%aHR2z! Cj5Uq` diff --git a/docs/graphics/eventmapping_devsports.png b/docs/graphics/eventmapping_devsports.png index 2fa98cc0c56790beb02fed27405ef16be98946cb..5cd391cfb7e63ecd69a5ef407430ad62a3c9229b 100644 GIT binary patch literal 8996 zcmZvC2T)UM*KR_Sj*5zOP(f6rNDUBBq9TSSDCN+Lbchm)^k4}pQbbB<0hEBDBQ-z( z4@id~Ep+ry0)(Oj5<|T~&-efH-FqjqC)w}pJ(FZV>nUr!uS|^ecsPYP0RRAx{+*kq z006L;am2B+F;;}2O5Yi8Y)(+Un}EZkR}Q}59b<*#(H$!|0B}6>=m1LhX(%%mkNN8x z-a7W3o#~{6z|-q$HH;-e|67*+5SWk8LtlRY#P6Y_|3epvAP;|c2|ay76N@Nb0RTV( zpnvnm{U;M^)A+b))d_&ZIO^I9=3nPbrN5kujj@$LOe*@ZmQww17kb##zl1Vdhh+}#bDl9B43fy7=tk=`e+nMv7W(62Dn6g~g@5M?CPn`b{ zf91LnVbNnNjKilna0xQWl=Z60PrH!cRzd%HHyEXX>rWj@Ck-43#c zqE}VoV}f~XX_dKffzX-ZsDNt1ERA4yY0hZ6|ACxW3I*+JV7}&9dQZZP=O4Rx^3v%? zJxDb@D=afPyBQ-4ClWLkilaHozsLS<@(n5&ewOo;WNRv}<`TX*Iu6TG=S*^1kuGc7 z#9b~)EHI}gMniSAxj@wO<}FIf|E#pAE{IHOatdNN$1O^IoI;{-gt`0sf-6zf1#E+D zr;KqayuJqk+IFa)KECQ9`E#+(;5jAlZ0t6xu0QZ*E`f+e0&0ze+j3F6YKqB!%<1XG1$FQ zK`mXXit=X}nu0gw5E^@1R;3v4PiIG^T_)aZK2x5`S(nQ7m#7RaJ$4zSHW>$=6V>FC z-0iM5T)}NKx`>Sp`*%Y?} zgE#DA+*nLN`vo`R_jz4|SX&h&7++K{}(nmjXfE(iRa;abVRA z?7}-rBLOUc#n%l;TvIJhD{@0!iABwcO&BlzShoy;CJ{^*z6vOVTN`23R-e`+$EeNg zs%yyiurXyT#Ee6`^y0sVE1~MIA{JIInb3 zTcF~GNB_XXUD$4f+*M2DVuiJ~^XGtKZg?tTP>nBCL{(1OK~6D3R7m*_Oud0;V2kfu0txk{FDK(kr^i_+iv5_9E z;KaF}XX_l+c(!!m%Bm?%78R|VtG{eNc}+#pz!#w&dw!VKCOxjZtBy&-k4U}7{K{t1 zRaJ-7&ZxX_=dVF;(2g^;6H2|qWz5g_rcX3`9@oHPLRY6H+f|x{$7;)@DDV7cYGtz% z$)2TU+DTidUk|%gLI_Wwmq}9(^V|eoM;7pYghG+KZY#m}msCMR=@Zg{iyOmlx(8+n z9H(&oZr%}oz0!Sevxh0#s&B7;VUTT8vH3NX-U%K~XK+6XZSM(_b=&wPUox%e}__H^@#>Mhio!u0=S*ot3C<%78zE;!uv>o8#EGNi;wtu z^9DQWk=D!>#7O$>w$cTzBN}H%0VEuluIo!*+~2zT-Nou+TyX<(VL0Ri#*QmqEWCC} z^B(Po-zFw?L1XfheF&<&(m}QA5nPTz`I+1Z$;qVSGVN!Q&}0f}mW7j1!2OSh%I6J7 zzPjv+xz5@~(s;F8GRDH+V1{NQWaP~teO@9K*xl|lqb1zgiPDB_EJdxR8M`;Xej+~E z*Z>VzOX#bQUu%ynlr-ewb>0>9s&b|Y4E{0}wU|}#qE!xjUrM;vTirMHmi+_km2HJd zf$-3eh0}oN-u#8rC~nM{BSvjJb5Z)tsi%vO>n1eq!bI|x-Tjd zu)xQ%FO_5GEswh&Lf2vq77F%v13zR5$uTH zr$dJ%Hn!`isB9GcEyZ$T*Sg48HkpJ$PK++y^MCemsOTzAZ>Z0?{}mV)hs$eZM{x** z%(}cu7=IyUy<3+*__B*I75?^+&9?{{iA6NHVMHe_BPM`aZY|cHD2o<@RV&%)n6bi+ zss`YbgIgDyqH_g8hjMsT zYOnr{^u(u+)s*F~%&{RJu zo7PN?Jw5bPAR#U-Kz&p=9Zmxe*BOS3y(10V*~Qu2D&3o(u<+D?_E{PkUa8S8Kr;-1 zqMCbi_~CMor<9_S7Q_u{#QHg5#3IOs0HWe;#Y0{|sN7WEu8~Qj3!O>AgQrkBXd)K-1?==h* z9#;V7TqrW!d1XsUaPA5yg^YG_kLxXkWSA|lRq%Ecr;S&SXxew2G3n1LxN64)11SOF z@^=Voctia8k+2>kw1- z7VnT$XM8V}G|hI&-@R6TEw^e>6=Amgd>*$C@BC@J0IK5dsDUnnctnZA+PXao7s{L` z+w=J0{SaiJGr3gn^iMT*v(GXv>LLBwyMsLAL3h4+!zQOov#4W%5D(6DN7b|~eu{w% zMs33A1D$Ai#U;aL_w}WB6?1&k7VZPx_eLcz!wJ_ea-bmKvL?IH8KH%NxrxhT zn8goOb5BurvtrEds}v*7(8o7uX=a}1r-;^8n;R*2FG@|@Jgm#dbbHBnXlxk-FN@iR z{@szu24BIQqxE?9P)1T~90V6XFZkg~3Uw@e?CWxuRM{XrZNt5Nx$V`%R-Y<@r4ssl*zo&?(+QlSHtb3cGq~MnDtTx zN#=viLMEIFd$3>e*Q*8mL0!#Cy`7E&(_^(H=&@cYrgU_-;%^K$xa1gVk?xUiD*DO& z&JZ7ds4oeK!ICv5tz%dWRBN}rrV(@9pl4GFvPT4+pf{YbI(*Wo0{TF-YidsPuk$Am zS=$N?TqoZVJA&kcgf9@P0LnSh`bdOcgd|k21lKs60=0S}cXFy$=I6{sitc! zvU?v^x({30=&0e^_r9z%-OQn{rWb+OzQxlO7){!VNIgHG;(^Kk+6S%uP6*&HAMI-& zZ{rQ8vZgorm`0!K*c@65#`wOgR2vs?FF+g1Wy!1GYFQ=6R%Khr3EC{O_4%fUFPD6E z77A7EQLQH|)I`{&E9pS+-^ScfEw)AYI*Bd1OB@MP1*3w0Imj3PDRYZru-yoFCDa`T56!XBXALJH;M$F%0Y0!U7p<-?tQTW+(g$l>}#YrCm8|wv3s-cp)}( zfj`k+W}1PTuA=0|@I!1Nd8dakBLzL7D?A_gK}UH?qpEN0c_t3uQ!RaHfH?tQHJdC3 zS#eNXG9qMj4V*~&mWTuPpI48~{u=;z*B@j$om(i|Kk%{F3}l~Ze!S>Q@VUtRC1#^ z*i-l!Ufg#k%;flzBU6xySN!I&GRd2K`~U_GTV_jIA%L?wp(?)`)+Z_%rgjw5SIrZG z(oe?i!{q7HD;u?ULwmwSzc*H9#c$Zn?5draPoJe_ZP-joI7nM8ny$AgzkY-kb8Ema zM~n)r`uVihaCiBh`u2EcJhI$Bg0;8x!H=f9qJ9PH{y}Qg-2`P_wC!`7HxPkG-s5fF zO4KQ7*NlW&nyAGRh-ci__k)17T7Db@q?Jb2PZ7aO4WC`>lxv~{IV0Ap>RYA5ci9F1 z;uyDBcSt+=tAW1kH8we!H+7vm*=z~4wytwQr(*j&#y5|x-6q)eK<36B908|#X;QM; z{N$!=&aRcalz8Zm=C)hunwINpQ(rQNNwlK%%W7a!->03FIr%&dc743t`x@{^!cvSjZAo)S@4r=_k_EUKL?|?X>LV zWNfTD*`9y1U{gw+ep*0!yaocX@zx4<6ru$nXmwra2pJ54c2Ge({;jV(`(?hg zGUAvh*x-6Ow>dE&fYFe6dYzBIt|ud6eG`9wO96Pjg@vL0IqE599}1b!j`^}M6Uh$fxDXq?4aKF; z>?tBS(c?r3HG!a7$Nmu$uOF9k4?Yr}K5XcFR+M?#t1t@nm#y}NtaVjLt1aB(V>bMl zP~ygbmV7`*Mp2k)d=b9Sq%|qMe`<&SUH`yi*|*tPf()gd3#cUXEmnmkTp;X@hI6FO zh&yfjHQe4N7r(I{D9IDlsag@#5AVKsAqy2Aa44smWwU*e_#rxTTW}0*^@)qEzfvsH z^Mm+qkhON%8k!`$8=N;*%N|@WV}jKv9KVp+<~QgAF8A zug|!DlTUQV4sLHf;_+FSrMIg+va5MgV+~AtxExkyCD3mgiQGJ|4hs@PYQuT^Nq+?M zyOi%@?|PrO51FCQ`eQFQzP-ewT3aZUzNgkgW&$jQqL>gD2^G@aClgrwg|Cx@UopID$l%e_|gt2pSrQ_qz__T7Ti^4yZZf#G&;bS0gooq z>Ru+{S^@ASy$NWk|u$5RrNbKZ{YZsEL;atO^f1Y^s%WaghA@SjbXiVO_kbX0A z_$toxzrdlKL#xMD>ly&f zggC-e;GL4=M+$^3p3aV!owp?17jx#ZS&q3~+3YZWP)5N=IuMza(SI)J2u7r0+{j&0Q)kSyyxr2rHW(Hubq$kTM4&!S;jc zl+PI9@>H!_uXV1ojINr4{N0Eh;mxM5%aV|I=sMT+mMIzaSn%v!pDi#64SsiHkXJa&V zNZWr5i(+bBT47@aY~$?8T3auZ=#K2xLoRHTdY*+w0Z}-Elw_4tQRz|;=jDFe_pXLw z>p%saV}BpzT76R9zPTuvzC#u;zR4U?mQi0yIHB;}Kn=l^kLdaY97>Q~y{U=;A$Ciibc!H|tVV(_nq z9G(18lmA^-f6j3B=_8X6aO94qKJ%keF<0;MVsd?bFy#j0v`NgB(c>fN9D>~&$epK0 znF%bMhN&N0y zLuw@xX?V7sTP28c?LO6c+0bWbKYW({MsSC>3l=w*Zk_GC`OYg+k^TxK4n+@*!5v>x zGhYiPu9>$;h2HR5FA_BJ;Vbf02}>fMgB2(#+lXikYXxW{sizwZ)pWw?T%tR?B|3eS zU&C6YQi3xB1kOE=!%KpVoCz-+E;*bTr&ynNe`70xjx8<-*Y;Ds2?Z14usRjH#$A*| zx>aFVefA5s92Jrb_ZqZZtM(tH3yJqMdW}s&4~=w{zS6Nkri-}+UJ+;MWfE$o+Iqk% zR_RO};c}IM^1kswJ$l|UyZs$A9!ixlAG6mC%`+AhmOn@8bNt-~AQo zC(@cux7yti3W)&MhWRCvEj6qOw;VM2^}zYj$r5X6Co zc>}OPSv&Ll`5m&`>a06@4w|~g`84&kwToB%huqNBzem6W|a(m}J zjGw1_-l09&lUlGsn8cMAA=1cl^g9B+vy_siw6@UI?c_w#d7}6SK84xVM7h%Z#~R&9 z`!*das#^HASs&4={shwC%3dOUd=B(N`~QQDlVQ<1{<%n-HZuaaAR>E$pMxH@QrEs& zH6lA+(I^YPqLQBp&!VAr6#ebsd2fp{p(RD-$6&LOg)8-F0fkA9=bRiUfQe_z8?i*g z;J5V-94#|ahC3$!Ky_iV@dhJ@AARadoY@*`M1b#ileYj4L~!qzgVi4u zN@V*M2JS6(7*8BZ$-|miYE2iPn2+P^h&^L%Vl6?Vc|-u_<`!qu6P8U<|x$e zdSVJA)U6`)54KtoXer}>#tP)>0VN#Pge{Z&ohy z-0sA6f=-Et%s>bb0}U+6X|0~Kw=$zFxk`9&(Z2R8$GB_Pb;YV|SD9K^1Cu>75tTZ%|y%z!iSAYW^yI8BTJF z2xsdyX-n|#9I%<4e&Wt|k-C9I$5Rdev2;>b)I$?@njCru7A@m12SpII?7c?a{=cPv za=0^g+h7zer5PmcvMM_;R^`<5=wp;o)>|Qsxd0=@<@ICi?d@>`z}EaMdETdo{lKAY!93a({sw+6;>{_nd+DI@?E>QPRjZ23Zt*GFMJC z&J!}SC+ukg9rkso_4SV2+onw?SvU0hWm3RmnvBx(xe3x6Uthb;p&nOsUhID754X^a z1jlJi^gqZ=rK*hz0x}N2%gDdTIJLl!{(YNJ#z1E-{c=Hqzn>2?JBlXss_A=GQ~QTW zlY?L6jc1?pH)z(Zzd-aq(HCD?@kyBc`NgVNp9AQWyHh;)Le_wWYO>KC!S)oT@?u+_ zu9|H7eBQnQ;oJ)U(I+#f6SLRda)IuBjw89@X&025xq)B<)s-mmd!@n|)q}4<_Gls_ zV5i>>>$9`7=L_!1!L^qMEm5Wh2lWLHSZgYa%97{nhImD1yf?hMH1`Jf48YHzSF>#v zG*XQE(@H=v-I~4EU85wfm$W}_B+zS7r6=@7v3cU=KTi`7>MBznAk(UZ(>9Oo*es0RNGcz`6bq)RAJG0DLT5$$&Kp z@}j2gnOf2qW14xW+on-V*uhrCAn^AP^S>b4(r@#{dymlpm#Sf^OqR?F^sK*n@3zO< z8YH<~meCTAVqs5-sg~-N!Q=6-r$6%@?iZNaZL&mcm*^tWs?3IbUR`?)gMnL*BK5yt zJLiH0j(_hbadsn|TGkwvC4~nqx6KD*_W>P>Bcm$K**o6y9Tt9QVmH|d+^EgoR`hmuCY@DOBOPbT}!$)(HKqNTXx3$hO7+Bot&(6Uue?3kkH+uI=Flj zuoOgDgxE09*KhEN-6AKOGaxng9R* literal 9253 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 E4^Wu9wEzGB literal 6803 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 deadzone sizeDeadzone area for axes on joysticks/gamepads-joydeadzone Digital paddle sensitivitySensitivity used when emulating a paddle using a digital device-dsense Mouse paddle sensitivitySensitivity used when emulating a paddle using a mouse-msense + Trackball sensitivitySensitivity used when emulating a trackball device using a mouse-tsense Allow all 4 ...Allow all 4 joystick directions to be pressed simultaneously-joyallow4 Grab mouse ...Keep mouse in window in emulation mode-grabmouse Use Control key combosEnable using Control key in keyboard actions-ctrlcombo diff --git a/src/emucore/AmigaMouse.hxx b/src/emucore/AmigaMouse.hxx index cb82c7a3b..f36b67eb3 100644 --- a/src/emucore/AmigaMouse.hxx +++ b/src/emucore/AmigaMouse.hxx @@ -20,24 +20,31 @@ #include "PointingDevice.hxx" -namespace { +class AmigaMouse : public PointingDevice +{ + public: + /** + Create a new Amiga Mouse controller plugged into the specified jack - class AmigaMouseHelper { + @param jack The jack the controller is plugged into + @param event The event object to use for events + @param system The system using this controller + */ + AmigaMouse(Jack jack, const Event& event, const System& system) + : PointingDevice(jack, event, system, Controller::AmigaMouse, + trackballSensitivity) { } + virtual ~AmigaMouse() = default; - public: - static uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8 left, uInt8 down) { - static constexpr uInt32 ourTableH[4] = { 0x00, 0x10, 0x50, 0x40 }; - static constexpr uInt32 ourTableV[4] = { 0x00, 0x80, 0xa0, 0x20 }; + protected: + uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8, uInt8) override + { + static constexpr uInt32 ourTableH[4] = { 0x00, 0x80, 0xa0, 0x20 }; + static constexpr uInt32 ourTableV[4] = { 0x00, 0x10, 0x50, 0x40 }; - return ourTableV[countV] | ourTableH[countH]; - } + return ourTableH[countH] | ourTableV[countV]; + } - public: - static constexpr Controller::Type controllerType = Controller::AmigaMouse; - static constexpr double trackballSensitivity = 0.8; // TODO: make configurable - }; -} - -using AmigaMouse = PointingDevice; + static constexpr float trackballSensitivity = 0.8; +}; #endif // AMIGAMOUSE_HXX diff --git a/src/emucore/AtariMouse.hxx b/src/emucore/AtariMouse.hxx index 2976cf233..3eb2fe0fe 100644 --- a/src/emucore/AtariMouse.hxx +++ b/src/emucore/AtariMouse.hxx @@ -20,24 +20,31 @@ #include "PointingDevice.hxx" -namespace { +class AtariMouse : public PointingDevice +{ + public: + /** + Create a new Atari Mouse controller plugged into the specified jack - class AtariMouseHelper { + @param jack The jack the controller is plugged into + @param event The event object to use for events + @param system The system using this controller + */ + AtariMouse(Jack jack, const Event& event, const System& system) + : PointingDevice(jack, event, system, Controller::AtariMouse, + trackballSensitivity) { } + virtual ~AtariMouse() = default; - public: - static uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8 left, uInt8 down) { - static constexpr uInt32 ourTableH[4] = { 0x00, 0x80, 0xc0, 0x40 }; - static constexpr uInt32 ourTableV[4] = { 0x00, 0x10, 0x30, 0x20 }; + protected: + uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8, uInt8) override + { + static constexpr uInt32 ourTableH[4] = { 0x00, 0x10, 0x30, 0x20 }; + static constexpr uInt32 ourTableV[4] = { 0x00, 0x80, 0xc0, 0x40 }; - return ourTableV[countV] | ourTableH[countH]; - } + return ourTableH[countH] | ourTableV[countV]; + } - public: - static constexpr Controller::Type controllerType = Controller::AtariMouse; - static constexpr double trackballSensitivity = 0.8; // TODO: make configurable - }; -} - -using AtariMouse = PointingDevice; + static constexpr float trackballSensitivity = 0.8; +}; #endif // ATARIMOUSE_HXX diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index 3ec2bfe56..cb043aa95 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -32,6 +32,7 @@ #include "OSystem.hxx" #include "Joystick.hxx" #include "Paddles.hxx" +#include "PointingDevice.hxx" #include "PropsSet.hxx" #include "ListWidget.hxx" #include "ScrollBarWidget.hxx" @@ -95,6 +96,7 @@ void EventHandler::initialize() Joystick::setDeadZone(myOSystem.settings().getInt("joydeadzone")); Paddles::setDigitalSensitivity(myOSystem.settings().getInt("dsense")); Paddles::setMouseSensitivity(myOSystem.settings().getInt("msense")); + PointingDevice::setSensitivity(myOSystem.settings().getInt("tsense")); // Set quick select delay when typing characters in listwidgets ListWidget::setQuickSelectDelay(myOSystem.settings().getInt("listdelay")); diff --git a/src/emucore/Paddles.hxx b/src/emucore/Paddles.hxx index 3d126381c..0f7c9777f 100644 --- a/src/emucore/Paddles.hxx +++ b/src/emucore/Paddles.hxx @@ -127,12 +127,12 @@ class Paddles : public Controller // Range of values over which digital and mouse movement is scaled // to paddle resistance - static const int TRIGMIN = 1; - static const int TRIGMAX = 4096; + static constexpr int TRIGMIN = 1; + static constexpr int TRIGMAX = 4096; static int TRIGRANGE; // This one is variable for the upper range - static const int MAX_DIGITAL_SENSE = 20; - static const int MAX_MOUSE_SENSE = 20; + static constexpr int MAX_DIGITAL_SENSE = 20; + static constexpr int MAX_MOUSE_SENSE = 20; static int DIGITAL_SENSITIVITY, DIGITAL_DISTANCE; static int MOUSE_SENSITIVITY; diff --git a/src/emucore/PointingDevice.cxx b/src/emucore/PointingDevice.cxx new file mode 100644 index 000000000..668889a46 --- /dev/null +++ b/src/emucore/PointingDevice.cxx @@ -0,0 +1,156 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2017 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#include "Control.hxx" +#include "Event.hxx" +#include "System.hxx" +#include "TIA.hxx" + +#include "PointingDevice.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PointingDevice::PointingDevice(Jack jack, const Event& event, + const System& system, Controller::Type type, + float sensitivity) + : Controller(jack, event, system, type), + mySensitivity(sensitivity), + myHCounterRemainder(0.0), myVCounterRemainder(0.0), + myTrackBallLinesH(1), myTrackBallLinesV(1), + myTrackBallLeft(false), myTrackBallDown(false), + myCountH(0), myCountV(0), + myScanCountH(0), myScanCountV(0), + myFirstScanOffsetH(0), myFirstScanOffsetV(0), + myMouseEnabled(false) +{ + // The code in ::read() is set up to always return IOPortA values in + // the lower 4 bits data value + // As such, the jack type (left or right) isn't necessary here +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 PointingDevice::read() +{ + int scanline = mySystem.tia().scanlines(); + + // Loop over all missed changes + while(myScanCountH < scanline) + { + if(myTrackBallLeft) myCountH--; + else myCountH++; + + // Define scanline of next change + myScanCountH += myTrackBallLinesH; + } + + // Loop over all missed changes + while(myScanCountV < scanline) + { + if(myTrackBallDown) myCountV--; + else myCountV++; + + // Define scanline of next change + myScanCountV += myTrackBallLinesV; + } + + myCountH &= 0x03; + myCountV &= 0x03; + + uInt8 portA = ioPortA(myCountH, myCountV, myTrackBallLeft, myTrackBallDown); + + myDigitalPinState[One] = portA & 0x10; + myDigitalPinState[Two] = portA & 0x20; + myDigitalPinState[Three] = portA & 0x40; + myDigitalPinState[Four] = portA & 0x80; + + return (portA >> 4); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PointingDevice::update() +{ + if(!myMouseEnabled) + return; + + // Update horizontal direction + updateDirection( myEvent.get(Event::MouseAxisXValue), myHCounterRemainder, + myTrackBallLeft, myTrackBallLinesH, myScanCountH, myFirstScanOffsetH); + + // Update vertical direction + updateDirection(-myEvent.get(Event::MouseAxisYValue), myVCounterRemainder, + myTrackBallDown, myTrackBallLinesV, myScanCountV, myFirstScanOffsetV); + + // Get mouse button state + myDigitalPinState[Six] = (myEvent.get(Event::MouseButtonLeftValue) == 0) && + (myEvent.get(Event::MouseButtonRightValue) == 0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool PointingDevice::setMouseControl( + Controller::Type xtype, int xid, Controller::Type ytype, int yid) +{ + // Currently, the various trakball controllers take full control of the + // mouse, and use both mouse buttons for the single fire button + // As well, there's no separate setting for x and y axis, so any + // combination of Controller and id is valid + myMouseEnabled = (xtype == myType || ytype == myType) && + (xid != -1 || yid != -1); + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PointingDevice::setSensitivity(int sensitivity) +{ + BSPF::clamp(sensitivity, 1, 20, 10); + TB_SENSITIVITY = sensitivity / 10.0; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PointingDevice::updateDirection(const int& counter, float& counterRemainder, + bool& trackBallDir, int& trackBallLines, int& scanCount, int& firstScanOffset) +{ + // Apply sensitivity and calculate remainder + float fTrackBallCount = counter * mySensitivity * TB_SENSITIVITY + counterRemainder; + int trackBallCount = std::lround(fTrackBallCount); + counterRemainder = fTrackBallCount - trackBallCount; + + if(trackBallCount) + { + trackBallDir = (trackBallCount > 0); + trackBallCount = abs(trackBallCount); + + // Calculate lines to wait between sending new horz/vert values + trackBallLines = mySystem.tia().scanlinesLastFrame() / trackBallCount; + + // Set lower limit in case of (unrealistic) ultra fast mouse movements + if (trackBallLines == 0) trackBallLines = 1; + + // Define scanline of first change + scanCount = (trackBallLines * firstScanOffset) >> 12; + } + else + { + // Prevent any change + scanCount = INT_MAX; + + // Define offset factor for first change, move randomly forward by up to 1/8th + firstScanOffset = (((firstScanOffset << 3) + rand() % + (1 << 12)) >> 3) & ((1 << 12) - 1); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +float PointingDevice::TB_SENSITIVITY = 1.0; diff --git a/src/emucore/PointingDevice.hxx b/src/emucore/PointingDevice.hxx index 9f4a7f42f..b1dace7bf 100644 --- a/src/emucore/PointingDevice.hxx +++ b/src/emucore/PointingDevice.hxx @@ -18,22 +18,23 @@ #ifndef POINTING_DEVICE_HXX #define POINTING_DEVICE_HXX +class Controller; +class Event; + #include "bspf.hxx" -#include "Control.hxx" -#include "Event.hxx" /** Common controller class for pointing devices (Atari Mouse, Amiga Mouse, Trak-Ball) This code was heavily borrowed from z26. @author Stephen Anthony, Thomas Jentzsch & z26 team - Template-ification by Christian Speckner */ -template class PointingDevice : public Controller { public: - PointingDevice(Jack jack, const Event& event, const System& system); + PointingDevice(Jack jack, const Event& event, + const System& system, Controller::Type type, + float sensitivity); virtual ~PointingDevice() = default; public: @@ -72,13 +73,28 @@ class PointingDevice : public Controller bool setMouseControl(Controller::Type xtype, int xid, Controller::Type ytype, int yid) override; + /** + Sets the sensitivity for analog emulation of trackball movement + using a mouse. + + @param sensitivity Value from 1 to 20, with larger values causing + more movement (10 represents the baseline) + */ + static void setSensitivity(int sensitivity); + + protected: + // Each derived class must implement this, to determine how its + // IOPortA values are calculated + virtual uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8 left, uInt8 down) = 0; + private: - void updateDirection(const int& counter, double& counterRemainder, + void updateDirection(const int& counter, float& counterRemainder, bool& trackBallDir, int& trackBallLines, int& scanCount, int& firstScanOffset); private: - double myHCounterRemainder, myVCounterRemainder; + // Mouse input to sensitivity emulation + float mySensitivity, myHCounterRemainder, myVCounterRemainder; // How many lines to wait between sending new horz and vert values int myTrackBallLinesH, myTrackBallLinesV; @@ -101,143 +117,17 @@ class PointingDevice : public Controller // Whether to use the mouse to emulate this controller bool myMouseEnabled; - private: + // User-defined sensitivity; adjustable since end-users may have different + // mouse speeds + static float TB_SENSITIVITY; + +private: // Following constructors and assignment operators not supported PointingDevice() = delete; - PointingDevice(const PointingDevice&) = delete; - PointingDevice(PointingDevice&&) = delete; - PointingDevice& operator=(const PointingDevice&) = delete; - PointingDevice& operator=(PointingDevice&&) = delete; + PointingDevice(const PointingDevice&) = delete; + PointingDevice(PointingDevice&&) = delete; + PointingDevice& operator=(const PointingDevice&) = delete; + PointingDevice& operator=(PointingDevice&&) = delete; }; -// ############################################################################ -// Implementation -// ############################################################################ - - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -template -PointingDevice::PointingDevice(Jack jack, const Event& event, const System& system) - : Controller(jack, event, system, T::controllerType), - myHCounterRemainder(0.0), myVCounterRemainder(0.0), - myTrackBallLinesH(1), myTrackBallLinesV(1), - myTrackBallLeft(false), myTrackBallDown(false), - myCountH(0), myCountV(0), - myScanCountH(0), myScanCountV(0), - myFirstScanOffsetH(0), myFirstScanOffsetV(0), - myMouseEnabled(false) -{ - // The code in ::read() is set up to always return IOPortA values in - // the lower 4 bits data value - // As such, the jack type (left or right) isn't necessary here -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -template -uInt8 PointingDevice::read() -{ - int scanline = mySystem.tia().scanlines(); - - // Loop over all missed changes - while(myScanCountH < scanline) - { - if(myTrackBallLeft) myCountH--; - else myCountH++; - - // Define scanline of next change - myScanCountH += myTrackBallLinesH; - } - - // Loop over all missed changes - while(myScanCountV < scanline) - { - if(myTrackBallDown) myCountV--; - else myCountV++; - - // Define scanline of next change - myScanCountV += myTrackBallLinesV; - } - - myCountH &= 0x03; - myCountV &= 0x03; - - uInt8 ioPortA = T::ioPortA(myCountV, myCountH, myTrackBallDown, myTrackBallLeft); - - myDigitalPinState[One] = ioPortA & 0x10; - myDigitalPinState[Two] = ioPortA & 0x20; - myDigitalPinState[Three] = ioPortA & 0x40; - myDigitalPinState[Four] = ioPortA & 0x80; - - return (ioPortA >> 4); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -template -void PointingDevice::update() -{ - if(!myMouseEnabled) - return; - - // Update horizontal direction - updateDirection( myEvent.get(Event::MouseAxisXValue), myHCounterRemainder, - myTrackBallLeft, myTrackBallLinesH, myScanCountH, myFirstScanOffsetH); - - // Update vertical direction - updateDirection(-myEvent.get(Event::MouseAxisYValue), myVCounterRemainder, - myTrackBallDown, myTrackBallLinesV, myScanCountV, myFirstScanOffsetV); - - // Get mouse button state - myDigitalPinState[Six] = (myEvent.get(Event::MouseButtonLeftValue) == 0) && - (myEvent.get(Event::MouseButtonRightValue) == 0); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -template -bool PointingDevice::setMouseControl( - Controller::Type xtype, int xid, Controller::Type ytype, int yid) -{ - // Currently, the various trakball controllers take full control of the - // mouse, and use both mouse buttons for the single fire button - // As well, there's no separate setting for x and y axis, so any - // combination of Controller and id is valid - myMouseEnabled = (xtype == myType || ytype == myType) && - (xid != -1 || yid != -1); - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -template -void PointingDevice::updateDirection(const int& counter, double& counterRemainder, - bool& trackBallDir, int& trackBallLines, int& scanCount, int& firstScanOffset) -{ - // Apply sensitivity and calculate remainder - float fTrackBallCount = counter * T::trackballSensitivity + counterRemainder; - int trackBallCount = std::lround(fTrackBallCount); - counterRemainder = fTrackBallCount - trackBallCount; - - if(trackBallCount) - { - trackBallDir = (trackBallCount > 0); - trackBallCount = abs(trackBallCount); - - // Calculate lines to wait between sending new horz/vert values - trackBallLines = mySystem.tia().scanlinesLastFrame() / trackBallCount; - - // Set lower limit in case of (unrealistic) ultra fast mouse movements - if (trackBallLines == 0) trackBallLines = 1; - - // Define scanline of first change - scanCount = (trackBallLines * firstScanOffset) >> 12; - } - else - { - // Prevent any change - scanCount = INT_MAX; - - // Define offset factor for first change, move randomly forward by up to 1/8th - firstScanOffset = (((firstScanOffset << 3) + rand() % - (1 << 12)) >> 3) & ((1 << 12) - 1); - } -} - #endif // POINTING_DEVICE_HXX diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx index 79fecf190..f2aacf1ad 100644 --- a/src/emucore/Settings.cxx +++ b/src/emucore/Settings.cxx @@ -90,6 +90,7 @@ Settings::Settings(OSystem& osystem) setInternal("cursor", "2"); setInternal("dsense", "10"); setInternal("msense", "10"); + setInternal("tsense", "10"); setInternal("saport", "lr"); setInternal("ctrlcombo", "true"); @@ -309,12 +310,16 @@ void Settings::validate() setInternal("cursor", "2"); i = getInt("dsense"); - if(i < 1) setInternal("dsense", "1"); - else if(i > 20) setInternal("dsense", "10"); + if(i < 1 || i > 20) + setInternal("dsense", "10"); i = getInt("msense"); - if(i < 1) setInternal("msense", "1"); - else if(i > 20) setInternal("msense", "15"); + if(i < 1 || i > 20) + setInternal("msense", "10"); + + i = getInt("tsense"); + if(i < 1 || i > 20) + setInternal("tsense", "10"); i = getInt("ssinterval"); if(i < 1) setInternal("ssinterval", "2"); @@ -413,6 +418,7 @@ void Settings::usage() const << " -cursor <0,1,2,3> Set cursor state in UI/emulation modes\n" << " -dsense Sensitivity of digital emulated paddle movement (1-20)\n" << " -msense Sensitivity of mouse emulated paddle movement (1-20)\n" + << " -tsense Sensitivity of mouse emulated trackball movement (1-20)\n" << " -saport How to assign virtual ports to multiple Stelladaptor/2600-daptors\n" << " -ctrlcombo <1|0> Use key combos involving the Control key (Control-Q for quit may be disabled!)\n" << " -autoslot <1|0> Automatically switch to next save slot when state saving\n" diff --git a/src/emucore/TrakBall.hxx b/src/emucore/TrakBall.hxx index e3ee97b46..37f21158b 100644 --- a/src/emucore/TrakBall.hxx +++ b/src/emucore/TrakBall.hxx @@ -20,24 +20,32 @@ #include "PointingDevice.hxx" -namespace { +class TrakBall : public PointingDevice +{ + public: + /** + Create a new trakball controller plugged into the specified jack - class TrakBallHelper { + @param jack The jack the controller is plugged into + @param event The event object to use for events + @param system The system using this controller + */ + TrakBall(Jack jack, const Event& event, const System& system) + : PointingDevice(jack, event, system, Controller::TrakBall, + trackballSensitivity) { } + virtual ~TrakBall() = default; - public: - static uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8 left, uInt8 down) { - static constexpr uInt32 ourTableH[2][2] = {{ 0x40, 0x00 }, { 0xc0, 0x80 }}; - static constexpr uInt32 ourTableV[2][2] = {{ 0x00, 0x10 }, { 0x20, 0x30 }}; + protected: + uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8 left, uInt8 down) override + { + static constexpr uInt32 ourTableH[2][2] = {{ 0x00, 0x10 }, { 0x20, 0x30 }}; + static constexpr uInt32 ourTableV[2][2] = {{ 0x40, 0x00 }, { 0xc0, 0x80 }}; - return ourTableV[countV & 0x01][down] | ourTableH[countH & 0x01][left]; - } + return ourTableH[countH & 0x01][left] | ourTableV[countV & 0x01][down]; + } - public: - static constexpr Controller::Type controllerType = Controller::TrakBall; - static constexpr double trackballSensitivity = 0.4; // 50% of Atari and Amiga mouse; TODO: make configurable - }; -} - -using TrakBall = PointingDevice; + // 50% of Atari and Amiga mouse + static constexpr float trackballSensitivity = 0.4; +}; #endif // TRAKBALL_HXX diff --git a/src/emucore/module.mk b/src/emucore/module.mk index 364b882af..8469d1a8f 100644 --- a/src/emucore/module.mk +++ b/src/emucore/module.mk @@ -67,6 +67,7 @@ MODULE_OBJS := \ src/emucore/MD5.o \ src/emucore/OSystem.o \ src/emucore/Paddles.o \ + src/emucore/PointingDevice.o \ src/emucore/Props.o \ src/emucore/PropsSet.o \ src/emucore/SaveKey.o \ diff --git a/src/gui/InputDialog.cxx b/src/gui/InputDialog.cxx index c0bc7ad68..2e3bfc8e5 100644 --- a/src/gui/InputDialog.cxx +++ b/src/gui/InputDialog.cxx @@ -20,6 +20,7 @@ #include "OSystem.hxx" #include "Joystick.hxx" #include "Paddles.hxx" +#include "PointingDevice.hxx" #include "Settings.hxx" #include "EventMappingWidget.hxx" #include "EditTextWidget.hxx" @@ -45,7 +46,7 @@ InputDialog::InputDialog(OSystem& osystem, DialogContainer& parent, // Set real dimensions _w = std::min(50 * fontWidth + 10, max_w); - _h = std::min(15 * (lineHeight + 4) + 14, max_h); + _h = std::min(16 * (lineHeight + 4) + 14, max_h); // The tab widget xpos = 2; ypos = vBorder; @@ -164,8 +165,8 @@ void InputDialog::addDevicePortTab(const GUI::Font& font) // Add paddle speed (digital emulation) xpos = 5; ypos += lineHeight + 4; myDPaddleSpeed = new SliderWidget(myTab, font, xpos, ypos, pwidth, lineHeight, - "Digital paddle sensitivity ", - lwidth, kDPSpeedChanged); + "Digital paddle sensitivity ", + lwidth, kDPSpeedChanged); myDPaddleSpeed->setMinValue(1); myDPaddleSpeed->setMaxValue(20); xpos += myDPaddleSpeed->getWidth() + 5; myDPaddleLabel = new StaticTextWidget(myTab, font, xpos, ypos+1, 24, lineHeight, @@ -176,8 +177,8 @@ void InputDialog::addDevicePortTab(const GUI::Font& font) // Add paddle speed (mouse emulation) xpos = 5; ypos += lineHeight + 4; myMPaddleSpeed = new SliderWidget(myTab, font, xpos, ypos, pwidth, lineHeight, - "Mouse paddle sensitivity ", - lwidth, kMPSpeedChanged); + "Mouse paddle sensitivity ", + lwidth, kMPSpeedChanged); myMPaddleSpeed->setMinValue(1); myMPaddleSpeed->setMaxValue(20); xpos += myMPaddleSpeed->getWidth() + 5; myMPaddleLabel = new StaticTextWidget(myTab, font, xpos, ypos+1, 24, lineHeight, @@ -185,6 +186,18 @@ void InputDialog::addDevicePortTab(const GUI::Font& font) myMPaddleSpeed->setFlags(WIDGET_CLEARBG); wid.push_back(myMPaddleSpeed); + // Add trackball speed + xpos = 5; ypos += lineHeight + 4; + myTrackBallSpeed = new SliderWidget(myTab, font, xpos, ypos, pwidth, lineHeight, + "Trackball sensitivity ", + lwidth, kTBSpeedChanged); + myTrackBallSpeed->setMinValue(1); myTrackBallSpeed->setMaxValue(20); + xpos += myTrackBallSpeed->getWidth() + 5; + myTrackBallLabel = new StaticTextWidget(myTab, font, xpos, ypos+1, 24, lineHeight, + "", kTextAlignLeft); + myTrackBallSpeed->setFlags(WIDGET_CLEARBG); + wid.push_back(myTrackBallSpeed); + // Add 'allow all 4 directions' for joystick xpos = 10; ypos += lineHeight + 12; myAllowAll4 = new CheckboxWidget(myTab, font, xpos, ypos, @@ -240,6 +253,10 @@ void InputDialog::loadConfig() myMPaddleSpeed->setValue(instance().settings().getInt("msense")); myMPaddleLabel->setLabel(instance().settings().getString("msense")); + // Trackball speed + myTrackBallSpeed->setValue(instance().settings().getInt("tsense")); + myTrackBallLabel->setLabel(instance().settings().getString("tsense")); + // AtariVox serial port myAVoxPort->setText(instance().settings().getString("avoxport")); @@ -279,6 +296,11 @@ void InputDialog::saveConfig() instance().settings().setValue("msense", sensitivity); Paddles::setMouseSensitivity(sensitivity); + // Trackball speed + sensitivity = myTrackBallSpeed->getValue(); + instance().settings().setValue("tsense", sensitivity); + PointingDevice::setSensitivity(sensitivity); + // AtariVox serial port instance().settings().setValue("avoxport", myAVoxPort->getText()); @@ -330,6 +352,8 @@ void InputDialog::setDefaults() myDPaddleLabel->setLabel("10"); myMPaddleSpeed->setValue(10); myMPaddleLabel->setLabel("10"); + myTrackBallSpeed->setValue(10); + myTrackBallLabel->setLabel("10"); // AtariVox serial port myAVoxPort->setText(""); @@ -433,6 +457,10 @@ void InputDialog::handleCommand(CommandSender* sender, int cmd, myMPaddleLabel->setValue(myMPaddleSpeed->getValue()); break; + case kTBSpeedChanged: + myTrackBallLabel->setValue(myTrackBallSpeed->getValue()); + break; + case kDBButtonPressed: if(!myJoyDialog) myJoyDialog = make_unique diff --git a/src/gui/InputDialog.hxx b/src/gui/InputDialog.hxx index d471c8e10..d2b56efc5 100644 --- a/src/gui/InputDialog.hxx +++ b/src/gui/InputDialog.hxx @@ -57,6 +57,7 @@ class InputDialog : public Dialog kDeadzoneChanged = 'DZch', kDPSpeedChanged = 'PDch', kMPSpeedChanged = 'PMch', + kTBSpeedChanged = 'TBch', kDBButtonPressed = 'DBbp' }; @@ -75,8 +76,10 @@ class InputDialog : public Dialog StaticTextWidget* myDeadzoneLabel; SliderWidget* myDPaddleSpeed; SliderWidget* myMPaddleSpeed; + SliderWidget* myTrackBallSpeed; StaticTextWidget* myDPaddleLabel; StaticTextWidget* myMPaddleLabel; + StaticTextWidget* myTrackBallLabel; CheckboxWidget* myAllowAll4; CheckboxWidget* myGrabMouse; CheckboxWidget* myCtrlCombo;