├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── fetch_deps.cmd ├── include ├── assets │ └── architects_daughter.hpp └── spi_screen.h ├── main_test.cpp ├── run.cmd ├── sd └── foo.txt ├── src └── main.cpp └── winduino_hardware └── spi_screen ├── CMakeLists.txt ├── spi_screen.cpp ├── spi_screen.h └── spi_screen_lib.h /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /build 3 | /lib -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.24) 2 | 3 | project(example VERSION 1.0) 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED True) 6 | set(FIND_LIBRARY_USE_LIB64_PATHS True) 7 | set( DXLIBS d2d1 ) 8 | set(CMAKE_STATIC_LIBRARY_PREFIX "") 9 | set(CMAKE_SHARED_LIBRARY_PREFIX "") 10 | 11 | add_subdirectory(lib/htcw_winduino) 12 | 13 | #uix/gfx library dependencies 14 | add_subdirectory(lib/htcw_bits) 15 | add_subdirectory(lib/htcw_data) 16 | add_subdirectory(lib/htcw_io) 17 | add_subdirectory(lib/htcw_ml) 18 | add_subdirectory(lib/htcw_gfx) 19 | add_subdirectory(lib/htcw_uix) 20 | 21 | #for SPI screen testing 22 | add_subdirectory(lib/htcw_tft_io) 23 | add_subdirectory(lib/htcw_ft6236) 24 | add_subdirectory(lib/htcw_st7789) 25 | 26 | #SPI screen hardware emulation 27 | add_subdirectory(winduino_hardware/spi_screen) 28 | 29 | add_executable(example 30 | src/main.cpp 31 | ) 32 | target_link_libraries(example htcw_winduino htcw_bits htcw_io htcw_data htcw_ml htcw_gfx htcw_uix htcw_tft_io htcw_st7789 htcw_ft6236) 33 | target_include_directories(example PUBLIC 34 | "${PROJECT_SOURCE_DIR}" 35 | "${PROJECT_BINARY_DIR}" 36 | "${PROJECT_SOURCE_DIR}/include" 37 | "${PROJECT_SOURCE_DIR}/src" 38 | ) 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ### GNU LESSER GENERAL PUBLIC LICENSE 2 | 3 | Version 2.1, February 1999 4 | 5 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 6 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 7 | 8 | Everyone is permitted to copy and distribute verbatim copies 9 | of this license document, but changing it is not allowed. 10 | 11 | [This is the first released version of the Lesser GPL. It also counts 12 | as the successor of the GNU Library Public License, version 2, hence 13 | the version number 2.1.] 14 | 15 | ### Preamble 16 | 17 | The licenses for most software are designed to take away your freedom 18 | to share and change it. By contrast, the GNU General Public Licenses 19 | are intended to guarantee your freedom to share and change free 20 | software--to make sure the software is free for all its users. 21 | 22 | This license, the Lesser General Public License, applies to some 23 | specially designated software packages--typically libraries--of the 24 | Free Software Foundation and other authors who decide to use it. You 25 | can use it too, but we suggest you first think carefully about whether 26 | this license or the ordinary General Public License is the better 27 | strategy to use in any particular case, based on the explanations 28 | below. 29 | 30 | When we speak of free software, we are referring to freedom of use, 31 | not price. Our General Public Licenses are designed to make sure that 32 | you have the freedom to distribute copies of free software (and charge 33 | for this service if you wish); that you receive source code or can get 34 | it if you want it; that you can change the software and use pieces of 35 | it in new free programs; and that you are informed that you can do 36 | these things. 37 | 38 | To protect your rights, we need to make restrictions that forbid 39 | distributors to deny you these rights or to ask you to surrender these 40 | rights. These restrictions translate to certain responsibilities for 41 | you if you distribute copies of the library or if you modify it. 42 | 43 | For example, if you distribute copies of the library, whether gratis 44 | or for a fee, you must give the recipients all the rights that we gave 45 | you. You must make sure that they, too, receive or can get the source 46 | code. If you link other code with the library, you must provide 47 | complete object files to the recipients, so that they can relink them 48 | with the library after making changes to the library and recompiling 49 | it. And you must show them these terms so they know their rights. 50 | 51 | We protect your rights with a two-step method: (1) we copyright the 52 | library, and (2) we offer you this license, which gives you legal 53 | permission to copy, distribute and/or modify the library. 54 | 55 | To protect each distributor, we want to make it very clear that there 56 | is no warranty for the free library. Also, if the library is modified 57 | by someone else and passed on, the recipients should know that what 58 | they have is not the original version, so that the original author's 59 | reputation will not be affected by problems that might be introduced 60 | by others. 61 | 62 | Finally, software patents pose a constant threat to the existence of 63 | any free program. We wish to make sure that a company cannot 64 | effectively restrict the users of a free program by obtaining a 65 | restrictive license from a patent holder. Therefore, we insist that 66 | any patent license obtained for a version of the library must be 67 | consistent with the full freedom of use specified in this license. 68 | 69 | Most GNU software, including some libraries, is covered by the 70 | ordinary GNU General Public License. This license, the GNU Lesser 71 | General Public License, applies to certain designated libraries, and 72 | is quite different from the ordinary General Public License. We use 73 | this license for certain libraries in order to permit linking those 74 | libraries into non-free programs. 75 | 76 | When a program is linked with a library, whether statically or using a 77 | shared library, the combination of the two is legally speaking a 78 | combined work, a derivative of the original library. The ordinary 79 | General Public License therefore permits such linking only if the 80 | entire combination fits its criteria of freedom. The Lesser General 81 | Public License permits more lax criteria for linking other code with 82 | the library. 83 | 84 | We call this license the "Lesser" General Public License because it 85 | does Less to protect the user's freedom than the ordinary General 86 | Public License. It also provides other free software developers Less 87 | of an advantage over competing non-free programs. These disadvantages 88 | are the reason we use the ordinary General Public License for many 89 | libraries. However, the Lesser license provides advantages in certain 90 | special circumstances. 91 | 92 | For example, on rare occasions, there may be a special need to 93 | encourage the widest possible use of a certain library, so that it 94 | becomes a de-facto standard. To achieve this, non-free programs must 95 | be allowed to use the library. A more frequent case is that a free 96 | library does the same job as widely used non-free libraries. In this 97 | case, there is little to gain by limiting the free library to free 98 | software only, so we use the Lesser General Public License. 99 | 100 | In other cases, permission to use a particular library in non-free 101 | programs enables a greater number of people to use a large body of 102 | free software. For example, permission to use the GNU C Library in 103 | non-free programs enables many more people to use the whole GNU 104 | operating system, as well as its variant, the GNU/Linux operating 105 | system. 106 | 107 | Although the Lesser General Public License is Less protective of the 108 | users' freedom, it does ensure that the user of a program that is 109 | linked with the Library has the freedom and the wherewithal to run 110 | that program using a modified version of the Library. 111 | 112 | The precise terms and conditions for copying, distribution and 113 | modification follow. Pay close attention to the difference between a 114 | "work based on the library" and a "work that uses the library". The 115 | former contains code derived from the library, whereas the latter must 116 | be combined with the library in order to run. 117 | 118 | ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 119 | 120 | **0.** This License Agreement applies to any software library or other 121 | program which contains a notice placed by the copyright holder or 122 | other authorized party saying it may be distributed under the terms of 123 | this Lesser General Public License (also called "this License"). Each 124 | licensee is addressed as "you". 125 | 126 | A "library" means a collection of software functions and/or data 127 | prepared so as to be conveniently linked with application programs 128 | (which use some of those functions and data) to form executables. 129 | 130 | The "Library", below, refers to any such software library or work 131 | which has been distributed under these terms. A "work based on the 132 | Library" means either the Library or any derivative work under 133 | copyright law: that is to say, a work containing the Library or a 134 | portion of it, either verbatim or with modifications and/or translated 135 | straightforwardly into another language. (Hereinafter, translation is 136 | included without limitation in the term "modification".) 137 | 138 | "Source code" for a work means the preferred form of the work for 139 | making modifications to it. For a library, complete source code means 140 | all the source code for all modules it contains, plus any associated 141 | interface definition files, plus the scripts used to control 142 | compilation and installation of the library. 143 | 144 | Activities other than copying, distribution and modification are not 145 | covered by this License; they are outside its scope. The act of 146 | running a program using the Library is not restricted, and output from 147 | such a program is covered only if its contents constitute a work based 148 | on the Library (independent of the use of the Library in a tool for 149 | writing it). Whether that is true depends on what the Library does and 150 | what the program that uses the Library does. 151 | 152 | **1.** You may copy and distribute verbatim copies of the Library's 153 | complete source code as you receive it, in any medium, provided that 154 | you conspicuously and appropriately publish on each copy an 155 | appropriate copyright notice and disclaimer of warranty; keep intact 156 | all the notices that refer to this License and to the absence of any 157 | warranty; and distribute a copy of this License along with the 158 | Library. 159 | 160 | You may charge a fee for the physical act of transferring a copy, and 161 | you may at your option offer warranty protection in exchange for a 162 | fee. 163 | 164 | **2.** You may modify your copy or copies of the Library or any 165 | portion of it, thus forming a work based on the Library, and copy and 166 | distribute such modifications or work under the terms of Section 1 167 | above, provided that you also meet all of these conditions: 168 | 169 | - **a)** The modified work must itself be a software library. 170 | - **b)** You must cause the files modified to carry prominent 171 | notices stating that you changed the files and the date of 172 | any change. 173 | - **c)** You must cause the whole of the work to be licensed at no 174 | charge to all third parties under the terms of this License. 175 | - **d)** If a facility in the modified Library refers to a function 176 | or a table of data to be supplied by an application program that 177 | uses the facility, other than as an argument passed when the 178 | facility is invoked, then you must make a good faith effort to 179 | ensure that, in the event an application does not supply such 180 | function or table, the facility still operates, and performs 181 | whatever part of its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of 185 | the application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | **3.** You may opt to apply the terms of the ordinary GNU General 212 | Public License instead of this License to a given copy of the Library. 213 | To do this, you must alter all the notices that refer to this License, 214 | so that they refer to the ordinary GNU General Public License, version 215 | 2, instead of to this License. (If a newer version than version 2 of 216 | the ordinary GNU General Public License has appeared, then you can 217 | specify that version instead if you wish.) Do not make any other 218 | change in these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for that 221 | copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of the 225 | Library into a program that is not a library. 226 | 227 | **4.** You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy from 235 | a designated place, then offering equivalent access to copy the source 236 | code from the same place satisfies the requirement to distribute the 237 | source code, even though third parties are not compelled to copy the 238 | source along with the object code. 239 | 240 | **5.** A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a work, 243 | in isolation, is not a derivative work of the Library, and therefore 244 | falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. Section 250 | 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data structure 260 | layouts and accessors, and small macros and small inline functions 261 | (ten lines or less in length), then the use of the object file is 262 | unrestricted, regardless of whether it is legally a derivative work. 263 | (Executables containing this object code plus portions of the Library 264 | will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | **6.** As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a work 273 | containing portions of the Library, and distribute that work under 274 | terms of your choice, provided that the terms permit modification of 275 | the work for the customer's own use and reverse engineering for 276 | debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | - **a)** Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood that 294 | the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | - **b)** Use a suitable shared library mechanism for linking with 298 | the Library. A suitable mechanism is one that (1) uses at run time 299 | a copy of the library already present on the user's computer 300 | system, rather than copying library functions into the executable, 301 | and (2) will operate properly with a modified version of the 302 | library, if the user installs one, as long as the modified version 303 | is interface-compatible with the version that the work was 304 | made with. 305 | - **c)** Accompany the work with a written offer, valid for at least 306 | three years, to give the same user the materials specified in 307 | Subsection 6a, above, for a charge no more than the cost of 308 | performing this distribution. 309 | - **d)** If distribution of the work is made by offering access to 310 | copy from a designated place, offer equivalent access to copy the 311 | above specified materials from the same place. 312 | - **e)** Verify that the user has already received a copy of these 313 | materials or that you have already sent this user a copy. 314 | 315 | For an executable, the required form of the "work that uses the 316 | Library" must include any data and utility programs needed for 317 | reproducing the executable from it. However, as a special exception, 318 | the materials to be distributed need not include anything that is 319 | normally distributed (in either source or binary form) with the major 320 | components (compiler, kernel, and so on) of the operating system on 321 | which the executable runs, unless that component itself accompanies 322 | the executable. 323 | 324 | It may happen that this requirement contradicts the license 325 | restrictions of other proprietary libraries that do not normally 326 | accompany the operating system. Such a contradiction means you cannot 327 | use both them and the Library together in an executable that you 328 | distribute. 329 | 330 | **7.** You may place library facilities that are a work based on the 331 | Library side-by-side in a single library together with other library 332 | facilities not covered by this License, and distribute such a combined 333 | library, provided that the separate distribution of the work based on 334 | the Library and of the other library facilities is otherwise 335 | permitted, and provided that you do these two things: 336 | 337 | - **a)** Accompany the combined library with a copy of the same work 338 | based on the Library, uncombined with any other 339 | library facilities. This must be distributed under the terms of 340 | the Sections above. 341 | - **b)** Give prominent notice with the combined library of the fact 342 | that part of it is a work based on the Library, and explaining 343 | where to find the accompanying uncombined form of the same work. 344 | 345 | **8.** You may not copy, modify, sublicense, link with, or distribute 346 | the Library except as expressly provided under this License. Any 347 | attempt otherwise to copy, modify, sublicense, link with, or 348 | distribute the Library is void, and will automatically terminate your 349 | rights under this License. However, parties who have received copies, 350 | or rights, from you under this License will not have their licenses 351 | terminated so long as such parties remain in full compliance. 352 | 353 | **9.** You are not required to accept this License, since you have not 354 | signed it. However, nothing else grants you permission to modify or 355 | distribute the Library or its derivative works. These actions are 356 | prohibited by law if you do not accept this License. Therefore, by 357 | modifying or distributing the Library (or any work based on the 358 | Library), you indicate your acceptance of this License to do so, and 359 | all its terms and conditions for copying, distributing or modifying 360 | the Library or works based on it. 361 | 362 | **10.** Each time you redistribute the Library (or any work based on 363 | the Library), the recipient automatically receives a license from the 364 | original licensor to copy, distribute, link with or modify the Library 365 | subject to these terms and conditions. You may not impose any further 366 | restrictions on the recipients' exercise of the rights granted herein. 367 | You are not responsible for enforcing compliance by third parties with 368 | this License. 369 | 370 | **11.** If, as a consequence of a court judgment or allegation of 371 | patent infringement or for any other reason (not limited to patent 372 | issues), conditions are imposed on you (whether by court order, 373 | agreement or otherwise) that contradict the conditions of this 374 | License, they do not excuse you from the conditions of this License. 375 | If you cannot distribute so as to satisfy simultaneously your 376 | obligations under this License and any other pertinent obligations, 377 | then as a consequence you may not distribute the Library at all. For 378 | example, if a patent license would not permit royalty-free 379 | redistribution of the Library by all those who receive copies directly 380 | or indirectly through you, then the only way you could satisfy both it 381 | and this License would be to refrain entirely from distribution of the 382 | Library. 383 | 384 | If any portion of this section is held invalid or unenforceable under 385 | any particular circumstance, the balance of the section is intended to 386 | apply, and the section as a whole is intended to apply in other 387 | circumstances. 388 | 389 | It is not the purpose of this section to induce you to infringe any 390 | patents or other property right claims or to contest validity of any 391 | such claims; this section has the sole purpose of protecting the 392 | integrity of the free software distribution system which is 393 | implemented by public license practices. Many people have made 394 | generous contributions to the wide range of software distributed 395 | through that system in reliance on consistent application of that 396 | system; it is up to the author/donor to decide if he or she is willing 397 | to distribute software through any other system and a licensee cannot 398 | impose that choice. 399 | 400 | This section is intended to make thoroughly clear what is believed to 401 | be a consequence of the rest of this License. 402 | 403 | **12.** If the distribution and/or use of the Library is restricted in 404 | certain countries either by patents or by copyrighted interfaces, the 405 | original copyright holder who places the Library under this License 406 | may add an explicit geographical distribution limitation excluding 407 | those countries, so that distribution is permitted only in or among 408 | countries not thus excluded. In such case, this License incorporates 409 | the limitation as if written in the body of this License. 410 | 411 | **13.** The Free Software Foundation may publish revised and/or new 412 | versions of the Lesser General Public License from time to time. Such 413 | new versions will be similar in spirit to the present version, but may 414 | differ in detail to address new problems or concerns. 415 | 416 | Each version is given a distinguishing version number. If the Library 417 | specifies a version number of this License which applies to it and 418 | "any later version", you have the option of following the terms and 419 | conditions either of that version or of any later version published by 420 | the Free Software Foundation. If the Library does not specify a 421 | license version number, you may choose any version ever published by 422 | the Free Software Foundation. 423 | 424 | **14.** If you wish to incorporate parts of the Library into other 425 | free programs whose distribution conditions are incompatible with 426 | these, write to the author to ask for permission. For software which 427 | is copyrighted by the Free Software Foundation, write to the Free 428 | Software Foundation; we sometimes make exceptions for this. Our 429 | decision will be guided by the two goals of preserving the free status 430 | of all derivatives of our free software and of promoting the sharing 431 | and reuse of software generally. 432 | 433 | **NO WARRANTY** 434 | 435 | **15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 436 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 437 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 438 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 439 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 440 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 441 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 442 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 443 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 444 | 445 | **16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 446 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 447 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 448 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 449 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 450 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 451 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 452 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 453 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 454 | DAMAGES. 455 | 456 | ### END OF TERMS AND CONDITIONS 457 | 458 | ### How to Apply These Terms to Your New Libraries 459 | 460 | If you develop a new library, and you want it to be of the greatest 461 | possible use to the public, we recommend making it free software that 462 | everyone can redistribute and change. You can do so by permitting 463 | redistribution under these terms (or, alternatively, under the terms 464 | of the ordinary General Public License). 465 | 466 | To apply these terms, attach the following notices to the library. It 467 | is safest to attach them to the start of each source file to most 468 | effectively convey the exclusion of warranty; and each file should 469 | have at least the "copyright" line and a pointer to where the full 470 | notice is found. 471 | 472 | one line to give the library's name and an idea of what it does. 473 | Copyright (C) year name of author 474 | 475 | This library is free software; you can redistribute it and/or 476 | modify it under the terms of the GNU Lesser General Public 477 | License as published by the Free Software Foundation; either 478 | version 2.1 of the License, or (at your option) any later version. 479 | 480 | This library is distributed in the hope that it will be useful, 481 | but WITHOUT ANY WARRANTY; without even the implied warranty of 482 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 483 | Lesser General Public License for more details. 484 | 485 | You should have received a copy of the GNU Lesser General Public 486 | License along with this library; if not, write to the Free Software 487 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 488 | 489 | Also add information on how to contact you by electronic and paper 490 | mail. 491 | 492 | You should also get your employer (if you work as a programmer) or 493 | your school, if any, to sign a "copyright disclaimer" for the library, 494 | if necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in 497 | the library `Frob' (a library for tweaking knobs) written 498 | by James Random Hacker. 499 | 500 | signature of Ty Coon, 1 April 1990 501 | Ty Coon, President of Vice 502 | 503 | That's all there is to it! -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # winduino 2 | 3 | ![winduino](https://github.com/codewitch-honey-crisis/winduino/assets/16417109/37ba76c2-b248-4aff-8fd1-795c4f443b19) 4 | 5 | A small bridge for Arduino applications to allow them to be roughly prototyped on a 6 | Windows PC using UIX or DirectX for the display. 7 | 8 | A rough port of the UIX benchmark example is included 9 | 10 | I have not tested with LVGL yet, but it should work. 11 | 12 | To build the project you'll need mingw. I use GCC 12 or above but earlier versions *might* choke. 13 | 14 | 1. Make a "lib" folder in the project directory. 15 | 2. Run fetch_deps.cmd to get all of the dependencies. 16 | 3. Use the CMake VS Code extension to Build All Projects on the root CMakeLists.txt 17 | 4. Use the ./run.cmd to run the project 18 | 19 | -------------------------------------------------------------------------------- /fetch_deps.cmd: -------------------------------------------------------------------------------- 1 | @rmdir lib /S /Q 2 | @mkdir lib 3 | @cd lib 4 | @git clone https://github.com/codewitch-honey-crisis/htcw_winduino htcw_winduino 5 | @git clone https://github.com/codewitch-honey-crisis/htcw_tft_io htcw_tft_io 6 | @git clone https://github.com/codewitch-honey-crisis/htcw_st7789 htcw_st7789 7 | @git clone https://github.com/codewitch-honey-crisis/htcw_ft6236 htcw_ft6236 8 | @git clone https://github.com/codewitch-honey-crisis/uix htcw_uix 9 | @.\htcw_uix\fetch_deps.cmd 10 | @cd .. 11 | -------------------------------------------------------------------------------- /include/spi_screen.h: -------------------------------------------------------------------------------- 1 | #ifndef SPI_SCREEN_H 2 | #define SPI_SCREEN_H 3 | 4 | #define SPI_SCREEN_PIN_MOSI 0 5 | #define SPI_SCREEN_PIN_MISO 1 6 | #define SPI_SCREEN_PIN_CLK 2 7 | #define SPI_SCREEN_PIN_CS 3 8 | #define SPI_SCREEN_PIN_DC 4 9 | #define SPI_SCREEN_PIN_RST 5 10 | #define SPI_SCREEN_PIN_BKL 6 11 | 12 | #define SPI_SCREEN_PROP_RESOLUTION 0 13 | #define SPI_SCREEN_PROP_BKL_LOW 1 14 | #define SPI_SCREEN_PROP_COLSET 2 15 | #define SPI_SCREEN_PROP_ROWSET 3 16 | #define SPI_SCREEN_PROP_WRITE 4 17 | #define SPI_SCREEN_PROP_READ 5 18 | #define SPI_SCREEN_PROP_OFFSETS 6 19 | 20 | #define LIB_SPI_SCREEN ".\\winduino_spi_screen.dll" 21 | #endif // SCREEN_H -------------------------------------------------------------------------------- /main_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | // downloaded from fontsquirrel.com and header generated with 5 | // https://honeythecodewitch.com/gfx/generator 6 | #define ARCHITECTS_DAUGHTER_IMPLEMENTATION 7 | #include 8 | static const gfx::open_font& text_font = architects_daughter; 9 | using namespace gfx; 10 | using namespace uix; 11 | // creates a BGRx pixel by making each channel 12 | // one quarter of the whole. Any remainder bits 13 | // are added to the green channel. One channel 14 | // is unused. Consumed by DirectX 15 | template 16 | using bgrx_pixel = gfx::pixel< 17 | gfx::channel_traits, 18 | gfx::channel_traits, 19 | gfx::channel_traits, 20 | gfx::channel_traits >; 21 | using screen_t = screen>; 22 | using color_t = color; 23 | // for access to RGB8888 colors which controls use 24 | using color32_t = color>; 25 | 26 | using button_t = uix::svg_button; 27 | 28 | // UIX allows you to use two buffers for maximum DMA efficiency 29 | // but it's not used in Winduino, which really doesn't need it. 30 | // So we declare one 64KB buffer for the transfer 31 | constexpr static const int lcd_screen_size = bitmap::sizeof_buffer({320, 240}); 32 | constexpr static const int lcd_buffer_size = lcd_screen_size > 64 * 1024 ? 64 * 1024 : lcd_screen_size; 33 | static uint8_t lcd_buffer1[lcd_buffer_size]; 34 | 35 | // the main screen 36 | screen_t main_screen({320, 240}, sizeof(lcd_buffer1), lcd_buffer1, nullptr); 37 | button_t test_button(main_screen); 38 | 39 | void uix_on_touch(point16* out_locations, 40 | size_t* in_out_locations_size, 41 | void* state) { 42 | if (!*in_out_locations_size) { 43 | return; 44 | } 45 | int x, y; 46 | if (read_mouse(&x, &y)) { 47 | if (x < 0) { 48 | x = 0; 49 | } 50 | if (x >= 320) { 51 | x = 319; 52 | } 53 | if (y < 0) { 54 | y = 0; 55 | } 56 | if (y >= 240) { 57 | y = 239; 58 | } 59 | *in_out_locations_size = 1; 60 | *out_locations = point16((unsigned)x, (unsigned)y); 61 | return; 62 | } 63 | *in_out_locations_size = 0; 64 | } 65 | void uix_on_flush(const rect16& bounds, const void* bmp, void* state) { 66 | flush_bitmap(bounds.x1, bounds.y1, bounds.x2 - bounds.x1 + 1, bounds.y2 - bounds.y1 + 1, bmp); 67 | main_screen.flush_complete(); 68 | } 69 | // initialize the screens and controls 70 | static void screen_init() { 71 | const rgba_pixel<32> transparent(0, 0, 0, 0); 72 | main_screen.background_color(color_t::white); 73 | 74 | test_button.background_color(color32_t::gray,true); 75 | test_button.border_color(color32_t::black,true); 76 | test_button.text_color(color32_t::black); 77 | test_button.text_open_font(&text_font); 78 | test_button.text_line_height(25); 79 | test_button.text("Hello!"); 80 | test_button.radiuses({0,0}); 81 | test_button.bounds(srect16(0,0,99,99).center_horizontal(main_screen.bounds()).offset(100,0)); 82 | main_screen.register_control(test_button); 83 | 84 | main_screen.on_flush_callback(uix_on_flush); 85 | main_screen.on_touch_callback(uix_on_touch); 86 | } 87 | 88 | void setup() { 89 | Serial.begin(115200); 90 | screen_init(); 91 | } 92 | void loop() { 93 | main_screen.update(); 94 | } -------------------------------------------------------------------------------- /run.cmd: -------------------------------------------------------------------------------- 1 | .\build\example.exe -------------------------------------------------------------------------------- /sd/foo.txt: -------------------------------------------------------------------------------- 1 | Hello world! -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // the code in this file is from my UIX benchmark demo, which is MIT licensed - honey the codewitch 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "spi_screen.h" 8 | #include 9 | #include 10 | #include 11 | using namespace gfx; 12 | using namespace uix; 13 | using namespace arduino; 14 | 15 | // using the SPI bus 1 (default is 0), CS of 5 16 | using bus_t = tft_spi<1,5>; 17 | // use the ST7789 driver with the above bus 18 | using lcd_t = st7789<135,240,2,4,-1,bus_t,1>; 19 | // declare the touch panel 20 | using touch_t = ft6236<240,135>; 21 | // creates a BGRx pixel by making each channel 22 | // one quarter of the whole. Any remainder bits 23 | // are added to the green channel. One channel 24 | // is unused. Consumed by DirectX 25 | template 26 | using bgrx_pixel = gfx::pixel< 27 | gfx::channel_traits, 28 | gfx::channel_traits, 29 | gfx::channel_traits, 30 | gfx::channel_traits 31 | >; 32 | using screen_t = screen>; 33 | using label_t = label; 34 | using color_t = color; 35 | using color_ctl_t = color>; 36 | using frame_buffer_t = bitmap; 37 | static bgrx_pixel<32> fire_cols[] = { 38 | bgrx_pixel<32>(0,0,0,255), bgrx_pixel<32>(24,0,0,255), bgrx_pixel<32>(24,0,0,255), bgrx_pixel<32>(28,0,0,255), 39 | bgrx_pixel<32>(32,0,0,255), bgrx_pixel<32>(32,0,0,255), bgrx_pixel<32>(36,0,0,255), bgrx_pixel<32>(40,0,0,255), 40 | bgrx_pixel<32>(40,0,8,255), bgrx_pixel<32>(36,0,16,255), bgrx_pixel<32>(36,0,24,255), bgrx_pixel<32>(32,0,32,255), 41 | bgrx_pixel<32>(28,0,40,255), bgrx_pixel<32>(28,0,48,255), bgrx_pixel<32>(24,0,56,255), bgrx_pixel<32>(20,0,64,255), 42 | bgrx_pixel<32>(20,0,72,255), bgrx_pixel<32>(16,0,80,255), bgrx_pixel<32>(16,0,88,255), bgrx_pixel<32>(12,0,96,255), 43 | bgrx_pixel<32>(8,0,104,255), bgrx_pixel<32>(8,0,112,255), bgrx_pixel<32>(4,0,120,255), bgrx_pixel<32>(0,0,128,255), 44 | bgrx_pixel<32>(0,0,128,255), bgrx_pixel<32>(0,0,132,255), bgrx_pixel<32>(0,0,136,255), bgrx_pixel<32>(0,0,140,255), 45 | bgrx_pixel<32>(0,0,144,255), bgrx_pixel<32>(0,0,144,255), bgrx_pixel<32>(0,0,148,255), bgrx_pixel<32>(0,0,152,255), 46 | bgrx_pixel<32>(0,0,156,255), bgrx_pixel<32>(0,0,160,255), bgrx_pixel<32>(0,0,160,255), bgrx_pixel<32>(0,0,164,255), 47 | bgrx_pixel<32>(0,0,168,255), bgrx_pixel<32>(0,0,172,255), bgrx_pixel<32>(0,0,176,255), bgrx_pixel<32>(0,0,180,255), 48 | bgrx_pixel<32>(0,4,184,255), bgrx_pixel<32>(0,4,188,255), bgrx_pixel<32>(0,8,192,255), bgrx_pixel<32>(0,8,196,255), 49 | bgrx_pixel<32>(0,12,200,255), bgrx_pixel<32>(0,12,204,255), bgrx_pixel<32>(0,16,208,255), bgrx_pixel<32>(0,16,212,255), 50 | bgrx_pixel<32>(0,20,216,255), bgrx_pixel<32>(0,20,220,255), bgrx_pixel<32>(0,24,224,255), bgrx_pixel<32>(0,24,228,255), 51 | bgrx_pixel<32>(0,28,232,255), bgrx_pixel<32>(0,28,236,255), bgrx_pixel<32>(0,32,240,255), bgrx_pixel<32>(0,32,244,255), 52 | bgrx_pixel<32>(0,36,252,255), bgrx_pixel<32>(0,36,252,255), bgrx_pixel<32>(0,40,252,255), bgrx_pixel<32>(0,40,252,255), 53 | bgrx_pixel<32>(0,44,252,255), bgrx_pixel<32>(0,44,252,255), bgrx_pixel<32>(0,48,252,255), bgrx_pixel<32>(0,48,252,255), 54 | bgrx_pixel<32>(0,52,252,255), bgrx_pixel<32>(0,52,252,255), bgrx_pixel<32>(0,56,252,255), bgrx_pixel<32>(0,56,252,255), 55 | bgrx_pixel<32>(0,60,252,255), bgrx_pixel<32>(0,60,252,255), bgrx_pixel<32>(0,64,252,255), bgrx_pixel<32>(0,64,252,255), 56 | bgrx_pixel<32>(0,68,252,255), bgrx_pixel<32>(0,68,252,255), bgrx_pixel<32>(0,72,252,255), bgrx_pixel<32>(0,72,252,255), 57 | bgrx_pixel<32>(0,76,252,255), bgrx_pixel<32>(0,76,252,255), bgrx_pixel<32>(0,80,252,255), bgrx_pixel<32>(0,80,252,255), 58 | bgrx_pixel<32>(0,84,252,255), bgrx_pixel<32>(0,84,252,255), bgrx_pixel<32>(0,88,252,255), bgrx_pixel<32>(0,88,252,255), 59 | bgrx_pixel<32>(0,92,252,255), bgrx_pixel<32>(0,96,252,255), bgrx_pixel<32>(0,96,252,255), bgrx_pixel<32>(0,100,252,255), 60 | bgrx_pixel<32>(0,100,252,255), bgrx_pixel<32>(0,104,252,255), bgrx_pixel<32>(0,104,252,255), bgrx_pixel<32>(0,108,252,255), 61 | bgrx_pixel<32>(0,108,252,255), bgrx_pixel<32>(0,112,252,255), bgrx_pixel<32>(0,112,252,255), bgrx_pixel<32>(0,116,252,255), 62 | bgrx_pixel<32>(0,116,252,255), bgrx_pixel<32>(0,120,252,255), bgrx_pixel<32>(0,120,252,255), bgrx_pixel<32>(0,124,252,255), 63 | bgrx_pixel<32>(0,124,252,255), bgrx_pixel<32>(0,128,252,255), bgrx_pixel<32>(0,128,252,255), bgrx_pixel<32>(0,132,252,255), 64 | bgrx_pixel<32>(0,132,252,255), bgrx_pixel<32>(0,136,252,255), bgrx_pixel<32>(0,136,252,255), bgrx_pixel<32>(0,140,252,255), 65 | bgrx_pixel<32>(0,140,252,255), bgrx_pixel<32>(0,144,252,255), bgrx_pixel<32>(0,144,252,255), bgrx_pixel<32>(0,148,252,255), 66 | bgrx_pixel<32>(0,152,252,255), bgrx_pixel<32>(0,152,252,255), bgrx_pixel<32>(0,156,252,255), bgrx_pixel<32>(0,156,252,255), 67 | bgrx_pixel<32>(0,160,252,255), bgrx_pixel<32>(0,160,252,255), bgrx_pixel<32>(0,164,252,255), bgrx_pixel<32>(0,164,252,255), 68 | bgrx_pixel<32>(0,168,252,255), bgrx_pixel<32>(0,168,252,255), bgrx_pixel<32>(0,172,252,255), bgrx_pixel<32>(0,172,252,255), 69 | bgrx_pixel<32>(0,176,252,255), bgrx_pixel<32>(0,176,252,255), bgrx_pixel<32>(0,180,252,255), bgrx_pixel<32>(0,180,252,255), 70 | bgrx_pixel<32>(0,184,252,255), bgrx_pixel<32>(0,184,252,255), bgrx_pixel<32>(0,188,252,255), bgrx_pixel<32>(0,188,252,255), 71 | bgrx_pixel<32>(0,192,252,255), bgrx_pixel<32>(0,192,252,255), bgrx_pixel<32>(0,196,252,255), bgrx_pixel<32>(0,196,252,255), 72 | bgrx_pixel<32>(0,200,252,255), bgrx_pixel<32>(0,200,252,255), bgrx_pixel<32>(0,204,252,255), bgrx_pixel<32>(0,208,252,255), 73 | bgrx_pixel<32>(0,208,252,255), bgrx_pixel<32>(0,208,252,255), bgrx_pixel<32>(0,208,252,255), bgrx_pixel<32>(0,208,252,255), 74 | bgrx_pixel<32>(0,212,252,255), bgrx_pixel<32>(0,212,252,255), bgrx_pixel<32>(0,212,252,255), bgrx_pixel<32>(0,212,252,255), 75 | bgrx_pixel<32>(0,216,252,255), bgrx_pixel<32>(0,216,252,255), bgrx_pixel<32>(0,216,252,255), bgrx_pixel<32>(0,216,252,255), 76 | bgrx_pixel<32>(0,216,252,255), bgrx_pixel<32>(0,220,252,255), bgrx_pixel<32>(0,220,252,255), bgrx_pixel<32>(0,220,252,255), 77 | bgrx_pixel<32>(0,220,252,255), bgrx_pixel<32>(0,224,252,255), bgrx_pixel<32>(0,224,252,255), bgrx_pixel<32>(0,224,252,255), 78 | bgrx_pixel<32>(0,224,252,255), bgrx_pixel<32>(0,228,252,255), bgrx_pixel<32>(0,228,252,255), bgrx_pixel<32>(0,228,252,255), 79 | bgrx_pixel<32>(0,228,252,255), bgrx_pixel<32>(0,228,252,255), bgrx_pixel<32>(0,232,252,255), bgrx_pixel<32>(0,232,252,255), 80 | bgrx_pixel<32>(0,232,252,255), bgrx_pixel<32>(0,232,252,255), bgrx_pixel<32>(0,236,252,255), bgrx_pixel<32>(0,236,252,255), 81 | bgrx_pixel<32>(0,236,252,255), bgrx_pixel<32>(0,236,252,255), bgrx_pixel<32>(0,240,252,255), bgrx_pixel<32>(0,240,252,255), 82 | bgrx_pixel<32>(0,240,252,255), bgrx_pixel<32>(0,240,252,255), bgrx_pixel<32>(0,240,252,255), bgrx_pixel<32>(0,244,252,255), 83 | bgrx_pixel<32>(0,244,252,255), bgrx_pixel<32>(0,244,252,255), bgrx_pixel<32>(0,244,252,255), bgrx_pixel<32>(0,248,252,255), 84 | bgrx_pixel<32>(0,248,252,255), bgrx_pixel<32>(0,248,252,255), bgrx_pixel<32>(0,248,252,255), bgrx_pixel<32>(0,252,252,255), 85 | bgrx_pixel<32>(4,252,252,255), bgrx_pixel<32>(8,252,252,255), bgrx_pixel<32>(12,252,252,255), bgrx_pixel<32>(16,252,252,255), 86 | bgrx_pixel<32>(20,252,252,255), bgrx_pixel<32>(24,252,252,255), bgrx_pixel<32>(28,252,252,255), bgrx_pixel<32>(32,252,252,255), 87 | bgrx_pixel<32>(36,252,252,255), bgrx_pixel<32>(40,252,252,255), bgrx_pixel<32>(40,252,252,255), bgrx_pixel<32>(44,252,252,255), 88 | bgrx_pixel<32>(48,252,252,255), bgrx_pixel<32>(52,252,252,255), bgrx_pixel<32>(56,252,252,255), bgrx_pixel<32>(60,252,252,255), 89 | bgrx_pixel<32>(64,252,252,255), bgrx_pixel<32>(68,252,252,255), bgrx_pixel<32>(72,252,252,255), bgrx_pixel<32>(76,252,252,255), 90 | bgrx_pixel<32>(80,252,252,255), bgrx_pixel<32>(84,252,252,255), bgrx_pixel<32>(84,252,252,255), bgrx_pixel<32>(88,252,252,255), 91 | bgrx_pixel<32>(92,252,252,255), bgrx_pixel<32>(96,252,252,255), bgrx_pixel<32>(100,252,252,255), bgrx_pixel<32>(104,252,252,255), 92 | bgrx_pixel<32>(108,252,252,255), bgrx_pixel<32>(112,252,252,255), bgrx_pixel<32>(116,252,252,255), bgrx_pixel<32>(120,252,252,255), 93 | bgrx_pixel<32>(124,252,252,255), bgrx_pixel<32>(124,252,252,255), bgrx_pixel<32>(128,252,252,255), bgrx_pixel<32>(132,252,252,255), 94 | bgrx_pixel<32>(136,252,252,255), bgrx_pixel<32>(140,252,252,255), bgrx_pixel<32>(144,252,252,255), bgrx_pixel<32>(148,252,252,255), 95 | bgrx_pixel<32>(152,252,252,255), bgrx_pixel<32>(156,252,252,255), bgrx_pixel<32>(160,252,252,255), bgrx_pixel<32>(164,252,252,255), 96 | bgrx_pixel<32>(168,252,252,255), bgrx_pixel<32>(168,252,252,255), bgrx_pixel<32>(172,252,252,255), bgrx_pixel<32>(176,252,252,255), 97 | bgrx_pixel<32>(180,252,252,255), bgrx_pixel<32>(184,252,252,255), bgrx_pixel<32>(188,252,252,255), bgrx_pixel<32>(192,252,252,255), 98 | bgrx_pixel<32>(196,252,252,255), bgrx_pixel<32>(200,252,252,255), bgrx_pixel<32>(204,252,252,255), bgrx_pixel<32>(208,252,252,255), 99 | bgrx_pixel<32>(208,252,252,255), bgrx_pixel<32>(212,252,252,255), bgrx_pixel<32>(216,252,252,255), bgrx_pixel<32>(220,252,252,255), 100 | bgrx_pixel<32>(224,252,252,255), bgrx_pixel<32>(228,252,252,255), bgrx_pixel<32>(232,252,252,255), bgrx_pixel<32>(236,252,252,255), 101 | bgrx_pixel<32>(240,252,252,255), bgrx_pixel<32>(244,252,252,255), bgrx_pixel<32>(248,252,252,255), bgrx_pixel<32>(252,252,252,255) 102 | }; 103 | 104 | // downloaded from fontsquirrel.com and header generated with 105 | // https://honeythecodewitch.com/gfx/generator 106 | #define ARCHITECTS_DAUGHTER_IMPLEMENTATION 107 | #include 108 | static const open_font& text_font = architects_daughter; 109 | 110 | // declare the format of the screen 111 | using screen_t = screen>; 112 | using color_t = color; 113 | using color2_t = color; 114 | // for access to RGB8888 colors which controls use 115 | using color32_t = color>; 116 | 117 | lcd_t lcd2; 118 | touch_t touch2; 119 | // UIX allows you to use two buffers for maximum DMA efficiency 120 | // but it's not used in Winduino, which really doesn't need it. 121 | // So we declare one 64KB buffer for the transfer 122 | constexpr static const int lcd_screen_size = bitmap::sizeof_buffer({320,240}); 123 | constexpr static const int lcd_buffer_size = lcd_screen_size > 64 * 1024 ? 64 * 1024 : lcd_screen_size; 124 | static uint8_t lcd_buffer1[lcd_buffer_size]; 125 | 126 | 127 | 128 | // the main screen 129 | screen_t anim_screen({320,240}, sizeof(lcd_buffer1), lcd_buffer1, nullptr); 130 | 131 | void uix_on_touch(point16* out_locations, 132 | size_t* in_out_locations_size, 133 | void* state) { 134 | if(!*in_out_locations_size) { 135 | return; 136 | } 137 | int x,y; 138 | if(read_mouse(&x,&y)) { 139 | if(x<0) { 140 | x=0; 141 | } 142 | if(x>=320) { 143 | x=319; 144 | } 145 | if(y<0) { 146 | y=0; 147 | } 148 | if(y>=240) { 149 | y=239; 150 | } 151 | *in_out_locations_size =1; 152 | *out_locations = point16((unsigned)x,(unsigned)y); 153 | return; 154 | 155 | } 156 | *in_out_locations_size = 0; 157 | } 158 | void uix_on_flush(const rect16& bounds, const void* bmp, void* state) { 159 | flush_bitmap(bounds.x1,bounds.y1,bounds.x2-bounds.x1+1,bounds.y2-bounds.y1+1,bmp); 160 | anim_screen.flush_complete(); 161 | } 162 | 163 | using label_t = label; 164 | static label_t fps(anim_screen); 165 | static label_t summaries[] = { 166 | label_t(anim_screen), 167 | label_t(anim_screen), 168 | label_t(anim_screen), 169 | label_t(anim_screen), 170 | label_t(anim_screen), 171 | label_t(anim_screen)}; 172 | template 173 | class fire_box : public control { 174 | int draw_state = 0; 175 | constexpr static const int V_WIDTH =320 / 4; 176 | constexpr static const int V_HEIGHT =240 / 4; 177 | constexpr static const int BUF_WIDTH =320 / 4; 178 | constexpr static const int BUF_HEIGHT =240 / 4+6; 179 | 180 | uint8_t p1[BUF_HEIGHT][BUF_WIDTH]; // VGA buffer, quarter resolution w/extra lines 181 | unsigned int i, j, k, l, delta; // looping variables, counters, and data 182 | char ch; 183 | 184 | public: 185 | using control_surface_type = ControlSurfaceType; 186 | using base_type = control; 187 | using pixel_type = typename base_type::pixel_type; 188 | using palette_type = typename base_type::palette_type; 189 | fire_box(uix::invalidation_tracker& parent, const palette_type* palette = nullptr) 190 | : base_type(parent, palette) { 191 | } 192 | fire_box(fire_box&& rhs) { 193 | do_move_control(rhs); 194 | draw_state = 0; 195 | } 196 | fire_box& operator=(fire_box&& rhs) { 197 | do_move_control(rhs); 198 | draw_state = 0; 199 | return *this; 200 | } 201 | fire_box(const fire_box& rhs) { 202 | do_copy_control(rhs); 203 | draw_state = 0; 204 | } 205 | fire_box& operator=(const fire_box& rhs) { 206 | do_copy_control(rhs); 207 | draw_state = 0; 208 | return *this; 209 | } 210 | virtual void on_before_render() { 211 | switch (draw_state) { 212 | case 0: 213 | // Initialize the buffer to 0s 214 | for (i = 0; i < BUF_HEIGHT; i++) { 215 | for (j = 0; j < BUF_WIDTH; j++) { 216 | p1[i][j] = 0; 217 | } 218 | } 219 | draw_state = 1; 220 | // fall through 221 | case 1: 222 | // Transform current buffer 223 | for (i = 1; i < BUF_HEIGHT; ++i) { 224 | for (j = 0; j < BUF_WIDTH; ++j) { 225 | if (j == 0) 226 | p1[i - 1][j] = (p1[i][j] + 227 | p1[i - 1][BUF_WIDTH - 1] + 228 | p1[i][j + 1] + 229 | p1[i + 1][j]) >> 230 | 2; 231 | else if (j == 79) 232 | p1[i - 1][j] = (p1[i][j] + 233 | p1[i][j - 1] + 234 | p1[i + 1][0] + 235 | p1[i + 1][j]) >> 236 | 2; 237 | else 238 | p1[i - 1][j] = (p1[i][j] + 239 | p1[i][j - 1] + 240 | p1[i][j + 1] + 241 | p1[i + 1][j]) >> 242 | 2; 243 | 244 | if (p1[i][j] > 11) 245 | p1[i][j] = p1[i][j] - 12; 246 | else if (p1[i][j] > 3) 247 | p1[i][j] = p1[i][j] - 4; 248 | else { 249 | if (p1[i][j] > 0) p1[i][j]--; 250 | if (p1[i][j] > 0) p1[i][j]--; 251 | if (p1[i][j] > 0) p1[i][j]--; 252 | } 253 | } 254 | } 255 | delta = 0; 256 | for (j = 0; j < BUF_WIDTH; j++) { 257 | if (rand() % 10 < 5) { 258 | delta = (rand() & 1) * 255; 259 | } 260 | p1[BUF_HEIGHT - 2][j] = delta; 261 | p1[BUF_HEIGHT - 1][j] = delta; 262 | } 263 | } 264 | } 265 | virtual void on_paint(control_surface_type& destination, const srect16& clip) override { 266 | for (int y = clip.y1; y <= clip.y2; ++y) { 267 | for (int x = clip.x1; x <= clip.x2; ++x) { 268 | int i = y >> 2; 269 | int j = x >> 2; 270 | destination.point(point16(x, y), fire_cols[p1[i][j]]); 271 | } 272 | } 273 | } 274 | virtual bool on_touch(size_t locations_size, const spoint16* locations) override { 275 | fps.visible(true); 276 | return true; 277 | } 278 | virtual void on_release() override { 279 | fps.visible(false); 280 | } 281 | }; 282 | using fire_box_t = fire_box; 283 | template 284 | class alpha_box : public control { 285 | constexpr static const int horizontal_alignment = 32; 286 | constexpr static const int vertical_alignment = 32; 287 | int draw_state = 0; 288 | constexpr static const size_t count = 10; 289 | constexpr static const int16_t radius = 25; 290 | spoint16 pts[count]; // locations 291 | spoint16 dts[count]; // deltas 292 | rgba_pixel<32> cls[count]; // colors 293 | template 294 | constexpr static T h_align_up(T value) { 295 | if (value % horizontal_alignment != 0) 296 | value += (T)(horizontal_alignment - value % horizontal_alignment); 297 | return value; 298 | } 299 | template 300 | constexpr static T h_align_down(T value) { 301 | value -= value % horizontal_alignment; 302 | return value; 303 | } 304 | template 305 | constexpr static T v_align_up(T value) { 306 | if (value % vertical_alignment != 0) 307 | value += (T)(vertical_alignment - value % vertical_alignment); 308 | return value; 309 | } 310 | template 311 | constexpr static T v_align_down(T value) { 312 | value -= value % vertical_alignment; 313 | return value; 314 | } 315 | constexpr static srect16 align(const srect16& value) { 316 | int x2 = h_align_up(value.x2); 317 | if (horizontal_alignment != 1) { 318 | --x2; 319 | } 320 | int y2 = v_align_up(value.y2); 321 | if (vertical_alignment != 1) { 322 | --y2; 323 | } 324 | return srect16(h_align_down(value.x1), v_align_down(value.y1), x2, y2); 325 | } 326 | 327 | public: 328 | using control_surface_type = ControlSurfaceType; 329 | using base_type = control; 330 | using pixel_type = typename base_type::pixel_type; 331 | using palette_type = typename base_type::palette_type; 332 | alpha_box(uix::invalidation_tracker& parent, const palette_type* palette = nullptr) 333 | : base_type(parent, palette) { 334 | } 335 | alpha_box(alpha_box&& rhs) { 336 | do_move_control(rhs); 337 | draw_state = 0; 338 | } 339 | alpha_box& operator=(alpha_box&& rhs) { 340 | do_move_control(rhs); 341 | draw_state = 0; 342 | return *this; 343 | } 344 | alpha_box(const alpha_box& rhs) { 345 | do_copy_control(rhs); 346 | draw_state = 0; 347 | } 348 | alpha_box& operator=(const alpha_box& rhs) { 349 | do_copy_control(rhs); 350 | draw_state = 0; 351 | return *this; 352 | } 353 | virtual void on_before_render() { 354 | switch (draw_state) { 355 | case 0: 356 | for (size_t i = 0; i < count; ++i) { 357 | // start at the center 358 | pts[i] = spoint16(this->dimensions().width / 2, this->dimensions().height / 2); 359 | dts[i] = {0, 0}; 360 | // random deltas. Retry on (dx=0||dy=0) 361 | while (dts[i].x == 0 || dts[i].y == 0) { 362 | dts[i].x = (rand() % 5) - 2; 363 | dts[i].y = (rand() % 5) - 2; 364 | } 365 | // random color RGBA8888 366 | cls[i] = rgba_pixel<32>((rand() % 255), (rand() % 255), (rand() % 255), (rand() % 224) + 32); 367 | } 368 | draw_state = 1; 369 | // fall through 370 | } 371 | } 372 | virtual void on_paint(control_surface_type& destination, const srect16& clip) override { 373 | srect16 clip_align = align(clip); 374 | for (int y = clip_align.y1; y <= clip_align.y2; y += vertical_alignment) { 375 | for (int x = clip_align.x1; x <= clip_align.x2; x += horizontal_alignment) { 376 | rect16 r(x, y, x + horizontal_alignment, y + vertical_alignment); 377 | bool w = ((x + y) % (horizontal_alignment + vertical_alignment)); 378 | if (w) { 379 | destination.fill(r, color_t::white); 380 | } 381 | } 382 | } 383 | // draw the circles 384 | for (size_t i = 0; i < count; ++i) { 385 | spoint16& pt = pts[i]; 386 | srect16 r(pt, radius); 387 | if (clip.intersects(r)) { 388 | rgba_pixel<32>& col = cls[i]; 389 | draw::filled_ellipse(destination, r, col, &clip); 390 | } 391 | } 392 | } 393 | virtual void on_after_render() { 394 | for (size_t i = 0; i < count; ++i) { 395 | spoint16& pt = pts[i]; 396 | spoint16& d = dts[i]; 397 | // move the circle 398 | pt.x += d.x; 399 | pt.y += d.y; 400 | // if it is about to hit the edge, invert 401 | // the respective deltas 402 | if (pt.x + d.x + -radius <= 0 || pt.x + d.x + radius >= this->dimensions().bounds().x2) { 403 | d.x = -d.x; 404 | } 405 | if (pt.y + d.y + -radius <= 0 || pt.y + d.y + radius >= this->dimensions().bounds().y2) { 406 | d.y = -d.y; 407 | } 408 | } 409 | } 410 | virtual bool on_touch(size_t locations_size, const spoint16* locations) override { 411 | fps.visible(true); 412 | return true; 413 | } 414 | virtual void on_release() override { 415 | fps.visible(false); 416 | } 417 | }; 418 | using alpha_box_t = alpha_box; 419 | 420 | template 421 | class plaid_box : public control { 422 | int draw_state = 0; 423 | constexpr static const size_t count = 10; 424 | constexpr static const int16_t width = 25; 425 | spoint16 pts[count]; // locations 426 | spoint16 dts[count]; // deltas 427 | rgba_pixel<32> cls[count]; // colors 428 | 429 | public: 430 | using control_surface_type = ControlSurfaceType; 431 | using base_type = control; 432 | using pixel_type = typename base_type::pixel_type; 433 | using palette_type = typename base_type::palette_type; 434 | plaid_box(uix::invalidation_tracker& parent, const palette_type* palette = nullptr) 435 | : base_type(parent, palette) { 436 | } 437 | plaid_box(plaid_box&& rhs) { 438 | do_move_control(rhs); 439 | draw_state = 0; 440 | } 441 | plaid_box& operator=(plaid_box&& rhs) { 442 | do_move_control(rhs); 443 | draw_state = 0; 444 | return *this; 445 | } 446 | plaid_box(const plaid_box& rhs) { 447 | do_copy_control(rhs); 448 | draw_state = 0; 449 | } 450 | plaid_box& operator=(const plaid_box& rhs) { 451 | do_copy_control(rhs); 452 | draw_state = 0; 453 | return *this; 454 | } 455 | virtual void on_before_render() { 456 | switch (draw_state) { 457 | case 0: 458 | size_t i; 459 | for (i = 0; i < count; ++i) { 460 | if ((i & 1)) { 461 | pts[i] = spoint16(0, (rand() % (this->dimensions().height - width)) + width / 2); 462 | dts[i] = {0, 0}; 463 | // random deltas. Retry on (dy=0) 464 | while (dts[i].y == 0) { 465 | dts[i].y = (rand() % 5) - 2; 466 | } 467 | } else { 468 | pts[i] = spoint16((rand() % (this->dimensions().width - width)) + width / 2, 0); 469 | dts[i] = {0, 0}; 470 | // random deltas. Retry on (dx=0) 471 | while (dts[i].x == 0) { 472 | dts[i].x = (rand() % 5) - 2; 473 | } 474 | } 475 | // random color RGBA8888 476 | cls[i] = rgba_pixel<32>((rand() % 255), (rand() % 255), (rand() % 255), (rand() % 224) + 32); 477 | } 478 | draw_state = 1; 479 | // fall through 480 | } 481 | } 482 | virtual void on_after_render() { 483 | switch (draw_state) { 484 | case 0: 485 | break; 486 | case 1: 487 | for (size_t i = 0; i < count; ++i) { 488 | spoint16& pt = pts[i]; 489 | spoint16& d = dts[i]; 490 | // move the bar 491 | pt.x += d.x; 492 | pt.y += d.y; 493 | // if it is about to hit the edge, invert 494 | // the respective deltas 495 | if (pt.x + d.x + -width / 2 < 0 || pt.x + d.x + width / 2 > this->bounds().x2) { 496 | d.x = -d.x; 497 | } 498 | if (pt.y + d.y + -width / 2 < 0 || pt.y + d.y + width / 2 > this->bounds().y2) { 499 | d.y = -d.y; 500 | } 501 | } 502 | break; 503 | } 504 | } 505 | virtual void on_paint(control_surface_type& destination, const srect16& clip) override { 506 | // draw the bars 507 | for (size_t i = 0; i < count; ++i) { 508 | spoint16& pt = pts[i]; 509 | spoint16& d = dts[i]; 510 | srect16 r; 511 | if (d.y == 0) { 512 | r = srect16(pt.x - width / 2, 0, pt.x + width / 2, this->bounds().y2); 513 | } else { 514 | r = srect16(0, pt.y - width / 2, this->bounds().x2, pt.y + width / 2); 515 | } 516 | if (clip.intersects(r)) { 517 | rgba_pixel<32>& col = cls[i]; 518 | draw::filled_rectangle(destination, r, col, &clip); 519 | } 520 | } 521 | } 522 | virtual bool on_touch(size_t locations_size, const spoint16* locations) override { 523 | fps.visible(true); 524 | return true; 525 | } 526 | virtual void on_release() override { 527 | fps.visible(false); 528 | } 529 | }; 530 | using plaid_box_t = plaid_box; 531 | 532 | // the controls 533 | static fire_box_t fire(anim_screen); 534 | static alpha_box_t alpha(anim_screen); 535 | static plaid_box_t plaid(anim_screen); 536 | // initialize the screens and controls 537 | static void screen_init() { 538 | const rgba_pixel<32> transparent(0, 0, 0, 0); 539 | alpha.bounds(anim_screen.bounds()); 540 | fire.bounds(anim_screen.bounds()); 541 | fire.visible(false); 542 | plaid.bounds(anim_screen.bounds()); 543 | plaid.visible(false); 544 | fps.text_color(color32_t::red); 545 | fps.text_open_font(&text_font); 546 | fps.text_line_height(40); 547 | fps.padding({0, 0}); 548 | rgba_pixel<32> bg(0, 0, 0, 224); 549 | fps.background_color(bg); 550 | fps.text_justify(uix_justify::bottom_right); 551 | fps.bounds(srect16(0, anim_screen.bounds().y2 - fps.text_line_height() + 2, anim_screen.bounds().x2, anim_screen.bounds().y2)); 552 | fps.visible(false); 553 | int y = 0; 554 | int lh = anim_screen.dimensions().height / 6 - 2; 555 | for (size_t i = 0; i < (sizeof(summaries) / sizeof(label_t)); ++i) { 556 | label_t& summary = summaries[i]; 557 | summary.text_line_height(lh); 558 | summary.padding({0, 0}); 559 | summary.bounds(srect16(0, y, anim_screen.bounds().x2, y + lh + 2)); 560 | summary.text_open_font(&text_font); 561 | summary.border_color(transparent); 562 | summary.background_color(transparent); 563 | summary.text_color(color32_t::red); 564 | summary.text_justify(uix_justify::top_left); 565 | summary.visible(false); 566 | y += summary.text_line_height() + 2; 567 | } 568 | anim_screen.register_control(alpha); 569 | anim_screen.register_control(fire); 570 | anim_screen.register_control(plaid); 571 | anim_screen.register_control(fps); 572 | for (size_t i = 0; i < (sizeof(summaries) / sizeof(label_t)); ++i) { 573 | label_t& summary = summaries[i]; 574 | anim_screen.register_control(summary); 575 | } 576 | anim_screen.background_color(color_t::black); 577 | anim_screen.on_flush_callback(uix_on_flush); 578 | anim_screen.on_touch_callback(uix_on_touch); 579 | } 580 | void setup() { 581 | Serial.begin(115200); 582 | Serial1.begin(115200); 583 | Serial1.println("Hello world!"); 584 | // make some virtual GPIOs 585 | pinMode(17,OUTPUT); 586 | digitalWrite(17,HIGH); 587 | // attach an interrupt to 18 588 | // so when we put a new value 589 | // in winduino it causes 590 | // an interrupt 591 | attachInterrupt(18,[]() { 592 | Serial.println("Rise!"); 593 | }, RISING); 594 | 595 | // init the UI screen 596 | screen_init(); 597 | #ifdef WINDUINO 598 | // finally we can initialize 599 | // the screen and touch 600 | if(!lcd2.initialize()) { 601 | Serial.println("Could not find the ST7789"); 602 | } 603 | 604 | if(touch2.initialize()==false) { 605 | Serial.println("Could not find FT6236"); 606 | } 607 | 608 | // draw our screen 609 | open_text_info oti; 610 | oti.font = &text_font; 611 | oti.scale = oti.font->scale(50); 612 | oti.text = "Hello world!"; 613 | 614 | // reading SPI displays is slow so we don't. In fact, bitmaps are really the best way to draw 615 | // create a bitmap with the same underlying format as the display 616 | auto bmp = create_bitmap_from(lcd2,lcd2.dimensions()); 617 | if(bmp.begin()) { // make sure not out of memory 618 | bmp.fill(bmp.bounds(),color2_t::orange); 619 | draw::text(bmp,bmp.bounds(),oti,color2_t::wheat); 620 | draw::bitmap(lcd2,lcd2.bounds(),bmp,bmp.bounds()); 621 | free(bmp.begin()); // free it when we're done 622 | } 623 | #endif 624 | } 625 | void loop() { 626 | constexpr static const int run_seconds = 5; 627 | constexpr static const int summary_seconds = 10; 628 | static char szsummaries[sizeof(summaries) / sizeof(label_t)][128]; 629 | static int seconds = 0; 630 | static int frames = 0; 631 | static int total_frames_alpha = 0; 632 | static int total_frames_fire = 0; 633 | static int total_frames_plaid = 0; 634 | static int fps_alpha[run_seconds]; 635 | static int fps_fire[run_seconds]; 636 | static int fps_plaid[run_seconds]; 637 | static bool showed_summary = false; 638 | static int fps_index = 0; 639 | static char szfps[32]; 640 | static uint32_t fps_ts = 0; 641 | uint32_t ms = millis(); 642 | static bool toggle = true; 643 | ++frames; 644 | 645 | if (ms > fps_ts + 1000) { 646 | fps_ts = ms; 647 | snprintf(szfps, sizeof(szfps), "fps: %d", frames); 648 | Serial.println(szfps); 649 | fps.text(szfps); 650 | if (alpha.visible()) { 651 | fps_alpha[fps_index++] = frames; 652 | } else if (fire.visible()) { 653 | fps_fire[fps_index++] = frames; 654 | } else if (plaid.visible()) { 655 | fps_plaid[fps_index++] = frames; 656 | } 657 | frames = 0; 658 | ++seconds; 659 | // toggle every second 660 | digitalWrite(17,toggle?HIGH:LOW); 661 | toggle=!toggle; 662 | 663 | } 664 | if (alpha.visible()) { 665 | alpha.invalidate(); 666 | ++total_frames_alpha; 667 | } else if (fire.visible()) { 668 | fire.invalidate(); 669 | ++total_frames_fire; 670 | } else if (plaid.visible()) { 671 | plaid.invalidate(); 672 | ++total_frames_plaid; 673 | } 674 | if (seconds == (run_seconds * 1)) { 675 | alpha.visible(false); 676 | fire.visible(true); 677 | plaid.visible(false); 678 | for (size_t i = 0; i < (sizeof(summaries) / sizeof(label_t)); ++i) { 679 | label_t& summary = summaries[i]; 680 | summary.visible(false); 681 | } 682 | fps_index = 0; 683 | } else if (seconds == (run_seconds * 2)) { 684 | alpha.visible(false); 685 | fire.visible(false); 686 | plaid.visible(true); 687 | for (size_t i = 0; i < (sizeof(summaries) / sizeof(label_t)); ++i) { 688 | label_t& summary = summaries[i]; 689 | summary.visible(false); 690 | } 691 | fps_index = 0; 692 | } else if (seconds == (run_seconds * 3)) { 693 | if (!showed_summary) { 694 | showed_summary = true; 695 | alpha.visible(false); 696 | fire.visible(false); 697 | plaid.visible(false); 698 | for (size_t i = 0; i < (sizeof(summaries) / sizeof(label_t)); ++i) { 699 | label_t& summary = summaries[i]; 700 | summary.visible(true); 701 | } 702 | int alpha_fps_max = 0, alpha_fps_sum = 0; 703 | for (size_t i = 0; i < run_seconds; ++i) { 704 | int fps = fps_alpha[i]; 705 | if (fps > alpha_fps_max) { 706 | alpha_fps_max = fps; 707 | } 708 | alpha_fps_sum += fps; 709 | } 710 | int fire_fps_max = 0, fire_fps_sum = 0; 711 | for (size_t i = 0; i < run_seconds; ++i) { 712 | int fps = fps_fire[i]; 713 | if (fps > fire_fps_max) { 714 | fire_fps_max = fps; 715 | } 716 | fire_fps_sum += fps; 717 | } 718 | int plaid_fps_max = 0, plaid_fps_sum = 0; 719 | for (size_t i = 0; i < run_seconds; ++i) { 720 | int fps = fps_plaid[i]; 721 | if (fps > plaid_fps_max) { 722 | plaid_fps_max = fps; 723 | } 724 | plaid_fps_sum += fps; 725 | } 726 | strcpy(szsummaries[0], "alpha max/avg/frames"); 727 | Serial.println(szsummaries[0]); 728 | summaries[0].text(szsummaries[0]); 729 | sprintf(szsummaries[1], "%d/%d/%d", alpha_fps_max, 730 | (int)roundf((float)alpha_fps_sum / (float)run_seconds), 731 | total_frames_alpha); 732 | Serial.println(szsummaries[1]); 733 | summaries[1].text(szsummaries[1]); 734 | strcpy(szsummaries[2], "fire max/avg/frames"); 735 | Serial.println(szsummaries[2]); 736 | summaries[2].text(szsummaries[2]); 737 | sprintf(szsummaries[3], "%d/%d/%d", fire_fps_max, 738 | (int)roundf((float)fire_fps_sum / (float)run_seconds), 739 | total_frames_fire); 740 | summaries[3].text(szsummaries[3]); 741 | Serial.println(szsummaries[3]); 742 | strcpy(szsummaries[4], "plaid max/avg/frames"); 743 | summaries[4].text(szsummaries[4]); 744 | Serial.println(szsummaries[4]); 745 | sprintf(szsummaries[5], "%d/%d/%d", plaid_fps_max, 746 | (int)roundf((float)plaid_fps_sum / (float)run_seconds), 747 | total_frames_plaid); 748 | Serial.println(szsummaries[5]); 749 | summaries[5].text(szsummaries[5]); 750 | fps_index = 0; 751 | } 752 | } else if (seconds >= (run_seconds * 3) + summary_seconds) { 753 | seconds = 0; 754 | total_frames_alpha = 0; 755 | total_frames_fire = 0; 756 | total_frames_plaid = 0; 757 | alpha.visible(true); 758 | fire.visible(false); 759 | plaid.visible(false); 760 | for (size_t i = 0; i < (sizeof(summaries) / sizeof(label_t)); ++i) { 761 | label_t& summary = summaries[i]; 762 | summary.visible(false); 763 | } 764 | fps_index = 0; 765 | showed_summary = false; 766 | } 767 | #ifdef WINDUINO 768 | // finger paint 769 | if(touch2.update()) { 770 | if(touch2.touches()) { 771 | uint16_t x,y; 772 | touch2.xy(&x,&y); 773 | draw::filled_ellipse(lcd2,rect16(point16(x,y),5),color2_t::purple); 774 | } 775 | } 776 | #endif 777 | anim_screen.update(); 778 | char buf[1024]; 779 | while(Serial1.available()) { 780 | Serial1.read(buf,sizeof(buf)); 781 | Serial.write(buf); 782 | } 783 | } 784 | // the following code runs before setup() only when executed in Winduino 785 | #ifdef WINDUINO 786 | void winduino() { 787 | log_print("hello from winduino()\r\n"); 788 | //hardware_set_screen_size(480,320); 789 | 790 | // load the SPI screen 791 | void* hw_screen = hardware_load(LIB_SPI_SCREEN); 792 | if(hw_screen==nullptr) { 793 | log_print("Unable to load external SPI screen\r\n"); 794 | } 795 | // attach the log (log all messages, including debug) 796 | hardware_attach_log(hw_screen,"[scr]",255); 797 | // set the resolution 798 | struct { 799 | uint16_t width; 800 | uint16_t height; 801 | } screen_size = {240,135}; 802 | if(!hardware_configure(hw_screen,SPI_SCREEN_PROP_RESOLUTION,&screen_size,sizeof(screen_size))) { 803 | log_print("Unable to configure hardware\r\n"); 804 | } 805 | // set the panel offsets 806 | struct { 807 | int16_t x; 808 | int16_t y; 809 | } screen_offsets = {40,53}; 810 | if(!hardware_configure(hw_screen,SPI_SCREEN_PROP_OFFSETS,&screen_offsets,sizeof(screen_offsets))) { 811 | log_print("Unable to configure hardware\r\n"); 812 | } 813 | // set the screen GPIOs (aside from SPI which are fixed and virtual) 814 | hardware_set_pin(hw_screen,15, SPI_SCREEN_PIN_BKL); 815 | hardware_set_pin(hw_screen,5, SPI_SCREEN_PIN_CS); 816 | hardware_set_pin(hw_screen,2,SPI_SCREEN_PIN_DC); 817 | hardware_set_pin(hw_screen,4,SPI_SCREEN_PIN_RST); 818 | // attach the SPI bus (for the screen) 819 | hardware_attach_spi(hw_screen,1); 820 | // attach the I2C bus (for touch) 821 | hardware_attach_i2c(hw_screen,0); 822 | // attach Serial1 to COM25 823 | //hardware_attach_serial(1,25); 824 | 825 | } 826 | #endif -------------------------------------------------------------------------------- /winduino_hardware/spi_screen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.24) 2 | project(winduino_spi_screen VERSION 1.0) 3 | set(CMAKE_CXX_STANDARD 17) 4 | set(CMAKE_CXX_STANDARD_REQUIRED True) 5 | set(FIND_LIBRARY_USE_LIB64_PATHS True) 6 | set( LIBS d2d1 htcw_winduino) 7 | set(CMAKE_STATIC_LIBRARY_PREFIX "") 8 | set(CMAKE_SHARED_LIBRARY_PREFIX "") 9 | 10 | add_library(winduino_spi_screen SHARED 11 | spi_screen.cpp) 12 | target_link_libraries(winduino_spi_screen ${LIBS} ) 13 | target_include_directories(winduino_spi_screen PUBLIC 14 | "${PROJECT_SOURCE_DIR}" 15 | "${PROJECT_SOURCE_DIR}/include" 16 | "${PROJECT_BINARY_DIR}" 17 | ) 18 | 19 | target_link_libraries(winduino_spi_screen ${LIBS} ) 20 | target_include_directories(winduino_spi_screen PRIVATE 21 | "${PROJECT_SOURCE_DIR}" 22 | "${PROJECT_BINARY_DIR}" 23 | ) 24 | 25 | -------------------------------------------------------------------------------- /winduino_hardware/spi_screen/spi_screen.cpp: -------------------------------------------------------------------------------- 1 | 2 | #define UNICODE 3 | #if defined(UNICODE) && !defined(_UNICODE) 4 | #define _UNICODE 5 | #elif defined(_UNICODE) && !defined(UNICODE) 6 | #define UNICODE 7 | #endif 8 | 9 | #include "spi_screen.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "spi_screen_lib.h" 18 | 19 | #pragma comment(lib, "d2d1.lib") 20 | 21 | static HMODULE hInstance = NULL; 22 | void spi_screen::initialize() { 23 | screen_size= {320, 240}; 24 | screen_offsets= {0,0}; 25 | screen_st = SCREEN_STATE_INITIAL; 26 | touch_address = 0x38; 27 | touch_factory_mode = false; 28 | touch_current_reg = -1; 29 | touch_threshhold = 0x80; 30 | bkl_low = false; 31 | colset= TFT_CASET; 32 | rowset= TFT_PASET; 33 | write= TFT_RAMWR; 34 | read = TFT_RAMRD; 35 | col_start=0; 36 | col_end=0; 37 | row_start=0; 38 | row_end=0; 39 | int cmd= -1; 40 | uint8_t in_byte = 0; 41 | int bit_count = 0; 42 | frame_buffer=NULL; 43 | data_word = 0; 44 | bytes_written=0; 45 | bytes_read=0; 46 | column=0; 47 | row=0; 48 | offset=0; 49 | can_configure = true; 50 | render_mutex = NULL; 51 | render_thread= NULL; 52 | quit_event= NULL; 53 | reset_state = false; 54 | screen_ready = NULL; 55 | touch_mutex = NULL; 56 | logger = NULL; 57 | in_pixel_transfer = false; 58 | render_changed = 1; 59 | } 60 | void spi_screen::logfmt(uint8_t level,const char* format, ...) { 61 | if (logger == nullptr) { 62 | return; 63 | } 64 | if(level>log_level) { 65 | return; 66 | } 67 | char loc_buf[1024]; 68 | char* temp = loc_buf; 69 | if(log_prefix!=nullptr) { 70 | strcpy(temp,log_prefix); 71 | strcpy(temp+strlen(temp),": "); 72 | } else { 73 | *temp=0; 74 | } 75 | va_list arg; 76 | va_list copy; 77 | va_start(arg, format); 78 | va_copy(copy, arg); 79 | int len = vsnprintf(temp+strlen(temp), sizeof(loc_buf), format, copy); 80 | va_end(copy); 81 | if (len < 0) { 82 | va_end(arg); 83 | return; 84 | } 85 | if (len >= (int)sizeof(loc_buf)) { // comparation of same sign type for the compiler 86 | temp = (char*)malloc(len + 1); 87 | if (temp == NULL) { 88 | va_end(arg); 89 | return; 90 | } 91 | len = vsnprintf(temp, len + 1, format, arg); 92 | } 93 | va_end(arg); 94 | logger(temp); 95 | if (temp != loc_buf) { 96 | free(temp); 97 | } 98 | return; 99 | } 100 | // updates the window title with the FPS and any mouse info 101 | void spi_screen::update_title(HWND hwnd) { 102 | wchar_t wsztitle[64]; 103 | uint16_t f; 104 | wcscpy(wsztitle, L"SPI Screen"); 105 | if(WAIT_OBJECT_0==WaitForSingleObject(touch_mutex,INFINITE)) { 106 | if (mouse_state) { 107 | wcscat(wsztitle, L" ("); 108 | f = mouse_loc.x; 109 | _itow((int)f, wsztitle + wcslen(wsztitle), 10); 110 | wcscat(wsztitle, L", "); 111 | f = mouse_loc.y; 112 | _itow((int)f, wsztitle + wcslen(wsztitle), 10); 113 | wcscat(wsztitle, L")"); 114 | } 115 | ReleaseMutex(touch_mutex); 116 | SetWindowTextW(hwnd, wsztitle); 117 | } 118 | 119 | } 120 | LRESULT spi_screen::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 121 | spi_screen* st = (spi_screen*)GetWindowLongPtrW(hWnd,GWLP_USERDATA); 122 | if(st!=NULL) { 123 | if (uMsg == WM_CLOSE) { 124 | SetEvent(st->quit_event); 125 | } 126 | } 127 | return DefWindowProcW(hWnd, uMsg, wParam, lParam); 128 | } 129 | 130 | DWORD spi_screen::window_thread_proc(void* state) { 131 | spi_screen* st = (spi_screen*)state; 132 | HRESULT hr; 133 | ResetEvent(st->screen_ready); 134 | 135 | if (st->frame_buffer == nullptr) { 136 | st->frame_buffer = (uint8_t*)malloc(4 * st->screen_size.width * st->screen_size.height); 137 | if (st->frame_buffer == nullptr) { 138 | return 1; 139 | } 140 | } 141 | if(st->touch_mutex==nullptr) { 142 | st->touch_mutex = CreateMutex(NULL, FALSE, NULL); 143 | if(st->touch_mutex==NULL) { 144 | return 1; 145 | } 146 | } 147 | if (st->render_mutex == nullptr) { 148 | if (!st->created_wndcls) { 149 | WNDCLASSW wc; 150 | wc.style = CS_HREDRAW | CS_VREDRAW; 151 | wc.lpfnWndProc = WindowProc; 152 | wc.cbClsExtra = 0; 153 | wc.cbWndExtra = 0; 154 | wc.hInstance = hInstance; // GetModuleHandle(NULL); 155 | wc.hbrBackground = NULL; 156 | wc.lpszMenuName = NULL; 157 | wc.hIcon = NULL; 158 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 159 | wc.lpszClassName = L"spi_screen"; 160 | RegisterClassW(&wc); 161 | st->created_wndcls = true; 162 | } 163 | if (st->quit_event == NULL) { 164 | // for signalling when to exit 165 | st->quit_event = CreateEventW( 166 | NULL, // default security attributes 167 | TRUE, // manual-reset event 168 | FALSE, // initial state is nonsignaled 169 | L"QuitEvent" // object name 170 | ); 171 | if (st->quit_event == NULL) { 172 | return 1; 173 | } 174 | } 175 | if (st->d2d_factory == nullptr) { 176 | // start DirectX 177 | hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &st->d2d_factory); 178 | if (!SUCCEEDED(hr)) { 179 | return 1; 180 | } 181 | } 182 | RECT r = {0, 0, st->screen_size.width, st->screen_size.height}; 183 | AdjustWindowRectEx(&r, WS_CAPTION | WS_BORDER, FALSE, WS_EX_TOOLWINDOW); 184 | st->hwnd_screen = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_APPWINDOW, L"spi_screen", L"SPI Screen", WS_CAPTION | WS_BORDER, CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, NULL, NULL, /* GetModuleHandleW(NULL)*/ hInstance, NULL); 185 | if (st->hwnd_screen == nullptr) { 186 | return 1; 187 | } 188 | SetWindowLongPtrW(st->hwnd_screen,GWLP_USERDATA,(LONG_PTR)st); 189 | if (st->render_target == nullptr) { 190 | RECT rc; 191 | GetClientRect(st->hwnd_screen, &rc); 192 | D2D1_SIZE_U size = D2D1::SizeU( 193 | (rc.right - rc.left), 194 | rc.bottom - rc.top); 195 | 196 | hr = st->d2d_factory->CreateHwndRenderTarget( 197 | D2D1::RenderTargetProperties(), 198 | D2D1::HwndRenderTargetProperties(st->hwnd_screen, size), 199 | &st->render_target); 200 | 201 | if (!SUCCEEDED(hr)) { 202 | return 1; 203 | } 204 | } 205 | if (st->render_bitmap == nullptr) { 206 | // initialize the render bitmap 207 | D2D1_SIZE_U size = {0}; 208 | D2D1_BITMAP_PROPERTIES props; 209 | st->render_target->GetDpi(&props.dpiX, &props.dpiY); 210 | D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat( 211 | DXGI_FORMAT_B8G8R8A8_UNORM, 212 | D2D1_ALPHA_MODE_IGNORE); 213 | props.pixelFormat = pixelFormat; 214 | size.width = st->screen_size.width; 215 | size.height = st->screen_size.height; 216 | 217 | hr = st->render_target->CreateBitmap(size, 218 | props, 219 | &st->render_bitmap); 220 | if (!SUCCEEDED(hr)) { 221 | return 1; 222 | } 223 | } 224 | if (st->render_mutex == NULL) { 225 | st->render_mutex = CreateMutexW(NULL, FALSE, NULL); 226 | if (st->render_mutex == NULL) { 227 | return 1; 228 | } 229 | } 230 | if (st->render_thread == NULL) { 231 | st->render_thread = CreateThread(NULL, 4000, render_thread_proc,st, 0, NULL); 232 | if (st->render_thread == NULL) { 233 | return 1; 234 | } 235 | } 236 | ShowWindow(st->hwnd_screen, SW_SHOW); 237 | UpdateWindow(st->hwnd_screen); 238 | } 239 | SetEvent(st->screen_ready); 240 | bool quit = false; 241 | while (!quit) { 242 | DWORD result = 0; 243 | MSG msg = {0}; 244 | if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 245 | if (msg.message == WM_QUIT) { 246 | quit = true; 247 | break; 248 | } 249 | // handle out of band 250 | // window messages here 251 | if (msg.message == WM_LBUTTONDOWN) { 252 | if(LOWORD(msg.lParam)screen_size.width && 253 | HIWORD(msg.lParam)screen_size.height) { 254 | SetCapture(msg.hwnd); 255 | 256 | if (WAIT_OBJECT_0 == WaitForSingleObject( 257 | st->touch_mutex, // handle to mutex 258 | INFINITE)) { // no time-out interval) 259 | st->old_mouse_state = st->mouse_state; 260 | st->mouse_state = 1; 261 | st->mouse_loc.x = LOWORD(msg.lParam); 262 | if(st->mouse_loc.x<0 || (st->mouse_loc.x & 0x8000)) st->mouse_loc.x=0; 263 | if(st->mouse_loc.x>=st->screen_size.width) st->mouse_loc.x = st->screen_size.width-1; 264 | st->mouse_loc.y = HIWORD(msg.lParam); 265 | if(st->mouse_loc.y<0 || (st->mouse_loc.y & 0x8000)) st->mouse_loc.y=0; 266 | if(st->mouse_loc.y>=st->screen_size.height) st->mouse_loc.y = st->screen_size.height-1; 267 | st->mouse_req = 1; 268 | ReleaseMutex(st->touch_mutex); 269 | } 270 | st->update_title(msg.hwnd); 271 | } 272 | } 273 | if (msg.message == WM_MOUSEMOVE) { 274 | if (WAIT_OBJECT_0 == WaitForSingleObject( 275 | st->touch_mutex, // handle to mutex 276 | INFINITE)) { // no time-out interval) 277 | if (st->mouse_state == 1 && MK_LBUTTON == msg.wParam) { 278 | st->mouse_req = 1; 279 | st->mouse_loc.x = LOWORD(msg.lParam); 280 | if(st->mouse_loc.x<0 || (st->mouse_loc.x & 0x8000)) st->mouse_loc.x=0; 281 | if(st->mouse_loc.x>=st->screen_size.width) st->mouse_loc.x = st->screen_size.width-1; 282 | st->mouse_loc.y = HIWORD(msg.lParam); 283 | if(st->mouse_loc.y<0 || (st->mouse_loc.y & 0x8000)) st->mouse_loc.y=0; 284 | if(st->mouse_loc.y>=st->screen_size.height) st->mouse_loc.y = st->screen_size.height-1; 285 | } 286 | ReleaseMutex(st->touch_mutex); 287 | } 288 | st->update_title(msg.hwnd); 289 | } 290 | if (msg.message == WM_LBUTTONUP) { 291 | ReleaseCapture(); 292 | if (WAIT_OBJECT_0 == WaitForSingleObject( 293 | st->touch_mutex, // handle to mutex 294 | INFINITE)) { // no time-out interval) 295 | 296 | st->old_mouse_state = st->mouse_state; 297 | st->mouse_req = 1; 298 | st->mouse_state = 0; 299 | st->mouse_loc.x = LOWORD(msg.lParam); 300 | if(st->mouse_loc.x<0 || (st->mouse_loc.x & 0x8000)) st->mouse_loc.x=0; 301 | if(st->mouse_loc.x>=st->screen_size.width) st->mouse_loc.x = st->screen_size.width-1; 302 | st->mouse_loc.y = HIWORD(msg.lParam); 303 | if(st->mouse_loc.y<0 || (st->mouse_loc.y & 0x8000)) st->mouse_loc.y=0; 304 | if(st->mouse_loc.y>=st->screen_size.height) st->mouse_loc.y = st->screen_size.height-1; 305 | ReleaseMutex(st->touch_mutex); 306 | } 307 | st->update_title(msg.hwnd); 308 | } 309 | TranslateMessage(&msg); 310 | DispatchMessage(&msg); 311 | } 312 | if (WAIT_OBJECT_0 == WaitForSingleObject(st->quit_event, 0)) { 313 | quit = true; 314 | } 315 | } 316 | ResetEvent(st->screen_ready); 317 | if (st->quit_event != NULL) { 318 | SetEvent(st->quit_event); 319 | } 320 | if (st->render_thread != NULL) { 321 | CloseHandle(st->render_thread); 322 | } 323 | if (st->render_bitmap != nullptr) { 324 | st->render_bitmap->Release(); 325 | st->render_bitmap = nullptr; 326 | } 327 | if (st->render_target != nullptr) { 328 | st->render_target->Release(); 329 | st->render_target = nullptr; 330 | } 331 | if (st->hwnd_screen != NULL) { 332 | DestroyWindow(st->hwnd_screen); 333 | st->hwnd_screen = NULL; 334 | } 335 | if (st->render_mutex != NULL) { 336 | CloseHandle(st->render_mutex); 337 | st->render_mutex = NULL; 338 | } 339 | if (st->quit_event != NULL) { 340 | CloseHandle(st->quit_event); 341 | st->quit_event = NULL; 342 | } 343 | return 0; 344 | } 345 | uint8_t spi_screen::process_byte_spi(uint8_t val) { 346 | if (can_configure) return val; 347 | if (dc.value()) { 348 | if (screen_st == SCREEN_STATE_INITIAL) { 349 | return 0; 350 | } 351 | int x, y; 352 | uint8_t* p; 353 | switch (screen_st) { 354 | case SCREEN_STATE_WRITE: 355 | switch (offset) { 356 | case 0: 357 | data_word = val << 8; 358 | offset = 1; 359 | ++bytes_written; 360 | break; 361 | case 1: 362 | 363 | data_word |= val; 364 | x = column - screen_offsets.x; 365 | y = row - screen_offsets.y; 366 | if (in_pixel_transfer && x >= 0 && x < screen_size.width && y >= 0 && y < screen_size.height) { 367 | uint8_t r = ((float)((data_word >> 11) & 31) / 31.0f) * 255; 368 | uint8_t g = ((float)((data_word >> 5) & 63) / 63.0f) * 255; 369 | uint8_t b = ((float)((data_word >> 0) & 31) / 31.0f) * 255; 370 | 371 | uint8_t* p = frame_buffer + ((x + screen_size.width * y) * 4); 372 | *p++ = b; 373 | *p++ = g; 374 | *p++ = r; 375 | *p = 0xFF; 376 | InterlockedExchange(&render_changed, 1); 377 | } 378 | ++column; 379 | if (column > col_end) { 380 | ++row; 381 | column = col_start; 382 | if (row > row_end) { 383 | if (in_pixel_transfer) { 384 | ReleaseMutex(render_mutex); 385 | } 386 | in_pixel_transfer = false; 387 | screen_st = SCREEN_STATE_IGNORING; 388 | break; 389 | } 390 | } 391 | ++bytes_written; 392 | offset = 0; 393 | if (bytes_written == sizeof(uint16_t) * (row_end - row_start + 1) * (col_start - col_end + 1)) { 394 | if (in_pixel_transfer) { 395 | ReleaseMutex(render_mutex); 396 | } 397 | in_pixel_transfer = false; 398 | screen_st = SCREEN_STATE_IGNORING; 399 | } 400 | 401 | break; 402 | } 403 | 404 | break; 405 | case SCREEN_STATE_READ1: 406 | // toss this byte. quirk of the hardware 407 | screen_st = SCREEN_STATE_READ2; 408 | bytes_read = 0; 409 | break; 410 | case SCREEN_STATE_READ2: 411 | ++bytes_read; 412 | val = 0; 413 | x = column - screen_offsets.x; 414 | y = row - screen_offsets.y; 415 | 416 | if (in_pixel_transfer && x >= 0 && x < screen_size.width && y >= 0 && y < screen_size.height) { 417 | switch (offset) { 418 | case 0: 419 | p = frame_buffer + ((x + screen_size.width * y) * 4) + 2; 420 | val = (*p) & 0xFC; 421 | offset = 1; 422 | 423 | break; 424 | case 1: 425 | p = frame_buffer + ((x + screen_size.width * y) * 4) + 1; 426 | val = (*p) & 0xFC; 427 | offset = 2; 428 | break; 429 | case 2: 430 | p = frame_buffer + ((x + screen_size.width * y) * 4) + 0; 431 | val = (*p) & 0xFC; 432 | offset = 0; 433 | break; 434 | default: 435 | break; 436 | } 437 | } 438 | if (bytes_read == sizeof(uint16_t) * (row_end - row_start + 1) * (col_start - col_end + 1)) { 439 | if (in_pixel_transfer) { 440 | ReleaseMutex(render_mutex); 441 | } 442 | in_pixel_transfer = false; 443 | screen_st = SCREEN_STATE_IGNORING; 444 | } 445 | break; 446 | case SCREEN_STATE_COLSET1: 447 | if (offset == 0) { 448 | data_word = val << 8; 449 | offset = 1; 450 | } else { 451 | data_word |= val; 452 | offset = 0; 453 | col_start = data_word; 454 | screen_st = SCREEN_STATE_COLSET2; 455 | } 456 | break; 457 | case SCREEN_STATE_COLSET2: 458 | if (offset == 0) { 459 | data_word = val << 8; 460 | offset = 1; 461 | } else { 462 | data_word |= val; 463 | offset = 0; 464 | col_end = data_word; 465 | screen_st = SCREEN_STATE_IGNORING; 466 | // logfmt("col_start: %d, col_end: %d",col_start,col_end); 467 | } 468 | break; 469 | case SCREEN_STATE_ROWSET1: 470 | if (offset == 0) { 471 | data_word = val << 8; 472 | offset = 1; 473 | } else { 474 | data_word |= val; 475 | offset = 0; 476 | row_start = data_word; 477 | screen_st = SCREEN_STATE_ROWSET2; 478 | } 479 | break; 480 | case SCREEN_STATE_ROWSET2: 481 | if (offset == 0) { 482 | data_word = val << 8; 483 | offset = 1; 484 | } else { 485 | data_word |= val; 486 | offset = 0; 487 | row_end = data_word; 488 | screen_st = SCREEN_STATE_IGNORING; 489 | // logfmt("row_start: %d, row_end: %d",row_start,row_end); 490 | } 491 | break; 492 | } 493 | } else { 494 | DWORD wr; 495 | bytes_written = 0; 496 | bytes_read = 0; 497 | cmd = val; 498 | if (cmd == colset) { 499 | if (in_pixel_transfer) { 500 | ReleaseMutex(render_mutex); 501 | } 502 | in_pixel_transfer = false; 503 | data_word = 0; 504 | offset = 0; 505 | screen_st = SCREEN_STATE_COLSET1; 506 | } else if (cmd == rowset) { 507 | if (in_pixel_transfer) { 508 | ReleaseMutex(render_mutex); 509 | } 510 | in_pixel_transfer = false; 511 | data_word = 0; 512 | offset = 0; 513 | screen_st = SCREEN_STATE_ROWSET1; 514 | } else if (cmd == write) { 515 | if (in_pixel_transfer) { 516 | ReleaseMutex(render_mutex); 517 | } 518 | offset = 0; 519 | data_word = 0; 520 | column = col_start; 521 | row = row_start; 522 | in_pixel_transfer = false; 523 | wr = WaitForSingleObject(screen_ready, INFINITE); 524 | if (WAIT_OBJECT_0 == wr) { // no time-out interval) 525 | wr = WaitForSingleObject( 526 | render_mutex, // handle to mutex 527 | INFINITE); 528 | if (WAIT_OBJECT_0 == wr) { // no time-out interval) 529 | in_pixel_transfer = true; 530 | screen_st = SCREEN_STATE_WRITE; 531 | } else { 532 | in_pixel_transfer = false; 533 | } 534 | } else { 535 | logfmt(1,"Error writing pixels (unable to wait screen ready): %x", GetLastError()); 536 | } 537 | } else if (cmd == read) { 538 | if (in_pixel_transfer) { 539 | ReleaseMutex(render_mutex); 540 | } 541 | offset = 0; 542 | data_word = 0; 543 | column = col_start; 544 | row = row_start; 545 | in_pixel_transfer = false; 546 | wr = WaitForSingleObject(screen_ready, INFINITE); 547 | if (WAIT_OBJECT_0 == wr) { // no time-out interval) 548 | wr = WaitForSingleObject( 549 | render_mutex, // handle to mutex 550 | INFINITE); 551 | if (WAIT_OBJECT_0 == wr) { // no time-out interval) 552 | in_pixel_transfer = true; 553 | screen_st = SCREEN_STATE_READ1; 554 | } else { 555 | in_pixel_transfer = false; 556 | } 557 | } 558 | } else { 559 | if (in_pixel_transfer) { 560 | ReleaseMutex(render_mutex); 561 | } 562 | in_pixel_transfer = false; 563 | screen_st = SCREEN_STATE_IGNORING; 564 | } 565 | } 566 | return val; 567 | } 568 | unsigned long __stdcall spi_screen::render_thread_proc(void* state) { 569 | spi_screen* st = (spi_screen*)state; 570 | bool quit = false; 571 | while (!quit) { 572 | if (0 != st->render_changed && st->frame_buffer && st->render_target && st->render_bitmap) { 573 | if (WAIT_OBJECT_0 == WaitForSingleObject( 574 | st->render_mutex, // handle to mutex 575 | INFINITE)) { // no time-out interval) 576 | st->render_bitmap->CopyFromMemory(NULL, st->frame_buffer, 4 * st->screen_size.width); 577 | st->render_target->BeginDraw(); 578 | D2D1_RECT_F rect_dest = { 579 | 0, 580 | 0, 581 | (float)st->screen_size.width, 582 | (float)st->screen_size.height}; 583 | st->render_target->DrawBitmap(st->render_bitmap, 584 | rect_dest, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, NULL); 585 | st->render_target->EndDraw(); 586 | ReleaseMutex(st->render_mutex); 587 | InterlockedExchange(&st->render_changed, 0); 588 | } 589 | } 590 | if (WAIT_OBJECT_0 == WaitForSingleObject(st->quit_event, 0)) { 591 | quit = true; 592 | } 593 | } 594 | return 0; 595 | } 596 | void spi_screen::on_rst_changed(void* state) { 597 | spi_screen* st = (spi_screen*)state; 598 | if (st->reset_state == 2 && !st->rst.value()) { 599 | st->reset_state = 0; 600 | if (st->quit_event != NULL) { 601 | SetEvent(st->quit_event); 602 | } 603 | } 604 | if (st->rst.value()) { 605 | ++st->reset_state; 606 | } 607 | if (st->reset_state == 2) { 608 | HRESULT hr; 609 | if (st->screen_ready == NULL) { 610 | st->screen_ready = CreateEventW( 611 | NULL, // default security attributes 612 | TRUE, // manual-reset event 613 | FALSE, // initial state is nonsignaled 614 | NULL // object name 615 | ); 616 | if (st->screen_ready == NULL) { 617 | st->logfmt(1,"Unable to create screen_ready handle"); 618 | } 619 | } 620 | if (st->quit_event == NULL) { 621 | CreateThread(NULL, 4000, window_thread_proc, st, 0, NULL); 622 | } 623 | } 624 | } 625 | int CALL spi_screen::CanConfigure() { 626 | return 1; 627 | } 628 | int CALL spi_screen::Configure(int prop, void* data, size_t size) { 629 | if (!can_configure) { 630 | logfmt(1,"Invalid state for configuration"); 631 | return 2; 632 | } 633 | switch (prop) { 634 | case SPI_SCREEN_PROP_RESOLUTION: 635 | if (size == sizeof(screen_size)) { 636 | memcpy(&screen_size, data, size); 637 | logfmt(3,"Resolution set to %dx%d",screen_size.width,screen_size.height); 638 | return 0; 639 | } 640 | break; 641 | case SPI_SCREEN_PROP_BKL_LOW: 642 | if (size = sizeof(int)) { 643 | int v = *((int*)data); 644 | bkl_low = (v != 0); 645 | logfmt(3,"Backlight set on = %s",bkl_low?"low":"high"); 646 | return 0; 647 | } 648 | break; 649 | case SPI_SCREEN_PROP_COLSET: 650 | if (size == sizeof(uint8_t)) { 651 | colset = *((uint8_t*)data); 652 | logfmt(3,"COLSET set to = %02X",colset); 653 | return 0; 654 | } 655 | break; 656 | case SPI_SCREEN_PROP_ROWSET: 657 | if (size == sizeof(uint8_t)) { 658 | rowset = *((uint8_t*)data); 659 | logfmt(3,"ROWSET set to = %02X",rowset); 660 | return 0; 661 | } 662 | break; 663 | case SPI_SCREEN_PROP_WRITE: 664 | if (size == sizeof(uint8_t)) { 665 | write = *((uint8_t*)data); 666 | logfmt(3,"RAMWR set to = %02X",write); 667 | return 0; 668 | } 669 | break; 670 | case SPI_SCREEN_PROP_READ: 671 | if (size == sizeof(uint8_t)) { 672 | read = *((uint8_t*)data); 673 | logfmt(3,"RAMRD set to = %02X",read); 674 | return 0; 675 | } 676 | break; 677 | case SPI_SCREEN_PROP_OFFSETS: 678 | if (size == sizeof(screen_offsets)) { 679 | memcpy(&screen_offsets, data, size); 680 | logfmt(3,"Offsets set to {%d,%d}",screen_offsets.x,screen_offsets.y); 681 | return 0; 682 | } 683 | break; 684 | default: 685 | break; 686 | } 687 | logfmt(1,"Configure: Unknown property or bad size"); 688 | return 1; 689 | } 690 | int CALL spi_screen::CanConnect() { 691 | return 1; 692 | } 693 | int CALL spi_screen::Connect(uint8_t pin, gpio_get_callback getter, gpio_set_callback setter, void* state) { 694 | switch (pin) { 695 | case SPI_SCREEN_PIN_CS: 696 | if (getter == nullptr) { 697 | return 1; 698 | } 699 | cs.read = getter; 700 | cs.read_state = state; 701 | break; 702 | case SPI_SCREEN_PIN_DC: 703 | if (getter == nullptr) { 704 | return 1; 705 | } 706 | dc.read = getter; 707 | dc.read_state = state; 708 | break; 709 | case SPI_SCREEN_PIN_RST: 710 | if (getter == nullptr) { 711 | return 1; 712 | } 713 | rst.read = getter; 714 | rst.read_state = state; 715 | rst.on_change_callback_state = this; 716 | rst.on_change_callback = on_rst_changed; 717 | break; 718 | case SPI_SCREEN_PIN_BKL: 719 | if (getter == nullptr) { 720 | return 1; 721 | } 722 | bkl.read = getter; 723 | bkl.read_state = state; 724 | break; 725 | default: 726 | return 1; 727 | } 728 | can_configure = false; 729 | return 0; 730 | } 731 | int CALL spi_screen::CanUpdate() { 732 | return 0; 733 | } 734 | int CALL Update() { 735 | return 0; 736 | } 737 | int CALL spi_screen::CanPinChange() { 738 | return 1; 739 | } 740 | 741 | int CALL spi_screen::PinChange(uint8_t pin, uint32_t value) { 742 | switch (pin) { 743 | case SPI_SCREEN_PIN_CS: 744 | cs.on_changed(value); 745 | break; 746 | case SPI_SCREEN_PIN_DC: 747 | dc.on_changed(value); 748 | break; 749 | case SPI_SCREEN_PIN_RST: 750 | rst.on_changed(value); 751 | break; 752 | case SPI_SCREEN_PIN_BKL: 753 | bkl.on_changed(value); 754 | break; 755 | default: 756 | return 1; 757 | } 758 | return 0; 759 | } 760 | int CALL spi_screen::CanAttachLog() { 761 | return 1; 762 | } 763 | 764 | int CALL spi_screen::AttachLog(log_callback logger, const char* prefix, uint8_t level) { 765 | this->logger = logger; 766 | this->log_prefix = prefix; 767 | this->log_level = level; 768 | return logger == NULL; 769 | } 770 | int CALL spi_screen::CanTransferBitsSPI() { 771 | return 1; 772 | } 773 | 774 | int CALL spi_screen::TransferBitsSPI(uint8_t* data, size_t size_bits) { 775 | if (cs.value()) { 776 | return 0; 777 | } 778 | size_t full_bytes = size_bits / 8; 779 | while (full_bytes--) { 780 | *data = process_byte_spi(*data); 781 | data++; 782 | } 783 | return 0; 784 | } 785 | int CALL spi_screen::CanTransferBytesI2C() { 786 | return 1; 787 | } 788 | 789 | int CALL spi_screen::TransferBytesI2C(const uint8_t* in, size_t in_size, uint8_t* out, size_t* in_out_out_size) { 790 | 791 | //logfmt("addr: %X, in_size: %d, out_size: %d%s",*in&0x7f,in_size,*in_out_out_size,*in&0x80?" (read)":" (write)"); 792 | 793 | if (in == nullptr) { 794 | return 1; 795 | } 796 | int val; 797 | size_t out_size = 0; 798 | touch_states_t touch_st = TOUCH_STATE_INITIAL; 799 | while(in_size) { 800 | switch (touch_st) { 801 | case TOUCH_STATE_INITIAL: 802 | touch_st = TOUCH_STATE_ADDRESS; 803 | // fall through 804 | case TOUCH_STATE_ADDRESS: 805 | if ((*in & 0x7F) != touch_address) { 806 | //logfmt("ignoring data not addressed to FT6236 - address %02X",(*in & 0x7F)); 807 | return 0; // not our message 808 | } 809 | if ((*in & 0x80)) { 810 | touch_st = TOUCH_STATE_READ; 811 | } else { 812 | touch_st = TOUCH_STATE_WRITE; 813 | } 814 | ++in; 815 | --in_size; 816 | if(touch_st!=TOUCH_STATE_READ) { 817 | break; 818 | } 819 | case TOUCH_STATE_READ: 820 | if(touch_current_reg<0) { 821 | // our I2C register was not set 822 | return 2; 823 | } 824 | switch(touch_current_reg) { 825 | case TOUCH_REG_XL: 826 | if(in_out_out_size==nullptr || out==nullptr || *in_out_out_size<1) { 827 | return 1; 828 | } 829 | if(WAIT_OBJECT_0==WaitForSingleObject(touch_mutex,INFINITE)) { 830 | *out = mouse_loc.x & 0xFF; 831 | ReleaseMutex(touch_mutex); 832 | } else { 833 | return 3; 834 | } 835 | ++out_size; 836 | touch_st = TOUCH_STATE_ADDRESS; 837 | break; 838 | case TOUCH_REG_XH: 839 | if(in_out_out_size==nullptr || out==nullptr || *in_out_out_size<1) { 840 | return 1; 841 | } 842 | if(WAIT_OBJECT_0==WaitForSingleObject(touch_mutex,INFINITE)) { 843 | *out = (mouse_loc.x & 0xFF00)>>8; 844 | ReleaseMutex(touch_mutex); 845 | } else { 846 | return 3; 847 | } 848 | ++out_size; 849 | touch_st = TOUCH_STATE_ADDRESS; 850 | break; 851 | case TOUCH_REG_YL: 852 | if(in_out_out_size==nullptr || out==nullptr || *in_out_out_size<1) { 853 | return 1; 854 | } 855 | if(WAIT_OBJECT_0==WaitForSingleObject(touch_mutex,INFINITE)) { 856 | *out = mouse_loc.y & 0xFF; 857 | ReleaseMutex(touch_mutex); 858 | } else { 859 | return 3; 860 | } 861 | ++out_size; 862 | touch_st = TOUCH_STATE_ADDRESS; 863 | break; 864 | case TOUCH_REG_YH: 865 | if(in_out_out_size==nullptr || out==nullptr || *in_out_out_size<1) { 866 | return 1; 867 | } 868 | ++out_size; 869 | 870 | if(WAIT_OBJECT_0==WaitForSingleObject(touch_mutex,INFINITE)) { 871 | *out = (mouse_loc.y & 0xFF00)>>8; 872 | ReleaseMutex(touch_mutex); 873 | } else { 874 | return 3; 875 | } 876 | touch_st = TOUCH_STATE_ADDRESS; 877 | break; 878 | case TOUCH_REG_NUMTOUCHES: 879 | if(in_out_out_size==nullptr || out==nullptr || *in_out_out_size<1) { 880 | return 1; 881 | } 882 | ++out_size; 883 | if(WAIT_OBJECT_0==WaitForSingleObject(touch_mutex,INFINITE)) { 884 | *out = !!mouse_state; 885 | ReleaseMutex(touch_mutex); 886 | } else { 887 | return 3; 888 | } 889 | touch_st = TOUCH_STATE_ADDRESS; 890 | break; 891 | case TOUCH_REG_THRESHHOLD: 892 | if(in_out_out_size==nullptr || out==nullptr || *in_out_out_size<1) { 893 | return 1; 894 | } 895 | ++out_size; 896 | *out = touch_threshhold; 897 | touch_st = TOUCH_STATE_ADDRESS; 898 | break; 899 | case TOUCH_REG_VENDID: 900 | if(in_out_out_size==nullptr || out==nullptr || *in_out_out_size<1) { 901 | return 1; 902 | } 903 | //logfmt("touch VENDID read"); 904 | *out = FT6236_VENDID; 905 | ++out_size; 906 | touch_st = TOUCH_STATE_ADDRESS; 907 | break; 908 | case TOUCH_REG_CHIPID: 909 | if(in_out_out_size==nullptr || out==nullptr || *in_out_out_size<1) { 910 | return 1; 911 | } 912 | *out = FT6236_CHIPID; 913 | ++out_size; 914 | touch_st = TOUCH_STATE_ADDRESS; 915 | break; 916 | case TOUCH_REG_MODE: 917 | if(in_out_out_size==nullptr || out==nullptr || *in_out_out_size<16) { 918 | return 1; // invalid args 919 | } 920 | *out++ = 0; // no idea what this is supposed to be? 921 | *out++ = 0; // ? 922 | if(WAIT_OBJECT_0==WaitForSingleObject(touch_mutex,INFINITE)) { 923 | *out++ = !!mouse_state; 924 | *out++ = (mouse_loc.x & 0xFF00)>>8; 925 | *out++ = mouse_loc.x & 0x00FF; 926 | *out++ = (mouse_loc.y & 0xFF00)>>8; 927 | *out++ = mouse_loc.y & 0x00FF; 928 | // garbage for the second touch 929 | // we don't support it 930 | out_size+=16; 931 | touch_st = TOUCH_STATE_ADDRESS; 932 | ReleaseMutex(touch_mutex); 933 | } else { 934 | return 3; 935 | } 936 | 937 | break; 938 | } 939 | break; 940 | case TOUCH_STATE_WRITE: 941 | //logfmt("touch state write"); 942 | if(in_size<1) { 943 | return 1; 944 | } 945 | //logfmt("touch set current register %02X",*in); 946 | touch_current_reg = *in++; 947 | --in_size; 948 | if(in_size) { 949 | switch(touch_current_reg) { 950 | case TOUCH_REG_THRESHHOLD: 951 | if(in_size<1) { 952 | return 1; 953 | } 954 | //logfmt("reg write threshold"); 955 | touch_threshhold = *in++; 956 | --in_size; 957 | break; 958 | default: 959 | touch_threshhold = *in++; 960 | --in_size; 961 | break; 962 | } 963 | } 964 | touch_st = TOUCH_STATE_ADDRESS; 965 | break; 966 | } 967 | //logfmt("loop tail - in_size: %d",in_size); 968 | } 969 | if(out_size!=0) { 970 | *in_out_out_size = out_size; 971 | } 972 | return 0; 973 | } 974 | int spi_screen::Update() { 975 | return 0; 976 | } 977 | int spi_screen::Destroy() { 978 | delete this; 979 | return 0; 980 | } 981 | 982 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { 983 | switch (fdwReason) { 984 | case DLL_PROCESS_ATTACH: 985 | hInstance = hinstDLL; 986 | break; 987 | 988 | case DLL_PROCESS_DETACH: 989 | 990 | break; 991 | 992 | case DLL_THREAD_ATTACH: 993 | break; 994 | 995 | case DLL_THREAD_DETACH: 996 | break; 997 | } 998 | 999 | return TRUE; 1000 | } 1001 | 1002 | int CALL CreateHardware(hardware_interface** out_hardware) { 1003 | if(out_hardware==NULL) { 1004 | return 1; 1005 | } 1006 | spi_screen* scr = new spi_screen(); 1007 | scr->initialize(); 1008 | if(scr==nullptr) { 1009 | return 3; 1010 | } 1011 | *out_hardware = static_cast(scr); 1012 | return 0; 1013 | } -------------------------------------------------------------------------------- /winduino_hardware/spi_screen/spi_screen.h: -------------------------------------------------------------------------------- 1 | #ifndef SPI_SCREEN_H 2 | #define SPI_SCREEN_H 3 | 4 | #define SPI_SCREEN_PIN_CS 3 5 | #define SPI_SCREEN_PIN_DC 4 6 | #define SPI_SCREEN_PIN_RST 5 7 | #define SPI_SCREEN_PIN_BKL 6 8 | 9 | #define SPI_SCREEN_PROP_RESOLUTION 0 10 | #define SPI_SCREEN_PROP_BKL_LOW 1 11 | #define SPI_SCREEN_PROP_COLSET 2 12 | #define SPI_SCREEN_PROP_ROWSET 3 13 | #define SPI_SCREEN_PROP_WRITE 4 14 | #define SPI_SCREEN_PROP_READ 5 15 | #define SPI_SCREEN_PROP_OFFSETS 6 16 | 17 | #define LIB_SPI_SCREEN ".\winduino_spi_screen.dll" 18 | #endif // SCREEN_H -------------------------------------------------------------------------------- /winduino_hardware/spi_screen/spi_screen_lib.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define DLL_API __declspec(dllexport) 6 | 7 | #define CALL __cdecl 8 | 9 | static const uint8_t TFT_CASET = 0x2A; 10 | static const uint8_t TFT_PASET = 0x2B; 11 | static const uint8_t TFT_RAMWR = 0x2C; 12 | static const uint8_t TFT_RAMRD = 0x2E; 13 | 14 | constexpr static const uint8_t TOUCH_REG_MODE = 0x00; 15 | constexpr static const uint8_t TOUCH_REG_XL = 0x04; 16 | constexpr static const uint8_t TOUCH_REG_XH = 0x03; 17 | constexpr static const uint8_t TOUCH_REG_YL = 0x06; 18 | constexpr static const uint8_t TOUCH_REG_YH = 0x05; 19 | constexpr static const uint8_t TOUCH_REG_NUMTOUCHES = 0x2; 20 | constexpr static const uint8_t TOUCH_REG_THRESHHOLD = 0x80; 21 | constexpr static const uint8_t TOUCH_REG_VENDID = 0xA8; 22 | constexpr static const uint8_t TOUCH_REG_CHIPID = 0xA3; 23 | constexpr static const uint8_t FT6236_VENDID = 0x11; 24 | constexpr static const uint8_t FT6206_CHIPID = 0x6; 25 | constexpr static const uint8_t FT6236_CHIPID = 0x36; 26 | constexpr static const uint8_t FT6236U_CHIPID = 0x64; 27 | typedef __cdecl void (*gpio_set_callback)(uint32_t value, void* state); 28 | typedef __cdecl uint8_t (*gpio_get_callback)(void* state); 29 | typedef __cdecl void(*log_callback)(const char* text); 30 | typedef struct input { 31 | void* read_state; 32 | bool has_cached_value; 33 | uint8_t cached_value; 34 | gpio_get_callback read; 35 | void (*on_change_callback)(void* state); 36 | void* on_change_callback_state; 37 | input() : read_state(nullptr), has_cached_value(false), cached_value(false), read(nullptr), on_change_callback(nullptr), on_change_callback_state(nullptr) { 38 | } 39 | void on_changed(uint8_t value) { 40 | has_cached_value = true; 41 | cached_value = value; 42 | if (on_change_callback != nullptr) { 43 | on_change_callback(on_change_callback_state); 44 | } 45 | } 46 | uint8_t value() const { 47 | if (!has_cached_value) { 48 | if (read == nullptr) { 49 | return 0; 50 | } 51 | return read(read_state); 52 | } 53 | return cached_value; 54 | } 55 | } input_t; 56 | 57 | typedef enum screen_states { 58 | SCREEN_STATE_INITIAL = 0, 59 | SCREEN_STATE_IGNORING, 60 | SCREEN_STATE_COLSET1, 61 | SCREEN_STATE_COLSET2, 62 | SCREEN_STATE_ROWSET1, 63 | SCREEN_STATE_ROWSET2, 64 | SCREEN_STATE_WRITE, 65 | SCREEN_STATE_READ1, 66 | SCREEN_STATE_READ2 67 | } screen_states_t; 68 | typedef enum touch_states { 69 | TOUCH_STATE_INITIAL = 0, 70 | TOUCH_STATE_IGNORING, 71 | TOUCH_STATE_ADDRESS, 72 | TOUCH_STATE_READ, 73 | TOUCH_STATE_WRITE 74 | } touch_states_t; 75 | 76 | class hardware_interface { 77 | public: 78 | virtual int CALL CanConfigure() =0; 79 | virtual int CALL Configure(int prop, 80 | void* data, 81 | size_t size) =0; 82 | virtual int CALL CanConnect() =0; 83 | virtual int CALL Connect(uint8_t pin, 84 | gpio_get_callback getter, 85 | gpio_set_callback setter, 86 | void* state) =0; 87 | virtual int CALL CanUpdate() =0; 88 | virtual int CALL Update() =0; 89 | virtual int CALL CanPinChange() =0; 90 | virtual int CALL PinChange(uint8_t pin, 91 | uint32_t value) =0; 92 | virtual int CALL CanTransferBitsSPI() =0; 93 | virtual int CALL TransferBitsSPI(uint8_t* data, 94 | size_t size_bits) =0; 95 | virtual int CALL CanTransferBytesI2C() =0; 96 | virtual int CALL TransferBytesI2C(const uint8_t* in, 97 | size_t in_size, 98 | uint8_t* out, 99 | size_t* in_out_out_size) =0; 100 | virtual int CALL CanAttachLog() =0; 101 | virtual int CALL AttachLog(log_callback logger, 102 | const char* prefix, 103 | uint8_t level) =0; 104 | virtual int CALL Destroy() = 0; 105 | }; 106 | class spi_screen : public hardware_interface { 107 | struct { 108 | uint16_t width; 109 | uint16_t height; 110 | } screen_size; //= {320, 240}; 111 | struct { 112 | int16_t x; 113 | int16_t y; 114 | } screen_offsets; // = {0,0}; 115 | screen_states_t screen_st;// = SCREEN_STATE_INITIAL; 116 | uint8_t touch_address;// = 0x38; 117 | bool touch_factory_mode;// = false; 118 | int touch_current_reg;// = -1; 119 | uint8_t touch_threshhold;// = 0x80; 120 | bool bkl_low;// = false; 121 | uint8_t colset;// = TFT_CASET; 122 | uint8_t rowset;// = TFT_PASET; 123 | uint8_t write;// = TFT_RAMWR; 124 | uint8_t read;// = TFT_RAMRD; 125 | uint16_t col_start, col_end, row_start, row_end; 126 | int cmd;// = -1; 127 | uint8_t in_byte;// = 0; 128 | int bit_count;// = 0; 129 | input_t cs; 130 | input_t dc; 131 | input_t rst; 132 | input_t bkl; 133 | uint8_t* frame_buffer; 134 | uint32_t data_word;// = 0; 135 | size_t bytes_written, bytes_read; 136 | uint16_t column, row; 137 | int offset; 138 | bool can_configure;// = true; 139 | HANDLE render_mutex;// = NULL; 140 | HANDLE render_thread;// = NULL; 141 | HANDLE quit_event;// = NULL; 142 | int reset_state;// = false; 143 | HANDLE screen_ready;// = NULL; 144 | HANDLE touch_mutex;// = NULL; 145 | log_callback logger;// = NULL; 146 | const char* log_prefix; 147 | uint8_t log_level; 148 | bool in_pixel_transfer;// = false; 149 | volatile long render_changed;// = 1; 150 | // mouse mess 151 | struct { 152 | int x; 153 | int y; 154 | } mouse_loc; 155 | int mouse_state;// = 0; // 0 = released, 1 = pressed 156 | int old_mouse_state;// = 0; 157 | int mouse_req;// = 0; 158 | // directX stuff 159 | ID2D1HwndRenderTarget* render_target;// = nullptr; 160 | ID2D1Factory* d2d_factory;// = nullptr; 161 | ID2D1Bitmap* render_bitmap;// = nullptr; 162 | HWND hwnd_screen;// = nullptr; 163 | bool created_wndcls;// = false; 164 | void logfmt(uint8_t level,const char* format, ...); 165 | void update_title(HWND hwnd); 166 | uint8_t process_byte_spi(uint8_t val); 167 | static unsigned long CALLBACK window_thread_proc(void* state); 168 | static unsigned long CALLBACK render_thread_proc(void* state); 169 | static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 170 | static void on_rst_changed(void* state); 171 | public: 172 | void initialize(); 173 | virtual int CALL CanConfigure(); 174 | virtual int CALL Configure(int prop, void* data, size_t size); 175 | virtual int CALL CanConnect(); 176 | virtual int CALL Connect(uint8_t pin, gpio_get_callback getter, gpio_set_callback setter, void* state); 177 | virtual int CALL CanUpdate(); 178 | virtual int CALL Update(); 179 | virtual int CALL CanPinChange(); 180 | virtual int CALL PinChange(uint8_t pin, uint32_t value); 181 | virtual int CALL CanTransferBitsSPI(); 182 | virtual int CALL TransferBitsSPI(uint8_t* data, size_t size_bits); 183 | virtual int CALL CanTransferBytesI2C(); 184 | virtual int CALL TransferBytesI2C(const uint8_t* in, size_t in_size, uint8_t* out, size_t* in_out_out_size); 185 | virtual int CALL CanAttachLog(); 186 | virtual int CALL AttachLog(log_callback logger,const char* prefix, uint8_t level); 187 | virtual int CALL Destroy(); 188 | }; 189 | 190 | typedef CALL void (*gpio_set_callback)(uint32_t value, void* state); 191 | typedef CALL uint8_t (*gpio_get_callback)(void* state); 192 | typedef CALL void (*log_callback)(const char* value); 193 | #ifdef __cplusplus 194 | extern "C" { 195 | #endif 196 | DLL_API int CALL CreateHardware(hardware_interface** out_hardware); 197 | #ifdef __cplusplus 198 | } // __cplusplus defined. 199 | #endif --------------------------------------------------------------------------------