diff options
304 files changed, 6243 insertions, 4494 deletions
diff --git a/.clang-tidy b/.clang-tidy index 6112d6bb..4439fa18 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -42,6 +42,14 @@ Checks: -cert-flp30-c, -modernize-redundant-void-arg, -hicpp-explicit-conversions, + -readability-function-size, + -cppcoreguidelines-avoid-goto, + -modernize-use-using, + -cppcoreguidelines-c-copy-assignment-signature, + -hicpp-avoid-goto, + -readability-inconsistent-declaration-parameter-name, + -cppcoreguidelines-avoid-magic-numbers, + -readability-magic-numbers, AnalyzeTemporaryDtors: true #HeaderFilterRegex: '/dev/opentrack' diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..2cbc03dd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# https://editorconfig.org/ + +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab + +[*.{bat,cmd}] +end_of_line = crlf @@ -1,5 +1,9 @@ -/build* +/build*/ +/build +/build-* /installer/Output .DS_Store /.idea/ /sdk-paths-*.cmake +/.vs/ +/CMakeSettings.json diff --git a/3rdparty-notices/Kinect-V2-SDK-Eula.rtf b/3rdparty-notices/Kinect-V2-SDK-Eula.rtf new file mode 100644 index 00000000..c2e634f7 --- /dev/null +++ b/3rdparty-notices/Kinect-V2-SDK-Eula.rtf @@ -0,0 +1,525 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff39\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;} +{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;}{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}{\f10\fbidi \fnil\fcharset2\fprq2{\*\panose 05000000000000000000}Wingdings;} +{\f34\fbidi \froman\fcharset1\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f38\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0502040204020203}Segoe UI;}{\f39\fbidi \fswiss\fcharset0\fprq2{\*\panose 00000000000000000000}Tahoma;} +{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f40\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\f41\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f43\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f44\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f45\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\f46\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f47\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f48\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f50\fbidi \fswiss\fcharset238\fprq2 Arial CE;} +{\f51\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;}{\f53\fbidi \fswiss\fcharset161\fprq2 Arial Greek;}{\f54\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}{\f55\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);} +{\f56\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);}{\f57\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;}{\f58\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}{\f60\fbidi \fmodern\fcharset238\fprq1 Courier New CE;} +{\f61\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;}{\f63\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f64\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f65\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);} +{\f66\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}{\f67\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f68\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f430\fbidi \fswiss\fcharset238\fprq2 Tahoma CE;} +{\f431\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr;}{\f433\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek;}{\f434\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur;}{\f435\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew);} +{\f436\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic);}{\f437\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic;}{\f438\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese);}{\f439\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai);} +{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;} +{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;} +{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} +{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} +{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0; +\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\caccentone\ctint255\cshade191\red46\green116\blue181;\caccentone\ctint255\cshade127\red31\green77\blue120;\red31\green73\blue125;} +{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{ +\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{ +\s1\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\outlinelevel0\rin0\lin0\itap0 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \slink15 \sqformat +heading 1;}{\s2\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\outlinelevel1\rin0\lin0\itap0 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext0 \slink16 \sqformat heading 2;}{\s3\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\outlinelevel2\rin0\lin0\itap0 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 +\fs24\lang1033\langfe1033\loch\f39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \slink17 \sqformat heading 3;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused +Normal Table;}{\*\cs15 \additive \rtlch\fcs1 \af0\afs32 \ltrch\fcs0 \fs32\cf17\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink1 \slocked \spriority9 Heading 1 Char;}{\*\cs16 \additive \rtlch\fcs1 \af0\afs26 \ltrch\fcs0 +\fs26\cf17\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink2 \slocked \ssemihidden \spriority9 Heading 2 Char;}{\*\cs17 \additive \rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs24\cf18\loch\f31502\hich\af31502\dbch\af31501 +\sbasedon10 \slink3 \slocked \ssemihidden \spriority9 Heading 3 Char;}{\s18\ql \li0\ri0\nowidctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 +\fs24\lang1033\langfe1033\loch\f39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext18 \slink19 \sunhideused \styrsid1930808 header;}{\*\cs19 \additive \rtlch\fcs1 \af39\afs24 \ltrch\fcs0 \f39\fs24 +\sbasedon10 \slink18 \slocked \styrsid1930808 Header Char;}{\s20\ql \li0\ri0\nowidctlpar\tqc\tx4680\tqr\tx9360\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 +\fs24\lang1033\langfe1033\loch\f39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext20 \slink21 \sunhideused \styrsid1930808 footer;}{\*\cs21 \additive \rtlch\fcs1 \af39\afs24 \ltrch\fcs0 \f39\fs24 +\sbasedon10 \slink20 \slocked \styrsid1930808 Footer Char;}{\*\cs22 \additive \rtlch\fcs1 \af0\afs16 \ltrch\fcs0 \fs16 \sbasedon10 \ssemihidden \sunhideused \styrsid4929922 annotation reference;}{ +\s23\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af39\afs20\alang1025 \ltrch\fcs0 \fs20\lang1033\langfe1033\loch\f39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext23 \slink24 \ssemihidden \sunhideused \styrsid4929922 annotation text;}{\*\cs24 \additive \rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \f39\fs20 \sbasedon10 \slink23 \slocked \ssemihidden \styrsid4929922 Comment Text Char;}{ +\s25\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \ab\af39\afs20\alang1025 \ltrch\fcs0 \b\fs20\lang1033\langfe1033\loch\f39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 +\sbasedon23 \snext23 \slink26 \ssemihidden \sunhideused \styrsid4929922 annotation subject;}{\*\cs26 \additive \rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\f39\fs20 \sbasedon24 \slink25 \slocked \ssemihidden \styrsid4929922 Comment Subject Char;}{ +\s27\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs18\alang1025 \ltrch\fcs0 \fs18\lang1033\langfe1033\loch\f38\hich\af38\dbch\af31505\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext27 \slink28 \ssemihidden \sunhideused \styrsid4929922 Balloon Text;}{\*\cs28 \additive \rtlch\fcs1 \af38\afs18 \ltrch\fcs0 \f38\fs18 \sbasedon10 \slink27 \slocked \ssemihidden \styrsid4929922 Balloon Text Char;}{ +\s29\ql \li720\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin720\itap0\contextualspace \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\f39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext29 \sqformat \spriority34 \styrsid5338358 List Paragraph;}}{\*\listtable{\list\listtemplateid-1933036156\listsimple{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat0\levelspace0\levelindent0{\leveltext +\'01*;}{\levelnumbers;}}{\listname ;}\listid-2}{\list\listtemplateid1663739596\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 +\fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2880\lin2880 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li6480\lin6480 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li7200\lin7200 }{\listname ;}\listid296491462}{\list\listtemplateid1166995046\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 +\fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li3600\lin3600 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li6480\lin6480 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext +\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li7200\lin7200 }{\listname ;}\listid728115947}{\list\listtemplateid-576179798\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li1260\lin1260 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1980\lin1980 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2700\lin2700 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0 \fi-360\li3420\lin3420 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li4140\lin4140 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4860\lin4860 }{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5580\lin5580 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li6300\lin6300 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li7020\lin7020 }{\listname ;}\listid844515658}{\list\listtemplateid2023228974\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li1800\lin1800 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li2520\lin2520 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext +\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li3240\lin3240 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li3960\lin3960 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 +\fi-360\li4680\lin4680 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li5400\lin5400 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li6120\lin6120 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li6840\lin6840 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li7560\lin7560 }{\listname ;}\listid1411999391}{\list\listtemplateid-926094602\listhybrid{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li1800\lin1800 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li2520\lin2520 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 +{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li3240\lin3240 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li3960\lin3960 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 +\fi-360\li4680\lin4680 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li5400\lin5400 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li6120\lin6120 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li6840\lin6840 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li7560\lin7560 }{\listname ;}\listid1653637060}{\list\listtemplateid11189262\listsimple{\listlevel\levelnfc4\levelnfcn4\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelold\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \af39 \ltrch\fcs0 \b\f39\fbias0 }{\listname ;}\listid1681809293}}{\*\listoverridetable{\listoverride\listid1681809293 +\listoverridecount0\ls1}{\listoverride\listid296491462\listoverridecount0\ls2}{\listoverride\listid-2\listoverridecount1{\lfolevel\listoverrideformat{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat0\levelold\levelspace0 +\levelindent360{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 }}\ls3}{\listoverride\listid1653637060\listoverridecount0\ls4}{\listoverride\listid1411999391\listoverridecount0\ls5}{\listoverride\listid728115947\listoverridecount0\ls6} +{\listoverride\listid844515658\listoverridecount0\ls7}}{\*\pgptbl {\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid73750\rsid91414\rsid675470\rsid1909739\rsid1930808\rsid2042763\rsid3882049\rsid3949644\rsid4196885\rsid4796860\rsid4929922\rsid5338358 +\rsid6816407\rsid6969745\rsid7096959\rsid7297413\rsid7679777\rsid7695099\rsid8139599\rsid8222967\rsid8745415\rsid9392452\rsid9909308\rsid10056964\rsid10234499\rsid10296048\rsid10437656\rsid10570424\rsid10897255\rsid11022658\rsid11090850\rsid11808396 +\rsid11829074\rsid11871500\rsid11937711\rsid11949759\rsid13329978\rsid13781786\rsid14225958\rsid14551182\rsid14579304\rsid15495172\rsid15621282\rsid16385968\rsid16457108}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0 +\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\creatim\yr2014\mo8\dy27\hr11\min25}{\revtim\yr2014\mo8\dy27\hr11\min25}{\version1}{\edmins0}{\nofpages6}{\nofwords1938}{\nofchars11052}{\nofcharsws12965}{\vern57437}}{\*\xmlnstbl {\xmlns1 http://schemas. +microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml1\donotembedlingdata0\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701 +\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewscale100\splytwnine\ftnlytwnine\htmautsp\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\rempersonalinfo\allowfieldendsel\wrppunct\asianbrkrule\rsidroot6816407 +\newtblstyruls\nogrowautofit\remdttm\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0{\*\wgrffmtfilter 2450} +\nofeaturethrottle1\ilfomacatclnup0{\*\ftnsep \ltrpar \pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid1930808 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 +\fs24\lang1033\langfe1033\loch\af39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af39 \ltrch\fcs0 \insrsid10296048 \chftnsep +\par }}{\*\ftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid1930808 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 { +\rtlch\fcs1 \af39 \ltrch\fcs0 \insrsid10296048 \chftnsepc +\par }}{\*\aftnsep \ltrpar \pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid1930808 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 { +\rtlch\fcs1 \af39 \ltrch\fcs0 \insrsid10296048 \chftnsep +\par }}{\*\aftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid1930808 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 { +\rtlch\fcs1 \af39 \ltrch\fcs0 \insrsid10296048 \chftnsepc +\par }}\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (} +{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sb120\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 +\af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Microsoft Kinect for Windows}{ +\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 v2}{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Software Development Kit (SDK) +\par }\pard \ltrpar\ql \li0\ri0\sa120\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \li0\ri0\sb120\sa120\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. Please read them. They apply to the software named above, \hich\af39\dbch\af31505\loch\f39 +which includes the media on which you received it, if any. It also applies to any Microsoft}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \fi-360\li360\ri0\sb120\sa120\nowidctlpar\tx360\wrapdefault\faauto\rin0\lin360\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 +\f1\fs20\insrsid8745415 \tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 updates}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 , +\par }\pard \ltrpar\ql \fi-360\li360\ri0\sb120\sa120\nowidctlpar\wrapdefault\faauto\rin0\lin360\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 +\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 supplements, +\par }{\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 \tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +documentation, and}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 +\par }{\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 \tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +support services}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \li0\ri0\sb120\sa120\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +for this software, unless other terms accompany those items. If so, those terms apply. +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 The softwar\hich\af39\dbch\af31505\loch\f39 e is licensed, not sold.}{\rtlch\fcs1 \af39 \ltrch\fcs0 \insrsid8745415 \hich\af39\dbch\af31505\loch\f39 }{ +\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 \hich\f39 +By downloading, installing, accessing, or using the software, you accept all terms in this agreement. If you do not accept them, do not download, install, access, or use the software. \'93\loch\f39 \hich\f39 You\'94\loch\f39 \hich\f39 or \'93\loch\f39 +\hich\f39 you\'94\loch\f39 means the individual who downloa\hich\af39\dbch\af31505\loch\f39 +ds, installs, accesses, or uses the software (and, if you represent a legal entity, it also means that entity, and you represent and warrant that you are authorized to enter into this agreement for that entity).}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 If you comply with these license terms, you\hich\af39\dbch\af31505\loch\f39 have the rights below.}{\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 +\b\f1\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \li0\ri0\sb200\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\insrsid8745415 \hich\af1\dbch\af31505\loch\f1 1. }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 INSTALLATION AND USE RIGHTS. +\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\f39\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 a.\tab}}\pard \ltrpar\ql \fi-270\li450\ri0\sa100\nowidctlpar\tx540\wrapdefault{\*\pn \pnlvlbody\ilvl0\ls1\pnrnot0\pnlcltr\pnb1 +\pnf39\pnstart1 {\pntxta .}}\faauto\ls1\rin0\lin450\itap0\pararsid6816407 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Installation and Use.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 You may (i) install and use any number of copies of the software (only when installed using the accompanying software installer package) on your computer to design, develop, and \hich\af39\dbch\af31505\loch\f39 +test your programs that run specifically on a Microsoft Windows operating system, and that are intended for use solely in connection with }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid11871500 \hich\af39\dbch\af31505\loch\f39 the }{\rtlch\fcs1 +\af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Microsoft Kinect for Windows}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid3882049 \hich\af39\dbch\af31505\loch\f39 v}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 2}{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 }{ +\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 sensor}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 \hich\f39 (\'93\loch\f39 Kinect }{\rtlch\fcs1 \af39\afs20 +\ltrch\fcs0 \fs20\insrsid3882049 \hich\af39\dbch\af31505\loch\f39 v}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid7679777 \hich\af39\dbch\af31505\loch\f39 + Sensor}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \loch\af39\dbch\af31505\hich\f39 \'94\loch\f39 )}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +, and its associated drivers and runtime software, and no other sensor }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid73750 \hich\af39\dbch\af31505\loch\f39 unless such sensor is supported by Microsoft }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 (}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid6969745 \hich\af39\dbch\af31505\loch\f39 collectively, the }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\loch\af39\dbch\af31505\hich\f39 \'93\loch\f39 Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 V2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 \hich\f39 + Applications\'94\loch\f39 ), and (ii) distribute your Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3882049 \hich\af39\dbch\af31505\loch\f39 v}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 2 }{ +\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Applications, subject to the terms in this agreement. +\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \b\f39\fs20\insrsid7695099 \hich\af39\dbch\af31505\loch\f39 b.\tab}}\pard \ltrpar\ql \fi-270\li450\ri0\sa100\nowidctlpar\tx540\wrapdefault{\*\pn \pnlvlbody\ilvl0\ls1\pnrnot0\pnlcltr\pnb1 +\pnf39\pnstart1 {\pntxta .}}\faauto\ls1\rin0\lin450\itap0 {\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \b\fs20\insrsid7695099 \hich\af39\dbch\af31505\loch\f39 Tele\hich\af39\dbch\af31505\loch\f39 metry Data Collection. }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid7695099 \hich\af39\dbch\af31505\loch\f39 When in use by you, the software will provide Microsoft with telemetry data }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\lang9\langfe1033\langnp9\insrsid7695099 \hich\af39\dbch\af31505\loch\f39 +(e.g. operating system, number of processors, graphic chipset, memory, device type, locale, time) regarding your installation and use. }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\lang9\langfe1033\langnp9\insrsid14225958 \hich\af39\dbch\af31505\loch\f39 +The data will not be u\hich\af39\dbch\af31505\loch\f39 sed to identify specific individuals. }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\lang9\langfe1033\langnp9\insrsid7695099 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\lang9\langfe1033\langnp9\insrsid8222967 \hich\af39\dbch\af31505\loch\f39 Microsoft will}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\lang9\langfe1033\langnp9\insrsid7695099 \hich\af39\dbch\af31505\loch\f39 use the Telemetry Data for product and }{ +\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\lang9\langfe1033\langnp9\insrsid91414 \hich\af39\dbch\af31505\loch\f39 service}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\lang9\langfe1033\langnp9\insrsid7695099 \hich\af39\dbch\af31505\loch\f39 improvements. +}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid7695099\charrsid7695099 +\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\f39\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 c.\tab}}\pard \ltrpar\ql \fi-270\li450\ri0\sa100\nowidctlpar\tx540\wrapdefault{\*\pn \pnlvlbody\ilvl0\ls1\pnrnot0\pnlcltr\pnb1 +\pnf39\pnstart1 {\pntxta .}}\faauto\ls1\rin0\lin450\itap0\pararsid6816407 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Included Microsoft Programs.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 The software includes other Microsoft programs. The license terms with those programs apply to your use o\hich\af39\dbch\af31505\loch\f39 f them. +\par {\pntext\pard\plain\ltrpar \rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\f39\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 d.\tab}}\pard \ltrpar\ql \fi-270\li450\ri0\sa100\nowidctlpar\tx540\wrapdefault{\*\pn \pnlvlbody\ilvl0\ls1\pnrnot0\pnlcltr\pnb1 +\pnf39\pnstart1 {\pntxta .}}\faauto\ls1\rin0\lin450\itap0\pararsid6816407 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 No High Risk Use. WARNING:}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 The Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 2 }{ +\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid7679777 \hich\af39\dbch\af31505\loch\f39 S}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 ensor and the software are not fault-tolerant. The Kinect }{\rtlch\fcs1 +\af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid7679777 +\hich\af39\dbch\af31505\loch\f39 Sensor }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 and the software are not designed or intended for use with any program where failure or fault of any kind of the Kinect }{ +\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid7679777 +\hich\af39\dbch\af31505\loch\f39 Sensor}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 or soft +\hich\af39\dbch\af31505\loch\f39 \hich\f39 ware could lead to death or serious bodily injury of any person, or to severe physical or environmental damage (\'93\loch\f39 \hich\f39 High Risk Use\'94\loch\f39 +). You are not licensed to, and you agree not to, use, distribute or sublicense the use of the Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid11871500 \hich\af39\dbch\af31505\loch\f39 v2 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Sensor and/or software in, or in conjunction with, High Risk Use. High Risk Use is STRICTLY PROHIBITED. }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 High Risk Use includes, for example, the following: aircraft navigation and control of other modes of human mass transportation, nuclear or chemical f\hich\af39\dbch\af31505\loch\f39 acilities. }{\rtlch\fcs1 \ab\af1\afs20 +\ltrch\fcs0 \b\f1\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \li0\ri0\sb200\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\insrsid8745415 \hich\af1\dbch\af31505\loch\f1 2}{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 . ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS +\par }\pard \ltrpar\ql \fi-183\li363\ri0\sa100\nowidctlpar\tx363\wrapdefault\faauto\rin0\lin363\itap0 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 a.\tab Distributable Code}{\rtlch\fcs1 \af39\afs20 +\ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 . The software contains code that you are permitted to distribute solely in Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v}{ +\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Applications if you comply with the terms below. +\par }\pard\plain \ltrpar\s3\ql \fi-357\li1077\ri0\sb120\sa120\nowidctlpar\tx1080\wrapdefault\faauto\outlinelevel2\rin0\lin1077\itap0 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 +\fs24\lang1033\langfe1033\loch\af39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 i.\tab Right to Use and \hich\af39\dbch\af31505\loch\f39 \hich\f39 +Distribute. The code and text files listed below are \'93\loch\f39 \hich\f39 Distributable Code.\'94 +\par }\pard\plain \ltrpar\ql \fi-358\li1435\ri0\sb120\sa120\nowidctlpar\tx1437\wrapdefault\faauto\rin0\lin1435\itap0 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 { +\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\ul\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 REDIST.TXT Files}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 . You may copy and distribute the object code form of code listed in REDIST.TXT files}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid11871500 \hich\af39\dbch\af31505\loch\f39 + as part of your Kinect v2 Application}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 . +\par }\pard \ltrpar\ql \fi-358\li1435\ri0\sb120\sa120\nowidctlpar\wrapdefault\faauto\rin0\lin1435\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\ul\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Sample Code}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 . You may modify, copy \hich\af39\dbch\af31505\loch\f39 +and distribute the source and object code form of code in the Samples subdirectory}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid11871500 \hich\af39\dbch\af31505\loch\f39 as part of your Kinect v2 Application}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 +\f1\fs20\insrsid8745415 .}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f0\fs20\insrsid8745415 +\par }{\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\ul\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Third Party Distribution}{\rtlch\fcs1 \af39\afs20 +\ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 . You may permit distributors of your Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Applications to copy and distribute the Distributable Code a\hich\af39\dbch\af31505\loch\f39 s part of those Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 +\hich\af39\dbch\af31505\loch\f39 v}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid4929922 \hich\af39\dbch\af31505\loch\f39 2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Applications. +\par {\listtext\pard\plain\ltrpar \s29 \rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \f3\fs20\insrsid5338358\charrsid11829074 \loch\af3\dbch\af31505\hich\f3 \'b7\tab}}\pard\plain \ltrpar\s29\ql \fi-360\li1440\ri0\nowidctlpar +\tx900\wrapdefault\faauto\ls7\rin0\lin1440\itap0\pararsid11829074\contextualspace \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\ul\insrsid5338358\charrsid11829074 \hich\af39\dbch\af31505\loch\f39 Third Party Programs}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid5338358\charrsid11829074 \hich\af39\dbch\af31505\loch\f39 +. This software may contain certain third-party programs. You agree that your use of them is governed by the license terms provided with those programs. +\par }\pard\plain \ltrpar\ql \li0\ri0\sa100\nowidctlpar\tx1512\wrapdefault\faauto\rin0\lin0\itap0\pararsid11829074 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 { +\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \li720\ri0\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin720\itap0 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 ii. Distribution Requirements.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 For any D\hich\af39\dbch\af31505\loch\f39 istributable Code you distribute, you must: }{\rtlch\fcs1 +\af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \fi-360\li1440\ri0\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin1440\itap0 {\rtlch\fcs1 \af10\afs20 \ltrch\fcs0 \f10\fs20\insrsid8745415 \loch\af10\dbch\af31505\hich\f10 \'a7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 add significant primary functionality to it in your Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid11871500 \hich\af39\dbch\af31505\loch\f39 v2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Applications; +\par }\pard \ltrpar\ql \fi-360\li1440\ri0\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin1440\itap0\pararsid11829074 {\rtlch\fcs1 \af10\afs20 \ltrch\fcs0 \f10\fs20\insrsid8745415 \loch\af10\dbch\af31505\hich\f10 \'a7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 distribute Distributable Code included in a setup program only as part of that setup program without modification}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid10234499 +\hich\af39\dbch\af31505\loch\f39 ;}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid11808396 +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \f10\fs20\insrsid14225958 \loch\af10\dbch\af31505\hich\f10 \'a7\tab}}\pard \ltrpar\ql \fi-360\li1440\ri0\sa100\nowidctlpar\wrapdefault\faauto\ls6\rin0\lin1440\itap0\pararsid11829074 { +\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid14225958 \hich\af39\dbch\af31505\loch\f39 c}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid13329978 \hich\af39\dbch\af31505\loch\f39 learly state in your Kinect V2 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 A}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid13329978 \hich\af39\dbch\af31505\loch\f39 pplication (as well as require your licensees to do so) }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid15495172 \hich\af39\dbch\af31505\loch\f39 a privacy statement}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid13329978 \hich\af39\dbch\af31505\loch\f39 + regarding the collection and use of customer data as well as the following statement regarding Microsoft\hich\f39 \rquote \loch\f39 s }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid91414 \hich\af39\dbch\af31505\loch\f39 collection and }{\rtlch\fcs1 +\af39\afs20 \ltrch\fcs0 \fs20\insrsid13329978 \hich\af39\dbch\af31505\loch\f39 use of customer data}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid10234499 \hich\af39\dbch\af31505\loch\f39 \hich\f39 : \'93}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\ul\insrsid10234499\charrsid11829074 \hich\af39\dbch\af31505\loch\f39 No\hich\af39\dbch\af31505\loch\f39 te}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid10234499 \hich\af39\dbch\af31505\loch\f39 : When using }{\rtlch\fcs1 \af39\afs20 +\ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 the _________________(insert name of Your}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid10234499 \hich\af39\dbch\af31505\loch\f39 Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid10234499 \hich\af39\dbch\af31505\loch\f39 2 Application}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 )} +{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid11871500 \hich\af39\dbch\af31505\loch\f39 with a Kinect for Windows v2 Sensor}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid14225958 ,}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid10234499 +\hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\lang9\langfe1033\langnp9\insrsid10234499 \hich\af39\dbch\af31505\loch\f39 +Microsoft will collect telemetry data (e.g. operating system, number of processors, graphic chipset, memory, device type, locale, time) in\hich\af39\dbch\af31505\loch\f39 order to improve }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\lang9\langfe1033\langnp9\insrsid91414 \hich\af39\dbch\af31505\loch\f39 Microsoft products and services}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\lang9\langfe1033\langnp9\insrsid10234499 \hich\af39\dbch\af31505\loch\f39 \hich\f39 +. The data will not be used to identify specific individuals.\'94\loch\f39 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid13329978\charrsid11871500 \hich\af39\dbch\af31505\loch\f39 +\par }\pard \ltrpar\ql \fi-360\li1440\ri0\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin1440\itap0 {\rtlch\fcs1 \af10\afs20 \ltrch\fcs0 \f10\fs20\insrsid8745415 \loch\af10\dbch\af31505\hich\f10 \'a7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 clearly state in marketing materials, documentation and other materials related to the Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v}{\rtlch\fcs1 +\af39\afs20 \ltrch\fcs0 \fs20\insrsid13329978 \hich\af39\dbch\af31505\loch\f39 2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Application (e.g. on the webpages on whi\hich\af39\dbch\af31505\loch\f39 +ch the Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid13329978 \hich\af39\dbch\af31505\loch\f39 V2 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Application is described or from which the Kinect }{ +\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid13329978 \hich\af39\dbch\af31505\loch\f39 V2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 + Application may be downloaded or otherwise obtained), that it is intended for use only with the Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid13329978 \hich\af39\dbch\af31505\loch\f39 V2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid16457108 \hich\af39\dbch\af31505\loch\f39 S}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 ensor; +\par }\pard \ltrpar\ql \fi-360\li1440\ri0\sb120\sa120\nowidctlpar\wrapdefault\faauto\rin0\lin1440\itap0 {\rtlch\fcs1 \af10\afs20 \ltrch\fcs0 \f10\fs20\insrsid8745415 \loch\af10\dbch\af31505\hich\f10 \'a7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 require }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 licensees, }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 distributors and external end users to agree to }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 license }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 terms that}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 are as protective of Microsoft\hich\f39 \rquote \loch\f39 +s rights and interests in the Distributable Code as stated in this agreement; and }{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f0\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \fi-360\li1440\ri0\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin1440\itap0 {\rtlch\fcs1 \af10\afs20 \ltrch\fcs0 \f10\fs20\insrsid8745415 \loch\af10\dbch\af31505\hich\f10 \'a7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 display your valid copyright notice on your Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid13329978 \hich\af39\dbch\af31505\loch\f39 2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Applications}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 . +\par }\pard\plain \ltrpar\s3\ql \fi-357\li1077\ri0\sb120\sa120\nowidctlpar\tx1077\tx1440\wrapdefault\faauto\outlinelevel2\rin0\lin1077\itap0 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 +\fs24\lang1033\langfe1033\loch\af39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 iii.\tab Distribution Restrictions. You may not: +\par }\pard\plain \ltrpar\ql \fi-358\li1435\ri0\sb120\sa120\nowidctlpar\tx1437\wrapdefault\faauto\rin0\lin1435\itap0 \rtlch\fcs1 \af39\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\loch\af39\hich\af39\dbch\af31505\cgrid\langnp1033\langfenp1033 { +\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 alter\hich\af39\dbch\af31505\loch\f39 + any copyright, trademark or patent notice in the Distributable Code; +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \f10\fs20\insrsid13329978 \loch\af10\dbch\af31505\hich\f10 \'a7\tab}}\pard \ltrpar\ql \fi-360\li1440\ri0\sb120\sa120\nowidctlpar +\tx1437\wrapdefault\faauto\ls2\rin0\lin1440\itap0\pararsid11829074 {\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid13329978 \hich\af39\dbch\af31505\loch\f39 +License the Microsoft Distributable Code in a manner inconsistent with the terms of this License. +\par }\pard \ltrpar\ql \fi-358\li1435\ri0\sb120\sa120\nowidctlpar\tx1437\wrapdefault\faauto\rin0\lin1435\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 use Microsoft\hich\f39 \rquote \loch\f39 s trademarks, including but not limited to Microsoft, Kinect and Window\hich\af39\dbch\af31505\loch\f39 s, in your Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Applications\hich\f39 \rquote \loch\f39 names or in a way that suggests your Kinect for }{\rtlch\fcs1 +\af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v2 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Applications come from or are endorsed by Microsoft; +\par }\pard \ltrpar\ql \fi-358\li1435\ri0\sb120\sa120\nowidctlpar\wrapdefault\faauto\rin0\lin1435\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 distribute Distributable Code to run on a platform other than a Microsoft Windows operating system; +\par }\pard \ltrpar\ql \fi-360\li1437\ri0\sb120\sa120\nowidctlpar\wrapdefault\faauto\rin0\lin1437\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 include Distr\hich\af39\dbch\af31505\loch\f39 ibutable Code in malicious, obscene, deceptive or unlawful programs; +\par }\pard \ltrpar\ql \fi-358\li1435\ri0\sb120\sa120\nowidctlpar\tx1437\wrapdefault\faauto\rin0\lin1435\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 include Distributable Code for any programs designed or intended for High Risk Use; or}{\rtlch\fcs1 \af0\afs20 \ltrch\fcs0 \f0\fs20\insrsid8745415 +\par }{\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +modify or distribute the source code of any Distributable Code so that any part of it becomes su\hich\af39\dbch\af31505\loch\f39 +bject to an Excluded License. An Excluded License is one that requires, as a condition of use, modification or distribution, that}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \fi-357\li1792\ri0\sb120\sa120\nowidctlpar\tx1795\wrapdefault\faauto\rin0\lin1792\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 the code be disclosed or distributed in source code form; or +\par }\pard \ltrpar\ql \fi-357\li1792\ri0\sb120\sa120\nowidctlpar\wrapdefault\faauto\rin0\lin1792\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 others have the right to modify it. +\par }\pard \ltrpar\ql \li0\ri0\sb200\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \ab\af1\afs20 \ltrch\fcs0 \b\f1\fs20\insrsid8745415 \hich\af1\dbch\af31505\loch\f1 3. }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 SCOPE OF LICENSE.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 + The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permit +\hich\af39\dbch\af31505\loch\f39 t\hich\af39\dbch\af31505\loch\f39 ed in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not:}{\rtlch\fcs1 \af1\afs20 +\ltrch\fcs0 \f1\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \fi-360\li720\ri0\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin720\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 access or use, or attempt to access or use, features of the Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v2 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 Sensor that are not \hich\af39\dbch\af31505\loch\f39 exposed or enabled by the software; +\par }{\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415\charrsid1909739 \hich\af39\dbch\af31505\loch\f39 distribute Kinect }{\rtlch\fcs1 +\af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644\charrsid1909739 \hich\af39\dbch\af31505\loch\f39 v2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415\charrsid1909739 \hich\af39\dbch\af31505\loch\f39 Applications for use with any sensor other than }{ +\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid11871500\charrsid1909739 \hich\af39\dbch\af31505\loch\f39 the }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415\charrsid1909739 \hich\af39\dbch\af31505\loch\f39 Kinect }{\rtlch\fcs1 \af39\afs20 +\ltrch\fcs0 \fs20\insrsid3949644\charrsid1909739 \hich\af39\dbch\af31505\loch\f39 v2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid16457108\charrsid1909739 \hich\af39\dbch\af31505\loch\f39 Sensor}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid14225958 \hich\af39\dbch\af31505\loch\f39 or }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415\charrsid1909739 \hich\af39\dbch\af31505\loch\f39 its associated drivers and runtime software;}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \fi-360\li720\ri0\sb200\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin720\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 use the software or any Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 \hich\af39\dbch\af31505\loch\f39 v2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 Applications in any High Risk Use; }{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \fi-360\li720\ri0\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin720\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 work \hich\af39\dbch\af31505\loch\f39 around any technical limitations in the software; +\par }{\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +reverse engineer, decompile, or disassemble any part of the software not provided in source code form, except and only to the extent that applicable law expressly permits, despite this limitation; +\par }\pard \ltrpar\ql \fi-360\li720\ri0\sb200\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin720\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 publi\hich\af39\dbch\af31505\loch\f39 sh the software for others to copy; +\par }\pard \ltrpar\ql \fi-360\li720\ri0\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin720\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 rent, lease, or lend the software; +\par }\pard \ltrpar\ql \fi-360\li720\ri0\sb200\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin720\itap0 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 transfer the software or this agreement to any third party; or +\par }\pard \ltrpar\ql \fi-360\li720\ri0\sb200\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin720\itap0\pararsid11829074 {\rtlch\fcs1 \af3\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid8745415 \loch\af3\dbch\af31505\hich\f3 \'b7\tab }{\rtlch\fcs1 \af39\afs20 +\ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 use the software for commercial software hosting services.}{\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\insrsid8745415 +\par }\pard \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 4. REGULATORY COMPLIANCE. }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 You agree that your developmen\hich\af39\dbch\af31505\loch\f39 t, marketing, sales, and distribution of Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid3949644 +\hich\af39\dbch\af31505\loch\f39 v2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 + Applications shall be in compliance with all applicable legal requirements, including compliance with the medical device regulatory requirements of the U.S. Federal Food, Drug, and Cosmetic Act and any ass\hich\af39\dbch\af31505\loch\f39 +ociated requirements, or similar laws, regulations, or policies in other countries or territories.\~ + To the extent required by law, you are solely responsible for obtaining or filing any approval, clearance, registration, permit, or other regulatory author\hich\af39\dbch\af31505\loch\f39 i\hich\af39\dbch\af31505\loch\f39 +zation and shall comply with the requirements of such authorization. +\par }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\cf19\insrsid8745415 +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 5. ACKNOWLEDGEMENT AND WAIVER. }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +You acknowledge the software may allow you to control the Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid16457108 \hich\af39\dbch\af31505\loch\f39 v2 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +Sensor, which are mechanical hardware devices that include motors to move the device, \hich\af39\dbch\af31505\loch\f39 +a fan to cool it, and other mechanical components. Depending on how you elect to use the software, you could harm persons or damage or destroy the Kinect}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid2042763 \hich\af39\dbch\af31505\loch\f39 v2}{ +\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Sensor, products incorporating the Kinect}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid2042763 \hich\af39\dbch\af31505\loch\f39 v2}{\rtlch\fcs1 \af39\afs20 +\ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Sensor, or other property. In using the software, you must take steps to design and test your Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid2042763 +\hich\af39\dbch\af31505\loch\f39 v2 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +Applications to ensure that your applications do not present unreasonable risks of personal injury or death, property damage, or other losses. Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid2042763 \hich\af39\dbch\af31505\loch\f39 v +\hich\af39\dbch\af31505\loch\f39 2 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +Sensors utilize complex hardware and software technology that may not always function as intended. You must design your application so that any failure of a Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid2042763 +\hich\af39\dbch\af31505\loch\f39 v2 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Sensor and/or the software does not cause personal injury or death, property damage, or\hich\af39\dbch\af31505\loch\f39 + other losses. If you choose to use the software, you assume all risk that your use of the Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid2042763 \hich\af39\dbch\af31505\loch\f39 v2 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 Sensors and/or the software causes any harm or loss, including to the end users of your Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid2042763 \hich\af39\dbch\af31505\loch\f39 v2}{\rtlch\fcs1 \af39\afs20 +\ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Applications, and you agree to waive all claims against M\hich\af39\dbch\af31505\loch\f39 +icrosoft and its affiliates related to such use (including but not limited to any claim that a Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid2042763 \hich\af39\dbch\af31505\loch\f39 v2 }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Sensor or the software is defective) and to hold Microsoft and its affiliates harmless from such claims. +\par }\pard \ltrpar\ql \li0\ri0\sb200\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 6. INDEMNIFICATION. }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 You agree to indemnify, \hich\af39\dbch\af31505\loch\f39 defend, and hold harmless Microsoft and its affiliates from any claims, including attorneys\hich\f39 \rquote \loch\f39 + fees, related to the distribution or use of your Kinect }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid2042763 \hich\af39\dbch\af31505\loch\f39 v2}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 + Applications. +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 7. BACKUP COPY.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 + You may make one backup copy of the software. You may use it only to reins\hich\af39\dbch\af31505\loch\f39 tall the software. +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 8. DOCUMENTATION.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 Any person }{\rtlch\fcs1 \af39\afs20 +\ltrch\fcs0 \fs20\insrsid1909739 \hich\af39\dbch\af31505\loch\f39 who }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 +has valid access to your computer or internal network may copy and use the documentation for your internal, reference purposes. +\par }\pard \ltrpar\ql \fi-360\li360\ri0\sb200\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin360\itap0 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 9. SUPPORT SERVICES.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 \hich\f39 Because this software is \'93\loch\f39 \hich\f39 as is,\'94\loch\f39 we may not provide\hich\af39\dbch\af31505\loch\f39 support services for it. +\par }\pard \ltrpar\ql \fi-360\li360\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin360\itap0 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 10.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\hich\af39\dbch\af31505\loch\f39 }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 EXPORT RESTRICTIONS.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 + The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrict\hich\af39\dbch\af31505\loch\f39 +ions on destinations, end users, and end use. For additional information, see }{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\ul\cf2\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 www.microsoft.com/exporting}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 .}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\ul\insrsid8745415 +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 11. ENTIRE AGREEMENT.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 + This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the enti\hich\af39\dbch\af31505\loch\f39 re agreement for the software and support services. +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 12. APPLICABLE LAW. +\par }\pard \ltrpar\ql \li360\ri0\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin360\itap0 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 a. United States.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 + If you acquired the software in the United States, Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other +\hich\af39\dbch\af31505\loch\f39 c\hich\af39\dbch\af31505\loch\f39 laims, including claims under state consumer protection laws, unfair competition laws, and in tort. +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 b. Outside the United States.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 + If you acquired the software in any other country, the laws of that country apply. +\par }\pard \ltrpar\ql \fi-360\li360\ri0\sb200\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin360\itap0 {\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 13. LEGAL EFFECT.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 +\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 This agreement describes \hich\af39\dbch\af31505\loch\f39 +certain legal rights. You may have other rights under the laws of your state, province or country. This agreement does not change your rights under the laws of your state, province or country if the laws of your state, province or country do not permit it +\hich\af39\dbch\af31505\loch\f39 \hich\af39\dbch\af31505\loch\f39 to do so. +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 \hich\f39 14. DISCLAIMER OF WARRANTY. The software is licensed \'93\loch\f39 \hich\f39 as-is.\'94\loch\f39 + You bear all risk of using it. Microsoft gives no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws that this agreement cannot\hich\af39\dbch\af31505\loch\f39 + change. To the extent permitted under your local laws, Microsoft excludes the implied warranties of merchantability, fitness for a particular purpose, and non-infringement.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 15. LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. You can recover from Micr\hich\af39\dbch\af31505\loch\f39 +osoft and its suppliers only direct damages up to U.S. $5.00. You cannot recover any other damages, including consequential, lost profits, special, indirect or incidental damages.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 +\par }\pard \ltrpar\ql \li360\ri0\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin360\itap0 {\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 This limitation applies to +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 a.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 anything related to the software, services, co +\hich\af39\dbch\af31505\loch\f39 ntent (including code) on third party Internet sites, or third party programs; and +\par }{\rtlch\fcs1 \ab\af39\afs20 \ltrch\fcs0 \b\fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 b.}{\rtlch\fcs1 \af39\afs20 \ltrch\fcs0 \fs20\insrsid8745415 \hich\af39\dbch\af31505\loch\f39 + claims for breach of contract; breach of warranty, guarantee, or condition; strict liability, negligence, or other tort, to the extent permitted by applicable law. +\par \hich\af39\dbch\af31505\loch\f39 It als\hich\af39\dbch\af31505\loch\f39 +o applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your state, province or country may not allow the exclusion or limitation of incidental, consequenti +\hich\af39\dbch\af31505\loch\f39 a\hich\af39\dbch\af31505\loch\f39 l, or other damages. +\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a +9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad +5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 +b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 +0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 +a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f +c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 +0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 +a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 +6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b +4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100aa5225dfc60600008b1a0000160000007468656d652f7468656d652f +7468656d65312e786d6cec595d8bdb46147d2ff43f08bd3bfe92fcb1c41b6cd9ceb6d94d42eca4e4716c8fadc98e344633de8d0981923c160aa569e943037deb +43691b48a02fe9afd936a54d217fa17746b63c638fbb9b2585a5640d8b343af7ce997bafce1d4997afdc8fa87384134e58dc708b970aae83e3211b9178d2706f +f7bbb99aeb7081e211a22cc60d778eb97b65f7c30f2ea31d11e2083b601ff31dd4704321a63bf93c1fc230e297d814c7706dcc920809384d26f951828ec16f44 +f3a542a1928f10895d274611b8bd311e932176fad2a5bbbb74dea1701a0b2e078634e949d7d8b050d8d1615122f89c0734718e106db830cf881df7f17de13a14 +7101171a6e41fdb9f9ddcb79b4b330a2628bad66d7557f0bbb85c1e8b0a4e64c26836c52cff3bd4a33f3af00546ce23ad54ea553c9fc29001a0e61a52917dda7 +dfaab7dafe02ab81d2438bef76b55d2e1a78cd7f798373d3973f03af40a97f6f03dfed06104503af4029dedfc07b5eb51478065e81527c65035f2d34db5ed5c0 +2b5048497cb8812ef89572b05c6d061933ba6785d77daf5b2d2d9caf50500d5975c929c62c16db6a2d42f758d2058004522448ec88f9148fd110aa3840940c12 +e2ec93490885374531e3305c2815ba8532fc973f4f1da988a01d8c346bc90b98f08d21c9c7e1c3844c45c3fd18bcba1ae4cdcb1fdfbc7cee9c3c7a71f2e89793 +c78f4f1efd9c3a32acf6503cd1ad5e7fffc5df4f3f75fe7afeddeb275fd9f15cc7fffed367bffdfaa51d082b5d85e0d5d7cffe78f1ecd5379ffff9c3130bbc99 +a0810eef930873e73a3e766eb10816a6426032c783e4ed2cfa2122ba45339e701423398bc57f478406fafa1c5164c1b5b019c13b09488c0d787576cf20dc0b93 +9920168fd7c2c8001e30465b2cb146e19a9c4b0b737f164fec9327331d770ba123dbdc018a8dfc766653d05662731984d8a07993a258a0098eb170e4357688b1 +6575770931e27a408609e36c2c9cbbc46921620d499f0c8c6a5a19ed9108f232b711847c1bb139b8e3b418b5adba8d8f4c24dc15885ac8f73135c27815cd048a +6c2efb28a27ac0f791086d247bf364a8e33a5c40a6279832a733c29cdb6c6e24b05e2de9d7405eec693fa0f3c84426821cda7cee23c674649b1d06218aa6366c +8fc4a18efd881f428922e7261336f80133ef10790e7940f1d674df21d848f7e96a701b9455a7b42a107965965872791533a37e7b733a4658490d08bfa1e71189 +4f15f73559f7ff5b5907217df5ed53cbaa2eaaa0371362bda3f6d6647c1b6e5dbc03968cc8c5d7ee369ac53731dc2e9b0decbd74bf976ef77f2fdddbeee7772f +d82b8d06f9965bc574abae36eed1d67dfb9850da13738af7b9daba73e84ca32e0c4a3bf5cc8ab3e7b8690887f24e86090cdc2441cac64998f88488b017a229ec +ef8bae7432e10bd713ee4c19876dbf1ab6fa96783a8b0ed8287d5c2d16e5a3692a1e1c89d578c1cfc6e15143a4e84a75f50896b9576c27ea51794940dabe0d09 +6d329344d942a2ba1c9441520fe610340b09b5b277c2a26e615193ee97a9da6001d4b2acc0d6c9810d57c3f53d30012378a242148f649ed2542fb3ab92f92e33 +bd2d984605c03e625901ab4cd725d7adcb93ab4b4bed0c99364868e566925091513d8c87688417d52947cf42e36d735d5fa5d4a02743a1e683d25ad1a8d6fe8d +c579730d76ebda40635d2968ec1c37dc4ad9879219a269c31dc3633f1c4653a81d2eb7bc884ee0ddd95024e90d7f1e6599265cb4110fd3802bd149d520220227 +0e2551c395cbcfd24063a5218a5bb104827061c9d541562e1a3948ba99643c1ee3a1d0d3ae8dc848a7a7a0f0a95658af2af3f383a5259b41ba7be1e8d819d059 +720b4189f9d5a20ce0887078fb534ca33922f03a3313b255fdad35a685eceaef13550da5e3884e43b4e828ba98a77025e5191d7596c5403b5bac1902aa8564d1 +080713d960f5a01add34eb1a2987ad5df7742319394d34573dd35015d935ed2a66ccb06c036bb13c5f93d7582d430c9aa677f854bad725b7bed4bab57d42d625 +20e059fc2c5df70c0d41a3b69acca026196fcab0d4ecc5a8d93b960b3c85da599a84a6fa95a5dbb5b8653dc23a1d0c9eabf383dd7ad5c2d078b9af549156df3d +f44f136c700fc4a30d2f81675470954af8f09020d810f5d49e24950db845ee8bc5ad0147ce2c210df741c16f7a41c90f72859adfc97965af90abf9cd72aee9fb +e562c72f16daadd243682c228c8a7efacda50bafa2e87cf1e5458d6f7c7d89966fdb2e0d599467eaeb4a5e11575f5f8aa5ed5f5f1c02a2f3a052ead6cbf55625 +572f37bb39afddaae5ea41a5956b57826abbdb0efc5abdfbd0758e14d86b9603afd2a9e52ac520c8799582a45fabe7aa5ea9d4f4aacd5ac76b3e5c6c6360e5a9 +7c2c6201e155bc76ff010000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f +7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be +9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980 +ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5b +babac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c0200001300000000000000000000000000000000005b436f6e74656e +745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000000000300100005f72656c732f +2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000000000190200007468656d652f7468656d652f74 +68656d654d616e616765722e786d6c504b01022d0014000600080000002100aa5225dfc60600008b1a00001600000000000000000000000000d6020000746865 +6d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b0100002700000000000000000000000000d00900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000cb0a00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax371\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdlocked0 heading 1;\lsdqformat1 \lsdlocked0 heading 2; +\lsdqformat1 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink; +\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web); +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Table;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Theme;\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List; +\lsdpriority62 \lsdlocked0 Light Grid;\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1; +\lsdpriority68 \lsdlocked0 Medium Grid 2;\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid; +\lsdpriority60 \lsdlocked0 Light Shading Accent 1;\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1; +\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote; +\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1; +\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2; +\lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2; +\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3; +\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; +\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; +\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; +\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; +\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; +\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; +\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; +\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; +\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; +\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; +\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;}}{\*\datastore 010500000200000018000000 +4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000f062 +dc4024c2cf01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}}
\ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index b2bbb12f..3e6657df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,25 +23,33 @@ set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) -set(CMAKE_C_LINKER_PREFERENCE CXX) -set(CMAKE_CXX_LINKER_PREFERENCE CXX) -set(CMAKE_C_LINKER_PREFERENCE_PROPAGATES ON) +set(CMAKE_C_LINKER_PREFERENCE_PROPAGATES OFF) set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES ON) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") -include(opentrack-mrproper) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/") +include(opentrack-policy NO_POLICY_SCOPE) project(opentrack) -cmake_minimum_required(VERSION 3.10 FATAL_ERROR) -include(opentrack-policy NO_POLICY_SCOPE) +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) + +if(POLICY CMP0083) + cmake_policy(SET CMP0083 NEW) + include(CheckPIESupported) + check_pie_supported() +endif() + +include(CMakeDetermineCCompiler) +include(CMakeDetermineCXXCompiler) +include(CMakeParseArguments) + include(opentrack-load-user-settings) +include(opentrack-mrproper) set_property(GLOBAL PROPERTY opentrack-all-modules "") set_property(GLOBAL PROPERTY opentrack-all-source-dirs "") set(opentrack_all-translations "nl_NL;ru_RU;stub;zh_CN") -include(opentrack-word-size) include(opentrack-hier) include(opentrack-platform) include(opentrack-i18n) @@ -1,6 +1,8 @@ ## Intro -opentrack project home at <<http://github.com/opentrack/opentrack>>. +[<img src="https://ci.appveyor.com/api/projects/status/n0j9h38jnif5qbe9/branch/unstable?svg=true"/>](https://ci.appveyor.com/project/sthalik/opentrack/branch/unstable) + +opentrack project home is located at <<http://github.com/opentrack/opentrack>>. For the latest **downloads** visit <<https://github.com/opentrack/opentrack/releases>> Download an `.exe` installer or a `.7z` archive. Currently installers and portable versions for Windows are available for each release. It supports [USB stick truly "portable" installations](https://github.com/opentrack/opentrack/wiki/portable-mode-for-USB-sticks) diff --git a/api/plugin-api.cpp b/api/plugin-api.cpp index 5a485d62..b2329f4b 100644 --- a/api/plugin-api.cpp +++ b/api/plugin-api.cpp @@ -2,21 +2,9 @@ #include <utility> -using namespace plugin_api::detail; - -// these exist so that vtable is emitted in a single compilation unit, not all of them. - -Metadata_::~Metadata_() = default; -IFilter::~IFilter() = default; -IProtocol::~IProtocol() = default; -ITracker::~ITracker() = default; -IExtension::~IExtension() = default; - -void ITrackerDialog::register_tracker(ITracker*) {} -void ITrackerDialog::unregister_tracker() {} +namespace plugin_api::detail { BaseDialog::BaseDialog() = default; - void BaseDialog::closeEvent(QCloseEvent*) { if (isVisible()) @@ -26,35 +14,40 @@ void BaseDialog::closeEvent(QCloseEvent*) } } -bool ITracker::center() { return false; } - -module_status ITracker::status_ok() +void BaseDialog::done(int) { - return module_status(); + if (isVisible()) + { + hide(); + close(); + } } -module_status ITracker::error(const QString& error) -{ - return module_status(error); -} +} // ns plugin_api::detail + +// these exist so that vtable is emitted in a single compilation unit, not all of them. Metadata_::Metadata_() = default; +Metadata_::~Metadata_() = default; +Metadata::Metadata() = default; +Metadata::~Metadata() = default; + IFilter::IFilter() = default; +IFilter::~IFilter() = default; IFilterDialog::IFilterDialog() = default; +IFilterDialog::~IFilterDialog() = default; IProtocol::IProtocol() = default; +IProtocol::~IProtocol() = default; IProtocolDialog::IProtocolDialog() = default; +IProtocolDialog::~IProtocolDialog() = default; ITracker::ITracker() = default; +ITracker::~ITracker() = default; +bool ITracker::center() { return false; } ITrackerDialog::ITrackerDialog() = default; - -void BaseDialog::done(int) -{ - if (isVisible()) - { - hide(); - close(); - } -} - +ITrackerDialog::~ITrackerDialog() = default; +void ITrackerDialog::register_tracker(ITracker*) {} +void ITrackerDialog::unregister_tracker() {} +IExtension::~IExtension() = default; IExtensionDialog::~IExtensionDialog() = default; bool module_status::is_ok() const @@ -62,8 +55,9 @@ bool module_status::is_ok() const return error.isNull(); } -module_status::module_status(QString error) : error(std::move(error)) {} - +module_status_mixin::~module_status_mixin() = default; +module_status::module_status(const QString& error) : error(error) {} +module_status::module_status() = default; module_status module_status_mixin::status_ok() { return {}; } module_status module_status_mixin::error(const QString& error) @@ -71,6 +65,12 @@ module_status module_status_mixin::error(const QString& error) return module_status(error.isEmpty() ? "Unknown error" : error); } +module_status ITracker::status_ok() +{ + return module_status(); +} -Metadata::Metadata() = default; -Metadata::~Metadata() = default; +module_status ITracker::error(const QString& error) +{ + return module_status(error); +} diff --git a/api/plugin-api.hpp b/api/plugin-api.hpp index 52776986..3fd605b2 100644 --- a/api/plugin-api.hpp +++ b/api/plugin-api.hpp @@ -21,10 +21,15 @@ using Pose = Mat<double, 6, 1>; -enum Axis { - TX, TY, TZ, Yaw, Pitch, Roll, - +enum Axis : int +{ NonAxis = -1, + TX = 0, TY = 1, TZ = 2, + + Yaw = 3, Pitch = 4, Roll = 5, + Axis_MIN = TX, Axis_MAX = 5, + + Axis_COUNT = 6, }; namespace plugin_api::detail { @@ -84,7 +89,7 @@ class OTR_API_EXPORT Metadata : public TR, public Metadata_ public: Metadata(); - ~Metadata(); + ~Metadata() override; }; struct OTR_API_EXPORT module_status final @@ -92,7 +97,8 @@ struct OTR_API_EXPORT module_status final QString error; bool is_ok() const; - module_status(QString error = {}); + module_status(); + explicit module_status(const QString& error); }; /* @@ -104,6 +110,7 @@ struct OTR_API_EXPORT module_status_mixin static module_status error(const QString& error); // return error message on init failure virtual module_status initialize() = 0; // where to return from + virtual ~module_status_mixin(); }; // implement this in filters @@ -114,7 +121,7 @@ struct OTR_API_EXPORT IFilter : module_status_mixin IFilter(); // optional destructor - virtual ~IFilter(); + ~IFilter() override; // perform filtering step. // you have to take care of dt on your own, try "opentrack-compat/timer.hpp" virtual void filter(const double *input, double *output) = 0; @@ -125,6 +132,7 @@ struct OTR_API_EXPORT IFilter : module_status_mixin struct OTR_API_EXPORT IFilterDialog : public plugin_api::detail::BaseDialog { IFilterDialog(); + ~IFilterDialog() override; // optional destructor //~IFilterDialog() override; @@ -142,12 +150,11 @@ struct OTR_API_EXPORT IFilterDialog : public plugin_api::detail::BaseDialog struct OTR_API_EXPORT IProtocol : module_status_mixin { IProtocol(); + ~IProtocol() override; IProtocol(const IProtocol&) = delete; IProtocol& operator=(const IProtocol&) = delete; - // optional destructor - virtual ~IProtocol(); // called 250 times a second with XYZ yaw pitch roll pose // try not to perform intense computation here. use a thread. virtual void pose(const double* headpose) = 0; @@ -165,6 +172,7 @@ struct OTR_API_EXPORT IProtocolDialog : public plugin_api::detail::BaseDialog virtual void unregister_protocol() = 0; IProtocolDialog(); + ~IProtocolDialog() override; }; // call once with your chosen class names in the plugin @@ -203,6 +211,7 @@ struct OTR_API_EXPORT ITrackerDialog : public plugin_api::detail::BaseDialog virtual void unregister_tracker(); ITrackerDialog(); + ~ITrackerDialog() override; }; // call once with your chosen class names in the plugin @@ -231,7 +240,7 @@ struct OTR_API_EXPORT IExtension : module_status_mixin }; IExtension() = default; - virtual ~IExtension(); + ~IExtension() override; virtual event_mask hook_types() = 0; diff --git a/api/plugin-support.hpp b/api/plugin-support.hpp index 5062a688..b9400e88 100644 --- a/api/plugin-support.hpp +++ b/api/plugin-support.hpp @@ -8,6 +8,7 @@ #pragma once #include "plugin-api.hpp" +#include "compat/library-path.hpp" #include <memory> #include <algorithm> @@ -21,69 +22,64 @@ #include <QList> #include <QIcon> -#if defined(__APPLE__) -# define OPENTRACK_SOLIB_EXT "dylib" -#elif defined(_WIN32) -# define OPENTRACK_SOLIB_EXT "dll" -#else -# define OPENTRACK_SOLIB_EXT "so" -#endif - -#define OPENTRACK_SOLIB_PREFIX "" - -extern "C" typedef void* (*OPENTRACK_CTOR_FUNPTR)(void); -extern "C" typedef Metadata_* (*OPENTRACK_METADATA_FUNPTR)(void); +extern "C" { + using module_ctor_t = void* (*)(void); + using module_metadata_t = Metadata_* (*)(void); +} struct dylib final { enum Type : unsigned { - Filter = 0xdeadbabeu, - Tracker = 0xcafebeefu, - Protocol = 0xdeadf00du, - Extension = 0xdeadf001u, - Invalid = 0xcafebabeu, + Filter = 0xdeadbabe, + Tracker = 0xcafebeef, + Protocol = 0xdeadf00d, + Extension = 0xcafebabe, + Invalid = (unsigned)-1, }; dylib(const QString& filename_, Type t) : - type(Invalid), full_filename(filename_), - module_name(trim_filename(filename_)), - Dialog(nullptr), - Constructor(nullptr), - Meta(nullptr) + module_name(trim_filename(filename_)) { // otherwise dlopen opens the calling executable - if (filename_.size() == 0 || module_name.size() == 0) + if (filename_.isEmpty() || module_name.isEmpty()) return; handle.setFileName(filename_); handle.setLoadHints(QLibrary::DeepBindHint | QLibrary::PreventUnloadHint | QLibrary::ResolveAllSymbolsHint); +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wcomma" +#endif + if (check(!handle.load())) return; - if (check((Dialog = (OPENTRACK_CTOR_FUNPTR) handle.resolve("GetDialog"), !Dialog))) + if (check((Dialog = (module_ctor_t) handle.resolve("GetDialog"), !Dialog))) return; - if (check((Constructor = (OPENTRACK_CTOR_FUNPTR) handle.resolve("GetConstructor"), !Constructor))) + if (check((Constructor = (module_ctor_t) handle.resolve("GetConstructor"), !Constructor))) return; - if (check((Meta = (OPENTRACK_METADATA_FUNPTR) handle.resolve("GetMetadata"), !Meta))) + if (check((Meta = (module_metadata_t) handle.resolve("GetMetadata"), !Meta))) return; - auto m = std::unique_ptr<Metadata_>(Meta()); + std::unique_ptr<Metadata_> m{Meta()}; icon = m->icon(); name = m->name(); type = t; - } - ~dylib() - { - // QLibrary refcounts the .dll's so don't forcefully unload +#ifdef __clang__ +# pragma clang diagnostic pop +#endif } + // QLibrary refcounts the .dll's so don't forcefully unload + ~dylib() = default; + static QList<std::shared_ptr<dylib>> enum_libraries(const QString& library_path) { QDir module_directory(library_path); @@ -91,14 +87,14 @@ struct dylib final using str = QLatin1String; - static const struct filter_ { + const struct filter_ { Type type; QLatin1String glob; } filters[] = { - { Filter, str(OPENTRACK_SOLIB_PREFIX "opentrack-filter-*." OPENTRACK_SOLIB_EXT), }, - { Tracker, str(OPENTRACK_SOLIB_PREFIX "opentrack-tracker-*." OPENTRACK_SOLIB_EXT), }, - { Protocol, str(OPENTRACK_SOLIB_PREFIX "opentrack-proto-*." OPENTRACK_SOLIB_EXT), }, - { Extension, str(OPENTRACK_SOLIB_PREFIX "opentrack-ext-*." OPENTRACK_SOLIB_EXT), }, + { Filter, str(OPENTRACK_LIBRARY_PREFIX "opentrack-filter-*." OPENTRACK_LIBRARY_EXTENSION), }, + { Tracker, str(OPENTRACK_LIBRARY_PREFIX "opentrack-tracker-*." OPENTRACK_LIBRARY_EXTENSION), }, + { Protocol, str(OPENTRACK_LIBRARY_PREFIX "opentrack-proto-*." OPENTRACK_LIBRARY_EXTENSION), }, + { Extension, str(OPENTRACK_LIBRARY_PREFIX "opentrack-ext-*." OPENTRACK_LIBRARY_EXTENSION), }, }; for (const filter_& filter : filters) @@ -127,16 +123,16 @@ struct dylib final return ret; } - Type type; + Type type{Invalid}; QString full_filename; QString module_name; QIcon icon; QString name; - OPENTRACK_CTOR_FUNPTR Dialog; - OPENTRACK_CTOR_FUNPTR Constructor; - OPENTRACK_METADATA_FUNPTR Meta; + module_ctor_t Dialog{nullptr}; + module_ctor_t Constructor{nullptr}; + module_metadata_t Meta{nullptr}; private: QLibrary handle; @@ -150,21 +146,21 @@ private: { in = in.mid(idx + 1); - if (in.startsWith(OPENTRACK_SOLIB_PREFIX) && - in.endsWith("." OPENTRACK_SOLIB_EXT)) + if (in.startsWith(OPENTRACK_LIBRARY_PREFIX) && + in.endsWith("." OPENTRACK_LIBRARY_EXTENSION)) { - constexpr unsigned pfx_len = sizeof(OPENTRACK_SOLIB_PREFIX) - 1; - constexpr unsigned rst_len = sizeof("." OPENTRACK_SOLIB_EXT) - 1; + constexpr unsigned pfx_len = sizeof(OPENTRACK_LIBRARY_PREFIX) - 1; + constexpr unsigned rst_len = sizeof("." OPENTRACK_LIBRARY_EXTENSION) - 1; in = in.mid(pfx_len); in = in.left(in.size() - rst_len); - static constexpr const char* const names[] = + const char* const names[] = { - "opentrack-tracker-", - "opentrack-proto-", - "opentrack-filter-", - "opentrack-ext-", + OPENTRACK_LIBRARY_PREFIX "opentrack-tracker-", + OPENTRACK_LIBRARY_PREFIX "opentrack-proto-", + OPENTRACK_LIBRARY_PREFIX "opentrack-filter-", + OPENTRACK_LIBRARY_PREFIX "opentrack-ext-", }; for (auto name : names) @@ -174,7 +170,7 @@ private: } } } - return QString(); + return {""}; } bool check(bool fail) @@ -213,6 +209,7 @@ struct Modules final dylib_list& trackers() { return tracker_modules; } dylib_list& protocols() { return protocol_modules; } dylib_list& extensions() { return extension_modules; } + private: dylib_list module_list; dylib_list filter_modules; @@ -229,7 +226,7 @@ private: dylib_list filter(dylib::Type t) { QList<std::shared_ptr<dylib>> ret; - for (auto x : module_list) + for (const auto& x : module_list) if (x->type == t) ret.push_back(x); @@ -238,10 +235,10 @@ private: }; template<typename t> -static inline std::shared_ptr<t> make_dylib_instance(const std::shared_ptr<dylib>& lib) +std::shared_ptr<t> make_dylib_instance(const std::shared_ptr<dylib>& lib) { std::shared_ptr<t> ret; if (lib != nullptr && lib->Constructor) - ret = std::shared_ptr<t>(reinterpret_cast<t*>(reinterpret_cast<OPENTRACK_CTOR_FUNPTR>(lib->Constructor)())); + ret = std::shared_ptr<t>(reinterpret_cast<t*>(reinterpret_cast<module_ctor_t>(lib->Constructor)())); return ret; } diff --git a/bin/cleye.config b/bin/cleye.config index 55a478ab..bfb37b47 100644 --- a/bin/cleye.config +++ b/bin/cleye.config @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?>
-<cleye>
- <item name="mode" value="advanced" />
+<?xml version="1.0" encoding="UTF-8"?> +<cleye> + <item name="mode" value="advanced" /> </cleye>
\ No newline at end of file diff --git a/cmake/apple.cmake b/cmake/apple.cmake index 627ef3c1..f6b83382 100644 --- a/cmake/apple.cmake +++ b/cmake/apple.cmake @@ -3,7 +3,7 @@ set(CMAKE_BUILD_TYPE_INIT RELEASE) set(CMAKE_CXX_FLAGS "-arch x86_64 -stdlib=libc++" CACHE STRING "" FORCE) set(CMAKE_C_FLAGS "-arch x86_64" CACHE STRING "" FORCE) -set(CMAKE_C_FLAGS_RELEASE "-ffast-math -O3 -flto -fvisibility=hidden -g" CACHE STRING "" FORCE) +set(CMAKE_C_FLAGS_RELEASE "-ffast-math -O3 -flto -g" CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS_RELEASE " ${CMAKE_C_FLAGS_RELEASE}" CACHE STRING "" FORCE) set(cmake-link-common "-stdlib=libc++ -arch x86_64") diff --git a/cmake/linux.cmake b/cmake/linux.cmake index 945f7e77..5513f511 100644 --- a/cmake/linux.cmake +++ b/cmake/linux.cmake @@ -1,13 +1,13 @@ set(CMAKE_BUILD_TYPE_INIT RELEASE) -set(CMAKE_CXX_FLAGS "-fPIC" CACHE STRING "" FORCE) -set(CMAKE_C_FLAGS "-fPIC" CACHE STRING "" FORCE) +set(CMAKE_CXX_FLAGS "" CACHE STRING "" FORCE) +set(CMAKE_C_FLAGS "" CACHE STRING "" FORCE) set(CMAKE_AR "gcc-ar" CACHE STRING "" FORCE) set(CMAKE_NM "gcc-nm" CACHE STRING "" FORCE) set(CMAKE_RANLIB "gcc-ranlib" CACHE STRING "" FORCE) -set(CMAKE_C_FLAGS_RELEASE "-ffast-math -O3 -flto -fuse-linker-plugin -fvisibility=hidden -fPIC" CACHE STRING "" FORCE) +set(CMAKE_C_FLAGS_RELEASE "-ffast-math -O3 -flto -fuse-linker-plugin -fPIC" CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS_RELEASE " ${CMAKE_C_FLAGS_RELEASE} " CACHE STRING "" FORCE) set(cmake-link-common "") diff --git a/cmake/mingw-w64.cmake b/cmake/mingw-w64.cmake index 1879ab3a..b543bbdd 100644 --- a/cmake/mingw-w64.cmake +++ b/cmake/mingw-w64.cmake @@ -3,14 +3,9 @@ # mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=$(pwd)/../cmake/mingw-w64.cmake # -sh 20140922 -if((NOT CMAKE_BUILD_TYPE STREQUAL "DEBUG") AND (NOT CMAKE_BUILD_TYPE STREQUAL "RELEASE")) - set(CMAKE_BUILD_TYPE "DEBUG" CACHE STRING "" FORCE) -endif() +include("${CMAKE_CURRENT_LIST_DIR}/opentrack-policy.cmake" NO_POLICY_SCOPE) -string(TOUPPER "${CMAKE_BUILD_TYPE}" __build_type) -if(NOT __build_type STREQUAL CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "${__build_type}" CACHE STRING "" FORCE) -endif() +string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE) if(NOT CMAKE_INSTALL_PREFIX) set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE STRING "" FORCE) @@ -19,29 +14,29 @@ endif() SET(CMAKE_SYSTEM_NAME Windows) SET(CMAKE_SYSTEM_VERSION 5) -# specify the cross compiler -if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - #set(p D:/cygwin64/opt/bin/) - #set(p "/mingw32/bin/") - set(p "") - set(e .exe) - set(c "${p}") -else() - set(p "") - set(c "${p}i686-w64-mingw32-") - set(e "") -endif() - -SET(CMAKE_C_COMPILER ${c}gcc${e}) -SET(CMAKE_CXX_COMPILER ${c}g++${e}) -set(CMAKE_RC_COMPILER ${c}windres${e}) -set(CMAKE_LINKER ${c}ld${e}) -set(CMAKE_AR ${c}gcc-ar${e} CACHE STRING "" FORCE) -set(CMAKE_NM ${c}gcc-nm${e} CACHE STRING "" FORCE) -set(CMAKE_RANLIB ${c}gcc-ranlib${e} CACHE STRING "" FORCE) -set(CMAKE_OBJCOPY ${c}objcopy${e} CACHE STRING "" FORCE) -set(CMAKE_OBJDUMP ${c}objdump${e} CACHE STRING "" FORCE) -set(CMAKE_STRIP ${c}strip${e} CACHE STRING "" FORCE) +## specify the cross compiler +#if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") +# #set(p D:/cygwin64/opt/bin/) +# #set(p "/mingw32/bin/") +# set(p "") +# set(e .exe) +# set(c "${p}") +#else() +# set(p "") +# set(c "${p}i686-w64-mingw32-") +# set(e "") +#endif() +# +#SET(CMAKE_C_COMPILER ${c}gcc${e}) +#SET(CMAKE_CXX_COMPILER ${c}g++${e}) +#set(CMAKE_RC_COMPILER ${c}windres${e}) +#set(CMAKE_LINKER ${c}ld${e}) +#set(CMAKE_AR ${c}gcc-ar${e} CACHE STRING "" FORCE) +#set(CMAKE_NM ${c}gcc-nm${e} CACHE STRING "" FORCE) +#set(CMAKE_RANLIB ${c}gcc-ranlib${e} CACHE STRING "" FORCE) +#set(CMAKE_OBJCOPY ${c}objcopy${e} CACHE STRING "" FORCE) +#set(CMAKE_OBJDUMP ${c}objdump${e} CACHE STRING "" FORCE) +#set(CMAKE_STRIP ${c}strip${e} CACHE STRING "" FORCE) # search for programs in the host directories @@ -51,30 +46,25 @@ SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # oldest CPU supported here is Northwood-based Pentium 4. -sh 20150811 -set(fpu "-ffast-math -mfpmath=both -mstackrealign -falign-functions=16 -falign-loops=16") -set(cpu "-O3 -march=native -frename-registers") +set(fpu "-ffast-math -mfpmath=sse -mstackrealign") +set(cpu "-O3 -msse3 -mtune=skylake") #set(lto "-fno-lto -fno-use-linker-plugin -flto-compression-level=9 -flto-partition=balanced -fno-ipa-pta -fno-lto-odr-type-merging") set(lto "") -set(sections "-ffunction-sections -fdata-sections -s") +set(sections "-ffunction-sections -fdata-sections") set(cc "") #set(cc "-fdump-statistics-details -fdump-ipa-cgraph") -set(clang-warns "") -if(CMAKE_COMPILER_IS_CLANG) - set(clang-warns "-Wweak-vtables") -endif() - set(noisy-warns "") set(suggest-final "") set(numerics "") set(missing-override "") if(WARNINGS_ENABLE) if(WARNINGS_FINAL_SUGGESTIONS) - set(suggest-final "-Wsuggest-final-types -Wsuggest-final-methods") + set(suggest-final "-Wsuggest-final-types") endif() if(WARNINGS_NUMERIC) - set(numerics "-Wdouble-promotion -Wsign-compare") + set(numerics "-Wdouble-promotion") endif() if(WARNINGS_MISSING_OVERRIDE) set(missing-override "-Wsuggest-override") @@ -86,12 +76,12 @@ set(_CXX_WARNS "") set(_C_WARNS "") if(WARNINGS_ENABLE) - set(usual-warns "-Wdelete-non-virtual-dtor -Wno-suggest-override -Wno-odr -Wno-attributes -Wcast-align") + set(usual-warns "-Wstrict-aliasing=3 -Wstrict-overflow=4 -Wdelete-non-virtual-dtor -Wno-odr -Wattributes") set(_C_WARNS "-Wall -Wextra -Wpedantic -Wcast-align") - set(_CXX_WARNS "${_C_WARNS} ${usual-warns} ${clang-warns} ${noisy-warns} ${missing-override}") + set(_CXX_WARNS "${_C_WARNS} ${usual-warns} ${noisy-warns} ${missing-override}") endif() -set(ccflags-common "-fvisibility=hidden -pipe -g3") +set(ccflags-common "-pipe -g3") set(_CXXFLAGS "${ccflags-common} ${_CXX_WARNS}") set(_CFLAGS "${ccflags-common} -std=c11 ${_C_WARNS}") set(_CFLAGS_RELEASE "${cpu} ${fpu} ${lto} ${sections} ${cc}") @@ -101,9 +91,9 @@ set(_CXXFLAGS_DEBUG "${_CFLAGS_DEBUG}") add_definitions(-DSTRSAFE_NO_DEPRECATE) -set(_LDFLAGS "-Wl,--dynamicbase,--nxcompat,--as-needed") -set(_LDFLAGS_RELEASE "-Wl,--gc-sections,--exclude-libs,ALL") -set(_LDFLAGS_DEBUG "") +#set(_LDFLAGS "-Wl,--dynamicbase,--nxcompat,--as-needed") +#set(_LDFLAGS_RELEASE "-Wl,--gc-sections,--exclude-libs,ALL") +#set(_LDFLAGS_DEBUG "") set(enable-val FALSE) if(CMAKE_PROJECT_NAME STREQUAL "opentrack") @@ -122,10 +112,8 @@ foreach(j C CXX) endforeach() endforeach() -foreach(j "" _DEBUG _RELEASE) - foreach(i MODULE EXE SHARED) - set(CMAKE_${i}_LINKER_FLAGS${j} "${_LDFLAGS${j}} ${CMAKE_${i}_LINKER_FLAGS${j}}") - endforeach() -endforeach() - - +#foreach(j "" _DEBUG _RELEASE) +# foreach(i MODULE EXE SHARED) +# set(CMAKE_${i}_LINKER_FLAGS${j} "${_LDFLAGS${j}} ${CMAKE_${i}_LINKER_FLAGS${j}}") +# endforeach() +#endforeach() diff --git a/cmake/mrproper.cmake b/cmake/mrproper.cmake index f531a6cb..1e8713ef 100644 --- a/cmake/mrproper.cmake +++ b/cmake/mrproper.cmake @@ -1,4 +1,4 @@ -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
-include(opentrack-mrproper)
-
-cleanup_build_dir()
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") +include(opentrack-mrproper) + +cleanup_build_dir() diff --git a/cmake/msvc.cmake b/cmake/msvc.cmake index 651d8075..7275badf 100644 --- a/cmake/msvc.cmake +++ b/cmake/msvc.cmake @@ -18,23 +18,18 @@ set(cc "${cc} -FS -arch:SSE2 -D_HAS_EXCEPTIONS=0") #set(cc "${cc} -Qvec-report:1") set(warns_ "") + +#C4457: declaration of 'id' hides function parameter +#C4456: declaration of 'i' hides previous local declaration +#C4263 - member function does not override any base class virtual member function +#C4264 - no override available for virtual member function from base class, function is hidden +#C4265 - class has virtual functions, but destructor is not virtual +#C4266 - no override available for virtual member function from base type, function is hidden +#C4928 - illegal copy-initialization, more than one user-defined conversion has been implicitly applied set(warns-disable 4530 4577 4789 4244 4702 4530 4244 4127 4458 4456 4251 4100 4702 4457) if(CMAKE_PROJECT_NAME STREQUAL "opentrack") include("${CMAKE_CURRENT_LIST_DIR}/opentrack-policy.cmake" NO_POLICY_SCOPE) - #C4457: declaration of 'id' hides function parameter - #C4456: declaration of 'i' hides previous local declaration - #C4263 - member function does not override any base class virtual member function - #C4264 - no override available for virtual member function from base class, function is hidden - #C4265 - class has virtual functions, but destructor is not virtual - #C4266 - no override available for virtual member function from base type, function is hidden - #C4928 - illegal copy-initialization, more than one user-defined conversion has been implicitly applied - - foreach(k CMP0020 CMP0022 CMP0058 CMP0028 CMP0042 CMP0063 CMP0053 CMP0011 CMP0054 CMP0012) - if(POLICY ${k}) - cmake_policy(SET ${k} NEW) - endif() - endforeach() # C4265: class has virtual functions, but destructor is not virtual set(warns 4265) @@ -58,7 +53,7 @@ if(CMAKE_PROJECT_NAME STREQUAL "opentrack") set(CMAKE_RC_FLAGS "/nologo /DWIN32") endif() -set(base-cflags "-MT -Zi -Zf -Zo -W4 -Zo -bigobj -cgthreads1 -diagnostics:caret ${warns_}") +set(base-cflags "-MT -Zi -Zf -Zo -W4 -bigobj -cgthreads1 -diagnostics:caret ${warns_}") #set(base-cflags "${base-cflags} -d2cgsummary") set(_CFLAGS "${base-cflags}") diff --git a/cmake/opentrack-boilerplate.cmake b/cmake/opentrack-boilerplate.cmake index 84bd3a03..683f63a4 100644 --- a/cmake/opentrack-boilerplate.cmake +++ b/cmake/opentrack-boilerplate.cmake @@ -1,47 +1,57 @@ include_guard(GLOBAL) -add_custom_target(moc COMMENT "Qt temporary files") - set(opentrack-perms-file WORLD_READ OWNER_WRITE OWNER_READ GROUP_READ) set(opentrack-perms-dir WORLD_READ WORLD_EXECUTE OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE) set(opentrack-perms-exec "${opentrack-perms-dir}") set(new-hier-path "#pragma once -#ifndef OPENTRACK_NO_QT_PATH +#ifdef QT_CORE_LIB # include <QCoreApplication> # include <QString> # include \"compat/base-path.hpp\" #endif + +#if defined __APPLE__ +# define OPENTRACK_LIBRARY_EXTENSION \"dylib\" +#elif defined _WIN32 +# define OPENTRACK_LIBRARY_EXTENSION \"dll\" +#else +# define OPENTRACK_LIBRARY_EXTENSION \"so\" +#endif + +#define OPENTRACK_LIBRARY_PREFIX \"\" #define OPENTRACK_LIBRARY_PATH \"${opentrack-hier-path}\" #define OPENTRACK_DOC_PATH \"${opentrack-hier-doc}\" #define OPENTRACK_CONTRIB_PATH \"${opentrack-hier-doc}contrib/\" #define OPENTRACK_I18N_PATH \"${opentrack-i18n-path}\" ") -include_directories("${CMAKE_BINARY_DIR}") +function(otr_write_library_paths) + set(hier-path-filename "${CMAKE_BINARY_DIR}/opentrack-library-path.hxx") + set(orig-hier-path "") + if(EXISTS "${hier-path-filename}") + file(READ ${hier-path-filename} orig-hier-path) + endif() + if(NOT (orig-hier-path STREQUAL new-hier-path)) + file(WRITE "${hier-path-filename}" "${new-hier-path}") + endif() +endfunction() -set(hier-path-filename "${CMAKE_BINARY_DIR}/__opentrack-library-path.h") -set(orig-hier-path "") -if(EXISTS "${hier-path-filename}") - file(READ ${hier-path-filename} orig-hier-path) -endif() -if(NOT (orig-hier-path STREQUAL new-hier-path)) - file(WRITE "${hier-path-filename}" "${new-hier-path}") -endif() +otr_write_library_paths() function(otr_glob_sources var) set(basedir "${CMAKE_CURRENT_SOURCE_DIR}") foreach(dir . ${ARGN}) set(dir "${basedir}/${dir}") - file(GLOB ${var}-cxx ${dir}/*.cpp) - file(GLOB ${var}-cc ${dir}/*.c) - file(GLOB ${var}-hh ${dir}/*.h ${dir}/*.hpp) - file(GLOB ${var}-res ${dir}/*.rc) + file(GLOB ${var}-cxx "${dir}/*.cpp") + file(GLOB ${var}-cc "${dir}/*.c") + file(GLOB ${var}-hh "${dir}/*.h" "${dir}/*.hpp" "${dir}/*.inc") + file(GLOB ${var}-res "${dir}/*.rc") foreach(f ${var}-res) set_source_files_properties(${f} PROPERTIES LANGUAGE RC) endforeach() - file(GLOB ${var}-ui ${dir}/*.ui) - file(GLOB ${var}-rc ${dir}/*.qrc) + file(GLOB ${var}-ui "${dir}/*.ui") + file(GLOB ${var}-rc "${dir}/*.qrc") set(${var}-all ${${var}-cc} ${${var}-cxx} ${${var}-hh} ${${var}-rc} ${${var}-res}) foreach(i ui rc res cc cxx hh all) set(${var}-${i} "${${var}-${i}}" PARENT_SCOPE) @@ -117,8 +127,6 @@ function(otr_install_pdb_current_project target) endif() endfunction() -include(CMakeParseArguments) - function(otr_module n_) message(STATUS "module ${n_}") cmake_parse_arguments(arg @@ -133,8 +141,6 @@ function(otr_module n_) endif() set(n "opentrack-${n_}") - # XXX TODO update callers to use instead of long name -sh 20180527 - set(self "${n}" PARENT_SCOPE) if(NOT arg_SUBDIRS) otr_glob_sources(${n} .) @@ -169,9 +175,11 @@ function(otr_module n_) set_property(TARGET "${n}" PROPERTY PREFIX "") endif() - set_property(SOURCE ${${n}-moc} ${${n}-uih} PROPERTY GENERATED TRUE) - add_custom_target(moc-${n} DEPENDS ${${n}-moc} ${${n}-uih} ${${n}-rc} COMMENT "") - add_dependencies(moc "moc-${n}") + if(NOT arg_NO-QT) + otr_qt2("${n}") + endif() + + set(self "${n}" PARENT_SCOPE) if(NOT arg_RELINK) set_property(TARGET ${n} PROPERTY LINK_DEPENDS_NO_SHARED TRUE) @@ -201,6 +209,18 @@ function(otr_module n_) set(arg_NO-INSTALL TRUE) endif() + otr_compat(${n}) + + if(CMAKE_COMPILER_IS_CLANGXX AND (arg_EXECUTABLE OR NOT arg_BIN)) + set(opts + weak-vtables + header-hygiene + ) + foreach(k ${opts}) + target_compile_options(${n} PRIVATE "-Wno-${k}") + endforeach() + endif() + if(NOT arg_NO-INSTALL) if(arg_BIN AND WIN32) install(TARGETS "${n}" RUNTIME DESTINATION . PERMISSIONS ${opentrack-perms-exec}) @@ -215,7 +235,6 @@ function(otr_module n_) endif() endif() - otr_compat(${n}) if(NOT arg_NO-QT) otr_i18n_for_target_directory(${n_}) endif() diff --git a/cmake/opentrack-hier.cmake b/cmake/opentrack-hier.cmake index 2b3159b4..950bfefb 100644 --- a/cmake/opentrack-hier.cmake +++ b/cmake/opentrack-hier.cmake @@ -1,56 +1,58 @@ -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
-# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
-# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
-# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
-# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
-# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-include_guard(GLOBAL)
-
-set(opentrack-install-rpath "")
-if(APPLE)
- set(opentrack-hier-pfx ".")
- set(opentrack-hier-path "/") # MUST HAVE A TRAILING BACKSLASH
- set(opentrack-hier-doc "/") # MUST HAVE A TRAILING BACKSLASH
- set(opentrack-hier-str RUNTIME DESTINATION . LIBRARY DESTINATION .)
- set(opentrack-doc-pfx "./doc")
- set(opentrack-doc-src-pfx "./source-code")
- set(opentrack-i18n-pfx "./i18n")
- set(opentrack-i18n-path "./i18n")
-elseif(WIN32)
- set(opentrack-hier-pfx "./modules")
- set(opentrack-hier-path "/modules/") # MUST HAVE A TRAILING BACKSLASH
- set(opentrack-hier-doc "/doc/") # MUST HAVE A TRAILING BACKSLASH
- set(opentrack-doc-pfx "./doc")
- set(opentrack-doc-src-pfx "./source-code")
- set(opentrack-hier-str RUNTIME DESTINATION ./modules/ LIBRARY DESTINATION ./modules/)
- set(opentrack-i18n-pfx "./i18n")
- set(opentrack-i18n-path "./i18n")
- set(opentrack-hier-debug "./debug")
-else()
- set(opentrack-hier-pfx "libexec/opentrack")
- set(opentrack-hier-path "/../libexec/opentrack/") # MUST HAVE A TRAILING BACKSLASH
- set(opentrack-hier-doc "/share/doc/opentrack/") # MUST HAVE A TRAILING BACKSLASH
- set(opentrack-doc-pfx "./share/doc/opentrack")
- set(opentrack-doc-src-pfx "./share/doc/opentrack/source-code")
- set(opentrack-install-rpath "${CMAKE_INSTALL_PREFIX}/${opentrack-hier-pfx}")
- set(opentrack-hier-str ARCHIVE DESTINATION share/opentrack LIBRARY DESTINATION ${opentrack-hier-pfx} RUNTIME DESTINATION bin)
- set(opentrack-i18n-pfx "./share/opentrack/i18n")
- set(opentrack-i18n-path "../share/opentrack/i18n")
-endif()
-
-function(otr_escape_string var str)
- string(REGEX REPLACE "([\$\\\"#])" "\\\\\\1" tmp__ "${str}")
- set(${var} "${tmp__}" PARENT_SCOPE)
-endfunction()
-
-set(opentrack-contrib-pfx "${opentrack-doc-pfx}/contrib")
-
-set(opentrack-binary-suffix "")
-if(APPLE)
- set(opentrack-binary-suffix ".bin")
-elseif(WIN32)
- set(opentrack-binary-suffix ".exe")
-endif()
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +include_guard(GLOBAL) + +set(opentrack-install-rpath "") +if(APPLE) + set(opentrack-hier-pfx ".") + set(opentrack-hier-path "/") # MUST HAVE A TRAILING BACKSLASH + set(opentrack-hier-doc "/") # MUST HAVE A TRAILING BACKSLASH + set(opentrack-hier-str RUNTIME DESTINATION . LIBRARY DESTINATION .) + set(opentrack-doc-pfx "./doc") + set(opentrack-doc-src-pfx "./source-code") + set(opentrack-i18n-pfx "./i18n") + set(opentrack-i18n-path "./i18n") +elseif(WIN32) + set(opentrack-hier-pfx "./modules") + set(opentrack-hier-path "/modules/") # MUST HAVE A TRAILING BACKSLASH + set(opentrack-hier-doc "/doc/") # MUST HAVE A TRAILING BACKSLASH + set(opentrack-doc-pfx "./doc") + set(opentrack-doc-src-pfx "./source-code") + set(opentrack-hier-str RUNTIME DESTINATION ./modules/ LIBRARY DESTINATION ./modules/) + set(opentrack-i18n-pfx "./i18n") + set(opentrack-i18n-path "./i18n") + set(opentrack-hier-debug "./debug") +else() + set(opentrack-hier-pfx "libexec/opentrack") + set(opentrack-hier-path "/../libexec/opentrack/") # MUST HAVE A TRAILING BACKSLASH + set(opentrack-hier-doc "/share/doc/opentrack/") # MUST HAVE A TRAILING BACKSLASH + set(opentrack-doc-pfx "./share/doc/opentrack") + set(opentrack-doc-src-pfx "./share/doc/opentrack/source-code") + set(opentrack-install-rpath "${CMAKE_INSTALL_PREFIX}/${opentrack-hier-pfx}") + set(opentrack-hier-str ARCHIVE DESTINATION share/opentrack LIBRARY DESTINATION ${opentrack-hier-pfx} RUNTIME DESTINATION bin) + set(opentrack-i18n-pfx "./share/opentrack/i18n") + set(opentrack-i18n-path "../share/opentrack/i18n") +endif() + +function(otr_escape_string var str) + string(REGEX REPLACE "([^_A-Za-z0-9./:-])" "\\\\\\1" str "${str}") + set(${var} "${str}" PARENT_SCOPE) +endfunction() + +set(opentrack-contrib-pfx "${opentrack-doc-pfx}/contrib") + +set(opentrack-binary-suffix "") +if(APPLE) + set(opentrack-binary-suffix ".bin") +elseif(WIN32) + set(opentrack-binary-suffix ".exe") +endif() + +set(CMAKE_INSTALL_RPATH "${opentrack-install-rpath}") diff --git a/cmake/opentrack-i18n.cmake b/cmake/opentrack-i18n.cmake index b98a469b..1d471fb2 100644 --- a/cmake/opentrack-i18n.cmake +++ b/cmake/opentrack-i18n.cmake @@ -1,69 +1,63 @@ include_guard(GLOBAL) -add_custom_target(i18n ALL) add_custom_target(i18n-lupdate) -add_custom_target(i18n-lrelease) -add_dependencies(i18n-lrelease i18n-lupdate) -add_dependencies(i18n i18n-lrelease) +set_property(TARGET i18n-lupdate PROPERTY FOLDER "i18n") +add_custom_target(i18n-lrelease DEPENDS i18n-lupdate) +set_property(TARGET i18n-lrelease PROPERTY FOLDER "i18n") +add_custom_target(i18n ALL DEPENDS i18n-lrelease) function(otr_i18n_for_target_directory n) set(k "opentrack-${n}") get_property(lupdate-binary TARGET "${Qt5_LUPDATE_EXECUTABLE}" PROPERTY IMPORTED_LOCATION) - set(target-names "") #make_directory("${CMAKE_CURRENT_BINARY_DIR}/lang") + set(ts-files "") + foreach(k ${opentrack_all-translations}) + list(APPEND ts-files "lang/${k}.ts") + set_property(GLOBAL APPEND PROPERTY "opentrack-ts-files-${k}" "${CMAKE_CURRENT_SOURCE_DIR}/lang/${k}.ts") + endforeach() + set(stamp "${CMAKE_CURRENT_BINARY_DIR}/lupdate.stamp") + foreach(i ${opentrack_all-translations}) set(t "${CMAKE_CURRENT_SOURCE_DIR}/lang/${i}.ts") - set(t2 "${i}.ts") set(input "${${k}-all}") if (NOT EXISTS "${t}") file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lang") file(READ "${CMAKE_SOURCE_DIR}/cmake/translation-stub.ts" stub) file(WRITE "${t}" "${stub}") endif() - add_custom_command(OUTPUT "${t2}" - COMMAND "${CMAKE_COMMAND}" -E make_directory "${CMAKE_CURRENT_SOURCE_DIR}/lang" - COMMAND "${CMAKE_COMMAND}" -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/lang" - COMMAND "${lupdate-binary}" - -I "${CMAKE_SOURCE_DIR}" - -silent - -recursive - -no-obsolete - -locations none - . - -ts "${t}" - COMMAND "${CMAKE_COMMAND}" -E copy "${t}" "${CMAKE_CURRENT_BINARY_DIR}/${t2}" - DEPENDS "${k}" "${t}" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMENT "Running lupdate for ${n}/${i}") - set(target-name "i18n-lang-${i}-ts-${n}") - list(APPEND target-names "${target-name}") - add_custom_target(${target-name} DEPENDS "${t2}" COMMENT "") - set_property(GLOBAL APPEND PROPERTY "opentrack-ts-files-${i}" "${CMAKE_CURRENT_BINARY_DIR}/${t2}") - set_property(GLOBAL APPEND PROPERTY "opentrack-ts-module-${n}" "${target-name}") - add_dependencies(i18n-lupdate "${target-name}") endforeach() + + add_custom_command(OUTPUT "${stamp}" + COMMAND "${lupdate-binary}" + -I "${CMAKE_SOURCE_DIR}" + -silent + -recursive + -no-obsolete + -locations none + . + -ts ${ts-files} + COMMAND "${CMAKE_COMMAND}" -E touch "${stamp}" + DEPENDS ${${k}-cc} ${${k}-hh} ${${k}-uih} ${${k}-moc} + COMMENT "Running lupdate for ${n}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + set(target-name "i18n-module-${n}") + add_custom_target(${target-name} DEPENDS "${stamp}" COMMENT "") + set_property(TARGET ${target-name} PROPERTY FOLDER "i18n") + add_dependencies(i18n-lupdate ${target-name}) endfunction() function(otr_merge_translations) otr_escape_string(i18n-pfx "${opentrack-i18n-pfx}") install(CODE "file(REMOVE_RECURSE \"\${CMAKE_INSTALL_PREFIX}/${i18n-pfx}\")") - set(all-ts-targets "") - - get_property(all-modules GLOBAL PROPERTY opentrack-all-modules) - foreach(target ${all-modules}) - get_property(ts-targets GLOBAL PROPERTY "opentrack-ts-module-${target}") - list(APPEND all-ts-targets "${ts-targets}") - endforeach() - foreach(i ${opentrack_all-translations}) get_property(ts-files GLOBAL PROPERTY "opentrack-ts-files-${i}") get_property(lrelease-binary TARGET "${Qt5_LRELEASE_EXECUTABLE}" PROPERTY IMPORTED_LOCATION) - set(qm-output "${i}.qm") + set(qm-output "${CMAKE_BINARY_DIR}/${i}.qm") # whines about duplicate messages since tracker-pt-base is static if(WIN32) @@ -80,15 +74,16 @@ function(otr_merge_translations) ${ts-files} -qm "${qm-output}" ${to-null} - DEPENDS ${all-ts-targets} + DEPENDS ${ts-files} i18n-lupdate COMMENT "Running lrelease for ${i}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") - set(target-name "i18n-qm-${i}") - add_custom_target("${target-name}" DEPENDS "${qm-output}") - add_dependencies(i18n-lrelease "${target-name}") + set(target-name i18n-qm-${i}) + add_custom_target(${target-name} DEPENDS "${qm-output}") + set_property(TARGET ${target-name} PROPERTY FOLDER "i18n") + add_dependencies(i18n-lrelease ${target-name}) - install(FILES "${CMAKE_BINARY_DIR}/${qm-output}" + install(FILES "${qm-output}" DESTINATION "${CMAKE_INSTALL_PREFIX}/${opentrack-i18n-pfx}" PERMISSIONS ${opentrack-perms-file}) endforeach() diff --git a/cmake/opentrack-load-user-settings.cmake b/cmake/opentrack-load-user-settings.cmake index a0284943..0ce6e9b0 100644 --- a/cmake/opentrack-load-user-settings.cmake +++ b/cmake/opentrack-load-user-settings.cmake @@ -20,9 +20,6 @@ else() endif() endif() -include(CMakeDetermineCCompiler) -include(CMakeDetermineCXXCompiler) - if(NOT CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME) set(__sdk_host_os "${CMAKE_HOST_SYSTEM_NAME}-cross-") else() diff --git a/cmake/opentrack-pkg-config.cmake b/cmake/opentrack-pkg-config.cmake index b12b786c..5b84cf40 100644 --- a/cmake/opentrack-pkg-config.cmake +++ b/cmake/opentrack-pkg-config.cmake @@ -2,27 +2,18 @@ include_guard(GLOBAL) include(FindPkgConfig) function(otr_pkgconfig target) - set(cflags "") - set(includes "") - set(ldflags "") - set(libs "") foreach(i ${ARGN}) set(k pkg-config_${i}) pkg_check_modules(${k} QUIET ${i}) if(${${k}_FOUND}) - set(cflags "${cflags} ${${k}_CFLAGS} ") - set(includes ${includes} ${${k}_INCLUDE_DIRS} ${${k}_INCLUDEDIR}) - set(ldflags "${ldflags} ${${k}_LDFLAGS} ") - set(libs ${libs} ${${k}_LIBRARIES}) - #message(STATUS "${target} | ${i} | ${cflags} | ${includes} | ${ldflags} | ${libs} | ") + target_compile_options(${target} PRIVATE "${${k}_CFLAGS}") + target_link_options(${target} PRIVATE ${${k}_LDFLAGS}) + target_include_directories(${target} SYSTEM PRIVATE ${${k}_INCLUDE_DIRS} ${${k}_INCLUDEDIR}) + target_link_libraries(${target} ${${k}_LIBRARIES}) else() message(FATAL_ERROR "Can't find '${i}'. Please install development files for this package.") endif() endforeach() - set_property(TARGET ${target} APPEND_STRING PROPERTY COMPILE_FLAGS "${cflags} ") - target_include_directories(${target} SYSTEM PRIVATE ${includes}) - set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS "${ldflags} ") - target_link_libraries(${target} ${libs}) endfunction() diff --git a/cmake/opentrack-platform.cmake b/cmake/opentrack-platform.cmake index 4b7cde09..99589a76 100644 --- a/cmake/opentrack-platform.cmake +++ b/cmake/opentrack-platform.cmake @@ -23,29 +23,50 @@ include_guard(GLOBAL) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "" FORCE) - if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "RELEASE" CACHE STRING "" FORCE) - endif() +if(MSVC AND MSVC_VERSION LESS "1915" AND NOT ".${CMAKE_CXX_COMPILER_ID}" STREQUAL ".Clang") + message(FATAL_ERROR "Visual Studio too old. Use Visual Studio 2017 or newer.") endif() -string(TOUPPER "${CMAKE_BUILD_TYPE}" __build_type) -set(CMAKE_BUILD_TYPE "${__build_type}" CACHE STRING "" FORCE) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT OR NOT CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "" FORCE) +endif() -if(APPLE) - if(NOT CMAKE_OSX_ARCHITECTURES) - set(CMAKE_OSX_ARCHITECTURES "x86_64") - endif() +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "RELEASE" CACHE STRING "" FORCE) endif() -if(MSVC AND MSVC_VERSION LESS "1915" AND NOT ".${CMAKE_CXX_COMPILER_ID}" STREQUAL ".Clang") - message(FATAL_ERROR "Visual Studio too old. Use Visual Studio 2017 Preview or newer.") +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # for clang + +string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE) +set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "" FORCE) + +include_directories("${CMAKE_SOURCE_DIR}") + +set(opentrack_maintainer-mode FALSE CACHE INTERNAL "Select if developing core code (not modules)") + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_DEFAULT 17) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS FALSE) + +set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +set(CMAKE_SKIP_INSTALL_RPATH FALSE) +set(CMAKE_SKIP_RPATH FALSE) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC OFF) +set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) + +set(CMAKE_C_VISIBILITY_PRESET hidden) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) + +if(NOT WIN32 AND NOT APPLE) + include(opentrack-pkg-config) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_COMPILER_IS_GNUCXX TRUE) - set(CMAKE_COMPILER_IS_CLANG TRUE) + set(CMAKE_COMPILER_IS_CLANGXX TRUE) endif() if(CMAKE_C_COMPILER_ID STREQUAL "Clang") @@ -53,30 +74,67 @@ if(CMAKE_C_COMPILER_ID STREQUAL "Clang") set(CMAKE_COMPILER_IS_CLANG TRUE) endif() -if((NOT CMAKE_COMPILER_IS_GNUCXX) EQUAL (NOT (NOT CMAKE_COMPILER_IS_GNUCC))) - # this build system has logic errors otherwise - message(FATAL_ERROR "use either use both gcc and g++ or neither") +if(APPLE AND NOT CMAKE_OSX_ARCHITECTURES) + set(CMAKE_OSX_ARCHITECTURES "x86_64") + set(opentrack-intel TRUE) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*|i[0-9]86.*|x86.*") + set(opentrack-intel TRUE) +elseif(MSVC AND CMAKE_SYSTEM_NAME STREQUAL "Windows" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "") + set(opentrack-intel TRUE) +else() + set(opentrack-intel FALSE) +endif() + +if(CMAKE_SIZEOF_VOID_P GREATER_EQUAL 8) + set(opentrack-64bit TRUE) +else() + set(opentrack-64bit FALSE) endif() IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(LINUX TRUE) endif() +if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-cxa-atexit") + + # assume binutils + foreach (i SHARED MODULE EXE) + set(CMAKE_${i}_LINKER_FLAGS "${CMAKE_${i}_LINKER_FLAGS} -Wl,--exclude-libs,ALL") + endforeach() + + if(UNIX) + foreach (i SHARED MODULE EXE) + set(CMAKE_${i}_LINKER_FLAGS "${CMAKE_${i}_LINKER_FLAGS} -Wl,-z,relro,-z,now") + endforeach() + endif() +endif() + if(WIN32) - add_definitions(-D_USE_MATH_DEFINES=1) + add_definitions(-D_USE_MATH_DEFINES=1 -DSTRSAFE_NO_DEPRECATE) +endif() + +if(MINGW) + add_definitions(-DMINGW_HAS_SECURE_API) endif() if(MSVC) - add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS -D_NO_DEBUG_HEAP) + add_definitions(-DNOMINMAX) + add_definitions(-DWIN32_LEAN_AND_MEAN) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_definitions(-D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1) + add_definitions(-D_SCL_SECURE_NO_WARNINGS) + add_definitions(-D_ITERATOR_DEBUG_LEVEL=0) add_definitions(-D_HAS_EXCEPTIONS=0) + add_definitions(-D_ENABLE_EXTENDED_ALIGNED_STORAGE) add_definitions(-D_SILENCE_CXX17_NEGATORS_DEPRECATION_WARNING) add_definitions(-D_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING) set(__stuff "-permissive- -diagnostics:caret") set(CMAKE_CXX_FLAGS "${__stuff} ${CMAKE_CXX_FLAGS}") - set(CMAKE_C_FLAGS "${__stuff} ${CMAKE_CXX_FLAGS}") + set(CMAKE_C_FLAGS "${__stuff} ${CMAKE_C_FLAGS}") if(opentrack-64bit) set(ent "-HIGHENTROPYVA") @@ -90,67 +148,11 @@ if(MSVC) endforeach() endif() -if(WIN32) - if(CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_RC_COMPILER_INIT i686-w64-mingw32-windres) - set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> --use-temp-file -O coff <DEFINES> -i <SOURCE> -o <OBJECT>") - endif() - enable_language(RC) -endif() - -if(opentrack-install-rpath) - set(CMAKE_INSTALL_RPATH "${opentrack-install-rpath}") -else() - set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}") -endif() - -set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -set(CMAKE_SKIP_INSTALL_RPATH FALSE) -set(CMAKE_SKIP_RPATH FALSE) -set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_AUTOMOC OFF) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) - -include_directories("${CMAKE_SOURCE_DIR}") - -if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) - set(_common "-fvisibility=hidden") - set(CMAKE_C_FLAGS "${_common} ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${_common} -fuse-cxa-atexit ${CMAKE_CXX_FLAGS}") -endif() - if(APPLE) set(CMAKE_MACOSX_RPATH OFF) - set(apple-frameworks "-stdlib=libc++ -framework Cocoa -framework CoreFoundation -lobjc -lz -framework Carbon") - set(CMAKE_SHARED_LINKER_FLAGS " ${apple-frameworks} ${CMAKE_SHARED_LINKER_FLAGS}") - #set(CMAKE_STATIC_LINKER_FLAGS " ${apple-frameworks} ${CMAKE_STATIC_LINKER_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS " ${apple-frameworks} ${CMAKE_EXE_LINKER_FLAGS}") - set(CMAKE_MODULE_LINKER_FLAGS " ${apple-frameworks} ${CMAKE_MODULE_LINKER_FLAGS}") - set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}") -endif() - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_DEFAULT 17) -set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -set(CMAKE_CXX_EXTENSIONS FALSE) - -foreach(k _RELEASE _DEBUG _RELWITHDEBINFO _MINSIZEREL) - set(CMAKE_C_FLAGS${k} "${CMAKE_C_FLAGS${k}} -UNDEBUG") - set(CMAKE_CXX_FLAGS${k} "${CMAKE_CXX_FLAGS${k}} -UNDEBUG") -endforeach() - -if(MINGW) - add_definitions(-DMINGW_HAS_SECURE_API) - add_definitions(-DSTRSAFE_NO_DEPRECATE) -endif() - -# assume binutils -if(UNIX AND NOT APPLE) - foreach (i SHARED MODULE EXE) - set(CMAKE_${i}_LINKER_FLAGS "-Wl,-z,relro,-z,now,--exclude-libs,ALL ${CMAKE_${i}_LINKER_FLAGS}") + set(apple-frameworks "-framework Cocoa -framework CoreFoundation -lobjc -lz -framework Carbon") + foreach (k SHARED EXE MODULE) + set(CMAKE_${k}_LINKER_FLAGS "-stdlib=libc++ ${CMAKE_${k}_LINKER_FLAGS} ${apple-frameworks}") endforeach() - include(opentrack-pkg-config) + set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}") endif() - -set(opentrack_maintainer-mode FALSE CACHE BOOL "Select if developing core code (not modules)") diff --git a/cmake/opentrack-policy.cmake b/cmake/opentrack-policy.cmake index e86cd284..209ef529 100644 --- a/cmake/opentrack-policy.cmake +++ b/cmake/opentrack-policy.cmake @@ -1,5 +1,20 @@ include_guard(GLOBAL) -foreach(k CMP0020 CMP0022 CMP0058 CMP0028 CMP0042 CMP0063 CMP0053 CMP0011 CMP0054 CMP0012) + +set(_policies + CMP0020 + CMP0022 + CMP0058 + CMP0028 + CMP0042 + CMP0063 + CMP0053 + CMP0011 + CMP0054 + CMP0012 + CMP0069 + CMP0063 +) +foreach(k ${_policies}) if(POLICY ${k}) cmake_policy(SET ${k} NEW) endif() diff --git a/cmake/opentrack-qt.cmake b/cmake/opentrack-qt.cmake index e90e3250..5a8c27a0 100644 --- a/cmake/opentrack-qt.cmake +++ b/cmake/opentrack-qt.cmake @@ -20,9 +20,12 @@ endfunction() function(otr_install_qt_libs) if(WIN32) foreach(i Qt5::Core Qt5::Gui Qt5::Network Qt5::SerialPort Qt5::Widgets) - get_property(path TARGET "${i}" PROPERTY LOCATION) - if("${path}" STREQUAL "") - message(FATAL_ERROR "${i} ${path}") + get_property(path TARGET "${i}" PROPERTY "LOCATION_${CMAKE_BUILD_TYPE}") + if(path STREQUAL "") + get_property(path TARGET "${i}" PROPERTY LOCATION) + endif() + if(path STREQUAL "") + message(FATAL_ERROR "can't find ${i}") endif() install(FILES "${path}" DESTINATION .) if(MSVC AND opentrack_install-debug-info) @@ -33,7 +36,13 @@ function(otr_install_qt_libs) endif() endforeach() - get_property(path TARGET Qt5::QWindowsIntegrationPlugin PROPERTY LOCATION) + get_property(path TARGET Qt5::QWindowsIntegrationPlugin PROPERTY "LOCATION_${CMAKE_BUILD_TYPE}") + if(path STREQUAL "") + get_property(path TARGET Qt5::QWindowsIntegrationPlugin PROPERTY LOCATION) + endif() + if(path STREQUAL "") + message(FATAL_ERROR "can't find Qt5::QWindowsIntegrationPlugin") + endif() install(FILES "${path}" DESTINATION ./platforms) if(MSVC AND opentrack_install-debug-info) otr_pdb_for_dll(pdb-path "${path}") @@ -59,8 +68,17 @@ function(otr_qt n) list(APPEND ${n}-all ${${n}-${i}}) endforeach() set(${n}-all "${${n}-all}" PARENT_SCOPE) - include_directories(SYSTEM ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS}) - add_definitions(${Qt5Core_DEFINITIONS} ${Qt5Gui_DEFINITIONS} ${Qt5Widgets_DEFINITIONS} ${Qt5Network_DEFINITIONS}) - add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT) - add_definitions(-DQT_DEPRECATED -DQT_DISABLE_DEPRECATED_BEFORE=-1) endfunction() + +function(otr_qt2 n) + target_include_directories("${n}" PRIVATE SYSTEM + ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} + ) + target_compile_definitions("${n}" PRIVATE + ${Qt5Core_DEFINITIONS} ${Qt5Gui_DEFINITIONS} ${Qt5Widgets_DEFINITIONS} ${Qt5Network_DEFINITIONS} + -DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT + -DQT_MESSAGELOGCONTEXT + ) +endfunction() + +include_directories("${CMAKE_BINARY_DIR}") diff --git a/cmake/opentrack-version.cmake b/cmake/opentrack-version.cmake index a85d7ffe..0ff516f2 100644 --- a/cmake/opentrack-version.cmake +++ b/cmake/opentrack-version.cmake @@ -6,19 +6,9 @@ if(GIT_FOUND) git_describe(OPENTRACK_COMMIT --tags --always) endif() -unset(_build_type) -if(CMAKE_BUILD_TYPE) - string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type) - if (NOT _build_type STREQUAL "DEBUG") - unset(_build_type) - else() - set(_build_type "-${_build_type}") - endif() -endif() - -file(WRITE ${CMAKE_BINARY_DIR}/opentrack-version.h "#define OPENTRACK_VERSION \"${OPENTRACK_COMMIT}${_build_type}\"") +file(WRITE ${CMAKE_BINARY_DIR}/opentrack-version.hxx "#define OPENTRACK_VERSION \"${OPENTRACK_COMMIT}\"") -set(version-string " +set(version-string "\ #ifdef __cplusplus extern \"C\" #else @@ -26,8 +16,7 @@ extern #endif const char* const opentrack_version; - -const char* const opentrack_version = \"${OPENTRACK_COMMIT}${_build_type}\"; +const char* const opentrack_version = \"${OPENTRACK_COMMIT}\"; ") set(file "${CMAKE_CURRENT_BINARY_DIR}/version.cpp") @@ -43,5 +32,5 @@ endif() add_library(opentrack-version STATIC "${file}") if(NOT MSVC) - set_property(TARGET opentrack-version APPEND_STRING PROPERTY COMPILE_FLAGS "-fno-lto ") + target_compile_options(opentrack-version PRIVATE -fno-lto) endif() diff --git a/cmake/opentrack-word-size.cmake b/cmake/opentrack-word-size.cmake deleted file mode 100644 index e539f1cd..00000000 --- a/cmake/opentrack-word-size.cmake +++ /dev/null @@ -1,6 +0,0 @@ -include_guard(GLOBAL)
-if(CMAKE_SIZEOF_VOID_P GREATER 4)
- set(opentrack-64bit TRUE)
-else()
- set(opentrack-64bit FALSE)
-endif()
diff --git a/compat/activation-context.cpp b/compat/activation-context.cpp new file mode 100644 index 00000000..8d34243d --- /dev/null +++ b/compat/activation-context.cpp @@ -0,0 +1,51 @@ +#ifdef _WIN32 + +#include "activation-context.hpp" +#include "compat/library-path.hpp" + +#include <QString> +#include <QFile> +#include <QDebug> + +#include <windows.h> + +static_assert(sizeof(std::uintptr_t) == sizeof(ULONG_PTR)); + +activation_context::activation_context(const QString& module_name, int resid) +{ + static const QString prefix = OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH + OPENTRACK_LIBRARY_PREFIX; + QString path = prefix + module_name; + QByteArray name = QFile::encodeName(path); + + ACTCTXA actx = {}; + actx.cbSize = sizeof(actx); + actx.lpResourceName = MAKEINTRESOURCEA(resid); + actx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; + actx.lpSource = name.constData(); + + handle = CreateActCtxA(&actx); + + if (handle != INVALID_HANDLE_VALUE) + { + if (!ActivateActCtx(handle, (ULONG_PTR*)&cookie)) + { + qDebug() << "win32: can't set activation context" << GetLastError(); + ReleaseActCtx(handle); + handle = INVALID_HANDLE_VALUE; + } + else + ok = true; + } else { + qDebug() << "win32: can't create activation context" << GetLastError(); + } +} + +activation_context::~activation_context() +{ + if (handle != INVALID_HANDLE_VALUE) + { + DeactivateActCtx(0, cookie); + ReleaseActCtx(handle); + } +} +#endif diff --git a/compat/activation-context.hpp b/compat/activation-context.hpp new file mode 100644 index 00000000..a3b0429e --- /dev/null +++ b/compat/activation-context.hpp @@ -0,0 +1,26 @@ +#pragma once + +#ifdef _WIN32 + +#include "export.hpp" + +#include <cstdint> +#include <QString> + +class OTR_COMPAT_EXPORT activation_context +{ +public: + explicit activation_context(const QString& module_name, int resid); + ~activation_context(); + + explicit operator bool() const { return ok; } + +private: + std::uintptr_t cookie = 0; + void* handle = (void*)-1; + bool ok = false; +}; + +#else +# error "tried to use win32-only activation context" +#endif diff --git a/compat/arch.hpp b/compat/arch.hpp new file mode 100644 index 00000000..33f8f8ca --- /dev/null +++ b/compat/arch.hpp @@ -0,0 +1,50 @@ +#pragma once + +// fix MSVC arch check macros + +// this file is too simple to fall under copyright, and +// can be copied, modified, and redistributed freely with +// no conditions. there's no warranty. -sh 20181226 + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wreserved-id-macro" +# pragma clang diagnostic ignored "-Wunused-macros" +#endif + +#if defined _MSC_VER +# if defined _M_AMD64 +# undef __x86_64__ +# define __x86_64__ 1 +# elif defined _M_IX86 +# undef __i386__ +# define __i386__ 1 +# endif + +# if defined __AVX__ || defined __x86_64__ || \ + defined _M_IX86 && _M_IX86_FP >= 2 +# undef __SSE__ +# undef __SSE2__ +# undef __SSE3__ +# define __SSE__ 1 +# define __SSE2__ 1 +# define __SSE3__ 1 // assume SSE3 in the _M_IX86_FP >= 2 case +# endif +#endif + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#if defined __SSE3__ +# define OTR_ARCH_DENORM_DAZ +# include <pmmintrin.h> +#elif defined __SSE2__ +# define OTR_ARCH_DENORM_FTZ +# include <emmintrin.h> +#endif + +#if defined __SSE2__ +# define OTR_ARCH_FPU_MASK +# include <xmmintrin.h> +#endif diff --git a/compat/base-path.cpp b/compat/base-path.cpp index 4ef3762e..b544a3d1 100644 --- a/compat/base-path.cpp +++ b/compat/base-path.cpp @@ -1,8 +1,12 @@ +#undef NDEBUG +#include <cassert> + #include "base-path.hpp" #include <QCoreApplication> const QString& application_base_path() { + assert(qApp && "logic error"); static QString const& const_path = QCoreApplication::applicationDirPath(); return const_path; } diff --git a/compat/base-path.hpp b/compat/base-path.hpp index 06e2c703..54e094f4 100644 --- a/compat/base-path.hpp +++ b/compat/base-path.hpp @@ -5,8 +5,7 @@ #include <QString> -OTR_COMPAT_EXPORT -cc_noinline +OTR_COMPAT_EXPORT never_inline const QString& application_base_path(); #define OPENTRACK_BASE_PATH (application_base_path()) diff --git a/compat/check-visible.cpp b/compat/check-visible.cpp index 96dba60f..f786ca14 100644 --- a/compat/check-visible.cpp +++ b/compat/check-visible.cpp @@ -1,67 +1,89 @@ #include "check-visible.hpp" +#include <QMutex> +#include <QWidget> +#include <QDebug> + +static QMutex lock; +static bool visible = true; + #if defined _WIN32 #include "timer.hpp" +#include "macros.hpp" -#include <QMutex> - -#include <windows.h> +static Timer timer; -constexpr int visible_timeout = 5000; +constexpr int visible_timeout = 1000; constexpr int invisible_timeout = 250; -static Timer timer; -static QMutex mtx; -static bool visible = true; +#include <windows.h> void set_is_visible(const QWidget& w, bool force) { - QMutexLocker l(&mtx); + QMutexLocker l(&lock); - if (!force && timer.elapsed_ms() < (visible ? visible_timeout : invisible_timeout)) + if (w.isHidden() || w.windowState() & Qt::WindowMinimized) + { + visible = false; return; + } - timer.start(); + HWND hwnd = (HWND)w.winId(); - const HWND id = (HWND) w.winId(); - const QPoint pt = w.mapToGlobal({ 0, 0 }); + if (!force && timer.elapsed_ms() < (visible ? visible_timeout : invisible_timeout)) + return; - const int W = w.width(), H = w.height(); + timer.start(); - const QPoint points[] = + if (RECT r; GetWindowRect(hwnd, &r)) { - pt, - pt + QPoint(W - 1, 0), - pt + QPoint(0, H - 1), - pt + QPoint(W - 1, H - 1), - pt + QPoint(W / 2, H / 2), - }; - - for (const QPoint& pt : points) + const int x = r.left, y = r.top; + const int w = r.right - x, h = r.bottom - y; + + const POINT xs[] { + { x + w - 1, y + 1 }, + { x + 1, y + h - 1 }, + { x + w - 1, y + h - 1 }, + { x + 1, y + 1 }, + { x + w/2, y + h/2 }, + }; + + visible = false; + + for (const POINT& pt : xs) + if (WindowFromPoint(pt) == hwnd) + { + + visible = true; + break; + } + } + else { - visible = WindowFromPoint({ pt.x(), pt.y() }) == id; - if (visible) - break; + eval_once(qDebug() << "check-visible: GetWindowRect failed"); + visible = true; } } -bool check_is_visible() -{ - QMutexLocker l(&mtx); - - return visible; -} - #else -void set_is_visible(const QWidget&, bool) +void set_is_visible(const QWidget& w, bool) { + QMutexLocker l(&lock); + visible = !(w.isHidden() || w.windowState() & Qt::WindowMinimized); } +#endif + bool check_is_visible() { - return true; + QMutexLocker l(&lock); + return visible; } -#endif +void force_is_visible(bool value) +{ + QMutexLocker l(&lock); + visible = value; +} diff --git a/compat/check-visible.hpp b/compat/check-visible.hpp index e24a654b..e24260ab 100644 --- a/compat/check-visible.hpp +++ b/compat/check-visible.hpp @@ -1,12 +1,15 @@ #pragma once #include "export.hpp" -#include "macros.hpp" +#include "macros1.h" -#include <QWidget> +class QWidget; -cc_noinline OTR_COMPAT_EXPORT +never_inline OTR_COMPAT_EXPORT void set_is_visible(QWidget const& w, bool force = false); -cc_noinline OTR_COMPAT_EXPORT +OTR_COMPAT_EXPORT bool check_is_visible(); + +OTR_COMPAT_EXPORT +void force_is_visible(bool value); diff --git a/compat/copyable-mutex.cpp b/compat/copyable-mutex.cpp deleted file mode 100644 index dde84c83..00000000 --- a/compat/copyable-mutex.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "copyable-mutex.hpp" - -mutex& mutex::operator=(const mutex& datum) -{ - inner.emplace(datum->isRecursive() ? QMutex::Recursive : QMutex::NonRecursive); - return *this; -} - -mutex::mutex(const mutex& datum) -{ - *this = datum; -} - -mutex::mutex(mutex::mode m) : - inner { std::in_place, static_cast<QMutex::RecursionMode>(int(m)) } -{ -} - -QMutex* mutex::operator&() const -{ - return *this; -} - -QMutex* mutex::operator->() const -{ - return *this; -} - -mutex::operator QMutex*() const -{ - return const_cast<QMutex*>(&inner.value()); -} - diff --git a/compat/copyable-mutex.hpp b/compat/copyable-mutex.hpp deleted file mode 100644 index 46c6c88c..00000000 --- a/compat/copyable-mutex.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include <optional> - -#include <QMutex> - -#include "export.hpp" - -class OTR_COMPAT_EXPORT mutex -{ - std::optional<QMutex> inner; - -public: - enum mode - { - recursive = QMutex::Recursive, - normal = QMutex::NonRecursive, - }; - - mutex& operator=(const mutex& datum); - mutex(const mutex& datum); - explicit mutex(mode m = normal); - - QMutex* operator&() const; - operator QMutex*() const; - QMutex* operator->() const; -}; diff --git a/compat/correlation-calibrator.cpp b/compat/correlation-calibrator.cpp index a58d31cd..01f3b14f 100644 --- a/compat/correlation-calibrator.cpp +++ b/compat/correlation-calibrator.cpp @@ -11,12 +11,9 @@ #define DEBUG_PRINT #ifdef DEBUG_PRINT # include <cstdio> -# include <cwchar> - using std::fwprintf; - using std::fflush; #endif -using namespace correlation_calibrator_impl; +namespace correlation_calibrator_impl { static constexpr unsigned nbuckets[6] = { @@ -40,9 +37,9 @@ static constexpr double spacing[6] = roll_spacing_in_degrees, }; -static constexpr wchar_t const* const names[6] { - L"x", L"y", L"z", - L"yaw", L"pitch", L"roll", +static constexpr char const* const names[6] { + "x", "y", "z", + "yaw", "pitch", "roll", }; bool correlation_calibrator::check_buckets(const vec6& data) @@ -53,7 +50,7 @@ bool correlation_calibrator::check_buckets(const vec6& data) for (unsigned k = 0; k < 6; k++) { const double val = clamp(data[k], min[k], max[k]); - pos[k] = (val-min[k])/spacing[k]; + pos[k] = unsigned((val-min[k])/spacing[k]); if (pos[k] >= nbuckets[k]) { @@ -129,18 +126,18 @@ mat66 correlation_calibrator::get_coefficients() const cs = cs * (1./(data.size() - 1)); #if defined DEBUG_PRINT - fwprintf(stderr, L"v:change-of h:due-to\n"); - fwprintf(stderr, L"%10s ", L""); - for (wchar_t const* k : names) - fwprintf(stderr, L"%10s", k); - fwprintf(stderr, L"\n"); + fprintf(stderr, "v:change-of h:due-to\n"); + fprintf(stderr, "%10s ", ""); + for (char const* k : names) + fprintf(stderr, "%10s", k); + fprintf(stderr, "\n"); for (unsigned i = 0; i < 6; i++) { - fwprintf(stderr, L"%10s ", names[i]); + fprintf(stderr, "%10s ", names[i]); for (unsigned k = 0; k < 6; k++) - fwprintf(stderr, L"%10.3f", cs(i, k)); - fwprintf(stderr, L"\n"); + fprintf(stderr, "%10.3f", cs(i, k)); + fprintf(stderr, "\n"); } fflush(stderr); #endif @@ -158,3 +155,5 @@ unsigned correlation_calibrator::sample_count() const { return data.size(); } + +} // ns correlation_calibrator_impl diff --git a/compat/correlation-calibrator.hpp b/compat/correlation-calibrator.hpp index 2c9e1937..44aee537 100644 --- a/compat/correlation-calibrator.hpp +++ b/compat/correlation-calibrator.hpp @@ -9,7 +9,7 @@ namespace correlation_calibrator_impl { -static constexpr inline double min[6] = { +static constexpr double min[6] = { -50, -50, 250, @@ -19,7 +19,7 @@ static constexpr inline double min[6] = { -180, }; -static constexpr inline double max[6] = { +static constexpr double max[6] = { 50, 50, 250, @@ -29,18 +29,18 @@ static constexpr inline double max[6] = { 180, }; -static constexpr inline double yaw_spacing_in_degrees = 1.5; -static constexpr inline double pitch_spacing_in_degrees = 1; -static constexpr inline double roll_spacing_in_degrees = 1; +static constexpr double yaw_spacing_in_degrees = 1.5; +static constexpr double pitch_spacing_in_degrees = 1; +static constexpr double roll_spacing_in_degrees = 1; -static constexpr inline unsigned yaw_nbuckets = 1+ 360./yaw_spacing_in_degrees; -static constexpr inline unsigned pitch_nbuckets = 1+ 360./pitch_spacing_in_degrees; -static constexpr inline unsigned roll_nbuckets = 1+ 360./roll_spacing_in_degrees; +static constexpr unsigned yaw_nbuckets = unsigned(1+ 360./yaw_spacing_in_degrees); +static constexpr unsigned pitch_nbuckets = unsigned(1+ 360./pitch_spacing_in_degrees); +static constexpr unsigned roll_nbuckets = unsigned(1+ 360./roll_spacing_in_degrees); -static constexpr inline double translation_spacing = .25; -static constexpr inline unsigned x_nbuckets = 1+ (max[0]-min[0])/translation_spacing; -static constexpr inline unsigned y_nbuckets = 1+ (max[1]-min[1])/translation_spacing; -static constexpr inline unsigned z_nbuckets = 1+ (max[2]-min[2])/translation_spacing; +static constexpr double translation_spacing = .25; +static constexpr unsigned x_nbuckets = unsigned(1+ (max[0]-min[0])/translation_spacing); +static constexpr unsigned y_nbuckets = unsigned(1+ (max[1]-min[1])/translation_spacing); +static constexpr unsigned z_nbuckets = unsigned(1+ (max[2]-min[2])/translation_spacing); using vec6 = Mat<double, 6, 1>; using mat66 = Mat<double, 6, 6>; @@ -68,7 +68,7 @@ public: mat66 get_coefficients() const; unsigned sample_count() const; - static constexpr inline unsigned min_samples = 25; + static constexpr unsigned min_samples = 25; }; } // ns correlation_calibrator_impl diff --git a/compat/enum-operators.hpp b/compat/enum-operators.hpp index dc39f2f1..188a081d 100644 --- a/compat/enum-operators.hpp +++ b/compat/enum-operators.hpp @@ -5,24 +5,41 @@ #define OTR_FLAGS_OP2(type, op) \ inline type operator op (type a, type b) \ { \ - using t__ = std::underlying_type_t<type>; \ - return static_cast<type>(t__((a)) op t__((b))); \ + using t_ = std::underlying_type_t<type>; \ + return type(t_((a)) op t_((b))); \ } // end -#define OTR_FLAGS_SHIFT(type, op) \ - type operator op (type, unsigned) = delete +#define OTR_FLAGS_DELETE_SHIFT(type, op) \ + template<typename u> \ + type operator op (type, u) = delete // end #define OTR_FLAGS_OP1(type, op) \ inline type operator op (type x) \ { \ - using t__ = std::underlying_type_t<type>; \ - return static_cast<type>(t__((x))); \ + using t_ = std::underlying_type_t<type>; \ + return type(op t_((x))); \ } // end +#define OTR_FLAGS_ASSIGN_OP(type, op) \ + inline type& operator op ## = (type& lhs, type rhs) \ + { \ + using t_ = std::underlying_type_t<decltype(rhs)>; \ + lhs = type(t_((lhs)) op t_((rhs))); \ + return lhs; \ + } //end + +#define OTR_FLAGS_DELETE_SHIFT_ASSIGN(type, op) \ + type operator op ## = (type& lhs, type rhs) = delete //end + #define DEFINE_ENUM_OPERATORS(type) \ OTR_FLAGS_OP2(type, |) \ OTR_FLAGS_OP2(type, &) \ OTR_FLAGS_OP2(type, ^) \ OTR_FLAGS_OP1(type, ~) \ - OTR_FLAGS_SHIFT(type, <<); \ - OTR_FLAGS_SHIFT(type, >>) // end + OTR_FLAGS_DELETE_SHIFT(type, <<); \ + OTR_FLAGS_DELETE_SHIFT(type, >>); \ + OTR_FLAGS_ASSIGN_OP(type, |) \ + OTR_FLAGS_ASSIGN_OP(type, &) \ + OTR_FLAGS_ASSIGN_OP(type, ^) \ + OTR_FLAGS_DELETE_SHIFT_ASSIGN(type, <<); \ + OTR_FLAGS_DELETE_SHIFT_ASSIGN(type, >>) // end diff --git a/compat/library-path.hpp b/compat/library-path.hpp index e14cf5e0..49e2c414 100644 --- a/compat/library-path.hpp +++ b/compat/library-path.hpp @@ -1,4 +1,4 @@ #pragma once // from build directory -#include "__opentrack-library-path.h" +#include "opentrack-library-path.hxx" diff --git a/compat/linkage-macros.hpp b/compat/linkage-macros.hpp index dff9a34c..3a64648a 100644 --- a/compat/linkage-macros.hpp +++ b/compat/linkage-macros.hpp @@ -5,13 +5,13 @@ # define OTR_GENERIC_IMPORT __declspec(dllimport) #elif defined _WIN32 && !defined __WINE__ # define OTR_GENERIC_EXPORT __attribute__((dllexport, visibility ("default"))) -# define OTR_GENERIC_IMPORT __attribute__((dllimport, visibility ("default"))) +# define OTR_GENERIC_IMPORT __attribute__((dllimport)) #else # define OTR_GENERIC_EXPORT __attribute__((visibility ("default"))) -# define OTR_GENERIC_IMPORT __attribute__((visibility ("default"))) +# define OTR_GENERIC_IMPORT #endif -#if defined __APPLE__ || (defined __MINGW32__ && defined _WIN64) +#if defined __APPLE__ || (defined __MINGW32__ && (defined __clang__ || defined _WIN64)) # define OTR_NO_TMPL_INST // link failure on both targets #endif diff --git a/compat/macros.hpp b/compat/macros.hpp index 5d82c4ee..08be9b4a 100644 --- a/compat/macros.hpp +++ b/compat/macros.hpp @@ -1,46 +1,8 @@ #pragma once -#if defined _MSC_VER -# define cc_noinline __declspec(noinline) -#else -# define cc_noinline __attribute__((noinline)) -#endif - -#if defined _MSC_VER -# define cc_forceinline __forceinline -#else -# define cc_forceinline __attribute__((always_inline)) -#endif - -#if !defined likely -# if defined __GNUC__ -# define likely(x) __builtin_expect(!!(x),1) -# define unlikely(x) __builtin_expect(!!(x),0) -# else -# define likely(x) (x) -# define unlikely(x) (x) -# endif -#endif +#include "macros1.h" -#if defined _MSC_VER -# define cc_function_name __FUNCSIG__ -#else -# define cc_function_name __PRETTY_FUNCTION__ -#endif - -#if !defined PP_CAT -# define PP_CAT(x,y) PP_CAT1(x,y) -# define PP_CAT1(x,y) PP_CAT2(x,y) -# define PP_CAT2(x,y) x ## y -#endif - -#ifndef PP_EXPAND -# define PP_EXPAND(x) PP_EXPAND__2(x) -# define PP_EXPAND__2(x) PP_EXPAND__3(x) x -# define PP_EXPAND__3(x) x -#endif - -#if defined __cplusplus +#ifdef __cplusplus // from now only C++ @@ -59,45 +21,26 @@ template<typename t> using remove_cvref_t = typename cxx20_compat::remove_cvref<t>::type; template<typename t> -using to_const_cvref_t = std::add_lvalue_reference_t<std::add_const_t<remove_cvref_t<t>>>; +using to_const_ref_t = std::add_lvalue_reference_t<std::add_const_t<remove_cvref_t<t>>>; // causes ICE in Visual Studio 2017 Preview. the ICE was reported and they handle them seriously in due time. // the ICE is caused by decltype(auto) and const& return value //#define eval_once(expr) ([&]() -> decltype(auto) { static decltype(auto) ret___1132 = (expr); return (decltype(ret___1132) const&) ret___1132; }()) -#define eval_once(expr) eval_once__2(expr, PP_CAT(_EVAL_ONCE__, __COUNTER__)) -#define eval_once__2(expr, ident) eval_once__3(expr, ident) +#define eval_once(expr) eval_once2(expr, __COUNTER__) -#define eval_once__3(expr, ident) \ - ([&]() -> decltype(auto) { \ - static auto INIT##ident = (expr); \ - return static_cast<to_const_cvref_t<decltype(INIT##ident)>>(INIT##ident); \ +#define eval_once2(expr, ctr) \ + ([&] { \ + [[maybe_unused]] \ + static auto PP_CAT(init, ctr) = (((void)(expr)), 0); \ }()) -#include <type_traits> - template<typename t> using cv_qualified = std::conditional_t<std::is_fundamental_v<remove_cvref_t<t>>, remove_cvref_t<t>, - to_const_cvref_t<t>>; - -template<bool> -[[deprecated]] constexpr cc_forceinline void static_warn() {} - -template<> -constexpr cc_forceinline void static_warn<true>() {} + to_const_ref_t<t>>; -#define static_warning(cond) \ - static_warn<(cond)>(); \ - -#define typed_progn(type, ...) ([&]() -> type { __VA_ARGS__ }()) #define progn(...) ([&]() -> decltype(auto) { __VA_ARGS__ }()) -#define prog1(x, ...) ([&]() -> decltype(auto) \ - { \ - decltype(auto) ret1324 = (x); __VA_ARGS__; return ret1324; \ - }()) - // end c++-only macros #endif - diff --git a/compat/macros1.h b/compat/macros1.h new file mode 100644 index 00000000..915b8d29 --- /dev/null +++ b/compat/macros1.h @@ -0,0 +1,43 @@ +#pragma once + +#if defined _MSC_VER +# define never_inline __declspec(noinline) +#else +# define never_inline __attribute__((noinline)) +#endif + +#if defined _MSC_VER +# define force_inline __forceinline +#else +# define force_inline __attribute__((always_inline)) +#endif + +#if !defined likely +# if defined __GNUC__ +# define likely(x) __builtin_expect(!!(x),1) +# define unlikely(x) __builtin_expect(!!(x),0) +# else +# define likely(x) (x) +# define unlikely(x) (x) +# endif +#endif + +#if defined _MSC_VER +# define function_name __FUNCSIG__ +#else +# define function_name __PRETTY_FUNCTION__ +#endif + +#define PP_CAT(x,y) PP_CAT1(x,y) +#define PP_CAT1(x,y) PP_CAT2(x,y) +#define PP_CAT2(x,y) x ## y + +#define PP_EXPAND(x) PP_EXPAND2(x) +#define PP_EXPAND2(x) PP_EXPAND3(x) x +#define PP_EXPAND3(x) x + +#ifdef _MSC_VER +# define unreachable() do { __assume(0); *(volatile int*)nullptr = 0; } while (0) /* NOLINT(clang-analyzer-core.NullDereference) */ +#else +# define unreachable() do { __builtin_unreachable(); *(volatile int*)nullptr = 0; } while (0) /* NOLINT(clang-analyzer-core.NullDereference) */ +#endif diff --git a/compat/math.hpp b/compat/math.hpp index 656e10a8..b8cba8f2 100644 --- a/compat/math.hpp +++ b/compat/math.hpp @@ -50,7 +50,7 @@ template<typename t, typename u, typename v> inline auto clamp(const t& val, const u& min, const v& max) { using w = cv_qualified<decltype(val + min + max)>; - return ::util_detail::clamp<w>::clamp_(val, min, max); + return util_detail::clamp<w>::clamp_(val, min, max); } template<typename t> @@ -66,7 +66,7 @@ inline auto uround(t val) -> std::enable_if_t<std::is_floating_point_v<remove_cv } template <typename t> -static cc_forceinline constexpr int signum(const t& x) +static force_inline constexpr int signum(const t& x) { return x < t{0} ? -1 : 1; } diff --git a/compat/mutex.cpp b/compat/mutex.cpp new file mode 100644 index 00000000..664677ea --- /dev/null +++ b/compat/mutex.cpp @@ -0,0 +1,33 @@ +#include "mutex.hpp" +#include <cstdlib> + +mutex& mutex::operator=(const mutex& rhs) +{ + if (rhs->isRecursive() != inner.isRecursive()) + std::abort(); + + return *this; +} + +mutex::mutex(const mutex& datum) : mutex{datum.inner.isRecursive() ? Recursive : NonRecursive} +{ +} + +mutex::mutex(RecursionMode m) : inner{m} +{ +} + +QMutex* mutex::operator&() const noexcept +{ + return &inner; +} + +mutex::operator QMutex*() const noexcept +{ + return &inner; +} + +QMutex* mutex::operator->() const noexcept +{ + return &inner; +} diff --git a/compat/mutex.hpp b/compat/mutex.hpp new file mode 100644 index 00000000..54758a08 --- /dev/null +++ b/compat/mutex.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <QMutex> + +#include "export.hpp" + +class OTR_COMPAT_EXPORT mutex +{ + mutable QMutex inner; + +public: + using RecursionMode = QMutex::RecursionMode; + static constexpr RecursionMode Recursive = RecursionMode::Recursive; + static constexpr RecursionMode NonRecursive = RecursionMode::NonRecursive; + + mutex& operator=(const mutex& datum); + mutex(const mutex& datum); + explicit mutex(RecursionMode m); + mutex() : mutex{NonRecursive} {} + + QMutex* operator&() const noexcept; + explicit operator QMutex*() const noexcept; + QMutex* operator->() const noexcept; +}; diff --git a/compat/qt-signal.cpp b/compat/qt-signal.cpp new file mode 100644 index 00000000..08aac663 --- /dev/null +++ b/compat/qt-signal.cpp @@ -0,0 +1,13 @@ +#include "qt-signal.hpp" + +namespace qt_sig { + +nullary::nullary(QObject* parent) : QObject(parent) {} +nullary::~nullary() = default; + +void nullary::operator()() const +{ + notify(); +} + +} // ns qt_sig diff --git a/compat/qt-signal.hpp b/compat/qt-signal.hpp new file mode 100644 index 00000000..119e063c --- /dev/null +++ b/compat/qt-signal.hpp @@ -0,0 +1,30 @@ +#pragma once + +// this is to avoid dealing with QMetaObject for the time being -sh 20190203 + +#include "export.hpp" +#include <QObject> + +namespace qt_sig { + +class OTR_COMPAT_EXPORT nullary : public QObject +{ + Q_OBJECT + +public: + template<typename t, typename F> + nullary(t* datum, F&& f, Qt::ConnectionType conntype = Qt::AutoConnection) : QObject(datum) + { + connect(this, &nullary::notify, datum, f, conntype); + } + + nullary(QObject* parent = nullptr); + ~nullary() override; + + void operator()() const; + +signals: + void notify() const; +}; + +} // ns qt_sig diff --git a/compat/run-in-thread.hpp b/compat/run-in-thread.hpp index afe279f2..b8ffc179 100644 --- a/compat/run-in-thread.hpp +++ b/compat/run-in-thread.hpp @@ -53,7 +53,7 @@ struct run_in_thread_traits<void> } template<typename F> -auto cc_noinline +auto never_inline run_in_thread_sync(QObject* obj, F&& fun) -> typename qt_impl_detail::run_in_thread_traits<decltype(fun())>::ret_type { @@ -108,8 +108,8 @@ template<typename F> void run_in_thread_async(QObject* obj, F&& fun) { QObject src; - QThread* t(obj->thread()); - assert(t); + QThread* t = obj->thread(); + if (!t) abort(); src.moveToThread(t); QObject::connect(&src, &QObject::destroyed, obj, std::move(fun), Qt::AutoConnection); } diff --git a/compat/shm.cpp b/compat/shm.cpp index 791cb303..1f863190 100644 --- a/compat/shm.cpp +++ b/compat/shm.cpp @@ -15,7 +15,7 @@ #include <accctrl.h> #include <aclapi.h> -#if !defined __WINE__ +#ifdef QT_CORE_LIB # include <QDebug> # define warn(str, ...) (qDebug() << "shm:" str ": " << __VA_ARGS__) #else diff --git a/compat/shm.h b/compat/shm.h index 814ce90c..5ea6c80a 100644 --- a/compat/shm.h +++ b/compat/shm.h @@ -32,10 +32,10 @@ class OTR_COMPAT_EXPORT shm_wrapper final #endif public: - cc_noinline shm_wrapper(const char *shm_name, const char *mutex_name, int map_size); - cc_noinline ~shm_wrapper(); - cc_noinline bool lock(); - cc_noinline bool unlock(); - cc_noinline bool success(); + never_inline shm_wrapper(const char *shm_name, const char *mutex_name, int map_size); + never_inline ~shm_wrapper(); + never_inline bool lock(); + never_inline bool unlock(); + never_inline bool success(); inline void* ptr() { return mem; } }; diff --git a/compat/simple-mat.hpp b/compat/simple-mat.hpp index 87e1ae83..43d136f9 100644 --- a/compat/simple-mat.hpp +++ b/compat/simple-mat.hpp @@ -8,13 +8,11 @@ #pragma once -#include "export.hpp" - #include <type_traits> #include <utility> #include <cmath> -namespace simple_mat_detail { +namespace simple_mat { // last param to fool SFINAE into overloading template<int i, int j, int> struct equals @@ -67,73 +65,63 @@ public: // parameters w_ and h_ are rebound so that SFINAE occurs // removing them causes a compile-time error -sh 20150811 - template<int Q = w_> std::enable_if_t<equals<Q, 1, 0>::value, num> - constexpr inline operator()(unsigned i) const& { return data[i][0]; } + template<typename t, int Q = w_> std::enable_if_t<equals<Q, 1, 0>::value, num> + constexpr inline operator()(t i) const& { return data[(unsigned)i][0]; } - template<int P = h_> std::enable_if_t<equals<P, 1, 1>::value, num> - constexpr inline operator()(unsigned i) const& { return data[0][i]; } + template<typename t, int P = h_> std::enable_if_t<equals<P, 1, 1>::value, num> + constexpr inline operator()(t i) const& { return data[0][(unsigned)i]; } - template<int Q = w_> std::enable_if_t<equals<Q, 1, 2>::value, num&> - constexpr inline operator()(unsigned i) & { return data[i][0]; } + template<typename t, int Q = w_> std::enable_if_t<equals<Q, 1, 2>::value, num&> + constexpr inline operator()(t i) & { return data[(unsigned)i][0]; } - template<int P = h_> std::enable_if_t<equals<P, 1, 3>::value, num&> - constexpr inline operator()(unsigned i) & { return data[0][i]; } + template<typename t, int P = h_> std::enable_if_t<equals<P, 1, 3>::value, num&> + constexpr inline operator()(t i) & { return data[0][(unsigned)i]; } -#define OPENTRACK_ASSERT_SWIZZLE static_assert(P == h_ && Q == w_) +#define OTR_MAT_ASSERT_SWIZZLE static_assert(P == h_ && Q == w_) // const variants template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 1, 4>::value, num> - constexpr inline x() const& { OPENTRACK_ASSERT_SWIZZLE; return operator()(0); } + constexpr inline x() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(0); } template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 2, 4>::value, num> - constexpr inline y() const& { OPENTRACK_ASSERT_SWIZZLE; return operator()(1); } + constexpr inline y() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(1); } template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 3, 4>::value, num> - constexpr inline z() const& { OPENTRACK_ASSERT_SWIZZLE; return operator()(2); } + constexpr inline z() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(2); } template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 4, 4>::value, num> - constexpr inline w() const& { OPENTRACK_ASSERT_SWIZZLE; return operator()(3); } + constexpr inline w() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(3); } // mutable variants template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 1, 4>::value, num&> - constexpr inline x() & { OPENTRACK_ASSERT_SWIZZLE; return operator()(0); } + constexpr inline x() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(0); } template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 2, 4>::value, num&> - constexpr inline y() & { OPENTRACK_ASSERT_SWIZZLE; return operator()(1); } + constexpr inline y() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(1); } template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 3, 4>::value, num&> - constexpr inline z() & { OPENTRACK_ASSERT_SWIZZLE; return operator()(2); } + constexpr inline z() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(2); } template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 4, 4>::value, num&> - constexpr inline w() & { OPENTRACK_ASSERT_SWIZZLE; return operator()(3); } - - template<int h_pos, int w_pos> - constexpr Mat<num, h_ - h_pos, w_ - w_pos> slice() const - { - return (const double*)*this; - } - - template<int off> std::enable_if_t<equals<h_, 1, 0>::value, Mat<num, 1, w_ - off>> - slice() const { return ((double const*)*this) + off; } - - template<int off> std::enable_if_t<!equals<h_, 1, 2>::value && equals<w_, 1, 1>::value, - Mat<num, h_ - off, 1>> - slice() const { return ((double const*)*this) + off; } + constexpr inline w() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(3); } template<int P = h_, int Q = w_> - std::enable_if_t<is_vector<P, Q>::value, num> - norm() const + constexpr auto norm_squared() const -> std::enable_if_t<is_vector<P, Q>::value, num> { static_assert(P == h_ && Q == w_); const num val = dot(*this); + constexpr num eps = num(1e-4); - if (val < num(1e-4)) + if (val < eps) return num(0); else - return std::sqrt(val); + return val; } + template<int P = h_, int Q = w_> + inline auto norm() const { return num(std::sqrt(norm_squared())); } + template<int R, int S, int P = h_, int Q = w_> std::enable_if_t<is_vector_pair<R, S, P, Q>::value, num> constexpr dot(const Mat<num, R, S>& p2) const @@ -152,7 +140,7 @@ public: constexpr cross(const Mat<num, R, S>& b) const { static_assert(P == h_ && Q == w_); - auto& a = *this; + const auto& a = *this; return Mat<num, R, S>(a.y()*b.z() - a.z()*b.y(), a.z()*b.x() - a.x()*b.z(), @@ -221,15 +209,10 @@ public: } template<typename t, typename u> - constexpr inline num operator()(t j, u i) const& { return data[(int) j][(int) i]; } + constexpr inline num operator()(t j, u i) const& { return data[(unsigned)j][(unsigned)i]; } template<typename t, typename u> - constexpr inline num& operator()(t j, u i) & { return data[(int) j][(int) i]; } - -#ifdef __GNUG__ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wmissing-braces" -#endif + constexpr inline num& operator()(t j, u i) & { return data[(unsigned)j][(unsigned)i]; } template<typename... ts, int h__ = h_, int w__ = w_, typename = std::enable_if_t<is_arglist_correct<num, h__, w__, ts...>::value>> @@ -238,10 +221,6 @@ public: static_assert(h__ == h_ && w__ == w_); } -#ifdef __GNUG__ -# pragma GCC diagnostic pop -#endif - constexpr Mat() { for (int j = 0; j < h_; j++) @@ -256,8 +235,8 @@ public: data[j][i] = mem[i*h_+j]; } - operator num*() { return reinterpret_cast<num*>(data); } - operator const num*() const { return reinterpret_cast<const num*>(data); } + constexpr operator num*() & { return (num*)data; } + constexpr operator const num*() const& { return (const num*)data; } // XXX add more operators as needed, third-party dependencies mostly // not needed merely for matrix algebra -sh 20141030 @@ -288,6 +267,15 @@ public: return ret; } + + constexpr Mat<num, w_, h_>& operator=(const Mat<num, w_, h_>& rhs) + { + for (unsigned j = 0; j < h_; j++) + for (unsigned i = 0; i < w_; i++) + data[j][i] = rhs(j, i); + + return *this; + } }; template<typename num, int h, int w> @@ -318,9 +306,7 @@ OTR_GENERIC_EXPORT inline void test() } #endif -} // ns simple_mat_detail +} // ns detail template<typename num, int h, int w> -using Mat = simple_mat_detail::Mat<num, h, w>; - - +using Mat = simple_mat::Mat<num, h, w>; diff --git a/compat/sleep.cpp b/compat/sleep.cpp index 5178ae2f..e64e6254 100644 --- a/compat/sleep.cpp +++ b/compat/sleep.cpp @@ -14,7 +14,7 @@ namespace portable { #ifdef _WIN32 - Sleep(milliseconds); + Sleep((unsigned)milliseconds); #else usleep(unsigned(milliseconds) * 1000U); // takes microseconds #endif diff --git a/compat/sysexits.hpp b/compat/sysexits.hpp index 33f19b9d..6747fc88 100644 --- a/compat/sysexits.hpp +++ b/compat/sysexits.hpp @@ -2,13 +2,27 @@ #include <cstdlib> // for EXIT_SUCCESS, EXIT_FAILRUE -/* FreeBSD sysexits(3) - * - * The input data was incorrect in some way. This - * should only be used for user's data and not system - * files. - */ - -#if !defined EX_OSFILE -# define EX_OSFILE 72 +#ifndef _WIN32 +# include <sysexits.h> +#else +// this conforms to BSD sysexits(3) +// reference the manual page on FreeBSD or Linux for semantics +# define EX_OK 0 +# define EX_USAGE 64 +# define EX_DATAERR 65 +# define EX_NOINPUT 66 +# define EX_NOUSER 67 +# define EX_NOHOST 68 +# define EX_UNAVAILABLE 69 +# define EX_SOFTWARE 70 +# define EX_OSERR 71 +# define EX_OSFILE 72 +# define EX_CANTCREAT 73 +# define EX_IOERR 74 +# define EX_TEMPFAIL 75 +# define EX_PROTOCOL 76 +# define EX_NOPERM 77 +# define EX_CONFIG 78 #endif + + diff --git a/compat/timer.cpp b/compat/timer.cpp index 15e9d8a4..b46ebe4a 100644 --- a/compat/timer.cpp +++ b/compat/timer.cpp @@ -6,6 +6,8 @@ * notice appear in all copies. */ +#undef NDEBUG + #include "timer.hpp" #include <cassert> #include <cmath> diff --git a/compat/warn.hpp b/compat/warn.hpp deleted file mode 100644 index 7f3e21d0..00000000 --- a/compat/warn.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "macros.hpp" - -#include <sstream> -#include <iostream> -#include <locale> -#include <utility> - -#include <string> - -namespace warn_detail { -template<typename t> using basic_string_stream = std::basic_ostringstream<t, std::char_traits<t>, std::allocator<t>>; -using string_stream = basic_string_stream<wchar_t>; - -cc_forceinline void do_warn(string_stream&) {} - -template<typename x, typename... xs> -cc_forceinline void do_warn(string_stream& acc, const x& datum, const xs&... rest) -{ - acc << datum; - if (sizeof...(rest) > 0u) - acc << L' '; - do_warn(acc, rest...); -} - -template<typename... xs> -cc_noinline void warn_(const char* file, int line, const char* level, const xs&... seq) -{ - using namespace warn_detail; - string_stream stream; - - do_warn(stream, seq...); - - std::wcerr << L'[' << level << L' ' - << file << L':' << line - << L"] " - << std::boolalpha - << stream.str() - << L'\n'; - std::wcerr.flush(); -} - -} // ns warn_detail - -// todo add runtime loglevel - -#define otr_impl_warn_base(level, ...) \ - (warn_detail::warn_(__FILE__, __LINE__, (level), __VA_ARGS__)) - -#define dbg_warn(...) \ - otr_impl_warn_base("WARN", __VA_ARGS__) - -#define dbg_log(...) \ - otr_impl_warn_base("INFO", __VA_ARGS__) - -#define dbg_crit(...) \ - otr_impl_warn_base("CRIT", __VA_ARGS__) - -#include <cstdlib> - -#define dbg_fatal(...) \ - do \ - { \ - otr_impl_warn_base("FATAL", __VA_ARGS__); \ - std::abort(); \ - } while (0) diff --git a/csv/csv.cpp b/csv/csv.cpp index 73c56aed..a1f62dc0 100644 --- a/csv/csv.cpp +++ b/csv/csv.cpp @@ -111,13 +111,12 @@ bool CSV::getGameData(int id, unsigned char* table, QString& gamename) CSV csv(&file); - int lineno = 0; unsigned tmp[8]; unsigned fuzz[3]; QStringList gameLine; - while (lineno++, csv.parseLine(gameLine)) + for (int lineno = 0; csv.parseLine(gameLine); lineno++) { //qDebug() << "Column 0: " << gameLine.at(0); // No. //qDebug() << "Column 1: " << gameLine.at(1); // Game Name @@ -137,43 +136,32 @@ bool CSV::getGameData(int id, unsigned char* table, QString& gamename) const QByteArray id_cstr = gameLine[7].toLatin1(); - if (proto == QStringLiteral("V160")) - { - /* nothing */ - } - else if (id_cstr.length() != 22 || - sscanf(id_cstr.constData(), - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - fuzz + 2, - fuzz + 0, - tmp + 3, - tmp + 2, - tmp + 1, - tmp + 0, - tmp + 7, - tmp + 6, - tmp + 5, - tmp + 4, - fuzz + 1) != 11) - { + auto do_scanf = [&]() { + return sscanf(id_cstr.constData(), + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + fuzz + 2, + fuzz + 0, + tmp + 3, tmp + 2, tmp + 1, tmp + 0, + tmp + 7, tmp + 6, tmp + 5, tmp + 4, + fuzz + 1); + }; + + if (proto == QStringLiteral("V160") || id_cstr.length() != 22) + (void)0; + else if (id_cstr.length() != 22 || do_scanf() != 11) qDebug() << "scanf failed" << lineno; - } else { + using uchar = unsigned char; for (int i = 0; i < 8; i++) - { - using t = unsigned char; - table[i] = t(tmp[i]); - } + table[i] = uchar(tmp[i]); } gamename = std::move(name); return true; } } else - { qDebug() << "malformed csv line" << lineno; - } } if (id) diff --git a/cv/CMakeLists.txt b/cv/CMakeLists.txt index 1007344c..dfc983aa 100644 --- a/cv/CMakeLists.txt +++ b/cv/CMakeLists.txt @@ -1,6 +1,6 @@ find_package(OpenCV QUIET) if(OpenCV_FOUND) otr_module(cv STATIC) - target_link_libraries(${self} opencv_core opencv_videoio) + target_link_libraries(${self} opencv_videoio opencv_core opentrack-video) target_include_directories(${self} SYSTEM PRIVATE ${OpenCV_INCLUDE_DIRS}) endif() diff --git a/cv/affine.cpp b/cv/affine.cpp index 4ec63e57..66e21aa6 100644 --- a/cv/affine.cpp +++ b/cv/affine.cpp @@ -9,9 +9,8 @@ namespace affine_impl { -Affine::Affine() : R(mat33::eye()), t(0,0,0) {} - -Affine::Affine(const mat33& R, const vec3& t) : R(R),t(t) {} +Affine::Affine() = default; +Affine::Affine(const mat33& R, const vec3& t) : R(R), t(t) {} Affine operator*(const Affine& X, const Affine& Y) { diff --git a/cv/affine.hpp b/cv/affine.hpp index 3bc85c95..882a1145 100644 --- a/cv/affine.hpp +++ b/cv/affine.hpp @@ -12,7 +12,7 @@ namespace affine_impl { -using namespace types; +using namespace numeric_types; class Affine final { @@ -20,8 +20,8 @@ public: Affine(); Affine(const mat33& R, const vec3& t); - mat33 R; - vec3 t; + mat33 R { mat33::eye() }; + vec3 t { 0, 0, 0 }; }; Affine operator*(const Affine& X, const Affine& Y); @@ -31,5 +31,5 @@ vec3 operator*(const Affine& X, const vec3& v); } // ns affine_impl -using affine_impl::Affine; -using affine_impl::operator *; +using Affine = affine_impl::Affine; +//using affine_impl::operator *; diff --git a/cv/numeric.hpp b/cv/numeric.hpp index 0edb7f58..ce8f7e82 100644 --- a/cv/numeric.hpp +++ b/cv/numeric.hpp @@ -1,13 +1,15 @@ #pragma once +#include <type_traits> #include <opencv2/core.hpp> -namespace types { - using f = double; +namespace numeric_types { + using f = float; - namespace constants { - static constexpr inline f eps = f(1e-8); - } + static_assert(std::is_floating_point_v<f>); + + static constexpr f eps = f(1e-8); + static constexpr f pi = f(M_PI); template<int n> using vec = cv::Vec<f, n>; using vec2 = vec<2>; diff --git a/cv/translation-calibrator.cpp b/cv/translation-calibrator.cpp index 708cc593..8117b1e7 100644 --- a/cv/translation-calibrator.cpp +++ b/cv/translation-calibrator.cpp @@ -80,7 +80,7 @@ tt TranslationCalibrator::get_estimate() }; } -static constexpr inline double r2d = 180/M_PI; +static constexpr double r2d = 180/M_PI; bool TranslationCalibrator::check_bucket(const cv::Matx33d& R_CM_k) { @@ -94,7 +94,7 @@ bool TranslationCalibrator::check_bucket(const cv::Matx33d& R_CM_k) const euler_t ypr = rmat_to_euler(r) * r2d; auto array_index = [](double val, double spacing) { - return iround((val + 180)/spacing); + return (unsigned)iround((val + 180)/spacing); }; const unsigned yaw_k = array_index(ypr(yaw_rdof), yaw_spacing_in_degrees); diff --git a/cv/translation-calibrator.hpp b/cv/translation-calibrator.hpp index da052526..406edb72 100644 --- a/cv/translation-calibrator.hpp +++ b/cv/translation-calibrator.hpp @@ -41,8 +41,8 @@ public: // we're bringing in 3DOF samples but the calibrator only // checks yaw and pitch - static constexpr inline unsigned num_cal_axis = 3; - static constexpr inline unsigned num_nsample_axis = 2; + static constexpr unsigned num_cal_axis = 3; + static constexpr unsigned num_nsample_axis = 2; using cv_cal_vec = cv::Vec<float, num_cal_axis>; using cv_nsample_vec = cv::Vec<unsigned, num_nsample_axis>; @@ -51,6 +51,6 @@ public: // get the current estimate for t_MH tt get_estimate(); - static constexpr inline double yaw_spacing_in_degrees = 2; - static constexpr inline double pitch_spacing_in_degrees = 1.5; + static constexpr double yaw_spacing_in_degrees = 2; + static constexpr double pitch_spacing_in_degrees = 1.5; }; diff --git a/cv/video-property-page.cpp b/cv/video-property-page.cpp index e76f8bc3..bae5e8b3 100644 --- a/cv/video-property-page.cpp +++ b/cv/video-property-page.cpp @@ -25,11 +25,6 @@ #include <QDebug> -#include <dshow.h> - -#define CHECK(expr) if (FAILED(hr = (expr))) { qDebug() << QLatin1String(#expr) << hr; goto done; } -#define CHECK2(expr) if (!(expr)) { qDebug() << QLatin1String(#expr); goto done; } - bool video_property_page::show_from_capture(cv::VideoCapture& cap, int /*index */) { return cap.set(cv::CAP_PROP_SETTINGS, 0); @@ -39,16 +34,18 @@ struct prop_settings_worker final : QThread { explicit prop_settings_worker(int idx); ~prop_settings_worker() override; - void _open_prop_page(); + +private: + void open_prop_page(); void run() override; cv::VideoCapture cap; - int _idx = -1; + int idx = -1; }; -prop_settings_worker::prop_settings_worker(int idx) +prop_settings_worker::prop_settings_worker(int idx_) { - int ret = cap.get(cv::CAP_PROP_SETTINGS); + int ret = (int)cap.get(cv::CAP_PROP_SETTINGS); if (ret != 0) run_in_thread_async(qApp, [] { @@ -60,16 +57,16 @@ prop_settings_worker::prop_settings_worker(int idx) }); else { - _idx = idx; + idx = idx_; // DON'T MOVE IT // ps3 eye will reset to default settings if done from another thread - _open_prop_page(); + open_prop_page(); } } -void prop_settings_worker::_open_prop_page() +void prop_settings_worker::open_prop_page() { - cap.open(_idx); + cap.open(idx); if (cap.isOpened()) { @@ -87,14 +84,14 @@ void prop_settings_worker::_open_prop_page() } qDebug() << "property-page: can't open camera"; - _idx = -1; + idx = -1; return; ok: portable::sleep(100); - qDebug() << "property-page: opening for" << _idx; + qDebug() << "property-page: opening for" << idx; if (!cap.set(cv::CAP_PROP_SETTINGS, 0)) { @@ -110,7 +107,7 @@ ok: prop_settings_worker::~prop_settings_worker() { - if (_idx != -1) + if (idx != -1) { // ax filter is race condition-prone portable::sleep(250); @@ -118,13 +115,13 @@ prop_settings_worker::~prop_settings_worker() // idem portable::sleep(250); - qDebug() << "property-page: closed" << _idx; + qDebug() << "property-page: closed" << idx; } } void prop_settings_worker::run() { - if (_idx != -1) + if (idx != -1) { while (cap.get(cv::CAP_PROP_SETTINGS) > 0) portable::sleep(1000); @@ -166,4 +163,3 @@ bool video_property_page::show_from_capture(cv::VideoCapture&, int idx) bool video_property_page::show(int) { return false; } bool video_property_page::show_from_capture(cv::VideoCapture&, int) { return false; } #endif - diff --git a/cv/video-property-page.hpp b/cv/video-property-page.hpp index 9e6b596f..c2b9525d 100644 --- a/cv/video-property-page.hpp +++ b/cv/video-property-page.hpp @@ -1,11 +1,6 @@ #pragma once #include <QString> - -#ifdef _WIN32 -# include <windows.h> -#endif - #include <opencv2/videoio.hpp> struct video_property_page final @@ -15,3 +10,4 @@ struct video_property_page final static bool show_from_capture(cv::VideoCapture& cap, int index); private: }; + diff --git a/cv/video-widget.cpp b/cv/video-widget.cpp index d93429cb..7cc6ca2d 100644 --- a/cv/video-widget.cpp +++ b/cv/video-widget.cpp @@ -1,129 +1,78 @@ -/* Copyright (c) 2012 Patrick Ruoff - * Copyright (c) 2014-2016 Stanislaw Halik <sthalik@misaki.pl> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - */ - #include "video-widget.hpp" -#include "compat/check-visible.hpp" -#include "compat/math.hpp" - -#include <cstring> #include <opencv2/imgproc.hpp> -cv_video_widget::cv_video_widget(QWidget* parent) : QWidget(parent) -{ - connect(&timer, SIGNAL(timeout()), this, SLOT(update_and_repaint()), Qt::DirectConnection); - timer.start(65); -} - void cv_video_widget::update_image(const cv::Mat& frame) { QMutexLocker l(&mtx); - if (!freshp) - { - if (width < 1 || height < 1) - return; - - if (_frame.cols != frame.cols || _frame.rows != frame.rows) - _frame = cv::Mat(frame.rows, frame.cols, CV_8UC3); - frame.copyTo(_frame); - freshp = true; - - if (_frame2.cols != _frame.cols || _frame2.rows != _frame.rows) - _frame2 = cv::Mat(_frame.rows, _frame.cols, CV_8UC4); - - if (_frame3.cols != width || _frame3.rows != height) - _frame3 = cv::Mat(height, width, CV_8UC4); - - cv::cvtColor(_frame, _frame2, cv::COLOR_BGR2BGRA); - - const cv::Mat* img; - - if (_frame.cols != width || _frame.rows != height) - { - cv::resize(_frame2, _frame3, cv::Size(width, height), 0, 0, cv::INTER_NEAREST); - - img = &_frame3; - } - else - img = &_frame2; - - const unsigned nbytes = 4 * img->rows * img->cols; - - vec.resize(nbytes); - - std::memcpy(vec.data(), img->data, nbytes); - - texture = QImage((const unsigned char*) vec.data(), width, height, QImage::Format_ARGB32); - } -} - -void cv_video_widget::update_image(const QImage& img) -{ - QMutexLocker l(&mtx); - if (freshp) return; - const unsigned nbytes = img.bytesPerLine() * img.height(); - - vec.resize(nbytes); - - std::memcpy(vec.data(), img.constBits(), nbytes); - - texture = QImage((const unsigned char*) vec.data(), img.width(), img.height(), img.format()); + if (W < 1 || H < 1 || frame.rows < 1 || frame.cols < 1) + return; - freshp = true; -} + cv::Mat const* __restrict frame_scaled = nullptr; -void cv_video_widget::paintEvent(QPaintEvent*) -{ - QMutexLocker foo(&mtx); + if (frame3.cols != W || frame3.rows != H) + { + frame3 = cv::Mat(H, W, frame.type()); + frame2 = cv::Mat(H, W, CV_8UC4); - QPainter painter(this); + if (!frame2.isContinuous() || !frame3.isContinuous()) + std::abort(); + } - double dpr = devicePixelRatioF(); + if (frame.cols != W || frame.rows != H) + { + cv::resize(frame, frame3, { W, H }, 0, 0, cv::INTER_NEAREST); + frame_scaled = &frame3; + } + else if (!frame.isContinuous()) + { + frame.copyTo(frame3); + frame_scaled = &frame3; + } + else + frame_scaled = &frame; - int W = iround(QWidget::width() * dpr); - int H = iround(QWidget::height() * dpr); + freshp = true; - painter.drawImage(rect(), texture); + int color_cvt = 0; + constexpr int nchannels = 4; - if (texture.width() != W || texture.height() != H) + switch (frame_scaled->channels()) { - texture = QImage(W, H, QImage::Format_ARGB32); - texture.setDevicePixelRatio(dpr); - - width = W, height = H; - - _frame = cv::Mat(); - _frame2 = cv::Mat(); - _frame3 = cv::Mat(); + case 1: + color_cvt = cv::COLOR_GRAY2BGRA; + break; + case 3: + color_cvt = cv::COLOR_BGR2BGRA; + break; + case nchannels: + break; + default: + unreachable(); + break; } -} - -void cv_video_widget::update_and_repaint() -{ - if (!check_is_visible()) - return; - QMutexLocker l(&mtx); + cv::Mat const* frame_color; - if (freshp) + if (color_cvt != cv::COLOR_COLORCVT_MAX) { - freshp = false; - repaint(); + cv::cvtColor(*frame_scaled, frame2, color_cvt); + frame_color = &frame2; } -} + else + frame_color = frame_scaled; -void cv_video_widget::get_preview_size(int& w, int& h) -{ - QMutexLocker l(&mtx); + int stride = frame_color->step.p[0], rows = frame_color->rows; + int nbytes = rows * stride; + vec.resize(nbytes); vec.shrink_to_fit(); + std::memcpy(vec.data(), frame_color->data, nbytes); - w = width, h = height; + texture = QImage((const unsigned char*) vec.data(), W, H, stride, QImage::Format_ARGB32); + texture.setDevicePixelRatio(devicePixelRatioF()); } + +cv_video_widget::cv_video_widget(QWidget* parent) : video_widget(parent) {} diff --git a/cv/video-widget.hpp b/cv/video-widget.hpp index 8ad62a3c..9d62f19e 100644 --- a/cv/video-widget.hpp +++ b/cv/video-widget.hpp @@ -1,5 +1,4 @@ -/* Copyright (c) 2012 Patrick Ruoff - * Copyright (c) 2014-2016 Stanislaw Halik <sthalik@misaki.pl> +/* Copyright (c) 2019 Stanislaw Halik <sthalik@misaki.pl> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -8,39 +7,14 @@ #pragma once -#include <memory> -#include <vector> - +#include "video/video-widget.hpp" #include <opencv2/core.hpp> -#include <QObject> -#include <QWidget> -#include <QPainter> -#include <QtEvents> -#include <QTimer> -#include <QMutex> -#include <QMutexLocker> -#include <QSize> -#include <QDebug> - -class cv_video_widget final : public QWidget +struct cv_video_widget final : video_widget { - Q_OBJECT - -public: - cv_video_widget(QWidget *parent); + cv_video_widget(QWidget* parent = nullptr); void update_image(const cv::Mat& frame); - void update_image(const QImage& image); - void get_preview_size(int& w, int& h); -private slots: - void paintEvent(QPaintEvent*) override; - void update_and_repaint(); + private: - QMutex mtx { QMutex::Recursive }; - QImage texture; - std::vector<unsigned char> vec; - QTimer timer; - cv::Mat _frame, _frame2, _frame3; - int width = 320, height = 240; - bool freshp = false; + cv::Mat frame2, frame3; }; diff --git a/dinput/dinput.cpp b/dinput/dinput.cpp index 8a3d0f4a..22781a32 100644 --- a/dinput/dinput.cpp +++ b/dinput/dinput.cpp @@ -1,21 +1,23 @@ #include "dinput.hpp" +#include "compat/macros.hpp" + #include <QDebug> -std::atomic<int> di_t::refcnt; -std::atomic_flag di_t::init_lock = ATOMIC_FLAG_INIT; +int di_t::refcnt{0}; diptr di_t::handle; +QMutex di_t::lock; diptr di_t::init_di_() { CoInitialize(nullptr); diptr di = nullptr; - if (HRESULT hr = DirectInput8Create(GetModuleHandle(nullptr), - DIRECTINPUT_VERSION, - IID_IDirectInput8, - (void**)&di, - nullptr); - !SUCCEEDED(hr)) + HRESULT hr = DirectInput8Create(GetModuleHandle(nullptr), + DIRECTINPUT_VERSION, + IID_IDirectInput8, + (void**)&di, + nullptr); + if (!SUCCEEDED(hr)) { qDebug() << "can't make dinput:" << (void*)(LONG_PTR)hr; qDebug() << "crashing!"; @@ -32,28 +34,24 @@ di_t::di_t() void di_t::ref_di() { - while (init_lock.test_and_set()) { /* busy loop */ } + QMutexLocker l(&lock); if (!handle) handle = init_di_(); ++refcnt; - - init_lock.clear(); } void di_t::unref_di() { + QMutexLocker l(&lock); + const int refcnt_ = --refcnt; if (refcnt_ == 0) { - while (init_lock.test_and_set()) { /* busy loop */ } - qDebug() << "exit: di handle"; handle->Release(); - - init_lock.clear(); } } @@ -62,3 +60,37 @@ di_t::~di_t() unref_di(); } +bool di_t::poll_device(LPDIRECTINPUTDEVICE8 dev) +{ + HRESULT hr; + + switch (dev->Poll()) + { + case DI_OK: + case DI_NOEFFECT: + return true; + default: + break; + } + + switch (hr = dev->Acquire()) + { + default: + break; + case DI_OK: + case S_FALSE: + switch (hr = dev->Poll()) + { + case DI_OK: + case DI_NOEFFECT: + return true; + default: + break; + } + break; + } + + eval_once(qDebug() << "dinput: device poll failed:" << (void*)hr); + + return false; +} diff --git a/dinput/dinput.hpp b/dinput/dinput.hpp index a9241504..394084e2 100644 --- a/dinput/dinput.hpp +++ b/dinput/dinput.hpp @@ -8,15 +8,19 @@ #pragma once -#include "export.hpp" +#include <QMutex> -#include <atomic> +#include "export.hpp" #undef DIRECTINPUT_VERSION #define DIRECTINPUT_VERSION 0x800 #include <dinput.h> +// XXX TODO -sh 20190209 +// keybinding_worker and joystick context are badly named +// add namespaces and rename, including inner joystick device struct + using diptr = IDirectInput8A*; class OTR_DINPUT_EXPORT di_t final @@ -25,8 +29,8 @@ class OTR_DINPUT_EXPORT di_t final static void ref_di(); static diptr handle; - static std::atomic<int> refcnt; - static std::atomic_flag init_lock; + static int refcnt; + static QMutex lock; static diptr init_di_(); public: @@ -36,16 +40,8 @@ public: di_t& operator=(const di_t&) = default; diptr operator->() const { return handle; } - operator bool() { return handle != nullptr; } + operator bool() const { return handle != nullptr; } + operator diptr() const { return handle; } - // for debugging bad usages. must use a dependent name. - template<typename t = void> - explicit operator void*() const - { - static_assert(sizeof(t) == -1); - static_assert(sizeof(t) == 0); - - return nullptr; - } + static bool poll_device(LPDIRECTINPUTDEVICE8 dev); }; - diff --git a/dinput/keybinding-worker.cpp b/dinput/keybinding-worker.cpp index a3331323..3d792778 100644 --- a/dinput/keybinding-worker.cpp +++ b/dinput/keybinding-worker.cpp @@ -48,12 +48,12 @@ bool KeybindingWorker::init() } if (din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, nullptr) != DI_OK) { - qDebug() << "setup CreateDevice function failed!" << GetLastError(); + qDebug() << "dinput: create keyboard failed" << GetLastError(); return false; } if (dinkeyboard->SetDataFormat(&c_dfDIKeyboard) != DI_OK) { - qDebug() << "setup SetDataFormat function failed!" << GetLastError(); + qDebug() << "dinput: keyboard SetDataFormat" << GetLastError(); dinkeyboard->Release(); dinkeyboard = nullptr; return false; @@ -62,34 +62,26 @@ bool KeybindingWorker::init() if (dinkeyboard->SetCooperativeLevel((HWND) fake_main_window.winId(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND) != DI_OK) { dinkeyboard->Release(); dinkeyboard = nullptr; - qDebug() << "setup SetCooperativeLevel function failed!" << GetLastError(); + qDebug() << "dinput: keyboard SetCooperativeLevel" << GetLastError(); return false; } { DIPROPDWORD dipdw; - dipdw.dwData = 128; + dipdw.dwData = num_keyboard_states; dipdw.diph.dwHeaderSize = sizeof(dipdw.diph); dipdw.diph.dwHow = DIPH_DEVICE; dipdw.diph.dwObj = 0; dipdw.diph.dwSize = sizeof(dipdw); if ( dinkeyboard->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph) != DI_OK) { - qDebug() << "setup keyboard buffer mode failed!"; + qDebug() << "dinput: DIPROP_BUFFERSIZE"; dinkeyboard->Release(); dinkeyboard = nullptr; return false; } } - if (dinkeyboard->Acquire() != DI_OK) - { - dinkeyboard->Release(); - dinkeyboard = nullptr; - qDebug() << "setup dinkeyboard Acquire failed!" << GetLastError(); - return false; - } - return true; } @@ -142,12 +134,15 @@ bool KeybindingWorker::run_keyboard_nolock() * Key-up events work on my end. */ + if (!di_t::poll_device(dinkeyboard)) + eval_once(qDebug() << "dinput: keyboard poll failed"); + DWORD sz = num_keyboard_states; - const HRESULT hr = dinkeyboard->GetDeviceData(sizeof(*keyboard_states), keyboard_states, &sz, 0); + HRESULT hr = dinkeyboard->GetDeviceData(sizeof(*keyboard_states), keyboard_states, &sz, 0); - if (hr != DI_OK) + if (FAILED(hr)) { - eval_once(qDebug() << "dinput: keyboard GetDeviceData failed" << hr); + eval_once(qDebug() << "dinput: keyboard GetDeviceData failed" << (void*)hr); return false; } @@ -210,7 +205,7 @@ bool KeybindingWorker::run_joystick_nolock() return true; } -KeybindingWorker::fun* KeybindingWorker::_add_receiver(fun& receiver) +KeybindingWorker::fun* KeybindingWorker::add_receiver(fun& receiver) { QMutexLocker l(&mtx); receivers.push_back(std::make_unique<fun>(receiver)); diff --git a/dinput/keybinding-worker.hpp b/dinput/keybinding-worker.hpp index fad9ec7e..7b9dd259 100644 --- a/dinput/keybinding-worker.hpp +++ b/dinput/keybinding-worker.hpp @@ -24,13 +24,13 @@ struct OTR_DINPUT_EXPORT Key { QString guid; + Timer timer; int keycode = 0; bool shift = false; bool ctrl = false; bool alt = false; bool held = true; bool enabled = true; - Timer timer; public: Key(); @@ -41,6 +41,9 @@ struct OTR_DINPUT_EXPORT KeybindingWorker : private QThread { using fun = std::function<void(const Key&)>; + KeybindingWorker(const KeybindingWorker&) = delete; + KeybindingWorker& operator=(KeybindingWorker&) = delete; + private: LPDIRECTINPUTDEVICE8 dinkeyboard { nullptr }; win32_joy_ctx joy_ctx; @@ -50,7 +53,6 @@ private: di_t din; bool keystate[256] {}; - bool old_keystate[256] {}; void run() override; bool run_keyboard_nolock(); @@ -60,15 +62,12 @@ private: KeybindingWorker(); static KeybindingWorker& make(); - fun* _add_receiver(fun &receiver); + fun* add_receiver(fun& receiver); void remove_receiver(fun* pos); - ~KeybindingWorker(); + ~KeybindingWorker() override; - static constexpr int num_keyboard_states = 16; + static constexpr int num_keyboard_states = 64; DIDEVICEOBJECTDATA keyboard_states[num_keyboard_states] {}; - - KeybindingWorker(const KeybindingWorker&) = delete; - KeybindingWorker& operator=(KeybindingWorker&) = delete; public: class Token { @@ -83,7 +82,7 @@ public: } Token(fun receiver) { - pos = make()._add_receiver(receiver); + pos = make().add_receiver(receiver); } }; }; diff --git a/dinput/win32-joystick.cpp b/dinput/win32-joystick.cpp index 4da5c57d..0f2687fe 100644 --- a/dinput/win32-joystick.cpp +++ b/dinput/win32-joystick.cpp @@ -3,19 +3,12 @@ #include "win32-joystick.hpp" #include "compat/macros.hpp" -// doesn't play well with Qt Creator clang code model -#if defined Q_CREATOR_RUN && defined _MSC_VER -# undef offsetof -# define offsetof(type, member) __builtin_offsetof(type, member) -#else -# include <cstddef> -#endif - +#include <cstddef> #include <algorithm> #include <cmath> +#include <iterator> #include <QWidget> - #include <QDebug> #include <objbase.h> @@ -54,33 +47,15 @@ bool win32_joy_ctx::poll_axis(const QString &guid, int* axes) return false; auto& j = iter->second; - auto& joy_handle = j->joy_handle; - bool ok = false; - HRESULT hr; - - if (!FAILED(hr = joy_handle->Poll())) - ok = true; - - if (!ok && FAILED(hr = joy_handle->Acquire())) - { - (void)0; - //qDebug() << "joy acquire failed" << hr; - } + DIJOYSTATE2 js = {}; - if (!ok) - { - (void)joy_handle->Unacquire(); - Sleep(100); + if (!di_t::poll_device(joy_handle)) continue; - } - - DIJOYSTATE2 js = {}; - if (FAILED(hr = joy_handle->GetDeviceState(sizeof(js), &js))) + if (FAILED(joy_handle->GetDeviceState(sizeof(js), &js))) { - //qDebug() << "joy get state failed" << guid << hr; - Sleep(500); + //qDebug() << "joy get state failed" << guid; continue; } @@ -96,7 +71,7 @@ bool win32_joy_ctx::poll_axis(const QString &guid, int* axes) js.rglSlider[1] }; - for (int i = 0; i < 8; i++) + for (unsigned i = 0; i < std::size(values); i++) axes[i] = values[i]; return true; @@ -133,11 +108,12 @@ void win32_joy_ctx::refresh() QString win32_joy_ctx::guid_to_string(const GUID& guid) { - char buf[40] = {0}; - wchar_t szGuidW[40] = {0}; + char buf[40] = {}; + wchar_t szGuidW[40] = {}; - StringFromGUID2(guid, szGuidW, 40); - WideCharToMultiByte(0, 0, szGuidW, -1, buf, 40, nullptr, nullptr); + StringFromGUID2(guid, szGuidW, sizeof(buf)); + WideCharToMultiByte(0, 0, szGuidW, -1, buf, sizeof(buf), nullptr, nullptr); + buf[sizeof(buf)-1] = 0; return QString(buf); } @@ -158,19 +134,18 @@ bool win32_joy_ctx::joy::poll(fn const& f) { HRESULT hr; - (void) joy_handle->Acquire(); - - if (FAILED(hr = joy_handle->Poll())) + if (!di_t::poll_device(joy_handle)) { - //qDebug() << "joy acquire failed" << guid << hr; - (void) joy_handle->Unacquire(); + eval_once(qDebug() << "joy poll failed" << guid << (void*)hr); + //(void)joy_handle->Unacquire(); + //Sleep(0); return false; } DWORD sz = num_buffers; if (FAILED(hr = joy_handle->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), keystate_buffers, &sz, 0))) { - eval_once(qDebug() << "joy get state failed" << guid << hr); + eval_once(qDebug() << "joy GetDeviceData failed" << guid << (void*)hr); return false; } @@ -195,7 +170,7 @@ bool win32_joy_ctx::joy::poll(fn const& f) default: if (event.dwOfs >= BUTTON_OFFSET(0) && event.dwOfs <= BUTTON_OFFSET(max_buttons - 1)) { - i = event.dwOfs - BUTTON_OFFSET(0); + i = int(event.dwOfs - BUTTON_OFFSET(0)); i /= sizeof(DIJOYSTATE2().rgbButtons[0]); i %= max_buttons; // defensive programming } @@ -254,7 +229,7 @@ void win32_joy_ctx::enum_state::refresh() if (!di) { - qDebug() << "can't create dinput"; + qDebug() << "dinput: can't create dinput"; return; } @@ -265,7 +240,7 @@ void win32_joy_ctx::enum_state::refresh() this, DIEDFL_ATTACHEDONLY))) { - eval_once(qDebug() << "failed enum joysticks" << hr); + eval_once(qDebug() << "dinput: failed enum joysticks" << (void*)hr); return; } @@ -298,12 +273,12 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINS LPDIRECTINPUTDEVICE8 h; if (FAILED(hr = state.di->CreateDevice(pdidInstance->guidInstance, &h, nullptr))) { - qDebug() << "createdevice" << guid << hr; + qDebug() << "dinput: failed joystick CreateDevice" << guid << (void*)hr; goto end; } - if (FAILED(h->SetDataFormat(&c_dfDIJoystick2))) + if (FAILED(hr = h->SetDataFormat(&c_dfDIJoystick2))) { - qDebug() << "format"; + qDebug() << "dinput: failed joystick SetDataFormat" << (void*)hr; h->Release(); goto end; } @@ -328,7 +303,7 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINS if (h->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph) != DI_OK) { - qDebug() << "setup joystick buffer mode failed!"; + qDebug() << "dinput: joystick DIPROP_BUFFERSIZE"; h->Release(); goto end; } @@ -336,7 +311,7 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumJoysticksCallback(const DIDEVICEINS if (FAILED(hr = h->EnumObjects(EnumObjectsCallback, h, DIDFT_ALL))) { - qDebug() << "enum-objects"; + qDebug() << "dinput: joystick EnumObjects"; h->Release(); goto end; } @@ -352,8 +327,8 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJEC if (pdidoi->dwType & DIDFT_AXIS) { DIPROPRANGE diprg = {}; - diprg.diph.dwSize = sizeof( DIPROPRANGE ); - diprg.diph.dwHeaderSize = sizeof( DIPROPHEADER ); + diprg.diph.dwSize = sizeof(DIPROPRANGE); + diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); diprg.diph.dwHow = DIPH_BYID; diprg.diph.dwObj = pdidoi->dwType; diprg.lMax = joy_axis_size; @@ -363,7 +338,7 @@ BOOL CALLBACK win32_joy_ctx::enum_state::EnumObjectsCallback(const DIDEVICEOBJEC if (FAILED(hr = reinterpret_cast<LPDIRECTINPUTDEVICE8>(ctx)->SetProperty(DIPROP_RANGE, &diprg.diph))) { - qDebug() << "DIPROP_RANGE" << hr; + qDebug() << "dinput: failed joystick DIPROP_RANGE" << (void*)hr; return DIENUM_STOP; } } @@ -384,5 +359,4 @@ win32_joy_ctx::joy::~joy() } } // ns win32_joy_impl - #endif diff --git a/dinput/win32-joystick.hpp b/dinput/win32-joystick.hpp index 1eef5bfa..331e4968 100644 --- a/dinput/win32-joystick.hpp +++ b/dinput/win32-joystick.hpp @@ -23,24 +23,24 @@ namespace win32_joy_impl { -static constexpr inline unsigned max_buttons = std::size(DIJOYSTATE2().rgbButtons); -static constexpr inline unsigned max_pov_hats = std::size(DIJOYSTATE2().rgdwPOV); +static constexpr unsigned max_buttons = std::size(DIJOYSTATE2().rgbButtons); +static constexpr unsigned max_pov_hats = std::size(DIJOYSTATE2().rgdwPOV); -static constexpr inline unsigned pov_hat_directions = 8; +static constexpr unsigned pov_hat_directions = 8; // cf. https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v=vs.85) // see also remarks on the page // no need to check for pos == unsigned(-1) || pos == 0xffff, // this logic doesn't require that -static constexpr inline unsigned value_per_pov_hat_direction = 36000 / pov_hat_directions; -static constexpr inline unsigned max_buttons_and_pov_hats = max_buttons + max_pov_hats * pov_hat_directions; +static constexpr unsigned value_per_pov_hat_direction = 36000 / pov_hat_directions; +static constexpr unsigned max_buttons_and_pov_hats = max_buttons + max_pov_hats * pov_hat_directions; //static_assert(pov_hat_directions == 4 || pov_hat_directions == 8); // XXX how many axis update events can we reasonably get in a short time frame? -static constexpr inline unsigned num_buffers = 16; +static constexpr unsigned num_buffers = 16; -#define WIN32_JOY_DEBUG +//#define WIN32_JOY_DEBUG struct OTR_DINPUT_EXPORT win32_joy_ctx final { @@ -51,7 +51,6 @@ struct OTR_DINPUT_EXPORT win32_joy_ctx final LPDIRECTINPUTDEVICE8 joy_handle; QString guid, name; bool last_state[max_buttons_and_pov_hats] {}; - Timer first_timer; static DIDEVICEOBJECTDATA keystate_buffers[num_buffers]; @@ -64,7 +63,7 @@ struct OTR_DINPUT_EXPORT win32_joy_ctx final using joys_t = std::unordered_map<QString, std::shared_ptr<joy>>; - static constexpr inline int joy_axis_size = 65536; + static constexpr int joy_axis_size = 65536; struct joy_info { diff --git a/filter-accela/accela-settings.hpp b/filter-accela/accela-settings.hpp index ce27f800..c873b0b4 100644 --- a/filter-accela/accela-settings.hpp +++ b/filter-accela/accela-settings.hpp @@ -1,8 +1,8 @@ #pragma once +#include "spline/spline.hpp" #include "options/options.hpp" using namespace options; -#include "spline/spline.hpp" // ------------------------------------ // debug knobs @@ -19,8 +19,7 @@ struct settings_accela : opts double x, y; }; - static constexpr inline gains const rot_gains[16] = - { + static constexpr gains const rot_gains[] { { 9, 300 }, { 8, 200 }, { 5, 100 }, @@ -30,8 +29,7 @@ struct settings_accela : opts { .5, .4 }, }; - static constexpr inline gains const pos_gains[16] = - { + static constexpr gains const pos_gains[] { { 9, 200 }, { 8, 150 }, { 7, 110 }, diff --git a/filter-accela/ftnoir_filter_accela.cpp b/filter-accela/ftnoir_filter_accela.cpp index f96bd2d2..5fa1676f 100644 --- a/filter-accela/ftnoir_filter_accela.cpp +++ b/filter-accela/ftnoir_filter_accela.cpp @@ -20,20 +20,20 @@ accela::accela() s.make_splines(spline_rot, spline_pos); } -template<int N = 3, typename F> -cc_noinline +template<typename F> +never_inline static void do_deltas(const double* deltas, double* output, F&& fun) { + constexpr unsigned N = 3; + double norm[N]; + double dist = 0; - const double dist = progn( - double ret = 0; - for (unsigned k = 0; k < N; k++) - ret += deltas[k]*deltas[k]; - return sqrt(ret); - ); + for (unsigned k = 0; k < N; k++) + dist += deltas[k]*deltas[k]; + dist = sqrt(dist); - const double value = double(fun(dist)); + const double value = fun(dist); for (unsigned k = 0; k < N; k++) { @@ -42,17 +42,17 @@ static void do_deltas(const double* deltas, double* output, F&& fun) } double n = 0; - for (unsigned k = 0; k < N; k++) + for (unsigned k = 0; k < N; k++) // NOLINT(modernize-loop-convert) n += norm[k]; if (n > 1e-6) { const double ret = 1./n; - for (unsigned k = 0; k < N; k++) + for (unsigned k = 0; k < N; k++) // NOLINT(modernize-loop-convert) norm[k] *= ret; } else - for (unsigned k = 0; k < N; k++) + for (unsigned k = 0; k < N; k++) // NOLINT(modernize-loop-convert) norm[k] = 0; for (unsigned k = 0; k < N; k++) @@ -84,14 +84,14 @@ void accela::filter(const double* input, double *output) return; } - const double rot_thres { s.rot_smoothing }; - const double pos_thres { s.pos_smoothing }; + const double rot_thres{s.rot_smoothing}; + const double pos_thres{s.pos_smoothing}; const double dt = t.elapsed_seconds(); t.start(); - const double rot_dz = s.rot_deadzone.to<double>(); - const double pos_dz = s.pos_deadzone.to<double>(); + const double rot_dz{ s.rot_deadzone}; + const double pos_dz{ s.pos_deadzone}; // rot @@ -141,15 +141,13 @@ void accela::filter(const double* input, double *output) void settings_accela::make_splines(spline& rot, spline& pos) { - rot = spline(); - pos = spline(); + rot.clear(); pos.clear(); for (const auto& val : rot_gains) - rot.add_point(QPointF(val.x, val.y)); + rot.add_point({ val.x, val.y }); for (const auto& val : pos_gains) - pos.add_point(QPointF(val.x, val.y)); + pos.add_point({ val.x, val.y }); } OPENTRACK_DECLARE_FILTER(accela, dialog_accela, accelaDll) - diff --git a/filter-accela/ftnoir_filter_accela.h b/filter-accela/ftnoir_filter_accela.h index a3f93756..9112b0b8 100644 --- a/filter-accela/ftnoir_filter_accela.h +++ b/filter-accela/ftnoir_filter_accela.h @@ -17,6 +17,8 @@ #include <QMutex> #include <QTimer> +//#define DEBUG_ACCELA + class accela : public IFilter { public: @@ -57,6 +59,6 @@ class accelaDll : public Metadata { Q_OBJECT - QString name() { return tr("Accela"); } - QIcon icon() { return QIcon(":/images/filter-16.png"); } + QString name() override { return tr("Accela"); } + QIcon icon() override { return QIcon(":/images/filter-16.png"); } }; diff --git a/filter-accela/ftnoir_filter_accela_dialog.cpp b/filter-accela/ftnoir_filter_accela_dialog.cpp index 51c31ca4..d63f27fd 100644 --- a/filter-accela/ftnoir_filter_accela_dialog.cpp +++ b/filter-accela/ftnoir_filter_accela_dialog.cpp @@ -42,7 +42,7 @@ dialog_accela::dialog_accela() QDialog dr; spline_widget r(&dr); dr.setWindowTitle("Accela rotation gain"); r.set_preview_only(true); r.setEnabled(true); - r.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); r.setConfig(&rot); + r.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); r.set_config(&rot); r.setFixedSize(1024, 600); dr.show(); dr.exec(); @@ -52,7 +52,7 @@ dialog_accela::dialog_accela() QDialog dt; spline_widget t(&dt); dt.setWindowTitle("Accela translation gain"); t.set_preview_only(true); t.setEnabled(true); - dt.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); t.setConfig(&pos); + dt.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); t.set_config(&pos); t.setFixedSize(1024, 600); dt.show(); dt.exec(); diff --git a/filter-ewma2/ftnoir_filter_ewma2.cpp b/filter-ewma2/ftnoir_filter_ewma2.cpp index 46c4796d..ce6cd040 100644 --- a/filter-ewma2/ftnoir_filter_ewma2.cpp +++ b/filter-ewma2/ftnoir_filter_ewma2.cpp @@ -48,7 +48,7 @@ void ewma::filter(const double *input, double *output) // scale curve .01->1 where 1.0 is sqrt(norm_noise). const double smoothing_scale_curve = *s.kSmoothingScaleCurve; // min/max smoothing .01->1 - const double min_smoothing = *s.kMinSmoothing; + const double min_smoothing{s.kMinSmoothing}; const double max_smoothing = std::fmax(min_smoothing, *s.kMaxSmoothing); // Calculate the new camera position. diff --git a/filter-kalman/kalman.cpp b/filter-kalman/kalman.cpp index f88d7f90..1960e0f6 100644 --- a/filter-kalman/kalman.cpp +++ b/filter-kalman/kalman.cpp @@ -192,7 +192,6 @@ void kalman::reset() for (int i = 0; i < 6; i++) { last_input[i] = 0; } - first_run = true; dt_since_last_input = 0; prev_slider_pos[0] = s.noise_pos_slider_value; @@ -311,7 +310,7 @@ double settings::map_slider_value(const slider_value& v_) | left_side_log10 */ - const int k = v * num_divisions; // in which division are we?! + const int k = int(v * num_divisions); // in which division are we?! const double f = v * num_divisions - k; // where in the division are we?! const double ff = f * 9. + 1.; const double multiplier = int(ff * 10.) / 10.; diff --git a/filter-kalman/kalman.h b/filter-kalman/kalman.h index 30f9e90f..2d8db86e 100644 --- a/filter-kalman/kalman.h +++ b/filter-kalman/kalman.h @@ -26,8 +26,8 @@ using namespace options; #include <QString> #include <QWidget> -static constexpr inline int NUM_STATE_DOF = 12; -static constexpr inline int NUM_MEASUREMENT_DOF = 6; +static constexpr int NUM_STATE_DOF = 12; +static constexpr int NUM_MEASUREMENT_DOF = 6; // These vectors are compile time fixed size, stack allocated using StateToMeasureMatrix = Eigen::Matrix<double, NUM_MEASUREMENT_DOF, NUM_STATE_DOF>; using StateMatrix = Eigen::Matrix<double, NUM_STATE_DOF, NUM_STATE_DOF>; @@ -86,11 +86,11 @@ struct settings : opts { value<slider_value> noise_rot_slider_value { b, "noise-rotation-slider", { .5, 0, 1 } }; value<slider_value> noise_pos_slider_value { b, "noise-position-slider", { .5, 0, 1 } }; - static constexpr inline double adaptivity_window_length = 0.25; // seconds - static constexpr inline double deadzone_scale = 8; - static constexpr inline double deadzone_exponent = 2.0; - static constexpr inline double process_sigma_pos = 0.5; - static constexpr inline double process_sigma_rot = 0.5; + static constexpr double adaptivity_window_length = 0.25; // seconds + static constexpr double deadzone_scale = 8; + static constexpr double deadzone_exponent = 2.0; + static constexpr double process_sigma_pos = 0.5; + static constexpr double process_sigma_rot = 0.5; static double map_slider_value(const slider_value &v); @@ -109,15 +109,19 @@ public: void center() override { reset(); } module_status initialize() override { return status_ok(); } - PoseVector last_input; - Timer timer; - bool first_run; double dt_since_last_input; - settings s; + PoseVector last_input; KalmanFilter kf; KalmanProcessNoiseScaler kf_adaptive_process_noise_cov; DeadzoneFilter dz_filter; - slider_value prev_slider_pos[2]; + settings s; + slider_value prev_slider_pos[2] { + *s.noise_pos_slider_value, + *s.noise_rot_slider_value, + }; + Timer timer; + + bool first_run = true; }; class kalmanDll : public Metadata diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index cb5e2bdc..db2f0b9e 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -5,7 +5,6 @@ target_link_libraries(${self} opentrack-logic opentrack-spline opentrack-pose-widget - opentrack-version ) # for process detector @@ -15,3 +14,7 @@ elseif(LINUX) otr_pkgconfig(${self} libprocps) endif() +if(NOT APPLE AND NOT WIN32) + target_compile_definitions(${self} PRIVATE -DOTR_X11_THREADS) + otr_pkgconfig(${self} x11) +endif() diff --git a/gui/init.cpp b/gui/init.cpp index 4609d535..77d90212 100644 --- a/gui/init.cpp +++ b/gui/init.cpp @@ -1,5 +1,3 @@ -#include "init.hpp" - /* Copyright (c) 2013-2017 Stanislaw Halik * * Permission to use, copy, modify, and/or distribute this software for any @@ -7,14 +5,12 @@ * copyright notice and this permission notice appear in all copies. */ -#if defined(Q_CREATOR_RUN) -# pragma clang diagnostic ignored "-Wmain" -#endif - +#include "init.hpp" #include "migration/migration.hpp" #include "options/options.hpp" using namespace options; #include "compat/library-path.hpp" +#include "compat/arch.hpp" #include <memory> #include <cstdlib> @@ -30,86 +26,129 @@ using namespace options; #include <QFile> #include <QFileDialog> #include <QString> -#include <QSysInfo> +#include <QOperatingSystemVersion> +#include <QMutex> #include <QDebug> -#if /* denormal control */ \ - /* GNU */ defined __x86_64__ || defined __SSE3__ || \ - /* MSVC */ defined _M_AMD64 || (defined _M_IX86_FP && _M_IX86_FP >= 2) - -# if defined __GNUG__ && defined __SSE2__ && !defined __SSE3__ && !defined __x86_64__ -# undef OTR_DENORM_DAZ -# include <emmintrin.h> -# else -# define OTR_DENORM_DAZ -# include <pmmintrin.h> -# endif -# include <cfloat> +#include <cfloat> +#include <cfenv> -# ifdef __APPLE__ -# include <fenv.h> -# endif +#ifdef __MINGW32__ +extern "C" __declspec(dllimport) unsigned __cdecl _controlfp(unsigned, unsigned); +#endif static void set_fp_mask() { -#ifdef OTR_DENORM_DAZ +#if defined OTR_ARCH_DENORM_DAZ _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); -#endif +#elif defined OTR_ARCH_DENORM_FTZ _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); - //_MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); +#endif + +#ifdef OTR_ARCH_FPU_MASK _MM_SET_EXCEPTION_MASK(_MM_MASK_MASK); +#endif #ifdef __APPLE__ fesetenv(FE_DFL_DISABLE_SSE_DENORMS_ENV); #endif -} -#else -static inline void set_fp_mask() {} + +#ifdef _WIN32 +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wreserved-id-macro" +# endif +# ifndef _DN_FLUSH +# define _DN_FLUSH 0x01000000 +# endif +# ifndef _MCW_DN +# define _MCW_DN 0x03000000 +# endif +# ifdef __clang__ +# pragma clang diagnostic pop +# endif + _controlfp(_DN_FLUSH, _MCW_DN); #endif +} -static void set_qt_style() +#ifdef OTR_X11_THREADS +#include <X11/Xlib.h> +static void enable_x11_threads() { -#if defined _WIN32 - if (QSysInfo::WindowsVersion == QSysInfo::WV_XP) - return; + (void)XInitThreads(); +} #endif +static void set_qt_style() +{ #if defined _WIN32 || defined __APPLE__ // our layouts on OSX make some control wrongly sized -sh 20160908 { - const char* const preferred[] { "fusion", "windowsvista", "macintosh" }; + const char* const preferred[] { +#ifdef __APPLE__ + "macintosh", "fusion", "windowsvista", "windows", +#else + "fusion", "windowsvista", "windows", "windowsxp", +#endif + }; for (const char* style_name : preferred) - { - QStyle* s = QStyleFactory::create(style_name); - if (s) + if (QStyle* s = QStyleFactory::create(style_name); s != nullptr) { QApplication::setStyle(s); break; } - } } #endif } +#include <string> + #ifdef _WIN32 +# include <windows.h> +# include <malloc.h> +#else +# include <alloca.h> +#endif static void qdebug_to_console(QtMsgType, const QMessageLogContext& ctx, const QString &msg) { - const unsigned short* const str_ = msg.utf16(); - const auto str = reinterpret_cast<wchar_t const*>(str_); - static_assert(sizeof(*str_) == sizeof(*str)); - - std::fflush(stderr); - if (ctx.function) - std::fprintf(stderr, "[%s:%d%s]: %ls\n", ctx.file, ctx.line, ctx.function, str); - else if (ctx.file) - std::fprintf(stderr, "[%s:%d]: %ls\n", ctx.file, ctx.line, str); +#ifdef _WIN32 + static_assert(sizeof(wchar_t) == sizeof(decltype(*msg.utf16()))); + + if (IsDebuggerPresent()) + { + static QMutex lock; + QMutexLocker l(&lock); + + const wchar_t* const bytes = (const wchar_t*)msg.utf16(); + + OutputDebugStringW(bytes); + OutputDebugStringW(L"\n"); + } else - std::fprintf(stderr, "%ls\n", str); - std::fflush(stderr); +#endif + { +#if defined _WIN32 && 1 + const wchar_t* const bytes = (const wchar_t*)msg.utf16(); +#else + unsigned len = (unsigned)msg.size()+1; + wchar_t* const bytes = (wchar_t*)alloca(len * sizeof(wchar_t)); + bytes[len-1] = 0; + (void)msg.toWCharArray(bytes); +#endif + { + if (ctx.file) + std::fprintf(stderr, "[%s:%d]: %ls\n", ctx.file, ctx.line, bytes); + else + std::fprintf(stderr, "%ls\n", bytes); + } + std::fflush(stderr); + } } +#ifdef _WIN32 + static void add_win32_path() { // see https://software.intel.com/en-us/articles/limitation-to-the-length-of-the-system-path-variable @@ -160,14 +199,13 @@ static void add_win32_path() } } -#include <windows.h> - static void attach_parent_console() { - std::fflush(stdin); - std::fflush(stderr); + if (GetConsoleWindow() != nullptr) + return; - (void)qInstallMessageHandler(qdebug_to_console); + fflush(stdin); + fflush(stderr); if (AttachConsole(ATTACH_PARENT_PROCESS)) { @@ -176,7 +214,7 @@ static void attach_parent_console() _wfreopen(L"CON", L"r", stdin); freopen("CON", "w", stdout); freopen("CON", "w", stderr); - freopen("CON", "w", stderr); + freopen("CON", "r", stdin); // skip prompt in cmd.exe window fprintf(stderr, "\n"); @@ -200,12 +238,17 @@ static int run_window(std::unique_ptr<QWidget> main_window) return status; } -int otr_main(int argc, char** argv, std::function<QWidget*()> const& make_main_window) +int otr_main(int argc, char** argv, std::function<std::unique_ptr<QWidget>()> const& make_main_window) { set_fp_mask(); +#ifdef OTR_X11_THREADS + enable_x11_threads(); +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QCoreApplication::setAttribute(Qt::AA_X11InitThreads, true); +#endif + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QApplication app(argc, argv); @@ -214,6 +257,8 @@ int otr_main(int argc, char** argv, std::function<QWidget*()> const& make_main_w attach_parent_console(); #endif + (void)qInstallMessageHandler(qdebug_to_console); + QDir::setCurrent(OPENTRACK_BASE_PATH); set_qt_style(); @@ -241,7 +286,7 @@ int otr_main(int argc, char** argv, std::function<QWidget*()> const& make_main_w } } - int ret = run_window(std::unique_ptr<QWidget>(make_main_window())); + int ret = run_window(make_main_window()); #if 0 // msvc crashes in Qt plugin system's dtor diff --git a/gui/init.hpp b/gui/init.hpp index 7c437bcb..4274f437 100644 --- a/gui/init.hpp +++ b/gui/init.hpp @@ -3,9 +3,10 @@ #include "export.hpp" #include <functional> +#include <memory> #include <QWidget> -OTR_GUI_EXPORT int otr_main(int argc, char** argv, std::function<QWidget*()> const& make_main_window); +OTR_GUI_EXPORT int otr_main(int argc, char** argv, std::function<std::unique_ptr<QWidget>()> const& make_main_window); // XXX TODO need split MainWindow into mixins each implementing part of the functionality diff --git a/gui/keyboard.cpp b/gui/keyboard.cpp index dbc3ab3a..90849bab 100644 --- a/gui/keyboard.cpp +++ b/gui/keyboard.cpp @@ -1,39 +1,43 @@ +#undef NDEBUG +#include <cassert> + #include "keyboard.h" #include <QDebug> #ifdef _WIN32 -auto keyboard_listener::make_token() + +void keyboard_listener::receive_key(const Key& k) { - return [this](const Key& k) { - if(!k.guid.isEmpty()) - { - int mods = 0; - if (k.alt) mods |= Qt::AltModifier; - if (k.shift) mods |= Qt::ShiftModifier; - if (k.ctrl) mods |= Qt::ControlModifier; - emit joystick_button_pressed(k.guid, k.keycode | mods, k.held); - } - else - { - Qt::KeyboardModifiers m; - QKeySequence k_; - if (win_key::to_qt(k, k_, m)) - emit key_pressed({ int(m) | int(k_) }); - } - }; + if(!k.guid.isEmpty()) + { + int mods = 0; + if (k.alt) mods |= Qt::AltModifier; + if (k.shift) mods |= Qt::ShiftModifier; + if (k.ctrl) mods |= Qt::ControlModifier; + + emit joystick_button_pressed(k.guid, k.keycode | mods, k.held); + } + else + { + Qt::KeyboardModifiers m; + QKeySequence k_; + + if (win_key::to_qt(k, k_, m)) + for (unsigned i = 0; i < (unsigned)k_.count(); i++) + emit key_pressed(QKeySequence(int(m) | k_[i])); + } } #endif -keyboard_listener::keyboard_listener(QWidget* parent) : - QDialog(parent) -#ifdef _WIN32 - , token(make_token()) -#endif +keyboard_listener::keyboard_listener(QWidget* parent) : QDialog(parent) { ui.setupUi(this); setFocusPolicy(Qt::StrongFocus); +#ifdef _WIN32 + (void)token; +#endif } #if !defined _WIN32 diff --git a/gui/keyboard.h b/gui/keyboard.h index 0a06a3af..ea35d372 100644 --- a/gui/keyboard.h +++ b/gui/keyboard.h @@ -22,13 +22,17 @@ class OTR_GUI_EXPORT keyboard_listener : public QDialog { Q_OBJECT - Ui_keyboard_listener ui; + #ifdef _WIN32 - auto make_token(); - KeybindingWorker::Token token; + void receive_key(const Key& k); + + KeybindingWorker::Token token{[this](const Key& k) {receive_key(k);}}; #else void keyPressEvent(QKeyEvent* event) override; #endif + + Ui_keyboard_listener ui; + public: keyboard_listener(QWidget* parent = nullptr); signals: diff --git a/gui/lang/nl_NL.ts b/gui/lang/nl_NL.ts index 64ee0621..60b84189 100644 --- a/gui/lang/nl_NL.ts +++ b/gui/lang/nl_NL.ts @@ -121,6 +121,10 @@ Press "clear calibration" to remove any calibration data pertaining to <source>%1°</source> <translation type="unfinished"></translation> </message> + <message> + <source>%1 cm</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>options_dialog</name> diff --git a/gui/lang/ru_RU.ts b/gui/lang/ru_RU.ts index 8e3ca4a7..d40b4775 100644 --- a/gui/lang/ru_RU.ts +++ b/gui/lang/ru_RU.ts @@ -121,6 +121,10 @@ Press "clear calibration" to remove any calibration data pertaining to <source>%1°</source> <translation></translation> </message> + <message> + <source>%1 cm</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>options_dialog</name> diff --git a/gui/lang/stub.ts b/gui/lang/stub.ts index 932f4348..b383a0d8 100644 --- a/gui/lang/stub.ts +++ b/gui/lang/stub.ts @@ -121,6 +121,10 @@ Press "clear calibration" to remove any calibration data pertaining to <source>%1°</source> <translation type="unfinished"></translation> </message> + <message> + <source>%1 cm</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>options_dialog</name> diff --git a/gui/lang/zh_CN.ts b/gui/lang/zh_CN.ts index 45414302..fa2d512c 100644 --- a/gui/lang/zh_CN.ts +++ b/gui/lang/zh_CN.ts @@ -122,6 +122,10 @@ Press "clear calibration" to remove any calibration data pertaining to <source>%1°</source> <translation></translation> </message> + <message> + <source>%1 cm</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>options_dialog</name> diff --git a/gui/mapping-dialog.cpp b/gui/mapping-dialog.cpp index e57cead2..6dc6d15f 100644 --- a/gui/mapping-dialog.cpp +++ b/gui/mapping-dialog.cpp @@ -94,20 +94,18 @@ void mapping_dialog::load() { nullptr, Yaw, nullptr, false } }; - ui.max_pitch_output->setItemData(0, int(axis_opts::o_r180)); - ui.max_pitch_output->setItemData(1, int(axis_opts::o_r90)); - using a = axis_opts::max_clamp; + ui.max_pitch_output->setItemData(0, int(a::o_r180)); + ui.max_pitch_output->setItemData(1, int(a::o_r90)); + for (QComboBox* x : { ui.max_yaw_rotation, ui.max_pitch_rotation, ui.max_roll_rotation }) for (a y : { a::r180, a::r90, a::r60, a::r45, a::r30, a::r25, a::r20, a::r15, a::r10 }) x->addItem(tr("%1°").arg(y), y); for (QComboBox* x : { ui.max_x_translation, ui.max_y_translation, ui.max_z_translation }) for (a y : { a::t30, a::t20, a::t15, a::t10, a::t100 }) - x->addItem(QStringLiteral("%1 cm").arg(int(y)), y); - - // XXX TODO add tie_setting overload for spline_widget!!! -sh 20171020 + x->addItem(tr("%1 cm").arg(int(y)), y); for (int i = 0; qfcs[i].qfc; i++) { @@ -121,20 +119,16 @@ void mapping_dialog::load() { connect(&axis.opts.altp, value_::value_changed<bool>(), - this, - [&](bool f) {qfc.setEnabled(f); qfc.force_redraw();}); + this, [&](bool f) { qfc.setEnabled(f); }); qfc.setEnabled(axis.opts.altp); - qfc.force_redraw(); } - using c = axis_opts::max_clamp; - auto update_xstep = [&qfc](int clamp_x) { int value; - if (clamp_x <= c::r20) + if (clamp_x <= a::r30) value = 1; - else if (clamp_x <= c::r30) + else if (clamp_x <= a::r45) value = 5; else value = 10; @@ -147,11 +141,11 @@ void mapping_dialog::load() switch (clamp_y) { default: - case c::o_r180: - value = 15; break; - case c::o_r90: + case a::o_r180: + value = 20; break; + case a::o_r90: value = 10; break; - case c::o_t75: + case a::o_t75: value = 5; break; } qfc.set_y_step(value); @@ -163,7 +157,7 @@ void mapping_dialog::load() connect(&axis.opts.clamp_y_, value_::value_changed<int>(), &qfc, update_ystep); // force signal to avoid duplicating the slot's logic - qfc.setConfig(&conf); + qfc.set_config(&conf); update_xstep(axis.opts.clamp_x_); update_ystep(axis.opts.clamp_y_); diff --git a/gui/process_detector.cpp b/gui/process_detector.cpp index 0ff05f93..b17d5dd0 100644 --- a/gui/process_detector.cpp +++ b/gui/process_detector.cpp @@ -20,8 +20,8 @@ #include <QSettings> #include <QtEvents> -static constexpr inline auto RECORD_SEPARATOR = QChar(char(0x1e)); // RS ^] -static constexpr inline auto UNIT_SEPARATOR = QChar(char(0x1f)); // US ^_ +static constexpr auto RECORD_SEPARATOR = QChar(char(0x1e)); // RS ^] +static constexpr auto UNIT_SEPARATOR = QChar(char(0x1f)); // US ^_ using namespace options; using namespace options::globals; @@ -106,7 +106,7 @@ int process_detector::add_row(QString const& exe_name, QString const& profile) { BrowseButton* b = new BrowseButton(twi); b->setText("..."); - QObject::connect(b, SIGNAL(clicked()), b, SLOT(browse())); + QObject::connect(b, &BrowseButton::clicked, b, &BrowseButton::browse); ui.tableWidget->setCellWidget(i, 2, b); } @@ -174,14 +174,14 @@ void process_detector::remove() bool process_detector_worker::should_stop() { - if (last_exe_name == "") + if (last_exe_name == QString()) return false; proc_detector_settings s; if (!s.is_enabled()) { - last_exe_name = ""; + last_exe_name = QString{}; return false; } @@ -190,17 +190,17 @@ bool process_detector_worker::should_stop() if (exe_list.contains(last_exe_name)) return false; - last_exe_name = ""; + last_exe_name = QString{}; return true; } -bool process_detector_worker::config_to_start(QString& str) +bool process_detector_worker::profile_to_start(QString& str) { proc_detector_settings s; if (!s.is_enabled()) { - last_exe_name = ""; + last_exe_name = QString{}; return false; } @@ -218,9 +218,9 @@ bool process_detector_worker::config_to_start(QString& str) { if (filenames.contains(name)) { - last_exe_name = name; str = filenames[name]; - return str != ""; + last_exe_name = std::move(name); + return str != QString{}; } } diff --git a/gui/process_detector.h b/gui/process_detector.h index 7439b19b..4e8842c6 100644 --- a/gui/process_detector.h +++ b/gui/process_detector.h @@ -63,7 +63,7 @@ class OTR_GUI_EXPORT process_detector_worker : QObject proc_detector_settings s; QString last_exe_name; public: - bool config_to_start(QString& s); + bool profile_to_start(QString& str); bool should_stop(); }; diff --git a/gui/settings.cpp b/gui/settings.cpp index 711fb0f0..79a8dbfb 100644 --- a/gui/settings.cpp +++ b/gui/settings.cpp @@ -45,7 +45,7 @@ void options_dialog::set_disable_translation_state(bool value) }); } -options_dialog::options_dialog(std::function<void(bool)>&& pause_keybindings) : +options_dialog::options_dialog(std::function<void(bool)> pause_keybindings) : pause_keybindings(std::move(pause_keybindings)) { ui.setupUi(this); diff --git a/gui/settings.hpp b/gui/settings.hpp index 50245a59..8ef5aa2d 100644 --- a/gui/settings.hpp +++ b/gui/settings.hpp @@ -17,7 +17,7 @@ class OTR_GUI_EXPORT options_dialog : public QDialog signals: void closing(); public: - explicit options_dialog(std::function<void(bool)>&& pause_keybindings); + explicit options_dialog(std::function<void(bool)> pause_keybindings); private: main_settings main; std::function<void(bool)> pause_keybindings; diff --git a/installer/opentrack-installer.iss b/installer/opentrack-installer.iss index e6551863..2f98aaac 100644 --- a/installer/opentrack-installer.iss +++ b/installer/opentrack-installer.iss @@ -1,7 +1,7 @@ ; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! -#include "../build/opentrack-version.h" +#include "../build/opentrack-version.hxx" #define MyAppName "opentrack" #define MyAppVersion OPENTRACK_VERSION #define MyAppPublisher "opentrack" @@ -45,7 +45,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl" Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] -Source: "..\build\install\*"; DestDir: "{app}"; Flags: ignoreversion createallsubdirs recursesubdirs +Source: "..\build-clion-msvc-dev\install\*"; DestDir: "{app}"; Flags: ignoreversion createallsubdirs recursesubdirs [Icons] Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" diff --git a/logic/extensions.cpp b/logic/extensions.cpp index 438f6dde..1c855923 100644 --- a/logic/extensions.cpp +++ b/logic/extensions.cpp @@ -39,7 +39,7 @@ event_handler::event_handler(Modules::dylib_list const& extensions) : ext_bundle { std::shared_ptr<IExtension> ext(reinterpret_cast<IExtension*>(lib->Constructor())); std::shared_ptr<IExtensionDialog> dlg(reinterpret_cast<IExtensionDialog*>(lib->Dialog())); - std::shared_ptr<Metadata_> m(reinterpret_cast<Metadata_*>(lib->Meta())); + std::shared_ptr<Metadata_> m(lib->Meta()); const ext_mask mask = ext->hook_types(); diff --git a/logic/main-settings.cpp b/logic/main-settings.cpp index 2bf0ad1f..3865e602 100644 --- a/logic/main-settings.cpp +++ b/logic/main-settings.cpp @@ -4,57 +4,8 @@ namespace main_settings_impl { using namespace options; -main_settings::main_settings() : - b(make_bundle("opentrack-ui")), - b_map(make_bundle("opentrack-mappings")), - a_x("x", TX), - a_y("y", TY), - a_z("z", TZ), - a_yaw("yaw", Yaw), - a_pitch("pitch", Pitch), - a_roll("roll", Roll), - all_axis_opts { &a_x, &a_y, &a_z, &a_yaw, &a_pitch, &a_roll }, - reltrans_disable_tx(b, "compensate-translation-disable-x-axis", false), - reltrans_disable_ty(b, "compensate-translation-disable-y-axis", false), - reltrans_disable_tz(b, "compensate-translation-disable-z-axis", false), - reltrans_disable_src_yaw(b, "compensate-translation-disable-source-yaw", false), - reltrans_disable_src_pitch(b, "compensate-translation-disable-source-pitch", false), - reltrans_disable_src_roll(b, "compensate-translation-disable-source-roll", false), - tray_enabled(b, "use-system-tray", false), - tray_start(b, "start-in-tray", false), - center_at_startup(b, "center-at-startup", true), - neck_z(b, "neck-depth", 0), - neck_enable(b, "neck-enable", false), - key_start_tracking1(b, "start-tracking"), - key_start_tracking2(b, "start-tracking-alt"), - key_stop_tracking1(b, "stop-tracking"), - key_stop_tracking2(b, "stop-tracking-alt"), - key_toggle_tracking1(b, "toggle-tracking"), - key_toggle_tracking2(b, "toggle-tracking-alt"), - key_restart_tracking1(b, "restart-tracking"), - key_restart_tracking2(b, "restart-tracking-alt"), - key_center1(b, "center"), - key_center2(b, "center-alt"), - key_toggle1(b, "toggle"), - key_toggle2(b, "toggle-alt"), - key_zero1(b, "zero"), - key_zero2(b, "zero-alt"), - key_toggle_press1(b, "toggle-press"), - key_toggle_press2(b, "toggle-press-alt"), - key_zero_press1(b, "zero-press"), - key_zero_press2(b, "zero-press-alt"), - tracklogging_enabled(b, "tracklogging-enabled", false), - tracklogging_filename(b, "tracklogging-filename", QString()) -{ -} - -module_settings::module_settings() : - b(make_bundle("modules")), - tracker_dll(b, "tracker-dll", "pt"), - filter_dll(b, "filter-dll", "accela"), - protocol_dll(b, "protocol-dll", "freetrack") -{ -} +main_settings::main_settings() = default; +module_settings::module_settings() = default; key_opts::key_opts(bundle b, const QString& name) : keycode(b, QString("keycode-%1").arg(name), ""), @@ -66,9 +17,9 @@ key_opts& key_opts::operator=(const key_opts& x) { if (&x != this) { - keycode = x.keycode(); - guid = x.guid(); - button = x.button(); + keycode = x.keycode; + guid = x.guid; + button = x.button; } return *this; diff --git a/logic/main-settings.hpp b/logic/main-settings.hpp index 41edf3e6..8fef7ea7 100644 --- a/logic/main-settings.hpp +++ b/logic/main-settings.hpp @@ -37,36 +37,67 @@ struct OTR_LOGIC_EXPORT key_opts struct OTR_LOGIC_EXPORT module_settings { - bundle b; - value<QString> tracker_dll, filter_dll, protocol_dll; + bundle b { make_bundle("modules") }; + value<QString> tracker_dll { b, "tracker-dll", "pt" }; + value<QString> filter_dll { b, "filter-dll", "accela" }; + value<QString> protocol_dll { b, "protocol-dll", "freetrack" }; module_settings(); }; struct OTR_LOGIC_EXPORT main_settings final { - bundle b, b_map; - axis_opts a_x, a_y, a_z; - axis_opts a_yaw, a_pitch, a_roll; - axis_opts* all_axis_opts[6]; + bundle b { make_bundle("opentrack-ui") }; + bundle b_map { make_bundle("opentrack-mappings") }; + axis_opts a_x{ "x", TX }, a_y { "y", TY }, a_z { "z", TZ }; + axis_opts a_yaw{ "yaw", Yaw }, a_pitch { "pitch", Pitch }, a_roll { "roll", Roll }; + axis_opts* all_axis_opts[6] { &a_x, &a_y, &a_z, &a_yaw, &a_pitch, &a_roll }; value<reltrans_state> reltrans_mode { b, "relative-translation-mode", reltrans_disabled }; - value<bool> reltrans_disable_tx, reltrans_disable_ty, reltrans_disable_tz; - value<bool> reltrans_disable_src_yaw, reltrans_disable_src_pitch, reltrans_disable_src_roll; - value<bool> tray_enabled, tray_start; - value<bool> center_at_startup; + + value<bool> reltrans_disable_tx { b, "compensate-translation-disable-x-axis", false }; + value<bool> reltrans_disable_ty { b, "compensate-translation-disable-y-axis", false }; + value<bool> reltrans_disable_tz { b, "compensate-translation-disable-z-axis", false }; + + value<bool> reltrans_disable_src_yaw { b, "compensate-translation-disable-source-yaw", false }; + value<bool> reltrans_disable_src_pitch { b, "compensate-translation-disable-source-pitch", false }; + value<bool> reltrans_disable_src_roll { b, "compensate-translation-disable-source-roll", false }; + + value<bool> tray_enabled { b, "use-system-tray", false }; + value<bool> tray_start { b, "start-in-tray", false }; + + value<bool> center_at_startup { b, "center-at-startup", true }; //value<int> center_method; - value<int> neck_z; - value<bool> neck_enable; - key_opts key_start_tracking1, key_start_tracking2; - key_opts key_stop_tracking1, key_stop_tracking2; - key_opts key_toggle_tracking1, key_toggle_tracking2; - key_opts key_restart_tracking1, key_restart_tracking2; - key_opts key_center1, key_center2; - key_opts key_toggle1, key_toggle2; - key_opts key_zero1, key_zero2; - key_opts key_toggle_press1, key_toggle_press2; - key_opts key_zero_press1, key_zero_press2; - value<bool> tracklogging_enabled; - value<QString> tracklogging_filename; + value<int> neck_z { b, "neck-depth", 0 }; + value<bool> neck_enable { b, "neck-enable", false }; + + key_opts key_start_tracking1 { b, "start-tracking" }; + key_opts key_start_tracking2 { b, "start-tracking-alt" }; + + key_opts key_stop_tracking1 { b, "stop-tracking" }; + key_opts key_stop_tracking2 { b, "stop-tracking-alt" }; + + key_opts key_toggle_tracking1 { b, "toggle-tracking" }; + key_opts key_toggle_tracking2 { b, "toggle-tracking-alt" }; + + key_opts key_restart_tracking1 { b, "restart-tracking" }; + key_opts key_restart_tracking2 { b, "restart-tracking-alt" }; + + key_opts key_center1 { b, "center" }; + key_opts key_center2 { b, "center-alt" }; + + key_opts key_toggle1 { b, "toggle" }; + key_opts key_toggle2 { b, "toggle-alt" }; + + key_opts key_zero1 { b, "zero" }; + key_opts key_zero2 { b, "zero-alt" }; + + key_opts key_toggle_press1 { b, "toggle-press" }; + key_opts key_toggle_press2 { b, "toggle-press-alt" }; + + key_opts key_zero_press1 { b, "zero-press" }; + key_opts key_zero_press2 { b, "zero-press-alt" }; + + value<bool> tracklogging_enabled { b, "tracklogging-enabled", false }; + value<QString> tracklogging_filename { b, "tracklogging-filename", {} }; main_settings(); }; diff --git a/logic/pipeline.cpp b/logic/pipeline.cpp index 021a576b..bc8026ed 100644 --- a/logic/pipeline.cpp +++ b/logic/pipeline.cpp @@ -26,26 +26,18 @@ #ifdef _WIN32 # include <windows.h> -#endif - -#if defined __llvm__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wlanguage-extension-token" -# pragma clang diagnostic ignored "-Wunknown-pragmas" +# include <mmsystem.h> #endif namespace pipeline_impl { -static constexpr inline double r2d = 180 / M_PI; -static constexpr inline double d2r = M_PI / 180; - reltrans::reltrans() = default; void reltrans::on_center() { interp_pos = { 0, 0, 0 }; in_zone = false; - cur = false; + moving_to_reltans = false; } euler_t reltrans::rotate(const rmat& R, const euler_t& in, vec3_bool disable) const @@ -83,20 +75,17 @@ Pose reltrans::apply_pipeline(reltrans_state state, const Pose& value, if (state != reltrans_disabled) { - const bool in_zone_ = progn( - if (state == reltrans_non_center) - { - const bool looking_down = value(Pitch) < 20; - return looking_down ? std::fabs(value(Yaw)) > 35 : std::fabs(value(Yaw)) > 65; - } - else - return true; - ); + bool in_zone_ = true; + if (state == reltrans_non_center) + { + const bool looking_down = value(Pitch) < 20; + in_zone_ = looking_down ? std::fabs(value(Yaw)) > 35 : std::fabs(value(Yaw)) > 65; + } - if (!cur && in_zone != in_zone_) + if (!moving_to_reltans && in_zone != in_zone_) { //qDebug() << "reltrans-interp: START" << tcomp_in_zone_; - cur = true; + moving_to_reltans = true; interp_timer.start(); interp_phase_timer.start(); RC_stage = 0; @@ -107,6 +96,8 @@ Pose reltrans::apply_pipeline(reltrans_state state, const Pose& value, // only when looking behind or downward if (in_zone) { + constexpr double d2r = M_PI / 180; + const rmat R = euler_to_rmat( euler_t(value(Yaw) * d2r * !disable(Yaw), value(Pitch) * d2r * !disable(Pitch), @@ -124,12 +115,12 @@ Pose reltrans::apply_pipeline(reltrans_state state, const Pose& value, } } - if (cur) + if (moving_to_reltans) { const double dt = interp_timer.elapsed_seconds(); - static constexpr float RC_stages[] = { 2, 1, .5, .1, .05 }; - static constexpr float RC_time_deltas[] = { 1, .25, .25, 2 }; + static constexpr double RC_stages[] = { 2, 1, .5, .1, .05 }; + static constexpr double RC_time_deltas[] = { 1, .25, .25, 2 }; interp_timer.start(); @@ -156,7 +147,7 @@ Pose reltrans::apply_pipeline(reltrans_state state, const Pose& value, if (delta < eps) { //qDebug() << "reltrans-interp: STOP"; - cur = false; + moving_to_reltans = false; } } else @@ -164,7 +155,7 @@ Pose reltrans::apply_pipeline(reltrans_state state, const Pose& value, } else { - cur = false; + moving_to_reltans = false; in_zone = false; } @@ -204,7 +195,7 @@ double pipeline::map(double pos, Map& axis) axis.spline_main.set_tracking_active(!altp); axis.spline_alt.set_tracking_active(altp); auto& fc = altp ? axis.spline_alt : axis.spline_main; - return double(fc.get_value(pos)); + return fc.get_value(pos); } //#define NO_NAN_CHECK @@ -213,10 +204,10 @@ double pipeline::map(double pos, Map& axis) #ifndef NO_NAN_CHECK template<int u, int w> -static bool is_nan(const dmat<u,w>& r) +static inline bool is_nan(const dmat<u,w>& r) { - for (int i = 0; i < u; i++) - for (int j = 0; j < w; j++) + for (unsigned i = 0; i < u; i++) + for (unsigned j = 0; j < w; j++) { int val = std::fpclassify(r(i, j)); if (val == FP_NAN || val == FP_INFINITE) @@ -226,21 +217,8 @@ static bool is_nan(const dmat<u,w>& r) return false; } -template<typename x> -static inline -bool nan_check_(const x& datum) -{ - return is_nan(datum); -} - -template<typename x, typename y, typename... xs> -static inline -bool nan_check_(const x& datum, const y& next, const xs&... rest) -{ - return nan_check_(datum) || nan_check_(next, rest...); -} - -static void emit_nan_check_msg(const char* text, const char* fun, int line) +static never_inline +void emit_nan_check_msg(const char* text, const char* fun, int line) { eval_once( qDebug() << "nan check failed" @@ -251,33 +229,31 @@ static void emit_nan_check_msg(const char* text, const char* fun, int line) } template<typename... xs> -static cc_noinline +static never_inline bool maybe_nan(const char* text, const char* fun, int line, const xs&... vals) { - if (nan_check_(vals...)) - { + bool ret = (is_nan(vals) || ... || false); + + if (ret) emit_nan_check_msg(text, fun, line); - return true; - } - return false; -} -// for MSVC `else' is like `unlikely' for GNU + return ret; +} #define nan_check(...) \ do \ { \ - if (likely(!maybe_nan(#__VA_ARGS__, cc_function_name, __LINE__, __VA_ARGS__))) \ + if (likely(!maybe_nan(#__VA_ARGS__, function_name, __LINE__, __VA_ARGS__))) \ (void)0; \ else \ goto error; \ - } while (false) + } \ + while (false) #else # define nan_check(...) (void)(__VA_ARGS__) #endif - bool pipeline::maybe_enable_center_on_tracking_started() { if (!tracking_started) @@ -299,7 +275,7 @@ bool pipeline::maybe_enable_center_on_tracking_started() return false; } -void pipeline::set_center_pose(const Pose& value, bool own_center_logic) +void pipeline::maybe_set_center_pose(const Pose& value, bool own_center_logic) { if (b.get(f_center | f_held_center)) { @@ -309,26 +285,12 @@ void pipeline::set_center_pose(const Pose& value, bool own_center_logic) libs.pFilter->center(); if (own_center_logic) - { - scaled_state.inv_rot_center = rmat::eye(); - scaled_state.t_center = {}; - } + center = {}; else - { - scaled_state.inv_rot_center = scaled_state.rotation.t(); - scaled_state.t_center = (const double*)(value); - } + center = value; } } -void pipeline::store_tracker_pose(const Pose& value) -{ - // alas, this is poor man's gimbal lock "prevention" - // this is some kind of "canonical" representation, - // if we can even talk of one - scaled_state.rotation = euler_to_rmat(scale_inv_c * d2r * euler_t(&value[Yaw])); -} - Pose pipeline::clamp_value(Pose value) const { // hatire, udp, and freepie trackers can mess up here @@ -347,28 +309,14 @@ Pose pipeline::clamp_value(Pose value) const Pose pipeline::apply_center(Pose value) const { - euler_t T = euler_t(value) - scaled_state.t_center; - euler_t R = scale_c * rmat_to_euler(scaled_state.rotation * scaled_state.inv_rot_center); - - // XXX check these lines, it's been here forever - T = rel.rotate(euler_to_rmat(R), T, {}); + // this is incorrect but people like it + value = value - center; - for (int i = 0; i < 3; i++) - { + for (int i = 0; i < 6; i++) // don't invert after reltrans // inverting here doesn't break centering - - if (m(i+3).opts.invert) - R(i) = -R(i); if (m(i).opts.invert) - T(i) = -T(i); - } - - for (int i = 0; i < 3; i++) - { - value(i) = T(i); - value(i+3) = R(i) * r2d; - } + value(i) = -value(i); return value; } @@ -408,7 +356,6 @@ Pose pipeline::maybe_apply_filter(const Pose& value) const Pose pipeline::apply_zero_pos(Pose value) const { - // custom zero position for (int i = 0; i < 6; i++) value(i) += m(i).opts.zero * (m(i).opts.invert ? -1 : 1); @@ -466,11 +413,9 @@ void pipeline::logic() value = clamp_value(value); { - store_tracker_pose(value); - maybe_enable_center_on_tracking_started(); - set_center_pose(value, own_center_logic); - value = apply_center(value); + maybe_set_center_pose(value, own_center_logic); + value = clamp_value(apply_center(value)); // "corrected" - after various transformations to account for camera position logger.write_pose(value); @@ -480,10 +425,11 @@ void pipeline::logic() ev.run_events(EV::ev_before_filter, value); // we must proceed with all the filtering since the filter // needs fresh values to prevent deconvergence - Pose tmp = maybe_apply_filter(value); - nan_check(tmp); - if (!center_ordered) - value = tmp; + if (center_ordered) + (void)maybe_apply_filter(value); + else + value = maybe_apply_filter(value); + nan_check(value); logger.write_pose(value); // "filtered" } @@ -631,7 +577,7 @@ void pipeline::run() void pipeline::raw_and_mapped_pose(double* mapped, double* raw) const { - QMutexLocker foo(&const_cast<pipeline&>(*this).mtx); + QMutexLocker foo(&mtx); for (int i = 0; i < 6; i++) { @@ -655,31 +601,28 @@ void pipeline::toggle_enabled() { b.negate(f_enabled_p); } void bits::set(bit_flags flag, bool val) { - const unsigned flag_ = unsigned(flag); - const unsigned val_ = unsigned(val); - unsigned b_ = 0; + QMutexLocker l(&lock); - for (;;) - if (b.compare_exchange_weak(b_, unsigned((b_ & ~flag_) | (flag_ * val_)))) - break; + flags &= ~flag; + if (val) + flags |= flag; } void bits::negate(bit_flags flag) { - const unsigned flag_= flag; - unsigned b_ = 0; + QMutexLocker l(&lock); - for (;;) - if (b.compare_exchange_weak(b_, b_ ^ flag_)) - break; + flags ^= flag; } bool bits::get(bit_flags flag) { - return !!(b & flag); + QMutexLocker l(&lock); + + return !!(flags & flag); } -bits::bits() : b(0u) +bits::bits() { set(f_center, false); set(f_held_center, false); @@ -689,7 +632,3 @@ bits::bits() : b(0u) } } // ns pipeline_impl - -#if defined __llvm__ -# pragma clang diagnostic pop -#endif diff --git a/logic/pipeline.hpp b/logic/pipeline.hpp index 91dfc668..545a7836 100644 --- a/logic/pipeline.hpp +++ b/logic/pipeline.hpp @@ -31,16 +31,6 @@ using namespace time_units; using vec6_bool = Mat<bool, 6, 1>; using vec3_bool = Mat<bool, 6, 1>; -static constexpr inline double scale_c = 8; -static constexpr inline double scale_inv_c = 1/scale_c; - -struct state_ -{ - rmat inv_rot_center { rmat::eye() }; - rmat rotation { rmat::eye() }; - euler_t t_center; -}; - class reltrans { euler_t interp_pos; @@ -51,7 +41,7 @@ class reltrans Timer interp_phase_timer; unsigned RC_stage = 0; - bool cur = false; + bool moving_to_reltans = false; bool in_zone = false; public: @@ -68,6 +58,7 @@ public: using namespace time_units; enum bit_flags : unsigned { + f_none = 0, f_center = 1 << 0, f_held_center = 1 << 1, f_enabled_h = 1 << 2, @@ -77,7 +68,8 @@ enum bit_flags : unsigned { struct OTR_LOGIC_EXPORT bits { - std::atomic<unsigned> b; + bit_flags flags{0}; + QMutex lock; void set(bit_flags flag, bool val); void negate(bit_flags flag); @@ -91,13 +83,13 @@ class OTR_LOGIC_EXPORT pipeline : private QThread { Q_OBJECT - QMutex mtx; + mutable QMutex mtx; main_settings s; Mappings& m; event_handler& ev; Timer t; - Pose output_pose, raw_6dof, last_mapped, last_raw; + Pose output_pose, raw_6dof; Pose newpose; runtime_libraries const& libs; @@ -108,8 +100,7 @@ class OTR_LOGIC_EXPORT pipeline : private QThread reltrans rel; - //state_ state, scaled_state; - state_ scaled_state; + Pose center; ns backlog_time {}; @@ -119,8 +110,7 @@ class OTR_LOGIC_EXPORT pipeline : private QThread void logic(); void run() override; bool maybe_enable_center_on_tracking_started(); - void set_center_pose(const Pose& value, bool own_center_logic); - void store_tracker_pose(const Pose& value); + void maybe_set_center_pose(const Pose& value, bool own_center_logic); Pose clamp_value(Pose value) const; Pose apply_center(Pose value) const; std::tuple<Pose, Pose, vec6_bool> get_selected_axis_values(const Pose& newpose) const; diff --git a/logic/runtime-libraries.cpp b/logic/runtime-libraries.cpp index b91c9635..754f52cd 100644 --- a/logic/runtime-libraries.cpp +++ b/logic/runtime-libraries.cpp @@ -3,10 +3,16 @@ #include <QMessageBox> #include <QDebug> +#ifdef __clang__ +# pragma clang diagnostic ignored "-Wcomma" +#endif + runtime_libraries::runtime_libraries(QFrame* frame, dylibptr t, dylibptr p, dylibptr f) { + auto error = [](const QString& msg) { return module_status_mixin::error(msg); }; + module_status status = - module_status_mixin::error(tr("Library load failure")); + error(tr("Library load failure")); using namespace options; @@ -22,8 +28,8 @@ runtime_libraries::runtime_libraries(QFrame* frame, dylibptr t, dylibptr p, dyli if(status = pProtocol->initialize(), !status.is_ok()) { - status = tr("Error occurred while loading protocol %1\n\n%2\n") - .arg(p->name, status.error); + status = error(tr("Error occurred while loading protocol %1\n\n%2\n") + .arg(p->name, status.error)); goto end; } @@ -36,7 +42,7 @@ runtime_libraries::runtime_libraries(QFrame* frame, dylibptr t, dylibptr p, dyli goto end; } - if (f && !pFilter) + if (f && f->Constructor && !pFilter) { qDebug() << "filter load failure"; goto end; @@ -45,15 +51,15 @@ runtime_libraries::runtime_libraries(QFrame* frame, dylibptr t, dylibptr p, dyli if (pFilter) if(status = pFilter->initialize(), !status.is_ok()) { - status = tr("Error occurred while loading filter %1\n\n%2\n") - .arg(f->name, status.error); + status = error(tr("Error occurred while loading filter %1\n\n%2\n") + .arg(f->name, status.error)); goto end; } if (status = pTracker->start_tracker(frame), !status.is_ok()) { - status = tr("Error occurred while loading tracker %1\n\n%2\n") - .arg(t->name, status.error); + status = error(tr("Error occurred while loading tracker %1\n\n%2\n") + .arg(t->name, status.error)); goto end; } @@ -66,6 +72,8 @@ end: pProtocol = nullptr; if (!status.is_ok()) - QMessageBox::critical(nullptr, tr("Startup failure"), status.error, QMessageBox::Cancel, QMessageBox::NoButton); + QMessageBox::critical(nullptr, + tr("Startup failure"), status.error, + QMessageBox::Cancel, QMessageBox::NoButton); } diff --git a/logic/runtime-libraries.hpp b/logic/runtime-libraries.hpp index acf5bf30..8c7fedd1 100644 --- a/logic/runtime-libraries.hpp +++ b/logic/runtime-libraries.hpp @@ -12,7 +12,7 @@ #include "compat/tr.hpp" #include "export.hpp" -#include <QFrame> +class QFrame; class OTR_LOGIC_EXPORT runtime_libraries final : public TR { @@ -26,7 +26,7 @@ public: std::shared_ptr<IProtocol> pProtocol; runtime_libraries(QFrame* frame, dylibptr t, dylibptr p, dylibptr f); - runtime_libraries() : pTracker(nullptr), pFilter(nullptr), pProtocol(nullptr), correct(false) {} + runtime_libraries() = default; bool correct = false; }; diff --git a/logic/state.cpp b/logic/state.cpp new file mode 100644 index 00000000..afdf5b12 --- /dev/null +++ b/logic/state.cpp @@ -0,0 +1,45 @@ +#include "state.hpp" + +#include <iterator> + +using dylib_ptr = Modules::dylib_ptr; +using dylib_list = Modules::dylib_list; + +std::tuple<dylib_ptr, int> State::module_by_name(const QString& name, dylib_list& list) +{ + auto it = std::find_if(list.cbegin(), list.cend(), [&name](const dylib_ptr& lib) { + if (!lib) + return name.isEmpty(); + else + return name == lib->module_name; + }); + + if (it == list.cend()) + return { nullptr, -1 }; + else + return { *it, int(std::distance(list.cbegin(), it)) }; +} + +State::State(const QString& library_path) : + modules(library_path), + ev(modules.extensions()), + pose(s.all_axis_opts) +{} + +dylib_ptr State::current_tracker() +{ + auto [ptr, idx] = module_by_name(m.tracker_dll, modules.trackers()); + return ptr; +} + +dylib_ptr State::current_protocol() +{ + auto [ptr, idx] = module_by_name(m.protocol_dll, modules.protocols()); + return ptr; +} + +dylib_ptr State::current_filter() +{ + auto [ptr, idx] = module_by_name(m.filter_dll, modules.filters()); + return ptr; +} diff --git a/logic/state.hpp b/logic/state.hpp index c400df18..abce1daf 100644 --- a/logic/state.hpp +++ b/logic/state.hpp @@ -14,19 +14,27 @@ #include "mappings.hpp" #include "extensions.hpp" #include "work.hpp" -#include <vector> +#include "export.hpp" + +#include <memory> #include <QString> -struct State +struct OTR_LOGIC_EXPORT State { - explicit State(const QString& library_path) : - modules(library_path), - ev(modules.extensions()), - pose(s.all_axis_opts) - {} + using dylib_ptr = Modules::dylib_ptr; + using dylib_list = Modules::dylib_list; + + explicit State(const QString& library_path); + static std::tuple<dylib_ptr, int> module_by_name(const QString& name, dylib_list& list); + + dylib_ptr current_tracker(); + dylib_ptr current_protocol(); + dylib_ptr current_filter(); + Modules modules; event_handler ev; main_settings s; + module_settings m; Mappings pose; std::shared_ptr<Work> work; }; diff --git a/logic/tracklogger.cpp b/logic/tracklogger.cpp index a287b633..758c2478 100644 --- a/logic/tracklogger.cpp +++ b/logic/tracklogger.cpp @@ -3,6 +3,8 @@ #include <QMessageBox> +TrackLogger::~TrackLogger() = default; + void TrackLogger::write_pose(const double* p) { write(p, 6); diff --git a/logic/tracklogger.hpp b/logic/tracklogger.hpp index d6c34157..134a27fd 100644 --- a/logic/tracklogger.hpp +++ b/logic/tracklogger.hpp @@ -13,7 +13,7 @@ class OTR_LOGIC_EXPORT TrackLogger public: TrackLogger() = default; - virtual ~TrackLogger() = default; + virtual ~TrackLogger(); virtual void write(const char *) {} virtual void write(const double *, int) {} diff --git a/logic/win32-shortcuts.cpp b/logic/win32-shortcuts.cpp index 1e2f1b77..c2920071 100644 --- a/logic/win32-shortcuts.cpp +++ b/logic/win32-shortcuts.cpp @@ -15,6 +15,7 @@ #include <QVariant> #include <QDebug> +#if 0 win_key const windows_key_mods[] { {DIK_LCONTROL, Qt::Key_Control}, {DIK_RCONTROL, Qt::Key_Control}, @@ -25,6 +26,7 @@ win_key const windows_key_mods[] { {DIK_LWIN, Qt::Key_Super_L}, {DIK_RWIN, Qt::Key_Super_R}, }; +#endif static const win_key windows_key_sequences[] { { DIK_F1, Qt::Key_F1 }, @@ -119,7 +121,7 @@ static const win_key windows_key_sequences[] { { DIK_PAUSE, Qt::Key_Pause}, { DIK_NUMLOCK, Qt::Key_NumLock}, { DIK_CAPSLOCK, Qt::Key_CapsLock}, -#define key_mod(x, y) Qt::Key(int(x) | int((y))) +#define key_mod(x, y) Qt::Key(int((x)) | int((y))) { DIK_NUMPAD0, key_mod(Qt::Key_0, Qt::KeypadModifier)}, { DIK_NUMPAD0, key_mod(Qt::Key_0, Qt::KeypadModifier)}, { DIK_NUMPAD1, key_mod(Qt::Key_1, Qt::KeypadModifier)}, @@ -161,26 +163,22 @@ bool win_key::to_qt(const Key& k, QKeySequence& qt_, Qt::KeyboardModifiers &mods bool win_key::from_qt(const QKeySequence& qt_, int& dik, Qt::KeyboardModifiers& mods) { // CAVEAT don't use QVariant::toUInt() or conversion fails -#if 0 - const unsigned qt = QVariant(qt_).toInt(); // verbose -#endif - const unsigned qt = int(qt_); // deprecated - const unsigned our_mods = qt & Qt::KeyboardModifierMask; + const unsigned qt = (unsigned)QVariant(qt_).toInt(); + const unsigned our_mods = qt & (unsigned)Qt::KeyboardModifierMask; if (qt == 0) return false; + for (const win_key& wk : windows_key_sequences) { - for (const win_key& wk : windows_key_sequences) + if (unsigned(wk.qt) == qt) { - if (unsigned(wk.qt) == qt) - { - dik = wk.win; - mods = Qt::NoModifier; - return true; - } + dik = wk.win; + mods = Qt::NoModifier; + return true; } } + { const unsigned key = qt & ~Qt::KeyboardModifierMask; for (const win_key& wk : windows_key_sequences) diff --git a/logic/work.cpp b/logic/work.cpp index da61b348..e0c13ac8 100644 --- a/logic/work.cpp +++ b/logic/work.cpp @@ -53,7 +53,7 @@ std::unique_ptr<TrackLogger> Work::make_logger(main_settings &s) QMessageBox::Ok, QMessageBox::NoButton); } else - return logger; + return std::move(logger); } } diff --git a/migration/20180428_00-module-names.cpp b/migration/20180428_00-module-names.cpp index 174ca7ee..0c59add9 100644 --- a/migration/20180428_00-module-names.cpp +++ b/migration/20180428_00-module-names.cpp @@ -12,6 +12,21 @@ struct module_names : migration using dylib_ptr = Modules::dylib_ptr; using dylib_list = Modules::dylib_list; + struct module_type { + QString name, def; + dylib_list const& list; + }; + + Modules m { OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH }; + + module_type types[3] { + { "tracker-dll", "pt", m.trackers() }, + { "protocol-dll", "freetrack", m.protocols() }, + { "filter-dll", "accela", m.filters() }, + }; + + bundle b { make_bundle("modules") }; + QString unique_date() const override { return "20180428_00"; @@ -24,46 +39,39 @@ struct module_names : migration bool should_run() const override { - return true; - } - - void run() override - { - struct module_type { - QString name, def; - dylib_list const& list; - }; + for (const module_type& type : types) + { + if (!b->contains(type.name)) + continue; - Modules m { OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH }; + const QString value = b->get_variant(type.name).value<QString>(); - module_type types[3] { - { "tracker-dll", "pt", m.trackers() }, - { "protocol-dll", "freetrack", m.protocols() }, - { "filter-dll", "accela", m.filters() }, - }; + for (const dylib_ptr& lib : type.list) + { + if (value == lib->name && value != lib->module_name) + return true; + } + } - bundle b = make_bundle("modules"); + return false; + } + void run() override + { for (module_type& type : types) { - QByteArray n = type.name.toUtf8(); - if (!b->contains(type.name)) - { - qDebug() << n.constData() << "=>" << "EMPTY"; - b->store_kv(type.name, type.def); continue; - } - QString value = b->get_variant(type.name).value<QString>(); + const QString value = b->get_variant(type.name).value<QString>(); bool found = false; - for (dylib_ptr lib : type.list) + for (const dylib_ptr& lib : type.list) { - if (value == lib->name) + if (value == lib->name && value != lib->module_name) { - qDebug() << n.constData() << "=>" << lib->module_name; + qDebug() << type.name << value << "=>" << lib->module_name; b->store_kv(type.name, lib->module_name); found = true; break; @@ -72,8 +80,8 @@ struct module_names : migration if (!found) { - qDebug() << n.constData() << "=>" << "not found" << value; - b->store_kv(type.name, type.def); + qDebug() << type.name << value << "not found"; + b->store_kv(type.name, QVariant()); } } diff --git a/migration/CMakeLists.txt b/migration/CMakeLists.txt index 9a7f7dc9..b2dfac6d 100644 --- a/migration/CMakeLists.txt +++ b/migration/CMakeLists.txt @@ -1,2 +1,5 @@ otr_module(migration BIN) target_link_libraries(opentrack-migration opentrack-logic opentrack-spline) +if(CMAKE_COMPILER_IS_CLANGXX) + target_compile_options(${self} PRIVATE -Wno-weak-vtables) +endif() diff --git a/migration/migration.cpp b/migration/migration.cpp index 45965bee..d4ec6493 100644 --- a/migration/migration.cpp +++ b/migration/migration.cpp @@ -24,9 +24,7 @@ using namespace options::globals; // individual migrations are run in the UI thread. they can be interactive if necessary. -namespace migrations { - -namespace detail { +namespace migrations::detail { static std::vector<mptr> migration_list; static std::vector<mfun> migration_thunks; @@ -174,29 +172,34 @@ std::vector<QString> migrator::run() done.push_back(m->name()); } } - mark_config_as_not_needing_migration(); + mark_profile_as_not_needing_migration(); }); return done; } -} +} // ns migrations::detail + +namespace migrations { + +migration::migration() = default; +migration::~migration() = default; -} // ns +} // ns migrations std::vector<QString> run_migrations() { return migrations::detail::migrator::run(); } -void mark_config_as_not_needing_migration() +void mark_profile_as_not_needing_migration() { using m = migrations::detail::migrator; - m::mark_config_as_not_needing_migration(); + m::mark_profile_as_not_needing_migration(); } -void migrations::detail::migrator::mark_config_as_not_needing_migration() +void migrations::detail::migrator::mark_profile_as_not_needing_migration() { set_last_migration_time(time_after_migrations()); } diff --git a/migration/migration.hpp b/migration/migration.hpp index 42c8bead..2c1fee5b 100644 --- a/migration/migration.hpp +++ b/migration/migration.hpp @@ -28,7 +28,7 @@ namespace detail { { static std::vector<QString> run(); static void add_migration_thunk(std::function<mptr()>& thunk); - static void mark_config_as_not_needing_migration(); + static void mark_profile_as_not_needing_migration(); private: static void sort_migrations(); @@ -71,26 +71,20 @@ namespace detail { #define OPENTRACK_MIGRATION(type) \ MIGRATE_EXPANDED1(type, MIGRATE_EXPAND1(__COUNTER__)) -#ifdef Q_CREATOR_RUN -# pragma clang diagnostic ignored "-Wweak-vtables" -#endif - struct migration { - migration() = default; + migration(); migration(const migration&) = delete; migration& operator=(const migration&) = delete; - inline virtual ~migration(); + virtual ~migration(); virtual QString unique_date() const = 0; virtual QString name() const = 0; virtual bool should_run() const = 0; virtual void run() = 0; }; -inline migration::~migration() {} - } // ns migrations OTR_MIGRATION_EXPORT std::vector<QString> run_migrations(); -OTR_MIGRATION_EXPORT void mark_config_as_not_needing_migration(); +OTR_MIGRATION_EXPORT void mark_profile_as_not_needing_migration(); diff --git a/options/base-value.cpp b/options/base-value.cpp index ec96fe6c..d4ec4b6c 100644 --- a/options/base-value.cpp +++ b/options/base-value.cpp @@ -2,7 +2,7 @@ using namespace options; -value_::value_(bundle const& b, const QString& name) : +value_::value_(bundle const& b, const QString& name) noexcept : b(b), self_name(name) { b->on_value_created(this); @@ -12,18 +12,3 @@ value_::~value_() { b->on_value_destructed(this); } - -void value_::notify() const -{ - bundle_value_changed(); -} - -namespace options::detail { - -void set_value_to_default(value_* val) -{ - val->set_to_default(); -} - -} // ns options::detail - diff --git a/options/base-value.hpp b/options/base-value.hpp index 14eaeb10..722107a4 100644 --- a/options/base-value.hpp +++ b/options/base-value.hpp @@ -9,15 +9,15 @@ #include "compat/macros.hpp" #include "value-traits.hpp" +#include <utility> + #include <QObject> #include <QString> #include <QList> #include <QPointF> #include <QVariant> -#include <utility> - -#define OTR_OPTIONS_SLOT(t) void setValue(t datum) { store_(datum); } +#define OTR_OPTIONS_SLOT(t) void setValue(t datum) noexcept { store_(datum); } #define OTR_OPTIONS_SIGNAL(t) void valueChanged(t) const namespace options { @@ -26,17 +26,14 @@ class OTR_OPTIONS_EXPORT value_ : public QObject { Q_OBJECT - friend class detail::connector; - template<typename t> using signal_sig = void(value_::*)(cv_qualified<t>) const; public: QString name() const { return self_name; } - value_(bundle const& b, const QString& name); + value_(bundle const& b, const QString& name) noexcept; ~value_() override; - // no C++17 "constexpr inline" for data declarations in MSVC template<typename t> static constexpr auto value_changed() { @@ -65,7 +62,8 @@ protected: bundle b; QString self_name; - virtual void store_variant(const QVariant& x) = 0; + virtual void store_variant(QVariant&&) noexcept = 0; + virtual void store_variant(const QVariant&) noexcept = 0; template<typename t> void store_(const t& datum) @@ -91,12 +89,9 @@ public slots: OTR_OPTIONS_SLOT(const QList<slider_value>&) OTR_OPTIONS_SLOT(const QList<QPointF>&) - virtual void bundle_value_changed() const = 0; - virtual void set_to_default() = 0; - - void notify() const; - - friend void ::options::detail::set_value_to_default(value_* val); + virtual void set_to_default() noexcept = 0; + virtual void notify() const = 0; + virtual QVariant get_variant() const noexcept = 0; }; } //ns options diff --git a/options/bundle.cpp b/options/bundle.cpp index 8db4f906..a17b04fb 100644 --- a/options/bundle.cpp +++ b/options/bundle.cpp @@ -13,22 +13,14 @@ #include <cstdlib> #include <QThread> -#include <QApplication> +#include <QCoreApplication> using namespace options; using namespace options::globals; namespace options::detail { -mutex::mutex(QMutex::RecursionMode mode) : QMutex(mode) {} - -mutex::operator QMutex*() const -{ - return const_cast<QMutex*>(static_cast<const QMutex*>(this)); -} - bundle::bundle(const QString& group_name) : - mtx(QMutex::Recursive), group_name(group_name), saved(group_name), transient(saved) @@ -37,58 +29,68 @@ bundle::bundle(const QString& group_name) : bundle::~bundle() = default; -void bundle::reload() +void bundle::reload_no_notify() +{ + if (group_name.isEmpty()) + return; + + QMutexLocker l{&mtx}; + + saved = group(group_name); + transient = saved; +} + +void bundle::notify() { - if (!group_name.isEmpty()) { QMutexLocker l(&mtx); + connector::notify_all_values(); + } - // XXX what do we do when values are and aren't equal? - // see QPointF -sh 20180830 - - // XXX we could probably skip assigning to `saved' -sh 20180830 - saved = group(group_name); - transient = saved; + emit reloading(); + emit changed(); +} +void bundle::reload() +{ + { + QMutexLocker l{&mtx}; + reload_no_notify(); connector::notify_all_values(); - emit reloading(); - emit changed(); } + emit reloading(); + emit changed(); } void bundle::set_all_to_default() { - QMutexLocker l(&mtx); - - forall([](value_* val) { - set_value_to_default(val); - }); + connector::set_all_to_default_(); } void bundle::store_kv(const QString& name, const QVariant& new_value) { - QMutexLocker l(&mtx); + if (group_name.isEmpty()) + return; - if (!group_name.isEmpty()) { - transient.put(name, new_value); - mark_ini_modified(); - + QMutexLocker l{&mtx}; + transient.put(name, new_value); connector::notify_values(name); - emit changed(); } + + emit changed(); } QVariant bundle::get_variant(const QString& name) const { - QMutexLocker l(mtx); + QMutexLocker l{&mtx}; return transient.get_variant(name); } bool bundle::contains(const QString &name) const { - QMutexLocker l(mtx); + QMutexLocker l{&mtx}; return transient.contains(name); } @@ -101,8 +103,7 @@ void bundle::save() return; { - QMutexLocker l(&mtx); - + QMutexLocker l{&mtx}; saved = transient; saved.save(); } @@ -110,7 +111,7 @@ void bundle::save() emit saving(); } -void bundler::after_profile_changed_() +void bundler::reload_no_notify_() { QMutexLocker l(&implsgl_mtx); @@ -121,16 +122,38 @@ void bundler::after_profile_changed_() if (bundle_) { //qDebug() << "bundle: reverting" << kv.first << "due to profile change"; - bundle_->reload(); + bundle_->reload_no_notify(); } } } -void bundler::refresh_all_bundles() +void bundler::notify_() { - bundler_singleton().after_profile_changed_(); + QMutexLocker l(&implsgl_mtx); + + for (auto& kv : implsgl_data) + { + weak bundle = kv.second; + shared bundle_ = bundle.lock(); + if (bundle_) + { + //qDebug() << "bundle: reverting" << kv.first << "due to profile change"; + bundle_->notify(); + } + } } +void bundler::reload_() +{ + QMutexLocker l(&implsgl_mtx); + notify_(); + reload_no_notify_(); +} + +void bundler::notify() { singleton().notify_(); } +void bundler::reload_no_notify() { singleton().reload_no_notify_(); } +void bundler::reload() { singleton().reload_(); } + bundler::bundler() = default; bundler::~bundler() = default; @@ -139,7 +162,6 @@ std::shared_ptr<bundler::v> bundler::make_bundle_(const k& key) QMutexLocker l(&implsgl_mtx); using iter = decltype(implsgl_data.cbegin()); - const iter it = implsgl_data.find(key); if (it != implsgl_data.end()) @@ -168,7 +190,7 @@ std::shared_ptr<bundler::v> bundler::make_bundle_(const k& key) return shr; } -bundler& bundler::bundler_singleton() +bundler& bundler::singleton() { static bundler ret; return ret; @@ -181,7 +203,7 @@ namespace options { std::shared_ptr<bundle_> make_bundle(const QString& name) { if (!name.isEmpty()) - return detail::bundler::bundler_singleton().make_bundle_(name); + return detail::bundler::singleton().make_bundle_(name); else return std::make_shared<bundle_>(QString()); } diff --git a/options/bundle.hpp b/options/bundle.hpp index 9ab7f74c..c1bb716d 100644 --- a/options/bundle.hpp +++ b/options/bundle.hpp @@ -29,14 +29,8 @@ #include "export.hpp" namespace options::detail { - class OTR_OPTIONS_EXPORT mutex final : public QMutex - { - public: - explicit mutex(QMutex::RecursionMode mode); - cc_noinline operator QMutex*() const; // NOLINT - }; - class bundle; + struct bundler; } // ns options::detail namespace options { @@ -51,11 +45,15 @@ class OTR_OPTIONS_EXPORT bundle final : public QObject, public connector { Q_OBJECT - mutex mtx; + friend struct bundler; + + mutable QMutex mtx { QMutex::Recursive }; const QString group_name; group saved; group transient; + void reload_no_notify(); + signals: void reloading(); void saving() const; @@ -65,7 +63,7 @@ public: bundle(const bundle&) = delete; bundle& operator=(const bundle&) = delete; - QMutex* get_mtx() const override { return mtx; } + QMutex* get_mtx() const override { return &mtx; } QString name() const { return group_name; } explicit bundle(const QString& group_name); @@ -75,6 +73,7 @@ public: bool contains(const QString& name) const; QVariant get_variant(const QString& name) const; + void notify(); public slots: void save(); @@ -89,19 +88,25 @@ struct OTR_OPTIONS_EXPORT bundler final using weak = std::weak_ptr<v>; using shared = std::shared_ptr<v>; - static void refresh_all_bundles(); + static void notify(); + static void reload_no_notify(); + + void reload(); private: QMutex implsgl_mtx { QMutex::Recursive }; std::unordered_map<k, weak> implsgl_data {}; - void after_profile_changed_(); + void notify_(); + void reload_no_notify_(); + + void reload_(); - friend OTR_OPTIONS_EXPORT + friend std::shared_ptr<v> options::make_bundle(const QString& name); - [[nodiscard]] std::shared_ptr<v> make_bundle_(const k& key); - [[nodiscard]] static bundler& bundler_singleton(); + std::shared_ptr<v> make_bundle_(const k& key); + static bundler& singleton(); bundler(); ~bundler(); diff --git a/options/connector.cpp b/options/connector.cpp index 838a2e0e..40c99a82 100644 --- a/options/connector.cpp +++ b/options/connector.cpp @@ -17,7 +17,6 @@ connector::~connector() = default; void connector::on_value_destructed(value_type val) { const QString& name = val->name(); - QMutexLocker l(get_mtx()); const auto it = connected_values.find(name); @@ -68,14 +67,21 @@ void connector::notify_values(const QString& name) const auto it = connected_values.find(name); if (it != connected_values.cend()) for (value_type val : it->second) - val->bundle_value_changed(); + val->notify(); } void connector::notify_all_values() const { for (const auto& [k, v] : connected_values) for (value_type val : v) - val->bundle_value_changed(); + val->notify(); +} + +void connector::set_all_to_default_() +{ + for (auto& pair : connected_values) + for (auto& val : pair.second) + val->set_to_default(); } } // ns options::detail diff --git a/options/connector.hpp b/options/connector.hpp index 11aa94da..e22cb20f 100644 --- a/options/connector.hpp +++ b/options/connector.hpp @@ -39,23 +39,11 @@ protected: void notify_values(const QString& name) const; void notify_all_values() const; virtual QMutex* get_mtx() const = 0; - - template<typename F> - void forall(F&& fun) - { - QMutexLocker l(get_mtx()); - - for (auto& pair : connected_values) - for (auto& val : pair.second) - fun(val); - } + void set_all_to_default_(); public: connector(); virtual ~connector(); - - connector(const connector&) = default; - connector& operator=(const connector&) = default; }; } // ns options::detail diff --git a/options/defs.hpp b/options/defs.hpp index a71a56e2..9ea4f3b3 100644 --- a/options/defs.hpp +++ b/options/defs.hpp @@ -1,11 +1,6 @@ #pragma once -#include "compat/macros.hpp" - -#include <QString> -#include <QMetaType> - -#define OPENTRACK_CONFIG_FILENAME_KEY "settings-filename" -#define OPENTRACK_DEFAULT_CONFIG "default.ini" +#define OPENTRACK_PROFILE_FILENAME_KEY "settings-filename" +#define OPENTRACK_DEFAULT_PROFILE "default.ini" diff --git a/options/globals.cpp b/options/globals.cpp index 33327090..0f0e536b 100644 --- a/options/globals.cpp +++ b/options/globals.cpp @@ -11,14 +11,15 @@ namespace options::globals::detail { ini_ctx::ini_ctx() = default; -bool is_portable_installation() +static bool is_portable_installation() { #if defined _WIN32 // must keep consistent between invocations static const bool ret = QFile::exists(OPENTRACK_BASE_PATH + "/portable.txt"); return ret; -#endif +#else return false; +#endif } saver_::~saver_() @@ -104,9 +105,9 @@ bool is_ini_modified() QString ini_filename() { return with_global_settings_object([&](QSettings& settings) { - const QString ret = settings.value(OPENTRACK_CONFIG_FILENAME_KEY, OPENTRACK_DEFAULT_CONFIG).toString(); + const QString ret = settings.value(OPENTRACK_PROFILE_FILENAME_KEY, OPENTRACK_DEFAULT_PROFILE).toString(); if (ret.size() == 0) - return QStringLiteral(OPENTRACK_DEFAULT_CONFIG); + return QStringLiteral(OPENTRACK_DEFAULT_PROFILE); return ret; }); } @@ -116,7 +117,7 @@ QString ini_pathname() const auto dir = ini_directory(); if (dir.isEmpty()) return {}; - return dir + "/" + ini_filename(); + return dir + QStringLiteral("/") + ini_filename(); } QString ini_combine(const QString& filename) @@ -128,9 +129,9 @@ QStringList ini_list() { const auto dirname = ini_directory(); if (dirname == "") - return QStringList(); + return {}; QDir settings_dir(dirname); - QStringList list = settings_dir.entryList( QStringList { "*.ini" } , QDir::Files, QDir::Name ); + QStringList list = settings_dir.entryList( QStringList { QStringLiteral("*.ini") } , QDir::Files, QDir::Name ); std::sort(list.begin(), list.end()); return list; } @@ -142,7 +143,7 @@ void mark_ini_modified(bool value) ini.mtx.unlock(); } -QString ini_directory() +static QString ini_directory_() { QString dir; @@ -172,4 +173,10 @@ QString ini_directory() return dir; } +QString ini_directory() +{ + static const QString dir = ini_directory_(); + return dir; +} + } // ns options::globals diff --git a/options/globals.hpp b/options/globals.hpp index 8032e5d8..ae7f8e4d 100644 --- a/options/globals.hpp +++ b/options/globals.hpp @@ -29,12 +29,12 @@ struct OTR_OPTIONS_EXPORT saver_ final { ini_ctx& ctx; - cc_noinline ~saver_(); - explicit cc_noinline saver_(ini_ctx& ini); + never_inline ~saver_(); + explicit never_inline saver_(ini_ctx& ini); }; template<typename F> -cc_noinline +never_inline auto with_settings_object_(ini_ctx& ini, F&& fun) { saver_ saver { ini }; @@ -44,7 +44,6 @@ auto with_settings_object_(ini_ctx& ini, F&& fun) OTR_OPTIONS_EXPORT ini_ctx& cur_settings(); OTR_OPTIONS_EXPORT ini_ctx& global_settings(); -OTR_OPTIONS_EXPORT bool is_portable_installation(); } // ns options::globals::detail diff --git a/options/group.cpp b/options/group.cpp index af66aaf2..41655d5e 100644 --- a/options/group.cpp +++ b/options/group.cpp @@ -22,7 +22,11 @@ using namespace options::globals; group::group(const QString& name) : name(name) { - if (name == "") + constexpr unsigned reserved_buckets = 64; + kvs.reserve(reserved_buckets); + kvs.max_load_factor(0.4375); + + if (name.isEmpty()) return; with_settings_object([&](QSettings& conf) { @@ -35,7 +39,7 @@ group::group(const QString& name) : name(name) void group::save() const { - if (name == "") + if (name.isEmpty()) return; with_settings_object([&](QSettings& s) { @@ -46,12 +50,15 @@ void group::save() const }); } -void group::put(const QString &s, const QVariant &d) +void group::put(const QString& s, const QVariant& d) { - kvs[s] = d; + if (d.isNull()) + kvs.erase(s); + else + kvs[s] = d; } -bool group::contains(const QString &s) const +bool group::contains(const QString& s) const { const auto it = kvs.find(s); return it != kvs.cend(); @@ -63,7 +70,7 @@ QVariant group::get_variant(const QString& name) const if (it != kvs.cend()) return it->second; - return QVariant(); + return {}; } } // ns options::detail diff --git a/options/group.hpp b/options/group.hpp index 76bb939b..45e9353c 100644 --- a/options/group.hpp +++ b/options/group.hpp @@ -31,7 +31,7 @@ namespace options::detail { void put(const QString& s, const QVariant& d); bool contains(const QString& s) const; - cc_noinline QVariant get_variant(const QString& name) const; + never_inline QVariant get_variant(const QString& name) const; }; } // ns options::detail diff --git a/options/metatype.cpp b/options/metatype.cpp index a4629aa6..3b8ec690 100644 --- a/options/metatype.cpp +++ b/options/metatype.cpp @@ -1,4 +1,5 @@ #include <QMetaType> +#include "compat/macros.hpp" namespace options::detail { @@ -13,19 +14,15 @@ int declare_metatype_for_type(const char* str) } // ns options::detail -#define OPENTRACK_DEFINE_METATYPE2(t, ctr) \ - OPENTRACK_DEFINE_METATYPE3(t, ctr) - -#define OPENTRACK_DEFINE_METATYPE3(t, ctr) \ - OPENTRACK_DEFINE_METATYPE4(t, init_metatype_ ## ctr) - -#define OPENTRACK_DEFINE_METATYPE4(t, sym) \ - class sym { /* NOLINT */ \ - static const int dribble; \ - } sym; /* NOLINT */ \ +#define OPENTRACK_DEFINE_METATYPE2(t, sym) \ + class sym { /* NOLINT */ \ + static const int dribble; \ + }; /* NOLINT */ \ + static sym sym; \ const int sym :: dribble = ::options::detail::declare_metatype_for_type<t>(#t); -#define OPENTRACK_DEFINE_METATYPE(t) OPENTRACK_DEFINE_METATYPE2(t, __COUNTER__) +#define OPENTRACK_DEFINE_METATYPE(t) \ + OPENTRACK_DEFINE_METATYPE2(t, PP_CAT(init, __COUNTER__)) #define OPENTRACK_METATYPE_(x) OPENTRACK_DEFINE_METATYPE(x) #include "metatype.hpp" diff --git a/options/scoped.cpp b/options/scoped.cpp index 8f1ef202..ef5b94d6 100644 --- a/options/scoped.cpp +++ b/options/scoped.cpp @@ -10,17 +10,17 @@ namespace options { // XXX hack: the flag shouldn't be here as action at distance -sh 20160926 -static std::atomic_bool teardown_flag(false); -static void set_teardown_flag(bool value); +static std::atomic<bool> teardown_flag{false}; +[[nodiscard]] static bool set_teardown_flag(bool value); static void ensure_thread(); static bool is_tracker_teardown(); -static void set_teardown_flag(bool value) +static bool set_teardown_flag(bool value) { // flag being set means "opts" is about to go out of scope due to tracker stop // in this case we don't reload options. we only want to reload when cancel is pressed. ensure_thread(); - teardown_flag = value; + return teardown_flag.exchange(value); } static void ensure_thread() @@ -59,14 +59,13 @@ opts::opts(const QString &name) : b(make_bundle(name)) { } -with_tracker_teardown::with_tracker_teardown() : old_value(teardown_flag) +with_tracker_teardown::with_tracker_teardown() : old_value{set_teardown_flag(true)} { - set_teardown_flag(true); } with_tracker_teardown::~with_tracker_teardown() { - set_teardown_flag(old_value); + (void)set_teardown_flag(old_value); } } // ns options diff --git a/options/scoped.hpp b/options/scoped.hpp index 452708f4..dd7dbacf 100644 --- a/options/scoped.hpp +++ b/options/scoped.hpp @@ -6,8 +6,6 @@ #include "export.hpp" -#include <atomic> - namespace options { struct OTR_OPTIONS_EXPORT with_tracker_teardown final @@ -26,11 +24,12 @@ struct OTR_OPTIONS_EXPORT opts bundle b; - virtual ~opts(); - explicit opts(const QString& name); - opts& operator=(const opts&) = delete; opts(const opts&) = delete; + +protected: + explicit opts(const QString& name); + ~opts(); }; } diff --git a/options/slider.cpp b/options/slider.cpp index aea3c48a..b64358c1 100644 --- a/options/slider.cpp +++ b/options/slider.cpp @@ -86,7 +86,7 @@ QDataStream& operator << (QDataStream& out, const options::slider_value& v) QDebug operator << (QDebug dbg, const options::slider_value& v) { return dbg << QStringLiteral("slider_value(cur=%1, min=%2, max=%3)") - .arg(v.cur()).arg(v.min()).arg(v.max()).toUtf8().data(); + .arg(v.cur()).arg(v.min()).arg(v.max()).toUtf8().constData(); } QDataStream& operator >> (QDataStream& in, options::slider_value& v) diff --git a/options/slider.hpp b/options/slider.hpp index 1dd9ec7c..abf39685 100644 --- a/options/slider.hpp +++ b/options/slider.hpp @@ -34,11 +34,11 @@ namespace options slider_value& operator=(const slider_value& v) = default; slider_value(const slider_value& v) = default; - slider_value() : slider_value{0, 0, 0} {}; + slider_value() : slider_value{0, 0, 0} {} bool operator==(const slider_value& v) const; bool operator!=(const slider_value& v) const; - operator double() const { return cur_; } + constexpr operator double() const { return cur_; } double cur() const { return cur_; } double min() const { return min_; } double max() const { return max_; } diff --git a/options/value-traits.hpp b/options/value-traits.hpp index f5b74f73..aeb34cfa 100644 --- a/options/value-traits.hpp +++ b/options/value-traits.hpp @@ -3,12 +3,11 @@ #include "slider.hpp" #include "export.hpp" -#include <QString> - #include <cmath> -#include <cinttypes> #include <type_traits> +#include <QString> + namespace options::detail { template<typename t, typename Enable = void> @@ -46,8 +45,7 @@ struct default_value_traits return self::qvariant_from_storage(self::storage_from_value(val)); } - static constexpr inline - value_type pass_value(const value_type& x) + static constexpr value_type pass_value(const value_type& x) { if constexpr(std::is_same_v<value_type, stored_type>) return x; @@ -79,26 +77,13 @@ struct value_traits : default_value_traits<t> {}; template<> struct value_traits<double> : default_value_traits<double> { - static bool is_equal(value_type x, value_type y) - { - if (x == y) - return true; - else - { - using I = std::int64_t; - constexpr int K = 1000; - - value_type x_, y_; - - return I(std::modf(x, &x_) * K) == I(std::modf(y, &y_) * K) && - I(x_) == I(y_); - } - } + static bool is_equal(value_type x, value_type y) { return std::fabs(x - y) < 1e-6; } }; -template<> struct value_traits<float> : value_traits<float, double> +template<> +struct value_traits<float> : default_value_traits<float> { - static constexpr inline value_type pass_value(const value_type& x) { return x; } + static bool is_equal(value_type x, value_type y) { return std::fabs(x - y) < 1e-6f; } }; template<> diff --git a/options/value.cpp b/options/value.cpp index e0be69ae..81a094e6 100644 --- a/options/value.cpp +++ b/options/value.cpp @@ -1,6 +1,5 @@ // instantiate the "template class" value<t> symbols -#include "compat/linkage-macros.hpp" -#define OTR_INST_VALUE(x) OTR_TEMPLATE_EXPORT(x) +#define OTR_OPTIONS_INST_VALUE OTR_TEMPLATE_EXPORT #include "value.hpp" diff --git a/options/value.hpp b/options/value.hpp index ea180b27..940e2a15 100644 --- a/options/value.hpp +++ b/options/value.hpp @@ -35,18 +35,17 @@ namespace options::detail { namespace options { -template<typename u> +template<typename t> class value final : public value_ { - using t = remove_cvref_t<u>; + static_assert(std::is_same_v<t, remove_cvref_t<t>>); const t def; - using traits = detail::value_traits<t>; - cc_noinline - t get() const + never_inline + auto get() const noexcept { - if (self_name.isEmpty() || !b->contains(self_name)) + if (!b->contains(self_name)) return traits::pass_value(def); QVariant variant = b->get_variant(self_name); @@ -57,18 +56,9 @@ class value final : public value_ return traits::pass_value(traits::value_with_default(traits::value_from_qvariant(variant), def)); } - friend class detail::connector; - void bundle_value_changed() const override - { - if (!self_name.isEmpty()) - emit valueChanged(traits::storage_from_value(get())); - } - - void store_variant(const QVariant& value) override + never_inline + void store_variant(QVariant&& value) noexcept override { - if (self_name.isEmpty()) - return; - if (traits::is_equal(get(), traits::value_from_qvariant(value))) return; @@ -78,76 +68,110 @@ class value final : public value_ b->store_kv(self_name, traits::qvariant_from_value(def)); } + never_inline + void store_variant(const QVariant& value) noexcept override + { + QVariant copy{value}; + store_variant(std::move(copy)); + } + + bool is_null() const + { + return self_name.isEmpty() || b->name().isEmpty(); + } + public: - cc_noinline - value<u>& operator=(const t& datum) + QVariant get_variant() const noexcept override + { + if (QVariant ret{b->get_variant(self_name)}; ret.isValid() && !ret.isNull()) + return ret; + + return traits::qvariant_from_value(def); + } + + void notify() const override + { + if (!is_null()) + emit valueChanged(traits::storage_from_value(get())); + } + + auto& operator=(t&& datum) noexcept { store_variant(traits::qvariant_from_value(traits::pass_value(datum))); + return *this; + } + auto& operator=(const t& datum) noexcept + { + t copy{datum}; + *this = std::move(copy); return *this; } - static constexpr inline Qt::ConnectionType DIRECT_CONNTYPE = Qt::DirectConnection; - static constexpr inline Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection; + auto& operator=(const value<t>& datum) noexcept + { + *this = *datum; + return *this; + } + + static constexpr Qt::ConnectionType DIRECT_CONNTYPE = Qt::DirectConnection; + static constexpr Qt::ConnectionType SAFE_CONNTYPE = Qt::QueuedConnection; - cc_noinline - value(bundle b, const QString& name, t def) : - value_(b, name), def(std::move(def)) + value(bundle b, const QString& name, t def) noexcept : value_(b, name), def(std::move(def)) { } - cc_noinline + value(const value<t>& other) noexcept : value{other.b, other.self_name, other.def} {} + t default_value() const { return def; } - cc_noinline - void set_to_default() override + void set_to_default() noexcept override { *this = def; } - operator t() const { return get(); } // NOLINT + operator t() const { return get(); } - template<typename w, typename = decltype(static_cast<w>(std::declval<t>()))> - explicit cc_forceinline operator w() const { return to<w>(); } + template<typename u> + explicit force_inline operator u() const { return to<u>(); } - auto operator->() const + auto operator->() const noexcept { - return detail::dereference_wrapper<t>{get()}; + return detail::dereference_wrapper{get()}; } - cc_forceinline t operator()() const { return get(); } - cc_forceinline t operator*() const { return get(); } + force_inline auto operator()() const noexcept { return get(); } + force_inline auto operator*() const noexcept { return get(); } - template<typename w> - w to() const + template<typename u> + u to() const noexcept { - return static_cast<w>(get()); + return static_cast<u>(get()); } }; -// some linker problems -#if !defined OTR_INST_VALUE -# define OTR_INST_VALUE OTR_TEMPLATE_IMPORT +#if !defined OTR_OPTIONS_INST_VALUE +# define OTR_OPTIONS_INST_VALUE OTR_TEMPLATE_IMPORT #endif -OTR_INST_VALUE(value<double>) -OTR_INST_VALUE(value<float>) -OTR_INST_VALUE(value<int>) -OTR_INST_VALUE(value<bool>) -OTR_INST_VALUE(value<QString>) -OTR_INST_VALUE(value<slider_value>) -OTR_INST_VALUE(value<QPointF>) -OTR_INST_VALUE(value<QVariant>) -OTR_INST_VALUE(value<QList<double>>) -OTR_INST_VALUE(value<QList<float>>) -OTR_INST_VALUE(value<QList<int>>) -OTR_INST_VALUE(value<QList<bool>>) -OTR_INST_VALUE(value<QList<QString>>) -OTR_INST_VALUE(value<QList<slider_value>>) -OTR_INST_VALUE(value<QList<QPointF>>) -OTR_INST_VALUE(value<QList<QVariant>>) +OTR_OPTIONS_INST_VALUE(value<double>) +OTR_OPTIONS_INST_VALUE(value<float>) +OTR_OPTIONS_INST_VALUE(value<int>) +OTR_OPTIONS_INST_VALUE(value<bool>) +OTR_OPTIONS_INST_VALUE(value<QString>) +OTR_OPTIONS_INST_VALUE(value<slider_value>) +OTR_OPTIONS_INST_VALUE(value<QPointF>) +OTR_OPTIONS_INST_VALUE(value<QVariant>) +OTR_OPTIONS_INST_VALUE(value<QList<double>>) +OTR_OPTIONS_INST_VALUE(value<QList<float>>) +OTR_OPTIONS_INST_VALUE(value<QList<int>>) +OTR_OPTIONS_INST_VALUE(value<QList<bool>>) +OTR_OPTIONS_INST_VALUE(value<QList<QString>>) +OTR_OPTIONS_INST_VALUE(value<QList<slider_value>>) +OTR_OPTIONS_INST_VALUE(value<QList<QPointF>>) +OTR_OPTIONS_INST_VALUE(value<QList<QVariant>>) } // ns options diff --git a/pose-widget/images/side1.png b/pose-widget/images/side1.png Binary files differindex d15c3658..2955bc01 100644 --- a/pose-widget/images/side1.png +++ b/pose-widget/images/side1.png diff --git a/pose-widget/images/side6.png b/pose-widget/images/side6.png Binary files differindex 7338690d..3bae0e50 100644 --- a/pose-widget/images/side6.png +++ b/pose-widget/images/side6.png diff --git a/pose-widget/pose-widget.cpp b/pose-widget/pose-widget.cpp index 12de49d3..943fc0ad 100644 --- a/pose-widget/pose-widget.cpp +++ b/pose-widget/pose-widget.cpp @@ -22,16 +22,13 @@ // XXX this needs rewriting in terms of scanline rendering -sh 20180105 // see: <https://mikro.naprvyraz.sk/docs/Coding/2/TEXTURE4.TXT> -using namespace pose_widget_impl; - -pose_transform::pose_transform(QWidget* dst) : - dst(dst), - front(":/images/side1.png", nullptr), - back(":/images/side6.png", nullptr), - image(w, h, QImage::Format_ARGB32), - image2(w, h, QImage::Format_ARGB32), - fresh(false) +namespace pose_widget_impl { + +pose_transform::pose_transform(QWidget* dst, double dpr) : dst(dst) { + for (QImage* img : { &image, &image2, &front, &back }) + img->setDevicePixelRatio(dpr); + image.fill(Qt::transparent); image2.fill(Qt::transparent); } @@ -79,7 +76,7 @@ end: } } -pose_widget::pose_widget(QWidget* parent) : QWidget(parent), xform(this) +pose_widget::pose_widget(QWidget* parent) : QWidget(parent), xform{this, devicePixelRatioF()} { rotate_sync(0,0,0, 0,0,0); } @@ -182,7 +179,7 @@ bool Triangle::barycentric_coords(const vec2& px, vec2& uv, int& i) const std::pair<vec2i, vec2i> pose_transform::get_bounds(const vec2& size) { - const int x = size.x(), y = size.y(); + const num x = size.x(), y = size.y(); const vec3 corners[] = { { -x, -y, 0 }, @@ -193,7 +190,7 @@ std::pair<vec2i, vec2i> pose_transform::get_bounds(const vec2& size) vec2 min(w-1, h-1), max(0, 0); - for (unsigned k = 0; k < 4; k++) + for (unsigned k = 0; k < 4; k++) // NOLINT(modernize-loop-convert) { const vec2 pt = project(corners[k]) + vec2(w/2, h/2); @@ -275,17 +272,15 @@ void pose_transform::project_quad_texture() const QImage& tex = dir < 0 ? back : front; - Triangle t(pt[0], pt[1], pt[2]); - - const unsigned orig_pitch = tex.bytesPerLine(); - const unsigned dest_pitch = image.bytesPerLine(); + const unsigned orig_pitch = (unsigned)tex.bytesPerLine(); + const unsigned dest_pitch = (unsigned)image.bytesPerLine(); unsigned char const* __restrict orig = tex.constBits(); unsigned char* __restrict dest = image.bits(); const int orig_depth = tex.depth() / 8; const int dest_depth = image.depth() / 8; - constexpr int const_depth = 4; + constexpr unsigned const_depth = 4; if (unlikely(orig_depth != const_depth || dest_depth != const_depth)) { @@ -294,10 +289,13 @@ void pose_transform::project_quad_texture() return; } + Triangle t(pt[0], pt[1], pt[2]); + const vec2u dist(max.x() - min.x(), max.y() - min.y()); + unsigned len = (unsigned)(dist.x() * dist.y()); - if (int(uv_vec.size()) < dist.x() * dist.y()) - uv_vec.resize(dist.x() * dist.y()); + if (uv_vec.size() < len) + uv_vec.resize(len); for (int y = 0; y < dist.y(); y++) for (int x = 0; x < dist.x(); x++) @@ -337,42 +335,14 @@ void pose_transform::project_quad_texture() vec2 const& uv = uv__->coords; int const i = uv__->i; - float fx = origs[i][0].x() - + uv.x() * origs[i][2].x() - + uv.y() * origs[i][1].x(); - float fy = origs[i][0].y() - + uv.x() * origs[i][2].y() - + uv.y() * origs[i][1].y(); - -//#define BILINEAR_FILTER - -#if defined BILINEAR_FILTER - const unsigned px_ = fx + 1; - const unsigned py_ = fy + 1; -#endif - const unsigned px = fx; - const unsigned py = fy; + unsigned px = (unsigned)(origs[i][0].x() + + uv.x() * origs[i][2].x() + + uv.y() * origs[i][1].x()); + unsigned py = (unsigned)(origs[i][0].y() + + uv.x() * origs[i][2].y() + + uv.y() * origs[i][1].y()); const unsigned orig_pos = py * orig_pitch + px * const_depth; -#if defined BILINEAR_FILTER - const unsigned orig_pos_ = py_ * orig_pitch + px_ * const_depth; - const unsigned orig_pos__ = py * orig_pitch + px_ * const_depth; - const unsigned orig_pos___ = py_ * orig_pitch + px * const_depth; -#endif - - // 1, 0 -- ax_, ay - // 0, 1 -- ax, ay_ - // 1, 1 -- ax_, ay_ - // 0, 0 -- ax, ay - //const uc alpha = (a1 * ax + a3 * ax_) * ay + (a4 * ax + a2 * ax_) * ay_; - -#if defined BILINEAR_FILTER - const float ax_ = fx - unsigned(fx); - const float ay_ = fy - unsigned(fy); - const float ax = 1 - ax_; - const float ay = 1 - ay_; -#endif - const unsigned pos = y * dest_pitch + x * const_depth; if (orig[orig_pos + 3] == uc(255)) // alpha @@ -380,7 +350,7 @@ void pose_transform::project_quad_texture() dest[pos + k] = orig[orig_pos + k]; else for (int k = 0; k < 3; k++) - dest[pos + k] = bgcolor(k); + dest[pos + k] = (unsigned char)bgcolor(k); } } } @@ -421,3 +391,5 @@ inline void pose_transform::with_image_lock(F&& fun) fun(image2); } + +} // ns pose_widget_impl diff --git a/pose-widget/pose-widget.hpp b/pose-widget/pose-widget.hpp index fa956b47..aa51957e 100644 --- a/pose-widget/pose-widget.hpp +++ b/pose-widget/pose-widget.hpp @@ -47,8 +47,8 @@ public: struct pose_transform final : QThread { - pose_transform(QWidget* dst); - ~pose_transform(); + pose_transform(QWidget* dst, double device_pixel_ratio); + ~pose_transform() override; void rotate_async(double xAngle, double yAngle, double zAngle, double x, double y, double z); void rotate_sync(double xAngle, double yAngle, double zAngle, double x, double y, double z); @@ -63,8 +63,7 @@ struct pose_transform final : QThread void project_quad_texture(); std::pair<vec2i, vec2i> get_bounds(const vec2& size); - template<typename F> - inline void with_image_lock(F&& fun); + template<typename F> inline void with_image_lock(F&& fun); rmat rotation, rotation_; vec3 translation, translation_; @@ -73,27 +72,28 @@ struct pose_transform final : QThread QWidget* dst; - QImage front, back; - QImage image, image2; + QImage front{QImage{":/images/side1.png"}.convertToFormat(QImage::Format_ARGB32)}; + QImage back{QImage{":/images/side6.png"}.convertToFormat(QImage::Format_ARGB32)}; + QImage image{w, h, QImage::Format_ARGB32}; + QImage image2{w, h, QImage::Format_ARGB32}; - struct uv_ + struct uv_ // NOLINT(cppcoreguidelines-pro-type-member-init) { vec2 coords; int i; }; std::vector<uv_> uv_vec; + std::atomic<bool> fresh{false}; - std::atomic<bool> fresh; - - static constexpr inline int w = 320, h = 240; + static constexpr int w = 320, h = 240; }; class OTR_POSE_WIDGET_EXPORT pose_widget final : public QWidget { public: pose_widget(QWidget *parent = nullptr); - ~pose_widget(); + ~pose_widget() override; void rotate_async(double xAngle, double yAngle, double zAngle, double x, double y, double z); void rotate_sync(double xAngle, double yAngle, double zAngle, double x, double y, double z); @@ -104,4 +104,4 @@ private: } -using pose_widget_impl::pose_widget; +using pose_widget = pose_widget_impl::pose_widget; diff --git a/proto-flightgear/ftnoir_protocol_fg.h b/proto-flightgear/ftnoir_protocol_fg.h index 45cd0cad..4b4efa29 100644 --- a/proto-flightgear/ftnoir_protocol_fg.h +++ b/proto-flightgear/ftnoir_protocol_fg.h @@ -44,8 +44,8 @@ class flightgear : TR, public IProtocol Q_OBJECT public: - void pose(const double *headpose); - QString game_name() { return tr("FlightGear"); } + void pose(const double *headpose) override; + QString game_name() override { return tr("FlightGear"); } module_status initialize() override; private: settings s; @@ -59,8 +59,8 @@ class FGControls: public IProtocolDialog Q_OBJECT public: FGControls(); - void register_protocol(IProtocol *) {} - void unregister_protocol() {} + void register_protocol(IProtocol *) override {} + void unregister_protocol() override {} private: Ui::UICFGControls ui; settings s; @@ -73,6 +73,6 @@ class flightgearDll : public Metadata { Q_OBJECT - QString name() { return tr("FlightGear"); } - QIcon icon() { return QIcon(":/images/flightgear.png"); } + QString name() override { return tr("FlightGear"); } + QIcon icon() override { return QIcon(":/images/flightgear.png"); } }; diff --git a/proto-fsuipc/CMakeLists.txt b/proto-fsuipc/CMakeLists.txt index 3ba2c90c..9b7bd77e 100644 --- a/proto-fsuipc/CMakeLists.txt +++ b/proto-fsuipc/CMakeLists.txt @@ -1,12 +1,11 @@ -if(WIN32) +if(WIN32 AND NOT opentrack-64bit AND opentrack-intel) set(SDK_FSUIPC "" CACHE PATH "FSUIPC for older MS FSX path") if(SDK_FSUIPC) otr_module(proto-fsuipc) target_link_libraries(opentrack-proto-fsuipc ${SDK_FSUIPC}/FSUIPC_User.lib) target_include_directories(opentrack-proto-fsuipc SYSTEM PUBLIC ${SDK_FSUIPC}) if(MSVC) - set_property(TARGET opentrack-proto-fsuipc APPEND_STRING PROPERTY - LINK_FLAGS "/NODEFAULTLIB:LIBC.lib ") + target_link_options(${self} PRIVATE -NODEFAULTLIB:LIBC.lib) endif() endif() endif() diff --git a/proto-fsuipc/ftnoir_fsuipccontrols.ui b/proto-fsuipc/ftnoir_fsuipccontrols.ui index 4c85c91c..5a28aad2 100644 --- a/proto-fsuipc/ftnoir_fsuipccontrols.ui +++ b/proto-fsuipc/ftnoir_fsuipccontrols.ui @@ -9,12 +9,12 @@ <rect> <x>0</x> <y>0</y> - <width>512</width> - <height>100</height> + <width>248</width> + <height>34</height> </rect> </property> <property name="windowTitle"> - <string>FSUIPC settings FaceTrackNoIR</string> + <string>FSUIPC</string> </property> <property name="windowIcon"> <iconset> @@ -26,99 +26,11 @@ <property name="autoFillBackground"> <bool>false</bool> </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="2" column="1"> - <widget class="QPushButton" name="btnCancel"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>100</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>100</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string>Cancel</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="txtLocationOfDLL"> - <property name="minimumSize"> - <size> - <width>230</width> - <height>0</height> - </size> - </property> - <property name="toolTip"> - <string>Location of FSUIPC.dll</string> - </property> - <property name="frameShape"> - <enum>QFrame::Box</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Sunken</enum> - </property> - <property name="lineWidth"> - <number>1</number> - </property> - <property name="text"> - <string>Location of FSUIPC.dll</string> - </property> - </widget> - </item> - <item row="1" column="0" colspan="2"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>The DLL should be located in the Modules/ directory of MS FS 2004</string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QPushButton" name="btnOK"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>100</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>100</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string>OK</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QPushButton" name="btnFindDLL"> - <property name="maximumSize"> - <size> - <width>35</width> - <height>16777215</height> - </size> - </property> - <property name="text"> - <string>...</string> + <layout class="QVBoxLayout" name="verticalLayout"> + <item alignment="Qt::AlignTop"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Ok</set> </property> </widget> </item> diff --git a/proto-fsuipc/ftnoir_protocol_fsuipc.cpp b/proto-fsuipc/ftnoir_protocol_fsuipc.cpp index 7ba466a8..678262fa 100644 --- a/proto-fsuipc/ftnoir_protocol_fsuipc.cpp +++ b/proto-fsuipc/ftnoir_protocol_fsuipc.cpp @@ -13,24 +13,18 @@ #include <cmath> -fsuipc::fsuipc() -{ - prevPosX = 0.0f; - prevPosY = 0.0f; - prevPosZ = 0.0f; - prevRotX = 0.0f; - prevRotY = 0.0f; - prevRotZ = 0.0f; -} +fsuipc::fsuipc() = default; fsuipc::~fsuipc() { FSUIPC_Close(); +#if 0 FSUIPCLib.unload(); +#endif } template<typename t> -int fsuipc::scale2AnalogLimits(t x, t min_x, t max_x) +int fsuipc::scale(t x, t min_x, t max_x) { t local_x = x; @@ -48,58 +42,44 @@ int fsuipc::scale2AnalogLimits(t x, t min_x, t max_x) return (int) y; } +#if 0 template<typename t> -static inline bool check_float_fresh(t x, t y) +static bool check_float_fresh(t x, t y) { constexpr t eps = t(1e-4); return std::fabs(x - y) >= eps; } +#endif -void fsuipc::pose(const double *headpose ) { +void fsuipc::pose(const double *headpose) +{ DWORD result; - TFSState pitch; - TFSState yaw; - TFSState roll; WORD FSZoom; - double virtPosX; - double virtPosY; - double virtPosZ; - - double virtRotX; - double virtRotY; - double virtRotZ; - - // qDebug() << "FSUIPCServer::run() says: started!"; - - virtRotX = -headpose[Pitch]; // degrees - virtRotY = headpose[Yaw]; - virtRotZ = headpose[Roll]; - - virtPosX = 0.0f; // cm, X and Y are not working for FS2002/2004! - virtPosY = 0.0f; - virtPosZ = headpose[TZ]; + // cm, X and Y are not working for FS2002/2004! + double pos_z = headpose[TZ]; + state pitch, yaw, roll; // NOLINT(cppcoreguidelines-pro-type-member-init) - // - // Init. the FSUIPC offsets (derived from Free-track...) - // + // offsets derived from freetrack pitch.Control = 66503; + pitch.Value = scale(-headpose[Pitch], -180., 180.); // degrees + yaw.Control = 66504; + yaw.Value = scale(headpose[Yaw], -180., 180.); + roll.Control = 66505; + roll.Value = scale(headpose[Roll], -180., 180.); - // +#if 0 // Only do this when the data has changed. This way, the HAT-switch can be used when tracking is OFF. - // if (check_float_fresh(prevRotX, virtRotX) || check_float_fresh(prevRotY, virtRotY) || check_float_fresh(prevRotZ, virtRotZ) || check_float_fresh(prevPosX, virtPosX) || check_float_fresh(prevPosY, virtPosY) || check_float_fresh(prevPosZ, virtPosZ)) +#endif { - // - // Open the connection - // FSUIPC_Open(SIM_ANY, &result); // @@ -110,16 +90,11 @@ void fsuipc::pose(const double *headpose ) { { // Write the 4! DOF-data to FS. Only rotations and zoom are possible. - pitch.Value = scale2AnalogLimits(virtRotX, -180., 180.); FSUIPC_Write(0x3110, 8, &pitch, &result); - - yaw.Value = scale2AnalogLimits(virtRotY, -180., 180.); FSUIPC_Write(0x3110, 8, &yaw, &result); - - roll.Value = scale2AnalogLimits(virtRotZ, -180., 180.); FSUIPC_Write(0x3110, 8, &roll, &result); - FSZoom = WORD(virtPosZ + 64); + FSZoom = WORD(pos_z + 64); FSUIPC_Write(0x832E, 2, &FSZoom, &result); // @@ -136,16 +111,19 @@ void fsuipc::pose(const double *headpose ) { } } +#if 0 prevPosX = virtPosX; prevPosY = virtPosY; prevPosZ = virtPosZ; prevRotX = virtRotX; prevRotY = virtRotY; prevRotZ = virtRotZ; +#endif } module_status fsuipc::initialize() { +#if 0 FSUIPCLib.setFileName( s.LocationOfDLL ); FSUIPCLib.setLoadHints(QLibrary::PreventUnloadHint); @@ -153,6 +131,9 @@ module_status fsuipc::initialize() return error(tr("Can't load fsuipc at '%1'").arg(s.LocationOfDLL)); else return status_ok(); +#else + return {}; +#endif } OPENTRACK_DECLARE_PROTOCOL(fsuipc, FSUIPCControls, fsuipcDll) diff --git a/proto-fsuipc/ftnoir_protocol_fsuipc.h b/proto-fsuipc/ftnoir_protocol_fsuipc.h index fa8bcd45..62238032 100644 --- a/proto-fsuipc/ftnoir_protocol_fsuipc.h +++ b/proto-fsuipc/ftnoir_protocol_fsuipc.h @@ -27,6 +27,7 @@ #include "options/options.hpp" using namespace options; +#if 0 #define FSUIPC_FILENAME "C:\\Program Files\\Microsoft Games\\Flight Simulator 9\\Modules\\FSUIPC.dll" struct settings : opts { @@ -36,13 +37,14 @@ struct settings : opts { LocationOfDLL(b, "dll-location", FSUIPC_FILENAME) {} }; +#endif #pragma pack(push,1) // All fields in structure must be unaligned -typedef struct +struct state { int Control; // Control identifier int Value; // Value of DOF -} TFSState; +}; #pragma pack(pop) class fsuipc : public TR, public IProtocol @@ -53,15 +55,19 @@ public: fsuipc(); ~fsuipc() override; module_status initialize() override; - void pose(const double* headpose); - QString game_name() { return tr("Microsoft Flight Simulator X"); } + void pose(const double* headpose) override; + QString game_name() override { return tr("Microsoft Flight Simulator X"); } private: +#if 0 QLibrary FSUIPCLib; - double prevPosX, prevPosY, prevPosZ, prevRotX, prevRotY, prevRotZ; + + double prevPosX = 0, prevPosY = 0, prevPosZ = 0, + prevRotX = 0, prevRotY = 0, prevRotZ = 0; settings s; +#endif template<typename t> - static int scale2AnalogLimits(t x, t min_x, t max_x ); + static int scale(t x, t min_x, t max_x); }; class FSUIPCControls: public IProtocolDialog @@ -69,23 +75,27 @@ class FSUIPCControls: public IProtocolDialog Q_OBJECT public: FSUIPCControls(); - void register_protocol(IProtocol *) {} - void unregister_protocol() {} + void register_protocol(IProtocol *) override {} + void unregister_protocol() override {} private: Ui::UICFSUIPCControls ui; +#if 0 settings s; +#endif private slots: - void doOK(); void doCancel(); +#if 0 + void doOK(); void getLocationOfDLL(); +#endif }; class fsuipcDll : public Metadata { Q_OBJECT - QString name() { return tr("FSUIPC -- Microsoft FS2002/FS2004"); } - QIcon icon() { return QIcon(":/images/fs9.png"); } + QString name() override { return tr("FSUIPC -- Microsoft FS2002/FS2004"); } + QIcon icon() override { return QIcon(":/images/fs9.png"); } }; diff --git a/proto-fsuipc/ftnoir_protocol_fsuipc_dialog.cpp b/proto-fsuipc/ftnoir_protocol_fsuipc_dialog.cpp index 07507cfb..122c9383 100644 --- a/proto-fsuipc/ftnoir_protocol_fsuipc_dialog.cpp +++ b/proto-fsuipc/ftnoir_protocol_fsuipc_dialog.cpp @@ -14,23 +14,30 @@ FSUIPCControls::FSUIPCControls() { ui.setupUi( this ); +#if 0 connect(ui.btnOK, SIGNAL(clicked()), this, SLOT(doOK())); connect(ui.btnCancel, SIGNAL(clicked()), this, SLOT(doCancel())); connect(ui.btnFindDLL, SIGNAL(clicked()), this, SLOT(getLocationOfDLL())); - tie_setting(s.LocationOfDLL, ui.txtLocationOfDLL); +#else + connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &FSUIPCControls::doCancel); +#endif } -void FSUIPCControls::doOK() { +#if 0 +void FSUIPCControls::doOK() +{ s.b->save(); close(); } +#endif void FSUIPCControls::doCancel() { close(); } +#if 0 void FSUIPCControls::getLocationOfDLL() { QString fileName = QFileDialog::getOpenFileName(this, tr("Locate file"), @@ -40,4 +47,4 @@ void FSUIPCControls::getLocationOfDLL() s.LocationOfDLL = fileName; } } - +#endif diff --git a/proto-fsuipc/lang/nl_NL.ts b/proto-fsuipc/lang/nl_NL.ts index dd7d7942..4a7578e9 100644 --- a/proto-fsuipc/lang/nl_NL.ts +++ b/proto-fsuipc/lang/nl_NL.ts @@ -15,27 +15,7 @@ <context> <name>UICFSUIPCControls</name> <message> - <source>FSUIPC settings FaceTrackNoIR</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Cancel</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Location of FSUIPC.dll</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>The DLL should be located in the Modules/ directory of MS FS 2004</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>OK</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>...</source> + <source>FSUIPC</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-fsuipc/lang/ru_RU.ts b/proto-fsuipc/lang/ru_RU.ts index 29f52f79..dd26dd22 100644 --- a/proto-fsuipc/lang/ru_RU.ts +++ b/proto-fsuipc/lang/ru_RU.ts @@ -15,27 +15,7 @@ <context> <name>UICFSUIPCControls</name> <message> - <source>FSUIPC settings FaceTrackNoIR</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Cancel</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Location of FSUIPC.dll</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>The DLL should be located in the Modules/ directory of MS FS 2004</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>OK</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>...</source> + <source>FSUIPC</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-fsuipc/lang/stub.ts b/proto-fsuipc/lang/stub.ts index 16662c44..031e1916 100644 --- a/proto-fsuipc/lang/stub.ts +++ b/proto-fsuipc/lang/stub.ts @@ -15,27 +15,7 @@ <context> <name>UICFSUIPCControls</name> <message> - <source>FSUIPC settings FaceTrackNoIR</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Cancel</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Location of FSUIPC.dll</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>The DLL should be located in the Modules/ directory of MS FS 2004</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>OK</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>...</source> + <source>FSUIPC</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-fsuipc/lang/zh_CN.ts b/proto-fsuipc/lang/zh_CN.ts index 16662c44..031e1916 100644 --- a/proto-fsuipc/lang/zh_CN.ts +++ b/proto-fsuipc/lang/zh_CN.ts @@ -15,27 +15,7 @@ <context> <name>UICFSUIPCControls</name> <message> - <source>FSUIPC settings FaceTrackNoIR</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Cancel</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Location of FSUIPC.dll</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>The DLL should be located in the Modules/ directory of MS FS 2004</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>OK</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>...</source> + <source>FSUIPC</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-ft/ftnoir_protocol_ft.cpp b/proto-ft/ftnoir_protocol_ft.cpp index 314bb451..46879475 100644 --- a/proto-ft/ftnoir_protocol_ft.cpp +++ b/proto-ft/ftnoir_protocol_ft.cpp @@ -23,9 +23,7 @@ freetrack::~freetrack() static_assert(sizeof(LONG) == sizeof(std::int32_t)); static_assert(sizeof(LONG) == 4u); -static constexpr inline float d2r = float(M_PI/180); - -cc_noinline void store(float volatile& place, const float value) +never_inline void store(float volatile& place, const float value) { union { @@ -55,15 +53,17 @@ static std::int32_t load(std::int32_t volatile& place) void freetrack::pose(const double* headpose) { - const float yaw = -headpose[Yaw] * d2r; - const float roll = headpose[Roll] * d2r; + constexpr double d2r = M_PI/180; + + const float yaw = float(-headpose[Yaw] * d2r); + const float roll = float(headpose[Roll] * d2r); const float tx = float(headpose[TX] * 10); const float ty = float(headpose[TY] * 10); const float tz = float(headpose[TZ] * 10); // HACK: Falcon BMS makes a "bump" if pitch is over the value -sh 20170615 const bool is_crossing_90 = std::fabs(headpose[Pitch] - 90) < .15; - const float pitch = -d2r * (is_crossing_90 ? 89.86 : headpose[Pitch]); + const float pitch = float(-d2r * (is_crossing_90 ? 89.86 : headpose[Pitch])); FTHeap* const ft = pMemData; FTData* const data = &ft->data; diff --git a/proto-iokit-foohid/CMakeLists.txt b/proto-iokit-foohid/CMakeLists.txt index cdc8fb69..31d3dcdc 100644 --- a/proto-iokit-foohid/CMakeLists.txt +++ b/proto-iokit-foohid/CMakeLists.txt @@ -1,4 +1,4 @@ -IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") +if(CMAKE_SYSTEM_NAME MATCHES "Darwin") otr_module(proto-iokit-foohid) - set_property(TARGET opentrack-proto-iokit-foohid APPEND_STRING PROPERTY LINK_FLAGS "-framework IOKit ") + target_link_options(${self} PRIVATE -framework IOKit) endif() diff --git a/proto-iokit-foohid/lang/zh_CN.ts b/proto-iokit-foohid/lang/zh_CN.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/proto-iokit-foohid/lang/zh_CN.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/proto-libevdev/ftnoir_protocol_libevdev.h b/proto-libevdev/ftnoir_protocol_libevdev.h index 4c220df4..1a8f2236 100644 --- a/proto-libevdev/ftnoir_protocol_libevdev.h +++ b/proto-libevdev/ftnoir_protocol_libevdev.h @@ -21,16 +21,8 @@ class evdev : public TR, public IProtocol public: evdev(); ~evdev() override; - bool correct() { - return dev != NULL; - } - void pose(const double *headpose); - - QString game_name() - { - return tr("Virtual joystick for Linux"); - } - + void pose(const double *headpose) override; + QString game_name() override { return tr("Virtual joystick for Linux"); } module_status initialize() override; private: @@ -60,6 +52,6 @@ class evdevDll : public Metadata { Q_OBJECT - QString name() { return tr("libevdev joystick receiver"); } - QIcon icon() { return QIcon(":/images/linux.png"); } + QString name() override { return tr("libevdev joystick receiver"); } + QIcon icon() override { return QIcon(":/images/linux.png"); } }; diff --git a/proto-mouse/ftnoir_mousecontrols.ui b/proto-mouse/ftnoir_mousecontrols.ui index f970f887..45b33470 100644 --- a/proto-mouse/ftnoir_mousecontrols.ui +++ b/proto-mouse/ftnoir_mousecontrols.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>UICMOUSEControls</class> - <widget class="QWidget" name="UICMOUSEControls"> + <class>UI_mouse</class> + <widget class="QWidget" name="UI_mouse"> <property name="windowModality"> <enum>Qt::NonModal</enum> </property> @@ -9,70 +9,42 @@ <rect> <x>0</x> <y>0</y> - <width>413</width> - <height>155</height> + <width>360</width> + <height>146</height> </rect> </property> <property name="minimumSize"> <size> - <width>413</width> + <width>360</width> <height>0</height> </size> </property> <property name="windowTitle"> - <string>Mouse protocol settings</string> + <string>Mouse protocol</string> </property> <property name="windowIcon"> <iconset resource="win32-mouse-protocol.qrc"> <normaloff>:/images/mouse.png</normaloff>:/images/mouse.png</iconset> </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <property name="autoFillBackground"> - <bool>false</bool> - </property> - <layout class="QGridLayout" name="gridLayout"> + <layout class="QFormLayout" name="formLayout"> + <property name="leftMargin"> + <number>9</number> + </property> + <property name="topMargin"> + <number>11</number> + </property> + <property name="rightMargin"> + <number>6</number> + </property> <item row="0" column="0"> <widget class="QLabel" name="textLabel2_2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Map mouse X to:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="wordWrap"> - <bool>false</bool> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="textLabel2_3"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> <property name="text"> - <string>Map mouse Y to:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="wordWrap"> - <bool>false</bool> + <string>X axis</string> </property> </widget> </item> - <item row="0" column="1" colspan="2"> - <widget class="QComboBox" name="cbxSelectMouse_X"> + <item row="0" column="1"> + <widget class="QComboBox" name="axis_x"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> @@ -81,16 +53,10 @@ </property> <property name="maximumSize"> <size> - <width>80</width> + <width>105</width> <height>16777215</height> </size> </property> - <property name="toolTip"> - <string>Select Number</string> - </property> - <property name="insertPolicy"> - <enum>QComboBox::InsertAlphabetically</enum> - </property> <item> <property name="text"> <string>None</string> @@ -128,8 +94,38 @@ </item> </widget> </item> - <item row="1" column="1" colspan="2"> - <widget class="QComboBox" name="cbxSelectMouse_Y"> + <item row="1" column="0"> + <widget class="QLabel" name="textLabel2_6"> + <property name="text"> + <string>Sensitivity</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSlider" name="sensitivity_x"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="tickInterval"> + <number>25</number> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="textLabel2_3"> + <property name="text"> + <string>Y axis</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="axis_y"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> @@ -138,16 +134,10 @@ </property> <property name="maximumSize"> <size> - <width>80</width> + <width>105</width> <height>16777215</height> </size> </property> - <property name="toolTip"> - <string>Select Number</string> - </property> - <property name="insertPolicy"> - <enum>QComboBox::InsertAlphabetically</enum> - </property> <item> <property name="text"> <string>None</string> @@ -185,36 +175,17 @@ </item> </widget> </item> - <item row="4" column="1"> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="textLabel2_4"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> + <item row="3" column="0"> + <widget class="QLabel" name="textLabel2_7"> <property name="text"> - <string>X axis sensitivity</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="wordWrap"> - <bool>false</bool> + <string>Sensitivity</string> </property> </widget> </item> - <item row="2" column="1"> - <widget class="QSlider" name="sensitivity_x"> + <item row="3" column="1"> + <widget class="QSlider" name="sensitivity_y"> <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> @@ -227,38 +198,49 @@ </property> </widget> </item> - <item row="3" column="0"> - <widget class="QLabel" name="textLabel2_5"> + <item row="4" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Method</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QComboBox" name="input_method"> <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="text"> - <string>Y axis sensitivity</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="wordWrap"> - <bool>false</bool> + <property name="maximumSize"> + <size> + <width>105</width> + <height>16777215</height> + </size> </property> + <item> + <property name="text"> + <string>Direct input</string> + </property> + </item> + <item> + <property name="text"> + <string>Legacy</string> + </property> + </item> </widget> </item> - <item row="3" column="1"> - <widget class="QSlider" name="sensitivity_y"> + <item row="5" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="tickInterval"> - <number>25</number> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> </property> </widget> </item> @@ -268,9 +250,4 @@ <include location="win32-mouse-protocol.qrc"/> </resources> <connections/> - <slots> - <slot>startEngineClicked()</slot> - <slot>stopEngineClicked()</slot> - <slot>cameraSettingsClicked()</slot> - </slots> </ui> diff --git a/proto-mouse/ftnoir_protocol_mouse.cpp b/proto-mouse/ftnoir_protocol_mouse.cpp index 3afee411..d57d8809 100644 --- a/proto-mouse/ftnoir_protocol_mouse.cpp +++ b/proto-mouse/ftnoir_protocol_mouse.cpp @@ -9,8 +9,10 @@ #include "api/plugin-api.hpp" #include "compat/math.hpp" + #include <cmath> #include <algorithm> + #include <windows.h> #ifndef MOUSEEVENTF_MOVE_NOCOALESCE @@ -24,38 +26,56 @@ static const double invert[] = { void mouse::pose(const double* headpose) { - const int axis_x = s.Mouse_X - 1; - const int axis_y = s.Mouse_Y - 1; + const int axis_x = s.mouse_x - 1; + const int axis_y = s.mouse_y - 1; int mouse_x = 0, mouse_y = 0; - if (axis_x >= 0 && axis_x < 6) - { + if (axis_x == clamp(axis_x, Axis_MIN, Axis_MAX)) mouse_x = get_value(headpose[axis_x] * invert[axis_x], - s.sensitivity_x(), + *s.sensitivity_x, axis_x >= 3); - } - if (axis_y >= 0 && axis_y < 6) + if (axis_y == clamp(axis_y, Axis_MIN, Axis_MAX)) mouse_y = get_value(headpose[axis_y] * invert[axis_y], - s.sensitivity_y(), + *s.sensitivity_y, axis_y >= 3); - MOUSEINPUT mi; - mi.dx = get_delta(mouse_x, last_x); - mi.dy = get_delta(mouse_y, last_y); - mi.mouseData = 0; - mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_MOVE_NOCOALESCE; - mi.time = 0; - mi.dwExtraInfo = 0; - INPUT input; - input.type = INPUT_MOUSE; - input.mi = mi; - if (mi.dx || mi.dy) + const int dx = get_delta(mouse_x, last_x), + dy = get_delta(mouse_y, last_y); + + last_x = mouse_x; last_y = mouse_y; + + if (dx || dy) { - (void) SendInput(1, &input, sizeof(INPUT)); - last_x = mouse_x; - last_y = mouse_y; + switch (s.input_method) + { + default: + eval_once(qDebug() << "proto/mouse: invalid input method"); + [[fallthrough]]; + case input_direct: + { + INPUT input; + input.type = INPUT_MOUSE; + MOUSEINPUT& mi = input.mi; + mi = {}; + mi.dx = dx; + mi.dy = dy; + mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_MOVE_NOCOALESCE; + + (void)SendInput(1, &input, sizeof(input)); + + break; + } + case input_legacy: + { + POINT pt{}; + (void)GetCursorPos(&pt); + (void)SetCursorPos(pt.x + dx, pt.y + dy); + + break; + } + } } } @@ -66,9 +86,7 @@ QString mouse::game_name() int mouse::get_delta(int val, int prev) { - using std::abs; - - const int a = abs(val - prev), b = abs(val + prev); + const int a = std::abs(val - prev), b = std::abs(val + prev); if (b < a) return val + prev; else @@ -77,10 +95,9 @@ int mouse::get_delta(int val, int prev) int mouse::get_value(double val, double sensitivity, bool is_rotation) { - constexpr double sgn[] = { 1e-2, 1 }; - constexpr double c = 1e-1; + constexpr double c[] = { 1e-3, 1e-1 }; - return iround(val * c * sensitivity * sgn[unsigned(is_rotation)]); + return iround(val * sensitivity * c[unsigned(is_rotation)]); } OPENTRACK_DECLARE_PROTOCOL(mouse, MOUSEControls, mouseDll) diff --git a/proto-mouse/ftnoir_protocol_mouse.h b/proto-mouse/ftnoir_protocol_mouse.h index 2b88ae1d..b7cf61cf 100644 --- a/proto-mouse/ftnoir_protocol_mouse.h +++ b/proto-mouse/ftnoir_protocol_mouse.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015 Stanislaw Halik <sthalik@misaki.pl> +/* Copyright (c) 2015, 2019 Stanislaw Halik <sthalik@misaki.pl> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -10,49 +10,50 @@ #include "ui_ftnoir_mousecontrols.h" #include "mouse-settings.hpp" -#include "compat/tr.hpp" +#include "api/plugin-api.hpp" #include <QDebug> -#include "api/plugin-api.hpp" + using namespace options; class mouse : public TR, public IProtocol { Q_OBJECT + static int get_delta(int val, int prev); + static int get_value(double val, double sensitivity, bool is_rotation); + + int last_x = 0, last_y = 0; + mouse_settings s; + public: mouse() = default; module_status initialize() override { return status_ok(); } void pose(const double* headpose) override; QString game_name() override; - - int last_x = 0, last_y = 0; -private: - static int get_delta(int val, int prev); - static int get_value(double val, double sensitivity, bool is_rotation); - - struct mouse_settings s; }; class MOUSEControls: public IProtocolDialog { Q_OBJECT -public: - MOUSEControls(); - void register_protocol(IProtocol *) {} - void unregister_protocol() {} -private: - Ui::UICMOUSEControls ui; + + Ui::UI_mouse ui; mouse_settings s; + private slots: void doOK(); void doCancel(); + +public: + MOUSEControls(); + void register_protocol(IProtocol*) override {} + void unregister_protocol() override {} }; class mouseDll : public Metadata { Q_OBJECT - QString name() { return tr("mouse emulation"); } - QIcon icon() { return QIcon(":/images/mouse.png"); } + QString name() override { return tr("mouse emulation"); } + QIcon icon() override { return QIcon(":/images/mouse.png"); } }; diff --git a/proto-mouse/ftnoir_protocol_mouse_dialog.cpp b/proto-mouse/ftnoir_protocol_mouse_dialog.cpp index 77b1ff2e..5646718c 100644 --- a/proto-mouse/ftnoir_protocol_mouse_dialog.cpp +++ b/proto-mouse/ftnoir_protocol_mouse_dialog.cpp @@ -3,19 +3,25 @@ MOUSEControls::MOUSEControls() { - ui.setupUi( this ); + ui.setupUi(this); - connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); - connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); + connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &MOUSEControls::doOK); + connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &MOUSEControls::doCancel); - tie_setting(s.Mouse_X, ui.cbxSelectMouse_X); - tie_setting(s.Mouse_Y, ui.cbxSelectMouse_Y); + tie_setting(s.mouse_x, ui.axis_x); + tie_setting(s.mouse_y, ui.axis_y); tie_setting(s.sensitivity_x, ui.sensitivity_x); tie_setting(s.sensitivity_y, ui.sensitivity_y); + + const int data[] = { input_direct, input_legacy }; + for (unsigned k = 0; k < std::size(data); k++) + ui.input_method->setItemData(k, data[k]); + tie_setting(s.input_method, ui.input_method); } -void MOUSEControls::doOK() { +void MOUSEControls::doOK() +{ s.b->save(); close(); } @@ -24,4 +30,3 @@ void MOUSEControls::doCancel() { close(); } - diff --git a/proto-mouse/lang/nl_NL.ts b/proto-mouse/lang/nl_NL.ts index 8994756b..13a75520 100644 --- a/proto-mouse/lang/nl_NL.ts +++ b/proto-mouse/lang/nl_NL.ts @@ -2,21 +2,13 @@ <!DOCTYPE TS> <TS version="2.1" language="nl_NL"> <context> - <name>UICMOUSEControls</name> + <name>UI_mouse</name> <message> - <source>Mouse protocol settings</source> + <source>Mouse protocol</source> <translation type="unfinished"></translation> </message> <message> - <source>Map mouse X to:</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Map mouse Y to:</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Select Number</source> + <source>X axis</source> <translation type="unfinished"></translation> </message> <message> @@ -48,11 +40,23 @@ <translation type="unfinished"></translation> </message> <message> - <source>X axis sensitivity</source> + <source>Y axis</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Sensitivity</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Method</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Direct input</source> <translation type="unfinished"></translation> </message> <message> - <source>Y axis sensitivity</source> + <source>Legacy</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-mouse/lang/ru_RU.ts b/proto-mouse/lang/ru_RU.ts index ab5e650c..bdadb6bd 100644 --- a/proto-mouse/lang/ru_RU.ts +++ b/proto-mouse/lang/ru_RU.ts @@ -2,21 +2,13 @@ <!DOCTYPE TS> <TS version="2.1" language="ru_RU"> <context> - <name>UICMOUSEControls</name> + <name>UI_mouse</name> <message> - <source>Mouse protocol settings</source> + <source>Mouse protocol</source> <translation type="unfinished"></translation> </message> <message> - <source>Map mouse X to:</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Map mouse Y to:</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Select Number</source> + <source>X axis</source> <translation type="unfinished"></translation> </message> <message> @@ -48,11 +40,23 @@ <translation type="unfinished"></translation> </message> <message> - <source>X axis sensitivity</source> + <source>Y axis</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Sensitivity</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Method</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Direct input</source> <translation type="unfinished"></translation> </message> <message> - <source>Y axis sensitivity</source> + <source>Legacy</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-mouse/lang/stub.ts b/proto-mouse/lang/stub.ts index 1bbd5991..2a811df1 100644 --- a/proto-mouse/lang/stub.ts +++ b/proto-mouse/lang/stub.ts @@ -2,21 +2,13 @@ <!DOCTYPE TS> <TS version="2.1"> <context> - <name>UICMOUSEControls</name> + <name>UI_mouse</name> <message> - <source>Mouse protocol settings</source> + <source>Mouse protocol</source> <translation type="unfinished"></translation> </message> <message> - <source>Map mouse X to:</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Map mouse Y to:</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Select Number</source> + <source>X axis</source> <translation type="unfinished"></translation> </message> <message> @@ -48,11 +40,23 @@ <translation type="unfinished"></translation> </message> <message> - <source>X axis sensitivity</source> + <source>Y axis</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Sensitivity</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Method</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Direct input</source> <translation type="unfinished"></translation> </message> <message> - <source>Y axis sensitivity</source> + <source>Legacy</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-mouse/lang/zh_CN.ts b/proto-mouse/lang/zh_CN.ts index 1bbd5991..2a811df1 100644 --- a/proto-mouse/lang/zh_CN.ts +++ b/proto-mouse/lang/zh_CN.ts @@ -2,21 +2,13 @@ <!DOCTYPE TS> <TS version="2.1"> <context> - <name>UICMOUSEControls</name> + <name>UI_mouse</name> <message> - <source>Mouse protocol settings</source> + <source>Mouse protocol</source> <translation type="unfinished"></translation> </message> <message> - <source>Map mouse X to:</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Map mouse Y to:</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>Select Number</source> + <source>X axis</source> <translation type="unfinished"></translation> </message> <message> @@ -48,11 +40,23 @@ <translation type="unfinished"></translation> </message> <message> - <source>X axis sensitivity</source> + <source>Y axis</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Sensitivity</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Method</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Direct input</source> <translation type="unfinished"></translation> </message> <message> - <source>Y axis sensitivity</source> + <source>Legacy</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-mouse/mouse-settings.hpp b/proto-mouse/mouse-settings.hpp index 71726588..fda06166 100644 --- a/proto-mouse/mouse-settings.hpp +++ b/proto-mouse/mouse-settings.hpp @@ -2,22 +2,24 @@ #include "options/options.hpp" +enum input_method { + input_direct = 0, input_legacy = 1, +}; + namespace mouse_impl { using namespace options; -struct mouse_settings : opts { - value<int> Mouse_X, Mouse_Y; - value<slider_value> sensitivity_x, sensitivity_y; - mouse_settings() : - opts("mouse-proto"), - Mouse_X(b, "mouse-x", 0), - Mouse_Y(b, "mouse-y", 0), - sensitivity_x(b, "mouse-sensitivity-x", { 200, 25, 500 }), - sensitivity_y(b, "mouse-sensitivity-y", { 200, 25, 500 }) - {} +struct mouse_settings : opts +{ + value<int> mouse_x { b, "mouse-x", 0 }, mouse_y { b, "mouse-y", 0 }; + value<slider_value> sensitivity_x { b, "mouse-sensitivity-x", { 200, 25, 500 } }; + value<slider_value> sensitivity_y { b, "mouse-sensitivity-y", { 200, 25, 500 } }; + value<input_method> input_method { b, "input-method", input_direct }; + + mouse_settings() : opts("mouse-proto") {} }; } // ns mouse_impl -using mouse_impl::mouse_settings; +using mouse_settings = mouse_impl::mouse_settings; diff --git a/proto-simconnect/CMakeLists.txt b/proto-simconnect/CMakeLists.txt index 26047b6f..f5cec5ac 100644 --- a/proto-simconnect/CMakeLists.txt +++ b/proto-simconnect/CMakeLists.txt @@ -1,3 +1,3 @@ -if(WIN32) +if(WIN32 AND opentrack-intel) otr_module(proto-simconnect) endif() diff --git a/proto-simconnect/ftnoir-protocol-sc.rc b/proto-simconnect/ftnoir-protocol-sc.rc index f1a7b531..0d501812 100644 --- a/proto-simconnect/ftnoir-protocol-sc.rc +++ b/proto-simconnect/ftnoir-protocol-sc.rc @@ -1,7 +1,7 @@ #define RT_MANIFEST 24 -142 RT_MANIFEST fsx_rtm.manifest -143 RT_MANIFEST fsx_sp1.manifest -144 RT_MANIFEST fsx_sp2.manifest -145 RT_MANIFEST fsx_p3d_sp2_xpack.manifest +142 RT_MANIFEST fsx_p3d_sp2_xpack.manifest +143 RT_MANIFEST fsx_rtm.manifest +144 RT_MANIFEST fsx_sp1.manifest +145 RT_MANIFEST fsx_sp2.manifest 146 RT_MANIFEST fsx_steam_orig.manifest 147 RT_MANIFEST fsx_steam_last.manifest diff --git a/proto-simconnect/ftnoir_protocol_sc.cpp b/proto-simconnect/ftnoir_protocol_sc.cpp index bd7f0960..9a1db9b5 100644 --- a/proto-simconnect/ftnoir_protocol_sc.cpp +++ b/proto-simconnect/ftnoir_protocol_sc.cpp @@ -2,8 +2,8 @@ * * * ISC License (ISC) * * * - * Copyright (c) 2015, Wim Vriend - * Copyright (c) 2014, Stanislaw Halik <sthalik@misaki.pl> + * Copyright (c) 2015, Wim Vriend * + * Copyright (c) 2014, 2017, 2019 Stanislaw Halik * * * * Permission to use, copy, modify, and/or distribute this software for any * * purpose with or without fee is hereby granted, provided that the above * @@ -13,6 +13,7 @@ #include "api/plugin-api.hpp" #include "compat/timer.hpp" #include "compat/library-path.hpp" +#include "compat/activation-context.hpp" simconnect::~simconnect() { @@ -26,61 +27,61 @@ void simconnect::run() if (event == nullptr) { - qDebug() << "simconnect: event create" << GetLastError(); + qDebug() << "fsx: create event failed, error code" << GetLastError(); return; } + constexpr unsigned sleep_time = 5; + while (!isInterruptionRequested()) { HRESULT hr; + reconnect = false; + handle = nullptr; - if (SUCCEEDED(hr = simconnect_open(&hSimConnect, "opentrack", nullptr, 0, event, 0))) + if (!SUCCEEDED(hr = simconnect_open(&handle, "opentrack", nullptr, 0, event, 0))) + qDebug() << "fsx: connect failed, retry in" << sleep_time << "seconds..."; + else { - if (!SUCCEEDED(hr = simconnect_subscribetosystemevent(hSimConnect, 0, "Frame"))) - { - qDebug() << "simconnect: can't subscribe to frame event:" << hr; - } - Timer tm; - should_reconnect = false; - if (SUCCEEDED(hr)) + if (!SUCCEEDED(hr = simconnect_subscribe(handle, 0, "Frame"))) + qDebug() << "fsx: can't subscribe to frame event:" << (void*)hr; + else + { while (!isInterruptionRequested()) { - if (should_reconnect) - break; + constexpr int max_idle_seconds = 2; - if (WaitForSingleObject(event, 100) == WAIT_OBJECT_0) - { + if (WaitForSingleObject(event, 0) == WAIT_OBJECT_0) tm.start(); - if (!SUCCEEDED(hr = simconnect_calldispatch(hSimConnect, processNextSimconnectEvent, reinterpret_cast<void*>(this)))) - { - qDebug() << "simconnect: calldispatch failed:" << hr; - break; - } - } - else + if ((int)tm.elapsed_seconds() > max_idle_seconds) { - const int idle_seconds = tm.elapsed_seconds(); + qDebug() << "fsx: timeout reached, reconnecting"; + break; + } - constexpr int max_idle_seconds = 2; + if (reconnect) + break; - if (idle_seconds >= max_idle_seconds) - { - qDebug() << "simconnect: reconnect"; - break; - } + if (!SUCCEEDED(hr = simconnect_calldispatch(handle, event_handler, (void*)this))) + { + qDebug() << "fsx: calldispatch failed:" << (void*)hr; + break; } - } + } + } - (void) simconnect_close(hSimConnect); + (void)simconnect_close(handle); } - else - qDebug() << "simconnect: can't open handle:" << hr; - if (!isInterruptionRequested()) - Sleep(3000); + for (unsigned k = 0; k < sleep_time * 25; k++) + { + if (isInterruptionRequested()) + break; + Sleep(1000 / 25); + } } qDebug() << "simconnect: exit"; @@ -88,112 +89,74 @@ void simconnect::run() CloseHandle(event); } -void simconnect::pose( const double *headpose ) +void simconnect::pose(const double* pose) { - // euler degrees - virtSCRotX = float(-headpose[Pitch]); - virtSCRotY = float(headpose[Yaw]); - virtSCRotZ = float(headpose[Roll]); - - // cm to meters - virtSCPosX = float(-headpose[TX]/100); - virtSCPosY = float(headpose[TY]/100); - virtSCPosZ = float(-headpose[TZ]/100); -} + QMutexLocker l(&mtx); -#ifdef __GNUC__ -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif + data[Pitch] = (float)-pose[Pitch]; + data[Yaw] = (float)pose[Yaw]; + data[Roll] = (float)pose[Roll]; -class ActivationContext -{ -public: - explicit ActivationContext(int resid) - { - ACTCTXA actx = {}; - actx.cbSize = sizeof(actx); - actx.lpResourceName = MAKEINTRESOURCEA(resid); - actx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; - static const QString prefix = OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH; - QString path = prefix + "lib" "opentrack-proto-simconnect.dll"; - QByteArray name = QFile::encodeName(path); - actx.lpSource = name.constData(); - hactctx = CreateActCtxA(&actx); - if (hactctx != INVALID_HANDLE_VALUE) - { - if (!ActivateActCtx(hactctx, &actctx_cookie)) - { - qDebug() << "simconnect: can't set win32 activation context" << GetLastError(); - ReleaseActCtx(hactctx); - hactctx = INVALID_HANDLE_VALUE; - } - else - ok = true; - } else { - qDebug() << "simconnect: can't create win32 activation context" << GetLastError(); - } - } - ~ActivationContext() { - if (hactctx != INVALID_HANDLE_VALUE) - { - DeactivateActCtx(0, actctx_cookie); - ReleaseActCtx(hactctx); - } - } - bool is_ok() const { return ok; } - -private: - ULONG_PTR actctx_cookie = 0; - HANDLE hactctx = INVALID_HANDLE_VALUE; - bool ok = false; -}; + constexpr float to_meters = 1e-2f; + data[TX] = (float)-pose[TX] * to_meters; + data[TY] = (float)pose[TY] * to_meters; + data[TZ] = (float)-pose[TZ] * to_meters; +} module_status simconnect::initialize() { - if (!SCClientLib.isLoaded()) + if (!library.isLoaded()) { - ActivationContext ctx(142 + s.sxs_manifest); + constexpr int resource_offset = 142; + activation_context ctx("opentrack-proto-simconnect" "." OPENTRACK_LIBRARY_EXTENSION, + resource_offset + s.sxs_manifest); - if (ctx.is_ok()) + if (ctx) { - SCClientLib.setFileName("SimConnect.dll"); - SCClientLib.setLoadHints(QLibrary::PreventUnloadHint | QLibrary::ResolveAllSymbolsHint); - if (!SCClientLib.load()) - return error(tr("dll load failed -- %1").arg(SCClientLib.errorString())); + library.setFileName("SimConnect.dll"); + library.setLoadHints(QLibrary::PreventUnloadHint | QLibrary::ResolveAllSymbolsHint); + if (!library.load()) + return error(tr("dll load failed: %1").arg(library.errorString())); } else - return error(tr("can't load SDK -- check selected simconnect version")); + // XXX TODO add instructions for fsx and p3d -sh 20190128 + return error(tr("Install FSX/Prepar3D SimConnect SDK.")); } - simconnect_open = (importSimConnect_Open) SCClientLib.resolve("SimConnect_Open"); - if (!simconnect_open) - return error("Open function not found in DLL!"); - simconnect_set6DOF = (importSimConnect_CameraSetRelative6DOF) SCClientLib.resolve("SimConnect_CameraSetRelative6DOF"); - if (!simconnect_set6DOF) - return error("CameraSetRelative6DOF function not found in DLL!"); - simconnect_close = (importSimConnect_Close) SCClientLib.resolve("SimConnect_Close"); - if (!simconnect_close) - return error("Close function not found in DLL!"); + using ptr = decltype(library.resolve("")); - simconnect_calldispatch = (importSimConnect_CallDispatch) SCClientLib.resolve("SimConnect_CallDispatch"); - if (!simconnect_calldispatch) - return error("CallDispatch function not found in DLL!"); + struct { + const char* name; + ptr& place; + } list[] = { + { "SimConnect_Open", (ptr&)simconnect_open }, + { "SimConnect_CameraSetRelative6DOF", (ptr&)simconnect_set6DOF }, + { "SimConnect_Close", (ptr&)simconnect_close }, + { "SimConnect_CallDispatch", (ptr&)simconnect_calldispatch }, + { "SimConnect_SubscribeToSystemEvent", (ptr&)simconnect_subscribe }, + }; - simconnect_subscribetosystemevent = (importSimConnect_SubscribeToSystemEvent) SCClientLib.resolve("SimConnect_SubscribeToSystemEvent"); - if (!simconnect_subscribetosystemevent) - return error("SubscribeToSystemEvent function not found in DLL!"); + for (auto& x : list) + { + x.place = library.resolve(x.name); + if (!x.place) + return error(tr("can't import %1: %2").arg(x.name, library.errorString())); + } start(); - return status_ok(); + return {}; } -void simconnect::handle() +void simconnect::handler() { - (void) simconnect_set6DOF(hSimConnect, virtSCPosX, virtSCPosY, virtSCPosZ, virtSCRotX, virtSCRotZ, virtSCRotY); + QMutexLocker l(&mtx); + (void)simconnect_set6DOF(handle, + data[TX], data[TY], data[TZ], + data[Pitch], data[Roll], data[Yaw]); } -void CALLBACK simconnect::processNextSimconnectEvent(SIMCONNECT_RECV* pData, DWORD, void *self_) +void simconnect::event_handler(SIMCONNECT_RECV* pData, DWORD, void* self_) { simconnect& self = *reinterpret_cast<simconnect*>(self_); @@ -202,17 +165,22 @@ void CALLBACK simconnect::processNextSimconnectEvent(SIMCONNECT_RECV* pData, DWO default: break; case SIMCONNECT_RECV_ID_EXCEPTION: - qDebug() << "simconnect: got exception"; - //self.should_reconnect = true; + // CAVEAT: can't reconnect here, it breaks Prepar3D. + // the timer on the event handle will take care of failures. break; case SIMCONNECT_RECV_ID_QUIT: - qDebug() << "simconnect: got quit event"; - self.should_reconnect = true; + qDebug() << "fsx: got quit event"; + self.reconnect = true; break; case SIMCONNECT_RECV_ID_EVENT_FRAME: - self.handle(); + self.handler(); break; } } -OPENTRACK_DECLARE_PROTOCOL(simconnect, SCControls, simconnectDll) +QString simconnect::game_name() +{ + return tr("FSX / Prepar3D"); +} + +OPENTRACK_DECLARE_PROTOCOL(simconnect, simconnect_ui, simconnect_metadata) diff --git a/proto-simconnect/ftnoir_protocol_sc.h b/proto-simconnect/ftnoir_protocol_sc.h index 749f7906..a51d1518 100644 --- a/proto-simconnect/ftnoir_protocol_sc.h +++ b/proto-simconnect/ftnoir_protocol_sc.h @@ -3,7 +3,7 @@ * ISC License (ISC) * * * * Copyright (c) 2015, Wim Vriend * - * Copyright (c) 2014, 2017 Stanislaw Halik * + * Copyright (c) 2014, 2017, 2019 Stanislaw Halik * * * * Permission to use, copy, modify, and/or distribute this software for any * * purpose with or without fee is hereby granted, provided that the above * @@ -16,6 +16,7 @@ #include <atomic> #include <QThread> +#include <QMutex> #include <QMessageBox> #include <QSettings> #include <QLibrary> @@ -30,7 +31,7 @@ struct settings : opts { value<int> sxs_manifest; settings() : opts("proto-simconnect"), - sxs_manifest(b, "simconnect-manifest", 2) + sxs_manifest(b, "simconnect-manifest", 0) {} }; @@ -41,78 +42,71 @@ public: simconnect() = default; ~simconnect() override; module_status initialize() override; - void pose(const double* headpose); - void handle(); - QString game_name() - { - return tr("FS2004/FSX"); - } + void pose(const double* headpose) override; + QString game_name() override; + void run() override; + private: + void handler(); + enum { - SIMCONNECT_RECV_ID_NULL, SIMCONNECT_RECV_ID_EXCEPTION = 2, SIMCONNECT_RECV_ID_QUIT = 3, SIMCONNECT_RECV_ID_EVENT_FRAME = 7, }; - #pragma pack(push, 1) struct SIMCONNECT_RECV { DWORD dwSize; DWORD dwVersion; DWORD dwID; }; - #pragma pack(pop) - typedef void (CALLBACK *DispatchProc)(SIMCONNECT_RECV*, DWORD, void*); + typedef void (CALLBACK *SC_DispatchProc)(SIMCONNECT_RECV*, DWORD, void*); - typedef HRESULT (WINAPI *importSimConnect_Open)(HANDLE * phSimConnect, LPCSTR szName, HWND hWnd, DWORD UserEventWin32, HANDLE hEventHandle, DWORD ConfigIndex); - typedef HRESULT (WINAPI *importSimConnect_Close)(HANDLE hSimConnect); - typedef HRESULT (WINAPI *importSimConnect_CameraSetRelative6DOF)(HANDLE hSimConnect, float fDeltaX, float fDeltaY, float fDeltaZ, float fPitchDeg, float fBankDeg, float fHeadingDeg); - typedef HRESULT (WINAPI *importSimConnect_CallDispatch)(HANDLE hSimConnect, DispatchProc pfcnDispatch, void * pContext); - typedef HRESULT (WINAPI *importSimConnect_SubscribeToSystemEvent)(HANDLE hSimConnect, DWORD EventID, const char * SystemEventName); + typedef HRESULT (WINAPI *SC_Open)(HANDLE* phSimConnect, LPCSTR szName, HWND hWnd, DWORD UserEventWin32, HANDLE hEventHandle, DWORD ConfigIndex); + typedef HRESULT (WINAPI *SC_Close)(HANDLE hSimConnect); + typedef HRESULT (WINAPI *SC_CameraSetRelative6DOF)(HANDLE hSimConnect, float fDeltaX, float fDeltaY, float fDeltaZ, float fPitchDeg, float fBankDeg, float fHeadingDeg); + typedef HRESULT (WINAPI *SC_CallDispatch)(HANDLE hSimConnect, SC_DispatchProc pfcnDispatch, void * pContext); + typedef HRESULT (WINAPI *SC_SubscribeToSystemEvent)(HANDLE hSimConnect, DWORD EventID, const char * SystemEventName); - void run() override; + float data[6] {}; + QMutex mtx; + + SC_Open simconnect_open = nullptr; + SC_Close simconnect_close = nullptr; + SC_CameraSetRelative6DOF simconnect_set6DOF = nullptr; + SC_CallDispatch simconnect_calldispatch = nullptr; + SC_SubscribeToSystemEvent simconnect_subscribe = nullptr; - std::atomic<float> virtSCPosX {0}; - std::atomic<float> virtSCPosY {0}; - std::atomic<float> virtSCPosZ {0}; - std::atomic<float> virtSCRotX {0}; - std::atomic<float> virtSCRotY {0}; - std::atomic<float> virtSCRotZ {0}; - - importSimConnect_Open simconnect_open; - importSimConnect_Close simconnect_close; - importSimConnect_CameraSetRelative6DOF simconnect_set6DOF; - importSimConnect_CallDispatch simconnect_calldispatch; - importSimConnect_SubscribeToSystemEvent simconnect_subscribetosystemevent; - - HANDLE hSimConnect = nullptr; - std::atomic<bool> should_reconnect = false; - static void CALLBACK processNextSimconnectEvent(SIMCONNECT_RECV* pData, DWORD cbData, void *pContext); + HANDLE handle = nullptr; + std::atomic<bool> reconnect = false; + static void CALLBACK event_handler(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext); settings s; - QLibrary SCClientLib; + QLibrary library; }; -class SCControls: public IProtocolDialog +class simconnect_ui: public IProtocolDialog { Q_OBJECT -public: - SCControls(); - void register_protocol(IProtocol *) {} - void unregister_protocol() {} -private: + Ui::UICSCControls ui; settings s; + +public: + simconnect_ui(); + void register_protocol(IProtocol *) override {} + void unregister_protocol() override {} + private slots: void doOK(); void doCancel(); }; -class simconnectDll : public Metadata +class simconnect_metadata : public Metadata { Q_OBJECT - QString name() { return tr("Microsoft FSX SimConnect"); } - QIcon icon() { return QIcon(":/images/fsx.png"); } + QString name() override { return tr("Microsoft FSX SimConnect"); } + QIcon icon() override { return QIcon(":/images/fsx.png"); } }; diff --git a/proto-simconnect/ftnoir_protocol_sc_dialog.cpp b/proto-simconnect/ftnoir_protocol_sc_dialog.cpp index a14c6b44..c329e75d 100644 --- a/proto-simconnect/ftnoir_protocol_sc_dialog.cpp +++ b/proto-simconnect/ftnoir_protocol_sc_dialog.cpp @@ -12,23 +12,23 @@ #include <QDebug> #include "api/plugin-api.hpp" -SCControls::SCControls() +simconnect_ui::simconnect_ui() { - ui.setupUi( this ); + ui.setupUi(this); - // Connect Qt signals to member-functions connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); tie_setting(s.sxs_manifest, ui.comboBox); } -void SCControls::doOK() { +void simconnect_ui::doOK() +{ s.b->save(); close(); } -void SCControls::doCancel() +void simconnect_ui::doCancel() { close(); } diff --git a/proto-simconnect/ftnoir_sccontrols.ui b/proto-simconnect/ftnoir_sccontrols.ui index 6439550f..c272d5da 100644 --- a/proto-simconnect/ftnoir_sccontrols.ui +++ b/proto-simconnect/ftnoir_sccontrols.ui @@ -53,22 +53,22 @@ </property> <item> <property name="text"> - <string>RTM</string> + <string>Prepar3d / SP2 XPACK</string> </property> </item> <item> <property name="text"> - <string>SP1</string> + <string>RTM</string> </property> </item> <item> <property name="text"> - <string>SP2 -- Acceleration</string> + <string>SP1</string> </property> </item> <item> <property name="text"> - <string>Prepar3d SP2 XPACK</string> + <string>SP2 -- Acceleration</string> </property> </item> <item> @@ -78,7 +78,7 @@ </item> <item> <property name="text"> - <string>Steam FSX (new)</string> + <string>Steam FSX (newer)</string> </property> </item> </widget> diff --git a/proto-simconnect/lang/nl_NL.ts b/proto-simconnect/lang/nl_NL.ts index 56e2b22c..5b9acbb3 100644 --- a/proto-simconnect/lang/nl_NL.ts +++ b/proto-simconnect/lang/nl_NL.ts @@ -24,39 +24,43 @@ <translation type="unfinished"></translation> </message> <message> - <source>Prepar3d SP2 XPACK</source> + <source>Steam FSX (older)</source> <translation type="unfinished"></translation> </message> <message> - <source>Steam FSX (older)</source> + <source>You need to install SimConnect SDK for your FSX version. For the Steam version, choose SP1 in the above combo box.</source> <translation type="unfinished"></translation> </message> <message> - <source>Steam FSX (new)</source> + <source>Prepar3d / SP2 XPACK</source> <translation type="unfinished"></translation> </message> <message> - <source>You need to install SimConnect SDK for your FSX version. For the Steam version, choose SP1 in the above combo box.</source> + <source>Steam FSX (newer)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>simconnect</name> <message> - <source>dll load failed -- %1</source> + <source>dll load failed: %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>can't import %1: %2</source> <translation type="unfinished"></translation> </message> <message> - <source>can't load SDK -- check selected simconnect version</source> + <source>Install FSX/Prepar3D SimConnect SDK.</source> <translation type="unfinished"></translation> </message> <message> - <source>FS2004/FSX</source> + <source>FSX / Prepar3D</source> <translation type="unfinished"></translation> </message> </context> <context> - <name>simconnectDll</name> + <name>simconnect_metadata</name> <message> <source>Microsoft FSX SimConnect</source> <translation type="unfinished"></translation> diff --git a/proto-simconnect/lang/ru_RU.ts b/proto-simconnect/lang/ru_RU.ts index f5bc8428..0d5bb1c3 100644 --- a/proto-simconnect/lang/ru_RU.ts +++ b/proto-simconnect/lang/ru_RU.ts @@ -24,39 +24,43 @@ <translation type="unfinished"></translation> </message> <message> - <source>Prepar3d SP2 XPACK</source> + <source>Steam FSX (older)</source> <translation type="unfinished"></translation> </message> <message> - <source>Steam FSX (older)</source> + <source>You need to install SimConnect SDK for your FSX version. For the Steam version, choose SP1 in the above combo box.</source> <translation type="unfinished"></translation> </message> <message> - <source>Steam FSX (new)</source> + <source>Prepar3d / SP2 XPACK</source> <translation type="unfinished"></translation> </message> <message> - <source>You need to install SimConnect SDK for your FSX version. For the Steam version, choose SP1 in the above combo box.</source> + <source>Steam FSX (newer)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>simconnect</name> <message> - <source>dll load failed -- %1</source> + <source>dll load failed: %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>can't import %1: %2</source> <translation type="unfinished"></translation> </message> <message> - <source>can't load SDK -- check selected simconnect version</source> + <source>Install FSX/Prepar3D SimConnect SDK.</source> <translation type="unfinished"></translation> </message> <message> - <source>FS2004/FSX</source> + <source>FSX / Prepar3D</source> <translation type="unfinished"></translation> </message> </context> <context> - <name>simconnectDll</name> + <name>simconnect_metadata</name> <message> <source>Microsoft FSX SimConnect</source> <translation type="unfinished"></translation> diff --git a/proto-simconnect/lang/stub.ts b/proto-simconnect/lang/stub.ts index 5dd8d4d6..50d0fa8a 100644 --- a/proto-simconnect/lang/stub.ts +++ b/proto-simconnect/lang/stub.ts @@ -24,39 +24,43 @@ <translation type="unfinished"></translation> </message> <message> - <source>Prepar3d SP2 XPACK</source> + <source>Steam FSX (older)</source> <translation type="unfinished"></translation> </message> <message> - <source>Steam FSX (older)</source> + <source>You need to install SimConnect SDK for your FSX version. For the Steam version, choose SP1 in the above combo box.</source> <translation type="unfinished"></translation> </message> <message> - <source>Steam FSX (new)</source> + <source>Prepar3d / SP2 XPACK</source> <translation type="unfinished"></translation> </message> <message> - <source>You need to install SimConnect SDK for your FSX version. For the Steam version, choose SP1 in the above combo box.</source> + <source>Steam FSX (newer)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>simconnect</name> <message> - <source>dll load failed -- %1</source> + <source>dll load failed: %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>can't import %1: %2</source> <translation type="unfinished"></translation> </message> <message> - <source>can't load SDK -- check selected simconnect version</source> + <source>Install FSX/Prepar3D SimConnect SDK.</source> <translation type="unfinished"></translation> </message> <message> - <source>FS2004/FSX</source> + <source>FSX / Prepar3D</source> <translation type="unfinished"></translation> </message> </context> <context> - <name>simconnectDll</name> + <name>simconnect_metadata</name> <message> <source>Microsoft FSX SimConnect</source> <translation type="unfinished"></translation> diff --git a/proto-simconnect/lang/zh_CN.ts b/proto-simconnect/lang/zh_CN.ts index 5dd8d4d6..50d0fa8a 100644 --- a/proto-simconnect/lang/zh_CN.ts +++ b/proto-simconnect/lang/zh_CN.ts @@ -24,39 +24,43 @@ <translation type="unfinished"></translation> </message> <message> - <source>Prepar3d SP2 XPACK</source> + <source>Steam FSX (older)</source> <translation type="unfinished"></translation> </message> <message> - <source>Steam FSX (older)</source> + <source>You need to install SimConnect SDK for your FSX version. For the Steam version, choose SP1 in the above combo box.</source> <translation type="unfinished"></translation> </message> <message> - <source>Steam FSX (new)</source> + <source>Prepar3d / SP2 XPACK</source> <translation type="unfinished"></translation> </message> <message> - <source>You need to install SimConnect SDK for your FSX version. For the Steam version, choose SP1 in the above combo box.</source> + <source>Steam FSX (newer)</source> <translation type="unfinished"></translation> </message> </context> <context> <name>simconnect</name> <message> - <source>dll load failed -- %1</source> + <source>dll load failed: %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>can't import %1: %2</source> <translation type="unfinished"></translation> </message> <message> - <source>can't load SDK -- check selected simconnect version</source> + <source>Install FSX/Prepar3D SimConnect SDK.</source> <translation type="unfinished"></translation> </message> <message> - <source>FS2004/FSX</source> + <source>FSX / Prepar3D</source> <translation type="unfinished"></translation> </message> </context> <context> - <name>simconnectDll</name> + <name>simconnect_metadata</name> <message> <source>Microsoft FSX SimConnect</source> <translation type="unfinished"></translation> diff --git a/proto-udp/ftnoir_protocol_ftn.cpp b/proto-udp/ftnoir_protocol_ftn.cpp index 4d57140f..000a0a79 100644 --- a/proto-udp/ftnoir_protocol_ftn.cpp +++ b/proto-udp/ftnoir_protocol_ftn.cpp @@ -18,16 +18,21 @@ udp::udp() QObject::connect(s.b.get(), &bundle_::changed, this, &udp::set_dest_address, - Qt::QueuedConnection); + Qt::DirectConnection); } -void udp::pose(const double *headpose) { +void udp::pose(const double *headpose) +{ + QMutexLocker l(&lock); + outSocket.writeDatagram((const char *) headpose, sizeof(double[6]), dest_ip, dest_port); } void udp::set_dest_address() { - dest_port = s.port; + QMutexLocker l(&lock); + + dest_port = (unsigned short)s.port; dest_ip = QHostAddress((s.ip1.to<unsigned>() & 0xff) << 24 | (s.ip2.to<unsigned>() & 0xff) << 16 | (s.ip3.to<unsigned>() & 0xff) << 8 | diff --git a/proto-udp/ftnoir_protocol_ftn.h b/proto-udp/ftnoir_protocol_ftn.h index dc6e3a6f..53387a6c 100644 --- a/proto-udp/ftnoir_protocol_ftn.h +++ b/proto-udp/ftnoir_protocol_ftn.h @@ -11,10 +11,10 @@ #pragma once #include "ui_ftnoir_ftncontrols.h" -#include <QUdpSocket> #include "api/plugin-api.hpp" #include "options/options.hpp" using namespace options; +#include <QUdpSocket> struct settings : opts { value<int> ip1, ip2, ip3, ip4, port; @@ -35,15 +35,14 @@ class udp : public QObject, public IProtocol public: udp(); module_status initialize() override; - void pose(const double *headpose); - QString game_name() - { - return tr("UDP over network"); - } + void pose(const double *headpose) override; + QString game_name() override { return tr("UDP over network"); } private: QUdpSocket outSocket; settings s; + mutable QMutex lock; + QHostAddress dest_ip { 127u << 24 | 1u }; unsigned short dest_port = 65535; @@ -58,8 +57,8 @@ class FTNControls: public IProtocolDialog public: FTNControls(); - void register_protocol(IProtocol *) {} - void unregister_protocol() {} + void register_protocol(IProtocol *) override {} + void unregister_protocol() override {} private: Ui::UICFTNControls ui; settings s; @@ -72,6 +71,6 @@ class udp_sender_dll : public Metadata { Q_OBJECT - QString name() { return tr("UDP over network"); } - QIcon icon() { return QIcon(":/images/opentrack.png"); } + QString name() override { return tr("UDP over network"); } + QIcon icon() override { return QIcon(":/images/opentrack.png"); } }; diff --git a/proto-vjoystick/CMakeLists.txt b/proto-vjoystick/CMakeLists.txt index 41af2883..3dbf5bea 100644 --- a/proto-vjoystick/CMakeLists.txt +++ b/proto-vjoystick/CMakeLists.txt @@ -1,4 +1,4 @@ -if(WIN32) +if(WIN32 AND opentrack-intel) set(SDK_VJOYSTICK "" CACHE PATH "vjoystick SDK path") if(SDK_VJOYSTICK) if(opentrack-64bit) diff --git a/proto-vjoystick/lang/nl_NL.ts b/proto-vjoystick/lang/nl_NL.ts index 46d0398d..22644924 100644 --- a/proto-vjoystick/lang/nl_NL.ts +++ b/proto-vjoystick/lang/nl_NL.ts @@ -4,53 +4,66 @@ <context> <name>vjoystick</name> <message> - <source>VJoy</source> + <source>vjoystick won't work without the driver installed.</source> <translation type="unfinished"></translation> </message> <message> - <source><html><head/><body><p>Go to the <a href="http://vjoystick.sourceforge.net/site/"><span style=" text-decoration: underline; color:#0000ff;">vjoystick</span></a> project site or <a href="https://sourceforge.net/projects/vjoystick/files/latest/download"><span style=" text-decoration: underline; color:#0000ff;">download directly</span></a></p></body></html></source> + <source>Download the driver</source> <translation type="unfinished"></translation> </message> -</context> -<context> - <name>vjoystick_metadata</name> <message> - <source>Joystick emulation -- vjoystick</source> + <source>Visit project site</source> <translation type="unfinished"></translation> </message> -</context> -<context> - <name>vjoystick_proto</name> <message> - <source>vjoystick driver missing</source> + <source>Virtual joystick</source> <translation type="unfinished"></translation> </message> <message> - <source>vjoystick won't work without the driver installed.</source> + <source>driver/SDK version mismatch</source> <translation type="unfinished"></translation> </message> <message> - <source>Download the driver</source> + <source>BUG: joystick claims it's in use</source> <translation type="unfinished"></translation> </message> <message> - <source>Visit project site</source> + <source>Virtual joystick already in use</source> <translation type="unfinished"></translation> </message> <message> - <source>vjoystick not installed or disabled</source> + <source>Device missing. Add joystick #1.</source> <translation type="unfinished"></translation> </message> <message> - <source>can't initialize vjoystick</source> + <source>Unknown error</source> <translation type="unfinished"></translation> </message> <message> - <source>unknown error</source> + <source>Unknown error #%1</source> <translation type="unfinished"></translation> </message> <message> - <source>Virtual joystick</source> + <source>vjoystick driver problem</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Driver problem.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>vjoystick</source> + <translation type="unfinished"></translation> + </message> + <message> + <source><html><head/><body><p>Go to the <a href="http://vjoystick.sourceforge.net/site/"><span style=" text-decoration: underline; color:#0000ff;">vjoystick</span></a> project site or <a href="https://sourceforge.net/projects/vjoystick/files/latest/download"><span style=" text-decoration: underline; color:#0000ff;">download directly</span></a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>vjoystick_metadata</name> + <message> + <source>Joystick emulation -- vjoystick</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-vjoystick/lang/ru_RU.ts b/proto-vjoystick/lang/ru_RU.ts index 6a618904..b02030e3 100644 --- a/proto-vjoystick/lang/ru_RU.ts +++ b/proto-vjoystick/lang/ru_RU.ts @@ -4,53 +4,66 @@ <context> <name>vjoystick</name> <message> - <source>VJoy</source> + <source>vjoystick won't work without the driver installed.</source> <translation type="unfinished"></translation> </message> <message> - <source><html><head/><body><p>Go to the <a href="http://vjoystick.sourceforge.net/site/"><span style=" text-decoration: underline; color:#0000ff;">vjoystick</span></a> project site or <a href="https://sourceforge.net/projects/vjoystick/files/latest/download"><span style=" text-decoration: underline; color:#0000ff;">download directly</span></a></p></body></html></source> + <source>Download the driver</source> <translation type="unfinished"></translation> </message> -</context> -<context> - <name>vjoystick_metadata</name> <message> - <source>Joystick emulation -- vjoystick</source> + <source>Visit project site</source> <translation type="unfinished"></translation> </message> -</context> -<context> - <name>vjoystick_proto</name> <message> - <source>vjoystick driver missing</source> + <source>Virtual joystick</source> <translation type="unfinished"></translation> </message> <message> - <source>vjoystick won't work without the driver installed.</source> + <source>driver/SDK version mismatch</source> <translation type="unfinished"></translation> </message> <message> - <source>Download the driver</source> + <source>BUG: joystick claims it's in use</source> <translation type="unfinished"></translation> </message> <message> - <source>Visit project site</source> + <source>Virtual joystick already in use</source> <translation type="unfinished"></translation> </message> <message> - <source>vjoystick not installed or disabled</source> + <source>Device missing. Add joystick #1.</source> <translation type="unfinished"></translation> </message> <message> - <source>can't initialize vjoystick</source> + <source>Unknown error</source> <translation type="unfinished"></translation> </message> <message> - <source>unknown error</source> + <source>Unknown error #%1</source> <translation type="unfinished"></translation> </message> <message> - <source>Virtual joystick</source> + <source>vjoystick driver problem</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Driver problem.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>vjoystick</source> + <translation type="unfinished"></translation> + </message> + <message> + <source><html><head/><body><p>Go to the <a href="http://vjoystick.sourceforge.net/site/"><span style=" text-decoration: underline; color:#0000ff;">vjoystick</span></a> project site or <a href="https://sourceforge.net/projects/vjoystick/files/latest/download"><span style=" text-decoration: underline; color:#0000ff;">download directly</span></a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>vjoystick_metadata</name> + <message> + <source>Joystick emulation -- vjoystick</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-vjoystick/lang/stub.ts b/proto-vjoystick/lang/stub.ts index c8909cfe..0fe38ef2 100644 --- a/proto-vjoystick/lang/stub.ts +++ b/proto-vjoystick/lang/stub.ts @@ -4,53 +4,66 @@ <context> <name>vjoystick</name> <message> - <source>VJoy</source> + <source>vjoystick won't work without the driver installed.</source> <translation type="unfinished"></translation> </message> <message> - <source><html><head/><body><p>Go to the <a href="http://vjoystick.sourceforge.net/site/"><span style=" text-decoration: underline; color:#0000ff;">vjoystick</span></a> project site or <a href="https://sourceforge.net/projects/vjoystick/files/latest/download"><span style=" text-decoration: underline; color:#0000ff;">download directly</span></a></p></body></html></source> + <source>Download the driver</source> <translation type="unfinished"></translation> </message> -</context> -<context> - <name>vjoystick_metadata</name> <message> - <source>Joystick emulation -- vjoystick</source> + <source>Visit project site</source> <translation type="unfinished"></translation> </message> -</context> -<context> - <name>vjoystick_proto</name> <message> - <source>vjoystick driver missing</source> + <source>Virtual joystick</source> <translation type="unfinished"></translation> </message> <message> - <source>vjoystick won't work without the driver installed.</source> + <source>driver/SDK version mismatch</source> <translation type="unfinished"></translation> </message> <message> - <source>Download the driver</source> + <source>BUG: joystick claims it's in use</source> <translation type="unfinished"></translation> </message> <message> - <source>Visit project site</source> + <source>Virtual joystick already in use</source> <translation type="unfinished"></translation> </message> <message> - <source>vjoystick not installed or disabled</source> + <source>Device missing. Add joystick #1.</source> <translation type="unfinished"></translation> </message> <message> - <source>can't initialize vjoystick</source> + <source>Unknown error</source> <translation type="unfinished"></translation> </message> <message> - <source>unknown error</source> + <source>Unknown error #%1</source> <translation type="unfinished"></translation> </message> <message> - <source>Virtual joystick</source> + <source>vjoystick driver problem</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Driver problem.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>vjoystick</source> + <translation type="unfinished"></translation> + </message> + <message> + <source><html><head/><body><p>Go to the <a href="http://vjoystick.sourceforge.net/site/"><span style=" text-decoration: underline; color:#0000ff;">vjoystick</span></a> project site or <a href="https://sourceforge.net/projects/vjoystick/files/latest/download"><span style=" text-decoration: underline; color:#0000ff;">download directly</span></a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>vjoystick_metadata</name> + <message> + <source>Joystick emulation -- vjoystick</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-vjoystick/lang/zh_CN.ts b/proto-vjoystick/lang/zh_CN.ts index c8909cfe..0fe38ef2 100644 --- a/proto-vjoystick/lang/zh_CN.ts +++ b/proto-vjoystick/lang/zh_CN.ts @@ -4,53 +4,66 @@ <context> <name>vjoystick</name> <message> - <source>VJoy</source> + <source>vjoystick won't work without the driver installed.</source> <translation type="unfinished"></translation> </message> <message> - <source><html><head/><body><p>Go to the <a href="http://vjoystick.sourceforge.net/site/"><span style=" text-decoration: underline; color:#0000ff;">vjoystick</span></a> project site or <a href="https://sourceforge.net/projects/vjoystick/files/latest/download"><span style=" text-decoration: underline; color:#0000ff;">download directly</span></a></p></body></html></source> + <source>Download the driver</source> <translation type="unfinished"></translation> </message> -</context> -<context> - <name>vjoystick_metadata</name> <message> - <source>Joystick emulation -- vjoystick</source> + <source>Visit project site</source> <translation type="unfinished"></translation> </message> -</context> -<context> - <name>vjoystick_proto</name> <message> - <source>vjoystick driver missing</source> + <source>Virtual joystick</source> <translation type="unfinished"></translation> </message> <message> - <source>vjoystick won't work without the driver installed.</source> + <source>driver/SDK version mismatch</source> <translation type="unfinished"></translation> </message> <message> - <source>Download the driver</source> + <source>BUG: joystick claims it's in use</source> <translation type="unfinished"></translation> </message> <message> - <source>Visit project site</source> + <source>Virtual joystick already in use</source> <translation type="unfinished"></translation> </message> <message> - <source>vjoystick not installed or disabled</source> + <source>Device missing. Add joystick #1.</source> <translation type="unfinished"></translation> </message> <message> - <source>can't initialize vjoystick</source> + <source>Unknown error</source> <translation type="unfinished"></translation> </message> <message> - <source>unknown error</source> + <source>Unknown error #%1</source> <translation type="unfinished"></translation> </message> <message> - <source>Virtual joystick</source> + <source>vjoystick driver problem</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Driver problem.</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>vjoystick</source> + <translation type="unfinished"></translation> + </message> + <message> + <source><html><head/><body><p>Go to the <a href="http://vjoystick.sourceforge.net/site/"><span style=" text-decoration: underline; color:#0000ff;">vjoystick</span></a> project site or <a href="https://sourceforge.net/projects/vjoystick/files/latest/download"><span style=" text-decoration: underline; color:#0000ff;">download directly</span></a>.</p></body></html></source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>vjoystick_metadata</name> + <message> + <source>Joystick emulation -- vjoystick</source> <translation type="unfinished"></translation> </message> </context> diff --git a/proto-vjoystick/vjoystick.cpp b/proto-vjoystick/vjoystick.cpp index bb0f0f51..268fa343 100644 --- a/proto-vjoystick/vjoystick.cpp +++ b/proto-vjoystick/vjoystick.cpp @@ -26,7 +26,7 @@ #define OPENTRACK_VJOYSTICK_ID 1 -const unsigned char handle::axis_ids[6] = +const unsigned char vjoystick::axis_ids[6] = { HID_USAGE_X, HID_USAGE_Y, @@ -39,7 +39,7 @@ const unsigned char handle::axis_ids[6] = // HID_USAGE_WHL, }; -static const double val_minmax[6] = +static constexpr double val_minmax[6] = { 50, 50, @@ -49,69 +49,96 @@ static const double val_minmax[6] = 180 }; -void handle::init() +bool vjoystick::init() { + if (!AcquireVJD(OPENTRACK_VJOYSTICK_ID)) + return false; + + unsigned cnt = 0; + for (unsigned i = 0; i < axis_count; i++) { - if (!GetVJDAxisExist(OPENTRACK_VJOYSTICK_ID, axis_ids[i])) + bool status = true; + + status &= !!GetVJDAxisExist(OPENTRACK_VJOYSTICK_ID, axis_ids[i]); + status &= !!GetVJDAxisMin(OPENTRACK_VJOYSTICK_ID, axis_ids[i], &axis_min[i]); + status &= !!GetVJDAxisMax(OPENTRACK_VJOYSTICK_ID, axis_ids[i], &axis_max[i]); + + if (!status) { - // avoid floating point division by zero axis_min[i] = 0; - axis_max[i] = 1; - continue; + axis_max[i] = 0; } - GetVJDAxisMin(OPENTRACK_VJOYSTICK_ID, axis_ids[i], &axis_min[i]); - GetVJDAxisMax(OPENTRACK_VJOYSTICK_ID, axis_ids[i], &axis_max[i]); - } - (void) ResetVJD(OPENTRACK_VJOYSTICK_ID); -} - -handle::handle() -{ - const bool ret = AcquireVJD(OPENTRACK_VJOYSTICK_ID); - if (!ret) - { - if (!isVJDExists(OPENTRACK_VJOYSTICK_ID)) - joy_state = state_notent; else - joy_state = state_fail; - } - else - { - joy_state = state_success; - init(); + cnt++; } -} -handle::~handle() -{ - if (joy_state == state_success) + if (!cnt) { - (void) RelinquishVJD(OPENTRACK_VJOYSTICK_ID); - joy_state = state_fail; + RelinquishVJD(OPENTRACK_VJOYSTICK_ID); + return false; } + else + return true; } -LONG handle::to_axis_value(unsigned axis_id, double val) +int vjoystick::to_axis_value(unsigned axis_id, double val) const { const double minmax = val_minmax[axis_id]; const double min = axis_min[axis_id]; const double max = axis_max[axis_id]; - return LONG(clamp((val+minmax) * max / (2*minmax) - min, min, max)); + return (int)(clamp((val+minmax) * max / (2*minmax) - min, min, max)); } -vjoystick_proto::vjoystick_proto() = default; -vjoystick_proto::~vjoystick_proto() = default; +vjoystick::vjoystick() = default; +vjoystick::~vjoystick() +{ + if (status) + RelinquishVJD(OPENTRACK_VJOYSTICK_ID); +} -module_status vjoystick_proto::initialize() +module_status vjoystick::initialize() { - if (h.get_state() != state_success) + QString msg; + + if (!vJoyEnabled()) + msg = tr("vjoystick won't work without the driver installed."); + else if (WORD VerDll, VerDrv; !DriverMatch(&VerDll, &VerDrv)) + msg = tr("driver/SDK version mismatch"); + else + { + int code; + switch (code = GetVJDStatus(OPENTRACK_VJOYSTICK_ID)) + { + case VJD_STAT_OWN: + msg = tr("BUG: handle leak."); + break; + case VJD_STAT_FREE: + break; + case VJD_STAT_BUSY: + msg = tr("Virtual joystick already in use."); + break; + case VJD_STAT_MISS: + msg = tr("Device missing. Add joystick #1."); + break; + case VJD_STAT_UNKN: + msg = tr("Unknown error."); + break; + default: + msg = tr("Unknown error #%1.").arg(code); + break; + } + } + + status = msg.isNull(); + + if (!status) { QMessageBox msgbox; msgbox.setIcon(QMessageBox::Critical); - msgbox.setText(tr("vjoystick driver missing")); - msgbox.setInformativeText(tr("vjoystick won't work without the driver installed.")); + msgbox.setText(tr("vjoystick driver problem")); + msgbox.setInformativeText(msg); QPushButton* driver_button = msgbox.addButton(tr("Download the driver"), QMessageBox::ActionRole); QPushButton* project_site_button = msgbox.addButton(tr("Visit project site"), QMessageBox::ActionRole); @@ -131,28 +158,32 @@ module_status vjoystick_proto::initialize() } } - switch (h.get_state()) - { - case state_notent: - return error(tr("vjoystick not installed or disabled")); - case state_fail: - return error(tr("can't initialize vjoystick")); - case state_success: - return status_ok(); - default: - return error(tr("unknown error")); - } + if (!status) + return error(tr("Driver problem.")); + else + return {}; } -void vjoystick_proto::pose(const double *pose) +void vjoystick::pose(const double *pose) { - if (h.get_state() != state_success) + if (first_run) + { + status = init(); + //status &= !!ResetVJD(OPENTRACK_VJOYSTICK_ID); + first_run = false; + } + + if (!status) return; - for (unsigned i = 0; i < handle::axis_count; i++) + for (unsigned i = 0; i < vjoystick::axis_count; i++) { - SetAxis(h.to_axis_value(i, pose[i]), OPENTRACK_VJOYSTICK_ID, handle::axis_ids[i]); + if (axis_min[i] == axis_max[i]) + continue; + + int val = to_axis_value(i, pose[i]); + SetAxis(val, OPENTRACK_VJOYSTICK_ID, vjoystick::axis_ids[i]); } } -OPENTRACK_DECLARE_PROTOCOL(vjoystick_proto, vjoystick_dialog, vjoystick_metadata) +OPENTRACK_DECLARE_PROTOCOL(vjoystick, vjoystick_dialog, vjoystick_metadata) diff --git a/proto-vjoystick/vjoystick.h b/proto-vjoystick/vjoystick.h index 81f70a7f..7e119b21 100644 --- a/proto-vjoystick/vjoystick.h +++ b/proto-vjoystick/vjoystick.h @@ -10,46 +10,32 @@ #include "api/plugin-api.hpp" #include "compat/macros.hpp" -#include <windows.h> - -enum state : signed char -{ - state_notent = -1, - state_fail = -2, - state_success = 1, -}; - -class handle final +enum status { -public: - static constexpr unsigned axis_count = 6; - static const unsigned char axis_ids[axis_count]; - -private: - state joy_state; - LONG axis_min[6] {}; - LONG axis_max[6] {}; - - void init(); -public: - handle(); - ~handle(); - state get_state() { return joy_state; } - LONG to_axis_value(unsigned axis_id, double val); }; -class vjoystick_proto : public TR, public IProtocol +class vjoystick : public TR, public IProtocol { Q_OBJECT - handle h; public: - vjoystick_proto(); - ~vjoystick_proto() override; + vjoystick(); + ~vjoystick() override; module_status initialize() override; void pose( const double *headpose ) override; QString game_name() override { return tr("Virtual joystick"); } + private: + long axis_min[6] {}; + long axis_max[6] {}; + [[nodiscard]] bool init(); + int to_axis_value(unsigned axis_id, double val) const; + + static constexpr unsigned axis_count = 6; + static const unsigned char axis_ids[axis_count]; + + bool status = false; + bool first_run = true; }; class vjoystick_dialog final : public IProtocolDialog @@ -68,6 +54,6 @@ class vjoystick_metadata : public Metadata { Q_OBJECT - QString name() { return tr("Joystick emulation -- vjoystick"); } - QIcon icon() { return QIcon(":/images/vjoystick.png"); } + QString name() override { return tr("Joystick emulation -- vjoystick"); } + QIcon icon() override { return QIcon(":/images/vjoystick.png"); } }; diff --git a/proto-vjoystick/vjoystick.ui b/proto-vjoystick/vjoystick.ui index 6e6a2b33..5bfdb02a 100644 --- a/proto-vjoystick/vjoystick.ui +++ b/proto-vjoystick/vjoystick.ui @@ -2,9 +2,6 @@ <ui version="4.0"> <class>vjoystick</class> <widget class="QWidget" name="vjoystick"> - <property name="windowModality"> - <enum>Qt::NonModal</enum> - </property> <property name="geometry"> <rect> <x>0</x> @@ -14,23 +11,17 @@ </rect> </property> <property name="windowTitle"> - <string>VJoy</string> + <string>vjoystick</string> </property> <property name="windowIcon"> <iconset resource="vjoystick.qrc"> <normaloff>:/images/vjoystick.png</normaloff>:/images/vjoystick.png</iconset> </property> - <property name="layoutDirection"> - <enum>Qt::LeftToRight</enum> - </property> - <property name="autoFillBackground"> - <bool>false</bool> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> <widget class="QLabel" name="label"> <property name="text"> - <string><html><head/><body><p>Go to the <a href="http://vjoystick.sourceforge.net/site/"><span style=" text-decoration: underline; color:#0000ff;">vjoystick</span></a> project site or <a href="https://sourceforge.net/projects/vjoystick/files/latest/download"><span style=" text-decoration: underline; color:#0000ff;">download directly</span></a></p></body></html></string> + <string><html><head/><body><p>Go to the <a href="http://vjoystick.sourceforge.net/site/"><span style=" text-decoration: underline; color:#0000ff;">vjoystick</span></a> project site or <a href="https://sourceforge.net/projects/vjoystick/files/latest/download"><span style=" text-decoration: underline; color:#0000ff;">download directly</span></a>.</p></body></html></string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> @@ -40,10 +31,10 @@ </property> </widget> </item> - <item> + <item row="1" column="0"> <widget class="QDialogButtonBox" name="buttonBox"> <property name="standardButtons"> - <set>QDialogButtonBox::Ok</set> + <set>QDialogButtonBox::Close</set> </property> </widget> </item> diff --git a/proto-vjoystick/vjoystick_dialog.cpp b/proto-vjoystick/vjoystick_dialog.cpp index 0a1fa9b0..382d8fb3 100644 --- a/proto-vjoystick/vjoystick_dialog.cpp +++ b/proto-vjoystick/vjoystick_dialog.cpp @@ -7,4 +7,5 @@ vjoystick_dialog::vjoystick_dialog() { ui.setupUi(this); connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &QWidget::close); + connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close); } diff --git a/proto-wine/CMakeLists.txt b/proto-wine/CMakeLists.txt index 86559435..b0f18bb2 100644 --- a/proto-wine/CMakeLists.txt +++ b/proto-wine/CMakeLists.txt @@ -1,10 +1,16 @@ if(NOT WIN32) - set(SDK_WINE_PREFIX "" CACHE PATH "Wine install prefix") - set(SDK_WINE_NO_WRAPPER FALSE CACHE BOOL "disable Wine wrapper -- use Wine only for X-Plane") - if(SDK_WINE_PREFIX) + set(SDK_WINE "" CACHE BOOL "Build for Wine") + set(no-wrapper FALSE) + if(NOT SDK_WINE AND SDK_XPLANE) + set(no-wrapper TRUE) + endif() + if(SDK_WINE OR no-wrapper) + if(no-wrapper) + add_definitions(-DOTR_WINE_NO_WRAPPER) + endif() otr_module(proto-wine) - target_link_libraries(opentrack-proto-wine opentrack-csv) - if(NOT SDK_WINE_NO_WRAPPER) + if(NOT no-wrapper) + target_link_libraries(opentrack-proto-wine opentrack-csv) set(my-rt -lrt) if(APPLE) set(my-rt) @@ -14,11 +20,11 @@ if(NOT WIN32) add_custom_command( OUTPUT opentrack-wrapper-wine.exe.so DEPENDS ${wine-deps} - COMMAND ${SDK_WINE_PREFIX}/bin/wineg++ -g -DNOMINMAX -O2 -m32 -std=c++17 -fPIC -o + COMMAND wineg++ -mconsole -g -DNOMINMAX -O2 -m32 -std=c++17 -fPIC -o opentrack-wrapper-wine.exe -I "${CMAKE_SOURCE_DIR}" -I "${CMAKE_BINARY_DIR}" - ${wine-deps} + ${wine-deps} -Wall -Wextra -Wpedantic ${my-rt}) - add_custom_target(wine-wrapper ALL DEPENDS opentrack-wrapper-wine.exe.so) + add_custom_target(wine-wrapper DEPENDS opentrack-wrapper-wine.exe.so) add_dependencies(opentrack-proto-wine wine-wrapper) add_dependencies(wine-wrapper opentrack-compat) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/opentrack-wrapper-wine.exe.so" DESTINATION ${opentrack-hier-pfx}) diff --git a/proto-wine/ftnoir_protocol_wine.cpp b/proto-wine/ftnoir_protocol_wine.cpp index 7d415555..edeb8ce4 100644 --- a/proto-wine/ftnoir_protocol_wine.cpp +++ b/proto-wine/ftnoir_protocol_wine.cpp @@ -7,28 +7,23 @@ #include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #include <fcntl.h> /* For O_* constants */ -#include "csv/csv.h" +#ifndef OTR_WINE_NO_WRAPPER +# include "csv/csv.h" +#endif #include "compat/macros.hpp" #include "compat/library-path.hpp" -wine::wine() : lck_shm(WINE_SHM_NAME, WINE_MTX_NAME, sizeof(WineSHM)), shm(NULL), gameid(0) -{ - if (lck_shm.success()) { - shm = (WineSHM*) lck_shm.ptr(); - memset(shm, 0, sizeof(*shm)); - } - static const QString library_path(QCoreApplication::applicationDirPath() + OPENTRACK_LIBRARY_PATH); - wrapper.setWorkingDirectory(QCoreApplication::applicationDirPath()); - wrapper.start("wine", QStringList() << (library_path + "opentrack-wrapper-wine.exe.so")); -} +wine::wine() = default; wine::~wine() { +#ifndef OTR_WINE_NO_WRAPPER if (shm) { shm->stop = true; wrapper.waitForFinished(100); } - wrapper.close(); + wrapper.kill(); +#endif //shm_unlink("/" WINE_SHM_NAME); } @@ -38,9 +33,10 @@ void wine::pose( const double *headpose ) { lck_shm.lock(); for (int i = 3; i < 6; i++) - shm->data[i] = headpose[i] / (180 / M_PI ); + shm->data[i] = (headpose[i] * M_PI) / 180; for (int i = 0; i < 3; i++) shm->data[i] = headpose[i] * 10; +#ifndef OTR_WINE_NO_WRAPPER if (shm->gameid != gameid) { QString gamename; @@ -51,16 +47,29 @@ void wine::pose( const double *headpose ) gameid = shm->gameid2 = shm->gameid; connected_game = gamename; } +#endif lck_shm.unlock(); } } module_status wine::initialize() { +#ifndef OTR_WINE_NO_WRAPPER + static const QString library_path(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH); + wrapper.setWorkingDirectory(OPENTRACK_BASE_PATH); + wrapper.start("wine", { library_path + "opentrack-wrapper-wine.exe.so" }); +#endif + + if (lck_shm.success()) + { + shm = (WineSHM*) lck_shm.ptr(); + memset(shm, 0, sizeof(*shm)); + } + if (lck_shm.success()) return status_ok(); else return error(tr("Can't open shared memory mapping")); } -OPENTRACK_DECLARE_PROTOCOL(wine, FTControls, wineDll) +OPENTRACK_DECLARE_PROTOCOL(wine, FTControls, wine_metadata) diff --git a/proto-wine/ftnoir_protocol_wine.h b/proto-wine/ftnoir_protocol_wine.h index 19b905e7..b4cbd305 100644 --- a/proto-wine/ftnoir_protocol_wine.h +++ b/proto-wine/ftnoir_protocol_wine.h @@ -1,16 +1,16 @@ #pragma once -#include "ui_ftnoir_winecontrols.h" -#include <QMessageBox> -#include <QProcess> -#include <QDebug> -#include <QMutex> -#include <QMutexLocker> -#include <QFile> #include "api/plugin-api.hpp" #include "compat/shm.h" #include "wine-shm.h" -#include "compat/tr.hpp" + +#include "ui_ftnoir_winecontrols.h" + +#include <QString> +#include <QProcess> +#include <QMutex> + +#include <QDebug> class wine : TR, public IProtocol { @@ -25,21 +25,29 @@ public: QString game_name() override { +#ifndef OTR_WINE_NO_WRAPPER QMutexLocker foo(&game_name_mutex); return connected_game; +#else + return QStringLiteral("X-Plane"); +#endif } private: - shm_wrapper lck_shm; - WineSHM* shm; + shm_wrapper lck_shm { WINE_SHM_NAME, WINE_MTX_NAME, sizeof(WineSHM) }; + WineSHM* shm = nullptr; + +#ifndef OTR_WINE_NO_WRAPPER QProcess wrapper; - int gameid; + int gameid = 0; QString connected_game; QMutex game_name_mutex; +#endif }; class FTControls: public IProtocolDialog { Q_OBJECT + public: FTControls(); void register_protocol(IProtocol *) override {} @@ -53,11 +61,16 @@ private slots: void doCancel(); }; -class wineDll : public Metadata +class wine_metadata : public Metadata { Q_OBJECT public: +#ifndef OTR_WINE_NO_WRAPPER QString name() override { return tr("Wine -- Windows layer for Unix"); } QIcon icon() override { return QIcon(":/images/wine.png"); } +#else + QString name() override { return tr("X-Plane"); } + QIcon icon() override { return {}; } +#endif }; diff --git a/proto-wine/lang/nl_NL.ts b/proto-wine/lang/nl_NL.ts index 49051961..239e73d1 100644 --- a/proto-wine/lang/nl_NL.ts +++ b/proto-wine/lang/nl_NL.ts @@ -28,10 +28,14 @@ </message> </context> <context> - <name>wineDll</name> + <name>wine_metadata</name> <message> <source>Wine -- Windows layer for Unix</source> <translation type="unfinished"></translation> </message> + <message> + <source>X-Plane</source> + <translation type="unfinished"></translation> + </message> </context> </TS> diff --git a/proto-wine/lang/ru_RU.ts b/proto-wine/lang/ru_RU.ts index 2ea9274b..1e80e43f 100644 --- a/proto-wine/lang/ru_RU.ts +++ b/proto-wine/lang/ru_RU.ts @@ -28,10 +28,14 @@ </message> </context> <context> - <name>wineDll</name> + <name>wine_metadata</name> <message> <source>Wine -- Windows layer for Unix</source> <translation type="unfinished"></translation> </message> + <message> + <source>X-Plane</source> + <translation type="unfinished"></translation> + </message> </context> </TS> diff --git a/proto-wine/lang/stub.ts b/proto-wine/lang/stub.ts index 21185491..2da10a0b 100644 --- a/proto-wine/lang/stub.ts +++ b/proto-wine/lang/stub.ts @@ -28,10 +28,14 @@ </message> </context> <context> - <name>wineDll</name> + <name>wine_metadata</name> <message> <source>Wine -- Windows layer for Unix</source> <translation type="unfinished"></translation> </message> + <message> + <source>X-Plane</source> + <translation type="unfinished"></translation> + </message> </context> </TS> diff --git a/proto-wine/lang/zh_CN.ts b/proto-wine/lang/zh_CN.ts index 21185491..2da10a0b 100644 --- a/proto-wine/lang/zh_CN.ts +++ b/proto-wine/lang/zh_CN.ts @@ -28,10 +28,14 @@ </message> </context> <context> - <name>wineDll</name> + <name>wine_metadata</name> <message> <source>Wine -- Windows layer for Unix</source> <translation type="unfinished"></translation> </message> + <message> + <source>X-Plane</source> + <translation type="unfinished"></translation> + </message> </context> </TS> diff --git a/proto-wine/opentrack-wrapper-wine-main.cxx b/proto-wine/opentrack-wrapper-wine-main.cxx index 1c88755a..6370e7f4 100644 --- a/proto-wine/opentrack-wrapper-wine-main.cxx +++ b/proto-wine/opentrack-wrapper-wine-main.cxx @@ -1,9 +1,9 @@ #include <cerrno> + // OSX sdk 10.8 build error otherwise -#ifdef _LIBCPP_MSVCRT -# undef _LIBCPP_MSVCRT -#endif +#undef _LIBCPP_MSVCRT #include <cstdio> + #include "freetrackclient/fttypes.h" #include "wine-shm.h" #include "compat/export.hpp" @@ -48,11 +48,11 @@ int main(void) ShmPosix lck_posix(WINE_SHM_NAME, WINE_MTX_NAME, sizeof(WineSHM)); ShmWine lck_wine("FT_SharedMem", "FT_Mutext", sizeof(FTHeap)); if(!lck_posix.success()) { - printf("Can't open posix map: %d\n", errno); + fprintf(stderr, "Can't open posix map: %d\n", errno); return 1; } if(!lck_wine.success()) { - printf("Can't open Wine map\n"); + fprintf(stderr, "Can't open Wine map\n"); return 1; } WineSHM* shm_posix = (WineSHM*) lck_posix.ptr(); diff --git a/proto-wine/opentrack-wrapper-wine-windows.cxx b/proto-wine/opentrack-wrapper-wine-windows.cxx index e1f91797..d464cf6c 100644 --- a/proto-wine/opentrack-wrapper-wine-windows.cxx +++ b/proto-wine/opentrack-wrapper-wine-windows.cxx @@ -6,7 +6,6 @@ #include "compat/shm.h" #include "compat/shm.cpp" #include "wine-shm.h" -#define OPENTRACK_NO_QT_PATH #include "compat/library-path.hpp" #include <cstring> diff --git a/qxt-mini/CMakeLists.txt b/qxt-mini/CMakeLists.txt index cc73e6a4..f355b943 100644 --- a/qxt-mini/CMakeLists.txt +++ b/qxt-mini/CMakeLists.txt @@ -1,11 +1,10 @@ if(UNIX OR APPLE) set(self opentrack-qxt-mini) otr_module(qxt-mini NO-COMPAT BIN) - otr_pkgconfig(${self} x11 xcb xproto) if(APPLE) - set_property(TARGET ${self} - APPEND_STRING PROPERTY - LINK_FLAGS " -framework Carbon -framework CoreFoundation ") + target_link_options(${self} PUBLIC -framework Carbon -framework CoreFoundation) + else() + otr_pkgconfig(${self} x11 xcb xproto) endif() target_compile_definitions(${self} PRIVATE -DQXT_BUILD) endif() diff --git a/qxt-mini/qxtglobalshortcut.h b/qxt-mini/qxtglobalshortcut.h index 98bee92d..9f8dcf9e 100644 --- a/qxt-mini/qxtglobalshortcut.h +++ b/qxt-mini/qxtglobalshortcut.h @@ -46,8 +46,8 @@ class QXT_GUI_EXPORT QxtGlobalShortcut : public QObject bool keystate; public: - explicit QxtGlobalShortcut(QObject* parent = 0); - explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = 0); + explicit QxtGlobalShortcut(QObject* parent = nullptr); + explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = nullptr); ~QxtGlobalShortcut() override; QKeySequence shortcut() const; diff --git a/qxt-mini/qxtglobalshortcut_mac.cpp b/qxt-mini/qxtglobalshortcut_mac.cpp index 5f55f579..d7cd7f84 100644 --- a/qxt-mini/qxtglobalshortcut_mac.cpp +++ b/qxt-mini/qxtglobalshortcut_mac.cpp @@ -43,104 +43,132 @@ typedef QPair<uint, uint> Identifier; static QMap<quint32, EventHotKeyRef> keyRefs; static QHash<Identifier, quint32> keyIDs; static quint32 hotKeySerial = 0; +static bool qxt_mac_handler_installed = false; OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, void* data) { Q_UNUSED(nextHandler); Q_UNUSED(data); - if (GetEventClass(event) == kEventClassKeyboard) + if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed) { - int kind = GetEventKind(event); - bool is_down; - - if (kind == kEventHotKeyPressed) - is_down = true; - else if (kind == kEventHotKeyReleased) - is_down = false; - else - return noErr; - EventHotKeyID keyID; GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID); Identifier id = keyIDs.key(keyID.id); if(id != Identifier()) - QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first, is_down); + QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first, true); } return noErr; } -// Constants found in NSEvent.h from AppKit.framework -// cf. https://stackoverflow.com/a/16125341 -// cf. https://web.archive.org/web/20100501161453/http://www.classicteck.com/rbarticles/mackeyboard.php - -static struct { - Qt::Key qt_key; - unsigned native_key; -} const key_list[] = { - { Qt::Key_Escape , 0x35 }, - { Qt::Key_Return , 0x24 }, - { Qt::Key_Return , 0x4c }, - { Qt::Key_Tab , 0x30 }, - { Qt::Key_Space , 0x31 }, - { Qt::Key_Delete , 0x33 }, - { Qt::Key_F17 , 0x40 }, - { Qt::Key_VolumeUp , 0x48 }, - { Qt::Key_VolumeDown , 0x49 }, - { Qt::Key_Mute , 0x4A }, - { Qt::Key_F1 , 0x7A }, - { Qt::Key_F2 , 0x78 }, - { Qt::Key_F3 , 0x63 }, - { Qt::Key_F4 , 0x76 }, - { Qt::Key_F5 , 0x60 }, - { Qt::Key_F6 , 0x61 }, - { Qt::Key_F7 , 0x62 }, - { Qt::Key_F8 , 0x64 }, - { Qt::Key_F9 , 0x65 }, - { Qt::Key_F10 , 0x6D }, - { Qt::Key_F11 , 0x67 }, - { Qt::Key_F12 , 0x6F }, - { Qt::Key_Help , 0x72 }, - { Qt::Key_PageUp , 0x74 }, - { Qt::Key_PageDown , 0x79 }, - { Qt::Key_Delete , 0x33 }, - { Qt::Key_Backspace , 0x75 }, - { Qt::Key_Home , 0x73 }, - { Qt::Key_End , 0x77 }, - { Qt::Key_LeftArrow , 0x7B }, - { Qt::Key_RightArrow , 0x7C }, - { Qt::Key_DownArrow , 0x7D }, - { Qt::Key_UpArrow , 0x7E }, -}; - -static struct modifiers { - Qt::KeyboardModifiers qt_mods; - unsigned native_mods; -} const modifier_list[] = { - { Qt::ShiftModifier, 56 }, - { Qt::ControlModifier, 59 }, - { Qt::AltModifier, 55 }, - { Qt::MetaModifier, 58 }, - //{ Qt::KeypadModifier, kEventKeyModifierNumLockMask }, -}; - quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) { quint32 native = 0; - - for (const auto& m : modifiers) - if (modifiers & m.qt_mods) - native |= m.native_mods; - + if (modifiers & Qt::ShiftModifier) + native |= shiftKey; + if (modifiers & Qt::ControlModifier) + native |= cmdKey; + if (modifiers & Qt::AltModifier) + native |= optionKey; + if (modifiers & Qt::MetaModifier) + native |= controlKey; + if (modifiers & Qt::KeypadModifier) + native |= kEventKeyModifierNumLockMask; return native; } quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key keys) { UTF16Char ch; - - for (k const& : key_list) - if (k.qt_key == keys) - return k.native_key; + // Constants found in NSEvent.h from AppKit.framework + switch (keys) + { + case Qt::Key_Return: + return kVK_Return; + case Qt::Key_Enter: + return kVK_ANSI_KeypadEnter; + case Qt::Key_Tab: + return kVK_Tab; + case Qt::Key_Space: + return kVK_Space; + case Qt::Key_Backspace: + return kVK_Delete; + case Qt::Key_Control: + return kVK_Command; + case Qt::Key_Shift: + return kVK_Shift; + case Qt::Key_CapsLock: + return kVK_CapsLock; + case Qt::Key_Option: + return kVK_Option; + case Qt::Key_Meta: + return kVK_Control; + case Qt::Key_F17: + return kVK_F17; + case Qt::Key_VolumeUp: + return kVK_VolumeUp; + case Qt::Key_VolumeDown: + return kVK_VolumeDown; + case Qt::Key_F18: + return kVK_F18; + case Qt::Key_F19: + return kVK_F19; + case Qt::Key_F20: + return kVK_F20; + case Qt::Key_F5: + return kVK_F5; + case Qt::Key_F6: + return kVK_F6; + case Qt::Key_F7: + return kVK_F7; + case Qt::Key_F3: + return kVK_F3; + case Qt::Key_F8: + return kVK_F8; + case Qt::Key_F9: + return kVK_F9; + case Qt::Key_F11: + return kVK_F11; + case Qt::Key_F13: + return kVK_F13; + case Qt::Key_F16: + return kVK_F16; + case Qt::Key_F14: + return kVK_F14; + case Qt::Key_F10: + return kVK_F10; + case Qt::Key_F12: + return kVK_F12; + case Qt::Key_F15: + return kVK_F15; + case Qt::Key_Help: + return kVK_Help; + case Qt::Key_Home: + return kVK_Home; + case Qt::Key_PageUp: + return kVK_PageUp; + case Qt::Key_Delete: + return kVK_ForwardDelete; + case Qt::Key_F4: + return kVK_F4; + case Qt::Key_End: + return kVK_End; + case Qt::Key_F2: + return kVK_F2; + case Qt::Key_PageDown: + return kVK_PageDown; + case Qt::Key_F1: + return kVK_F1; + case Qt::Key_Left: + return kVK_LeftArrow; + case Qt::Key_Right: + return kVK_RightArrow; + case Qt::Key_Down: + return kVK_DownArrow; + case Qt::Key_Up: + return kVK_UpArrow; + default: + ; + } if (keys == Qt::Key_Escape) ch = 27; else if (keys == Qt::Key_Return) ch = 13; @@ -200,21 +228,16 @@ quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key keys) return 0; } -static bool register_event_handler() -{ - EventTypeSpec specs[2] = { - { kEventClassKeyboard, kEventHotKeyPressed }, - { kEventClassKeyboard, kEventHotKeyReleased }, - }; - - (void)InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 2, &t, NULL, NULL); - - return true; -} - bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) { - static const bool once = register_event_handler(); + if (!qxt_mac_handler_installed) + { + qxt_mac_handler_installed = true; + EventTypeSpec t; + t.eventClass = kEventClassKeyboard; + t.eventKind = kEventHotKeyPressed; + InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, NULL, NULL); + } EventHotKeyID keyID; keyID.signature = 'cute'; @@ -232,16 +255,15 @@ bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativ bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) { - Identifier id{nativeMods, nativeKey}; - if (!keyIDs.contains(id)) - return false; + Identifier id(nativeMods, nativeKey); + if (!keyIDs.contains(id)) return false; EventHotKeyRef ref = keyRefs.take(keyIDs[id]); keyIDs.remove(id); return !UnregisterEventHotKey(ref); } - -bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, void *message, long *result) +bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, + void *message, long *result) { return false; } diff --git a/redist/x64/msvcp110.dll b/redist/x64/msvcp110.dll Binary files differnew file mode 100644 index 00000000..7e278956 --- /dev/null +++ b/redist/x64/msvcp110.dll diff --git a/redist/x64/msvcr110.dll b/redist/x64/msvcr110.dll Binary files differnew file mode 100644 index 00000000..dd484a58 --- /dev/null +++ b/redist/x64/msvcr110.dll diff --git a/redist/x86/msvcp110.dll b/redist/x86/msvcp110.dll Binary files differnew file mode 100644 index 00000000..93fab568 --- /dev/null +++ b/redist/x86/msvcp110.dll diff --git a/redist/x86/msvcr110.dll b/redist/x86/msvcr110.dll Binary files differnew file mode 100644 index 00000000..1ce960d7 --- /dev/null +++ b/redist/x86/msvcr110.dll diff --git a/sdk-paths-sthalik@Clang-windows.cmake b/sdk-paths-sthalik@Clang-windows.cmake new file mode 100644 index 00000000..895d68b2 --- /dev/null +++ b/sdk-paths-sthalik@Clang-windows.cmake @@ -0,0 +1,72 @@ +# +# qtbase configure line for safekeeping +# + +# "../configure" -prefix d:\dev\qt-5.10.0 -no-ico -no-gif -no-libjpeg \ +# -opengl desktop -no-angle -no-fontconfig -no-harfbuzz \ +# -nomake tests -no-mp -release -opensource -shared -confirm-license \ +# -no-freetype -force-debug-fo -make-tool make.exe -platform win32-g++ + +set(__depdir "${CMAKE_CURRENT_LIST_DIR}/../opentrack-depends") + +function(setq name value) + set("${name}" "${__depdir}/${value}" CACHE PATH "" FORCE) +endfunction() + +set(opentrack_install-debug-info TRUE CACHE INTERNAL "" FORCE) + +#setq(OpenCV_DIR "opencv/build-mingw-64") +setq(SDK_ARUCO_LIBPATH "aruco/build-mingw-w64/src/libaruco.a") + +setq(EIGEN3_INCLUDE_DIR "eigen") + +setq(SDK_FSUIPC "fsuipc") +setq(SDK_HYDRA "SixenseSDK") + +setq(SDK_RIFT_140 "LibOVR-140/build-mingw-w64") + +setq(SDK_VALVE_STEAMVR "steamvr") +setq(SDK_TOBII_EYEX "Tobii-EyeX") +setq(SDK_VJOYSTICK "vjoystick") + +setq(SDK_REALSENSE "RSSDK-R2") + +# WARNING: this is utter experimental nonsense +set(_cxxflags + -Weverything + + -Wno-global-constructors + -Wno-exit-time-destructors + -Wno-deprecated + -Wno-self-assign-overloaded + -Wno-double-promotion + -Wno-c++98-compat-pedantic + -Wno-old-style-cast + -Wno-shadow + -Wno-sign-conversion + -Wno-used-but-marked-unused + -Wno-covered-switch-default + -Wno-missing-prototypes + -Wno-padded + -Wno-switch-enum + + -Werror + -Werror=inconsistent-missing-destructor-override + #-Wno-error=padded + -fdiagnostics-color=always + + -fno-exceptions -fno-rtti # get rid of evil +) +set(base-cxxflags "") +foreach(k ${_cxxflags}) + set(base-cxxflags "${base-cxxflags} ${k}") +endforeach() + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${base-cxxflags}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${base-cxxflags}") + +set(CMAKE_CXX_FLAGS_RELEASE "-ffast-math ${CMAKE_CXX_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_RELEASE "-ffast-math ${CMAKE_C_FLAGS_RELEASE}") + +set(CMAKE_CXX_FLAGS_DEBUG "-ggdb ${CMAKE_CXX_FLAGS_DEBUG}") +set(CMAKE_C_FLAGS_DEBUG "-ggdb ${CMAKE_C_FLAGS_DEBUG}") diff --git a/sdk-paths-sthalik@GNU-windows.cmake b/sdk-paths-sthalik@GNU-windows.cmake new file mode 100644 index 00000000..b9617719 --- /dev/null +++ b/sdk-paths-sthalik@GNU-windows.cmake @@ -0,0 +1,42 @@ +# +# qtbase configure line for safekeeping +# + +# "../configure" -prefix d:\dev\qt-5.10.0 -no-ico -no-gif -no-libjpeg \ +# -opengl desktop -no-angle -no-fontconfig -no-harfbuzz \ +# -nomake tests -no-mp -release -opensource -shared -confirm-license \ +# -no-freetype -force-debug-fo -make-tool make.exe -platform win32-g++ + +set(__depdir "${CMAKE_CURRENT_LIST_DIR}/../opentrack-depends") + +function(setq name value) + set("${name}" "${__depdir}/${value}" CACHE PATH "" FORCE) +endfunction() + +set(opentrack_install-debug-info TRUE CACHE INTERNAL "" FORCE) + +#setq(OpenCV_DIR "opencv/build-mingw-64") +setq(SDK_ARUCO_LIBPATH "aruco/build-mingw-w64/src/libaruco.a") + +setq(EIGEN3_INCLUDE_DIR "eigen") + +setq(SDK_FSUIPC "fsuipc") +setq(SDK_HYDRA "SixenseSDK") + +setq(SDK_RIFT_140 "LibOVR-140/build-mingw-w64") + +setq(SDK_VALVE_STEAMVR "steamvr") +setq(SDK_TOBII_EYEX "Tobii-EyeX") +setq(SDK_VJOYSTICK "vjoystick") + +setq(SDK_REALSENSE "RSSDK-R2") + +set(base-flags "-Wall -Wextra -Wpedantic") +set(CMAKE_C_FLAGS "-std=c11 ${base-flags} -ggdb ${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "-std=c++17 ${base-flags} -ggdb ${CMAKE_CXX_FLAGS}") + +set(CMAKE_CXX_FLAGS_RELEASE "-O3 -ffast-math ${CMAKE_CXX_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_RELEASE "-O3 -ffast-math ${CMAKE_C_FLAGS_RELEASE}") + +set(CMAKE_CXX_FLAGS_DEBUG "-ggdb ${CMAKE_CXX_FLAGS_DEBUG}") +set(CMAKE_C_FLAGS_DEBUG "-ggdb ${CMAKE_C_FLAGS_DEBUG}") diff --git a/sdk-paths-sthalik@MSVC-windows.cmake b/sdk-paths-sthalik@MSVC-windows.cmake index 17ea107c..0f619b22 100644 --- a/sdk-paths-sthalik@MSVC-windows.cmake +++ b/sdk-paths-sthalik@MSVC-windows.cmake @@ -20,7 +20,7 @@ endfunction() set(opentrack_install-debug-info TRUE CACHE INTERNAL "" FORCE) set(opentrack_maintainer-mode TRUE CACHE INTERNAL "" FORCE) -setq(Qt5_DIR "../qt-5.11.2/lib/cmake/Qt5") +setq(Qt5_DIR "../qt-5.12.0/lib/cmake/Qt5") setq(OpenCV_DIR "opencv/build") setq(SDK_ARUCO_LIBPATH "aruco/build/src/aruco.lib") @@ -35,6 +35,7 @@ setq(SDK_VALVE_STEAMVR "steamvr") setq(SDK_VJOYSTICK "vjoystick") setq(SDK_REALSENSE "RSSDK-R2") +setq(SDK_KINECT20 "Kinect-v2.0") if(CMAKE_GENERATOR STREQUAL "NMake Makefiles") set(CMAKE_MAKE_PROGRAM "jom" CACHE STRING "" FORCE) diff --git a/settings/facetracknoir supported games.csv b/settings/facetracknoir supported games.csv index 7efe7eb7..f0527bca 100644 --- a/settings/facetracknoir supported games.csv +++ b/settings/facetracknoir supported games.csv @@ -664,3 +664,36 @@ No;Game Name;Game protocol;Supported since;Verified;By;INTERNATIONAL_ID;FTN_ID 798;VBS3 IG;FreeTrack20;V160;;;7507;031EB220C156D485EE6D00 799;Weltjen;FreeTrack20;V160;;;20153;031F851F64EE45273D2E00 800;WHSIL;FreeTrack20;V160;;;23125;0320F09528081A724FAD00 +801;Animech;FreeTrack20;V160;;;23200;032187D09D27D146691B00 +802;Brightrock;FreeTrack20;V160;;;8120;03224C095524F3EF5A8500 +803;CNU;FreeTrack20;V160;;;23255;032381B5E5281A79299800 +804;couby;FreeTrack20;V160;;;23190;03245BEE3F3A461B88CF00 +805;DayZ;FreeTrack20;V160;;;8135;0325AF804AA8E17DB87600 +806;Deblur;FreeTrack20;V160;;;23205;032674750FD1B63B4B1E00 +807;Diginext;FreeTrack20;V160;;;23165;0327E437168A93B7895500 +808;Doublethink;FreeTrack20;V160;;;23250;0328464ECD2ECEEADD9B00 +809;End Space;FreeTrack20;V160;;;23275;03298C6351EC0541B58900 +810;Go For Launch: Mercury;FreeTrack20;V160;;;23225;032A4139613E6C7910F200 +811;Hunting Wild Game;FreeTrack20;V160;;;23210;032B24B33A3DE9644AA600 +812;i3dix;FreeTrack20;V160;;;23265;032C186D9CC013EEF68400 +813;Inv3rsion;FreeTrack20;V160;;;23170;032D9C6EA428D6440D7A00 +814;Kitakyushu Univ;FreeTrack20;V160;;;23245;032E1E74FFF43002711200 +815;LWHVR;FreeTrack20;V160;;;23155;032FE902A041F663753A00 +816;MS;FreeTrack20;V160;;;23220;033057BE4ABA00F41B0000 +817;NPS;FreeTrack20;V160;;;23145;033119034A67900AE32000 +818;NTU;FreeTrack20;V160;;;23230;033242762D5CD9491F5C00 +819;OSSimTech;FreeTrack20;V160;;;23260;0333A3498349E6BD60C100 +820;SEAAT;FreeTrack20;V160;;;23185;0334B167A9690ED8F73300 +821;SKKU;FreeTrack20;V160;;;23215;0335EB1AF5882E3E5DCC00 +822;SUTD;FreeTrack20;V160;;;23160;033633F20953BCB826EF00 +823;Technicolor;FreeTrack20;V160;;;23270;03370882B65696DB3F1100 +824;Tecknotrove;FreeTrack20;V160;;;23240;0338E5656CCF17E25ACB00 +825;Tourist Bus Simulator;FreeTrack20;V160;;;8130;03395E485D939DF3C8E600 +826;Trans Sim;FreeTrack20;V160;;;23285;033AB1354A142CAB9F8F00 +827;UCL;FreeTrack20;V160;;;23150;033B9C235BD639F5855E00 +828;UMD;FreeTrack20;V160;;;23280;033C4CEE887A176D7B1D00 +829;Uni-Hamburg;FreeTrack20;V160;;;23195;033D1923F4C98158E4E300 +830;University of Korea;FreeTrack20;V160;;;23235;033E81D5E01D25F0258F00 +831;vJoy;FreeTrack20;V160;;;23175;033FBC6527D3DF2DA61600 +832;Vox Machinae;FreeTrack20;V160;;;8125;03408C95C383696F38EB00 +833;w-hs;FreeTrack20;V160;;;23180;0341D88F5D050D64BB1E00 diff --git a/spline/axis-opts.cpp b/spline/axis-opts.cpp index b5f11aaa..489008e6 100644 --- a/spline/axis-opts.cpp +++ b/spline/axis-opts.cpp @@ -1,5 +1,7 @@ #include "axis-opts.hpp" +namespace axis_opts_impl { + using max_clamp = axis_opts::max_clamp; static max_clamp get_max_x(Axis k) @@ -25,14 +27,14 @@ static max_clamp get_max_y(Axis k) } axis_opts::axis_opts(QString pfx, Axis idx) : + prefix_(pfx), + axis_(idx), zero(b_settings_window, n(pfx, "zero-pos"), 0), src(b_settings_window, n(pfx, "source-index"), idx), invert(b_settings_window, n(pfx, "invert-sign"), false), altp(b_mapping_window, n(pfx, "alt-axis-sign"), false), clamp_x_(b_mapping_window, n(pfx, "max-value"), get_max_x(idx)), - clamp_y_(b_mapping_window, n(pfx, "max-output-value"), get_max_y(idx)), - prefix_(pfx), - axis_(idx) + clamp_y_(b_mapping_window, n(pfx, "max-output-value"), get_max_y(idx)) {} QString const& axis_opts::prefix() const { return prefix_; } @@ -43,3 +45,5 @@ QString axis_opts::n(QString const& pfx, QString const& name) { return QString("%1-%2").arg(pfx, name); } + +} // ns axis_opts_impl diff --git a/spline/axis-opts.hpp b/spline/axis-opts.hpp index b4475502..c773dd61 100644 --- a/spline/axis-opts.hpp +++ b/spline/axis-opts.hpp @@ -9,8 +9,14 @@ namespace axis_opts_impl { using namespace options; -struct OTR_SPLINE_EXPORT axis_opts final +class OTR_SPLINE_EXPORT axis_opts final { + QString prefix_; + Axis axis_; + + static inline QString n(QString const& pfx, QString const& name); + +public: enum max_clamp { r180 = 180, @@ -37,8 +43,8 @@ struct OTR_SPLINE_EXPORT axis_opts final }; // note, these two bundles can be the same value with no issues - bundle b_settings_window = make_bundle("opentrack-ui"); - bundle b_mapping_window = make_bundle("opentrack-mappings"); + bundle b_settings_window{ make_bundle(axis_ == Axis(-1) ? QString() : "opentrack-ui") }; + bundle b_mapping_window{ make_bundle(axis_ == Axis(-1) ? QString() : "opentrack-mappings") }; value<double> zero; value<int> src; value<bool> invert, altp; @@ -49,11 +55,6 @@ struct OTR_SPLINE_EXPORT axis_opts final QString const& prefix() const; Axis axis() const; -private: - static inline QString n(QString const& pfx, QString const& name); - - QString prefix_; - Axis axis_; }; } // ns axis_opts_impl diff --git a/spline/broken/qfunctionconfiguratorplugin.h b/spline/broken/qfunctionconfiguratorplugin.h index ca68e0e2..11b9a58c 100644 --- a/spline/broken/qfunctionconfiguratorplugin.h +++ b/spline/broken/qfunctionconfiguratorplugin.h @@ -16,7 +16,7 @@ class QFunctionConfiguratorPlugin : public QObject, public QDesignerCustomWidget Q_INTERFACES(QDesignerCustomWidgetInterface) public: - QFunctionConfiguratorPlugin(QObject *parent = 0); + QFunctionConfiguratorPlugin(QObject *parent = nullptr); bool isContainer() const; bool isInitialized() const; diff --git a/spline/spline-widget.cpp b/spline/spline-widget.cpp index 64f9f156..d329d8e2 100644 --- a/spline/spline-widget.cpp +++ b/spline/spline-widget.cpp @@ -1,10 +1,3 @@ -/* Copyright (c) 2012-2016 Stanislaw Halik <sthalik@misaki.pl> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - */ - #include "spline-widget.hpp" #include "compat/math.hpp" #include "compat/macros.hpp" @@ -19,7 +12,7 @@ #include <QDebug> -using namespace spline_detail; +namespace spline_detail { spline_widget::spline_widget(QWidget *parent) : QWidget(parent) { @@ -31,28 +24,31 @@ spline_widget::spline_widget(QWidget *parent) : QWidget(parent) spline_widget::~spline_widget() { if (connection) + { QObject::disconnect(connection); + connection = {}; + } } -void spline_widget::setConfig(base_spline* spl) +void spline_widget::set_config(base_spline* spl) { if (connection) { QObject::disconnect(connection); - connection = QMetaObject::Connection(); + connection = {}; } - _config = spl; + config = spl; if (spl) { - update_range(); - - std::shared_ptr<base_spline::base_settings> s = spl->get_settings(); - connection = connect(s.get(), &base_spline::base_settings::recomputed, + std::shared_ptr<base_settings> s = spl->get_settings(); + connection = connect(s.get(), &base_settings::recomputed, this, [this] { reload_spline(); }, Qt::QueuedConnection); } + + reload_spline(); } QColor spline_widget::colorBezier() const @@ -68,37 +64,39 @@ void spline_widget::setColorBezier(QColor const& color) void spline_widget::force_redraw() { - _background = QPixmap(); + background_img = {}; repaint(); } void spline_widget::set_preview_only(bool val) { - _preview_only = val; + preview_only = val; } bool spline_widget::is_preview_only() const { - return _preview_only; + return preview_only; } void spline_widget::drawBackground() { - QPainter painter(&_background); + QPainter painter(&background_img); painter.fillRect(rect(), widget_bg_color); { QColor bg_color(112, 154, 209); - if (!isEnabled() && !_preview_only) + if (!isEnabled() && !preview_only) bg_color = QColor(176,176,180); painter.fillRect(pixel_bounds, bg_color); } QFont font; font.setPointSize(8); + font.setStyleHint(QFont::Monospace, QFont::PreferAntialias); painter.setFont(font); - QFontMetricsF metrics(font); + const QFontMetricsF metrics(font); + const double height = metrics.height(); QColor color__(176, 190, 209, 127); @@ -107,11 +105,11 @@ void spline_widget::drawBackground() const QPen pen(color__, 1, Qt::SolidLine, Qt::FlatCap); - const int ystep = _y_step, xstep = _x_step; - const double maxx = _config->max_input(); - const double maxy = _config->max_output(); + const int ystep = (int)std::ceil(y_step_), xstep = (int)std::ceil(x_step_); + const double maxx = config->max_input(); + const double maxy = config->max_output(); - // horizontal grid + // vertical grid for (int i = 0; i <= maxy; i += ystep) { const double y = pixel_bounds.height() - i * c.y() + pixel_bounds.y(); @@ -120,13 +118,13 @@ void spline_widget::drawBackground() QPointF(pixel_bounds.x() + pixel_bounds.width(), y), pen); painter.drawText(QRectF(10, - y - metrics.height()/2., + y - height/2, pixel_bounds.left(), - metrics.height()), + height), QString::number(i)); } - // vertical grid + // horizontal grid for (int i = 0; i <= maxx; i += xstep) { const double x = pixel_bounds.x() + i * c.x(); @@ -134,21 +132,29 @@ void spline_widget::drawBackground() QPointF(x, pixel_bounds.y()), QPointF(x, pixel_bounds.y() + pixel_bounds.height()), pen); + const QString text = QString::number(i); - painter.drawText(QRectF(x - metrics.width(text)/2., - pixel_bounds.height() + 10 + metrics.height(), - metrics.width(text), - metrics.height()), + + const double width = +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + metrics.horizontalAdvance(text); +#else + metrics.width(text); +#endif + + painter.drawText(QRectF{x - width/2, + pixel_bounds.height() + 10 + height, + width, height}, text); } } void spline_widget::drawFunction() { - QPainter painter(&_function); + QPainter painter(&spline_img); painter.setRenderHint(QPainter::Antialiasing, true); - const points_t points = _config->get_points(); + const points_t& points = config->get_points(); if (moving_control_point_idx >= 0 && moving_control_point_idx < points.size()) @@ -156,7 +162,7 @@ void spline_widget::drawFunction() const QPen pen(Qt::white, 1, Qt::SolidLine, Qt::FlatCap); const QPointF prev_ = point_to_pixel({}); QPointF prev(iround(prev_.x()), iround(prev_.y())); - for (auto point : points) + for (const auto& point : points) { const QPointF tmp = point_to_pixel(point); drawLine(painter, prev, tmp, pen); @@ -165,7 +171,7 @@ void spline_widget::drawFunction() } const QColor color_ = progn( - if (!isEnabled() && !_preview_only) + if (!isEnabled() && !preview_only) { QColor color(spline_color); const int avg = int(float(color.red() + color.green() + color.blue())/3); @@ -187,7 +193,7 @@ void spline_widget::drawFunction() const double dpr = devicePixelRatioF(); const double line_length_pixels = std::fmax(1, 2 * dpr); const double step = std::fmax(.1, line_length_pixels / c.x()); - const double maxx = _config->max_input(); + const double maxx = config->max_input(); //#define USE_CUBIC_SPLINE #if defined USE_CUBIC_SPLINE @@ -205,9 +211,9 @@ void spline_widget::drawFunction() for (double k = 0; k < maxx; k += step*3) // NOLINT { - const auto next_1 = (double) _config->get_value_no_save(k + step*1); - const auto next_2 = (double) _config->get_value_no_save(k + step*2); - const auto next_3 = (double) _config->get_value_no_save(k + step*3); + const auto next_1 = config->get_value_no_save(k + step*1); + const auto next_2 = config->get_value_no_save(k + step*2); + const auto next_3 = config->get_value_no_save(k + step*3); QPointF b(clamp(point_to_pixel({k + step*1, next_1}))), c(clamp(point_to_pixel({k + step*2, next_2}))), @@ -221,14 +227,14 @@ void spline_widget::drawFunction() QPointF prev = point_to_pixel({}); for (double i = 0; i < maxx; i += step) // NOLINT { - const auto val = (double) _config->get_value_no_save(i); + const auto val = config->get_value_no_save(i); const QPointF cur = point_to_pixel({i, val}); painter.drawLine(prev, cur); prev = cur; } { - const double maxx = _config->max_input(); - const double maxy = double(_config->get_value_no_save(maxx)); + const double maxx = config->max_input(); + const double maxy = config->get_value_no_save(maxx); painter.drawLine(prev, point_to_pixel({ maxx, maxy })); } #endif @@ -242,7 +248,7 @@ void spline_widget::drawFunction() painter.fillRect(r2, widget_bg_color); const int alpha = !isEnabled() ? 64 : 120; - if (!_preview_only) + if (!preview_only) { for (auto const& point : points) { @@ -256,7 +262,7 @@ void spline_widget::drawFunction() void spline_widget::paintEvent(QPaintEvent *e) { - if (!_config) + if (!config) return; QPainter p(this); @@ -265,28 +271,30 @@ void spline_widget::paintEvent(QPaintEvent *e) const int W = iround(width() * dpr); const int H = iround(height() * dpr); - if (_background.size() != QSize(W, H)) + if (background_img.size() != QSize(W, H)) { - _background = QPixmap(W, H); - _background.setDevicePixelRatio(dpr); - _draw_function = true; + update_range(); + + background_img = { W, H }; + background_img.setDevicePixelRatio(dpr); drawBackground(); + draw_function = true; } - if (_draw_function) + if (draw_function) { - _draw_function = false; - _function = _background; + draw_function = false; + spline_img = background_img; drawFunction(); } - p.drawPixmap(e->rect(), _function); + p.drawPixmap(e->rect(), spline_img); // If the Tracker is active, the 'Last Point' it requested is recorded. // Show that point on the graph, with some lines to assist. // This new feature is very handy for tweaking the curves! QPointF last; - if (_config->get_last_value(last) && isEnabled()) + if (config->get_last_value(last) && isEnabled()) drawPoint(p, point_to_pixel(last), QColor(255, 0, 0, 120)); } @@ -312,14 +320,14 @@ void spline_widget::drawLine(QPainter& painter, const QPointF& start, const QPoi void spline_widget::mousePressEvent(QMouseEvent *e) { - if (!_config || !isEnabled() || !is_in_bounds(e->localPos()) || _preview_only) + if (!config || !isEnabled() || !is_in_bounds(e->localPos()) || preview_only) return; const double min_dist = min_pt_distance(); moving_control_point_idx = -1; - points_t points = _config->get_points(); + const points_t& points = config->get_points(); if (e->button() == Qt::LeftButton) { @@ -352,50 +360,50 @@ void spline_widget::mousePressEvent(QMouseEvent *e) if (!too_close) { - _config->add_point(pixel_to_point(e->localPos())); + config->add_point(pixel_to_point(e->localPos())); show_tooltip(e->pos()); } } - _draw_function = true; + draw_function = true; } if (e->button() == Qt::RightButton) { - if (_config) + if (config) { for (int i = 0; i < points.size(); i++) { if (point_within_pixel(points[i], e->localPos())) { - _config->remove_point(i); - _draw_function = true; + config->remove_point(i); + draw_function = true; break; } } } } - if (_draw_function) + if (draw_function) repaint(); } void spline_widget::mouseMoveEvent(QMouseEvent *e) { - if (_preview_only && _config) + if (preview_only && config) { show_tooltip(e->pos()); return; } - if (!_config || !isEnabled() || !isActiveWindow()) + if (!config || !isEnabled() || !isActiveWindow()) { QToolTip::hideText(); return; } const int i = moving_control_point_idx; - const points_t points = _config->get_points(); + const points_t& points = config->get_points(); const int sz = points.size(); if (i >= 0 && i < sz) @@ -428,8 +436,8 @@ void spline_widget::mouseMoveEvent(QMouseEvent *e) if ((!has_prev || check_prev()) && (!has_next || check_next())) { - _config->move_point(i, new_pt); - _draw_function = true; + config->move_point(i, new_pt); + draw_function = true; repaint(); } } @@ -456,7 +464,7 @@ void spline_widget::mouseMoveEvent(QMouseEvent *e) void spline_widget::mouseReleaseEvent(QMouseEvent *e) { - if (!_config || !isEnabled() || !isActiveWindow() || _preview_only) + if (!config || !isEnabled() || !isActiveWindow() || preview_only) return; const bool redraw = moving_control_point_idx != -1; @@ -479,7 +487,7 @@ void spline_widget::mouseReleaseEvent(QMouseEvent *e) if (redraw) { - _draw_function = true; + draw_function = true; repaint(); } } @@ -487,7 +495,7 @@ void spline_widget::mouseReleaseEvent(QMouseEvent *e) void spline_widget::reload_spline() { // don't recompute here as the value's about to be recomputed in the callee - update_range(); + background_img = {}; update(); } @@ -504,8 +512,8 @@ void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_) double x = value.x(), y = value.y(); - if (_preview_only) - y = _config->get_value_no_save(x); + if (preview_only) + y = config->get_value_no_save(x); const int x_ = iround(x), y_ = iround(y); @@ -514,16 +522,13 @@ void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_) if (std::fabs(y_ - y) < 1e-3) y = y_; - // the style on OSX has different offsets - static const bool is_fusion = + // the native OSX style doesn't look right otherwise #if defined __APPLE__ - true; + constexpr int off_x = 0, off_y = 0; #else - false; + constexpr int off_x = 25, off_y = 15; #endif - const int off_x = (is_fusion ? 25 : 0), off_y = (is_fusion ? 15 : 0); - const QPoint pix(pos.x() + off_x, pos.y() + off_y); QToolTip::showText(mapToGlobal(pix), @@ -535,8 +540,8 @@ void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_) bool spline_widget::is_in_bounds(const QPointF& pos) const { - const int grace = point_size_in_pixels * 3; - const int bottom_grace = int(point_size_in_pixels * 1.5); + const int grace = (int)std::ceil(point_size_in_pixels * 3); + const int bottom_grace = (int)std::ceil(point_size_in_pixels * 1.5); return (pos.x() + grace > pixel_bounds.left() && pos.x() - grace < pixel_bounds.right() && pos.y() + grace > pixel_bounds.top() && @@ -545,21 +550,22 @@ bool spline_widget::is_in_bounds(const QPointF& pos) const void spline_widget::update_range() { - if (!_config) + if (!config) return; const int w = width(), h = height(); const int mwl = 40, mhl = 20; const int mwr = 15, mhr = 35; - pixel_bounds = QRect(mwl, mhl, (w - mwl - mwr), (h - mhl - mhr)); - c = { pixel_bounds.width() / _config->max_input(), pixel_bounds.height() / _config->max_output() }; - _draw_function = true; - - _background = QPixmap(); - _function = QPixmap(); + pixel_bounds = { mwl, mhl, (w - mwl - mwr), (h - mhl - mhr) }; + c = { + pixel_bounds.width() / std::fmax(1, config->max_input()), + pixel_bounds.height() / std::fmax(1, config->max_output()) + }; - repaint(); + draw_function = true; + background_img = {}; + spline_img = {}; } bool spline_widget::point_within_pixel(const QPointF& pt, const QPointF& pixel) @@ -573,7 +579,7 @@ void spline_widget::focusOutEvent(QFocusEvent* e) if (moving_control_point_idx != -1) QToolTip::hideText(); moving_control_point_idx = -1; - _draw_function = true; + draw_function = true; lower(); setCursor(Qt::ArrowCursor); e->accept(); @@ -604,8 +610,8 @@ QPointF spline_widget::pixel_to_point(const QPointF& point) if (snap_y > 0) y = snap(y, snap_y); - x = clamp(x, 0, _config->max_input()); - y = clamp(y, 0, _config->max_output()); + x = clamp(x, 0, config->max_input()); + y = clamp(y, 0, config->max_output()); return { x, y }; } @@ -620,19 +626,19 @@ QPointF spline_widget::point_to_pixel(const QPointF& point) void spline_widget::resizeEvent(QResizeEvent *) { - update_range(); + reload_spline(); } bool spline_widget::is_on_pt(const QPointF& pos, int* pt) { - if (!_config) + if (!config) { if (pt) *pt = -1; return false; } - const points_t points = _config->get_points(); + const points_t& points = config->get_points(); for (int i = 0; i < points.size(); i++) { @@ -648,3 +654,17 @@ bool spline_widget::is_on_pt(const QPointF& pos, int* pt) *pt = -1; return false; } + +void spline_widget::changeEvent(QEvent* e) +{ + switch (e->type()) + { + case QEvent::EnabledChange: + background_img = {}; spline_img = {}; + update(); + break; + default: + break; + } +} +} // ns spline_detail diff --git a/spline/spline-widget.hpp b/spline/spline-widget.hpp index a6d0eb9d..b0a76e2d 100644 --- a/spline/spline-widget.hpp +++ b/spline/spline-widget.hpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016 Stanislaw Halik <sthalik@misaki.pl> +/* Copyright (c) 2012-2019 Stanislaw Halik <sthalik@misaki.pl> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -34,12 +34,11 @@ class OTR_SPLINE_EXPORT spline_widget final : public QWidget Q_PROPERTY(int x_step READ x_step WRITE set_x_step) Q_PROPERTY(int y_step READ y_step WRITE set_y_step) - using points_t = base_spline::points_t; public: explicit spline_widget(QWidget *parent = nullptr); ~spline_widget() override; - void setConfig(base_spline* spl); + void set_config(base_spline* spl); QColor colorBezier() const; void setColorBezier(QColor const& color); @@ -48,10 +47,10 @@ public: void set_preview_only(bool val); bool is_preview_only() const; - double x_step() const { return _x_step; } - double y_step() const { return _y_step; } - void set_x_step(double val) { _x_step = std::fmax(1., val); } - void set_y_step(double val) { _y_step = std::fmax(1., val); } + double x_step() const { return x_step_; } + double y_step() const { return y_step_; } + void set_x_step(double val) { x_step_ = std::fmax(1., val); } + void set_y_step(double val) { y_step_ = std::fmax(1., val); } void set_snap(double x, double y) { snap_x = x; snap_y = y; } void get_snap(double& x, double& y) const { x = snap_x; y = snap_y; } @@ -78,6 +77,7 @@ private: bool is_on_pt(const QPointF& pos, int* pt = nullptr); void update_range(); + void changeEvent(QEvent* e) override; QPointF pixel_to_point(const QPointF& point); QPointF point_to_pixel(const QPointF& point); @@ -85,10 +85,10 @@ private: static double snap(double x, double snap_value); QPointF c; - base_spline* _config = nullptr; + base_spline* config = nullptr; - QPixmap _background; - QPixmap _function; + QPixmap background_img; + QPixmap spline_img; QColor spline_color; QColor widget_bg_color = palette().background().color(); @@ -98,12 +98,12 @@ private: QMetaObject::Connection connection; double snap_x = 0, snap_y = 0; - double _x_step = 10, _y_step = 10; + double x_step_ = 10, y_step_ = 10; int moving_control_point_idx = -1; - bool _draw_function = true, _preview_only = false; + bool draw_function = true, preview_only = false; // point's circle radius on the widget - static constexpr inline int point_size_in_pixels_ = 4; + static constexpr int point_size_in_pixels_ = 4; const double point_size_in_pixels = point_size_in_pixels_ * std::fmax(1, devicePixelRatioF() * .66); }; diff --git a/spline/spline.cpp b/spline/spline.cpp index cd3daf30..3ec3b5dc 100644 --- a/spline/spline.cpp +++ b/spline/spline.cpp @@ -1,11 +1,3 @@ -/* Copyright (c) 2012-2016, Stanislaw Halik <sthalik@misaki.pl> - - * Permission to use, copy, modify, and/or distribute this - * software for any purpose with or without fee is hereby granted, - * provided that the above copyright notice and this permission - * notice appear in all copies. - */ - #include "spline.hpp" #include "compat/math.hpp" @@ -18,14 +10,19 @@ #include <QObject> #include <QMutexLocker> -#include <QCoreApplication> #include <QPointF> #include <QSettings> #include <QString> #include <QDebug> -using namespace spline_detail; +namespace spline_detail { + +settings::~settings() = default; +base_spline_::~base_spline_() = default; +base_spline::~base_spline() = default; +spline_modify_mixin::~spline_modify_mixin() = default; +spline_settings_mixin::~spline_settings_mixin() = default; spline::spline(const QString& name, const QString& axis_name, Axis axis) { @@ -34,8 +31,7 @@ spline::spline(const QString& name, const QString& axis_name, Axis axis) spline::~spline() { - QMutexLocker l(&_mutex); - + QMutexLocker l(&mtx); disconnect_signals(); } @@ -43,101 +39,101 @@ spline::spline() : spline(QString{}, QString{}, Axis(-1)) {} void spline::set_tracking_active(bool value) { - QMutexLocker l(&_mutex); - activep = value; + std::shared_ptr<settings> S; + { + QMutexLocker l(&mtx); + S = s; + activep = value; + } + emit S->recomputed(); } bundle spline::get_bundle() { - QMutexLocker l(&_mutex); // avoid logic errors due to changes in value<t> data + QMutexLocker l(&mtx); return s->b; } void spline::clear() { + std::shared_ptr<settings> S; { - QMutexLocker l(&_mutex); + QMutexLocker l(&mtx); + S = s; s->points = {}; + points = {}; + update_interp_data(); } - - invalidate_settings(); + emit S->recomputed(); } -float spline::get_value(double x) +double spline::get_value(double x) const { - QMutexLocker foo(&_mutex); + QMutexLocker l(&mtx); - const float ret = get_value_no_save(x); - last_input_value.setX(std::fabs(x)); - last_input_value.setY(double(std::fabs(ret))); + const double ret = get_value_no_save(x); + last_input_value = { std::fabs(x), std::fabs((double)ret) }; return ret; } -float spline::get_value_no_save(double x) const +double spline::get_value_no_save(double x) const { - return const_cast<spline&>(*this).get_value_no_save_internal(x); -} - -float spline::get_value_no_save_internal(double x) -{ - QMutexLocker foo(&_mutex); + QMutexLocker l(&mtx); - float q = float(x * bucket_size_coefficient(points)); + double q = x * bucket_size_coefficient(points); int xi = (int)q; - float yi = get_value_internal(xi); - float yiplus1 = get_value_internal(xi+1); - float f = (q-xi); - float ret = yiplus1 * f + yi * (1.0f - f); // at least do a linear interpolation. + double yi = get_value_internal(xi); + double yiplus1 = get_value_internal(xi+1); + double f = (q-xi); + double ret = yiplus1 * f + yi * (1 - f); // at least do a linear interpolation. return ret; } bool spline::get_last_value(QPointF& point) { - QMutexLocker foo(&_mutex); + QMutexLocker foo(&mtx); point = last_input_value; - return activep; + return activep && point.y() >= 0; } -float spline::get_value_internal(int x) +double spline::get_value_internal(int x) const { - if (!validp) - { - update_interp_data(); - validp = true; - } - const float sign = signum(x); x = std::abs(x); - const float ret_ = data[std::min(unsigned(x), unsigned(value_count)-1u)]; + const float ret_ = data[std::min(unsigned(x), value_count - 1)]; return sign * clamp(ret_, 0, 1000); } -void spline::add_lone_point() -{ - points = { QPointF(s->opts.clamp_x_, s->opts.clamp_y_) }; - s->points = points; -} - -QPointF spline::ensure_in_bounds(const QList<QPointF>& points, int i) +void spline::ensure_in_bounds(const QList<QPointF>& points, int i, f& x, f& y) { const int sz = points.size(); if (i < 0 || sz == 0) - return {}; - - if (i < sz) - return points[i]; - - return points[sz - 1]; + { + x = 0; + y = 0; + } + else if (i < sz) + { + const QPointF& pt = points[i]; + x = (f)pt.x(); + y = (f)pt.y(); + } + else + { + const QPointF& pt = points[sz - 1]; + x = (f)pt.x(); + y = (f)pt.y(); + } } int spline::element_count(const QList<QPointF>& points, double max_input) { - const unsigned sz = points.size(); - for (unsigned k = 0; k < sz; k++) + const unsigned sz = (unsigned)points.size(); + for (int k = sz-1; k >= 0; k--) { const QPointF& pt = points[k]; - if (max_input > 1e-4 && pt.x() - 1e-2 > max_input) + if (max_input > 1e-4 && pt.x() - 1e-4 >= max_input) return k; } return sz; @@ -148,21 +144,17 @@ bool spline::sort_fn(const QPointF& one, const QPointF& two) return one.x() < two.x(); } -void spline::update_interp_data() +void spline::update_interp_data() const { points_t list = points; ensure_valid(list); const int sz = list.size(); - const double maxx = max_input(); - if (list.isEmpty()) - list.prepend({ maxx, max_output() }); + list.prepend({ max_input(), max_output() }); const double c = bucket_size_coefficient(list); - const double c_interp = c * 30; - - enum { magic_fill_value = -255 }; + const double c_ = c * c_interp; for (unsigned i = 0; i < value_count; i++) data[i] = magic_fill_value; @@ -172,35 +164,35 @@ void spline::update_interp_data() const QPointF& pt = list[0]; const double x = pt.x(); const double y = pt.y(); - const unsigned max = clamp(uround(x * c), 0, value_count-2); + const unsigned max = clamp(uround(x * c), 0, value_count-1); for (unsigned k = 0; k <= max; k++) data[k] = float(y * k / max); // no need for bresenham } else { - if (list[0].x() > 1e-2 && list[0].x() <= maxx) - list.push_front(QPointF(0, 0)); + if (list[0].x() > 1e-2) + list.push_front({}); // now this is hella expensive due to `c_interp' for (int i = 0; i < sz; i++) { - const QPointF p0 = ensure_in_bounds(list, i - 1); - const QPointF p1 = ensure_in_bounds(list, i + 0); - const QPointF p2 = ensure_in_bounds(list, i + 1); - const QPointF p3 = ensure_in_bounds(list, i + 2); - const double p0_x = p0.x(), p1_x = p1.x(), p2_x = p2.x(), p3_x = p3.x(); - const double p0_y = p0.y(), p1_y = p1.y(), p2_y = p2.y(), p3_y = p3.y(); - - const double cx[4] = { + f p0_x, p1_x, p2_x, p3_x; + f p0_y, p1_y, p2_y, p3_y; + + ensure_in_bounds(list, i - 1, p0_x, p0_y); + ensure_in_bounds(list, i + 0, p1_x, p1_y); + ensure_in_bounds(list, i + 1, p2_x, p2_y); + ensure_in_bounds(list, i + 2, p3_x, p3_y); + + const f cx[4] = { 2 * p1_x, // 1 -p0_x + p2_x, // t 2 * p0_x - 5 * p1_x + 4 * p2_x - p3_x, // t^2 -p0_x + 3 * p1_x - 3 * p2_x + p3_x, // t3 }; - const double cy[4] = - { + const f cy[4] = { 2 * p1_y, // 1 -p0_y + p2_y, // t 2 * p0_y - 5 * p1_y + 4 * p2_y - p3_y, // t^2 @@ -208,182 +200,205 @@ void spline::update_interp_data() }; // multiplier helps fill in all the x's needed - const unsigned end = int(c_interp * (p2_x - p1_x)) + 1; + const unsigned end = (unsigned)(c_ * (p2_x - p1_x)) + 1; + const f end_(end); for (unsigned k = 0; k <= end; k++) { - const double t = k / double(end); - const double t2 = t*t; - const double t3 = t*t*t; + const f t = k / end_; + const f t2 = t*t; + const f t3 = t*t*t; - const int x = int(.5 * c * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3)); - const float y = float(.5 * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3)); + const unsigned x = unsigned(f(.5) * c * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3)); + const float y = (float)(f(.5) * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3)); - if (unsigned(x) < value_count) + if (x < value_count) data[x] = y; } } } - double maxy = max_output(); + float maxy = (float)max_output(); float last = 0; - for (unsigned i = 0; i < unsigned(value_count); i++) + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" // stupid clang +#endif + + for (unsigned i = 0; i < value_count; i++) { if (data[i] == magic_fill_value) data[i] = last; - data[i] = (float)clamp(data[i], 0, maxy); + data[i] = clamp(data[i], 0, maxy); last = data[i]; } + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif } void spline::remove_point(int i) { - QMutexLocker foo(&_mutex); + std::shared_ptr<settings> S; + { + QMutexLocker foo(&mtx); + S = s; - const int sz = element_count(points, max_input()); + const int sz = element_count(points, max_input()); - if (i >= 0 && i < sz) - { - points.erase(points.begin() + i); - s->points = points; - validp = false; + if (i >= 0 && i < sz) + { + points.erase(points.begin() + i); + s->points = points; + update_interp_data(); + } } + + emit S->recomputed(); } void spline::add_point(QPointF pt) { - QMutexLocker foo(&_mutex); - - points.push_back(pt); - std::stable_sort(points.begin(), points.end(), sort_fn); - s->points = points; - validp = false; + std::shared_ptr<settings> S; + { + QMutexLocker foo(&mtx); + S = s; + points.push_back(pt); + std::stable_sort(points.begin(), points.end(), sort_fn); + s->points = points; + update_interp_data(); + } + emit S->recomputed(); } void spline::add_point(double x, double y) { - add_point(QPointF(x, y)); + add_point({ x, y }); } void spline::move_point(int idx, QPointF pt) { - QMutexLocker foo(&_mutex); + std::shared_ptr<settings> S; + { + QMutexLocker foo(&mtx); + S = s; - const int sz = element_count(points, max_input()); + const int sz = element_count(points, max_input()); - if (idx >= 0 && idx < sz) - { - points[idx] = pt; - std::stable_sort(points.begin(), points.end(), sort_fn); - s->points = points; - validp = false; + if (idx >= 0 && idx < sz) + { + points[idx] = pt; + std::stable_sort(points.begin(), points.end(), sort_fn); + s->points = points; + update_interp_data(); + } } + emit S->recomputed(); } -const base_spline_::points_t& spline::get_points() const +const points_t& spline::get_points() const { - QMutexLocker foo(&_mutex); return points; } int spline::get_point_count() const { - QMutexLocker foo(&_mutex); - return element_count(points, s->opts.clamp_x_); + QMutexLocker foo(&mtx); + return element_count(points, clamp_x); } void spline::reload() { - QMutexLocker foo(&_mutex); + QMutexLocker foo(&mtx); s->b->reload(); } void spline::save() { - QMutexLocker foo(&_mutex); + QMutexLocker foo(&mtx); s->b->save(); } -void spline::invalidate_settings() +void spline::invalidate_settings_() { - // we're holding the mutex to allow signal disconnection in spline dtor - // before this slot gets called for the next time + points = s->points; + clamp_x = s->opts.clamp_x_; + clamp_y = s->opts.clamp_y_; + update_interp_data(); +} +void spline::invalidate_settings() +{ + std::shared_ptr<settings> S; { - QMutexLocker l(&_mutex); - validp = false; - points = s->points; + QMutexLocker l(&mtx); + S = s; + invalidate_settings_(); } - emit s->recomputed(); + emit S->recomputed(); } void spline::set_bundle(bundle b, const QString& axis_name, Axis axis) { - QMutexLocker l(&_mutex); + if (!b) + b = make_bundle({}); + + std::shared_ptr<settings> S; - // gets called from ctor hence the need for nullptr checks - // the sentinel settings/bundle objects don't need any further branching once created - if (!s || s->b != b) { - disconnect_signals(); + QMutexLocker l(&mtx); - if (!b) - b = make_bundle(QString{}); + disconnect_signals(); s = std::make_shared<settings>(b, axis_name, axis); - - conn_changed = QObject::connect(b.get(), &bundle_::changed, - s.get(), [&] { invalidate_settings(); }, Qt::QueuedConnection); - // this isn't strictly necessary for the spline but helps the widget - conn_maxx = QObject::connect(&s->opts.clamp_x_, value_::value_changed<int>(), - ctx.get(), [&](double) { invalidate_settings(); }, Qt::QueuedConnection); - conn_maxy = QObject::connect(&s->opts.clamp_y_, value_::value_changed<int>(), - ctx.get(), [&](double) { invalidate_settings(); }, Qt::QueuedConnection); - - invalidate_settings(); + invalidate_settings_(); + S = s; + + conn_points = QObject::connect(&s->points, value_::value_changed<QList<QPointF>>(), + ctx.get(), [this] { invalidate_settings(); }, Qt::DirectConnection); + conn_maxx = QObject::connect(&s->opts.clamp_x_, value_::value_changed<int>(), + ctx.get(), [this](double) { invalidate_settings(); }, Qt::DirectConnection); + conn_maxy = QObject::connect(&s->opts.clamp_y_, value_::value_changed<int>(), + ctx.get(), [this](double) { invalidate_settings(); }, Qt::DirectConnection); } + + emit S->recomputed(); } double spline::max_input() const { - QMutexLocker l(&_mutex); - using m = axis_opts::max_clamp; - const value<m>& clamp = s->opts.clamp_x_; - if (clamp == m::x1000 && !points.empty()) - return points[points.size() - 1].x(); - return std::fabs(clamp.to<double>()); + QMutexLocker l(&mtx); + if (clamp_x == axis_opts::x1000) + return std::fmax(1, points.empty() ? 0 : points[points.size() - 1].x()); + return std::fabs((double)clamp_x); } double spline::max_output() const { - QMutexLocker l(&_mutex); - using m = axis_opts::max_clamp; - const value<m>& clamp = s->opts.clamp_y_; - if (clamp == m::x1000 && !points.empty()) - return points[points.size() - 1].y(); - return std::fabs(clamp.to<double>()); + QMutexLocker l(&mtx); + if (clamp_y == axis_opts::x1000 && !points.empty()) + return std::fmax(1, points.empty() ? 0 : points[points.size() - 1].y()); + return std::fabs((double)clamp_y); } -void spline::ensure_valid(points_t& list) +void spline::ensure_valid(points_t& list) const { - QMutexLocker foo(&_mutex); - - // storing to s->points fires bundle::changed and that leads to an infinite loop - // thus, only store if we can't help it std::stable_sort(list.begin(), list.end(), sort_fn); - const int sz = list.size(); + const unsigned sz = (unsigned)list.size(); - QList<QPointF> all_points, tmp; - all_points.reserve(sz), tmp.reserve(sz); + QList<QPointF> tmp_points, all_points; + tmp_points.reserve(sz); all_points.reserve(sz); const double maxx = max_input(); - for (int i = 0; i < sz; i++) + for (unsigned i = 0; i < sz; i++) { QPointF& pt{list[i]}; bool overlap = false; - for (int j = i+1; j < sz; j++) + for (unsigned j = i+1; j < sz; j++) { const QPointF& pt2{list[j]}; const QPointF tmp(pt - pt2); @@ -398,57 +413,56 @@ void spline::ensure_valid(points_t& list) if (!overlap) { - tmp.append(pt); // all points total + all_points.append(pt); // all points total // points within selected limit, for use in `update_interp_data' if (pt.x() - 1e-4 <= maxx && pt.x() >= 0) - all_points.push_back(pt); + tmp_points.push_back(pt); } } - // size check guards against livelock with value<t>/bundle_ signals + // simply storing to s->points fires bundle::changed leading to a livelock + // hence only store if we can't help it - // points that are within bounds - if (tmp.size() < points.size()) + if (all_points.size() < points.size()) { - points = std::move(tmp); - // can't stuff there unconditionally - // fires signals from `value<t>' and `bundle_' otherwise + // all points that don't overlap + points = std::move(all_points); s->points = points; } - if (all_points.size() < list.size()) - // all points that don't overlap, after clamping - list = std::move(all_points); + if (tmp_points.size() < list.size()) + // points that are within currently-specified bounds + list = std::move(tmp_points); last_input_value = {}; activep = false; } -// the return value is only safe to use with no spline::set_bundle calls std::shared_ptr<base_settings> spline::get_settings() { - QMutexLocker foo(&_mutex); + QMutexLocker foo(&mtx); return std::static_pointer_cast<base_settings>(s); } std::shared_ptr<const base_settings> spline::get_settings() const { - QMutexLocker foo(&_mutex); + QMutexLocker foo(&mtx); return std::static_pointer_cast<const base_settings>(s); } double spline::bucket_size_coefficient(const QList<QPointF>& points) const { constexpr double eps = 1e-4; - const double maxx = max_input(); if (maxx < eps) return 0; - // needed to fill the buckets up to the last control point. - // space between that point and max_x doesn't matter. + // buckets are only used between 0 and the rightmost point's + // x coordinate. even if `clamp_x' is set to a much larger value, + // buckets retain more precision lower the x coordinate of the + // last point. const int sz = element_count(points, maxx); const double last_x = sz ? points[sz - 1].x() : maxx; @@ -458,26 +472,17 @@ double spline::bucket_size_coefficient(const QList<QPointF>& points) const void spline::disconnect_signals() { - if (conn_changed) + if (conn_points) { - QObject::disconnect(conn_changed); - QObject::disconnect(conn_maxx); - QObject::disconnect(conn_maxy); - - conn_changed = {}; - conn_maxx = {}; - conn_maxy = {}; + QObject::disconnect(conn_points); conn_points = {}; + QObject::disconnect(conn_maxx); conn_maxx = {}; + QObject::disconnect(conn_maxy); conn_maxy = {}; } } -namespace spline_detail { - settings::settings(bundle const& b, const QString& axis_name, Axis idx): - b(b ? b : make_bundle("")), + b(b ? b : make_bundle({})), opts(axis_name, idx) {} -settings::~settings() = default; - -} - +} // ns spline_detail diff --git a/spline/spline.hpp b/spline/spline.hpp index 3d2d6e57..a78db8dd 100644 --- a/spline/spline.hpp +++ b/spline/spline.hpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, Stanislaw Halik <sthalik@misaki.pl> +/* Copyright (c) 2012-2019, Stanislaw Halik <sthalik@misaki.pl> * Permission to use, copy, modify, and/or distribute this * software for any purpose with or without fee is hereby granted, @@ -8,18 +8,15 @@ #pragma once -#include "compat/copyable-mutex.hpp" #include "options/options.hpp" - #include "axis-opts.hpp" - #include "export.hpp" +#include "compat/mutex.hpp" #include <cstddef> #include <vector> #include <limits> #include <memory> -#include <functional> #include <QObject> #include <QPointF> @@ -28,6 +25,7 @@ namespace spline_detail { +using points_t = QList<QPointF>; using namespace options; class OTR_SPLINE_EXPORT base_settings : public QObject @@ -50,10 +48,10 @@ public: struct OTR_SPLINE_EXPORT base_spline_ { - virtual inline ~base_spline_(); + virtual ~base_spline_(); - virtual float get_value(double x) = 0; - virtual float get_value_no_save(double x) const = 0; + virtual double get_value(double x) const = 0; + virtual double get_value_no_save(double x) const = 0; [[nodiscard]] virtual bool get_last_value(QPointF& point) = 0; virtual void set_tracking_active(bool value) = 0; @@ -61,20 +59,16 @@ struct OTR_SPLINE_EXPORT base_spline_ virtual double max_input() const = 0; virtual double max_output() const = 0; - using points_t = QList<QPointF>; - - virtual points_t const& get_points() const = 0; + virtual const points_t& get_points() const = 0; virtual int get_point_count() const = 0; }; struct OTR_SPLINE_EXPORT spline_settings_mixin { - using base_settings = spline_detail::base_settings; + virtual std::shared_ptr<base_settings> get_settings() = 0; + virtual std::shared_ptr<const base_settings> get_settings() const = 0; - virtual std::shared_ptr<spline_detail::base_settings> get_settings() = 0; - virtual std::shared_ptr<const spline_detail::base_settings> get_settings() const = 0; - - virtual inline ~spline_settings_mixin(); + virtual ~spline_settings_mixin(); }; struct OTR_SPLINE_EXPORT spline_modify_mixin @@ -85,42 +79,44 @@ struct OTR_SPLINE_EXPORT spline_modify_mixin virtual void remove_point(int i) = 0; virtual void clear() = 0; - virtual inline ~spline_modify_mixin(); + virtual ~spline_modify_mixin(); }; struct OTR_SPLINE_EXPORT base_spline : base_spline_, spline_modify_mixin, spline_settings_mixin { + ~base_spline() override; }; class OTR_SPLINE_EXPORT spline : public base_spline { + using f = float; + double bucket_size_coefficient(const QList<QPointF>& points) const; - void update_interp_data(); - float get_value_internal(int x); - void add_lone_point(); - float get_value_no_save_internal(double x); + void update_interp_data() const; + double get_value_internal(int x) const; static bool sort_fn(const QPointF& one, const QPointF& two); - static QPointF ensure_in_bounds(const QList<QPointF>& points, int i); + static void ensure_in_bounds(const QList<QPointF>& points, int i, f& x, f& y); static int element_count(const QList<QPointF>& points, double max_input); void disconnect_signals(); + void invalidate_settings_(); - std::shared_ptr<spline_detail::settings> s; - QMetaObject::Connection conn_changed, conn_maxx, conn_maxy; + mutex mtx { mutex::Recursive }; + std::shared_ptr<settings> s; + QMetaObject::Connection conn_points, conn_maxx, conn_maxy; - static constexpr inline std::size_t value_count = 4096; - - std::vector<float> data = std::vector<float>(value_count, float(-16)); - - mutex _mutex { mutex::recursive }; - QPointF last_input_value; std::shared_ptr<QObject> ctx { std::make_shared<QObject>() }; - bool activep = false; - bool validp = false; + mutable QPointF last_input_value{-1, -1}; + mutable std::vector<float> data = std::vector<float>(value_count, magic_fill_value); + mutable points_t points; + mutable axis_opts::max_clamp clamp_x = axis_opts::x1000, clamp_y = axis_opts::x1000; + mutable bool activep = false; - points_t points; + static constexpr unsigned value_count = 8192; + static constexpr float magic_fill_value = -(1 << 24) + 1; + static constexpr double c_interp = 5; public: void invalidate_settings(); @@ -136,11 +132,10 @@ public: spline(const QString& name, const QString& axis_name, Axis axis); ~spline() override; - spline& operator=(const spline&) = default; spline(const spline&) = default; - float get_value(double x) override; - float get_value_no_save(double x) const override; + double get_value(double x) const override; + double get_value_no_save(double x) const override; [[nodiscard]] bool get_last_value(QPointF& point) override; void add_point(QPointF pt) override; @@ -149,24 +144,18 @@ public: void remove_point(int i) override; void clear() override; - points_t const& get_points() const override; + const points_t& get_points() const override; void set_tracking_active(bool value) override; bundle get_bundle(); - void ensure_valid(points_t& in_out); + void ensure_valid(points_t& in_out) const; - std::shared_ptr<spline_detail::base_settings> get_settings() override; - std::shared_ptr<const spline_detail::base_settings> get_settings() const override; + std::shared_ptr<base_settings> get_settings() override; + std::shared_ptr<const base_settings> get_settings() const override; int get_point_count() const override; - - using settings = spline_detail::settings; }; -inline base_spline_::~base_spline_() = default; -inline spline_modify_mixin::~spline_modify_mixin() = default; -inline spline_settings_mixin::~spline_settings_mixin() = default; - } // ns spline_detail using spline = spline_detail::spline; diff --git a/tracker-aruco/CMakeLists.txt b/tracker-aruco/CMakeLists.txt index 253cdd5f..bfa7a348 100644 --- a/tracker-aruco/CMakeLists.txt +++ b/tracker-aruco/CMakeLists.txt @@ -15,7 +15,7 @@ if(OpenCV_FOUND) maybe_add_static_define() - set(modules "${SDK_ARUCO_LIBPATH}" opencv_core opencv_calib3d opencv_imgproc opencv_videoio) + set(modules "${SDK_ARUCO_LIBPATH}" opencv_calib3d opencv_imgproc opencv_core) get_filename_component(dir "${SDK_ARUCO_LIBPATH}" DIRECTORY) get_filename_component(dir "${dir}" ABSOLUTE) diff --git a/tracker-aruco/aruco-trackercontrols.ui b/tracker-aruco/aruco-trackercontrols.ui index a1600a8f..9c3bd78a 100644 --- a/tracker-aruco/aruco-trackercontrols.ui +++ b/tracker-aruco/aruco-trackercontrols.ui @@ -154,36 +154,6 @@ <verstretch>0</verstretch> </sizepolicy> </property> - <item> - <property name="text"> - <string>Default</string> - </property> - </item> - <item> - <property name="text"> - <string>30</string> - </property> - </item> - <item> - <property name="text"> - <string>60</string> - </property> - </item> - <item> - <property name="text"> - <string>75</string> - </property> - </item> - <item> - <property name="text"> - <string>125</string> - </property> - </item> - <item> - <property name="text"> - <string>200</string> - </property> - </item> </widget> </item> <item row="4" column="1"> diff --git a/tracker-aruco/ftnoir_tracker_aruco.cpp b/tracker-aruco/ftnoir_tracker_aruco.cpp index 502b50ad..8928566f 100644 --- a/tracker-aruco/ftnoir_tracker_aruco.cpp +++ b/tracker-aruco/ftnoir_tracker_aruco.cpp @@ -5,7 +5,6 @@ * copyright notice and this permission notice appear in all copies. */ -#include "cv/video-widget.hpp" #include "ftnoir_tracker_aruco.h" #include "cv/video-property-page.hpp" #include "compat/camera-names.hpp" @@ -28,6 +27,7 @@ #include <QDebug> #include <vector> +#include <tuple> #include <cstdio> #include <cmath> #include <algorithm> @@ -134,35 +134,35 @@ bool aruco_tracker::detect_without_roi() return markers.size() == 1 && markers[0].size() == 4; } +static int enum_to_fps(int value) +{ + int fps; + + switch (value) + { + default: eval_once(qDebug() << "aruco: invalid fps enum value"); + [[fallthrough]]; + case fps_default: fps = 0; break; + case fps_30: fps = 30; break; + case fps_60: fps = 60; break; + case fps_75: fps = 75; break; + case fps_125: fps = 125; break; + case fps_200: fps = 200; break; + case fps_50: fps = 50; break; + case fps_100: fps = 100; break; + case fps_120: fps = 120; break; + } + + return fps; +} + bool aruco_tracker::open_camera() { int rint = s.resolution; if (rint < 0 || rint >= (int)(sizeof(resolution_choices) / sizeof(resolution_tuple))) rint = 0; resolution_tuple res = resolution_choices[rint]; - int fps; - switch (*s.force_fps) - { - default: - case 0: - fps = 0; - break; - case 1: - fps = 30; - break; - case 2: - fps = 60; - break; - case 3: - fps = 75; - break; - case 4: - fps = 125; - break; - case 5: - fps = 200; - break; - } + int fps = enum_to_fps(s.force_fps); QMutexLocker l(&camera_mtx); @@ -373,7 +373,10 @@ void aruco_tracker::run() QMutexLocker l(&camera_mtx); if (!camera.read(color)) + { + portable::sleep(100); continue; + } } cv::cvtColor(color, grayscale, cv::COLOR_BGR2GRAY); @@ -451,17 +454,52 @@ void aruco_tracker::data(double *data) data[TZ] = pose[TZ]; } +void aruco_dialog::make_fps_combobox() +{ + std::vector<std::tuple<int, int>> resolutions; + resolutions.reserve(fps_MAX); + + for (int k = 0; k < fps_MAX; k++) + { + int hz = enum_to_fps(k); + resolutions.emplace_back(k, hz); + } + + std::sort(resolutions.begin(), resolutions.end(), [](const auto& a, const auto& b) { + auto [idx1, hz1] = a; + auto [idx2, hz2] = b; + + return hz1 < hz2; + }); + + for (auto [idx, hz] : resolutions) + { + QString name; + + if (hz == 0) + name = tr("Default"); + else + name = QString::number(hz); + + ui.cameraFPS->addItem(name, idx); + } +} + aruco_dialog::aruco_dialog() : calibrator(1, 0) { + ui.setupUi(this); + //setAttribute(Qt::WA_NativeWindow, true); + + make_fps_combobox(); + tie_setting(s.force_fps, ui.cameraFPS); + tracker = nullptr; calib_timer.setInterval(100); - ui.setupUi(this); - setAttribute(Qt::WA_NativeWindow, true); ui.cameraName->addItems(get_camera_names()); + tie_setting(s.camera_name, ui.cameraName); tie_setting(s.resolution, ui.resolution); - tie_setting(s.force_fps, ui.cameraFPS); tie_setting(s.fov, ui.cameraFOV); tie_setting(s.headpos_x, ui.cx); tie_setting(s.headpos_y, ui.cy); @@ -546,4 +584,6 @@ void aruco_dialog::update_camera_settings_state(const QString& name) ui.camera_settings->setEnabled(true); } +settings::settings() : opts("aruco-tracker") {} + OPENTRACK_DECLARE_TRACKER(aruco_tracker, aruco_dialog, aruco_metadata) diff --git a/tracker-aruco/ftnoir_tracker_aruco.h b/tracker-aruco/ftnoir_tracker_aruco.h index 6c249c6a..1d6fd107 100644 --- a/tracker-aruco/ftnoir_tracker_aruco.h +++ b/tracker-aruco/ftnoir_tracker_aruco.h @@ -37,34 +37,48 @@ using namespace options; -struct settings : opts { - value<QString> camera_name { b, "camera-name", ""}; - value<int> fov { b, "field-of-view", 56 }; +enum aruco_fps +{ + fps_default = 0, + fps_30 = 1, + fps_60 = 2, + fps_75 = 3, + fps_125 = 4, + fps_200 = 5, + fps_50 = 6, + fps_100 = 7, + fps_120 = 8, + fps_MAX = 9, +}; +struct settings : opts { value<double> headpos_x { b, "headpos-x", 0 }, headpos_y { b, "headpos-y", 0 }, headpos_z { b, "headpos-z", 0 }; - value<int> force_fps { b, "force-fps", 0 }, - resolution { b, "force-resolution", 0 }; + value<QString> camera_name { b, "camera-name", ""}; + value<int> resolution { b, "force-resolution", 0 }; + value<int> fov { b, "field-of-view", 56 }; + value<aruco_fps> force_fps { b, "force-fps", fps_default }; - settings() : opts("aruco-tracker") {} + settings(); }; -class aruco_dialog; - class aruco_tracker : protected virtual QThread, public ITracker { Q_OBJECT - friend class aruco_dialog; - static constexpr inline float c_search_window = 1.3f; + static constexpr float c_search_window = 1.3f; public: aruco_tracker(); ~aruco_tracker() override; module_status start_tracker(QFrame* frame) override; void data(double *data) override; void run() override; + void getRT(cv::Matx33d &r, cv::Vec3d &t); + QMutex camera_mtx; + cv::VideoCapture camera; + private: bool detect_with_roi(); bool detect_without_roi(); @@ -81,48 +95,46 @@ private: void set_detector_params(); void cycle_detection_params(); - cv::VideoCapture camera; - QMutex camera_mtx; QMutex mtx; std::unique_ptr<cv_video_widget> videoWidget; std::unique_ptr<QHBoxLayout> layout; settings s; double pose[6] {}, fps = 0; double no_detection_timeout = 0; - cv::Mat frame, grayscale, color; cv::Matx33d r; - std::vector<cv::Point3f> obj_points {4}; cv::Matx33d intrinsics = cv::Matx33d::eye(); - aruco::MarkerDetector detector; - std::vector<aruco::Marker> markers; cv::Vec3d t; cv::Vec3d rvec, tvec; - std::vector<cv::Point2f> roi_projection; - std::vector<cv::Point2f> repr2; cv::Matx33d m_r, m_q, rmat = cv::Matx33d::eye(); cv::Vec3d euler; std::vector<cv::Point3f> roi_points {4}; + std::vector<cv::Point2f> roi_projection; + std::vector<cv::Point2f> repr2; + std::vector<cv::Point3f> obj_points {4}; + aruco::MarkerDetector detector; + std::vector<aruco::Marker> markers; + cv::Mat frame, grayscale, color; cv::Rect last_roi { 65535, 65535, 0, 0 }; Timer fps_timer, last_detection_timer; unsigned adaptive_size_pos { 0 }; bool use_otsu = false; #if !defined USE_EXPERIMENTAL_CANNY - static constexpr inline int adaptive_thres = 6; + static constexpr int adaptive_thres = 6; #else - static constexpr inline int adaptive_thres = 3; + static constexpr int adaptive_thres = 3; #endif - static constexpr inline double timeout = .35; - static constexpr inline double timeout_backoff_c = .25; + static constexpr double timeout = .35; + static constexpr double timeout_backoff_c = .25; - static constexpr inline float size_min = 0.05; - static constexpr inline float size_max = 0.5; + static constexpr float size_min = 0.05f; + static constexpr float size_max = 0.5f; - static constexpr inline double RC = .25; + static constexpr double RC = .25; #ifdef DEBUG_UNSHARP_MASKING - static constexpr inline double gauss_kernel_size = 3; + static constexpr double gauss_kernel_size = 3; cv::Mat blurred; #endif }; @@ -135,6 +147,8 @@ public: void register_tracker(ITracker * x) override { tracker = static_cast<aruco_tracker*>(x); } void unregister_tracker() override { tracker = nullptr; } private: + void make_fps_combobox(); + Ui::Form ui; aruco_tracker* tracker; settings s; @@ -153,6 +167,6 @@ private Q_SLOTS: class aruco_metadata : public Metadata { Q_OBJECT - QString name() { return QString("aruco -- paper marker tracker"); } - QIcon icon() { return QIcon(":/images/aruco.png"); } + QString name() override { return QString("aruco -- paper marker tracker"); } + QIcon icon() override { return QIcon(":/images/aruco.png"); } }; diff --git a/tracker-aruco/lang/nl_NL.ts b/tracker-aruco/lang/nl_NL.ts index a4136086..87040565 100644 --- a/tracker-aruco/lang/nl_NL.ts +++ b/tracker-aruco/lang/nl_NL.ts @@ -36,30 +36,6 @@ <translation><html><head/><body><p>Lees de <a href="https://github.com/opentrack/opentrack/wiki/Aruco-tracker"><span style=" text-decoration: underline; color:#0000ff;">wiki-pagina</span></a> en voornamelijk de laatste paragraaf voordat u de markering uitprint.</p></body></html></translation> </message> <message> - <source>Default</source> - <translation>Standaard</translation> - </message> - <message> - <source>30</source> - <translation>30</translation> - </message> - <message> - <source>60</source> - <translation>30</translation> - </message> - <message> - <source>75</source> - <translation>75</translation> - </message> - <message> - <source>125</source> - <translation>125</translation> - </message> - <message> - <source>200</source> - <translation>200</translation> - </message> - <message> <source>Default (not recommended!)</source> <translation>Standaard (niet aanbevolen)</translation> </message> @@ -84,4 +60,11 @@ <translation>Schakel tussen kalibratie</translation> </message> </context> +<context> + <name>aruco_dialog</name> + <message> + <source>Default</source> + <translation type="unfinished">Standaard</translation> + </message> +</context> </TS> diff --git a/tracker-aruco/lang/ru_RU.ts b/tracker-aruco/lang/ru_RU.ts index f2088d1d..57c8170d 100644 --- a/tracker-aruco/lang/ru_RU.ts +++ b/tracker-aruco/lang/ru_RU.ts @@ -16,30 +16,6 @@ <translation type="unfinished"></translation> </message> <message> - <source>Default</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>30</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>60</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>75</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>125</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>200</source> - <translation type="unfinished"></translation> - </message> - <message> <source>640x480</source> <translation type="unfinished"></translation> </message> @@ -84,4 +60,11 @@ <translation type="unfinished"></translation> </message> </context> +<context> + <name>aruco_dialog</name> + <message> + <source>Default</source> + <translation type="unfinished"></translation> + </message> +</context> </TS> diff --git a/tracker-aruco/lang/stub.ts b/tracker-aruco/lang/stub.ts index aadcbd03..67cb36b8 100644 --- a/tracker-aruco/lang/stub.ts +++ b/tracker-aruco/lang/stub.ts @@ -16,30 +16,6 @@ <translation type="unfinished"></translation> </message> <message> - <source>Default</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>30</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>60</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>75</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>125</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>200</source> - <translation type="unfinished"></translation> - </message> - <message> <source>640x480</source> <translation type="unfinished"></translation> </message> @@ -84,4 +60,11 @@ <translation type="unfinished"></translation> </message> </context> +<context> + <name>aruco_dialog</name> + <message> + <source>Default</source> + <translation type="unfinished"></translation> + </message> +</context> </TS> diff --git a/tracker-aruco/lang/zh_CN.ts b/tracker-aruco/lang/zh_CN.ts index 2f497e98..b5093b4e 100644 --- a/tracker-aruco/lang/zh_CN.ts +++ b/tracker-aruco/lang/zh_CN.ts @@ -28,30 +28,6 @@ <translation type="unfinished"></translation> </message> <message> - <source>Default</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>30</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>60</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>75</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>125</source> - <translation type="unfinished"></translation> - </message> - <message> - <source>200</source> - <translation type="unfinished"></translation> - </message> - <message> <source>Camera name</source> <translation type="unfinished"></translation> </message> @@ -84,4 +60,11 @@ <translation type="unfinished"></translation> </message> </context> +<context> + <name>aruco_dialog</name> + <message> + <source>Default</source> + <translation type="unfinished"></translation> + </message> +</context> </TS> diff --git a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp index 269f8266..6622a3b7 100644 --- a/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp +++ b/tracker-freepie-udp/ftnoir_tracker_freepie-udp.cpp @@ -61,12 +61,13 @@ void tracker_freepie::run() { continue; case flag_Raw | flag_Orient: for (int i = 0; i < 3; i++) - orient[i] = tmp.fl[i+9]; + orient[i] = (double)tmp.fl[i+9]; break; case flag_Orient: for (int i = 0; i < 3; i++) - orient[i] = tmp.fl[i]; + orient[i] = (double)tmp.fl[i]; break; + default: goto fail; } filled = true; @@ -100,6 +101,7 @@ void tracker_freepie::run() { pose[Yaw + i] = r2d * orient[axis] + add; } } +fail: usleep(4000); } } diff --git a/tracker-fusion/fusion.cpp b/tracker-fusion/fusion.cpp index 5bf979ff..3e849caa 100644 --- a/tracker-fusion/fusion.cpp +++ b/tracker-fusion/fusion.cpp @@ -6,6 +6,8 @@ * notice appear in all copies. */ +#undef NDEBUG + #include "fusion.h" #include "compat/library-path.hpp" diff --git a/tracker-hatire/CMakeLists.txt b/tracker-hatire/CMakeLists.txt index 39431724..a73a471c 100644 --- a/tracker-hatire/CMakeLists.txt +++ b/tracker-hatire/CMakeLists.txt @@ -1,5 +1,5 @@ if(Qt5SerialPort_FOUND) otr_module(tracker-hatire) - target_link_libraries(opentrack-tracker-hatire ${Qt5SerialPort_LIBRARIES}) - target_include_directories(opentrack-tracker-hatire SYSTEM PUBLIC ${Qt5SerialPort_INCLUDE_DIRS}) + target_link_libraries(${self} ${Qt5SerialPort_LIBRARIES}) + target_include_directories(${self} SYSTEM PUBLIC ${Qt5SerialPort_INCLUDE_DIRS}) endif() diff --git a/tracker-hatire/ftnoir_tracker_hat.cpp b/tracker-hatire/ftnoir_tracker_hat.cpp index d9f0a33b..b53a9213 100644 --- a/tracker-hatire/ftnoir_tracker_hat.cpp +++ b/tracker-hatire/ftnoir_tracker_hat.cpp @@ -126,22 +126,22 @@ void hatire::data(double *data) const struct { + double& place; + float input; bool enable; bool sign; - float input; - double& place; } spec[] = { - { s.EnableX, s.InvertX, HAT.Trans[s.XAxis], data[TX] }, - { s.EnableY, s.InvertY, HAT.Trans[s.YAxis], data[TY] }, - { s.EnableZ, s.InvertZ, HAT.Trans[s.ZAxis], data[TZ] }, - { s.EnableYaw, s.InvertYaw, HAT.Rot[s.YawAxis], data[Yaw] }, - { s.EnablePitch, s.InvertPitch, HAT.Rot[s.PitchAxis], data[Pitch] }, - { s.EnableRoll, s.InvertRoll, HAT.Rot[s.RollAxis], data[Roll] }, + { data[TX], HAT.Trans[s.XAxis], s.EnableX, s.InvertX, }, + { data[TY], HAT.Trans[s.YAxis], s.EnableY, s.InvertY, }, + { data[TZ], HAT.Trans[s.ZAxis], s.EnableZ, s.InvertZ, }, + { data[Yaw], HAT.Rot[s.YawAxis], s.EnableYaw, s.InvertYaw }, + { data[Pitch], HAT.Rot[s.PitchAxis], s.EnablePitch, s.InvertPitch }, + { data[Roll], HAT.Rot[s.RollAxis], s.EnableRoll, s.InvertRoll }, }; for (auto& k : spec) - k.place = (k.sign ? -1 : 1) * (k.enable ? k.input : 0); + k.place = (k.sign ? -1 : 1) * (k.enable ? (double)k.input : 0); } #include "ftnoir_tracker_hat_dialog.h" diff --git a/tracker-hatire/ftnoir_tracker_hat.h b/tracker-hatire/ftnoir_tracker_hat.h index 0f88c7f6..b2300556 100644 --- a/tracker-hatire/ftnoir_tracker_hat.h +++ b/tracker-hatire/ftnoir_tracker_hat.h @@ -20,14 +20,14 @@ class hatire : public QObject, public ITracker public: hatire(); - ~hatire(); + ~hatire() override; - module_status start_tracker(QFrame*); - void data(double *data); + module_status start_tracker(QFrame*) override; + void data(double *data) override; //void center(); //bool notifyZeroed(); void reset(); - void get_info( int *tps ); + void get_info(int *tps); void serial_info(); void send_serial_command(const QByteArray& x); @@ -50,6 +50,6 @@ class hatire_metadata : public Metadata { Q_OBJECT - QString name() { return tr("Hatire Arduino"); } - QIcon icon() { return QIcon(":/images/hat.png"); } + QString name() override { return tr("Hatire Arduino"); } + QIcon icon() override { return QIcon(":/images/hat.png"); } }; diff --git a/tracker-hatire/thread.cpp b/tracker-hatire/thread.cpp index 3eb2579f..4938e77f 100644 --- a/tracker-hatire/thread.cpp +++ b/tracker-hatire/thread.cpp @@ -169,7 +169,6 @@ serial_result hatire_thread::init_serial_port_impl() && com_port.setStopBits((QSerialPort::StopBits)s.pStopBits) && com_port.setFlowControl((QSerialPort::FlowControl)s.pFlowControl) && com_port.clear(QSerialPort::AllDirections) - && com_port.setDataErrorPolicy(QSerialPort::IgnorePolicy) ) { Log(tr("Port Parameters set")); @@ -304,7 +303,7 @@ void hatire_thread::serial_info_impl() void hatire_thread::on_serial_read() { - const int sz = com_port.read(buf, sizeof(buf)); + const int sz = (int)com_port.read(buf, sizeof(buf)); if (sz > 0) { diff --git a/tracker-hydra/CMakeLists.txt b/tracker-hydra/CMakeLists.txt index 2e263a19..b99c1d7a 100644 --- a/tracker-hydra/CMakeLists.txt +++ b/tracker-hydra/CMakeLists.txt @@ -1,5 +1,5 @@ set(SDK_HYDRA "" CACHE PATH "libSixense path for Razer Hydra") -if(SDK_HYDRA) +if(SDK_HYDRA AND opentrack-intel) otr_module(tracker-hydra) target_include_directories(opentrack-tracker-hydra SYSTEM PUBLIC ${SDK_HYDRA}/include ${SDK_HYDRA}/include/sixense_utils) if(opentrack-64bit) diff --git a/tracker-hydra/ftnoir_tracker_hydra.cpp b/tracker-hydra/ftnoir_tracker_hydra.cpp index 2e9e1fc5..5f548bd6 100644 --- a/tracker-hydra/ftnoir_tracker_hydra.cpp +++ b/tracker-hydra/ftnoir_tracker_hydra.cpp @@ -18,8 +18,9 @@ #include <cstdio> #include <cmath> #ifdef _WIN32 +# undef WIN32 +# define WIN32 # define SIXENSE_STATIC_LIB -# define SIXENSE_UTILS_STATIC_LIB #endif #include <sixense.h> diff --git a/tracker-hydra/ftnoir_tracker_hydra.h b/tracker-hydra/ftnoir_tracker_hydra.h index 9d0ab835..8af44344 100644 --- a/tracker-hydra/ftnoir_tracker_hydra.h +++ b/tracker-hydra/ftnoir_tracker_hydra.h @@ -5,7 +5,8 @@ #include "options/options.hpp" using namespace options; -struct settings : opts { +struct settings : opts +{ settings() : opts("tracker-hydra") {} @@ -15,7 +16,7 @@ class Hydra_Tracker : public ITracker { public: Hydra_Tracker(); - ~Hydra_Tracker(); + ~Hydra_Tracker() override; module_status start_tracker(QFrame *) override; void data(double *data) override; @@ -29,8 +30,8 @@ class dialog_hydra: public ITrackerDialog Q_OBJECT public: dialog_hydra(); - void register_tracker(ITracker *) {} - void unregister_tracker() {} + void register_tracker(ITracker *) override {} + void unregister_tracker() override {} private: settings s; Ui::UIHydraControls ui; @@ -43,7 +44,7 @@ class hydraDll : public Metadata { Q_OBJECT - QString name() { return QString("Razer Hydra -- inertial device"); } - QIcon icon() { return QIcon(":/images/opentrack.png"); } + QString name() override { return QString("Razer Hydra -- inertial device"); } + QIcon icon() override { return QIcon(":/images/opentrack.png"); } }; diff --git a/tracker-joystick/ftnoir_tracker_joystick.h b/tracker-joystick/ftnoir_tracker_joystick.h index c61a1c8f..d59c70de 100644 --- a/tracker-joystick/ftnoir_tracker_joystick.h +++ b/tracker-joystick/ftnoir_tracker_joystick.h @@ -45,7 +45,7 @@ public: void data(double *data); settings s; QString guid; - static constexpr inline int AXIS_MAX = win32_joy_ctx::joy_axis_size; + static constexpr int AXIS_MAX = win32_joy_ctx::joy_axis_size; win32_joy_ctx joy_ctx; }; @@ -63,7 +63,7 @@ public: QString name; QString guid; }; - QList<joys> _joys; + QList<joys> joys_; private slots: void doOK(); void doCancel(); diff --git a/tracker-joystick/ftnoir_tracker_joystick_dialog.cpp b/tracker-joystick/ftnoir_tracker_joystick_dialog.cpp index aa4b783a..aaf3037b 100644 --- a/tracker-joystick/ftnoir_tracker_joystick_dialog.cpp +++ b/tracker-joystick/ftnoir_tracker_joystick_dialog.cpp @@ -12,18 +12,18 @@ dialog_joystick::dialog_joystick() : tracker(nullptr) { win32_joy_ctx joy_ctx; - _joys = QList<joys>(); + joys_ = {}; - for (auto j : joy_ctx.get_joy_info()) - _joys.push_back(joys { j.name, j.guid }); + for (const auto& j : joy_ctx.get_joy_info()) + joys_.push_back(joys { j.name, j.guid }); } { const QString guid = s.guid; int idx = 0; - for (int i = 0; i < _joys.size(); i++) + for (int i = 0; i < joys_.size(); i++) { - const joys& j = _joys[i]; + const joys& j = joys_[i]; if (j.guid == guid) idx = i; ui.joylist->addItem(j.name + " " + j.guid); @@ -41,8 +41,8 @@ dialog_joystick::dialog_joystick() : tracker(nullptr) void dialog_joystick::doOK() { int idx = ui.joylist->currentIndex(); - joys def { "", "" }; - auto val = _joys.value(idx, def); + static const joys def { {}, {} }; + auto val = joys_.value(idx, def); s.guid = val.guid; s.b->save(); close(); diff --git a/tracker-kinect-face/CMakeLists.txt b/tracker-kinect-face/CMakeLists.txt new file mode 100644 index 00000000..92ed00c1 --- /dev/null +++ b/tracker-kinect-face/CMakeLists.txt @@ -0,0 +1,44 @@ +# Kinect SDK is Windows only +if (WIN32 AND opentrack-intel) + # Setup cache variable to Kinect SDK path + set(SDK_KINECT20 "$ENV{KINECTSDK20_DIR}" CACHE PATH "Kinect SDK path") + # If we have a valid SDK path, try build that tracker + if(SDK_KINECT20) + # Register our module + otr_module(tracker-kinect-face) + + if(MSVC) + # workaround warning in SDK + target_compile_options(${self} PRIVATE "-wd4471") + endif() + + # Add include path to Kinect SDK + target_include_directories(${self} SYSTEM PRIVATE "${SDK_KINECT20}/inc") + + # Check processor architecture + if(NOT opentrack-64bit) + # 32 bits + set(kinect-arch-dir "x86") + else() + # 64 bits + set(kinect-arch-dir "x64") + endif() + + # Link against Kinect SDK libraries + target_link_libraries(${self} "${SDK_KINECT20}/lib/${kinect-arch-dir}/Kinect20.lib" "${SDK_KINECT20}/lib/${kinect-arch-dir}/Kinect20.Face.lib") + # Link against OpenCV stuff, needed for video preview + target_link_libraries(${self} opentrack-video) + + # Install Kinect Face DLL + install(FILES "${SDK_KINECT20}/Redist/Face/${kinect-arch-dir}/Kinect20.Face.dll" DESTINATION "${opentrack-hier-pfx}" PERMISSIONS ${opentrack-perms-exec}) + # Install Kinect Face Database + install(DIRECTORY "${SDK_KINECT20}/Redist/Face/${kinect-arch-dir}/NuiDatabase" DESTINATION "${opentrack-hier-pfx}") + + set(redist-dir "${CMAKE_SOURCE_DIR}/redist/${kinect-arch-dir}") + install( + FILES "${redist-dir}/msvcp110.dll" "${redist-dir}/msvcr110.dll" + DESTINATION "${opentrack-hier-pfx}" + PERMISSIONS ${opentrack-perms-exec} + ) + endif() +endif() diff --git a/tracker-kinect-face/images/kinect.png b/tracker-kinect-face/images/kinect.png Binary files differnew file mode 100644 index 00000000..fd8f5f77 --- /dev/null +++ b/tracker-kinect-face/images/kinect.png diff --git a/tracker-kinect-face/kinect_face.qrc b/tracker-kinect-face/kinect_face.qrc new file mode 100644 index 00000000..8b27c81e --- /dev/null +++ b/tracker-kinect-face/kinect_face.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>images/kinect.png</file> + </qresource> +</RCC> diff --git a/tracker-kinect-face/kinect_face_settings.cpp b/tracker-kinect-face/kinect_face_settings.cpp new file mode 100644 index 00000000..c27b20bd --- /dev/null +++ b/tracker-kinect-face/kinect_face_settings.cpp @@ -0,0 +1,39 @@ +/* Copyright (c) 2014, Stanislaw Halik <sthalik@misaki.pl> + + * Permission to use, copy, modify, and/or distribute this + * software for any purpose with or without fee is hereby granted, + * provided that the above copyright notice and this permission + * notice appear in all copies. + */ + +#include "kinect_face_settings.h" +#include "kinect_face_tracker.h" +#include "api/plugin-api.hpp" +#include "compat/math-imports.hpp" + +#include <QPushButton> + +#include <cmath> +#include <QDebug> + + +KinectFaceSettings::KinectFaceSettings() +{ + ui.setupUi(this); + + connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); + connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); +} + +void KinectFaceSettings::doOK() +{ + //s.b->save(); + close(); +} + +void KinectFaceSettings::doCancel() +{ + close(); +} + +OPENTRACK_DECLARE_TRACKER(KinectFaceTracker, KinectFaceSettings, KinectFaceMetadata) diff --git a/tracker-kinect-face/kinect_face_settings.h b/tracker-kinect-face/kinect_face_settings.h new file mode 100644 index 00000000..647aa754 --- /dev/null +++ b/tracker-kinect-face/kinect_face_settings.h @@ -0,0 +1,28 @@ +#pragma once +#include "ui_kinect_face_settings.h" +#include "compat/macros.hpp" +#include "api/plugin-api.hpp" + + +class KinectFaceSettings : public ITrackerDialog +{ + Q_OBJECT + + Ui::KinectFaceUi ui; +public: + KinectFaceSettings(); + void register_tracker(ITracker *) override {} + void unregister_tracker() override {} +private slots: + void doOK(); + void doCancel(); +}; + +class KinectFaceMetadata : public Metadata +{ + Q_OBJECT + + QString name() { return tr("Kinect Face 0.1"); } + QIcon icon() { return QIcon(":/images/kinect.png"); } +}; + diff --git a/tracker-kinect-face/kinect_face_settings.ui b/tracker-kinect-face/kinect_face_settings.ui new file mode 100644 index 00000000..e7524e4a --- /dev/null +++ b/tracker-kinect-face/kinect_face_settings.ui @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>KinectFaceUi</class> + <widget class="QWidget" name="KinectFaceUi"> + <property name="windowModality"> + <enum>Qt::NonModal</enum> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>278</width> + <height>58</height> + </rect> + </property> + <property name="windowTitle"> + <string>Kinect Face Tracker</string> + </property> + <property name="windowIcon"> + <iconset> + <normaloff>:/images/kinect.png</normaloff>:/images/kinect.png</iconset> + </property> + <property name="layoutDirection"> + <enum>Qt::LeftToRight</enum> + </property> + <property name="autoFillBackground"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Start OpenTrack to check if Kinect Face Tracker is working.</string> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Close</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> + <slots> + <slot>startEngineClicked()</slot> + <slot>stopEngineClicked()</slot> + <slot>cameraSettingsClicked()</slot> + </slots> +</ui> diff --git a/tracker-kinect-face/kinect_face_tracker.cpp b/tracker-kinect-face/kinect_face_tracker.cpp new file mode 100644 index 00000000..d4cf89e3 --- /dev/null +++ b/tracker-kinect-face/kinect_face_tracker.cpp @@ -0,0 +1,608 @@ + + +#include "kinect_face_tracker.h" + +#include <QLayout> +#include <QPainter> + +#include "compat/check-visible.hpp" + +static const int KColorWidth = 1920; +static const int KColorHeight = 1080; + +/// +bool IsValidRect(const RectI& aRect) +{ + if (aRect.Bottom != 0) + { + return true; + } + + if (aRect.Left != 0) + { + return true; + } + + if (aRect.Right != 0) + { + return true; + } + + if (aRect.Top != 0) + { + return true; + } + + return false; +} + +/// +bool IsNullVetor(const Vector4& aVector) +{ + if (aVector.w != 0) + { + return false; + } + + if (aVector.x != 0) + { + return false; + } + + if (aVector.y != 0) + { + return false; + } + + if (aVector.z != 0) + { + return false; + } + + return true; +} + +/// +bool IsNullPoint(const CameraSpacePoint& aPoint) +{ + if (aPoint.X != 0) + { + return false; + } + + if (aPoint.Y != 0) + { + return false; + } + + if (aPoint.Z != 0) + { + return false; + } + + return true; +} + + +KinectFaceTracker::KinectFaceTracker() +{ + // create heap storage for color pixel data in RGBX format + iColorRGBX = new RGBQUAD[KColorWidth * KColorHeight]; +} + +KinectFaceTracker::~KinectFaceTracker() +{ + if (iColorRGBX) + { + delete[] iColorRGBX; + iColorRGBX = nullptr; + } + + // clean up Direct2D + //SafeRelease(m_pD2DFactory); + + // done with face sources and readers + SafeRelease(iFaceFrameSource); + SafeRelease(iFaceFrameReader); + + // done with body frame reader + SafeRelease(iBodyFrameReader); + + // done with color frame reader + SafeRelease(iColorFrameReader); + + // close the Kinect Sensor + if (iKinectSensor) + { + iKinectSensor->Close(); + } + + SafeRelease(iKinectSensor); +} + +module_status KinectFaceTracker::start_tracker(QFrame* aFrame) +{ + iTimer.start(); + + if (SUCCEEDED(InitializeDefaultSensor())) + { + // Setup our video preview widget + iVideoWidget = std::make_unique<video_widget>(aFrame); + iLayout = std::make_unique<QHBoxLayout>(aFrame); + iLayout->setContentsMargins(0, 0, 0, 0); + iLayout->addWidget(iVideoWidget.get()); + aFrame->setLayout(iLayout.get()); + //video_widget->resize(video_frame->width(), video_frame->height()); + aFrame->show(); + + return status_ok(); + } + + return error("Kinect init failed!"); +} + + +bool KinectFaceTracker::center() +{ + // Mark our center + iFacePositionCenter = iFacePosition; + iFaceRotationCenter = iFaceRotation; + return true; +} + +// +// +// +void KinectFaceTracker::data(double *data) +{ + const double dt = iTimer.elapsed_seconds(); + + const double KMinDelayInSeconds = 1.0 / 30.0; // Pointless running faster than Kinect hardware itself + if (dt > KMinDelayInSeconds) + { + iTimer.start(); // Reset our timer + //OutputDebugStringA("Updating frame!\n"); + Update(); + ExtractFaceRotationInDegrees(&iFaceRotationQuaternion, &iFaceRotation.X, &iFaceRotation.Y, &iFaceRotation.Z); + //Check if data is valid + if (IsValidRect(iFaceBox)) + { + // We have valid tracking retain position and rotation + iLastFacePosition = iFacePosition; + iLastFaceRotation = iFaceRotation; + } + else + { + //TODO: after like 5s without tracking reset position to zero + //TODO: Instead of hardcoding that delay add it to our settings + } + } + else + { + //OutputDebugStringA("Skipping frame!\n"); + } + + // Feed our framework our last valid position and rotation + data[0] = (iLastFacePosition.X - iFacePositionCenter.X) * 100; // Convert to centimer to be in a range that suites OpenTrack. + data[1] = (iLastFacePosition.Y - iFacePositionCenter.Y) * 100; + data[2] = (iLastFacePosition.Z - iFacePositionCenter.Z) * 100; + + // Yaw, Picth, Roll + data[3] = -(iLastFaceRotation.X - iFaceRotationCenter.X); // Invert to be compatible with ED out-of-the-box + data[4] = (iLastFaceRotation.Y - iFaceRotationCenter.Y); + data[5] = (iLastFaceRotation.Z - iFaceRotationCenter.Z); +} + + +/// <summary> +/// Converts rotation quaternion to Euler angles +/// And then maps them to a specified range of values to control the refresh rate +/// </summary> +/// <param name="pQuaternion">face rotation quaternion</param> +/// <param name="pPitch">rotation about the X-axis</param> +/// <param name="pYaw">rotation about the Y-axis</param> +/// <param name="pRoll">rotation about the Z-axis</param> +void KinectFaceTracker::ExtractFaceRotationInDegrees(const Vector4* pQuaternion, float* pYaw, float* pPitch, float* pRoll) +{ + double x = pQuaternion->x; + double y = pQuaternion->y; + double z = pQuaternion->z; + double w = pQuaternion->w; + + // convert face rotation quaternion to Euler angles in degrees + double dPitch, dYaw, dRoll; + dPitch = atan2(2 * (y * z + w * x), w * w - x * x - y * y + z * z) / M_PI * 180.0; + dYaw = asin(2 * (w * y - x * z)) / M_PI * 180.0; + dRoll = atan2(2 * (x * y + w * z), w * w + x * x - y * y - z * z) / M_PI * 180.0; + + // clamp rotation values in degrees to a specified range of values to control the refresh rate + /* + double increment = c_FaceRotationIncrementInDegrees; + *pPitch = static_cast<int>(floor((dPitch + increment/2.0 * (dPitch > 0 ? 1.0 : -1.0)) / increment) * increment); + *pYaw = static_cast<int>(floor((dYaw + increment/2.0 * (dYaw > 0 ? 1.0 : -1.0)) / increment) * increment); + *pRoll = static_cast<int>(floor((dRoll + increment/2.0 * (dRoll > 0 ? 1.0 : -1.0)) / increment) * increment); + */ + + *pPitch = dPitch; + *pYaw = dYaw; + *pRoll = dRoll; +} + + + +/// <summary> +/// Initializes the default Kinect sensor +/// </summary> +/// <returns>S_OK on success else the failure code</returns> +HRESULT KinectFaceTracker::InitializeDefaultSensor() +{ + HRESULT hr; + + // Get and open Kinect sensor + hr = GetDefaultKinectSensor(&iKinectSensor); + if (SUCCEEDED(hr)) + { + hr = iKinectSensor->Open(); + } + + // Create color frame reader + if (SUCCEEDED(hr)) + { + UniqueInterface<IColorFrameSource> colorFrameSource; + hr = iKinectSensor->get_ColorFrameSource(colorFrameSource.PtrPtr()); + colorFrameSource.Reset(); + + if (SUCCEEDED(hr)) + { + hr = colorFrameSource->OpenReader(&iColorFrameReader); + } + } + + // Create body frame reader + if (SUCCEEDED(hr)) + { + UniqueInterface<IBodyFrameSource> bodyFrameSource; + hr = iKinectSensor->get_BodyFrameSource(bodyFrameSource.PtrPtr()); + bodyFrameSource.Reset(); + + if (SUCCEEDED(hr)) + { + hr = bodyFrameSource->OpenReader(&iBodyFrameReader); + } + } + + // Create HD face frame source + if (SUCCEEDED(hr)) + { + // create the face frame source by specifying the required face frame features + hr = CreateHighDefinitionFaceFrameSource(iKinectSensor, &iFaceFrameSource); + } + + // Create HD face frame reader + if (SUCCEEDED(hr)) + { + // open the corresponding reader + hr = iFaceFrameSource->OpenReader(&iFaceFrameReader); + } + + return hr; +} + + + +/// <summary> +/// Main processing function +/// </summary> +void KinectFaceTracker::Update() +{ + if (!iColorFrameReader || !iBodyFrameReader) + { + return; + } + + IColorFrame* pColorFrame = nullptr; + HRESULT hr = iColorFrameReader->AcquireLatestFrame(&pColorFrame); + + if (SUCCEEDED(hr)) + { + INT64 nTime = 0; + IFrameDescription* pFrameDescription = nullptr; + int nWidth = 0; + int nHeight = 0; + ColorImageFormat imageFormat = ColorImageFormat_None; + UINT nBufferSize = 0; + RGBQUAD *pBuffer = nullptr; + + hr = pColorFrame->get_RelativeTime(&nTime); + + if (SUCCEEDED(hr)) + { + hr = pColorFrame->get_FrameDescription(&pFrameDescription); + } + + if (SUCCEEDED(hr)) + { + hr = pFrameDescription->get_Width(&nWidth); + } + + if (SUCCEEDED(hr)) + { + hr = pFrameDescription->get_Height(&nHeight); + } + + if (SUCCEEDED(hr)) + { + hr = pColorFrame->get_RawColorImageFormat(&imageFormat); + } + + if (SUCCEEDED(hr)) + { + //DrawStreams(nTime, pBuffer, nWidth, nHeight); + ProcessFaces(); + } + + if (check_is_visible()) + { + //OutputDebugStringA("Widget visible!\n"); + // If our widget is visible we feed it our frame + if (SUCCEEDED(hr)) + { + // Fetch color buffer + if (imageFormat == ColorImageFormat_Rgba) + { + hr = pColorFrame->AccessRawUnderlyingBuffer(&nBufferSize, reinterpret_cast<BYTE**>(&pBuffer)); + } + else if (iColorRGBX) + { + pBuffer = iColorRGBX; + nBufferSize = KColorWidth * KColorHeight * sizeof(RGBQUAD); + hr = pColorFrame->CopyConvertedFrameDataToArray(nBufferSize, reinterpret_cast<BYTE*>(pBuffer), ColorImageFormat_Rgba); + } + else + { + hr = E_FAIL; + } + + } + + if (SUCCEEDED(hr)) + { + // Setup our image + QImage image((const unsigned char*)pBuffer, KColorWidth, KColorHeight, sizeof(RGBQUAD)*KColorWidth, QImage::Format_RGBA8888); + if (IsValidRect(iFaceBox)) + { + // Draw our face bounding box + QPainter painter(&image); + painter.setBrush(Qt::NoBrush); + painter.setPen(QPen(Qt::red, 8)); + painter.drawRect(iFaceBox.Left, iFaceBox.Top, iFaceBox.Right - iFaceBox.Left, iFaceBox.Bottom - iFaceBox.Top); + bool bEnd = painter.end(); + (void)bEnd; + } + + // Update our video preview + iVideoWidget->update_image(image); + } + + } + + + SafeRelease(pFrameDescription); + } + + SafeRelease(pColorFrame); +} + + +/// <summary> +/// Updates body data +/// </summary> +/// <param name="ppBodies">pointer to the body data storage</param> +/// <returns>indicates success or failure</returns> +HRESULT KinectFaceTracker::UpdateBodyData(IBody** ppBodies) +{ + HRESULT hr = E_FAIL; + + if (iBodyFrameReader != nullptr) + { + IBodyFrame* pBodyFrame = nullptr; + hr = iBodyFrameReader->AcquireLatestFrame(&pBodyFrame); + if (SUCCEEDED(hr)) + { + hr = pBodyFrame->GetAndRefreshBodyData(BODY_COUNT, ppBodies); + } + SafeRelease(pBodyFrame); + } + + return hr; +} + + +float VectorLengthSquared(CameraSpacePoint point) +{ + float lenghtSquared = pow(point.X, 2) + pow(point.Y, 2) + pow(point.Z, 2); + + //result = Math.Sqrt(result); + return lenghtSquared; +} + +// +// Finds the closest body from the sensor if any +// +IBody* KinectFaceTracker::FindClosestBody(IBody** aBodies) +{ + IBody* result = nullptr; + float closestBodyDistance = std::numeric_limits<float>::max(); + + for(int i=0;i<BODY_COUNT;i++) + { + BOOLEAN tracked; + aBodies[i]->get_IsTracked(&tracked); + + if (tracked) + { + Joint joints[JointType_Count]; + HRESULT hr = aBodies[i]->GetJoints(JointType_Count,joints); + if (FAILED(hr)) + { + continue; + } + + auto currentLocation = joints[JointType_SpineBase].Position; + auto currentDistance = VectorLengthSquared(currentLocation); + + if (result == nullptr || currentDistance < closestBodyDistance) + { + result = aBodies[i]; + closestBodyDistance = currentDistance; + } + } + } + + return result; +} + +// +// Search our list of body for the one matching our id +// +IBody* KinectFaceTracker::FindTrackedBodyById(IBody** aBodies, UINT64 aTrackingId) +{ + float closestBodyDistance = std::numeric_limits<float>::max(); + (void)closestBodyDistance; + + for (int i = 0; i < BODY_COUNT; i++) + { + BOOLEAN tracked; + HRESULT hr = aBodies[i]->get_IsTracked(&tracked); + + if (tracked) + { + if (SUCCEEDED(hr) && tracked) + { + UINT64 trackingId = 0; + hr = aBodies[i]->get_TrackingId(&trackingId); + + if (SUCCEEDED(hr) && aTrackingId == trackingId) + { + return aBodies[i]; + } + } + } + } + + return nullptr; +} + + +/// <summary> +/// Processes new face frames +/// </summary> +void KinectFaceTracker::ProcessFaces() +{ + HRESULT hr=0; + IBody* bodies[BODY_COUNT] = { 0 }; // Each bodies will need to be released + bool bHaveBodyData = SUCCEEDED(UpdateBodyData(bodies)); + if (!bHaveBodyData) + { + return; + } + + // Try keep tracking the same body + IBody* body = FindTrackedBodyById(bodies, iTrackingId); + if (body == nullptr) + { + // The body we were tracking is gone, try tracking the closest body if any + body = FindClosestBody(bodies); + if (body != nullptr) + { + // Update our face source with our new body id + hr = body->get_TrackingId(&iTrackingId); + if (SUCCEEDED(hr)) + { + // Tell our face source to use the given body id + hr = iFaceFrameSource->put_TrackingId(iTrackingId); + //OutputDebugStringA("Tracking new body!\n"); + } + } + } + + // retrieve the latest face frame from this reader + IHighDefinitionFaceFrame* pFaceFrame = nullptr; + if (SUCCEEDED(hr)) + { + hr = iFaceFrameReader->AcquireLatestFrame(&pFaceFrame); + } + + BOOLEAN bFaceTracked = false; + if (SUCCEEDED(hr) && nullptr != pFaceFrame) + { + // check if a valid face is tracked in this face frame + hr = pFaceFrame->get_IsTrackingIdValid(&bFaceTracked); + } + + if (SUCCEEDED(hr)) + { + if (bFaceTracked) + { + //OutputDebugStringA("Tracking face!\n"); + + //IFaceFrameResult* pFaceFrameResult = nullptr; + IFaceAlignment* pFaceAlignment = nullptr; + CreateFaceAlignment(&pFaceAlignment); // TODO: check return? + //D2D1_POINT_2F faceTextLayout; + + //hr = pFaceFrame->get_FaceFrameResult(&pFaceFrameResult); + + hr = pFaceFrame->GetAndRefreshFaceAlignmentResult(pFaceAlignment); + + // need to verify if pFaceFrameResult contains data before trying to access it + if (SUCCEEDED(hr) && pFaceAlignment != nullptr) + { + hr = pFaceAlignment->get_FaceBoundingBox(&iFaceBox); + //pFaceFrameResult->get_FaceBoundingBoxInColorSpace(); + + if (SUCCEEDED(hr)) + { + //hr = pFaceFrameResult->GetFacePointsInColorSpace(FacePointType::FacePointType_Count, facePoints); + hr = pFaceAlignment->get_HeadPivotPoint(&iFacePosition); + } + + if (SUCCEEDED(hr)) + { + //hr = pFaceFrameResult->get_FaceRotationQuaternion(&faceRotation); + hr = pFaceAlignment->get_FaceOrientation(&iFaceRotationQuaternion); + } + + if (SUCCEEDED(hr)) + { + //hr = pFaceFrameResult->GetFaceProperties(FaceProperty::FaceProperty_Count, faceProperties); + } + + if (SUCCEEDED(hr)) + { + //hr = GetFaceTextPositionInColorSpace(ppBodies[0], &faceTextLayout); + } + + if (SUCCEEDED(hr)) + { + // draw face frame results + //m_pDrawDataStreams->DrawFaceFrameResults(0, &faceBox, facePoints, &faceRotation, faceProperties, &faceTextLayout); + } + } + + SafeRelease(pFaceAlignment); + } + + SafeRelease(pFaceFrame); + } + + if (bHaveBodyData) + { + for (int i = 0; i < _countof(bodies); ++i) + { + SafeRelease(bodies[i]); + } + } +} + + diff --git a/tracker-kinect-face/kinect_face_tracker.h b/tracker-kinect-face/kinect_face_tracker.h new file mode 100644 index 00000000..3ed28bea --- /dev/null +++ b/tracker-kinect-face/kinect_face_tracker.h @@ -0,0 +1,117 @@ + + + +#include <cmath> + +#include "api/plugin-api.hpp" +#include "compat/timer.hpp" +#include "compat/macros.hpp" +#include "video/video-widget.hpp" + +// Kinect Header files +#include <Kinect.h> +#include <Kinect.Face.h> + +#pragma once + +// @deprecated Use UniqueInterface instead. Remove it at some point. +template<class Interface> +inline void SafeRelease(Interface *& pInterfaceToRelease) +{ + if (pInterfaceToRelease != nullptr) + { + pInterfaceToRelease->Release(); + pInterfaceToRelease = nullptr; + } +} + +template<class Interface> +inline void ReleaseInterface(Interface* pInterfaceToRelease) +{ + if (pInterfaceToRelease != nullptr) + { + pInterfaceToRelease->Release(); + } +} + +// Safely use Microsoft interfaces. +template<typename T> +class UniqueInterface : public std::unique_ptr<T, decltype(&ReleaseInterface<T>)> ///**/ +{ +public: + UniqueInterface() : std::unique_ptr<T, decltype(&ReleaseInterface<T>)>(nullptr, ReleaseInterface<T>){} + // Access pointer, typically for creation + T** PtrPtr() { return &iPtr; }; + // Called this once the pointer was created + void Reset() { std::unique_ptr<T, decltype(&ReleaseInterface<T>)>::reset(iPtr); } + // If ever you want to release that interface before the object is deleted + void Free() { iPtr = nullptr; Reset(); } +private: + T* iPtr = nullptr; +}; + + +// +// +// +class KinectFaceTracker : public ITracker +{ +public: + KinectFaceTracker(); + ~KinectFaceTracker() override; + module_status start_tracker(QFrame* aFrame) override; + void data(double *data) override; + bool center() override; + +private: + + + // Kinect stuff + void Update(); + HRESULT InitializeDefaultSensor(); + void ProcessFaces(); + HRESULT UpdateBodyData(IBody** ppBodies); + void ExtractFaceRotationInDegrees(const Vector4* pQuaternion, float* pPitch, float* pYaw, float* pRoll); + static IBody* FindClosestBody(IBody** aBodies); + static IBody* FindTrackedBodyById(IBody** aBodies,UINT64 aTrackingId); + + // + Timer iTimer; + + // Current Kinect + IKinectSensor* iKinectSensor = nullptr; + + // Color reader + IColorFrameReader* iColorFrameReader = nullptr; + + // Body reader + IBodyFrameReader* iBodyFrameReader = nullptr; + + // Face sources + IHighDefinitionFaceFrameSource* iFaceFrameSource = nullptr; + + // Face readers + IHighDefinitionFaceFrameReader* iFaceFrameReader = nullptr; + + // + RGBQUAD* iColorRGBX = nullptr; + + RectI iFaceBox = { 0 }; + + // Face position + CameraSpacePoint iLastFacePosition = { 0 }; + CameraSpacePoint iFacePosition = { 0 }; + CameraSpacePoint iFacePositionCenter = { 0 }; + + Vector4 iFaceRotationQuaternion = { 0 }; + // As Yaw, Pitch, Roll + CameraSpacePoint iLastFaceRotation = { 0 }; + CameraSpacePoint iFaceRotation = { 0 }; + CameraSpacePoint iFaceRotationCenter = { 0 }; + // + std::unique_ptr<video_widget> iVideoWidget; + std::unique_ptr<QLayout> iLayout; + + // Id of the body currently being tracked + UINT64 iTrackingId = 0; +}; diff --git a/tracker-kinect-face/lang/nl_NL.ts b/tracker-kinect-face/lang/nl_NL.ts new file mode 100644 index 00000000..a0a737ee --- /dev/null +++ b/tracker-kinect-face/lang/nl_NL.ts @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="nl_NL"> +<context> + <name>KinectFaceMetadata</name> + <message> + <source>Kinect Face 0.1</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>KinectFaceUi</name> + <message> + <source>Kinect Face Tracker</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Start OpenTrack to check if Kinect Face Tracker is working.</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/tracker-kinect-face/lang/ru_RU.ts b/tracker-kinect-face/lang/ru_RU.ts new file mode 100644 index 00000000..72f91367 --- /dev/null +++ b/tracker-kinect-face/lang/ru_RU.ts @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ru_RU"> +<context> + <name>KinectFaceMetadata</name> + <message> + <source>Kinect Face 0.1</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>KinectFaceUi</name> + <message> + <source>Kinect Face Tracker</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Start OpenTrack to check if Kinect Face Tracker is working.</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/tracker-kinect-face/lang/stub.ts b/tracker-kinect-face/lang/stub.ts new file mode 100644 index 00000000..055ec4e1 --- /dev/null +++ b/tracker-kinect-face/lang/stub.ts @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +<context> + <name>KinectFaceMetadata</name> + <message> + <source>Kinect Face 0.1</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>KinectFaceUi</name> + <message> + <source>Kinect Face Tracker</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Start OpenTrack to check if Kinect Face Tracker is working.</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/tracker-kinect-face/lang/zh_CN.ts b/tracker-kinect-face/lang/zh_CN.ts new file mode 100644 index 00000000..055ec4e1 --- /dev/null +++ b/tracker-kinect-face/lang/zh_CN.ts @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +<context> + <name>KinectFaceMetadata</name> + <message> + <source>Kinect Face 0.1</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>KinectFaceUi</name> + <message> + <source>Kinect Face Tracker</source> + <translation type="unfinished"></translation> + </message> + <message> + <source>Start OpenTrack to check if Kinect Face Tracker is working.</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/tracker-pt/CMakeLists.txt b/tracker-pt/CMakeLists.txt index 4010b6c1..f12f530b 100644 --- a/tracker-pt/CMakeLists.txt +++ b/tracker-pt/CMakeLists.txt @@ -2,7 +2,7 @@ find_package(OpenCV QUIET) if(OpenCV_FOUND) otr_module(tracker-pt-base STATIC) target_include_directories(${self} SYSTEM PUBLIC ${OpenCV_INCLUDE_DIRS}) - target_link_libraries(${self} opentrack-cv opencv_core opencv_imgproc opencv_videoio) + target_link_libraries(${self} opencv_imgproc opentrack-cv opencv_core) set_property(TARGET ${self} PROPERTY OUTPUT_NAME "pt-base") endif() add_subdirectory(module) diff --git a/tracker-pt/ftnoir_tracker_pt.cpp b/tracker-pt/ftnoir_tracker_pt.cpp index 0d99ccd0..b717e4a1 100644 --- a/tracker-pt/ftnoir_tracker_pt.cpp +++ b/tracker-pt/ftnoir_tracker_pt.cpp @@ -7,14 +7,12 @@ */ #include "ftnoir_tracker_pt.h" -#include "cv/video-widget.hpp" +#include "video/video-widget.hpp" #include "compat/camera-names.hpp" #include "compat/math-imports.hpp" #include "pt-api.hpp" -#include <cmath> - #include <QHBoxLayout> #include <QDebug> #include <QFile> @@ -49,11 +47,7 @@ Tracker_PT::~Tracker_PT() void Tracker_PT::run() { -#ifdef PT_PERF_LOG - QFile log_file(OPENTRACK_BASE_PATH + "/PointTrackerPerformance.txt"); - if (!log_file.open(QIODevice::WriteOnly | QIODevice::Text)) return; - QTextStream log_stream(&log_file); -#endif + maybe_reopen_camera(); while(!isInterruptionRequested()) { @@ -69,58 +63,50 @@ void Tracker_PT::run() if (new_frame) { - while (center_flag.test_and_set()) - (void)0; - *preview_frame = *frame; point_extractor->extract_points(*frame, *preview_frame, points); point_count = points.size(); - const double fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y); - + const f fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y); const bool success = points.size() >= PointModel::N_POINTS; - if (success) - { - point_tracker.track(points, - PointModel(s), - info, - s.dynamic_pose ? s.init_phase_timeout : 0); - ever_success = true; - } + Affine X_CM; { - Affine X_CM; + QMutexLocker l(¢er_lock); + + if (success) { - QMutexLocker l(&data_mtx); - X_CM = point_tracker.pose(); + point_tracker.track(points, + PointModel(s), + info, + s.dynamic_pose ? s.init_phase_timeout : 0); + ever_success = true; } - // just copy pasted these lines from below - Affine X_MH(mat33::eye(), vec3(s.t_MH_x, s.t_MH_y, s.t_MH_z)); - Affine X_GH = X_CM * X_MH; - vec3 p = X_GH.t; // head (center?) position in global space - - preview_frame->draw_head_center((p[0] * fx) / p[2], (p[1] * fx) / p[2]); + QMutexLocker l2(&data_lock); + X_CM = point_tracker.pose(); } - video_widget->update_image(preview_frame->get_bitmap()); + Affine X_MH(mat33::eye(), vec3(s.t_MH_x, s.t_MH_y, s.t_MH_z)); + Affine X_GH = X_CM * X_MH; + vec3 p = X_GH.t; // head (center?) position in global space + + preview_frame->draw_head_center((p[0] * fx) / p[2], (p[1] * fx) / p[2]); + widget->update_image(preview_frame->get_bitmap()); { int w = -1, h = -1; - video_widget->get_preview_size(w, h); + widget->get_preview_size(w, h); if (w != preview_width || h != preview_height) { - preview_width = w, preview_height = h; + preview_width = w; preview_height = h; preview_frame = traits->make_preview(w, h); } } - - center_flag.clear(); } } - qDebug() << "pt: thread stopped"; } bool Tracker_PT::maybe_reopen_camera() @@ -141,17 +127,14 @@ module_status Tracker_PT::start_tracker(QFrame* video_frame) { //video_frame->setAttribute(Qt::WA_NativeWindow); - video_widget = std::make_unique<cv_video_widget>(video_frame); + widget = std::make_unique<video_widget>(video_frame); layout = std::make_unique<QHBoxLayout>(video_frame); layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(video_widget.get()); + layout->addWidget(widget.get()); video_frame->setLayout(layout.get()); //video_widget->resize(video_frame->width(), video_frame->height()); video_frame->show(); - if (!maybe_reopen_camera()) - return { tr("Can't open camera") }; - start(QThread::HighPriority); return {}; @@ -161,71 +144,70 @@ void Tracker_PT::data(double *data) { if (ever_success) { - Affine X_CM = pose(); + Affine X_CM; + { + QMutexLocker l(&data_lock); + X_CM = point_tracker.pose(); + } Affine X_MH(mat33::eye(), vec3(s.t_MH_x, s.t_MH_y, s.t_MH_z)); - Affine X_GH = X_CM * X_MH; + Affine X_GH(X_CM * X_MH); // translate rotation matrix from opengl (G) to roll-pitch-yaw (E) frame // -z -> x, y -> z, x -> -y mat33 R_EG(0, 0,-1, -1, 0, 0, 0, 1, 0); - mat33 R = R_EG * X_GH.R * R_EG.t(); + mat33 R(R_EG * X_GH.R * R_EG.t()); // get translation(s) const vec3& t = X_GH.t; // extract rotation angles - { - f alpha, beta, gamma; - beta = atan2( -R(2,0), sqrt(R(2,1)*R(2,1) + R(2,2)*R(2,2)) ); - alpha = atan2( R(1,0), R(0,0)); - gamma = atan2( R(2,1), R(2,2)); - - data[Yaw] = rad2deg * alpha; - data[Pitch] = -rad2deg * beta; - data[Roll] = rad2deg * gamma; - } + double alpha, beta, gamma; + beta = atan2( (double)-R(2,0), (double)sqrt(R(2,1)*R(2,1) + R(2,2)*R(2,2)) ); + alpha = atan2( (double)R(1,0), (double)R(0,0) ); + gamma = atan2( (double)R(2,1), (double)R(2,2) ); + + constexpr double rad2deg = 180/M_PI; + + data[Yaw] = rad2deg * alpha; + data[Pitch] = -rad2deg * beta; + data[Roll] = rad2deg * gamma; // convert to cm - data[TX] = t[0] / 10; - data[TY] = t[1] / 10; - data[TZ] = t[2] / 10; + data[TX] = (double)t[0] / 10; + data[TY] = (double)t[1] / 10; + data[TZ] = (double)t[2] / 10; } } bool Tracker_PT::center() { - while (center_flag.test_and_set()) - (void)0; + QMutexLocker l(¢er_lock); point_tracker.reset_state(); - - center_flag.clear(); - return false; } -Affine Tracker_PT::pose() -{ - QMutexLocker l(&data_mtx); - - return point_tracker.pose(); -} - int Tracker_PT::get_n_points() { return int(point_count); } -bool Tracker_PT::get_cam_info(pt_camera_info* info) +bool Tracker_PT::get_cam_info(pt_camera_info& info) { - QMutexLocker lock(&camera_mtx); + QMutexLocker l(&camera_mtx); bool ret; - std::tie(ret, *info) = camera->get_info(); + std::tie(ret, info) = camera->get_info(); return ret; } +Affine Tracker_PT::pose() const +{ + QMutexLocker l(&data_lock); + return point_tracker.pose(); +} + } // ns pt_module diff --git a/tracker-pt/ftnoir_tracker_pt.h b/tracker-pt/ftnoir_tracker_pt.h index 8cf609c3..77835602 100644 --- a/tracker-pt/ftnoir_tracker_pt.h +++ b/tracker-pt/ftnoir_tracker_pt.h @@ -24,11 +24,11 @@ #include <QLayout> class TrackerDialog_PT; -class cv_video_widget; +class video_widget; namespace pt_module { -using namespace types; +using namespace numeric_types; class Tracker_PT : public QThread, public ITracker { @@ -45,9 +45,9 @@ public: void data(double* data) override; bool center() override; - Affine pose(); int get_n_points(); - bool get_cam_info(pt_camera_info* info); + [[nodiscard]] bool get_cam_info(pt_camera_info& info); + Affine pose() const; public slots: bool maybe_reopen_camera(); void set_fov(int value); @@ -57,7 +57,6 @@ private: pointer<pt_runtime_traits> traits; QMutex camera_mtx; - QMutex data_mtx; PointTracker point_tracker; @@ -70,16 +69,13 @@ private: pointer<pt_point_extractor> point_extractor; pointer<pt_camera> camera; - pointer<cv_video_widget> video_widget; + pointer<video_widget> widget; pointer<pt_frame> frame; pointer<pt_preview> preview_frame; std::atomic<unsigned> point_count { 0 }; std::atomic<bool> ever_success { false }; - std::atomic_flag center_flag = ATOMIC_FLAG_INIT; - - static constexpr inline f rad2deg = f(180/M_PI); - //static constexpr float deg2rad = float(M_PI/180); + mutable QMutex center_lock, data_lock; }; } // ns pt_impl diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.cpp b/tracker-pt/ftnoir_tracker_pt_dialog.cpp index 2a156cbc..87f4069f 100644 --- a/tracker-pt/ftnoir_tracker_pt_dialog.cpp +++ b/tracker-pt/ftnoir_tracker_pt_dialog.cpp @@ -123,10 +123,16 @@ QString TrackerDialog_PT::threshold_display_text(int threshold_value) int w = s.cam_res_x, h = s.cam_res_y; if (w * h <= 0) - w = 640, h = 480; + { + w = 640; + h = 480; + } - if (tracker && tracker->get_cam_info(&info) && info.res_x * info.res_y != 0) - w = info.res_x, h = info.res_y; + if (tracker && tracker->get_cam_info(info) && info.res_x * info.res_y != 0) + { + w = info.res_x; + h = info.res_y; + } double value = pt_point_extractor::threshold_radius_value(w, h, threshold_value); @@ -193,7 +199,7 @@ void TrackerDialog_PT::startstop_trans_calib(bool start) void TrackerDialog_PT::poll_tracker_info_impl() { pt_camera_info info; - if (tracker && tracker->get_cam_info(&info)) + if (tracker && tracker->get_cam_info(info)) { ui.caminfo_label->setText(tr("%1x%2 @ %3 FPS").arg(info.res_x).arg(info.res_y).arg(iround(info.fps))); diff --git a/tracker-pt/ftnoir_tracker_pt_dialog.h b/tracker-pt/ftnoir_tracker_pt_dialog.h index f36fe7b2..3bd1a561 100644 --- a/tracker-pt/ftnoir_tracker_pt_dialog.h +++ b/tracker-pt/ftnoir_tracker_pt_dialog.h @@ -12,7 +12,7 @@ #include "ftnoir_tracker_pt.h" #include "tracker-pt/ui_FTNoIR_PT_Controls.h" #include "cv/translation-calibrator.hpp" -#include "cv/video-widget.hpp" +#include "video/video-widget.hpp" #include <QTimer> #include <QMutex> diff --git a/tracker-pt/lang/nl_NL.ts b/tracker-pt/lang/nl_NL.ts index efa66875..c20f12d0 100644 --- a/tracker-pt/lang/nl_NL.ts +++ b/tracker-pt/lang/nl_NL.ts @@ -281,13 +281,6 @@ Don't roll or change position.</source> </message> </context> <context> - <name>pt_module::Tracker_PT</name> - <message> - <source>Can't open camera</source> - <translation type="unfinished"></translation> - </message> -</context> -<context> <name>pt_module::metadata_pt</name> <message> <source>PointTracker 1.1</source> diff --git a/tracker-pt/lang/ru_RU.ts b/tracker-pt/lang/ru_RU.ts index 44ccffc1..494a5a17 100644 --- a/tracker-pt/lang/ru_RU.ts +++ b/tracker-pt/lang/ru_RU.ts @@ -286,13 +286,6 @@ ROLL или X/Y-смещения.</translation> </message> </context> <context> - <name>pt_module::Tracker_PT</name> - <message> - <source>Can't open camera</source> - <translation type="unfinished"></translation> - </message> -</context> -<context> <name>pt_module::metadata_pt</name> <message> <source>PointTracker 1.1</source> diff --git a/tracker-pt/lang/stub.ts b/tracker-pt/lang/stub.ts index f5ebbb49..a1b9819f 100644 --- a/tracker-pt/lang/stub.ts +++ b/tracker-pt/lang/stub.ts @@ -281,13 +281,6 @@ Don't roll or change position.</source> </message> </context> <context> - <name>pt_module::Tracker_PT</name> - <message> - <source>Can't open camera</source> - <translation type="unfinished"></translation> - </message> -</context> -<context> <name>pt_module::metadata_pt</name> <message> <source>PointTracker 1.1</source> diff --git a/tracker-pt/lang/zh_CN.ts b/tracker-pt/lang/zh_CN.ts index c2f1d910..38fd40b1 100644 --- a/tracker-pt/lang/zh_CN.ts +++ b/tracker-pt/lang/zh_CN.ts @@ -281,13 +281,6 @@ Don't roll or change position.</source> </message> </context> <context> - <name>pt_module::Tracker_PT</name> - <message> - <source>Can't open camera</source> - <translation type="unfinished">无法打开摄像头</translation> - </message> -</context> -<context> <name>pt_module::metadata_pt</name> <message> <source>PointTracker 1.1</source> diff --git a/tracker-pt/module/camera.cpp b/tracker-pt/module/camera.cpp index e2edfcb6..1afecc92 100644 --- a/tracker-pt/module/camera.cpp +++ b/tracker-pt/module/camera.cpp @@ -18,7 +18,7 @@ #include <cstdlib> -using namespace pt_module; +namespace pt_module { Camera::Camera(const QString& module_name) : s { module_name } { @@ -56,16 +56,16 @@ Camera::result Camera::get_frame(pt_frame& frame_) { cv::Mat& frame = frame_.as<Frame>()->mat; - const bool new_frame = _get_frame(frame); + const bool new_frame = get_frame_(frame); if (new_frame) { - const double dt = t.elapsed_seconds(); + const f dt = (f)t.elapsed_seconds(); t.start(); // measure fps of valid frames - constexpr double RC = .1; // seconds - const double alpha = dt/(dt + RC); + constexpr f RC = f{1}/10; // seconds + const f alpha = dt/(dt + RC); if (dt_mean < dt_eps) dt_mean = dt; @@ -88,7 +88,7 @@ bool Camera::start(int idx, int fps, int res_x, int res_y) if (idx >= 0 && fps >= 0 && res_x >= 0 && res_y >= 0) { if (cam_desired.idx != idx || - cam_desired.fps != fps || + (int)cam_desired.fps != fps || cam_desired.res_x != res_x || cam_desired.res_y != res_y || !cap || !cap->isOpened() || !cap->grab()) @@ -104,10 +104,12 @@ bool Camera::start(int idx, int fps, int res_x, int res_y) cap = camera_ptr(new cv::VideoCapture(idx)); - if (cam_desired.res_x) + if (cam_desired.res_x > 0 && cam_desired.res_y > 0) + { cap->set(cv::CAP_PROP_FRAME_WIDTH, res_x); - if (cam_desired.res_y) cap->set(cv::CAP_PROP_FRAME_HEIGHT, res_y); + } + if (fps > 0) cap->set(cv::CAP_PROP_FPS, fps); @@ -120,7 +122,7 @@ bool Camera::start(int idx, int fps, int res_x, int res_y) cv::Mat tmp; - if (_get_frame(tmp)) + if (get_frame_(tmp)) { t.start(); return true; @@ -141,13 +143,13 @@ bool Camera::start(int idx, int fps, int res_x, int res_y) void Camera::stop() { cap = nullptr; - desired_name = QString(); - active_name = QString(); - cam_info = pt_camera_info(); - cam_desired = pt_camera_info(); + desired_name = QString{}; + active_name = QString{}; + cam_info = {}; + cam_desired = {}; } -bool Camera::_get_frame(cv::Mat& frame) +bool Camera::get_frame_(cv::Mat& frame) { if (cap && cap->isOpened()) { @@ -171,3 +173,4 @@ void Camera::camera_deleter::operator()(cv::VideoCapture* cap) } } +} // ns pt_module diff --git a/tracker-pt/module/camera.h b/tracker-pt/module/camera.h index f8f140de..2ea633d0 100644 --- a/tracker-pt/module/camera.h +++ b/tracker-pt/module/camera.h @@ -33,13 +33,13 @@ struct Camera final : pt_camera QString get_desired_name() const override; QString get_active_name() const override; - void set_fov(double value) override { fov = value; } + void set_fov(f value) override { fov = value; } void show_camera_settings() override; private: - [[nodiscard]] bool _get_frame(cv::Mat& Frame); + [[nodiscard]] bool get_frame_(cv::Mat& frame); - double dt_mean = 0, fov = 30; + f dt_mean = 0, fov = 30; Timer t; pt_camera_info cam_info; pt_camera_info cam_desired; @@ -56,7 +56,7 @@ private: pt_settings s; - static constexpr inline double dt_eps = 1./256; + static constexpr f dt_eps = f{1}/256; }; } // ns pt_module diff --git a/tracker-pt/module/frame.cpp b/tracker-pt/module/frame.cpp index 6734edf6..c88099f1 100644 --- a/tracker-pt/module/frame.cpp +++ b/tracker-pt/module/frame.cpp @@ -4,7 +4,7 @@ #include <opencv2/imgproc.hpp> -using namespace pt_module; +namespace pt_module { Preview& Preview::operator=(const pt_frame& frame_) { @@ -52,7 +52,7 @@ QImage Preview::get_bitmap() QImage::Format_ARGB32); } -void Preview::draw_head_center(double x, double y) +void Preview::draw_head_center(f x, f y) { auto [px_, py_] = to_pixel_pos(x, y, frame_copy.cols, frame_copy.rows); @@ -76,3 +76,5 @@ void Preview::ensure_size(cv::Mat& frame, int w, int h, int type) if (frame.cols != w || frame.rows != h) frame = cv::Mat(h, w, type); } + +} // ns pt_module diff --git a/tracker-pt/module/frame.hpp b/tracker-pt/module/frame.hpp index 49dde49e..89334599 100644 --- a/tracker-pt/module/frame.hpp +++ b/tracker-pt/module/frame.hpp @@ -5,6 +5,11 @@ #include <opencv2/core.hpp> #include <QImage> +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wweak-vtables" +#endif + namespace pt_module { struct Frame final : pt_frame @@ -21,7 +26,7 @@ struct Preview final : pt_preview Preview& operator=(const pt_frame& frame) override; QImage get_bitmap() override; - void draw_head_center(double x, double y) override; + void draw_head_center(f x, f y) override; operator cv::Mat&() { return frame_copy; } operator cv::Mat const&() const { return frame_copy; } @@ -29,8 +34,11 @@ struct Preview final : pt_preview private: static void ensure_size(cv::Mat& frame, int w, int h, int type); - bool fresh = true; - cv::Mat frame_copy, frame_color, frame_out, frame_out2; + cv::Mat frame_copy, frame_out; }; } // ns pt_module + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif diff --git a/tracker-pt/module/module.cpp b/tracker-pt/module/module.cpp index 4731175a..f665face 100644 --- a/tracker-pt/module/module.cpp +++ b/tracker-pt/module/module.cpp @@ -12,7 +12,11 @@ static const QString module_name = "tracker-pt"; -using namespace pt_module; +#ifdef __clang__ +# pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +namespace pt_module { struct pt_module_traits final : pt_runtime_traits { @@ -54,13 +58,15 @@ struct dialog_pt : TrackerDialog_PT dialog_pt(); }; -// ns pt_module - -using namespace pt_module; - dialog_pt::dialog_pt() : TrackerDialog_PT(module_name) {} QString metadata_pt::name() { return tr("PointTracker 1.1"); } QIcon metadata_pt::icon() { return QIcon(":/Resources/Logo_IR.png"); } +} + +// ns pt_module + +using namespace pt_module; + OPENTRACK_DECLARE_TRACKER(tracker_pt, dialog_pt, metadata_pt) diff --git a/tracker-pt/module/module.hpp b/tracker-pt/module/module.hpp index 3afe8bc9..0b3f12cf 100644 --- a/tracker-pt/module/module.hpp +++ b/tracker-pt/module/module.hpp @@ -13,8 +13,8 @@ class OTR_GENERIC_EXPORT metadata_pt : public Metadata { Q_OBJECT - QString name(); - QIcon icon(); + QString name() override; + QIcon icon() override; }; } // ns pt_module diff --git a/tracker-pt/module/point_extractor.cpp b/tracker-pt/module/point_extractor.cpp index a634768c..596b3d93 100644 --- a/tracker-pt/module/point_extractor.cpp +++ b/tracker-pt/module/point_extractor.cpp @@ -29,8 +29,7 @@ #include <QDebug> -using namespace types; -using namespace pt_module; +using namespace numeric_types; // meanshift code written by Michael Welter @@ -50,10 +49,10 @@ corresponding location is a good candidate for the extracted point. The idea similar to the window scaling suggested in Berglund et al. "Fast, bias-free algorithm for tracking single particles with variable size and shape." (2008). */ -static cv::Vec2d MeanShiftIteration(const cv::Mat &frame_gray, const vec2 ¤t_center, f filter_width) +static vec2 MeanShiftIteration(const cv::Mat1b &frame_gray, const vec2 ¤t_center, f filter_width) { // Most amazingly this function runs faster with doubles than with floats. - const f s = 1.0 / filter_width; + const f s = 1 / filter_width; f m = 0; vec2 com { 0, 0 }; @@ -63,12 +62,12 @@ static cv::Vec2d MeanShiftIteration(const cv::Mat &frame_gray, const vec2 &curre for (int j = 0; j < frame_gray.cols; j++) { f val = frame_ptr[j]; - val = val * val; // taking the square wights brighter parts of the image stronger. + val = val * val; // taking the square weighs brighter parts of the image stronger. { f dx = (j - current_center[0])*s; f dy = (i - current_center[1])*s; - f f = std::fmax(0, 1 - dx*dx - dy*dy); - val *= f; + f max = std::fmax(f{0}, 1 - dx*dx - dy*dy); + val *= max; } m += val; com[0] += j * val; @@ -77,13 +76,15 @@ static cv::Vec2d MeanShiftIteration(const cv::Mat &frame_gray, const vec2 &curre } if (m > f(.1)) { - com *= f(1) / m; + com *= 1 / m; return com; } else return current_center; } +namespace pt_module { + PointExtractor::PointExtractor(const QString& module_name) : s(module_name) { blobs.reserve(max_blobs); @@ -92,7 +93,7 @@ PointExtractor::PointExtractor(const QString& module_name) : s(module_name) void PointExtractor::ensure_channel_buffers(const cv::Mat& orig_frame) { if (ch[0].rows != orig_frame.rows || ch[0].cols != orig_frame.cols) - for (unsigned k = 0; k < 3; k++) + for (unsigned k = 0; k < 3; k++) // NOLINT(modernize-loop-convert) ch[k] = cv::Mat1b(orig_frame.rows, orig_frame.cols); } @@ -108,7 +109,7 @@ void PointExtractor::ensure_buffers(const cv::Mat& frame) } } -void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat& dest) +void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat1b& dest) { ensure_channel_buffers(orig_frame); @@ -119,13 +120,6 @@ void PointExtractor::extract_single_channel(const cv::Mat& orig_frame, int idx, cv::mixChannels(&orig_frame, 1, &dest, 1, from_to, 1); } -void PointExtractor::extract_channels(const cv::Mat& orig_frame, const int* order, int order_npairs) -{ - ensure_channel_buffers(orig_frame); - - cv::mixChannels(&orig_frame, 1, (cv::Mat*) ch, order_npairs, order, order_npairs); -} - void PointExtractor::color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output) { switch (s.blob_color) @@ -147,15 +141,15 @@ void PointExtractor::color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output) } case pt_color_average: { - const int W = frame.cols, H = frame.rows; - const cv::Mat tmp = frame.reshape(1, W * H); - cv::Mat output_ = output.reshape(1, W * H); - cv::reduce(tmp, output_, 1, cv::REDUCE_AVG); + const int W = frame.cols, H = frame.rows, sz = W*H; + cv::reduce(frame.reshape(1, sz), + output.reshape(1, sz), + 1, cv::REDUCE_AVG); break; } default: eval_once(qDebug() << "wrong pt_color_type enum value" << int(s.blob_color)); - /*FALLTHROUGH*/ + [[fallthrough]]; case pt_color_natural: cv::cvtColor(frame, output, cv::COLOR_BGR2GRAY); break; @@ -182,31 +176,80 @@ void PointExtractor::threshold_image(const cv::Mat& frame_gray, cv::Mat1b& outpu cv::noArray(), hist, 1, - (int const*) &hist_size, + &hist_size, &ranges); const f radius = (f) threshold_radius_value(frame_gray.cols, frame_gray.rows, threshold_slider_value); float const* const __restrict ptr = hist.ptr<float>(0); - const unsigned area = uround(3 * M_PI * radius*radius); + const unsigned area = uround(3 * pi * radius*radius); const unsigned sz = unsigned(hist.cols * hist.rows); - unsigned thres = 32; + constexpr unsigned min_thres = 64; + unsigned thres = min_thres; for (unsigned i = sz-1, cnt = 0; i > 32; i--) { - cnt += ptr[i]; + cnt += (unsigned)ptr[i]; if (cnt >= area) break; thres = i; } + if (thres > min_thres) + thres = uround(thres * .8); + cv::threshold(frame_gray, output, thres, 255, cv::THRESH_BINARY); } } +static void draw_blobs(cv::Mat& preview_frame, const blob* blobs, unsigned nblobs, cv::Size size) +{ + for (unsigned k = 0; k < nblobs; k++) + { + const blob& b = blobs[k]; + + if (b.radius < 0) + continue; + + const f dpi = preview_frame.cols / f(320); + const f offx = 10 * dpi, offy = f{7.5} * dpi; + + const f cx = preview_frame.cols / f(size.width), + cy = preview_frame.rows / f(size.height), + c_ = (cx+cy)/2; + + static constexpr unsigned fract_bits = 16; + static constexpr double c_fract(1 << fract_bits); + + cv::Point p(iround(b.pos[0] * cx * c_fract), iround(b.pos[1] * cy * c_fract)); + + auto circle_color = k >= PointModel::N_POINTS + ? cv::Scalar(192, 192, 192) + : cv::Scalar(255, 255, 0); + + const f overlay_size = dpi > 1.5 ? 2 : 1; + + cv::circle(preview_frame, p, iround((b.radius + 3.3) * c_ * c_fract), + circle_color, (int)overlay_size, + cv::LINE_AA, fract_bits); + + char buf[16]; + buf[sizeof(buf)-1] = '\0'; + std::snprintf(buf, sizeof(buf) - 1, "%.2fpx", b.radius); + + auto text_color = k >= PointModel::N_POINTS + ? cv::Scalar(160, 160, 160) + : cv::Scalar(0, 0, 255); + + cv::Point pos(iround(b.pos[0]*cx+offx), iround(b.pos[1]*cy+offy)); + cv::putText(preview_frame, buf, pos, + cv::FONT_HERSHEY_PLAIN, overlay_size, text_color, + 1); + } +} + void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_frame_, std::vector<vec2>& points) { const cv::Mat& frame = frame_.as_const<Frame>()->mat; - cv::Mat& preview_frame = *preview_frame_.as<Preview>(); ensure_buffers(frame); color_to_grayscale(frame, frame_gray_unmasked); @@ -219,8 +262,8 @@ void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_ threshold_image(frame_gray_unmasked, frame_bin); frame_gray_unmasked.copyTo(frame_gray, frame_bin); - const f region_size_min = s.min_point_size; - const f region_size_max = s.max_point_size; + const f region_size_min = (f)s.min_point_size; + const f region_size_max = (f)s.max_point_size; unsigned idx = 0; @@ -265,12 +308,12 @@ void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_ } } - const double radius = std::sqrt(cnt / M_PI); + const double radius = std::sqrt(cnt / pi); if (radius > region_size_max || radius < region_size_min) continue; blobs.emplace_back(radius, - vec2(rect.width/2., rect.height/2.), + vec2(rect.width/f(2), rect.height/f(2)), std::pow(f(norm), f(1.1))/cnt, rect); @@ -280,9 +323,7 @@ void PointExtractor::extract_points(const pt_frame& frame_, pt_preview& preview_ // XXX we could go to the next scanline unless the points are really small. // i'd expect each point being present on at least one unique scanline // but it turns out some people are using 2px points -sh 20180110 -#if BROKEN && 0 - break; -#endif + //break; } } end: @@ -296,7 +337,7 @@ end: for (idx = 0; idx < sz; ++idx) { - blob &b = blobs[idx]; + blob& b = blobs[idx]; cv::Rect rect = b.rect; rect.x -= rect.width / 2; @@ -311,14 +352,14 @@ end: static constexpr f radius_c = f(1.75); const f kernel_radius = b.radius * radius_c; - vec2 pos(rect.width/2., rect.height/2.); // position relative to ROI. + vec2 pos(rect.width/f{2}, rect.height/f{2}); // position relative to ROI. for (int iter = 0; iter < 10; ++iter) { vec2 com_new = MeanShiftIteration(frame_roi, pos, kernel_radius); vec2 delta = com_new - pos; pos = com_new; - if (delta.dot(delta) < 1e-2) + if (delta.dot(delta) < f(1e-3)) break; } @@ -326,43 +367,10 @@ end: b.pos[1] = pos[1] + rect.y; } - for (unsigned k = 0; k < blobs.size(); k++) - { - blob& b = blobs[k]; - - const f dpi = preview_frame.cols / f(320); - const f offx = 10 * dpi, offy = 7.5 * dpi; - - const f cx = preview_frame.cols / f(frame.cols), - cy = preview_frame.rows / f(frame.rows), - c_ = (cx+cy)/2; - - static constexpr unsigned fract_bits = 16; - static constexpr double c_fract(1 << fract_bits); - - cv::Point p(iround(b.pos[0] * cx * c_fract), iround(b.pos[1] * cy * c_fract)); - - auto circle_color = k >= PointModel::N_POINTS - ? cv::Scalar(192, 192, 192) - : cv::Scalar(255, 255, 0); - - const f overlay_size = dpi > 1.5 ? 2 : 1; - - cv::circle(preview_frame, p, iround((b.radius + 3.3) * c_ * c_fract), circle_color, overlay_size, cv::LINE_AA, fract_bits); - - char buf[16]; - buf[sizeof(buf)-1] = '\0'; - std::snprintf(buf, sizeof(buf) - 1, "%.2fpx", b.radius); - - auto text_color = k >= PointModel::N_POINTS - ? cv::Scalar(160, 160, 160) - : cv::Scalar(0, 0, 255); + draw_blobs(preview_frame_.as<Frame>()->mat, + blobs.data(), blobs.size(), + frame_gray.size()); - cv::Point pos(iround(b.pos[0]*cx+offx), iround(b.pos[1]*cy+offy)); - cv::putText(preview_frame, buf, pos, - cv::FONT_HERSHEY_PLAIN, overlay_size, text_color, - 1); - } // End of mean shift code. At this point, blob positions are updated with hopefully less noisy less biased values. points.reserve(max_blobs); @@ -383,3 +391,5 @@ blob::blob(f radius, const vec2& pos, f brightness, const cv::Rect& rect) : { //qDebug() << "radius" << radius << "pos" << pos[0] << pos[1]; } + +} // ns pt_module diff --git a/tracker-pt/module/point_extractor.h b/tracker-pt/module/point_extractor.h index 2288f1a1..a6103667 100644 --- a/tracker-pt/module/point_extractor.h +++ b/tracker-pt/module/point_extractor.h @@ -17,9 +17,9 @@ namespace pt_module { -using namespace types; +using namespace numeric_types; -struct blob +struct blob final { f radius, brightness; vec2 pos; @@ -36,7 +36,7 @@ public: void extract_points(const pt_frame& frame, pt_preview& preview_frame, std::vector<vec2>& points) override; PointExtractor(const QString& module_name); private: - static constexpr inline int max_blobs = 16; + static constexpr int max_blobs = 16; pt_settings s; @@ -48,8 +48,7 @@ private: void ensure_channel_buffers(const cv::Mat& orig_frame); void ensure_buffers(const cv::Mat& frame); - void extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat& dest); - void extract_channels(const cv::Mat& orig_frame, const int* order, int order_npairs); + void extract_single_channel(const cv::Mat& orig_frame, int idx, cv::Mat1b& dest); void color_to_grayscale(const cv::Mat& frame, cv::Mat1b& output); void threshold_image(const cv::Mat& frame_gray, cv::Mat1b& output); diff --git a/tracker-pt/point_tracker.cpp b/tracker-pt/point_tracker.cpp index 63c6f47c..bda412e2 100644 --- a/tracker-pt/point_tracker.cpp +++ b/tracker-pt/point_tracker.cpp @@ -8,14 +8,16 @@ #include "point_tracker.h" #include "compat/math-imports.hpp" -using namespace types; - #include <vector> #include <algorithm> #include <cmath> #include <QDebug> +namespace pt_module { + +using namespace numeric_types; + static void get_row(const mat33& m, int i, vec3& v) { v[0] = m(i,0); @@ -48,6 +50,9 @@ void PointModel::set_model(const pt_settings& s) { switch (s.active_model_panel) { + default: + eval_once(qDebug() << "pt: wrong model type selected"); + [[fallthrough]]; case Clip: M01 = vec3(0, s.clip_ty, -s.clip_tz); M02 = vec3(0, -s.clip_by, -s.clip_bz); @@ -65,18 +70,19 @@ void PointModel::set_model(const pt_settings& s) void PointModel::get_d_order(const vec2* points, unsigned* d_order, const vec2& d) const { + constexpr unsigned cnt = PointModel::N_POINTS; // fit line to orthographically projected points using t = std::pair<f,unsigned>; - t d_vals[3]; + t d_vals[cnt]; // get sort indices with respect to d scalar product - for (unsigned i = 0; i < PointModel::N_POINTS; ++i) + for (unsigned i = 0; i < cnt; ++i) d_vals[i] = t(d.dot(points[i]), i); std::sort(d_vals, - d_vals + 3u, + d_vals + 3, [](const t& a, const t& b) { return a.first < b.first; }); - for (unsigned i = 0; i < PointModel::N_POINTS; ++i) + for (unsigned i = 0; i < cnt; ++i) d_order[i] = d_vals[i].second; } @@ -87,38 +93,34 @@ PointTracker::PointOrder PointTracker::find_correspondences_previous(const vec2* const PointModel& model, const pt_camera_info& info) { - const double fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y); + const f fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y); PointTracker::PointOrder p; p[0] = project(vec3(0,0,0), fx); p[1] = project(model.M01, fx); p[2] = project(model.M02, fx); - const int diagonal = int(std::sqrt(f(info.res_x*info.res_x + info.res_y*info.res_y))); - constexpr int div = 100; - const int max_dist = diagonal / div; // 8 pixels for 640x480 + constexpr unsigned sz = PointModel::N_POINTS; // set correspondences by minimum distance to projected model point - bool point_taken[PointModel::N_POINTS]; - for (unsigned i=0; i<PointModel::N_POINTS; ++i) + bool point_taken[sz]; + for (unsigned i=0; i < sz; ++i) // NOLINT(modernize-loop-convert) point_taken[i] = false; - for (unsigned i=0; i<PointModel::N_POINTS; ++i) + for (unsigned i=0; i < sz; ++i) { f min_sdist = 0; unsigned min_idx = 0; // find closest point to projected model point i - for (unsigned j=0; j<PointModel::N_POINTS; ++j) + for (unsigned j=0; j < sz; ++j) { vec2 d = p[i]-points[j]; f sdist = d.dot(d); - if (sdist < min_sdist || j==0) + if (sdist < min_sdist || j == 0) { min_idx = j; min_sdist = sdist; } } - if (min_sdist > max_dist) - return find_correspondences(points, model); // if one point is closest to more than one model point, fallback if (point_taken[min_idx]) @@ -133,119 +135,61 @@ PointTracker::PointOrder PointTracker::find_correspondences_previous(const vec2* return p; } -bool PointTracker::maybe_use_old_point_order(const PointOrder& order, const pt_camera_info& info) -{ - constexpr f std_width = 640, std_height = 480; - - PointOrder scaled_order; - - const f cx = std_width / info.res_x; - const f cy = std_height / info.res_y; - - for (unsigned k = 0; k < 3; k++) - { - // note, the .y component is actually scaled by width - scaled_order[k][0] = std_width * cx * order[k][0]; - scaled_order[k][1] = std_width * cy * order[k][1]; - } - - f sum = 0; - - for (unsigned k = 0; k < 3; k++) - { - vec2 tmp = prev_scaled_order[k] - scaled_order[k]; - sum += std::sqrt(tmp.dot(tmp)); - } - - // CAVEAT don't increase too much, it visibly loses precision - constexpr f max_dist = f(.13); - - const bool validp = sum < max_dist; - - prev_order_valid &= validp; - - if (!prev_order_valid) - { - prev_order = order; - prev_scaled_order = scaled_order; - } - -#if 0 - { - static Timer tt; - static int cnt1 = 0, cnt2 = 0; - if (tt.elapsed_ms() >= 1000) - { - tt.start(); - if (cnt1 + cnt2) - { - qDebug() << "old-order" << ((cnt1 * 100) / f(cnt1 + cnt2)) << "nsamples" << (cnt1 + cnt2); - cnt1 = 0, cnt2 = 0; - } - } - if (validp) - cnt1++; - else - cnt2++; - } -#endif - - prev_order_valid = validp; - - return validp; -} - void PointTracker::track(const std::vector<vec2>& points, const PointModel& model, const pt_camera_info& info, int init_phase_timeout) { - const double fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y); + const f fx = pt_camera_info::get_focal_length(info.fov, info.res_x, info.res_y); PointOrder order; - if (init_phase_timeout > 0 && t.elapsed_ms() > init_phase_timeout) + if (init_phase_timeout <= 0 || t.elapsed_ms() > init_phase_timeout || init_phase) { - t.start(); init_phase = true; - } - - if (!(init_phase_timeout > 0 && !init_phase)) order = find_correspondences(points.data(), model); + } else order = find_correspondences_previous(points.data(), model, info); - if (maybe_use_old_point_order(order, info) || - POSIT(model, order, fx) != -1) + if (POSIT(model, order, fx) != -1) { init_phase = false; t.start(); } + else + reset_state(); } PointTracker::PointOrder PointTracker::find_correspondences(const vec2* points, const PointModel& model) { - static const Affine a(mat33::eye(), vec3(0, 0, 1)); + constexpr unsigned cnt = PointModel::N_POINTS; // We do a simple freetrack-like sorting in the init phase... - unsigned point_d_order[PointModel::N_POINTS]; - unsigned model_d_order[PointModel::N_POINTS]; - // sort points + unsigned point_d_order[cnt]; + unsigned model_d_order[cnt]; + // calculate d and d_order for simple freetrack-like point correspondence vec2 d(model.M01[0]-model.M02[0], model.M01[1]-model.M02[1]); + // sort points model.get_d_order(points, point_d_order, d); - // calculate d and d_order for simple freetrack-like point correspondence - vec2 pts[3] = { - vec2(0, 0), - vec2(model.M01[0], model.M01[1]), - vec2(model.M02[0], model.M02[1]) + vec2 pts[cnt] { + { 0, 0 }, + { model.M01[0], model.M01[1] }, + { model.M02[0], model.M02[1] }, }; model.get_d_order(pts, model_d_order, d); + // set correspondences PointOrder p; - for (unsigned i = 0; i < PointModel::N_POINTS; ++i) + for (unsigned i = 0; i < cnt; ++i) p[model_d_order[i]] = points[point_d_order[i]]; return p; } +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#endif + int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f focal_length) { // POSIT algorithm for coplanar points as presented in @@ -254,7 +198,7 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca // The expected rotation used for resolving the ambiguity in POSIT: // In every iteration step the rotation closer to R_expected is taken - static const mat33 R_expected(X_CM.R); + const mat33& R_expected{X_CM_expected.R}; // initial pose = last (predicted) pose vec3 k; @@ -263,8 +207,7 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca f old_epsilon_1 = 0; f old_epsilon_2 = 0; - f epsilon_1 = 1; - f epsilon_2 = 1; + f epsilon_1, epsilon_2; vec3 I0, J0; vec2 I0_coeff, J0_coeff; @@ -275,8 +218,8 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca constexpr int max_iter = 100; - int i=1; - for (; i<max_iter; ++i) + int i; + for (i = 1; i < max_iter; ++i) { epsilon_1 = k.dot(model.M01)/Z0; epsilon_2 = k.dot(model.M02)/Z0; @@ -301,14 +244,14 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca // CAVEAT don't change to comparison with an epsilon -sh 20160423 if (JJ0 == II0) { rho = sqrt(fabs(2*IJ0)); - theta = -M_PI/4; + theta = -pi/4; if (IJ0<0) theta *= -1; } else { rho = sqrt(sqrt( (JJ0-II0)*(JJ0-II0) + 4*IJ0*IJ0 )); theta = atan( -2*IJ0 / (JJ0-II0) ); // avoid branch misprediction - theta += (JJ0 - II0 < 0) * M_PI; + theta += (JJ0 - II0 < 0) * pi; theta *= f(.5); } @@ -319,7 +262,7 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca J_1 = J0 + rho*sin(theta)*model.u; J_2 = J0 - rho*sin(theta)*model.u; - f norm_const = 1/cv::norm(I_1); // all have the same norm + f norm_const = (f)(1/cv::norm(I_1)); // all have the same norm // create rotation matrices I_1 *= norm_const; J_1 *= norm_const; @@ -338,8 +281,8 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca // pick the rotation solution closer to the expected one // in simple metric d(A,B) = || I - A * B^T || - f R_1_deviation = cv::norm(mat33::eye() - R_expected * R_1.t()); - f R_2_deviation = cv::norm(mat33::eye() - R_expected * R_2.t()); + f R_1_deviation = (f)(cv::norm(mat33::eye() - R_expected * R_1.t())); + f R_2_deviation = (f)(cv::norm(mat33::eye() - R_expected * R_2.t())); if (R_1_deviation < R_2_deviation) R_current = &R_1; @@ -351,7 +294,7 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca // check for convergence condition const f delta = fabs(epsilon_1 - old_epsilon_1) + fabs(epsilon_2 - old_epsilon_2); - if (!(delta > constants::eps)) + if (delta < eps) break; old_epsilon_1 = epsilon_1; @@ -371,17 +314,17 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca int ret = std::fpclassify(r(i, j)); if (ret == FP_NAN || ret == FP_INFINITE) { - qDebug() << "posit nan -- R"; + qDebug() << "posit nan R"; return -1; } } - for (unsigned i = 0; i < 3; i++) + for (unsigned i = 0; i < 3; i++) // NOLINT(modernize-loop-convert) { int ret = std::fpclassify(t[i]); if (ret == FP_NAN || ret == FP_INFINITE) { - qDebug() << "posit nan -- T"; + qDebug() << "posit nan T"; return -1; } } @@ -392,11 +335,17 @@ int PointTracker::POSIT(const PointModel& model, const PointOrder& order, f foca X_CM.t[1] = t[1]; X_CM.t[2] = t[2]; + X_CM_expected = X_CM; + //qDebug() << "iter:" << i; return i; } +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + vec2 PointTracker::project(const vec3& v_M, f focal_length) { return project(v_M, focal_length, X_CM); @@ -410,7 +359,8 @@ vec2 PointTracker::project(const vec3& v_M, f focal_length, const Affine& X_CM) void PointTracker::reset_state() { - prev_order_valid = false; init_phase = true; + X_CM_expected = {}; } +} // ns pt_module diff --git a/tracker-pt/point_tracker.h b/tracker-pt/point_tracker.h index 095b79d2..6f3e0cee 100644 --- a/tracker-pt/point_tracker.h +++ b/tracker-pt/point_tracker.h @@ -28,11 +28,11 @@ namespace pt_module { // nomenclature as in // [Denis Oberkampf, Daniel F. DeMenthon, Larry S. Davis: "Iterative Pose Estimation Using Coplanar Feature Points"] -using namespace types; +using namespace numeric_types; struct PointModel final { - static constexpr inline unsigned N_POINTS = 3; + static constexpr unsigned N_POINTS = 3; vec3 M01; // M01 in model frame vec3 M02; // M02 in model frame @@ -61,7 +61,7 @@ public: // f : (focal length)/(sensor width) // dt : time since last call void track(const std::vector<vec2>& projected_points, const PointModel& model, const pt_camera_info& info, int init_phase_timeout); - Affine pose() { return X_CM; } + Affine pose() const { return X_CM; } vec2 project(const vec3& v_M, f focal_length); vec2 project(const vec3& v_M, f focal_length, const Affine& X_CM); void reset_state(); @@ -70,17 +70,16 @@ private: // the points in model order using PointOrder = std::array<vec2, 3>; - bool maybe_use_old_point_order(const PointOrder& order, const pt_camera_info& info); - PointOrder find_correspondences(const vec2* projected_points, const PointModel &model); PointOrder find_correspondences_previous(const vec2* points, const PointModel &model, const pt_camera_info& info); // The POSIT algorithm, returns the number of iterations int POSIT(const PointModel& point_model, const PointOrder& order, f focal_length); - Affine X_CM; // transform from model to camera - PointOrder prev_order, prev_scaled_order; + Affine X_CM; // transform from model to camera + Affine X_CM_expected; + PointOrder prev_positions; Timer t; - bool init_phase = true, prev_order_valid = false; + bool init_phase = true; }; } // ns pt_module diff --git a/tracker-pt/pt-api.cpp b/tracker-pt/pt-api.cpp index d137a60a..f64d5c9a 100644 --- a/tracker-pt/pt-api.cpp +++ b/tracker-pt/pt-api.cpp @@ -1,19 +1,19 @@ #include "pt-api.hpp" #include "cv/numeric.hpp" -using namespace types; +using namespace numeric_types; pt_camera_info::pt_camera_info() = default; -double pt_camera_info::get_focal_length(f fov, int res_x, int res_y) +f pt_camera_info::get_focal_length(f fov, int res_x, int res_y) { - const double diag_len = std::sqrt(double(res_x*res_x + res_y*res_y)); - const double aspect_x = res_x / diag_len; + const f diag_len = std::sqrt(f(res_x*res_x + res_y*res_y)); + const f aspect_x = res_x / diag_len; //const double aspect_y = res_y / diag_len; - const double diag_fov = fov * M_PI/180; - const double fov_x = 2*std::atan(std::tan(diag_fov*.5) * aspect_x); + const f diag_fov = fov * pi/180; + const f fov_x = 2*std::atan(std::tan(diag_fov*f{.5}) * aspect_x); //const double fov_y = 2*atan(tan(diag_fov*.5) * aspect_y); - const double fx = .5 / std::tan(fov_x * .5); + const f fx = f{.5} / std::tan(fov_x * f{.5}); return fx; //fy = .5 / tan(fov_y * .5); //static bool once = false; if (!once) { once = true; qDebug() << "f" << ret << "fov" << (fov * 180/M_PI); } @@ -26,27 +26,27 @@ pt_runtime_traits::~pt_runtime_traits() = default; pt_point_extractor::pt_point_extractor() = default; pt_point_extractor::~pt_point_extractor() = default; -double pt_point_extractor::threshold_radius_value(int w, int h, int threshold) +f pt_point_extractor::threshold_radius_value(int w, int h, int threshold) { - double cx = w / 640., cy = h / 480.; + f cx = w / f{640}, cy = h / f{480}; - const double min_radius = 1.75 * cx; - const double max_radius = 15 * cy; + const f min_radius = f{1.75} * cx; + const f max_radius = f{15} * cy; - const double radius = std::fmax(0., (max_radius-min_radius) * threshold / f(255) + min_radius); + const f radius = std::fmax(f{0}, (max_radius-min_radius) * threshold / f(255) + min_radius); return radius; } -std::tuple<double, double> pt_pixel_pos_mixin::to_pixel_pos(double x, double y, int w, int h) +std::tuple<f, f> pt_pixel_pos_mixin::to_pixel_pos(f x, f y, int w, int h) { - return std::make_tuple(w*(x+.5), .5*(h - 2*y*w)); + return std::make_tuple(w*(x+f{.5}), f{.5}*(h - 2*y*w)); } -std::tuple<double, double> pt_pixel_pos_mixin::to_screen_pos(double px, double py, int w, int h) +std::tuple<f, f> pt_pixel_pos_mixin::to_screen_pos(f px, f py, int w, int h) { - px *= w/(w-1.), py *= h/(h-1.); - return std::make_tuple((px - w/2.)/w, -(py - h/2.)/w); + px *= w/(w-f{1}); py *= h/(h-f{1}); + return std::make_tuple((px - w/f{2})/w, -(py - h/f{2})/w); } pt_frame::pt_frame() = default; diff --git a/tracker-pt/pt-api.hpp b/tracker-pt/pt-api.hpp index 12085560..b44cfea2 100644 --- a/tracker-pt/pt-api.hpp +++ b/tracker-pt/pt-api.hpp @@ -13,15 +13,20 @@ #include <QImage> +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wweak-vtables" +#endif + struct pt_camera_info final { - using f = types::f; + using f = numeric_types::f; pt_camera_info(); - static double get_focal_length(f fov, int res_x, int res_y); + static f get_focal_length(f fov, int res_x, int res_y); - double fov = 0; - double fps = 0; + f fov = 0; + f fps = 0; int res_x = 0; int res_y = 0; @@ -30,8 +35,10 @@ struct pt_camera_info final struct pt_pixel_pos_mixin { - static std::tuple<double, double> to_pixel_pos(double x, double y, int w, int h); - static std::tuple<double, double> to_screen_pos(double px, double py, int w, int h); + using f = numeric_types::f; + + static std::tuple<f, f> to_pixel_pos(f x, f y, int w, int h); + static std::tuple<f, f> to_screen_pos(f px, f py, int w, int h); }; struct pt_frame : pt_pixel_pos_mixin @@ -42,16 +49,13 @@ struct pt_frame : pt_pixel_pos_mixin template<typename t> t* as() & { - using u = remove_cvref_t<t>; - static_assert(std::is_convertible_v<u*, pt_frame*>, "must be derived from pt_frame"); - return static_cast<t*>(this); } template<typename t> t const* as_const() const& { - return const_cast<pt_frame*>(this)->as<const t>(); + return static_cast<t const*>(this); } }; @@ -59,12 +63,13 @@ struct pt_preview : pt_frame { virtual pt_preview& operator=(const pt_frame&) = 0; virtual QImage get_bitmap() = 0; - virtual void draw_head_center(double x, double y) = 0; + virtual void draw_head_center(f x, f y) = 0; }; struct pt_camera { using result = std::tuple<bool, pt_camera_info>; + using f = numeric_types::f; pt_camera(); virtual ~pt_camera(); @@ -79,19 +84,20 @@ struct pt_camera virtual QString get_desired_name() const = 0; virtual QString get_active_name() const = 0; - virtual void set_fov(double value) = 0; + virtual void set_fov(f value) = 0; virtual void show_camera_settings() = 0; }; struct pt_point_extractor : pt_pixel_pos_mixin { - using vec2 = types::vec2; + using vec2 = numeric_types::vec2; + using f = numeric_types::f; pt_point_extractor(); virtual ~pt_point_extractor(); virtual void extract_points(const pt_frame& image, pt_preview& preview_frame, std::vector<vec2>& points) = 0; - static double threshold_radius_value(int w, int h, int threshold); + static f threshold_radius_value(int w, int h, int threshold); }; struct pt_runtime_traits @@ -110,3 +116,7 @@ struct pt_runtime_traits template<typename t> using pt_pointer = typename pt_runtime_traits::pointer<t>; + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif diff --git a/tracker-pt/pt-settings.hpp b/tracker-pt/pt-settings.hpp index 088dfe5a..df45b499 100644 --- a/tracker-pt/pt-settings.hpp +++ b/tracker-pt/pt-settings.hpp @@ -19,6 +19,11 @@ namespace pt_settings_detail { using namespace options; +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wweak-vtables" +#endif + struct pt_settings final : options::opts { using slider_value = options::slider_value; @@ -57,9 +62,12 @@ struct pt_settings final : options::opts value<slider_value> threshold_slider { b, "threshold-slider", { 128, 0, 255 } }; explicit pt_settings(const QString& name) : opts(name) {} - ~pt_settings() = default; }; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + } // ns pt_settings_detail using pt_settings = pt_settings_detail::pt_settings; diff --git a/tracker-rift-140/CMakeLists.txt b/tracker-rift-140/CMakeLists.txt index c0235142..6e54412a 100644 --- a/tracker-rift-140/CMakeLists.txt +++ b/tracker-rift-140/CMakeLists.txt @@ -1,3 +1,5 @@ -include(opentrack-rift) -otr_rift(tracker-rift-140 SDK_RIFT_140) -SET(SDK_RIFT_140 "" CACHE PATH "libOVR 1.4.0 path for Oculus Rift") +if(WIN32 OR APPLE) + include(opentrack-rift) + otr_rift(tracker-rift-140 SDK_RIFT_140) + SET(SDK_RIFT_140 "" CACHE PATH "libOVR 1.4.0 path for Oculus Rift") +endif() diff --git a/tracker-rs/CMakeLists.txt b/tracker-rs/CMakeLists.txt index fc8eb75b..8350c551 100644 --- a/tracker-rs/CMakeLists.txt +++ b/tracker-rs/CMakeLists.txt @@ -1,6 +1,6 @@ set(SDK_REALSENSE "$ENV{RSSDK_DIR}" CACHE PATH "Intel RealSense SDK dir") -if(WIN32 AND SDK_REALSENSE) +if(WIN32 AND SDK_REALSENSE AND opentrack-intel) if(CMAKE_COMPILER_IS_GNUCC) add_definitions(-fpermissive -Wno-error) # for SDK headers endif() diff --git a/tracker-rs/ftnoir_tracker_rs.h b/tracker-rs/ftnoir_tracker_rs.h index 881e03ca..f322d0f6 100644 --- a/tracker-rs/ftnoir_tracker_rs.h +++ b/tracker-rs/ftnoir_tracker_rs.h @@ -20,7 +20,7 @@ class RSTracker : public QObject, public ITracker public: RSTracker(); - ~RSTracker(); + ~RSTracker() override; module_status start_tracker(QFrame *) override; void data(double *data) override; @@ -50,6 +50,6 @@ class RSTrackerMetaData : public Metadata { Q_OBJECT - QString name(); - QIcon icon(); + QString name() override; + QIcon icon() override; }; diff --git a/tracker-rs/ftnoir_tracker_rs_worker.cpp b/tracker-rs/ftnoir_tracker_rs_worker.cpp index 0e2c86f4..15843824 100644 --- a/tracker-rs/ftnoir_tracker_rs_worker.cpp +++ b/tracker-rs/ftnoir_tracker_rs_worker.cpp @@ -7,6 +7,7 @@ #include "ftnoir_tracker_rs_worker.h" #include "rs_impl/ftnoir_tracker_rs_impl.h" +#include <cstdlib> #include <QImage> #include <QDebug> diff --git a/tracker-rs/ftnoir_tracker_rs_worker.h b/tracker-rs/ftnoir_tracker_rs_worker.h index 098d6c10..8758e520 100644 --- a/tracker-rs/ftnoir_tracker_rs_worker.h +++ b/tracker-rs/ftnoir_tracker_rs_worker.h @@ -16,7 +16,7 @@ class RSTrackerWorkerThread : public QThread public: RSTrackerWorkerThread(); - ~RSTrackerWorkerThread(); + ~RSTrackerWorkerThread() override; void getPose(double* pose); const QImage getPreview(); diff --git a/tracker-rs/imagewidget.h b/tracker-rs/imagewidget.h index f04eb1a3..a6d5932b 100644 --- a/tracker-rs/imagewidget.h +++ b/tracker-rs/imagewidget.h @@ -14,7 +14,7 @@ class ImageWidget : public QWidget { Q_OBJECT public: - ImageWidget(QWidget* parent = 0); + ImageWidget(QWidget* parent = nullptr); void setImage(QImage image); private: diff --git a/tracker-rs/rs_impl/CMakeLists.txt b/tracker-rs/rs_impl/CMakeLists.txt index d4bf0925..97c6fd99 100644 --- a/tracker-rs/rs_impl/CMakeLists.txt +++ b/tracker-rs/rs_impl/CMakeLists.txt @@ -5,7 +5,7 @@ if(WIN32) target_link_libraries(opentrack-tracker-rs-impl advapi32) # for SDK headers - if(CMAKE_COMPILER_IS_GNUCC) + if(CMAKE_COMPILER_IS_GNUCXX) add_definitions(-fpermissive -Wno-error -w #-Wno-missing-field-initializers -Wno-switch -Wno-sign-compare #-Wno-unknown-pragmas -Wno-attributes diff --git a/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.cpp b/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.cpp index 41406193..3230ef56 100644 --- a/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.cpp +++ b/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.cpp @@ -14,6 +14,8 @@ #include <pxcfacemodule.h> #include <pxcfaceconfiguration.h> +#include <cstdlib> + const size_t kPreviewStreamWidth = 640; const size_t kPreviewStreamHeight = 480; diff --git a/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.h b/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.h index a4c03ce8..c71f30fa 100644 --- a/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.h +++ b/tracker-rs/rs_impl/ftnoir_tracker_rs_impl.h @@ -6,15 +6,8 @@ */ #pragma once -#ifdef EXPORT_RS_IMPL -#define RSTRACKERIMPL_VISIBILITY __declspec( dllexport ) -#else -#define RSTRACKERIMPL_VISIBILITY -#endif -extern "C" { - RSTRACKERIMPL_VISIBILITY int rs_tracker_impl_start(); - RSTRACKERIMPL_VISIBILITY int rs_tracker_impl_update_pose(double *pose); - RSTRACKERIMPL_VISIBILITY int rs_tracker_impl_get_preview(void* data, int width, int height); - RSTRACKERIMPL_VISIBILITY int rs_tracker_impl_end(); -} +int rs_tracker_impl_start(); +int rs_tracker_impl_update_pose(double *pose); +int rs_tracker_impl_get_preview(void* data, int width, int height); +int rs_tracker_impl_end(); diff --git a/tracker-s2bot/ftnoir_tracker_s2bot.cpp b/tracker-s2bot/ftnoir_tracker_s2bot.cpp index 0b8976fc..c1cb7d9b 100644 --- a/tracker-s2bot/ftnoir_tracker_s2bot.cpp +++ b/tracker-s2bot/ftnoir_tracker_s2bot.cpp @@ -28,8 +28,10 @@ static constexpr int add_cbx[] = }; void tracker_s2bot::run() { - if (s.freq == 0) s.freq = 10; - timer.setInterval(1000.0/s.freq); + int freq = s.freq; + if (freq <= 0) + freq = 10; + timer.setInterval((int)(1000./freq)); timer.setSingleShot(false); connect(&timer, &QTimer::timeout, [this] { auto reply = m_nam->get(QNetworkRequest(QUrl("http://localhost:17317/poll"))); diff --git a/tracker-steamvr/CMakeLists.txt b/tracker-steamvr/CMakeLists.txt index 512a49ff..dc487cba 100644 --- a/tracker-steamvr/CMakeLists.txt +++ b/tracker-steamvr/CMakeLists.txt @@ -2,10 +2,13 @@ set(steamvr-dir "") set(steamvr-dll "") set(steamvr-lib "") -if(LINUX AND opentrack-64bit) - set(steamvr-dir "linux64") +if(LINUX) + if (opentrack-64bit) + set(steamvr-dir "linux64") + else() + set(steamvr-dir "linux32") + endif() set(steamvr-dll "libopenvr_api.so") - set(steamvr-lib "${steamvr-dll}") endif() if(WIN32) @@ -18,19 +21,26 @@ if(WIN32) set(steamvr-lib "openvr_api.lib") endif() -if(APPLE AND NOT opentrack-64bit) - set(steamvr-dir "osx32") +if(APPLE) + # expect user compiled it as a non-framework version + if(opentrack-64bit) + set(steamvr-dir "osx64") + else() + set(steamvr-dir "osx32") + endif() set(steamvr-dll "libopenvr_api.dylib") - set(steamvr-lib "${steamvr-dll}") endif() -if(steamvr-dll) +if(steamvr-dll AND opentrack-intel) + if(steamvr-lib STREQUAL "") + set(steamvr-lib "${steamvr-dll}") + endif() SET(SDK_VALVE_STEAMVR "" CACHE PATH "Valve's SteamVR") if(SDK_VALVE_STEAMVR) otr_module(tracker-steamvr) install(FILES "${SDK_VALVE_STEAMVR}/bin/${steamvr-dir}/${steamvr-dll}" DESTINATION "${opentrack-hier-pfx}") - target_include_directories(opentrack-tracker-steamvr SYSTEM PUBLIC "${SDK_VALVE_STEAMVR}/headers") + target_include_directories(opentrack-tracker-steamvr SYSTEM PRIVATE "${SDK_VALVE_STEAMVR}/headers") target_link_libraries(opentrack-tracker-steamvr "${SDK_VALVE_STEAMVR}/lib/${steamvr-dir}/${steamvr-lib}") endif() endif() diff --git a/tracker-steamvr/steamvr.cpp b/tracker-steamvr/steamvr.cpp index 04ae691e..9a845d55 100644 --- a/tracker-steamvr/steamvr.cpp +++ b/tracker-steamvr/steamvr.cpp @@ -20,10 +20,8 @@ #include "api/plugin-api.hpp" -#include <cstdlib> #include <cmath> -#include <type_traits> -#include <algorithm> +#include <cstdlib> #include <QMessageBox> #include <QDebug> @@ -59,13 +57,13 @@ void device_list::fill_device_specs(QList<device_spec>& list) { if (v->GetTrackedDeviceClass(k) == vr::ETrackedDeviceClass::TrackedDeviceClass_Invalid) { - qDebug() << "no device with index"; + qDebug() << "steamvr: no device with index"; continue; } if (!device_states[k].bDeviceIsConnected) { - qDebug() << "device not connected but proceeding"; + qDebug() << "steamvr: device not connected but proceeding"; continue; } @@ -170,14 +168,19 @@ tt device_list::vr_init_() QString device_list::error_string(vr_error_t err) { - const char* str(vr::VR_GetVRInitErrorAsSymbol(err)); - return QString(str ? str : "No description"); -} + const char* str = vr::VR_GetVRInitErrorAsSymbol(err); + const char* desc = vr::VR_GetVRInitErrorAsEnglishDescription(err); -steamvr::steamvr() : device_index(-1) -{ + if (!desc) + desc = "No description"; + + if (str) + return QStringLiteral("%1: %2").arg(str, desc); + else + return { "Unknown error" }; } +steamvr::steamvr() = default; steamvr::~steamvr() = default; module_status steamvr::start_tracker(QFrame*) @@ -200,18 +203,16 @@ module_status steamvr::start_tracker(QFrame*) if (sz == 0) err = tr("No HMD connected"); - device_index = -1; - for (const device_spec& spec : specs) { if (serial == "" || serial == spec.to_string()) { - device_index = int(spec.k); + device_index = spec.k; break; } } - if (device_index == -1 && err.isEmpty()) + if (device_index == UINT_MAX && err.isEmpty()) err = tr("Can't find device with that serial"); if (err.isEmpty()) @@ -223,7 +224,7 @@ module_status steamvr::start_tracker(QFrame*) void steamvr::data(double* data) { - if (device_index != -1) + if (device_index != UINT_MAX) { auto [ok, pose] = device_list::get_pose(device_index); if (ok) @@ -269,15 +270,11 @@ bool steamvr::center() void steamvr::matrix_to_euler(double& yaw, double& pitch, double& roll, const vr::HmdMatrix34_t& result) { - using std::atan2; - using std::sqrt; - using std::asin; - using d = double; - yaw = atan2(d(result.m[2][0]), d(result.m[0][0])); - pitch = atan2(-d(result.m[1][2]), d(result.m[1][1])); - roll = asin(d(result.m[1][0])); + yaw = std::atan2(d(result.m[2][0]), d(result.m[0][0])); + pitch = std::atan2(-d(result.m[1][2]), d(result.m[1][1])); + roll = std::asin(d(result.m[1][0])); } steamvr_dialog::steamvr_dialog() diff --git a/tracker-steamvr/steamvr.hpp b/tracker-steamvr/steamvr.hpp index 8c76ad17..61da2e05 100644 --- a/tracker-steamvr/steamvr.hpp +++ b/tracker-steamvr/steamvr.hpp @@ -1,25 +1,19 @@ #pragma once -#include "api/plugin-api.hpp" + #include "ui_dialog.h" +#include "api/plugin-api.hpp" #include "options/options.hpp" -#include "compat/euler.hpp" - -#include <openvr.h> - -#include <cmath> -#include <memory> #include <tuple> +#include <climits> #include <QString> #include <QMutex> -#include <QMutexLocker> #include <QList> -using namespace options; +#include <openvr.h> -using rmat = euler::rmat; -using euler_t = euler::euler_t; +using namespace options; using vr_error_t = vr::EVRInitError; using vr_t = vr::IVRSystem*; @@ -54,9 +48,9 @@ struct device_list final void refresh_device_list(); const QList<device_spec>& devices() const { return device_specs; } - static cc_noinline maybe_pose get_pose(int k); + static never_inline maybe_pose get_pose(int k); static QString error_string(vr_error_t error); - static constexpr inline unsigned max_devices = vr::k_unMaxTrackedDeviceCount; + static constexpr unsigned max_devices = vr::k_unMaxTrackedDeviceCount; template<typename F> friend auto with_vr_lock(F&& fun) -> decltype(fun(vr_t(), vr_error_t())); @@ -76,7 +70,7 @@ class steamvr : public QObject, public ITracker static void matrix_to_euler(double& yaw, double& pitch, double& roll, const vr::HmdMatrix34_t& result); settings s; - int device_index; + unsigned device_index{UINT_MAX}; public: steamvr(); diff --git a/tracker-udp/ftnoir_tracker_udp.cpp b/tracker-udp/ftnoir_tracker_udp.cpp index b6e5eb86..6badaa0f 100644 --- a/tracker-udp/ftnoir_tracker_udp.cpp +++ b/tracker-udp/ftnoir_tracker_udp.cpp @@ -44,16 +44,20 @@ void udp::run() } while (sock.hasPendingDatagrams()); - if (ok && - progn( - for (unsigned i = 0; i < 6; i++) + if (ok) + { + for (unsigned i = 0; i < 6; i++) + { + int val = std::fpclassify(last_recv_pose2[i]); + if (val == FP_NAN || val == FP_INFINITE) { - int val = std::fpclassify(last_recv_pose2[i]); - if (val == FP_NAN || val == FP_INFINITE) - return false; + ok = false; + break; } - return true; - )) + } + } + + if (ok) { for (unsigned i = 0; i < 6; i++) last_recv_pose[i] = last_recv_pose2[i]; diff --git a/tracker-wii/CMakeLists.txt b/tracker-wii/CMakeLists.txt index e40692ba..f43dca55 100644 --- a/tracker-wii/CMakeLists.txt +++ b/tracker-wii/CMakeLists.txt @@ -3,7 +3,8 @@ if(WIN32) if(OpenCV_FOUND) add_subdirectory(wiiyourself) otr_module(tracker-wii) - target_link_libraries(opentrack-tracker-wii opentrack-tracker-pt-base opentrack-wiiyourself bthprops) - target_include_directories(opentrack-tracker-wii PRIVATE "${CMAKE_SOURCE_DIR}/tracker-pt") + target_link_libraries(${self} opentrack-tracker-pt-base opentrack-wiiyourself bthprops) + target_include_directories(${self} PRIVATE SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/wiiyourself") + target_include_directories(${self} PRIVATE "${CMAKE_SOURCE_DIR}/tracker-pt") endif() endif() diff --git a/tracker-wii/wii_camera.cpp b/tracker-wii/wii_camera.cpp index 38cbf26a..a393b843 100644 --- a/tracker-wii/wii_camera.cpp +++ b/tracker-wii/wii_camera.cpp @@ -7,8 +7,19 @@ */ // silence #pragma deprecated in bluetoothapis.h + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif + #undef _WIN32_WINNT #define _WIN32_WINNT _WIN32_WINNT_VISTA + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + #undef NTDDI_VERSION #define NTDDI_VERSION NTDDI_VISTASP1 @@ -24,14 +35,14 @@ #include <bthsdpdef.h> #include <bluetoothapis.h> -using namespace pt_module; +namespace pt_module { WIICamera::WIICamera(const QString& module_name) : s { module_name } { - cam_info.fps = 70; - cam_info.res_x = 1024; - cam_info.res_y = 768; - cam_info.fov = 42.0f; + cam_info.fps = 70; + cam_info.res_x = 1024; + cam_info.res_y = 768; + cam_info.fov = 42; cam_info.idx = 0; } @@ -42,12 +53,12 @@ WIICamera::~WIICamera() QString WIICamera::get_desired_name() const { - return desired_name; + return {}; } QString WIICamera::get_active_name() const { - return active_name; + return {}; } void WIICamera::show_camera_settings() @@ -67,7 +78,7 @@ WIICamera::result WIICamera::get_frame(pt_frame& frame_) cv::Mat& frame = frame_.as<WIIFrame>()->mat; struct wii_info& wii = frame_.as<WIIFrame>()->wii; - const wii_camera_status new_frame = _get_frame(frame); + const wii_camera_status new_frame = get_frame_(frame); //create a fake blank frame frame = cv::Mat(cam_info.res_x, cam_info.res_y, CV_8UC3, cv::Scalar(0, 0, 0)); wii.status = new_frame; @@ -75,8 +86,8 @@ WIICamera::result WIICamera::get_frame(pt_frame& frame_) switch (new_frame) { case wii_cam_data_change: - _get_status(wii); - _get_points(wii); + get_status(wii); + get_points(wii); break; case wii_cam_data_no_change: return { false, cam_info }; @@ -97,22 +108,16 @@ bool WIICamera::start(int, int, int, int) void WIICamera::stop() { - onExit = true; - m_pDev->ChangedCallback = nullptr; - m_pDev->Disconnect(); - Beep(1000, 200); - if (m_pDev) { - m_pDev=nullptr; - m_pDev = nullptr; - } + m_pDev->ChangedCallback = nullptr; + m_pDev->Disconnect(); + Beep(1000, 200); + m_pDev = nullptr; - desired_name = QString(); - active_name = QString(); - cam_info = pt_camera_info(); - cam_desired = pt_camera_info(); + cam_info = {}; + cam_desired = {}; } -wii_camera_status WIICamera::_pair() +wii_camera_status WIICamera::pair() { #if defined __MINGW32__ // missing prototypes and implib entries @@ -204,14 +209,14 @@ wii_camera_status WIICamera::_pair() #endif } -wii_camera_status WIICamera::_get_frame(cv::Mat& frame) +wii_camera_status WIICamera::get_frame_(cv::Mat& frame) { (void)frame; wii_camera_status ret = wii_cam_wait_for_connect; if (!m_pDev->IsConnected()) { qDebug() << "wii wait"; - ret = _pair(); + ret = pair(); switch (ret) { case wii_cam_wait_for_sync: m_pDev->Disconnect(); @@ -244,7 +249,7 @@ goodbye: return ret; } -bool WIICamera::_get_points(struct wii_info& wii) +bool WIICamera::get_points(struct wii_info& wii) { bool dot_sizes = (m_pDev->IR.Mode == wiimote_state::ir::EXTENDED); bool ret = false; @@ -275,23 +280,21 @@ bool WIICamera::_get_points(struct wii_info& wii) return ret; } -void WIICamera::_get_status(struct wii_info& wii) +void WIICamera::get_status(struct wii_info& wii) { //draw battery status wii.BatteryPercent = m_pDev->BatteryPercent; wii.bBatteryDrained = m_pDev->bBatteryDrained; //draw horizon - static int p = 0; - static int r = 0; if (m_pDev->Nunchuk.Acceleration.Orientation.UpdateAge < 10) { - p = m_pDev->Acceleration.Orientation.Pitch; - r = m_pDev->Acceleration.Orientation.Roll; + horizon.p = m_pDev->Acceleration.Orientation.Pitch; + horizon.r = m_pDev->Acceleration.Orientation.Roll; } - wii.Pitch = p; - wii.Roll = r; + wii.Pitch = horizon.p; + wii.Roll = horizon.r; } void WIICamera::on_state_change(wiimote &remote, @@ -336,3 +339,5 @@ void WIICamera::on_state_change(wiimote &remote, remote.SetReportType(wiimote::IN_BUTTONS_ACCEL_IR); } } + +} // ns pt_module diff --git a/tracker-wii/wii_camera.h b/tracker-wii/wii_camera.h index f4ee5c00..e7d93b83 100644 --- a/tracker-wii/wii_camera.h +++ b/tracker-wii/wii_camera.h @@ -21,7 +21,7 @@ #include <QString> -#include <wiiyourself/wiimote.h> +#include <wiimote.h> #include "wii_frame.hpp" namespace pt_module { @@ -41,33 +41,26 @@ struct WIICamera final : pt_camera QString get_desired_name() const override; QString get_active_name() const override; - void set_fov(double value) override { (void)value; } + void set_fov(f value) override { (void)value; } void show_camera_settings() override; private: - std::unique_ptr<wiimote> m_pDev; - static void on_state_change(wiimote &remote, - state_change_flags changed, - const wiimote_state &new_state); - bool onExit = false; - pt_frame internalframe; - - wii_camera_status _pair(); - wii_camera_status _get_frame(cv::Mat& Frame); - bool _get_points(struct wii_info&); - void _get_status(struct wii_info&); - - double dt_mean = 0; - - Timer t; + static void on_state_change(wiimote &remote, + state_change_flags changed, + const wiimote_state &new_state); + wii_camera_status pair(); + wii_camera_status get_frame_(cv::Mat& Frame); + bool get_points(struct wii_info& wii); + void get_status(struct wii_info& wii); + + std::unique_ptr<wiimote> m_pDev; pt_camera_info cam_info; pt_camera_info cam_desired; - QString desired_name, active_name; - pt_settings s; + struct { float p = 0, r = 0; } horizon; - static constexpr inline double dt_eps = 1./384; + pt_settings s; }; } // ns pt_module diff --git a/tracker-wii/wii_frame.cpp b/tracker-wii/wii_frame.cpp index 432b1f18..4520fde4 100644 --- a/tracker-wii/wii_frame.cpp +++ b/tracker-wii/wii_frame.cpp @@ -15,7 +15,7 @@ #include <opencv2/imgproc.hpp> -using namespace pt_module; +namespace pt_module { WIIPreview& WIIPreview::operator=(const pt_frame& frame_) { @@ -78,7 +78,7 @@ QImage WIIPreview::get_bitmap() QImage::Format_ARGB32); } -void WIIPreview::draw_head_center(double x, double y) +void WIIPreview::draw_head_center(f x, f y) { auto [px_, py_] = to_pixel_pos(x, y, frame_copy.cols, frame_copy.rows); @@ -102,3 +102,5 @@ void WIIPreview::ensure_size(cv::Mat& frame, int w, int h, int type) if (frame.cols != w || frame.rows != h) frame = cv::Mat(h, w, type); } + +} // ns pt_module diff --git a/tracker-wii/wii_frame.hpp b/tracker-wii/wii_frame.hpp index 2107646b..8c4508b2 100644 --- a/tracker-wii/wii_frame.hpp +++ b/tracker-wii/wii_frame.hpp @@ -12,7 +12,10 @@ #include <opencv2/core.hpp> #include <QImage> - +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wweak-vtables" +#endif namespace pt_module { @@ -49,7 +52,7 @@ struct WIIPreview final : pt_preview WIIPreview& operator=(const pt_frame& frame) override; QImage get_bitmap() override; - void draw_head_center(double x, double y) override; + void draw_head_center(f x, f y) override; operator cv::Mat&() { return frame_copy; } operator cv::Mat const&() const { return frame_copy; } @@ -57,9 +60,12 @@ struct WIIPreview final : pt_preview private: static void ensure_size(cv::Mat& frame, int w, int h, int type); - bool fresh = true; - cv::Mat frame_copy, frame_color, frame_resize, frame_out; - wii_camera_status status = wii_cam_wait_for_dongle; + cv::Mat frame_copy, frame_out; + wii_camera_status status = wii_cam_wait_for_dongle; }; } // ns pt_module + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif diff --git a/tracker-wii/wii_module.cpp b/tracker-wii/wii_module.cpp index 9f2a4b04..c4884f22 100644 --- a/tracker-wii/wii_module.cpp +++ b/tracker-wii/wii_module.cpp @@ -20,7 +20,12 @@ static const QString module_name = "tracker-wii-pt"; -using namespace pt_module; +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +namespace pt_module { struct wii_pt_module_traits final : pt_runtime_traits { @@ -63,6 +68,12 @@ struct wii_dialog_pt : TrackerDialog_PT wii_dialog_pt(); }; +} // ns pt_module + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + QString wii_metadata_pt::name() { return tr("WiiPointTracker 1.1"); @@ -73,13 +84,11 @@ QIcon wii_metadata_pt::icon() return QIcon(":/Resources/wii.png"); } -// ns pt_module - using namespace pt_module; wii_dialog_pt::wii_dialog_pt() : TrackerDialog_PT(module_name) { - ui.tabWidget->removeTab(0); + ui.tabWidget->removeTab(0); } OPENTRACK_DECLARE_TRACKER(wii_tracker_pt, wii_dialog_pt, wii_metadata_pt) diff --git a/tracker-wii/wii_module.hpp b/tracker-wii/wii_module.hpp index 34a8561d..b167e150 100644 --- a/tracker-wii/wii_module.hpp +++ b/tracker-wii/wii_module.hpp @@ -2,13 +2,10 @@ #include "api/plugin-api.hpp" -#include <QIcon> -#include <QtGlobal> - -class Q_DECL_EXPORT wii_metadata_pt : public Metadata +class wii_metadata_pt : public Metadata { Q_OBJECT - QString name(); - QIcon icon(); + QString name() override; + QIcon icon() override; }; diff --git a/tracker-wii/wii_point_extractor.cpp b/tracker-wii/wii_point_extractor.cpp index 1be6049b..a16cf233 100644 --- a/tracker-wii/wii_point_extractor.cpp +++ b/tracker-wii/wii_point_extractor.cpp @@ -30,9 +30,9 @@ #include <QDebug> -using namespace types; -using namespace pt_module; +using namespace numeric_types; +namespace pt_module { WIIPointExtractor::WIIPointExtractor(const QString& module_name) : s(module_name) { @@ -40,40 +40,40 @@ WIIPointExtractor::WIIPointExtractor(const QString& module_name) : s(module_name } //define a temp draw function -void WIIPointExtractor::_draw_point(cv::Mat& preview_frame, const vec2& p, const cv::Scalar& color, int thinkness) +void WIIPointExtractor::draw_point(cv::Mat& preview_frame, const vec2& p, const cv::Scalar& color, int thickness) { static constexpr int len = 9; - cv::Point p2(iround(p[0] * preview_frame.cols + preview_frame.cols / 2), - iround(-p[1] * preview_frame.cols + preview_frame.rows / 2)); + cv::Point p2(iround(p[0] * preview_frame.cols + preview_frame.cols / f{2}), + iround(-p[1] * preview_frame.cols + preview_frame.rows / f{2})); cv::line(preview_frame, cv::Point(p2.x - len, p2.y), cv::Point(p2.x + len, p2.y), color, - thinkness); + thickness); cv::line(preview_frame, cv::Point(p2.x, p2.y - len), cv::Point(p2.x, p2.y + len), color, - thinkness); + thickness); } -bool WIIPointExtractor::_draw_points(cv::Mat& preview_frame, const struct wii_info &wii, std::vector<vec2>& points) +bool WIIPointExtractor::draw_points(cv::Mat& preview_frame, const struct wii_info& wii, std::vector<vec2>& points) { - const float W = 1024.0f; - const float H = 768.0f; + constexpr int W = 1024; + constexpr int H = 768; points.reserve(4); points.clear(); - for (unsigned index = 0; index < 4; index++) + for (unsigned index = 0; index < 4; index++) // NOLINT(modernize-loop-convert) { const struct wii_info_points &dot = wii.Points[index]; if (dot.bvis) { //qDebug() << "wii:" << dot.RawX << "+" << dot.RawY; //anti-clockwise rotate the 2D point - const float RX = W - dot.ux; - const float RY = H - dot.uy; + const f RX = W - dot.ux; + const f RY = H - dot.uy; //vec2 dt((dot.RawX - W / 2.0f) / W, -(dot.RawY - H / 2.0f) / W); //vec2 dt((RX - W / 2.0f) / W, -(RY - H / 2.0f) / W); //vec2 dt((2.0f*RX - W) / W, -(2.0f*RY - H ) / W); @@ -81,7 +81,7 @@ bool WIIPointExtractor::_draw_points(cv::Mat& preview_frame, const struct wii_in std::tie(dt[0], dt[1]) = to_screen_pos(RX, RY, W, H); points.push_back(dt); - _draw_point(preview_frame, dt, cv::Scalar(0, 255, 0), dot.isize); + draw_point(preview_frame, dt, cv::Scalar(0, 255, 0), dot.isize); } } const bool success = points.size() >= PointModel::N_POINTS; @@ -89,7 +89,7 @@ bool WIIPointExtractor::_draw_points(cv::Mat& preview_frame, const struct wii_in return success; } -void WIIPointExtractor::_draw_bg(cv::Mat& preview_frame, const struct wii_info &wii) +void WIIPointExtractor::draw_bg(cv::Mat& preview_frame, const struct wii_info& wii) { //draw battery status cv::line(preview_frame, @@ -99,8 +99,8 @@ void WIIPointExtractor::_draw_bg(cv::Mat& preview_frame, const struct wii_info & 2); //draw horizon - int pdelta = iround((preview_frame.rows / 4) * tan((wii.Pitch)* M_PI / 180.0f)); - int rdelta = iround((preview_frame.cols / 4) * tan((wii.Roll)* M_PI / 180.0f)); + int pdelta = iround((preview_frame.rows / f{4}) * tan(((double)wii.Pitch)* pi / f(180))); + int rdelta = iround((preview_frame.cols / f{4}) * tan(((double)wii.Roll)* pi / f(180))); cv::line(preview_frame, cv::Point(0, preview_frame.rows / 2 + rdelta - pdelta), @@ -114,11 +114,15 @@ void WIIPointExtractor::extract_points(const pt_frame& frame_, pt_preview& previ const struct wii_info& wii = frame_.as_const<WIIFrame>()->wii; cv::Mat& preview_frame = *preview_frame_.as<WIIPreview>(); - switch (wii.status) { - case wii_cam_data_change: - _draw_bg(preview_frame, wii); - _draw_points(preview_frame, wii, points); - break; - } + switch (wii.status) + { + case wii_cam_data_change: + draw_bg(preview_frame, wii); + draw_points(preview_frame, wii, points); + break; + default: + break; + } } +} // ns pt_module diff --git a/tracker-wii/wii_point_extractor.h b/tracker-wii/wii_point_extractor.h index be0e5f45..661ce35f 100644 --- a/tracker-wii/wii_point_extractor.h +++ b/tracker-wii/wii_point_extractor.h @@ -16,23 +16,19 @@ namespace pt_module { -using namespace types; +using namespace numeric_types; class WIIPointExtractor final : public pt_point_extractor { public: - // extracts points from frame and draws some processing info into frame, if draw_output is set - // dt: time since last call in seconds void extract_points(const pt_frame& frame, pt_preview& preview_frame, std::vector<vec2>& points) override; WIIPointExtractor(const QString& module_name); -private: - static constexpr int max_blobs = 16; +private: pt_settings s; - void _draw_point(cv::Mat& preview_frame, const vec2& p, const cv::Scalar& color, int thinkness = 1); - bool _draw_points(cv::Mat& preview_frame, const struct wii_info &wii, std::vector<vec2>& points); - void _draw_bg(cv::Mat& preview_frame, const struct wii_info &wii); + void draw_point(cv::Mat& preview_frame, const vec2& p, const cv::Scalar& color, int thickness = 1); + bool draw_points(cv::Mat& preview_frame, const struct wii_info& wii, std::vector<vec2>& points); + void draw_bg(cv::Mat& preview_frame, const struct wii_info& wii); }; } // ns impl - diff --git a/tracker-wii/wiiyourself/CMakeLists.txt b/tracker-wii/wiiyourself/CMakeLists.txt index fe6d62e8..91fa245a 100644 --- a/tracker-wii/wiiyourself/CMakeLists.txt +++ b/tracker-wii/wiiyourself/CMakeLists.txt @@ -1,4 +1,7 @@ if(WIN32) otr_module(wiiyourself STATIC NO-COMPAT NO-QT) target_link_libraries(${self} setupapi hid winmm) + if(CMAKE_COMPILER_IS_CLANGXX OR CMAKE_COMPILER_IS_GNUCXX) + target_compile_options(${self} PRIVATE -Wno-error) + endif() endif() diff --git a/tracker-wii/wiiyourself/warns-begin.hpp b/tracker-wii/wiiyourself/warns-begin.hpp index ca2b6b93..0d0365a9 100644 --- a/tracker-wii/wiiyourself/warns-begin.hpp +++ b/tracker-wii/wiiyourself/warns-begin.hpp @@ -3,7 +3,22 @@ # pragma GCC diagnostic ignored "-Wswitch" # pragma GCC diagnostic ignored "-Wreorder" # pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wunused-but-set-variable" # pragma GCC diagnostic ignored "-Wunused-parameter" -# pragma GCC diagnostic ignored "-Wcast-function-type" +# pragma GCC diagnostic ignored "-Wcast-align" +# ifndef __clang__ +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wcast-function-type" +# endif +#endif + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +# pragma clang diagnostic ignored "-Wextra-semi" +# pragma clang diagnostic ignored "-Wshadow-field" +# pragma clang diagnostic ignored "-Wreserved-id-macro" +# pragma clang diagnostic ignored "-Wconversion" +# pragma clang diagnostic ignored "-Wfloat-equal" +# pragma clang diagnostic ignored "-Wunused-macros" +# pragma clang diagnostic ignored "-Wcast-qual" #endif diff --git a/tracker-wii/wiiyourself/warns-end.hpp b/tracker-wii/wiiyourself/warns-end.hpp index beaf245d..3de03ca5 100644 --- a/tracker-wii/wiiyourself/warns-end.hpp +++ b/tracker-wii/wiiyourself/warns-end.hpp @@ -1,3 +1,7 @@ #ifdef __GNUG__ # pragma GCC diagnostic pop #endif + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif diff --git a/tracker-wii/wiiyourself/wiimote.cpp b/tracker-wii/wiiyourself/wiimote.cpp index 0da0113b..503a0783 100644 --- a/tracker-wii/wiiyourself/wiimote.cpp +++ b/tracker-wii/wiiyourself/wiimote.cpp @@ -10,16 +10,10 @@ #include "warns-begin.hpp" -// VC-specifics: -#ifdef _MSC_VER - // disable warning "C++ exception handler used, but unwind semantics are not enabled." - // in <xstring> (I don't use it - or just enable C++ exceptions) -# pragma warning(disable: 4530) -// auto-link with the necessary libs -//# pragma comment(lib, "setupapi.lib") -//# pragma comment(lib, "hid.lib") // for HID API (from DDK) -//# pragma comment(lib, "winmm.lib") // for timeGetTime() -#endif // _MSC_VER +#include <cmath> +#include <new> +#include <cstring> +#include <cstdio> #include "wiimote.h" #include <setupapi.h> @@ -29,8 +23,6 @@ extern "C" { #include <sys/types.h> // for _stat #include <sys/stat.h> // " -#include <cstring> -#include <cstdio> #include <process.h> // for _beginthreadex() #include <math.h> // for orientation #include <mmreg.h> // for WAVEFORMATEXTENSIBLE @@ -251,8 +243,9 @@ bool wiimote::Connect(unsigned wiimote_index, bool force_hidwrites) // (bizarre way of doing it) create a buffer large enough to hold the // fixed-size detail struct components, and the variable string size - SP_DEVICE_INTERFACE_DETAIL_DATA *didetail = - (SP_DEVICE_INTERFACE_DETAIL_DATA*) new BYTE[req_size]; + using spdidd = SP_DEVICE_INTERFACE_DETAIL_DATA; + constexpr std::align_val_t align { alignof(spdidd) }; + spdidd *didetail = (spdidd*)operator new(req_size, align); _ASSERT(didetail); didetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); @@ -1203,7 +1196,7 @@ bool wiimote::EstimateOrientationFrom(wiimote_state::acceleration &accel) // wiimote seems to be stationary: normalize the current acceleration // (ie. the assumed gravity vector) - float inv_len = 1.f / sqrt(length_sq); + float inv_len = 1.f / std::sqrt(length_sq); float x = accel.X * inv_len; float y = accel.Y * inv_len; float z = accel.Z * inv_len; @@ -1215,9 +1208,9 @@ bool wiimote::EstimateOrientationFrom(wiimote_state::acceleration &accel) // and extract pitch & roll from them: // (may not be optimal) - float pitch = -asin(y) * 57.2957795f; + float pitch = -std::asin(y) * 57.2957795f; // float roll = asin(x) * 57.2957795f; - float roll = atan2(x, z) * 57.2957795f; + float roll = std::atan2(x, z) * 57.2957795f; if (z < 0) { pitch = (y < 0) ? 180 - pitch : -180 - pitch; roll = (x < 0) ? -180 - roll : 180 - roll; @@ -1439,7 +1432,7 @@ int wiimote::ParseExtension(BYTE *buff, unsigned offset) float raw_x = buff[offset + 0]; float raw_y = buff[offset + 1]; - if ((raw_x != joy.RawX) || (raw_y != joy.RawY)) + if (std::fabs(raw_x - joy.RawX) < 1e-6f || std::fabs(raw_y - joy.RawY) < 1e-6f) changed |= NUNCHUK_JOYSTICK_CHANGED; joy.RawX = raw_x; @@ -2376,11 +2369,7 @@ bool wiimote::Load16bitMonoSampleWAV(const TCHAR* filepath, wiimote_sample &out) TRACE(_T("Loading '%s'"), filepath); FILE *file; -#if (_MSC_VER >= 1400) // VC 2005+ _tfopen_s(&file, filepath, _T("rb")); -#else - file = _tfopen(filepath, _T("rb")); -#endif _ASSERT(file); if (!file) { WARN(_T("Couldn't open '%s"), filepath); @@ -2569,11 +2558,7 @@ bool wiimote::Load16BitMonoSampleRAW(const TCHAR* filepath, // load them FILE *file; bool res; -#if (_MSC_VER >= 1400) // VC 2005+ _tfopen_s(&file, filepath, _T("rb")); -#else - file = _tfopen(filepath, _T("rb")); -#endif _ASSERT(file); if (!file) { TRACE(_T("Couldn't open '%s"), filepath); diff --git a/tracker-wii/wiiyourself/wiimote.h b/tracker-wii/wiiyourself/wiimote.h index dac949c7..73212e05 100644 --- a/tracker-wii/wiiyourself/wiimote.h +++ b/tracker-wii/wiiyourself/wiimote.h @@ -10,8 +10,11 @@ #pragma once +#undef NDEBUG + #include "warns-begin.hpp" +#undef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <tchar.h> // auto Unicode/Ansi support @@ -471,7 +474,7 @@ volatile int MotionPlusDetectCount; // waiting for the result volatile DWORD AsyncRumbleTimeout; // orientation estimation unsigned WiimoteNearGUpdates; - unsigned NunchukNearGUpdates; + //unsigned NunchukNearGUpdates; // audio HANDLE SampleThread; const wiimote_sample* volatile CurrentSample; // otherwise playing square wave diff --git a/variant/default/CMakeLists.txt b/variant/default/CMakeLists.txt index a9e3c754..a8829aa5 100644 --- a/variant/default/CMakeLists.txt +++ b/variant/default/CMakeLists.txt @@ -6,4 +6,4 @@ set_target_properties(opentrack-executable PROPERTIES PREFIX "" ) -target_link_libraries(opentrack-executable opentrack-user-interface) +target_link_libraries(${self} opentrack-user-interface opentrack-version) diff --git a/variant/default/_variant.cmake b/variant/default/_variant.cmake index b7ba90b3..161fefda 100644 --- a/variant/default/_variant.cmake +++ b/variant/default/_variant.cmake @@ -22,7 +22,9 @@ function(otr_init_variant) "macosx" "cv" "migration" - "main-window") + "main-window" + "video" + ) set_property(GLOBAL PROPERTY opentrack-subprojects "${subprojects}") endfunction() diff --git a/variant/default/main-window.cpp b/variant/default/main-window.cpp index 0112e2cc..a223c32f 100644 --- a/variant/default/main-window.cpp +++ b/variant/default/main-window.cpp @@ -19,7 +19,6 @@ #include "compat/sysexits.hpp" #include <algorithm> -#include <iterator> #include <utility> #include <QMessageBox> @@ -39,179 +38,138 @@ main_window::main_window() : State(OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH) annoy_if_root(); #endif - update_button_state(false, false); + init_profiles(); + init_buttons(); + init_tray_menu(); + init_dylibs(); + init_shortcuts(); - if (ini_directory().isEmpty()) - { - die_on_config_not_writable(); - return; - } + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | windowFlags()); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + adjustSize(); - if (!refresh_config_list()) + if (!start_in_tray()) { - exit(EX_OSFILE); - return; + setVisible(true); + show(); } + else + setVisible(false); - connect(ui.btnEditCurves, SIGNAL(clicked()), this, SLOT(show_mapping_window())); - connect(ui.btnShortcuts, SIGNAL(clicked()), this, SLOT(show_options_dialog())); - connect(ui.btnShowEngineControls, SIGNAL(clicked()), this, SLOT(show_tracker_settings())); - connect(ui.btnShowServerControls, SIGNAL(clicked()), this, SLOT(show_proto_settings())); - connect(ui.btnShowFilterControls, SIGNAL(clicked()), this, SLOT(show_filter_settings())); - connect(ui.btnStartTracker, SIGNAL(clicked()), this, SLOT(start_tracker_())); - connect(ui.btnStopTracker, SIGNAL(clicked()), this, SLOT(stop_tracker_())); - connect(ui.iconcomboProfile, &QComboBox::currentTextChanged, this, [&](const QString& x) { set_profile(x); }); + connect(&pose_update_timer, &QTimer::timeout, + this, &main_window::show_pose, Qt::DirectConnection); + connect(&det_timer, &QTimer::timeout, + this, &main_window::maybe_start_profile_from_executable); + det_timer.start(1000); +} - // fill dylib comboboxen - { - modules.filters().push_front(std::make_shared<dylib>(QString(), dylib::Filter)); +void main_window::init_shortcuts() +{ + register_shortcuts(); - for (dylib_ptr& x : modules.trackers()) - ui.iconcomboTrackerSource->addItem(x->icon, x->name, x->module_name); + // ctrl+q exits + connect(&kbd_quit, &QShortcut::activated, this, [this]() { main_window::exit(EXIT_SUCCESS); }, Qt::DirectConnection); + kbd_quit.setEnabled(true); +} - for (dylib_ptr& x : modules.protocols()) - ui.iconcomboProtocol->addItem(x->icon, x->name, x->module_name); +void main_window::init_dylibs() +{ + using dylib_ptr = Modules::dylib_ptr; + using dylib_list = Modules::dylib_list; - for (dylib_ptr& x : modules.filters()) - ui.iconcomboFilter->addItem(x->icon, x->name, x->module_name); - } + modules.filters().push_front(std::make_shared<dylib>("", dylib::Filter)); - // timers - connect(&config_list_timer, &QTimer::timeout, this, [this] { refresh_config_list(); }); - connect(&pose_update_timer, &QTimer::timeout, this, &main_window::show_pose, Qt::DirectConnection); - connect(&det_timer, SIGNAL(timeout()), this, SLOT(maybe_start_profile_from_executable())); + for (dylib_ptr& x : modules.trackers()) + ui.iconcomboTrackerSource->addItem(x->icon, x->name, x->module_name); - // ctrl+q exits - connect(&kbd_quit, &QShortcut::activated, this, [this]() { main_window::exit(EXIT_SUCCESS); }, Qt::DirectConnection); + for (dylib_ptr& x : modules.protocols()) + ui.iconcomboProtocol->addItem(x->icon, x->name, x->module_name); - // profile menu - { - profile_menu.addAction(tr("Create new empty config"), this, SLOT(make_empty_config())); - profile_menu.addAction(tr("Create new copied config"), this, SLOT(make_copied_config())); - profile_menu.addAction(tr("Open configuration directory"), this, SLOT(open_config_directory())); - ui.profile_button->setMenu(&profile_menu); - } + for (dylib_ptr& x : modules.filters()) + ui.iconcomboFilter->addItem(x->icon, x->name, x->module_name); - { - const QString cur = ini_filename(); - bool ok = is_config_listed(cur) ? set_profile(cur) : set_profile(OPENTRACK_DEFAULT_CONFIG); - if (!ok) - { - exit(EX_OSFILE); - return; - } - } + connect(ui.iconcomboTrackerSource, &QComboBox::currentTextChanged, + this, [&](const QString&) { pTrackerDialog = nullptr; }); - // only tie and connect main screen options after migrations are done - // below is fine, set_profile() is called already + connect(ui.iconcomboProtocol, &QComboBox::currentTextChanged, + this, [&](const QString&) { pProtocolDialog = nullptr; }); - // dylibs - { - connect(ui.iconcomboTrackerSource, - &QComboBox::currentTextChanged, - this, - [&](const QString&) { pTrackerDialog = nullptr; }); + connect(ui.iconcomboFilter, &QComboBox::currentTextChanged, + this, [&](const QString&) { pFilterDialog = nullptr; }); - connect(ui.iconcomboTrackerSource, - &QComboBox::currentTextChanged, - this, - [&](const QString&) { pProtocolDialog = nullptr; }); + connect(&m.tracker_dll, value_::value_changed<QString>(), + this, &main_window::save_modules, + Qt::DirectConnection); - connect(ui.iconcomboTrackerSource, - &QComboBox::currentTextChanged, - this, - [&](const QString&) { pFilterDialog = nullptr; }); + connect(&m.protocol_dll, value_::value_changed<QString>(), + this, &main_window::save_modules, + Qt::DirectConnection); - connect(&m.tracker_dll, value_::value_changed<QString>(), - this, &main_window::save_modules, - Qt::DirectConnection); + connect(&m.filter_dll, value_::value_changed<QString>(), + this, &main_window::save_modules, + Qt::DirectConnection); - connect(&m.protocol_dll, value_::value_changed<QString>(), - this, &main_window::save_modules, - Qt::DirectConnection); + { + struct list { + dylib_list& libs; + QComboBox* input; + value<QString>& place; + }; - connect(&m.filter_dll, value_::value_changed<QString>(), - this, &main_window::save_modules, - Qt::DirectConnection); + list types[] { + { modules.trackers(), ui.iconcomboTrackerSource, m.tracker_dll }, + { modules.protocols(), ui.iconcomboProtocol, m.protocol_dll }, + { modules.filters(), ui.iconcomboFilter, m.filter_dll }, + }; + for (list& type : types) { - struct list { - dylib_list& libs; - QComboBox* input; - value<QString>& place; - }; - - list types[] { - { modules.trackers(), ui.iconcomboTrackerSource, m.tracker_dll }, - { modules.protocols(), ui.iconcomboProtocol, m.protocol_dll }, - { modules.filters(), ui.iconcomboFilter, m.filter_dll }, - }; - - for (list& type : types) - { - list& t = type; - tie_setting(t.place, t.input, - [t](const QString& name) { - auto [ptr, idx] = module_by_name(name, t.libs); - return idx; - }, - [t](int, const QVariant& userdata) { - auto [ptr, idx] = module_by_name(userdata.toString(), t.libs); - if (ptr) - return ptr->module_name; - else - return QString(); - }); - } + list t = type; + tie_setting(t.place, t.input, + [t](const QString& name) { + auto [ptr, idx] = module_by_name(name, t.libs); + return idx; + }, + [t](int, const QVariant& userdata) { + auto [ptr, idx] = module_by_name(userdata.toString(), t.libs); + if (ptr) + return ptr->module_name; + else + return QString(); + }); } } +} - connect(this, &main_window::start_tracker, - this, [&] { qDebug() << "start tracker"; start_tracker_(); }, - Qt::QueuedConnection); - - connect(this, &main_window::stop_tracker, - this, [&] { qDebug() << "stop tracker"; stop_tracker_(); }, - Qt::QueuedConnection); - - connect(this, &main_window::toggle_tracker, - this, [&] { qDebug() << "toggle tracker"; if (work) stop_tracker_(); else start_tracker_(); }, - Qt::QueuedConnection); - - connect(this, &main_window::restart_tracker, - this, [&] { qDebug() << "restart tracker"; stop_tracker_(); start_tracker_(); }, - Qt::QueuedConnection); +void main_window::init_profiles() +{ + refresh_profile_list(); + // implicitly created by `ini_directory()' + if (ini_directory().isEmpty() || !QDir(ini_directory()).isReadable()) + die_on_profile_not_writable(); - init_tray(); - ensure_tray(); + set_profile(ini_filename()); - register_shortcuts(); - det_timer.start(1000); - config_list_timer.start(1000 * 5); - kbd_quit.setEnabled(true); + // profile menu + profile_menu.addAction(tr("Create new empty config"), this, &main_window::create_empty_profile); + profile_menu.addAction(tr("Create new copied config"), this, &main_window::create_copied_profile); + profile_menu.addAction(tr("Open configuration directory"), this, &main_window::open_profile_directory); + ui.profile_button->setMenu(&profile_menu); - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | windowFlags()); - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - adjustSize(); + connect(&profile_list_timer, &QTimer::timeout, this, &main_window::refresh_profile_list); + profile_list_timer.start(1000 * 5); - if (!start_in_tray()) - { - setVisible(true); - show(); - } - else - setVisible(false); + connect(ui.iconcomboProfile, &QComboBox::currentTextChanged, + this, [this](const QString& x) { main_window::set_profile(x); }); } -void main_window::init_tray() +void main_window::init_tray_menu() { tray_menu.clear(); QString display_name(opentrack_version); if (display_name.startsWith("opentrack-")) - { display_name = tr("opentrack") + " " + display_name.mid(sizeof("opentrack-") - 1); - } if (display_name.endsWith("-DEBUG")) display_name.replace(display_name.size() - int(sizeof("DEBUG")), display_name.size(), tr(" (debug)")); @@ -261,10 +219,22 @@ void main_window::init_tray() QObject::connect(&menu_action_exit, &QAction::triggered, this, &main_window::exit); tray_menu.addAction(&menu_action_exit); - connect(&s.tray_enabled, - static_cast<void (value_::*)(bool) const>(&value_::valueChanged), - this, - &main_window::ensure_tray); + connect(&s.tray_enabled, value_::value_changed<bool>(), + this, &main_window::ensure_tray); + + ensure_tray(); +} + +void main_window::init_buttons() +{ + update_button_state(false, false); + connect(ui.btnEditCurves, &QPushButton::clicked, this, &main_window::show_mapping_window); + connect(ui.btnShortcuts, &QPushButton::clicked, this, &main_window::show_options_dialog); + connect(ui.btnShowEngineControls, &QPushButton::clicked, this, &main_window::show_tracker_settings); + connect(ui.btnShowServerControls, &QPushButton::clicked, this, &main_window::show_proto_settings); + connect(ui.btnShowFilterControls, &QPushButton::clicked, this, &main_window::show_filter_settings); + connect(ui.btnStartTracker, &QPushButton::clicked, this, &main_window::start_tracker_); + connect(ui.btnStopTracker, &QPushButton::clicked, this, &main_window::stop_tracker_); } void main_window::register_shortcuts() @@ -287,7 +257,7 @@ void main_window::register_shortcuts() work->reload_shortcuts(); } -void main_window::die_on_config_not_writable() +void main_window::die_on_profile_not_writable() { stop_tracker_(); @@ -301,32 +271,7 @@ void main_window::die_on_config_not_writable() exit(EX_OSFILE); } -bool main_window::maybe_die_on_config_not_writable(const QString& current, QStringList* ini_list_) -{ - const bool writable = - with_settings_object([&](QSettings& s) { - return s.isWritable(); - }); - - if (writable) - return false; - - const bool open = QFile(ini_combine(current)).open(QFile::ReadWrite); - QStringList list = ini_list(); - - if (!list.contains(current) || !open) - { - die_on_config_not_writable(); - return true; - } - - if (ini_list_ != nullptr) - *ini_list_ = std::move(list); - - return false; -} - -bool main_window::get_new_config_name_from_dialog(QString& ret) +bool main_window::profile_name_from_dialog(QString& ret) { new_file_dialog dlg; dlg.exec(); @@ -336,113 +281,93 @@ bool main_window::get_new_config_name_from_dialog(QString& ret) main_window::~main_window() { // stupid ps3 eye has LED issues - if (work) + if (work && ui.video_frame->layout()) { + hide(); stop_tracker_(); - QEventLoop ev; - ev.processEvents(); - portable::sleep(2000); + close(); + + constexpr int inc = 25, max = 1000; + + for (int k = 0; k < max; k += inc) + { + QEventLoop ev; + ev.processEvents(); + portable::sleep(inc); + } } exit(); } -void main_window::set_working_directory() -{ - QDir::setCurrent(OPENTRACK_BASE_PATH); -} - void main_window::save_modules() { m.b->save(); } -void main_window::make_empty_config() +void main_window::create_empty_profile() { QString name; - if (get_new_config_name_from_dialog(name)) + if (profile_name_from_dialog(name)) { QFile(ini_combine(name)).open(QFile::ReadWrite); + refresh_profile_list(); - if (!refresh_config_list()) - return; - - if (is_config_listed(name)) + if (profile_list.contains(name)) { QSignalBlocker q(ui.iconcomboProfile); - if (!set_profile(name)) - return; - mark_config_as_not_needing_migration(); + set_profile(name, false); + mark_profile_as_not_needing_migration(); } } } -void main_window::make_copied_config() +void main_window::create_copied_profile() { const QString cur = ini_pathname(); QString name; - if (cur != "" && get_new_config_name_from_dialog(name)) + if (!cur.isEmpty() && profile_name_from_dialog(name)) { const QString new_name = ini_combine(name); (void) QFile::remove(new_name); QFile::copy(cur, new_name); - if (!refresh_config_list()) - return; + refresh_profile_list(); - if (is_config_listed(name)) + if (profile_list.contains(name)) { QSignalBlocker q(ui.iconcomboProfile); - if (!set_profile(name)) - return; - mark_config_as_not_needing_migration(); + set_profile(name, false); + mark_profile_as_not_needing_migration(); } } } -void main_window::open_config_directory() +void main_window::open_profile_directory() { QDesktopServices::openUrl("file:///" + QDir::toNativeSeparators(ini_directory())); } -bool main_window::refresh_config_list() +void main_window::refresh_profile_list() { if (work) - return true; + return; QStringList list = ini_list(); - - // check for sameness - const bool exact_same = !list.empty() && progn( - if (list.size() == ui.iconcomboProfile->count()) - { - const int sz = list.size(); - for (int i = 0; i < sz; i++) - { - if (list[i] != ui.iconcomboProfile->itemText(i)) - return false; - } - return true; - } - - return false; - ); - QString current = ini_filename(); - if (!list.contains(current)) - current = OPENTRACK_DEFAULT_CONFIG; + if (list == profile_list) + return; - if (maybe_die_on_config_not_writable(current, &list)) - return false; + if (!list.contains(current)) + current = OPENTRACK_DEFAULT_PROFILE; - if (exact_same) - return true; + profile_list = list; - const QIcon icon(":/images/settings16.png"); + static const QIcon icon(":/images/settings16.png"); QSignalBlocker l(ui.iconcomboProfile); @@ -453,42 +378,9 @@ bool main_window::refresh_config_list() ui.iconcomboProfile->setItemIcon(i, icon); ui.iconcomboProfile->setCurrentText(current); - - return true; } -std::tuple<main_window::dylib_ptr, int> main_window::module_by_name(const QString& name, Modules::dylib_list& list) -{ - auto it = std::find_if(list.cbegin(), list.cend(), [&name](const dylib_ptr& lib) { - if (!lib) - return name.isEmpty(); - else - return name == lib->module_name; - }); - if (it == list.cend()) - return { nullptr, -1 }; - else - return { *it, int(std::distance(list.cbegin(), it)) }; -} - -main_window::dylib_ptr main_window::current_tracker() -{ - auto [ptr, idx] = module_by_name(m.tracker_dll, modules.trackers()); - return ptr; -} - -main_window::dylib_ptr main_window::current_protocol() -{ - auto [ptr, idx] = module_by_name(m.protocol_dll, modules.protocols()); - return ptr; -} - -main_window::dylib_ptr main_window::current_filter() -{ - auto [ptr, idx] = module_by_name(m.filter_dll, modules.filters()); - return ptr; -} void main_window::update_button_state(bool running, bool inertialp) { @@ -515,11 +407,6 @@ void main_window::start_tracker_() if (work) return; - { - double p[6] = {0,0,0, 0,0,0}; - display_pose(p, p); - } - work = std::make_shared<Work>(pose, ev, ui.video_frame, current_tracker(), current_protocol(), current_filter()); if (!work->is_ok()) @@ -528,6 +415,11 @@ void main_window::start_tracker_() return; } + { + double p[6] = {0,0,0, 0,0,0}; + show_pose_(p, p); + } + if (pTrackerDialog) pTrackerDialog->register_tracker(work->libs.pTracker.get()); @@ -552,6 +444,7 @@ void main_window::stop_tracker_() if (!work) return; + force_is_visible(true); with_tracker_teardown sentinel; pose_update_timer.stop(); @@ -569,8 +462,8 @@ void main_window::stop_tracker_() work = nullptr; { - double p[6] = {0,0,0, 0,0,0}; - display_pose(p, p); + double p[6] {}; + show_pose_(p, p); } update_button_state(false, false); @@ -578,7 +471,7 @@ void main_window::stop_tracker_() ui.btnStartTracker->setFocus(); } -void main_window::display_pose(const double *mapped, const double *raw) +void main_window::show_pose_(const double* mapped, const double* raw) { ui.pose_display->rotate_async(mapped[Yaw], mapped[Pitch], -mapped[Roll], mapped[TX], mapped[TY], mapped[TZ]); @@ -634,32 +527,29 @@ void main_window::show_pose() work->pipeline_.raw_and_mapped_pose(mapped, raw); - display_pose(mapped, raw); -} - -template<typename t, typename... Args> -bool mk_window(std::unique_ptr<t>& place, Args&&... params) -{ - return mk_window_common(place, [&] { - return std::make_unique<t>(params...); - }); + show_pose_(mapped, raw); } -template<typename t> -bool mk_dialog(std::unique_ptr<t>& place, const std::shared_ptr<dylib>& lib) +static void show_window(QWidget& d, bool fresh) { - using u = std::unique_ptr<t>; + if (fresh) + { + d.setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | d.windowFlags()); + d.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - return mk_window_common(place, [&] { - if (lib && lib->Dialog) - return u{ (t*)lib->Dialog() }; - else - return u{}; - }); + d.show(); + d.adjustSize(); + d.raise(); + } + else + { + d.show(); + d.raise(); + } } template<typename t, typename F> -bool mk_window_common(std::unique_ptr<t>& d, F&& fun) +static bool mk_window_common(std::unique_ptr<t>& d, F&& fun) { bool fresh = false; @@ -672,22 +562,25 @@ bool mk_window_common(std::unique_ptr<t>& d, F&& fun) return fresh; } -void show_window(QWidget& d, bool fresh) +template<typename t, typename... Args> +static bool mk_window(std::unique_ptr<t>& place, Args&&... params) { - if (fresh) - { - d.setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | d.windowFlags()); - d.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + return mk_window_common(place, [&] { + return std::make_unique<t>(params...); + }); +} - d.show(); - d.adjustSize(); - d.raise(); - } - else - { - d.show(); - d.raise(); - } +template<typename t> +static bool mk_dialog(std::unique_ptr<t>& place, const std::shared_ptr<dylib>& lib) +{ + using u = std::unique_ptr<t>; + + return mk_window_common(place, [&] { + if (lib && lib->Dialog) + return u{ (t*)lib->Dialog() }; + else + return u{}; + }); } void main_window::show_tracker_settings() @@ -725,7 +618,7 @@ void main_window::show_options_dialog() // also doesn't work when switching profiles with options dialog open // move shortcuts to a separate bundle and add a migration -sh 20180218 connect(options_widget.get(), &options_dialog::closing, - this, &main_window::register_shortcuts); + this, &main_window::register_shortcuts); } } @@ -755,36 +648,41 @@ void main_window::exit(int status) QApplication::exit(status); } -bool main_window::set_profile(const QString& new_name_) +void main_window::set_profile(const QString& new_name_, bool migrate) { - if (!refresh_config_list()) - return false; + QSignalBlocker b(ui.iconcomboProfile); QString new_name = new_name_; - if (!is_config_listed(new_name)) - new_name = OPENTRACK_DEFAULT_CONFIG; + if (!profile_list.contains(new_name)) + { + new_name = OPENTRACK_DEFAULT_PROFILE; + if (!profile_list.contains(new_name)) + migrate = false; + } - if (maybe_die_on_config_not_writable(new_name, nullptr)) - return false; + const bool status = new_name != ini_filename(); - ui.iconcomboProfile->setCurrentText(new_name); - set_profile_in_registry(new_name); + if (status) + set_profile_in_registry(new_name); - // XXX workaround migration breakage -sh 20180428 - QSignalBlocker b1(ui.iconcomboTrackerSource); - QSignalBlocker b2(ui.iconcomboProtocol); - QSignalBlocker b3(ui.iconcomboFilter); + using bundler = options::detail::bundler; - options::detail::bundler::refresh_all_bundles(); + bundler::reload_no_notify(); - // migrations are for config layout changes and other user-visible - // incompatibilities in future versions - run_migrations(); + if (migrate) + // migrations are for config layout changes and other user-visible + // incompatibilities in future versions + run_migrations(); + else + mark_profile_as_not_needing_migration(); + + bundler::notify(); set_title(); - return true; + if (status) + ui.iconcomboProfile->setCurrentText(new_name); } void main_window::ensure_tray() @@ -814,9 +712,7 @@ void main_window::ensure_tray() } else { - const bool is_hidden = isHidden() || !isVisible(); - - if (is_hidden) + if (!isVisible()) { show(); setVisible(true); @@ -848,19 +744,14 @@ void main_window::toggle_restore_from_tray(QSystemTrayIcon::ActivationReason e) ensure_tray(); - const bool is_minimized = isHidden() || !is_tray_enabled(); + const bool is_minimized = isHidden() || !tray_enabled(); menu_action_show.setText(!isHidden() ? tr("Show the Octopus") : tr("Hide the Octopus")); setVisible(is_minimized); setHidden(!is_minimized); - setWindowState(typed_progn(Qt::WindowStates, - if (is_minimized) - return windowState() & ~Qt::WindowMinimized; - else - return Qt::WindowNoState; - )); + setWindowState(is_minimized ? windowState() & ~Qt::WindowMinimized : Qt::WindowNoState); if (is_minimized) { @@ -878,7 +769,7 @@ bool main_window::maybe_hide_to_tray(QEvent* e) { if (e->type() == QEvent::WindowStateChange && (windowState() & Qt::WindowMinimized) && - is_tray_enabled()) + tray_enabled()) { e->accept(); ensure_tray(); @@ -899,10 +790,10 @@ void main_window::maybe_start_profile_from_executable() { if (!work) { - QString prof; - if (det.config_to_start(prof)) + QString profile; + if (det.profile_to_start(profile) && profile_list.contains(profile)) { - set_profile(prof); + set_profile(profile); start_tracker_(); } } @@ -922,18 +813,7 @@ void main_window::set_keys_enabled(bool flag) global_shortcuts.reload({}); } else - { register_shortcuts(); - } -} - -bool main_window::is_config_listed(const QString& name) -{ - const int sz = ui.iconcomboProfile->count(); - for (int i = 0; i < sz; i++) - if (ui.iconcomboProfile->itemText(i) == name) - return true; - return false; } void main_window::changeEvent(QEvent* e) @@ -964,23 +844,41 @@ bool main_window::event(QEvent* event) return QMainWindow::event(event); } -bool main_window::is_tray_enabled() +bool main_window::tray_enabled() { return s.tray_enabled && QSystemTrayIcon::isSystemTrayAvailable(); } bool main_window::start_in_tray() { - return s.tray_enabled && s.tray_start && QSystemTrayIcon::isSystemTrayAvailable(); + return tray_enabled() && s.tray_start; } void main_window::set_profile_in_registry(const QString &profile) { with_global_settings_object([&](QSettings& s) { - s.setValue(OPENTRACK_CONFIG_FILENAME_KEY, profile); + s.setValue(OPENTRACK_PROFILE_FILENAME_KEY, profile); }); } +void main_window::restart_tracker_() +{ + qDebug() << "restart tracker"; + + stop_tracker_(); + start_tracker_(); +} + +void main_window::toggle_tracker_() +{ + qDebug() << "toggle tracker"; + + if (work) + stop_tracker_(); + else + start_tracker_(); +} + #if !defined _WIN32 # include <unistd.h> void main_window::annoy_if_root() diff --git a/variant/default/main-window.hpp b/variant/default/main-window.hpp index 55fca177..9ffb7019 100644 --- a/variant/default/main-window.hpp +++ b/variant/default/main-window.hpp @@ -18,6 +18,7 @@ #include "logic/work.hpp" #include "logic/state.hpp" #include "options/options.hpp" +#include "compat/qt-signal.hpp" #include <QApplication> #include <QMainWindow> @@ -31,6 +32,7 @@ #include <QAction> #include <QEvent> #include <QCloseEvent> +#include <QList> #include <tuple> #include <memory> @@ -39,20 +41,23 @@ class main_window final : public QMainWindow, private State { - Q_OBJECT + Q_DECLARE_TR_FUNCTIONS(main_window) Ui::main_window ui; - Shortcuts global_shortcuts; - module_settings m; std::unique_ptr<QSystemTrayIcon> tray; - QMenu tray_menu; + QMenu tray_menu { this }; + QTimer pose_update_timer { this }; QTimer det_timer; - QTimer config_list_timer; + QTimer profile_list_timer; + + Shortcuts global_shortcuts; + QShortcut kbd_quit { QKeySequence("Ctrl+Q"), this }; + std::unique_ptr<options_dialog> options_widget; std::unique_ptr<mapping_dialog> mapping_widget; - QShortcut kbd_quit { QKeySequence("Ctrl+Q"), this }; + std::unique_ptr<IFilterDialog> pFilterDialog; std::unique_ptr<IProtocolDialog> pProtocolDialog; std::unique_ptr<ITrackerDialog> pTrackerDialog; @@ -60,6 +65,8 @@ class main_window final : public QMainWindow, private State process_detector_worker det; QMenu profile_menu; + QList<QString> profile_list; + QAction menu_action_header { &tray_menu }, menu_action_show { &tray_menu }, menu_action_exit { &tray_menu }, @@ -71,72 +78,66 @@ class main_window final : public QMainWindow, private State bool exiting_already { false }; - using dylib_ptr = Modules::dylib_ptr; - using dylib_list = Modules::dylib_list; - - dylib_ptr current_tracker(); - dylib_ptr current_protocol(); - dylib_ptr current_filter(); + qt_sig::nullary start_tracker { this, &main_window::start_tracker_, Qt::QueuedConnection }; + qt_sig::nullary stop_tracker { this, &main_window::stop_tracker_, Qt::QueuedConnection }; + qt_sig::nullary toggle_tracker { this, &main_window::toggle_tracker_, Qt::QueuedConnection }; + qt_sig::nullary restart_tracker { this, &main_window::restart_tracker_, Qt::QueuedConnection }; - static std::tuple<dylib_ptr, int> module_by_name(const QString& name, Modules::dylib_list& list); + void init_dylibs(); + void init_tray_menu(); + void init_profiles(); + void init_buttons(); - void update_button_state(bool running, bool inertialp); - void display_pose(const double* mapped, const double* raw); - void set_title(const QString& game_title = QString()); - static bool get_new_config_name_from_dialog(QString &ret); - void set_profile_in_registry(const QString& profile); + void init_shortcuts(); void register_shortcuts(); void set_keys_enabled(bool flag); - bool is_config_listed(const QString& name); - void init_tray(); + void update_button_state(bool running, bool inertialp); - void changeEvent(QEvent* e) override; - bool event(QEvent *event) override; - bool maybe_hide_to_tray(QEvent* e); #if !defined _WIN32 void annoy_if_root(); #endif + void changeEvent(QEvent* e) override; + bool maybe_hide_to_tray(QEvent* e); void closeEvent(QCloseEvent *event) override; - - bool maybe_die_on_config_not_writable(const QString& current, QStringList* ini_list); - void die_on_config_not_writable(); - bool is_tray_enabled(); - bool start_in_tray(); - -private slots: - void save_modules(); - void exit(int status = EXIT_SUCCESS); - bool set_profile(const QString& new_name); + bool event(QEvent *event) override; void show_tracker_settings(); void show_proto_settings(); void show_filter_settings(); + void show_options_dialog(); void show_mapping_window(); - void show_pose(); - void maybe_start_profile_from_executable(); - - void make_empty_config(); - void make_copied_config(); - void open_config_directory(); - bool refresh_config_list(); + void show_pose(); + void show_pose_(const double* mapped, const double* raw); + void set_title(const QString& game_title = QString()); void start_tracker_(); void stop_tracker_(); + void restart_tracker_(); + void toggle_tracker_(); - void ensure_tray(); + void set_profile(const QString& new_name, bool migrate = true); + void set_profile_in_registry(const QString& profile); + void refresh_profile_list(); + void die_on_profile_not_writable(); + void maybe_start_profile_from_executable(); + [[nodiscard]] static bool profile_name_from_dialog(QString& ret); + + void create_empty_profile(); + void create_copied_profile(); + void open_profile_directory(); + void ensure_tray(); void toggle_restore_from_tray(QSystemTrayIcon::ActivationReason e); - static void set_working_directory(); + bool tray_enabled(); + bool start_in_tray(); -signals: - void start_tracker(); - void stop_tracker(); - void toggle_tracker(); - void restart_tracker(); + void save_modules(); + + void exit(int status = EXIT_SUCCESS); public: main_window(); diff --git a/variant/default/main.cpp b/variant/default/main.cpp index 09bb5e2a..2c1dc607 100644 --- a/variant/default/main.cpp +++ b/variant/default/main.cpp @@ -5,13 +5,13 @@ # include <windows.h> #endif -#if defined Q_CREATOR_RUN +#ifdef __clang__ # pragma GCC diagnostic ignored "-Wmain" #endif int main(int argc, char** argv) { - return run_application(argc, argv, [] { return new main_window; }); + return run_application(argc, argv, [] { return std::make_unique<main_window>(); }); } #if defined _MSC_VER diff --git a/variant/default/new_file_dialog.h b/variant/default/new_file_dialog.h index 5669e4a9..7244e524 100644 --- a/variant/default/new_file_dialog.h +++ b/variant/default/new_file_dialog.h @@ -11,7 +11,7 @@ class new_file_dialog : public QDialog { Q_OBJECT public: - new_file_dialog(QWidget* parent = 0); + new_file_dialog(QWidget* parent = nullptr); bool is_ok(QString& name_); private: diff --git a/variant/trackmouse/CMakeLists.txt b/variant/trackmouse/CMakeLists.txt index c57479d4..6240d4b3 100644 --- a/variant/trackmouse/CMakeLists.txt +++ b/variant/trackmouse/CMakeLists.txt @@ -6,4 +6,4 @@ set_target_properties(opentrack-executable PROPERTIES PREFIX "" ) -target_link_libraries(opentrack-executable opentrack-user-interface) +target_link_libraries(${self} opentrack-user-interface opentrack-version) diff --git a/variant/trackmouse/window.cpp b/variant/trackmouse/window.cpp index a1160c50..d6c8a8dd 100644 --- a/variant/trackmouse/window.cpp +++ b/variant/trackmouse/window.cpp @@ -184,9 +184,16 @@ main_window::~main_window() if (work) { stop_tracker_(); - QEventLoop ev; - ev.processEvents(); - portable::sleep(2000); + close(); + + constexpr int inc = 100, max = 2000; + + for (int k = 0; k < max; k += inc) + { + QEventLoop ev; + ev.processEvents(); + portable::sleep(inc); + } } exit(); @@ -309,9 +316,6 @@ void main_window::stop_tracker_() update_button_state(false, false); set_title(); ui.btnStartTracker->setFocus(); - - // ps3 eye issues - portable::sleep(1000); } void main_window::set_title(const QString& game_title) diff --git a/variant/trackmouse/window.hpp b/variant/trackmouse/window.hpp index fd5d5f44..2c196852 100644 --- a/variant/trackmouse/window.hpp +++ b/variant/trackmouse/window.hpp @@ -70,7 +70,7 @@ class main_window final : public QMainWindow, private State bool maybe_die_on_config_not_writable(const QString& current); void die_on_config_not_writable(); - static constexpr inline int save_settings_interval_ms = 2500; + static constexpr int save_settings_interval_ms = 2500; private slots: void save_modules(); diff --git a/video/CMakeLists.txt b/video/CMakeLists.txt new file mode 100644 index 00000000..0a9dfd24 --- /dev/null +++ b/video/CMakeLists.txt @@ -0,0 +1 @@ +otr_module(video BIN) diff --git a/tracker-wii/export.hpp b/video/export.hpp index e186b1aa..fc850193 100644 --- a/tracker-wii/export.hpp +++ b/video/export.hpp @@ -4,8 +4,8 @@ #include "compat/linkage-macros.hpp" -#ifdef BUILD_TRACKER_WII -# define OTR_WII_EXPORT OTR_GENERIC_EXPORT +#ifdef BUILD_VIDEO +# define OTR_VIDEO_EXPORT OTR_GENERIC_EXPORT #else -# define OTR_WII_EXPORT OTR_GENERIC_IMPORT +# define OTR_VIDEO_EXPORT OTR_GENERIC_IMPORT #endif diff --git a/video/lang/nl_NL.ts b/video/lang/nl_NL.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video/lang/nl_NL.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/video/lang/ru_RU.ts b/video/lang/ru_RU.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video/lang/ru_RU.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/video/lang/stub.ts b/video/lang/stub.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video/lang/stub.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/video/lang/zh_CN.ts b/video/lang/zh_CN.ts new file mode 100644 index 00000000..6401616d --- /dev/null +++ b/video/lang/zh_CN.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +</TS> diff --git a/video/video-widget.cpp b/video/video-widget.cpp new file mode 100644 index 00000000..3e6d9038 --- /dev/null +++ b/video/video-widget.cpp @@ -0,0 +1,84 @@ +#include "video-widget.hpp" + +#include "compat/check-visible.hpp" +#include "compat/math.hpp" + +#include <cstddef> +#include <cstring> + +#include <QPainter> + +void video_widget::init_image_nolock() +{ + texture = QImage(W, H, QImage::Format_ARGB32); + texture.setDevicePixelRatio(devicePixelRatioF()); +} + +video_widget::video_widget(QWidget* parent) : QWidget(parent) +{ + W = width(); H = height(); + init_image_nolock(); texture.fill(Qt::gray); + + connect(&timer, &QTimer::timeout, this, &video_widget::update_and_repaint, Qt::DirectConnection); + timer.start(65); +} + +void video_widget::update_image(const QImage& img) +{ + QMutexLocker l(&mtx); + + if (freshp) + return; + freshp = true; + + unsigned nbytes = (unsigned)(img.bytesPerLine() * img.height()); + vec.resize(nbytes); vec.shrink_to_fit(); + std::memcpy(vec.data(), img.constBits(), nbytes); + + texture = QImage((const unsigned char*) vec.data(), img.width(), img.height(), img.bytesPerLine(), img.format()); + texture.setDevicePixelRatio(devicePixelRatioF()); +} + +void video_widget::paintEvent(QPaintEvent*) +{ + QMutexLocker foo(&mtx); + + if (texture.width() != W || texture.height() != H) + { + init_image_nolock(); + texture.fill(Qt::gray); + } + + QPainter painter(this); + painter.drawImage(rect(), texture); +} + +void video_widget::update_and_repaint() +{ + if (!check_is_visible()) + return; + + QMutexLocker l(&mtx); + + if (freshp) + { + freshp = false; + repaint(); + } +} + +void video_widget::resizeEvent(QResizeEvent*) +{ + QMutexLocker l(&mtx); + double dpr = devicePixelRatioF(); + W = iround(width() * dpr); + H = iround(height() * dpr); + init_image_nolock(); +} + +void video_widget::get_preview_size(int& w, int& h) +{ + QMutexLocker l(&mtx); + w = W; h = H; +} + diff --git a/video/video-widget.hpp b/video/video-widget.hpp new file mode 100644 index 00000000..563f468c --- /dev/null +++ b/video/video-widget.hpp @@ -0,0 +1,46 @@ +/* Copyright (c) 2014-2016, 2019 Stanislaw Halik <sthalik@misaki.pl> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ + +#pragma once + +#include "compat/math.hpp" +#include "export.hpp" + +#include <vector> + +#include <QWidget> +#include <QTimer> +#include <QMutex> + +class OTR_VIDEO_EXPORT video_widget : public QWidget +{ + Q_OBJECT + +public: + video_widget(QWidget* parent = nullptr); + + void update_image(const QImage& image); + void get_preview_size(int& w, int& h); + void resizeEvent(QResizeEvent*) override; +protected slots: + void paintEvent(QPaintEvent*) override; + void update_and_repaint(); +private: + QTimer timer; + +protected: + QMutex mtx { QMutex::Recursive }; + QImage texture; + std::vector<unsigned char> vec; + + bool freshp = false; + + int W = iround(QWidget::width() * devicePixelRatioF()); + int H = iround(QWidget::height() * devicePixelRatioF()); + + void init_image_nolock(); +}; diff --git a/x-plane-plugin/CMakeLists.txt b/x-plane-plugin/CMakeLists.txt index 23881ca3..1d686d98 100644 --- a/x-plane-plugin/CMakeLists.txt +++ b/x-plane-plugin/CMakeLists.txt @@ -1,5 +1,5 @@ if(LINUX OR APPLE) - set(SDK_XPLANE "" CACHE PATH "Path to X-Plane SDK") + set(SDK_XPLANE "" CACHE PATH "Path to the X-Plane SDK") if(SDK_XPLANE) otr_module(xplane-plugin NO-QT) @@ -8,29 +8,28 @@ if(LINUX OR APPLE) target_include_directories(opentrack-xplane-plugin SYSTEM PUBLIC ${SDK_XPLANE}/CHeaders ${SDK_XPLANE}/CHeaders/XPLM) if(APPLE) - set_property(TARGET opentrack-xplane-plugin APPEND_STRING PROPERTY - COMPILE_FLAGS "-iframework ${SDK_XPLANE}/Libraries/Mac/ -DAPL -DXPLM200 -DXPLM210 -framework XPLM -framework XPWidgets ") - set_property(TARGET opentrack-xplane-plugin APPEND_STRING PROPERTY - LINK_FLAGS "-F${SDK_XPLANE}/Libraries/Mac/ -framework XPLM -framework XPWidgets ") + target_compile_options(${self} PRIVATE + -iframework "${SDK_XPLANE}/Libraries/Mac/" + -DAPL -DXPLM200 -DXPLM210 + -framework XPLM -framework XPWidgets) + target_link_options(${self} + "-F${SDK_XPLANE}/Libraries/Mac/" + -framework XPLM -framework XPWidgets) elseif(CMAKE_COMPILER_IS_GNUCXX) - set_property(TARGET opentrack-xplane-plugin APPEND_STRING PROPERTY - COMPILE_FLAGS "-fPIC -DLIN -DXPLM200 -DXPLM210 ") - set_property(TARGET opentrack-xplane-plugin APPEND_STRING PROPERTY - LINK_FLAGS "-rdynamic -nodefaultlibs -fPIC ") + target_compile_options(${self} PRIVATE -DLIN -DXPLM200 -DXPLM210) + target_link_options(${self} PRIVATE -rdynamic -nodefaultlibs) endif() - if(CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_COMPILER_IS_CLANG) - set_property(TARGET opentrack-xplane-plugin APPEND_STRING PROPERTY - LINK_FLAGS "-undefined_warning ") + if(CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_COMPILER_IS_CLANG) + target_link_options(${self} PRIVATE -undefined_warning) endif() - set_target_properties(opentrack-xplane-plugin PROPERTIES + set_target_properties(${self} PROPERTIES LIBRARY_OUTPUT_NAME "opentrack.xpl" - PREFIX "" - SUFFIX "") + PREFIX "" SUFFIX "") if(UNIX AND NOT APPLE) - target_link_libraries(opentrack-xplane-plugin rt) + target_link_libraries(${self} rt) endif() endif() endif() diff --git a/x-plane-plugin/plugin.c b/x-plane-plugin/plugin.c index b0482d69..b062bb02 100644 --- a/x-plane-plugin/plugin.c +++ b/x-plane-plugin/plugin.c @@ -159,6 +159,15 @@ static int TranslationToggleHandler(XPLMCommandRef inCommand, return 0; } +static inline +void volatile_explicit_bzero(void volatile* restrict ptr, size_t len) +{ + for (size_t i = 0; i < len; i++) + *((char volatile* restrict)ptr + i) = 0; + + asm volatile("" ::: "memory"); +} + PLUGIN_API OTR_GENERIC_EXPORT int XPluginStart (char* outName, char* outSignature, char* outDescription) { view_x = XPLMFindDataRef("sim/aircraft/view/acf_peX"); @@ -189,7 +198,7 @@ int XPluginStart (char* outName, char* outSignature, char* outDescription) { return 0; } shm_posix = lck_posix->mem; - memset(shm_posix, 0, sizeof(WineSHM)); + volatile_explicit_bzero(shm_posix, sizeof(WineSHM)); strcpy(outName, "opentrack"); strcpy(outSignature, "opentrack - freetrack lives!"); strcpy(outDescription, "head tracking view control"); |