├── .github └── workflows │ ├── build_gnulinux.yml │ ├── build_win_mingw.yml │ └── build_win_msvc6.yml ├── .gitignore ├── COPYING ├── Makefile ├── Makefile.wat ├── README.md ├── miniglut.c ├── miniglut.dsp ├── miniglut.dsw ├── miniglut.h ├── test.c └── test.dsp /.github/workflows/build_gnulinux.yml: -------------------------------------------------------------------------------- 1 | name: GNU/Linux build 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: install dependencies 17 | run: | 18 | sudo apt-get update 19 | sudo apt-get install libgl-dev libx11-dev 20 | 21 | - name: build 22 | run: make 23 | 24 | - name: package 25 | run: | 26 | mkdir miniglut-gnulinux64 27 | cp libminiglut.a miniglut-gnulinux64 28 | cp test miniglut-gnulinux64 29 | 30 | - uses: actions/upload-artifact@v4 31 | with: 32 | name: miniglut-gnulinux64 33 | path: miniglut-gnulinux64 34 | 35 | # vi:ts=2 sts=2 sw=2 expandtab: 36 | -------------------------------------------------------------------------------- /.github/workflows/build_win_mingw.yml: -------------------------------------------------------------------------------- 1 | name: Windows MinGW build 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: windows-latest 12 | defaults: 13 | run: 14 | shell: msys2 {0} 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: msys2/setup-msys2@v2 19 | with: 20 | msystem: MINGW32 21 | install: mingw-w64-i686-gcc mingw-w64-i686-make 22 | 23 | - name: build 24 | run: mingw32-make 25 | 26 | - name: package 27 | run: | 28 | mkdir miniglut-win32-mingw 29 | cp libminiglut-w32.a miniglut-win32-mingw/libminiglut.a 30 | cp test.exe miniglut-win32-mingw 31 | 32 | - uses: actions/upload-artifact@v4 33 | with: 34 | name: miniglut-win32-mingw 35 | path: miniglut-win32-mingw 36 | 37 | # vi:ts=2 sts=2 sw=2:expandtab 38 | -------------------------------------------------------------------------------- /.github/workflows/build_win_msvc6.yml: -------------------------------------------------------------------------------- 1 | name: Windows MSVC 6.0 build 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: cache toolchain 16 | id: cache-tc 17 | uses: actions/cache@v3 18 | with: 19 | path: | 20 | msvc6lt 21 | vc6varsgh.bat 22 | key: msvc6lt-cache1 23 | 24 | - name: install toolchain 25 | if: steps.cache-tc.outputs.cache-hit != 'true' 26 | run: | 27 | C:\msys64\usr\bin\wget.exe -q http://mutantstargoat.com/~nuclear/msvc6.zip 28 | 7z x msvc6.zip 29 | C:\msys64\usr\bin\wget.exe -q http://mutantstargoat.com/~nuclear/vc6fullvarsgh.bat 30 | 31 | - name: build miniglut 32 | shell: cmd 33 | run: | 34 | call vc6fullvarsgh.bat 35 | msdev miniglut.dsp /MAKE "miniglut - Win32 Release" 36 | mkdir miniglut-win32-msvc 37 | copy Release\miniglut.lib miniglut-win32-msvc 38 | 39 | - name: build test 40 | shell: cmd 41 | run: | 42 | call vc6fullvarsgh.bat 43 | msdev test.dsp /MAKE "test - Win32 Release" 44 | copy Release\test.exe miniglut-win32-msvc 45 | 46 | - uses: actions/upload-artifact@v4 47 | with: 48 | name: miniglut-win32-msvc 49 | path: miniglut-win32-msvc 50 | 51 | # vi:ts=2 sts=2 sw=2 expandtab: 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | *.swp 4 | test 5 | test.exe 6 | Debug/ 7 | Release/ 8 | *.obj 9 | *.suo 10 | *.ncb 11 | *.opt 12 | *.plg 13 | *.a 14 | *.lib 15 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX = /usr/local 2 | 3 | olib = miniglut.o 4 | otest = test.o 5 | alib = libminiglut.a 6 | bin = test 7 | 8 | CFLAGS = -pedantic -Wall -g 9 | 10 | isx86 ?= $(shell uname -m | sed 's/x86_64/x86/; s/i.86/x86/') 11 | 12 | sys ?= $(shell uname -s | sed 's/MINGW.*/mingw/; s/IRIX.*/IRIX/') 13 | ifeq ($(sys), mingw) 14 | olib = miniglut.w32.o 15 | otest = test.w32.o 16 | alib = libminiglut-w32.a 17 | bin = test.exe 18 | 19 | # on windows/mingw we can build without linking to libc 20 | CFLAGS += -DMINIGLUT_NO_LIBC 21 | LDFLAGS = -mconsole -lopengl32 -lgdi32 -lwinmm 22 | else 23 | ifeq ($(sys)-$(isx86), Linux-x86) 24 | # for Linux x86/x86-64 we can build without linking to libc 25 | CFLAGS += -I/usr/X11R6/include -DMINIGLUT_NO_LIBC 26 | LDFLAGS = -L/usr/X11R6/lib -lX11 -lGL 27 | else 28 | # for other UNIX or non-x86 where sys_ and trig functions are not 29 | # implemented, just use libc 30 | LDFLAGS = -lX11 -lGL -lm 31 | ifeq ($(sys), IRIX) 32 | CC = gcc 33 | endif 34 | endif 35 | endif 36 | 37 | $(bin): $(otest) $(alib) 38 | $(CC) -o $@ $(otest) $(alib) $(LDFLAGS) 39 | 40 | $(alib): $(olib) 41 | $(AR) rcs $@ $(olib) 42 | 43 | %.w32.o: %.c 44 | $(CC) -o $@ $(CFLAGS) -c $< 45 | 46 | .PHONY: clean 47 | clean: 48 | rm -f $(alib) $(olib) $(otest) $(bin) 49 | 50 | .PHONY: install 51 | install: $(alib) 52 | mkdir -p $(DESTDIR)$(PREFIX)/include $(DESTDIR)$(PREFIX)/lib 53 | cp miniglut.h $(DESTDIR)$(PREFIX)/include/miniglut.h 54 | cp $(alib) $(DESTDIR)$(PREFIX)/lib/libminiglut.a 55 | 56 | .PHONY: uninstall 57 | uninstall: 58 | rm -f $(DESTDIR)$(PREFIX)/include/miniglut.h 59 | rm -f $(DESTDIR)$(PREFIX)/lib/libminiglut.a 60 | 61 | .PHONY: cross 62 | cross: 63 | $(MAKE) CC=i686-w64-mingw32-gcc sys=mingw 64 | 65 | .PHONY: cross-clean 66 | cross-clean: 67 | $(MAKE) CC=i686-w64-mingw32-gcc sys=mingw clean 68 | -------------------------------------------------------------------------------- /Makefile.wat: -------------------------------------------------------------------------------- 1 | obj = test.obj miniglut.obj 2 | bin = test.exe 3 | 4 | CC = wcc386 5 | LD = wlink 6 | CFLAGS = -d3 -6r -otexan -zq -bt=nt 7 | LDFLAGS = system nt library { opengl32 winmm } 8 | 9 | $(bin): $(obj) 10 | $(LD) name $@ debug all file { $(obj) } $(LDFLAGS) 11 | 12 | .c.obj: .autodepend 13 | $(CC) -fo=$@ $(CFLAGS) $[* 14 | 15 | !ifdef __UNIX__ 16 | clean: .symbolic 17 | rm -f $(obj) $(bin) 18 | !else 19 | clean: .symbolic 20 | del $(obj) 21 | del $(bin) 22 | !endif 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MiniGLUT 2 | ======== 3 | 4 | ![logo](http://nuclear.mutantstargoat.com/sw/miniglut/img/miniglut_logo_sm_alpha.png) 5 | 6 | ![GNU/Linux build status](https://github.com/jtsiomb/miniglut/actions/workflows/build_gnulinux.yml/badge.svg) 7 | ![Windows MinGW build status](https://github.com/jtsiomb/miniglut/actions/workflows/build_win_mingw.yml/badge.svg) 8 | ![Windows MSVC6 build status](https://github.com/jtsiomb/miniglut/actions/workflows/build_win_msvc6.yml/badge.svg) 9 | 10 | MiniGLUT is a subset of GLUT (or more precisely a subset of FreeGLUT) 11 | implemented in a single C source file, without any non-essential dependencies, 12 | not even the C library. 13 | 14 | You can use MiniGLUT by simply dropping two files: `miniglut.h` and `miniglut.c` 15 | into your project source tree, or by building MiniGLUT as a static library and 16 | linking with it. 17 | 18 | MiniGLUT does not intend to replace a full GLUT library, like FreeGLUT, for 19 | hacking small to medium OpenGL programs. The purpose of MiniGLUT is to 20 | potentially replace a full GLUT library when it's time for release, in order to 21 | minimize runtime dependencies of the resulting binary. 22 | 23 | A second reason to use MiniGLUT is to ease porting of UNIX OpenGL programs to 24 | Windows, especially when using the microsoft compiler, where setting up and 25 | linking with a proper 3rd-party library is an ordeal in itself. Even more so if 26 | you decide to statically link, at which point you need to deal with the whole 27 | "MSVC runtime" chaos. 28 | 29 | On GNU/Linux x86/x86-64 and 32bit Windows, MiniGLUT can be compiled to never 30 | call any C library functions whatsoever (which is the default if you use the 31 | included makefile/msvc project to build a static library). This is useful to 32 | avoid dependencies on any specific libc or msvc runtime. 33 | 34 | Download 35 | -------- 36 | Latest release (v0.5): 37 | - https://github.com/jtsiomb/miniglut/releases/download/v0.5/miniglut-0.5.tar.gz 38 | - https://github.com/jtsiomb/miniglut/releases/download/v0.5/miniglut-0.5.zip 39 | 40 | Both archives are identical, choose whichever is more convenient. 41 | 42 | Source repository (git): 43 | - https://github.com/jtsiomb/miniglut 44 | 45 | Build 46 | ----- 47 | Under X11 MiniGLUT depends only on Xlib and OpenGL. Therefore to build a program 48 | using MiniGLUT you'll need to link with `-lX11 -lGL`. 49 | 50 | Under Windows MiniGLUT depends only on OpenGL, GDI and winmm. Therefore to build 51 | a program using MiniGLUT you'll need to link with `-lopengl32 -lgdi32 -lwinmm`. 52 | When building with MSVC, linking with the correct libraries is taken care by 53 | pragmas in the header file. If you wish to avoid the winmm dependency, define 54 | `MINIGLUT_NO_WINMM`. 55 | 56 | To disable calling any C library functions, make sure to have `MINIGLUT_NO_LIBC` 57 | defined when building `miniglut.c`. Either add that to your build system, or 58 | just modify `miniglut.c` and define it at the top. 59 | 60 | > Note: in previous versions (including v0.5), building without libc was the 61 | > default, and you had to define `MINIGLUT_USE_LIBC` to make it use libc. But 62 | > it turns out usually when you're building miniglut as part of your project, 63 | > there's no real downside to using libc in most use cases, so I decided to 64 | > change the default, and have the extra define go to the static library build 65 | > files instead of *every* project which drops `miniglut.h`/`miniglut.c` in the 66 | > source tree. 67 | 68 | To avoid calling C library functions, MiniGLUT uses inline assembly code for 69 | system calls and trigonometric operations. This is currently implemented only 70 | on x86 (32 and 64bit), and only on 32bit when building with MSVC (which doesn't 71 | support inline assembly on x86-64). For all other systems you need to link with 72 | libc. 73 | 74 | License 75 | ------- 76 | Copyright (C) 2020-2024 John Tsiombikas 77 | 78 | MiniGLUT is free software. Feel free to use, modify and/or redistribute it, 79 | under the terms of the GNU General Public License v3, or at your option any 80 | newer version published by the Free Software Foundation. See COPYING for 81 | details. 82 | 83 | The intention is not to dictate a specific free software license (GPL) but to 84 | shut the door to proprietary programs. If you want to use MiniGLUT in a free 85 | software project with an incompatible license, contact me and we will figure out 86 | a way to enable that. 87 | 88 | To learn more about GPL-incompatible free software licenses where this might 89 | be an issue, see: 90 | https://www.gnu.org/licenses/license-list.en.html#GPLIncompatibleLicenses 91 | 92 | Implementation Notes 93 | -------------------- 94 | On UNIX systems, spaceball callbacks are supported by talking to a 6dof device 95 | driver through the *Magellan X11 ClientMessage protocol*. This works with 96 | either the free software *spacenavd* driver, or the proprietary *3dxsrv*. 97 | Spacenavd supports all 6dof devices from the first serial ones to current 98 | models. 99 | 100 | On Windows, spaceball support relies on the 3Dconnexion driver and its old 101 | siapp API. This should work on any version of the driver from very old ones 102 | running on windows 9x and supporting serial devices, to the latest current 103 | driver for USB and bluetooth spacemice. 104 | 105 | Known Issues 106 | ------------ 107 | MiniGLUT being a subset of GLUT, is missing a number of features. Some of them 108 | on purpose to keep it minimal, and some of them because I didn't happen to use 109 | them in a program I wanted to link with MiniGLUT yet. 110 | 111 | Missing GLUT features: 112 | - The only supported systems are: UNIX with X11 (GLX), and Windows (WGL). 113 | - Multiple windows. 114 | - Subwindows. 115 | - Overlays. 116 | - Game mode (video mode switching). 117 | - Menus. 118 | - Font rendering. 119 | - Some of the primitives. 120 | - Buttons and Dials callbacks. 121 | - Tablet callbacks. 122 | - Timer callback. 123 | 124 | Missing FreeGLUT features: 125 | - Mobile callbacks. 126 | - Context version and profile selection and other context flags. 127 | - Window close actions. 128 | - Multi-touch/multi-pointer callbacks. 129 | - User-pointer callbacks. 130 | - Joystick callbacks. 131 | - More missing primitives. 132 | 133 | If wish to let me know how much you need one of the missing features, or even 134 | better if you are volunteering to implement it yourself, send me an email at: 135 | nuclear@member.fsf.org 136 | 137 | Only plain-text emails, hard-wrapped at 72 columns will be accepted. 138 | -------------------------------------------------------------------------------- /miniglut.c: -------------------------------------------------------------------------------- 1 | /* 2 | MiniGLUT - minimal GLUT subset without dependencies 3 | Copyright (C) 2020-2024 John Tsiombikas 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | #if defined(unix) || defined(__unix__) 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #define BUILD_X11 25 | 26 | #ifndef GLX_SAMPLE_BUFFERS_ARB 27 | #define GLX_SAMPLE_BUFFERS_ARB 100000 28 | #define GLX_SAMPLES_ARB 100001 29 | #endif 30 | #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 31 | #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2 32 | #endif 33 | 34 | static Display *dpy; 35 | static Window win, root; 36 | static Colormap cmap; 37 | static int cmap_size; 38 | static int scr; 39 | static GLXContext ctx; 40 | static Atom xa_wm_proto, xa_wm_del_win; 41 | static Atom xa_net_wm_state, xa_net_wm_state_fullscr; 42 | static Atom xa_motif_wm_hints; 43 | static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event; 44 | static unsigned int evmask; 45 | static Cursor blank_cursor; 46 | 47 | static int have_netwm_fullscr(void); 48 | 49 | #elif defined(_WIN32) 50 | 51 | #include 52 | #define BUILD_WIN32 53 | 54 | static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam); 55 | 56 | static HINSTANCE hinst; 57 | static HWND win; 58 | static HDC dc; 59 | static HGLRC ctx; 60 | static HPALETTE cmap; 61 | static int cmap_size; 62 | 63 | #else 64 | #error unsupported platform 65 | #endif 66 | #include 67 | #include "miniglut.h" 68 | 69 | #ifdef _MSC_VER 70 | #pragma warning (disable: 4244 4305) 71 | #endif 72 | 73 | 74 | struct ctx_info { 75 | int rsize, gsize, bsize, asize; 76 | int zsize, ssize; 77 | int dblbuf; 78 | int samples; 79 | int stereo; 80 | int srgb; 81 | }; 82 | 83 | static void cleanup(void); 84 | static void create_window(const char *title); 85 | static void get_window_pos(int *x, int *y); 86 | static void get_window_size(int *w, int *h); 87 | static void get_screen_size(int *scrw, int *scrh); 88 | 89 | static long get_msec(void); 90 | static void panic(const char *msg); 91 | static void sys_exit(int status); 92 | static int sys_write(int fd, const void *buf, int count); 93 | 94 | 95 | static int init_x = -1, init_y, init_width = 256, init_height = 256; 96 | static unsigned int init_mode; 97 | 98 | static struct ctx_info ctx_info; 99 | static int cur_cursor = GLUT_CURSOR_INHERIT; 100 | static int ignore_key_repeat; 101 | 102 | static glut_cb cb_display; 103 | static glut_cb cb_idle; 104 | static glut_cb_reshape cb_reshape; 105 | static glut_cb_state cb_vis, cb_entry; 106 | static glut_cb_keyb cb_keydown, cb_keyup; 107 | static glut_cb_special cb_skeydown, cb_skeyup; 108 | static glut_cb_mouse cb_mouse; 109 | static glut_cb_motion cb_motion, cb_passive; 110 | static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate; 111 | static glut_cb_sbbutton cb_sball_button; 112 | 113 | static int fullscreen; 114 | static int prev_win_x, prev_win_y, prev_win_width, prev_win_height; 115 | 116 | static int win_width, win_height; 117 | static int mapped; 118 | static int quit; 119 | static int upd_pending; 120 | static int modstate; 121 | 122 | static int has_sball, sball_nbuttons; 123 | 124 | void glutInit(int *argc, char **argv) 125 | { 126 | #ifdef BUILD_X11 127 | Pixmap blankpix = 0; 128 | XColor xcol; 129 | 130 | if(!(dpy = XOpenDisplay(0))) { 131 | panic("Failed to connect to the X server\n"); 132 | } 133 | scr = DefaultScreen(dpy); 134 | root = RootWindow(dpy, scr); 135 | xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False); 136 | xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 137 | xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False); 138 | xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); 139 | if(have_netwm_fullscr()) { 140 | xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); 141 | } 142 | 143 | xa_motion_event = XInternAtom(dpy, "MotionEvent", True); 144 | xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True); 145 | xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True); 146 | xa_command_event = XInternAtom(dpy, "CommandEvent", True); 147 | 148 | evmask = ExposureMask | StructureNotifyMask; 149 | 150 | if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) { 151 | blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0); 152 | XFreePixmap(dpy, blankpix); 153 | } 154 | 155 | #endif 156 | #ifdef BUILD_WIN32 157 | WNDCLASSEX wc = {0}; 158 | 159 | hinst = GetModuleHandle(0); 160 | 161 | wc.cbSize = sizeof wc; 162 | wc.hbrBackground = GetStockObject(BLACK_BRUSH); 163 | wc.hCursor = LoadCursor(0, IDC_ARROW); 164 | wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION); 165 | wc.hInstance = hinst; 166 | wc.lpfnWndProc = handle_message; 167 | wc.lpszClassName = "MiniGLUT"; 168 | wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 169 | if(!RegisterClassEx(&wc)) { 170 | panic("Failed to register \"MiniGLUT\" window class\n"); 171 | } 172 | 173 | if(init_x == -1) { 174 | get_screen_size(&init_x, &init_y); 175 | init_x >>= 3; 176 | init_y >>= 3; 177 | } 178 | #endif 179 | } 180 | 181 | void glutInitWindowPosition(int x, int y) 182 | { 183 | init_x = x; 184 | init_y = y; 185 | } 186 | 187 | void glutInitWindowSize(int xsz, int ysz) 188 | { 189 | init_width = xsz; 190 | init_height = ysz; 191 | } 192 | 193 | void glutInitDisplayMode(unsigned int mode) 194 | { 195 | init_mode = mode; 196 | } 197 | 198 | void glutCreateWindow(const char *title) 199 | { 200 | create_window(title); 201 | } 202 | 203 | void glutExit(void) 204 | { 205 | quit = 1; 206 | } 207 | 208 | void glutMainLoop(void) 209 | { 210 | while(!quit) { 211 | glutMainLoopEvent(); 212 | } 213 | } 214 | 215 | void glutPostRedisplay(void) 216 | { 217 | upd_pending = 1; 218 | } 219 | 220 | void glutIgnoreKeyRepeat(int ignore) 221 | { 222 | ignore_key_repeat = ignore; 223 | } 224 | 225 | #define UPD_EVMASK(x) \ 226 | do { \ 227 | if(func) { \ 228 | evmask |= x; \ 229 | } else { \ 230 | evmask &= ~(x); \ 231 | } \ 232 | if(win) XSelectInput(dpy, win, evmask); \ 233 | } while(0) 234 | 235 | 236 | void glutIdleFunc(glut_cb func) 237 | { 238 | cb_idle = func; 239 | } 240 | 241 | void glutDisplayFunc(glut_cb func) 242 | { 243 | cb_display = func; 244 | } 245 | 246 | void glutReshapeFunc(glut_cb_reshape func) 247 | { 248 | cb_reshape = func; 249 | } 250 | 251 | void glutVisibilityFunc(glut_cb_state func) 252 | { 253 | cb_vis = func; 254 | #ifdef BUILD_X11 255 | UPD_EVMASK(VisibilityChangeMask); 256 | #endif 257 | } 258 | 259 | void glutEntryFunc(glut_cb_state func) 260 | { 261 | cb_entry = func; 262 | #ifdef BUILD_X11 263 | UPD_EVMASK(EnterWindowMask | LeaveWindowMask); 264 | #endif 265 | } 266 | 267 | void glutKeyboardFunc(glut_cb_keyb func) 268 | { 269 | cb_keydown = func; 270 | #ifdef BUILD_X11 271 | UPD_EVMASK(KeyPressMask); 272 | #endif 273 | } 274 | 275 | void glutKeyboardUpFunc(glut_cb_keyb func) 276 | { 277 | cb_keyup = func; 278 | #ifdef BUILD_X11 279 | UPD_EVMASK(KeyReleaseMask); 280 | #endif 281 | } 282 | 283 | void glutSpecialFunc(glut_cb_special func) 284 | { 285 | cb_skeydown = func; 286 | #ifdef BUILD_X11 287 | UPD_EVMASK(KeyPressMask); 288 | #endif 289 | } 290 | 291 | void glutSpecialUpFunc(glut_cb_special func) 292 | { 293 | cb_skeyup = func; 294 | #ifdef BUILD_X11 295 | UPD_EVMASK(KeyReleaseMask); 296 | #endif 297 | } 298 | 299 | void glutMouseFunc(glut_cb_mouse func) 300 | { 301 | cb_mouse = func; 302 | #ifdef BUILD_X11 303 | UPD_EVMASK(ButtonPressMask | ButtonReleaseMask); 304 | #endif 305 | } 306 | 307 | void glutMotionFunc(glut_cb_motion func) 308 | { 309 | cb_motion = func; 310 | #ifdef BUILD_X11 311 | UPD_EVMASK(ButtonMotionMask); 312 | #endif 313 | } 314 | 315 | void glutPassiveMotionFunc(glut_cb_motion func) 316 | { 317 | cb_passive = func; 318 | #ifdef BUILD_X11 319 | UPD_EVMASK(PointerMotionMask); 320 | #endif 321 | } 322 | 323 | void glutSpaceballMotionFunc(glut_cb_sbmotion func) 324 | { 325 | cb_sball_motion = func; 326 | } 327 | 328 | void glutSpaceballRotateFunc(glut_cb_sbmotion func) 329 | { 330 | cb_sball_rotate = func; 331 | } 332 | 333 | void glutSpaceballButtonFunc(glut_cb_sbbutton func) 334 | { 335 | cb_sball_button = func; 336 | } 337 | 338 | int glutGet(unsigned int s) 339 | { 340 | int x, y; 341 | switch(s) { 342 | case GLUT_WINDOW_X: 343 | get_window_pos(&x, &y); 344 | return x; 345 | case GLUT_WINDOW_Y: 346 | get_window_pos(&x, &y); 347 | return y; 348 | case GLUT_WINDOW_WIDTH: 349 | get_window_size(&x, &y); 350 | return x; 351 | case GLUT_WINDOW_HEIGHT: 352 | get_window_size(&x, &y); 353 | return y; 354 | case GLUT_WINDOW_BUFFER_SIZE: 355 | return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize; 356 | case GLUT_WINDOW_STENCIL_SIZE: 357 | return ctx_info.ssize; 358 | case GLUT_WINDOW_DEPTH_SIZE: 359 | return ctx_info.zsize; 360 | case GLUT_WINDOW_RED_SIZE: 361 | return ctx_info.rsize; 362 | case GLUT_WINDOW_GREEN_SIZE: 363 | return ctx_info.gsize; 364 | case GLUT_WINDOW_BLUE_SIZE: 365 | return ctx_info.bsize; 366 | case GLUT_WINDOW_ALPHA_SIZE: 367 | return ctx_info.asize; 368 | case GLUT_WINDOW_DOUBLEBUFFER: 369 | return ctx_info.dblbuf; 370 | case GLUT_WINDOW_RGBA: 371 | return 1; 372 | case GLUT_WINDOW_NUM_SAMPLES: 373 | return ctx_info.samples; 374 | case GLUT_WINDOW_STEREO: 375 | return ctx_info.stereo; 376 | case GLUT_WINDOW_SRGB: 377 | return ctx_info.srgb; 378 | case GLUT_WINDOW_CURSOR: 379 | return cur_cursor; 380 | case GLUT_WINDOW_COLORMAP_SIZE: 381 | return cmap_size; 382 | case GLUT_SCREEN_WIDTH: 383 | get_screen_size(&x, &y); 384 | return x; 385 | case GLUT_SCREEN_HEIGHT: 386 | get_screen_size(&x, &y); 387 | return y; 388 | case GLUT_INIT_DISPLAY_MODE: 389 | return init_mode; 390 | case GLUT_INIT_WINDOW_X: 391 | return init_x; 392 | case GLUT_INIT_WINDOW_Y: 393 | return init_y; 394 | case GLUT_INIT_WINDOW_WIDTH: 395 | return init_width; 396 | case GLUT_INIT_WINDOW_HEIGHT: 397 | return init_height; 398 | case GLUT_ELAPSED_TIME: 399 | return get_msec(); 400 | default: 401 | break; 402 | } 403 | return 0; 404 | } 405 | 406 | int glutDeviceGet(unsigned int x) 407 | { 408 | switch(x) { 409 | case GLUT_HAS_KEYBOARD: 410 | case GLUT_HAS_MOUSE: 411 | return 1; 412 | case GLUT_NUM_MOUSE_BUTTONS: 413 | return 3; 414 | case GLUT_HAS_SPACEBALL: 415 | return has_sball; 416 | case GLUT_NUM_SPACEBALL_BUTTONS: 417 | return sball_nbuttons; 418 | case GLUT_HAS_DIAL_AND_BUTTON_BOX: 419 | case GLUT_HAS_TABLET: 420 | case GLUT_NUM_BUTTON_BOX_BUTTONS: 421 | case GLUT_NUM_DIALS: 422 | case GLUT_NUM_TABLET_BUTTONS: 423 | return 0; 424 | default: 425 | break; 426 | } 427 | return -1; 428 | } 429 | 430 | int glutGetModifiers(void) 431 | { 432 | return modstate; 433 | } 434 | 435 | static int is_space(int c) 436 | { 437 | return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r'; 438 | } 439 | 440 | static const char *skip_space(const char *s) 441 | { 442 | while(*s && is_space(*s)) s++; 443 | return s; 444 | } 445 | 446 | int glutExtensionSupported(char *ext) 447 | { 448 | const char *str, *eptr; 449 | 450 | if(!(str = (const char*)glGetString(GL_EXTENSIONS))) { 451 | return 0; 452 | } 453 | 454 | while(*str) { 455 | str = skip_space(str); 456 | eptr = skip_space(ext); 457 | while(*str && !is_space(*str) && *eptr && *str == *eptr) { 458 | str++; 459 | eptr++; 460 | } 461 | if((!*str || is_space(*str)) && !*eptr) { 462 | return 1; 463 | } 464 | while(*str && !is_space(*str)) str++; 465 | } 466 | 467 | return 0; 468 | } 469 | 470 | 471 | /* --------------- UNIX/X11 implementation ----------------- */ 472 | #ifdef BUILD_X11 473 | enum { 474 | SPNAV_EVENT_ANY, /* used by spnav_remove_events() */ 475 | SPNAV_EVENT_MOTION, 476 | SPNAV_EVENT_BUTTON /* includes both press and release */ 477 | }; 478 | 479 | struct spnav_event_motion { 480 | int type; 481 | int x, y, z; 482 | int rx, ry, rz; 483 | unsigned int period; 484 | int *data; 485 | }; 486 | 487 | struct spnav_event_button { 488 | int type; 489 | int press; 490 | int bnum; 491 | }; 492 | 493 | union spnav_event { 494 | int type; 495 | struct spnav_event_motion motion; 496 | struct spnav_event_button button; 497 | }; 498 | 499 | 500 | static void handle_event(XEvent *ev); 501 | 502 | static int spnav_window(Window win); 503 | static int spnav_event(const XEvent *xev, union spnav_event *event); 504 | static int spnav_remove_events(int type); 505 | 506 | 507 | void glutMainLoopEvent(void) 508 | { 509 | XEvent ev; 510 | 511 | if(!cb_display) { 512 | panic("display callback not set"); 513 | } 514 | 515 | if(!upd_pending && !cb_idle) { 516 | XNextEvent(dpy, &ev); 517 | handle_event(&ev); 518 | if(quit) goto end; 519 | } 520 | while(XPending(dpy)) { 521 | XNextEvent(dpy, &ev); 522 | handle_event(&ev); 523 | if(quit) goto end; 524 | } 525 | 526 | if(cb_idle) { 527 | cb_idle(); 528 | } 529 | 530 | if(upd_pending && mapped) { 531 | upd_pending = 0; 532 | cb_display(); 533 | } 534 | 535 | end: 536 | if(quit) { 537 | cleanup(); 538 | } 539 | } 540 | 541 | static void cleanup(void) 542 | { 543 | if(win) { 544 | spnav_window(root); 545 | glXMakeCurrent(dpy, 0, 0); 546 | XDestroyWindow(dpy, win); 547 | } 548 | } 549 | 550 | static KeySym translate_keysym(KeySym sym) 551 | { 552 | switch(sym) { 553 | case XK_Escape: 554 | return 27; 555 | case XK_BackSpace: 556 | return '\b'; 557 | case XK_Linefeed: 558 | return '\r'; 559 | case XK_Return: 560 | return '\r'; 561 | case XK_Delete: 562 | return 127; 563 | case XK_Tab: 564 | return '\t'; 565 | default: 566 | break; 567 | } 568 | return sym; 569 | } 570 | 571 | static void handle_event(XEvent *ev) 572 | { 573 | KeySym sym; 574 | union spnav_event sev; 575 | 576 | switch(ev->type) { 577 | case MapNotify: 578 | mapped = 1; 579 | break; 580 | case UnmapNotify: 581 | mapped = 0; 582 | break; 583 | case ConfigureNotify: 584 | if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) { 585 | win_width = ev->xconfigure.width; 586 | win_height = ev->xconfigure.height; 587 | cb_reshape(ev->xconfigure.width, ev->xconfigure.height); 588 | } 589 | break; 590 | 591 | case ClientMessage: 592 | if(ev->xclient.message_type == xa_wm_proto) { 593 | if(ev->xclient.data.l[0] == xa_wm_del_win) { 594 | quit = 1; 595 | } 596 | } 597 | if(spnav_event(ev, &sev)) { 598 | switch(sev.type) { 599 | case SPNAV_EVENT_MOTION: 600 | if(cb_sball_motion) { 601 | cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z); 602 | } 603 | if(cb_sball_rotate) { 604 | cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz); 605 | } 606 | spnav_remove_events(SPNAV_EVENT_MOTION); 607 | break; 608 | 609 | case SPNAV_EVENT_BUTTON: 610 | if(cb_sball_button) { 611 | cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP); 612 | } 613 | break; 614 | 615 | default: 616 | break; 617 | } 618 | } 619 | break; 620 | 621 | case Expose: 622 | upd_pending = 1; 623 | break; 624 | 625 | case KeyPress: 626 | if(0) { 627 | case KeyRelease: 628 | if(ignore_key_repeat && XEventsQueued(dpy, QueuedAfterReading)) { 629 | XEvent next; 630 | XPeekEvent(dpy, &next); 631 | 632 | if(next.type == KeyPress && next.xkey.keycode == ev->xkey.keycode && 633 | next.xkey.time == ev->xkey.time) { 634 | /* this is a key-repeat event, ignore the release and consume 635 | * the following press 636 | */ 637 | XNextEvent(dpy, &next); 638 | break; 639 | } 640 | } 641 | } 642 | modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask); 643 | if(!(sym = XLookupKeysym(&ev->xkey, 0))) { 644 | break; 645 | } 646 | sym = translate_keysym(sym); 647 | if(sym < 256) { 648 | if(ev->type == KeyPress) { 649 | if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y); 650 | } else { 651 | if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y); 652 | } 653 | } else { 654 | if(ev->type == KeyPress) { 655 | if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y); 656 | } else { 657 | if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y); 658 | } 659 | } 660 | break; 661 | 662 | case ButtonPress: 663 | case ButtonRelease: 664 | modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask); 665 | if(cb_mouse) { 666 | int bn = ev->xbutton.button - Button1; 667 | cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP, 668 | ev->xbutton.x, ev->xbutton.y); 669 | } 670 | break; 671 | 672 | case MotionNotify: 673 | if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) { 674 | if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y); 675 | } else { 676 | if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y); 677 | } 678 | break; 679 | 680 | case VisibilityNotify: 681 | if(cb_vis) { 682 | cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE); 683 | } 684 | break; 685 | case EnterNotify: 686 | if(cb_entry) cb_entry(GLUT_ENTERED); 687 | break; 688 | case LeaveNotify: 689 | if(cb_entry) cb_entry(GLUT_LEFT); 690 | break; 691 | } 692 | } 693 | 694 | void glutSwapBuffers(void) 695 | { 696 | glXSwapBuffers(dpy, win); 697 | } 698 | 699 | /* BUG: 700 | * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it 701 | * needs to resize the window to make it fullscreen. The way it does this is by 702 | * querying the size of the root window (see get_screen_size), which in the 703 | * case of multi-monitor setups will be the combined size of all monitors. 704 | * This is problematic; the way to solve it is to use the XRandR extension, or 705 | * the Xinerama extension, to figure out the dimensions of the correct video 706 | * output, which would add potentially two extension support libraries to our 707 | * dependencies list. 708 | * Moreover, any X installation modern enough to support XR&R will almost 709 | * certainly be running a window manager supporting the EHWM 710 | * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely 711 | * on manual resizing, and is used in preference if available, making this 712 | * whole endeavor pointless. 713 | * So I'll just leave it with set_fullscreen_mwm covering the entire 714 | * multi-monitor area for now. 715 | */ 716 | 717 | struct mwm_hints { 718 | unsigned long flags; 719 | unsigned long functions; 720 | unsigned long decorations; 721 | long input_mode; 722 | unsigned long status; 723 | }; 724 | 725 | #define MWM_HINTS_DECORATIONS 2 726 | #define MWM_DECOR_ALL 1 727 | 728 | static void set_fullscreen_mwm(int fs) 729 | { 730 | struct mwm_hints hints; 731 | int scr_width, scr_height; 732 | 733 | if(fs) { 734 | get_window_pos(&prev_win_x, &prev_win_y); 735 | get_window_size(&prev_win_width, &prev_win_height); 736 | get_screen_size(&scr_width, &scr_height); 737 | 738 | hints.decorations = 0; 739 | hints.flags = MWM_HINTS_DECORATIONS; 740 | XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32, 741 | PropModeReplace, (unsigned char*)&hints, 5); 742 | 743 | XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height); 744 | } else { 745 | XDeleteProperty(dpy, win, xa_motif_wm_hints); 746 | XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height); 747 | } 748 | } 749 | 750 | static int have_netwm_fullscr(void) 751 | { 752 | int fmt; 753 | long offs = 0; 754 | unsigned long i, count, rem; 755 | Atom *prop, type; 756 | Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False); 757 | 758 | do { 759 | XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType, 760 | &type, &fmt, &count, &rem, (unsigned char**)&prop); 761 | 762 | for(i=0; i 0); 771 | 772 | return 0; 773 | } 774 | 775 | static void set_fullscreen_ewmh(int fs) 776 | { 777 | XClientMessageEvent msg = {0}; 778 | 779 | msg.type = ClientMessage; 780 | msg.window = win; 781 | msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */ 782 | msg.format = 32; 783 | msg.data.l[0] = fs ? 1 : 0; 784 | msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */ 785 | msg.data.l[2] = 0; 786 | msg.data.l[3] = 1; /* source regular application */ 787 | XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg); 788 | } 789 | 790 | static void set_fullscreen(int fs) 791 | { 792 | if(fullscreen == fs) return; 793 | 794 | if(xa_net_wm_state && xa_net_wm_state_fullscr) { 795 | set_fullscreen_ewmh(fs); 796 | fullscreen = fs; 797 | } else if(xa_motif_wm_hints) { 798 | set_fullscreen_mwm(fs); 799 | fullscreen = fs; 800 | } 801 | } 802 | 803 | void glutPositionWindow(int x, int y) 804 | { 805 | set_fullscreen(0); 806 | XMoveWindow(dpy, win, x, y); 807 | } 808 | 809 | void glutReshapeWindow(int xsz, int ysz) 810 | { 811 | set_fullscreen(0); 812 | XResizeWindow(dpy, win, xsz, ysz); 813 | } 814 | 815 | void glutFullScreen(void) 816 | { 817 | set_fullscreen(1); 818 | } 819 | 820 | void glutSetWindowTitle(const char *title) 821 | { 822 | XTextProperty tprop; 823 | if(!XStringListToTextProperty((char**)&title, 1, &tprop)) { 824 | return; 825 | } 826 | XSetWMName(dpy, win, &tprop); 827 | XFree(tprop.value); 828 | } 829 | 830 | void glutSetIconTitle(const char *title) 831 | { 832 | XTextProperty tprop; 833 | if(!XStringListToTextProperty((char**)&title, 1, &tprop)) { 834 | return; 835 | } 836 | XSetWMIconName(dpy, win, &tprop); 837 | XFree(tprop.value); 838 | } 839 | 840 | void glutSetCursor(int cidx) 841 | { 842 | Cursor cur = None; 843 | 844 | switch(cidx) { 845 | case GLUT_CURSOR_LEFT_ARROW: 846 | cur = XCreateFontCursor(dpy, XC_left_ptr); 847 | break; 848 | case GLUT_CURSOR_INHERIT: 849 | break; 850 | case GLUT_CURSOR_NONE: 851 | cur = blank_cursor; 852 | break; 853 | default: 854 | return; 855 | } 856 | 857 | XDefineCursor(dpy, win, cur); 858 | cur_cursor = cidx; 859 | } 860 | 861 | void glutWarpPointer(int x, int y) 862 | { 863 | XWarpPointer(dpy, None, win, 0, 0, 0, 0, x, y); 864 | } 865 | 866 | void glutSetColor(int idx, float r, float g, float b) 867 | { 868 | XColor color; 869 | 870 | if(idx >= 0 && idx < cmap_size) { 871 | color.pixel = idx; 872 | color.red = (unsigned short)(r * 65535.0f); 873 | color.green = (unsigned short)(g * 65535.0f); 874 | color.blue = (unsigned short)(b * 65535.0f); 875 | color.flags = DoRed | DoGreen | DoBlue; 876 | XStoreColor(dpy, cmap, &color); 877 | } 878 | } 879 | 880 | float glutGetColor(int idx, int comp) 881 | { 882 | XColor color; 883 | 884 | if(idx < 0 || idx >= cmap_size) { 885 | return -1.0f; 886 | } 887 | 888 | color.pixel = idx; 889 | XQueryColor(dpy, cmap, &color); 890 | switch(comp) { 891 | case GLUT_RED: 892 | return color.red / 65535.0f; 893 | case GLUT_GREEN: 894 | return color.green / 65535.0f; 895 | case GLUT_BLUE: 896 | return color.blue / 65535.0f; 897 | default: 898 | break; 899 | } 900 | return -1.0f; 901 | } 902 | 903 | void glutSetKeyRepeat(int repmode) 904 | { 905 | if(repmode) { 906 | XAutoRepeatOn(dpy); 907 | } else { 908 | XAutoRepeatOff(dpy); 909 | } 910 | } 911 | 912 | static XVisualInfo *choose_visual(unsigned int mode) 913 | { 914 | XVisualInfo *vi; 915 | int attr[32]; 916 | int *aptr = attr; 917 | int *samples = 0; 918 | 919 | if(mode & GLUT_DOUBLE) { 920 | *aptr++ = GLX_DOUBLEBUFFER; 921 | } 922 | 923 | if(mode & GLUT_INDEX) { 924 | *aptr++ = GLX_BUFFER_SIZE; 925 | *aptr++ = 1; 926 | } else { 927 | *aptr++ = GLX_RGBA; 928 | *aptr++ = GLX_RED_SIZE; *aptr++ = 1; 929 | *aptr++ = GLX_GREEN_SIZE; *aptr++ = 1; 930 | *aptr++ = GLX_BLUE_SIZE; *aptr++ = 1; 931 | } 932 | if(mode & GLUT_ALPHA) { 933 | *aptr++ = GLX_ALPHA_SIZE; 934 | *aptr++ = 4; 935 | } 936 | if(mode & GLUT_DEPTH) { 937 | *aptr++ = GLX_DEPTH_SIZE; 938 | *aptr++ = 8; 939 | } 940 | if(mode & GLUT_STENCIL) { 941 | *aptr++ = GLX_STENCIL_SIZE; 942 | *aptr++ = 1; 943 | } 944 | if(mode & GLUT_ACCUM) { 945 | *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1; 946 | *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1; 947 | *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1; 948 | } 949 | if(mode & GLUT_STEREO) { 950 | *aptr++ = GLX_STEREO; 951 | } 952 | if(mode & GLUT_SRGB) { 953 | *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB; 954 | } 955 | if(mode & GLUT_MULTISAMPLE) { 956 | *aptr++ = GLX_SAMPLE_BUFFERS_ARB; 957 | *aptr++ = 1; 958 | *aptr++ = GLX_SAMPLES_ARB; 959 | samples = aptr; 960 | *aptr++ = 32; 961 | } 962 | *aptr++ = None; 963 | 964 | if(!samples) { 965 | return glXChooseVisual(dpy, scr, attr); 966 | } 967 | while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) { 968 | *samples >>= 1; 969 | if(!*samples) { 970 | aptr[-3] = None; 971 | } 972 | } 973 | return vi; 974 | } 975 | 976 | static void create_window(const char *title) 977 | { 978 | XSetWindowAttributes xattr = {0}; 979 | XVisualInfo *vi; 980 | unsigned int xattr_mask; 981 | unsigned int mode = init_mode; 982 | 983 | if(!(vi = choose_visual(mode))) { 984 | mode &= ~GLUT_SRGB; 985 | if(!(vi = choose_visual(mode))) { 986 | panic("Failed to find compatible visual\n"); 987 | } 988 | } 989 | 990 | if(!(ctx = glXCreateContext(dpy, vi, 0, True))) { 991 | XFree(vi); 992 | panic("Failed to create OpenGL context\n"); 993 | } 994 | 995 | glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize); 996 | glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize); 997 | glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize); 998 | glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize); 999 | glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize); 1000 | glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize); 1001 | glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf); 1002 | glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo); 1003 | glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples); 1004 | glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb); 1005 | 1006 | if(!(cmap = XCreateColormap(dpy, root, vi->visual, mode & GLUT_INDEX ? AllocAll : AllocNone))) { 1007 | XFree(vi); 1008 | glXDestroyContext(dpy, ctx); 1009 | panic("Failed to create colormap\n"); 1010 | } 1011 | cmap_size = GLUT_INDEX ? vi->colormap_size : 0; 1012 | 1013 | xattr.background_pixel = BlackPixel(dpy, scr); 1014 | xattr.colormap = cmap; 1015 | xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel; 1016 | if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0, 1017 | vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) { 1018 | XFree(vi); 1019 | glXDestroyContext(dpy, ctx); 1020 | XFreeColormap(dpy, cmap); 1021 | panic("Failed to create window\n"); 1022 | } 1023 | XFree(vi); 1024 | 1025 | XSelectInput(dpy, win, evmask); 1026 | 1027 | spnav_window(win); 1028 | 1029 | glutSetWindowTitle(title); 1030 | glutSetIconTitle(title); 1031 | XSetWMProtocols(dpy, win, &xa_wm_del_win, 1); 1032 | XMapWindow(dpy, win); 1033 | 1034 | glXMakeCurrent(dpy, win, ctx); 1035 | } 1036 | 1037 | static void get_window_pos(int *x, int *y) 1038 | { 1039 | Window child; 1040 | XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child); 1041 | } 1042 | 1043 | static void get_window_size(int *w, int *h) 1044 | { 1045 | XWindowAttributes wattr; 1046 | XGetWindowAttributes(dpy, win, &wattr); 1047 | *w = wattr.width; 1048 | *h = wattr.height; 1049 | } 1050 | 1051 | static void get_screen_size(int *scrw, int *scrh) 1052 | { 1053 | XWindowAttributes wattr; 1054 | XGetWindowAttributes(dpy, root, &wattr); 1055 | *scrw = wattr.width; 1056 | *scrh = wattr.height; 1057 | } 1058 | 1059 | 1060 | /* spaceball */ 1061 | enum { 1062 | CMD_APP_WINDOW = 27695, 1063 | CMD_APP_SENS 1064 | }; 1065 | 1066 | static Window get_daemon_window(Display *dpy); 1067 | static int catch_badwin(Display *dpy, XErrorEvent *err); 1068 | 1069 | #define SPNAV_INITIALIZED (xa_motion_event) 1070 | 1071 | static int spnav_window(Window win) 1072 | { 1073 | int (*prev_xerr_handler)(Display*, XErrorEvent*); 1074 | XEvent xev; 1075 | Window daemon_win; 1076 | 1077 | if(!SPNAV_INITIALIZED) { 1078 | return -1; 1079 | } 1080 | 1081 | if(!(daemon_win = get_daemon_window(dpy))) { 1082 | has_sball = sball_nbuttons = 0; 1083 | return -1; 1084 | } 1085 | has_sball = 1; 1086 | sball_nbuttons = 2; 1087 | 1088 | prev_xerr_handler = XSetErrorHandler(catch_badwin); 1089 | 1090 | xev.type = ClientMessage; 1091 | xev.xclient.send_event = False; 1092 | xev.xclient.display = dpy; 1093 | xev.xclient.window = win; 1094 | xev.xclient.message_type = xa_command_event; 1095 | xev.xclient.format = 16; 1096 | xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16; 1097 | xev.xclient.data.s[1] = (unsigned int)win & 0xffff; 1098 | xev.xclient.data.s[2] = CMD_APP_WINDOW; 1099 | 1100 | XSendEvent(dpy, daemon_win, False, 0, &xev); 1101 | XSync(dpy, False); 1102 | 1103 | XSetErrorHandler(prev_xerr_handler); 1104 | return 0; 1105 | } 1106 | 1107 | static Bool match_events(Display *dpy, XEvent *xev, char *arg) 1108 | { 1109 | int evtype = *(int*)arg; 1110 | 1111 | if(xev->type != ClientMessage) { 1112 | return False; 1113 | } 1114 | 1115 | if(xev->xclient.message_type == xa_motion_event) { 1116 | return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False; 1117 | } 1118 | if(xev->xclient.message_type == xa_button_press_event || 1119 | xev->xclient.message_type == xa_button_release_event) { 1120 | return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False; 1121 | } 1122 | return False; 1123 | } 1124 | 1125 | static int spnav_remove_events(int type) 1126 | { 1127 | int rm_count = 0; 1128 | XEvent xev; 1129 | while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) { 1130 | rm_count++; 1131 | } 1132 | return rm_count; 1133 | } 1134 | 1135 | static int spnav_event(const XEvent *xev, union spnav_event *event) 1136 | { 1137 | int i; 1138 | int xmsg_type; 1139 | 1140 | xmsg_type = xev->xclient.message_type; 1141 | 1142 | if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event && 1143 | xmsg_type != xa_button_release_event) { 1144 | return 0; 1145 | } 1146 | 1147 | if(xmsg_type == xa_motion_event) { 1148 | event->type = SPNAV_EVENT_MOTION; 1149 | event->motion.data = &event->motion.x; 1150 | 1151 | for(i=0; i<6; i++) { 1152 | event->motion.data[i] = xev->xclient.data.s[i + 2]; 1153 | } 1154 | event->motion.period = xev->xclient.data.s[8]; 1155 | } else { 1156 | event->type = SPNAV_EVENT_BUTTON; 1157 | event->button.press = xmsg_type == xa_button_press_event ? 1 : 0; 1158 | event->button.bnum = xev->xclient.data.s[2]; 1159 | } 1160 | return event->type; 1161 | } 1162 | 1163 | static int mglut_strcmp(const char *s1, const char *s2) 1164 | { 1165 | while(*s1 && *s1 == *s2) { 1166 | s1++; 1167 | s2++; 1168 | } 1169 | return *s1 - *s2; 1170 | } 1171 | 1172 | static Window get_daemon_window(Display *dpy) 1173 | { 1174 | Window win; 1175 | XTextProperty wname; 1176 | Atom type; 1177 | int fmt; 1178 | unsigned long nitems, bytes_after; 1179 | unsigned char *prop; 1180 | 1181 | XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType, 1182 | &type, &fmt, &nitems, &bytes_after, &prop); 1183 | if(!prop) { 1184 | return 0; 1185 | } 1186 | 1187 | win = *(Window*)prop; 1188 | XFree(prop); 1189 | 1190 | wname.value = 0; 1191 | if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) { 1192 | win = 0; 1193 | } 1194 | XFree(wname.value); 1195 | 1196 | return win; 1197 | } 1198 | 1199 | static int catch_badwin(Display *dpy, XErrorEvent *err) 1200 | { 1201 | has_sball = sball_nbuttons = 0; 1202 | return 0; 1203 | } 1204 | 1205 | 1206 | 1207 | #endif /* BUILD_X11 */ 1208 | 1209 | 1210 | /* --------------- windows implementation ----------------- */ 1211 | #ifdef BUILD_WIN32 1212 | static int reshape_pending; 1213 | 1214 | static void init_sball(void); 1215 | static void shutdown_sball(void); 1216 | static int handle_6dof(MSG *msg); 1217 | static void update_modkeys(void); 1218 | static int translate_vkey(int vkey); 1219 | static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam); 1220 | 1221 | #ifdef MINIGLUT_WINMAIN 1222 | int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd) 1223 | { 1224 | int argc = 1; 1225 | char *argv[] = { "miniglut.exe", 0 }; 1226 | return main(argc, argv); 1227 | } 1228 | #endif 1229 | 1230 | void glutMainLoopEvent(void) 1231 | { 1232 | MSG msg; 1233 | 1234 | if(!cb_display) { 1235 | panic("display callback not set"); 1236 | } 1237 | 1238 | if(reshape_pending && cb_reshape) { 1239 | reshape_pending = 0; 1240 | get_window_size(&win_width, &win_height); 1241 | cb_reshape(win_width, win_height); 1242 | } 1243 | 1244 | if(!upd_pending && !cb_idle) { 1245 | GetMessage(&msg, 0, 0, 0); 1246 | if(!handle_6dof(&msg)) { 1247 | TranslateMessage(&msg); 1248 | DispatchMessage(&msg); 1249 | } 1250 | if(quit) return; 1251 | } 1252 | while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { 1253 | if(!handle_6dof(&msg)) { 1254 | TranslateMessage(&msg); 1255 | DispatchMessage(&msg); 1256 | } 1257 | if(quit) return; 1258 | } 1259 | 1260 | if(cb_idle) { 1261 | cb_idle(); 1262 | } 1263 | 1264 | if(upd_pending && mapped) { 1265 | upd_pending = 0; 1266 | cb_display(); 1267 | } 1268 | } 1269 | 1270 | static void cleanup(void) 1271 | { 1272 | shutdown_sball(); 1273 | 1274 | if(win) { 1275 | wglMakeCurrent(dc, 0); 1276 | wglDeleteContext(ctx); 1277 | UnregisterClass("MiniGLUT", hinst); 1278 | } 1279 | } 1280 | 1281 | void glutSwapBuffers(void) 1282 | { 1283 | SwapBuffers(dc); 1284 | } 1285 | 1286 | void glutPositionWindow(int x, int y) 1287 | { 1288 | RECT rect; 1289 | unsigned int flags = SWP_SHOWWINDOW; 1290 | 1291 | if(fullscreen) { 1292 | rect.left = prev_win_x; 1293 | rect.top = prev_win_y; 1294 | rect.right = rect.left + prev_win_width; 1295 | rect.bottom = rect.top + prev_win_height; 1296 | SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW); 1297 | fullscreen = 0; 1298 | flags |= SWP_FRAMECHANGED; 1299 | } else { 1300 | GetWindowRect(win, &rect); 1301 | } 1302 | SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags); 1303 | } 1304 | 1305 | static void calc_win_rect(RECT *rect, int x, int y, int w, int h) 1306 | { 1307 | rect->left = x; 1308 | rect->top = y; 1309 | rect->right = x + w; 1310 | rect->bottom = y + h; 1311 | AdjustWindowRect(rect, WS_OVERLAPPEDWINDOW, 0); 1312 | } 1313 | 1314 | void glutReshapeWindow(int xsz, int ysz) 1315 | { 1316 | RECT rect; 1317 | unsigned int flags = SWP_SHOWWINDOW; 1318 | 1319 | if(fullscreen) { 1320 | calc_win_rect(&rect, prev_win_x, prev_win_y, xsz, ysz); 1321 | SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW); 1322 | fullscreen = 0; 1323 | flags |= SWP_FRAMECHANGED; 1324 | } else { 1325 | GetWindowRect(win, &rect); 1326 | calc_win_rect(&rect, rect.left, rect.top, xsz, ysz); 1327 | } 1328 | 1329 | xsz = rect.right - rect.left; 1330 | ysz = rect.bottom - rect.top; 1331 | SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags); 1332 | } 1333 | 1334 | void glutFullScreen(void) 1335 | { 1336 | RECT rect; 1337 | int scr_width, scr_height; 1338 | 1339 | if(fullscreen) return; 1340 | 1341 | GetWindowRect(win, &rect); 1342 | prev_win_x = rect.left; 1343 | prev_win_y = rect.top; 1344 | prev_win_width = rect.right - rect.left; 1345 | prev_win_height = rect.bottom - rect.top; 1346 | 1347 | get_screen_size(&scr_width, &scr_height); 1348 | 1349 | SetWindowLong(win, GWL_STYLE, 0); 1350 | SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW); 1351 | 1352 | fullscreen = 1; 1353 | } 1354 | 1355 | void glutSetWindowTitle(const char *title) 1356 | { 1357 | SetWindowText(win, title); 1358 | } 1359 | 1360 | void glutSetIconTitle(const char *title) 1361 | { 1362 | } 1363 | 1364 | void glutSetCursor(int cidx) 1365 | { 1366 | switch(cidx) { 1367 | case GLUT_CURSOR_NONE: 1368 | ShowCursor(0); 1369 | break; 1370 | case GLUT_CURSOR_INHERIT: 1371 | case GLUT_CURSOR_LEFT_ARROW: 1372 | default: 1373 | SetCursor(LoadCursor(0, IDC_ARROW)); 1374 | ShowCursor(1); 1375 | } 1376 | } 1377 | 1378 | void glutWarpPointer(int x, int y) 1379 | { 1380 | POINT pt; 1381 | pt.x = x; 1382 | pt.y = y; 1383 | 1384 | ClientToScreen(win, &pt); 1385 | SetCursorPos(pt.x, pt.y); 1386 | } 1387 | 1388 | void glutSetColor(int idx, float r, float g, float b) 1389 | { 1390 | PALETTEENTRY col; 1391 | 1392 | if(idx < 0 || idx >= 256 || !cmap) { 1393 | return; 1394 | } 1395 | 1396 | col.peRed = (int)(r * 255.0f); 1397 | col.peGreen = (int)(g * 255.0f); 1398 | col.peBlue = (int)(b * 255.0f); 1399 | col.peFlags = PC_NOCOLLAPSE; 1400 | 1401 | SetPaletteEntries(cmap, idx, 1, &col); 1402 | 1403 | if(dc) { 1404 | UnrealizeObject(cmap); 1405 | SelectPalette(dc, cmap, 0); 1406 | RealizePalette(dc); 1407 | } 1408 | } 1409 | 1410 | float glutGetColor(int idx, int comp) 1411 | { 1412 | PALETTEENTRY col; 1413 | 1414 | if(idx < 0 || idx >= 256 || !cmap) { 1415 | return -1.0f; 1416 | } 1417 | 1418 | if(!GetPaletteEntries(cmap, idx, 1, &col)) { 1419 | return -1.0f; 1420 | } 1421 | 1422 | switch(comp) { 1423 | case GLUT_RED: 1424 | return col.peRed / 255.0f; 1425 | case GLUT_GREEN: 1426 | return col.peGreen / 255.0f; 1427 | case GLUT_BLUE: 1428 | return col.peBlue / 255.0f; 1429 | default: 1430 | break; 1431 | } 1432 | return -1.0f; 1433 | } 1434 | 1435 | void glutSetKeyRepeat(int repmode) 1436 | { 1437 | } 1438 | 1439 | #define WGL_DRAW_TO_WINDOW 0x2001 1440 | #define WGL_ACCELERATION 0x2003 1441 | #define WGL_SUPPORT_OPENGL 0x2010 1442 | #define WGL_DOUBLE_BUFFER 0x2011 1443 | #define WGL_STEREO 0x2012 1444 | #define WGL_PIXEL_TYPE 0x2013 1445 | #define WGL_COLOR_BITS 0x2014 1446 | #define WGL_RED_BITS 0x2015 1447 | #define WGL_GREEN_BITS 0x2017 1448 | #define WGL_BLUE_BITS 0x2019 1449 | #define WGL_ALPHA_BITS 0x201b 1450 | #define WGL_ACCUM_BITS 0x201d 1451 | #define WGL_DEPTH_BITS 0x2022 1452 | #define WGL_STENCIL_BITS 0x2023 1453 | #define WGL_FULL_ACCELERATION 0x2027 1454 | 1455 | #define WGL_TYPE_RGBA 0x202b 1456 | #define WGL_TYPE_COLORINDEX 0x202c 1457 | 1458 | #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 1459 | #define WGL_SAMPLE_BUFFERS_ARB 0x2041 1460 | #define WGL_SAMPLES_ARB 0x2042 1461 | 1462 | static PROC wglChoosePixelFormat; 1463 | static PROC wglGetPixelFormatAttribiv; 1464 | 1465 | #define ATTR(a, v) \ 1466 | do { *aptr++ = (a); *aptr++ = (v); } while(0) 1467 | 1468 | static unsigned int choose_pixfmt(unsigned int mode) 1469 | { 1470 | unsigned int num_pixfmt, pixfmt = 0; 1471 | int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1, 1472 | WGL_ACCELERATION, WGL_FULL_ACCELERATION }; 1473 | float fattr[2] = {0, 0}; 1474 | 1475 | int *aptr = attr + 6; 1476 | int *samples = 0; 1477 | 1478 | if(mode & GLUT_DOUBLE) { 1479 | ATTR(WGL_DOUBLE_BUFFER, 1); 1480 | } 1481 | 1482 | ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA); 1483 | ATTR(WGL_COLOR_BITS, mode & GLUT_INDEX ? 8 : 24); 1484 | if(mode & GLUT_ALPHA) { 1485 | ATTR(WGL_ALPHA_BITS, 4); 1486 | } 1487 | if(mode & GLUT_DEPTH) { 1488 | ATTR(WGL_DEPTH_BITS, 16); 1489 | } 1490 | if(mode & GLUT_STENCIL) { 1491 | ATTR(WGL_STENCIL_BITS, 1); 1492 | } 1493 | if(mode & GLUT_ACCUM) { 1494 | ATTR(WGL_ACCUM_BITS, 1); 1495 | } 1496 | if(mode & GLUT_STEREO) { 1497 | ATTR(WGL_STEREO, 1); 1498 | } 1499 | if(mode & GLUT_SRGB) { 1500 | ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1); 1501 | } 1502 | if(mode & GLUT_MULTISAMPLE) { 1503 | ATTR(WGL_SAMPLE_BUFFERS_ARB, 1); 1504 | *aptr++ = WGL_SAMPLES_ARB; 1505 | samples = aptr; 1506 | *aptr++ = 32; 1507 | } 1508 | *aptr++ = 0; 1509 | 1510 | while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) { 1511 | *samples >>= 1; 1512 | if(!*samples) { 1513 | aptr[-3] = 0; 1514 | } 1515 | } 1516 | return pixfmt; 1517 | } 1518 | 1519 | static PIXELFORMATDESCRIPTOR pfd; 1520 | static PIXELFORMATDESCRIPTOR tmppfd = { 1521 | sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, 1522 | PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0, 1523 | PFD_MAIN_PLANE, 0, 0, 0, 0 1524 | }; 1525 | #define TMPCLASS "TempMiniGLUT" 1526 | 1527 | #define GETATTR(attr, vptr) \ 1528 | do { \ 1529 | int gattr = attr; \ 1530 | wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \ 1531 | } while(0) 1532 | 1533 | static int create_window_wglext(const char *title, int width, int height) 1534 | { 1535 | WNDCLASSEX wc = {0}; 1536 | HWND tmpwin = 0; 1537 | HDC tmpdc = 0; 1538 | HGLRC tmpctx = 0; 1539 | int pixfmt; 1540 | 1541 | /* create a temporary window and GL context, just to query and retrieve 1542 | * the wglChoosePixelFormatEXT function 1543 | */ 1544 | wc.cbSize = sizeof wc; 1545 | wc.hbrBackground = GetStockObject(BLACK_BRUSH); 1546 | wc.hCursor = LoadCursor(0, IDC_ARROW); 1547 | wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION); 1548 | wc.hInstance = hinst; 1549 | wc.lpfnWndProc = DefWindowProc; 1550 | wc.lpszClassName = TMPCLASS; 1551 | wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 1552 | if(!RegisterClassEx(&wc)) { 1553 | return 0; 1554 | } 1555 | if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0, 1556 | width, height, 0, 0, hinst, 0))) { 1557 | goto fail; 1558 | } 1559 | tmpdc = GetDC(tmpwin); 1560 | 1561 | if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) || 1562 | !SetPixelFormat(tmpdc, pixfmt, &tmppfd) || 1563 | !(tmpctx = wglCreateContext(tmpdc))) { 1564 | goto fail; 1565 | } 1566 | wglMakeCurrent(tmpdc, tmpctx); 1567 | 1568 | if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) { 1569 | if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) { 1570 | goto fail; 1571 | } 1572 | if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) { 1573 | goto fail; 1574 | } 1575 | } else { 1576 | if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) { 1577 | goto fail; 1578 | } 1579 | } 1580 | wglMakeCurrent(0, 0); 1581 | wglDeleteContext(tmpctx); 1582 | DestroyWindow(tmpwin); 1583 | UnregisterClass(TMPCLASS, hinst); 1584 | 1585 | /* create the real window and context */ 1586 | if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x, 1587 | init_y, width, height, 0, 0, hinst, 0))) { 1588 | panic("Failed to create window\n"); 1589 | } 1590 | dc = GetDC(win); 1591 | 1592 | if(!(pixfmt = choose_pixfmt(init_mode))) { 1593 | panic("Failed to find suitable pixel format\n"); 1594 | } 1595 | if(!SetPixelFormat(dc, pixfmt, &pfd)) { 1596 | panic("Failed to set the selected pixel format\n"); 1597 | } 1598 | if(!(ctx = wglCreateContext(dc))) { 1599 | panic("Failed to create the OpenGL context\n"); 1600 | } 1601 | wglMakeCurrent(dc, ctx); 1602 | 1603 | GETATTR(WGL_RED_BITS, &ctx_info.rsize); 1604 | GETATTR(WGL_GREEN_BITS, &ctx_info.gsize); 1605 | GETATTR(WGL_BLUE_BITS, &ctx_info.bsize); 1606 | GETATTR(WGL_ALPHA_BITS, &ctx_info.asize); 1607 | GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize); 1608 | GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize); 1609 | GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf); 1610 | GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb); 1611 | GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples); 1612 | 1613 | return 0; 1614 | 1615 | fail: 1616 | if(tmpctx) { 1617 | wglMakeCurrent(0, 0); 1618 | wglDeleteContext(tmpctx); 1619 | } 1620 | if(tmpwin) { 1621 | DestroyWindow(tmpwin); 1622 | } 1623 | UnregisterClass(TMPCLASS, hinst); 1624 | return -1; 1625 | } 1626 | 1627 | static void create_window(const char *title) 1628 | { 1629 | RECT rect; 1630 | int i, pixfmt, width, height; 1631 | char palbuf[sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY)]; 1632 | LOGPALETTE *logpal; 1633 | 1634 | calc_win_rect(&rect, init_x, init_y, init_width, init_height); 1635 | width = rect.right - rect.left; 1636 | height = rect.bottom - rect.top; 1637 | 1638 | memset(&pfd, 0, sizeof pfd); 1639 | pfd.nSize = sizeof pfd; 1640 | pfd.nVersion = 1; 1641 | pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 1642 | if(init_mode & GLUT_STEREO) { 1643 | pfd.dwFlags |= PFD_STEREO; 1644 | } 1645 | if(init_mode & GLUT_INDEX) { 1646 | pfd.iPixelType = PFD_TYPE_COLORINDEX; 1647 | pfd.cColorBits = 8; 1648 | } else { 1649 | pfd.iPixelType = PFD_TYPE_RGBA; 1650 | pfd.cColorBits = 24; 1651 | } 1652 | if(init_mode & GLUT_ALPHA) { 1653 | pfd.cAlphaBits = 8; 1654 | } 1655 | if(init_mode & GLUT_ACCUM) { 1656 | pfd.cAccumBits = 24; 1657 | } 1658 | if(init_mode & GLUT_DEPTH) { 1659 | pfd.cDepthBits = 24; 1660 | } 1661 | if(init_mode & GLUT_STENCIL) { 1662 | pfd.cStencilBits = 8; 1663 | } 1664 | pfd.iLayerType = PFD_MAIN_PLANE; 1665 | 1666 | if(init_mode & (GLUT_SRGB | GLUT_MULTISAMPLE)) { 1667 | if(create_window_wglext(title, width, height) != -1) { 1668 | goto ctxdone; 1669 | } 1670 | } 1671 | 1672 | /* if we don't need sRGB or multisample, or if the wglChoosePixelFormat method 1673 | * failed, just use the old-style ChoosePixelFormat method instead 1674 | */ 1675 | if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, 1676 | rect.left, rect.top, width, height, 0, 0, hinst, 0))) { 1677 | panic("Failed to create window\n"); 1678 | } 1679 | dc = GetDC(win); 1680 | 1681 | if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) { 1682 | panic("Failed to find suitable pixel format\n"); 1683 | } 1684 | if(!SetPixelFormat(dc, pixfmt, &pfd)) { 1685 | panic("Failed to set the selected pixel format\n"); 1686 | } 1687 | if(!(ctx = wglCreateContext(dc))) { 1688 | panic("Failed to create the OpenGL context\n"); 1689 | } 1690 | wglMakeCurrent(dc, ctx); 1691 | 1692 | DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd); 1693 | ctx_info.rsize = pfd.cRedBits; 1694 | ctx_info.gsize = pfd.cGreenBits; 1695 | ctx_info.bsize = pfd.cBlueBits; 1696 | ctx_info.asize = pfd.cAlphaBits; 1697 | ctx_info.zsize = pfd.cDepthBits; 1698 | ctx_info.ssize = pfd.cStencilBits; 1699 | ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0; 1700 | ctx_info.samples = 0; 1701 | ctx_info.srgb = 0; 1702 | 1703 | ctxdone: 1704 | ShowWindow(win, 1); 1705 | SetForegroundWindow(win); 1706 | SetFocus(win); 1707 | 1708 | if(init_mode & GLUT_INDEX) { 1709 | logpal = (LOGPALETTE*)palbuf; 1710 | 1711 | GetSystemPaletteEntries(dc, 0, 256, logpal->palPalEntry); 1712 | 1713 | logpal->palVersion = 0x300; 1714 | logpal->palNumEntries = 256; 1715 | 1716 | if(!(cmap = CreatePalette(logpal))) { 1717 | panic("Failed to create palette in indexed mode"); 1718 | } 1719 | SelectPalette(dc, cmap, 0); 1720 | RealizePalette(dc); 1721 | 1722 | cmap_size = 256; 1723 | } else { 1724 | if(GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES) <= 8) { 1725 | /* for RGB mode in 8bpp displays we also need to set up a palette 1726 | * with RGB 332 colors 1727 | */ 1728 | logpal = (LOGPALETTE*)palbuf; 1729 | 1730 | logpal->palVersion = 0x300; 1731 | logpal->palNumEntries = 256; 1732 | 1733 | for(i=0; i<256; i++) { 1734 | int r = i & 7; 1735 | int g = (i >> 3) & 7; 1736 | int b = (i >> 5) & 3; 1737 | 1738 | logpal->palPalEntry[i].peRed = (r << 5) | (r << 2) | (r >> 1); 1739 | logpal->palPalEntry[i].peGreen = (g << 5) | (g << 2) | (g >> 1); 1740 | logpal->palPalEntry[i].peBlue = (b << 6) | (b << 4) | (b << 2) | b; 1741 | logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE; 1742 | } 1743 | 1744 | if((cmap = CreatePalette(logpal))) { 1745 | SelectPalette(dc, cmap, 0); 1746 | RealizePalette(dc); 1747 | cmap_size = 256; 1748 | } 1749 | } 1750 | } 1751 | 1752 | init_sball(); 1753 | 1754 | upd_pending = 1; 1755 | reshape_pending = 1; 1756 | } 1757 | 1758 | static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam) 1759 | { 1760 | static int mouse_x, mouse_y; 1761 | int x, y, key; 1762 | 1763 | switch(msg) { 1764 | case WM_CLOSE: 1765 | if(win) DestroyWindow(win); 1766 | break; 1767 | 1768 | case WM_DESTROY: 1769 | cleanup(); 1770 | quit = 1; 1771 | PostQuitMessage(0); 1772 | break; 1773 | 1774 | case WM_PAINT: 1775 | upd_pending = 1; 1776 | ValidateRect(win, 0); 1777 | break; 1778 | 1779 | case WM_SIZE: 1780 | x = lparam & 0xffff; 1781 | y = lparam >> 16; 1782 | if(x != win_width && y != win_height) { 1783 | win_width = x; 1784 | win_height = y; 1785 | if(cb_reshape) { 1786 | reshape_pending = 0; 1787 | cb_reshape(win_width, win_height); 1788 | } 1789 | } 1790 | break; 1791 | 1792 | case WM_SHOWWINDOW: 1793 | mapped = wparam; 1794 | if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE); 1795 | break; 1796 | 1797 | case WM_KEYDOWN: 1798 | case WM_SYSKEYDOWN: 1799 | update_modkeys(); 1800 | key = translate_vkey(wparam); 1801 | if(key < 256) { 1802 | if(cb_keydown) { 1803 | cb_keydown((unsigned char)key, mouse_x, mouse_y); 1804 | } 1805 | } else { 1806 | if(cb_skeydown) { 1807 | cb_skeydown(key, mouse_x, mouse_y); 1808 | } 1809 | } 1810 | break; 1811 | 1812 | case WM_KEYUP: 1813 | case WM_SYSKEYUP: 1814 | update_modkeys(); 1815 | key = translate_vkey(wparam); 1816 | if(key < 256) { 1817 | if(cb_keyup) { 1818 | cb_keyup((unsigned char)key, mouse_x, mouse_y); 1819 | } 1820 | } else { 1821 | if(cb_skeyup) { 1822 | cb_skeyup(key, mouse_x, mouse_y); 1823 | } 1824 | } 1825 | break; 1826 | 1827 | case WM_LBUTTONDOWN: 1828 | handle_mbutton(0, 1, wparam, lparam); 1829 | break; 1830 | case WM_MBUTTONDOWN: 1831 | handle_mbutton(1, 1, wparam, lparam); 1832 | break; 1833 | case WM_RBUTTONDOWN: 1834 | handle_mbutton(2, 1, wparam, lparam); 1835 | break; 1836 | case WM_LBUTTONUP: 1837 | handle_mbutton(0, 0, wparam, lparam); 1838 | break; 1839 | case WM_MBUTTONUP: 1840 | handle_mbutton(1, 0, wparam, lparam); 1841 | break; 1842 | case WM_RBUTTONUP: 1843 | handle_mbutton(2, 0, wparam, lparam); 1844 | break; 1845 | 1846 | case WM_MOUSEMOVE: 1847 | if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) { 1848 | if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16); 1849 | } else { 1850 | if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16); 1851 | } 1852 | break; 1853 | 1854 | case WM_SYSCOMMAND: 1855 | wparam &= 0xfff0; 1856 | if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) { 1857 | return 0; 1858 | } 1859 | default: 1860 | return DefWindowProc(win, msg, wparam, lparam); 1861 | } 1862 | 1863 | return 0; 1864 | } 1865 | 1866 | static void update_modkeys(void) 1867 | { 1868 | if(GetKeyState(VK_SHIFT) & 0x8000) { 1869 | modstate |= GLUT_ACTIVE_SHIFT; 1870 | } else { 1871 | modstate &= ~GLUT_ACTIVE_SHIFT; 1872 | } 1873 | if(GetKeyState(VK_CONTROL) & 0x8000) { 1874 | modstate |= GLUT_ACTIVE_CTRL; 1875 | } else { 1876 | modstate &= ~GLUT_ACTIVE_CTRL; 1877 | } 1878 | if(GetKeyState(VK_MENU) & 0x8000) { 1879 | modstate |= GLUT_ACTIVE_ALT; 1880 | } else { 1881 | modstate &= ~GLUT_ACTIVE_ALT; 1882 | } 1883 | } 1884 | 1885 | #ifndef VK_OEM_1 1886 | #define VK_OEM_1 0xba 1887 | #define VK_OEM_2 0xbf 1888 | #define VK_OEM_3 0xc0 1889 | #define VK_OEM_4 0xdb 1890 | #define VK_OEM_5 0xdc 1891 | #define VK_OEM_6 0xdd 1892 | #define VK_OEM_7 0xde 1893 | #endif 1894 | 1895 | static int translate_vkey(int vkey) 1896 | { 1897 | switch(vkey) { 1898 | case VK_PRIOR: return GLUT_KEY_PAGE_UP; 1899 | case VK_NEXT: return GLUT_KEY_PAGE_DOWN; 1900 | case VK_END: return GLUT_KEY_END; 1901 | case VK_HOME: return GLUT_KEY_HOME; 1902 | case VK_LEFT: return GLUT_KEY_LEFT; 1903 | case VK_UP: return GLUT_KEY_UP; 1904 | case VK_RIGHT: return GLUT_KEY_RIGHT; 1905 | case VK_DOWN: return GLUT_KEY_DOWN; 1906 | case VK_OEM_1: return ';'; 1907 | case VK_OEM_2: return '/'; 1908 | case VK_OEM_3: return '`'; 1909 | case VK_OEM_4: return '['; 1910 | case VK_OEM_5: return '\\'; 1911 | case VK_OEM_6: return ']'; 1912 | case VK_OEM_7: return '\''; 1913 | default: 1914 | break; 1915 | } 1916 | 1917 | if(vkey >= 'A' && vkey <= 'Z') { 1918 | vkey += 32; 1919 | } else if(vkey >= VK_F1 && vkey <= VK_F12) { 1920 | vkey -= VK_F1 + GLUT_KEY_F1; 1921 | } 1922 | 1923 | return vkey; 1924 | } 1925 | 1926 | static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam) 1927 | { 1928 | int x, y; 1929 | 1930 | update_modkeys(); 1931 | 1932 | if(cb_mouse) { 1933 | x = lparam & 0xffff; 1934 | y = lparam >> 16; 1935 | cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y); 1936 | } 1937 | } 1938 | 1939 | static void get_window_pos(int *x, int *y) 1940 | { 1941 | RECT rect; 1942 | GetWindowRect(win, &rect); 1943 | *x = rect.left; 1944 | *y = rect.top; 1945 | } 1946 | 1947 | static void get_window_size(int *w, int *h) 1948 | { 1949 | RECT rect; 1950 | GetClientRect(win, &rect); 1951 | *w = rect.right - rect.left; 1952 | *h = rect.bottom - rect.top; 1953 | } 1954 | 1955 | static void get_screen_size(int *scrw, int *scrh) 1956 | { 1957 | *scrw = GetSystemMetrics(SM_CXSCREEN); 1958 | *scrh = GetSystemMetrics(SM_CYSCREEN); 1959 | } 1960 | 1961 | 1962 | /* ---- 3DxWare SDK stuff for spaceballs ---- */ 1963 | #define SI_EVENT 1 1964 | 1965 | enum SpwRetVal { SPW_NO_ERROR, SPW_ERROR, SI_BAD_HANDLE, SI_BAD_ID, SI_BAD_VALUE, SI_IS_EVENT }; 1966 | typedef enum { SI_BUTTON_EVENT = 1, SI_MOTION_EVENT, SI_COMBO_EVENT, SI_ZERO_EVENT } SiEventType; 1967 | typedef struct { HWND win; void *transctl; DWORD pid; char exe[MAX_PATH]; int flag; } SiOpenData; 1968 | typedef struct { UINT msg; WPARAM wParam; LPARAM lParam; } SiGetEventData; 1969 | typedef struct { unsigned int last, current, pressed, released; } SiButtonData; 1970 | typedef struct { SiButtonData bData; long mData[6]; long period; } SiSpwData; 1971 | 1972 | typedef struct { 1973 | char firmware[128]; 1974 | int devType, numButtons, numDegrees; 1975 | long canBeep; 1976 | int majorVersion, minorVersion; 1977 | } SiDevInfo; 1978 | 1979 | typedef struct { 1980 | int type; 1981 | union { 1982 | SiSpwData spwData; 1983 | unsigned char eventsize[5120]; /* from SI_KEY_MAXBUF array in SiKeyboardData */ 1984 | } u; 1985 | } SiSpwEvent; 1986 | 1987 | typedef enum SpwRetVal (WINAPI *si_init_func)(void); 1988 | typedef void (WINAPI *si_term_func)(void); 1989 | typedef void *(WINAPI *si_open_func)(char*, int, void*, int, SiOpenData*); 1990 | typedef void (WINAPI *si_open_win_init_func)(SiOpenData*, HWND); 1991 | typedef enum SpwRetVal (WINAPI *si_close_func)(void*); 1992 | typedef enum SpwRetVal (WINAPI *si_get_dev_info_func)(void*, SiDevInfo*); 1993 | typedef enum SpwRetVal (WINAPI *si_getevent_func)(void*, int, SiGetEventData*, SiSpwEvent*); 1994 | typedef void (WINAPI *si_getev_win_init_func)(SiGetEventData*, UINT, WPARAM, LPARAM); 1995 | 1996 | static si_init_func SiInitialize; 1997 | static si_term_func SiTerminate; 1998 | static si_open_func SiOpen; 1999 | static si_open_win_init_func SiOpenWinInit; 2000 | static si_close_func SiClose; 2001 | static si_get_dev_info_func SiGetDeviceInfo; 2002 | static si_getevent_func SiGetEvent; 2003 | static si_getev_win_init_func SiGetEventWinInit; 2004 | 2005 | static void *siappdll; 2006 | static void *sidev; 2007 | 2008 | static void init_sball(void) 2009 | { 2010 | SiOpenData odat; 2011 | SiDevInfo devinfo; 2012 | 2013 | if(!siappdll && !(siappdll = LoadLibrary("siappdll.dll"))) { 2014 | return; 2015 | } 2016 | if(!(SiInitialize = (si_init_func)GetProcAddress(siappdll, "SiInitialize"))) return; 2017 | if(!(SiTerminate = (si_term_func)GetProcAddress(siappdll, "SiTerminate"))) return; 2018 | if(!(SiOpen = (si_open_func)GetProcAddress(siappdll, "SiOpen"))) return; 2019 | if(!(SiOpenWinInit = (si_open_win_init_func)GetProcAddress(siappdll, "SiOpenWinInit"))) return; 2020 | if(!(SiClose = (si_close_func)GetProcAddress(siappdll, "SiClose"))) return; 2021 | if(!(SiGetEvent = (si_getevent_func)GetProcAddress(siappdll, "SiGetEvent"))) return; 2022 | if(!(SiGetEventWinInit = (si_getev_win_init_func)GetProcAddress(siappdll, "SiGetEventWinInit"))) return; 2023 | 2024 | SiGetDeviceInfo = (si_get_dev_info_func)GetProcAddress(siappdll, "SiGetDeviceInfo"); 2025 | 2026 | if(SiInitialize() != 0) { 2027 | return; 2028 | } 2029 | SiOpenWinInit(&odat, win); 2030 | if(!(sidev = SiOpen("miniglut", -1, 0, SI_EVENT, &odat))) { 2031 | SiTerminate(); 2032 | return; 2033 | } 2034 | 2035 | has_sball = 1; 2036 | if(SiGetDeviceInfo && SiGetDeviceInfo(sidev, &devinfo) == 0) { 2037 | sball_nbuttons = devinfo.numButtons; 2038 | } else { 2039 | sball_nbuttons = 2; 2040 | } 2041 | } 2042 | 2043 | static void shutdown_sball(void) 2044 | { 2045 | if(sidev) { 2046 | SiClose(sidev); 2047 | } 2048 | if(siappdll) { 2049 | FreeLibrary(siappdll); 2050 | } 2051 | } 2052 | 2053 | static int handle_6dof(MSG* msg) 2054 | { 2055 | SiSpwEvent siev; 2056 | SiGetEventData sievdata; 2057 | 2058 | if(!sidev) return 0; 2059 | 2060 | SiGetEventWinInit(&sievdata, msg->message, msg->wParam, msg->lParam); 2061 | if(SiGetEvent(sidev, 0, &sievdata, &siev) != SI_IS_EVENT) { 2062 | return 0; 2063 | } 2064 | 2065 | switch(siev.type) { 2066 | case SI_MOTION_EVENT: 2067 | if(cb_sball_motion) { 2068 | cb_sball_motion(siev.u.spwData.mData[0], siev.u.spwData.mData[1], siev.u.spwData.mData[2]); 2069 | } 2070 | if(cb_sball_rotate) { 2071 | cb_sball_rotate(siev.u.spwData.mData[3], siev.u.spwData.mData[4], siev.u.spwData.mData[5]); 2072 | } 2073 | break; 2074 | 2075 | case SI_BUTTON_EVENT: 2076 | if(cb_sball_button) { 2077 | int idx = 0; 2078 | unsigned long diff = siev.u.spwData.bData.current ^ siev.u.spwData.bData.last; 2079 | unsigned long bnstate = siev.u.spwData.bData.current; 2080 | while(diff) { 2081 | if(diff & 1) { 2082 | cb_sball_button(idx, bnstate & 1); 2083 | } 2084 | diff >>= 1; 2085 | bnstate >>= 1; 2086 | idx++; 2087 | } 2088 | } 2089 | break; 2090 | 2091 | case SI_ZERO_EVENT: 2092 | if(cb_sball_motion) { 2093 | cb_sball_motion(0, 0, 0); 2094 | } 2095 | if(cb_sball_rotate) { 2096 | cb_sball_rotate(0, 0, 0); 2097 | } 2098 | break; 2099 | 2100 | default: 2101 | break; 2102 | } 2103 | return 1; 2104 | } 2105 | 2106 | 2107 | #endif /* BUILD_WIN32 */ 2108 | 2109 | #if defined(unix) || defined(__unix__) || defined(__APPLE__) 2110 | #include 2111 | 2112 | #ifndef MINIGLUT_NO_LIBC 2113 | #define sys_gettimeofday(tv, tz) gettimeofday(tv, tz) 2114 | #else 2115 | static int sys_gettimeofday(struct timeval *tv, struct timezone *tz); 2116 | #endif 2117 | 2118 | static long get_msec(void) 2119 | { 2120 | static struct timeval tv0; 2121 | struct timeval tv; 2122 | 2123 | sys_gettimeofday(&tv, 0); 2124 | if(tv0.tv_sec == 0 && tv0.tv_usec == 0) { 2125 | tv0 = tv; 2126 | return 0; 2127 | } 2128 | return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000; 2129 | } 2130 | #endif /* UNIX */ 2131 | #ifdef _WIN32 2132 | static long get_msec(void) 2133 | { 2134 | static long t0; 2135 | long tm; 2136 | 2137 | #ifdef MINIGLUT_NO_WINMM 2138 | tm = GetTickCount(); 2139 | #else 2140 | tm = timeGetTime(); 2141 | #endif 2142 | if(!t0) { 2143 | t0 = tm; 2144 | return 0; 2145 | } 2146 | return tm - t0; 2147 | } 2148 | #endif 2149 | 2150 | static void panic(const char *msg) 2151 | { 2152 | const char *end = msg; 2153 | while(*end) end++; 2154 | sys_write(2, msg, end - msg); 2155 | sys_exit(1); 2156 | } 2157 | 2158 | 2159 | #ifndef MINIGLUT_NO_LIBC 2160 | #include 2161 | #ifdef _WIN32 2162 | #include 2163 | #else 2164 | #include 2165 | #endif 2166 | 2167 | static void sys_exit(int status) 2168 | { 2169 | exit(status); 2170 | } 2171 | 2172 | static int sys_write(int fd, const void *buf, int count) 2173 | { 2174 | return write(fd, buf, count); 2175 | } 2176 | 2177 | #else /* MINIGLUT_NO_LIBC */ 2178 | 2179 | #ifdef __linux__ 2180 | #ifdef __x86_64__ 2181 | static void sys_exit(int status) 2182 | { 2183 | asm volatile( 2184 | "syscall\n\t" 2185 | :: "a"(60), "D"(status)); 2186 | } 2187 | static int sys_write(int fd, const void *buf, int count) 2188 | { 2189 | long res; 2190 | asm volatile( 2191 | "syscall\n\t" 2192 | : "=a"(res) 2193 | : "a"(1), "D"(fd), "S"(buf), "d"(count)); 2194 | return res; 2195 | } 2196 | static int sys_gettimeofday(struct timeval *tv, struct timezone *tz) 2197 | { 2198 | int res; 2199 | asm volatile( 2200 | "syscall\n\t" 2201 | : "=a"(res) 2202 | : "a"(96), "D"(tv), "S"(tz)); 2203 | return res; 2204 | } 2205 | #endif /* __x86_64__ */ 2206 | #ifdef __i386__ 2207 | static void sys_exit(int status) 2208 | { 2209 | asm volatile( 2210 | "int $0x80\n\t" 2211 | :: "a"(1), "b"(status)); 2212 | } 2213 | static int sys_write(int fd, const void *buf, int count) 2214 | { 2215 | int res; 2216 | asm volatile( 2217 | "int $0x80\n\t" 2218 | : "=a"(res) 2219 | : "a"(4), "b"(fd), "c"(buf), "d"(count)); 2220 | return res; 2221 | } 2222 | static int sys_gettimeofday(struct timeval *tv, struct timezone *tz) 2223 | { 2224 | int res; 2225 | asm volatile( 2226 | "int $0x80\n\t" 2227 | : "=a"(res) 2228 | : "a"(78), "b"(tv), "c"(tz)); 2229 | return res; 2230 | } 2231 | #endif /* __i386__ */ 2232 | #endif /* __linux__ */ 2233 | 2234 | #ifdef _WIN32 2235 | static void sys_exit(int status) 2236 | { 2237 | ExitProcess(status); 2238 | } 2239 | static int sys_write(int fd, const void *buf, int count) 2240 | { 2241 | unsigned long wrsz = 0; 2242 | 2243 | HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE); 2244 | if(!WriteFile(out, buf, count, &wrsz, 0)) { 2245 | return -1; 2246 | } 2247 | return wrsz; 2248 | } 2249 | #endif /* _WIN32 */ 2250 | #endif /* MINIGLUT_NO_LIBC */ 2251 | 2252 | 2253 | /* ----------------- primitives ------------------ */ 2254 | #ifndef MINIGLUT_NO_LIBC 2255 | #include 2256 | #include 2257 | 2258 | void mglut_sincos(float angle, float *sptr, float *cptr) 2259 | { 2260 | *sptr = sin(angle); 2261 | *cptr = cos(angle); 2262 | } 2263 | 2264 | float mglut_atan(float x) 2265 | { 2266 | return atan(x); 2267 | } 2268 | 2269 | #else /* MINIGLUT_NO_LIBC */ 2270 | 2271 | #ifdef __GNUC__ 2272 | void mglut_sincos(float angle, float *sptr, float *cptr) 2273 | { 2274 | asm volatile( 2275 | "flds %2\n\t" 2276 | "fsincos\n\t" 2277 | "fstps %1\n\t" 2278 | "fstps %0\n\t" 2279 | : "=m"(*sptr), "=m"(*cptr) 2280 | : "m"(angle) 2281 | ); 2282 | } 2283 | 2284 | float mglut_atan(float x) 2285 | { 2286 | float res; 2287 | asm volatile( 2288 | "flds %1\n\t" 2289 | "fld1\n\t" 2290 | "fpatan\n\t" 2291 | "fstps %0\n\t" 2292 | : "=m"(res) 2293 | : "m"(x) 2294 | ); 2295 | return res; 2296 | } 2297 | #endif 2298 | 2299 | #ifdef _MSC_VER 2300 | void mglut_sincos(float angle, float *sptr, float *cptr) 2301 | { 2302 | float s, c; 2303 | __asm { 2304 | fld angle 2305 | fsincos 2306 | fstp c 2307 | fstp s 2308 | } 2309 | *sptr = s; 2310 | *cptr = c; 2311 | } 2312 | 2313 | float mglut_atan(float x) 2314 | { 2315 | float res; 2316 | __asm { 2317 | fld x 2318 | fld1 2319 | fpatan 2320 | fstp res 2321 | } 2322 | return res; 2323 | } 2324 | #endif 2325 | 2326 | #ifdef __WATCOMC__ 2327 | #pragma aux mglut_sincos = \ 2328 | "fsincos" \ 2329 | "fstp dword ptr [edx]" \ 2330 | "fstp dword ptr [eax]" \ 2331 | parm[8087][eax][edx] \ 2332 | modify[8087]; 2333 | 2334 | #pragma aux mglut_atan = \ 2335 | "fld1" \ 2336 | "fpatan" \ 2337 | parm[8087] \ 2338 | value[8087] \ 2339 | modify [8087]; 2340 | #endif /* __WATCOMC__ */ 2341 | 2342 | #endif /* MINIGLUT_NO_LIBC */ 2343 | 2344 | #define PI 3.1415926536f 2345 | 2346 | void glutSolidSphere(float rad, int slices, int stacks) 2347 | { 2348 | int i, j, k, gray; 2349 | float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi; 2350 | float du = 1.0f / (float)slices; 2351 | float dv = 1.0f / (float)stacks; 2352 | 2353 | glBegin(GL_QUADS); 2354 | for(i=0; i> 1); 2360 | s = gray & 1 ? u + du : u; 2361 | t = gray & 2 ? v + dv : v; 2362 | theta = s * PI * 2.0f; 2363 | phi = t * PI; 2364 | mglut_sincos(theta, &sintheta, &costheta); 2365 | mglut_sincos(phi, &sinphi, &cosphi); 2366 | x = sintheta * sinphi; 2367 | y = costheta * sinphi; 2368 | z = cosphi; 2369 | 2370 | glColor3f(s, t, 1); 2371 | glTexCoord2f(s, t); 2372 | glNormal3f(x, y, z); 2373 | glVertex3f(x * rad, y * rad, z * rad); 2374 | } 2375 | } 2376 | } 2377 | glEnd(); 2378 | } 2379 | 2380 | void glutWireSphere(float rad, int slices, int stacks) 2381 | { 2382 | glPushAttrib(GL_POLYGON_BIT); 2383 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 2384 | glutSolidSphere(rad, slices, stacks); 2385 | glPopAttrib(); 2386 | } 2387 | 2388 | void glutSolidCube(float sz) 2389 | { 2390 | int i, j, idx, gray, flip, rotx; 2391 | float vpos[3], norm[3]; 2392 | float rad = sz * 0.5f; 2393 | 2394 | glBegin(GL_QUADS); 2395 | for(i=0; i<6; i++) { 2396 | flip = i & 1; 2397 | rotx = i >> 2; 2398 | idx = (~i & 2) - rotx; 2399 | norm[0] = norm[1] = norm[2] = 0.0f; 2400 | norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1; 2401 | glNormal3fv(norm); 2402 | vpos[idx] = norm[idx] * rad; 2403 | for(j=0; j<4; j++) { 2404 | gray = j ^ (j >> 1); 2405 | vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad; 2406 | vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad; 2407 | glTexCoord2f(gray & 1, gray >> 1); 2408 | glVertex3fv(vpos); 2409 | } 2410 | } 2411 | glEnd(); 2412 | } 2413 | 2414 | void glutWireCube(float sz) 2415 | { 2416 | glPushAttrib(GL_POLYGON_BIT); 2417 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 2418 | glutSolidCube(sz); 2419 | glPopAttrib(); 2420 | } 2421 | 2422 | static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks) 2423 | { 2424 | int i, j, k, gray; 2425 | float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad; 2426 | float du = 1.0f / (float)slices; 2427 | float dv = 1.0f / (float)stacks; 2428 | 2429 | rad = rbot - rtop; 2430 | phi = mglut_atan((rad < 0 ? -rad : rad) / height); 2431 | mglut_sincos(phi, &sinphi, &cosphi); 2432 | 2433 | glBegin(GL_QUADS); 2434 | for(i=0; i> 1); 2440 | s = gray & 2 ? u + du : u; 2441 | t = gray & 1 ? v + dv : v; 2442 | rad = rbot + (rtop - rbot) * t; 2443 | theta = s * PI * 2.0f; 2444 | mglut_sincos(theta, &sintheta, &costheta); 2445 | 2446 | x = sintheta * cosphi; 2447 | y = costheta * cosphi; 2448 | z = sinphi; 2449 | 2450 | glColor3f(s, t, 1); 2451 | glTexCoord2f(s, t); 2452 | glNormal3f(x, y, z); 2453 | glVertex3f(sintheta * rad, costheta * rad, t * height); 2454 | } 2455 | } 2456 | } 2457 | glEnd(); 2458 | } 2459 | 2460 | void glutSolidCone(float base, float height, int slices, int stacks) 2461 | { 2462 | draw_cylinder(base, 0, height, slices, stacks); 2463 | } 2464 | 2465 | void glutWireCone(float base, float height, int slices, int stacks) 2466 | { 2467 | glPushAttrib(GL_POLYGON_BIT); 2468 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 2469 | glutSolidCone(base, height, slices, stacks); 2470 | glPopAttrib(); 2471 | } 2472 | 2473 | void glutSolidCylinder(float rad, float height, int slices, int stacks) 2474 | { 2475 | draw_cylinder(rad, rad, height, slices, stacks); 2476 | } 2477 | 2478 | void glutWireCylinder(float rad, float height, int slices, int stacks) 2479 | { 2480 | glPushAttrib(GL_POLYGON_BIT); 2481 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 2482 | glutSolidCylinder(rad, height, slices, stacks); 2483 | glPopAttrib(); 2484 | } 2485 | 2486 | void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings) 2487 | { 2488 | int i, j, k, gray; 2489 | float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi; 2490 | float du = 1.0f / (float)rings; 2491 | float dv = 1.0f / (float)sides; 2492 | 2493 | glBegin(GL_QUADS); 2494 | for(i=0; i> 1); 2500 | s = gray & 1 ? u + du : u; 2501 | t = gray & 2 ? v + dv : v; 2502 | theta = s * PI * 2.0f; 2503 | phi = t * PI * 2.0f; 2504 | mglut_sincos(theta, &sintheta, &costheta); 2505 | mglut_sincos(phi, &sinphi, &cosphi); 2506 | x = sintheta * sinphi; 2507 | y = costheta * sinphi; 2508 | z = cosphi; 2509 | 2510 | glColor3f(s, t, 1); 2511 | glTexCoord2f(s, t); 2512 | glNormal3f(x, y, z); 2513 | 2514 | x = x * inner_rad + sintheta * outer_rad; 2515 | y = y * inner_rad + costheta * outer_rad; 2516 | z *= inner_rad; 2517 | glVertex3f(x, y, z); 2518 | } 2519 | } 2520 | } 2521 | glEnd(); 2522 | } 2523 | 2524 | void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings) 2525 | { 2526 | glPushAttrib(GL_POLYGON_BIT); 2527 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 2528 | glutSolidTorus(inner_rad, outer_rad, sides, rings); 2529 | glPopAttrib(); 2530 | } 2531 | 2532 | #define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index) 2533 | #define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts) 2534 | 2535 | #define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16) 2536 | 2537 | #define PATCH_SUBDIV 7 2538 | 2539 | static float teapot_part_flip[] = { 2540 | 1, 1, 1, 1, /* rim flip */ 2541 | 1, 1, 1, 1, /* body1 flip */ 2542 | 1, 1, 1, 1, /* body2 flip */ 2543 | 1, 1, 1, 1, /* lid patch 1 flip */ 2544 | 1, 1, 1, 1, /* lid patch 2 flip */ 2545 | 1, -1, /* handle 1 flip */ 2546 | 1, -1, /* handle 2 flip */ 2547 | 1, -1, /* spout 1 flip */ 2548 | 1, -1, /* spout 2 flip */ 2549 | 1, 1, 1, 1 /* bottom flip */ 2550 | }; 2551 | 2552 | static float teapot_part_rot[] = { 2553 | 0, 90, 180, 270, /* rim rotations */ 2554 | 0, 90, 180, 270, /* body patch 1 rotations */ 2555 | 0, 90, 180, 270, /* body patch 2 rotations */ 2556 | 0, 90, 180, 270, /* lid patch 1 rotations */ 2557 | 0, 90, 180, 270, /* lid patch 2 rotations */ 2558 | 0, 0, /* handle 1 rotations */ 2559 | 0, 0, /* handle 2 rotations */ 2560 | 0, 0, /* spout 1 rotations */ 2561 | 0, 0, /* spout 2 rotations */ 2562 | 0, 90, 180, 270 /* bottom rotations */ 2563 | }; 2564 | 2565 | 2566 | static int teapot_index[] = { 2567 | /* rim */ 2568 | 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 2569 | 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 2570 | 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 2571 | 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 2572 | /* body1 */ 2573 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 2574 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 2575 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 2576 | 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 2577 | /* body 2 */ 2578 | 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 2579 | 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 2580 | 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 2581 | 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 2582 | /* lid 1 */ 2583 | 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3, 2584 | 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3, 2585 | 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3, 2586 | 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3, 2587 | /* lid 2 */ 2588 | 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 2589 | 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 2590 | 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 2591 | 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 2592 | /* handle 1 */ 2593 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 2594 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 2595 | /* handle 2 */ 2596 | 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67, 2597 | 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67, 2598 | /* spout 1 */ 2599 | 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 2600 | 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 2601 | /* spout 2 */ 2602 | 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 2603 | 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 2604 | /* bottom */ 2605 | 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37, 2606 | 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37, 2607 | 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37, 2608 | 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37 2609 | }; 2610 | 2611 | 2612 | static float teapot_verts[][3] = { 2613 | { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 }, 2614 | { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 }, 2615 | { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 }, 2616 | { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 }, 2617 | { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 }, 2618 | { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 }, 2619 | { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 }, 2620 | { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 }, 2621 | { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 }, 2622 | { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 }, 2623 | { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 }, 2624 | { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 }, 2625 | { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 }, 2626 | { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 }, 2627 | { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 }, 2628 | { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 }, 2629 | { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 }, 2630 | { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 }, 2631 | { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 }, 2632 | { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 }, 2633 | { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 }, 2634 | { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 }, 2635 | { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 }, 2636 | { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 }, 2637 | { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 }, 2638 | { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 }, 2639 | { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 }, 2640 | { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 }, 2641 | { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 }, 2642 | { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 }, 2643 | { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 }, 2644 | { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 }, 2645 | { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 }, 2646 | { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 }, 2647 | { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 }, 2648 | { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 }, 2649 | { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 }, 2650 | { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 }, 2651 | { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 }, 2652 | { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 }, 2653 | { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 }, 2654 | { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 }, 2655 | { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 }, 2656 | { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 }, 2657 | { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 }, 2658 | { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 }, 2659 | { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 }, 2660 | { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 }, 2661 | { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 }, 2662 | { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 }, 2663 | { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 }, 2664 | { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 }, 2665 | { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 }, 2666 | { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 }, 2667 | { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 }, 2668 | { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 }, 2669 | { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 }, 2670 | { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 }, 2671 | { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 }, 2672 | { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 }, 2673 | { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 }, 2674 | { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 }, 2675 | { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 }, 2676 | { 0.8400, -1.5000, 0.07500 } 2677 | }; 2678 | 2679 | static void draw_patch(int *index, int flip, float scale); 2680 | static float bernstein(int i, float x); 2681 | 2682 | void glutSolidTeapot(float size) 2683 | { 2684 | int i; 2685 | 2686 | size /= 2.0; 2687 | 2688 | for(i=0; i> 1); 2739 | x = *(float*)&i; 2740 | x = x * (1.5f - xhalf * x * x); 2741 | return x; 2742 | } 2743 | 2744 | 2745 | #define CROSS(res, a, b) \ 2746 | do { \ 2747 | (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \ 2748 | (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \ 2749 | (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \ 2750 | } while(0) 2751 | 2752 | #define NORMALIZE(v) \ 2753 | do { \ 2754 | float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \ 2755 | (v)[0] *= s; \ 2756 | (v)[1] *= s; \ 2757 | (v)[2] *= s; \ 2758 | } while(0) 2759 | 2760 | #define DT 0.001 2761 | 2762 | static void bezier_patch_norm(float *res, float *cp, float u, float v) 2763 | { 2764 | float tang[3], bitan[3], tmp[3]; 2765 | 2766 | bezier_patch(tang, cp, u + DT, v); 2767 | bezier_patch(tmp, cp, u - DT, v); 2768 | tang[0] -= tmp[0]; 2769 | tang[1] -= tmp[1]; 2770 | tang[2] -= tmp[2]; 2771 | 2772 | bezier_patch(bitan, cp, u, v + DT); 2773 | bezier_patch(tmp, cp, u, v - DT); 2774 | bitan[0] -= tmp[0]; 2775 | bitan[1] -= tmp[1]; 2776 | bitan[2] -= tmp[2]; 2777 | 2778 | CROSS(res, tang, bitan); 2779 | NORMALIZE(res); 2780 | } 2781 | 2782 | 2783 | 2784 | static float bernstein(int i, float x) 2785 | { 2786 | float invx = 1.0f - x; 2787 | 2788 | switch(i) { 2789 | case 0: 2790 | return invx * invx * invx; 2791 | case 1: 2792 | return 3.0f * x * invx * invx; 2793 | case 2: 2794 | return 3.0f * x * x * invx; 2795 | case 3: 2796 | return x * x * x; 2797 | default: 2798 | break; 2799 | } 2800 | return 0.0f; 2801 | } 2802 | 2803 | static void draw_patch(int *index, int flip, float scale) 2804 | { 2805 | static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}}; 2806 | static const float voffs[4] = {0, 1, 1, 0}; 2807 | 2808 | int i, j, k; 2809 | float cp[16 * 3]; 2810 | float pt[3], n[3]; 2811 | float u, v; 2812 | float du = 1.0 / PATCH_SUBDIV; 2813 | float dv = 1.0 / PATCH_SUBDIV; 2814 | 2815 | /* collect control points */ 2816 | for(i=0; i<16; i++) { 2817 | cp[i * 3] = teapot_verts[index[i]][0]; 2818 | cp[i * 3 + 1] = teapot_verts[index[i]][1]; 2819 | cp[i * 3 + 2] = teapot_verts[index[i]][2]; 2820 | } 2821 | 2822 | glBegin(GL_QUADS); 2823 | glColor3f(1, 1, 1); 2824 | 2825 | u = 0; 2826 | for(i=0; i 3.14) { 2835 | n[0] = n[1] = 0.0f; 2836 | n[2] = 1.0f; 2837 | } else if(pt[2] < 0.00001) { 2838 | n[0] = n[1] = 0.0f; 2839 | n[2] = -1.0f; 2840 | } else { 2841 | bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv); 2842 | } 2843 | 2844 | glTexCoord2f(u, v); 2845 | glNormal3fv(n); 2846 | glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale); 2847 | } 2848 | 2849 | v += dv; 2850 | } 2851 | u += du; 2852 | } 2853 | 2854 | glEnd(); 2855 | } 2856 | -------------------------------------------------------------------------------- /miniglut.dsp: -------------------------------------------------------------------------------- 1 | # Microsoft Developer Studio Project File - Name="miniglut" - Package Owner=<4> 2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00 3 | # ** DO NOT EDIT ** 4 | 5 | # TARGTYPE "Win32 (x86) Static Library" 0x0104 6 | 7 | CFG=miniglut - Win32 Debug 8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE, 9 | !MESSAGE use the Export Makefile command and run 10 | !MESSAGE 11 | !MESSAGE NMAKE /f "miniglut.mak". 12 | !MESSAGE 13 | !MESSAGE You can specify a configuration when running NMAKE 14 | !MESSAGE by defining the macro CFG on the command line. For example: 15 | !MESSAGE 16 | !MESSAGE NMAKE /f "miniglut.mak" CFG="miniglut - Win32 Debug" 17 | !MESSAGE 18 | !MESSAGE Possible choices for configuration are: 19 | !MESSAGE 20 | !MESSAGE "miniglut - Win32 Release" (based on "Win32 (x86) Static Library") 21 | !MESSAGE "miniglut - Win32 Debug" (based on "Win32 (x86) Static Library") 22 | !MESSAGE 23 | 24 | # Begin Project 25 | # PROP AllowPerConfigDependencies 0 26 | # PROP Scc_ProjName "" 27 | # PROP Scc_LocalPath "" 28 | CPP=cl.exe 29 | RSC=rc.exe 30 | 31 | !IF "$(CFG)" == "miniglut - Win32 Release" 32 | 33 | # PROP BASE Use_MFC 0 34 | # PROP BASE Use_Debug_Libraries 0 35 | # PROP BASE Output_Dir "Release" 36 | # PROP BASE Intermediate_Dir "Release" 37 | # PROP BASE Target_Dir "" 38 | # PROP Use_MFC 0 39 | # PROP Use_Debug_Libraries 0 40 | # PROP Output_Dir "Release" 41 | # PROP Intermediate_Dir "Release" 42 | # PROP Target_Dir "" 43 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c 44 | # ADD CPP /nologo /W2 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c 45 | # ADD BASE RSC /l 0x409 /d "NDEBUG" 46 | # ADD RSC /l 0x409 /d "NDEBUG" 47 | BSC32=bscmake.exe 48 | # ADD BASE BSC32 /nologo 49 | # ADD BSC32 /nologo 50 | LIB32=link.exe -lib 51 | # ADD BASE LIB32 /nologo 52 | # ADD LIB32 /nologo /NODEFAULTLIB 53 | 54 | !ELSEIF "$(CFG)" == "miniglut - Win32 Debug" 55 | 56 | # PROP BASE Use_MFC 0 57 | # PROP BASE Use_Debug_Libraries 1 58 | # PROP BASE Output_Dir "Debug" 59 | # PROP BASE Intermediate_Dir "Debug" 60 | # PROP BASE Target_Dir "" 61 | # PROP Use_MFC 0 62 | # PROP Use_Debug_Libraries 1 63 | # PROP Output_Dir "Debug" 64 | # PROP Intermediate_Dir "Debug" 65 | # PROP Target_Dir "" 66 | # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c 67 | # ADD CPP /nologo /W2 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c 68 | # ADD BASE RSC /l 0x409 /d "_DEBUG" 69 | # ADD RSC /l 0x409 /d "_DEBUG" 70 | BSC32=bscmake.exe 71 | # ADD BASE BSC32 /nologo 72 | # ADD BSC32 /nologo 73 | LIB32=link.exe -lib 74 | # ADD BASE LIB32 /nologo 75 | # ADD LIB32 /nologo 76 | 77 | !ENDIF 78 | 79 | # Begin Target 80 | 81 | # Name "miniglut - Win32 Release" 82 | # Name "miniglut - Win32 Debug" 83 | # Begin Group "src" 84 | 85 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" 86 | # Begin Source File 87 | 88 | SOURCE=.\miniglut.c 89 | # End Source File 90 | # Begin Source File 91 | 92 | SOURCE=.\miniglut.h 93 | # End Source File 94 | # End Group 95 | # End Target 96 | # End Project 97 | -------------------------------------------------------------------------------- /miniglut.dsw: -------------------------------------------------------------------------------- 1 | Microsoft Developer Studio Workspace File, Format Version 6.00 2 | # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! 3 | 4 | ############################################################################### 5 | 6 | Project: "miniglut"=.\miniglut.dsp - Package Owner=<4> 7 | 8 | Package=<5> 9 | {{{ 10 | }}} 11 | 12 | Package=<4> 13 | {{{ 14 | }}} 15 | 16 | ############################################################################### 17 | 18 | Project: "test"=.\test.dsp - Package Owner=<4> 19 | 20 | Package=<5> 21 | {{{ 22 | }}} 23 | 24 | Package=<4> 25 | {{{ 26 | Begin Project Dependency 27 | Project_Dep_Name miniglut 28 | End Project Dependency 29 | }}} 30 | 31 | ############################################################################### 32 | 33 | Global: 34 | 35 | Package=<5> 36 | {{{ 37 | }}} 38 | 39 | Package=<3> 40 | {{{ 41 | }}} 42 | 43 | ############################################################################### 44 | 45 | -------------------------------------------------------------------------------- /miniglut.h: -------------------------------------------------------------------------------- 1 | /* 2 | MiniGLUT - minimal GLUT subset without dependencies 3 | Copyright (C) 2020-2024 John Tsiombikas 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | #ifndef MINIGLUT_H_ 19 | #define MINIGLUT_H_ 20 | 21 | #ifdef _WIN32 22 | #define WIN32_LEAN_AND_MEAN 1 23 | #include 24 | 25 | #ifdef _MSC_VER 26 | #pragma comment (lib, "opengl32") 27 | #ifndef MINIGLUT_NO_WINMM 28 | #pragma comment (lib, "winmm") 29 | #endif 30 | #endif /* MSVC */ 31 | 32 | #endif 33 | #include 34 | 35 | /* mode flags for glutInitDisplayMode */ 36 | #define GLUT_RGB 0 37 | #define GLUT_RGBA 0 38 | #define GLUT_INDEX 0x001 39 | #define GLUT_SINGLE 0 40 | #define GLUT_DOUBLE 0x002 41 | #define GLUT_ACCUM 0x004 42 | #define GLUT_ALPHA 0x008 43 | #define GLUT_DEPTH 0x010 44 | #define GLUT_STENCIL 0x020 45 | #define GLUT_STEREO 0x040 46 | #define GLUT_MULTISAMPLE 0x100 47 | #define GLUT_SRGB 0x200 48 | 49 | enum { GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON }; 50 | enum { GLUT_UP, GLUT_DOWN }; 51 | enum { GLUT_NOT_VISIBLE, GLUT_VISIBLE }; 52 | enum { GLUT_LEFT, GLUT_ENTERED }; 53 | 54 | /* cursors */ 55 | enum { 56 | GLUT_CURSOR_INHERIT, 57 | GLUT_CURSOR_LEFT_ARROW, 58 | GLUT_CURSOR_NONE 59 | }; 60 | 61 | /* glutGet */ 62 | enum { 63 | GLUT_WINDOW_X, 64 | GLUT_WINDOW_Y, 65 | GLUT_WINDOW_WIDTH, 66 | GLUT_WINDOW_HEIGHT, 67 | GLUT_WINDOW_BUFFER_SIZE, 68 | GLUT_WINDOW_STENCIL_SIZE, 69 | GLUT_WINDOW_DEPTH_SIZE, 70 | GLUT_WINDOW_RED_SIZE, 71 | GLUT_WINDOW_GREEN_SIZE, 72 | GLUT_WINDOW_BLUE_SIZE, 73 | GLUT_WINDOW_ALPHA_SIZE, 74 | GLUT_WINDOW_DOUBLEBUFFER, 75 | GLUT_WINDOW_RGBA, 76 | GLUT_WINDOW_NUM_SAMPLES, 77 | GLUT_WINDOW_STEREO, 78 | GLUT_WINDOW_SRGB, 79 | GLUT_WINDOW_CURSOR, 80 | GLUT_SCREEN_WIDTH, 81 | GLUT_SCREEN_HEIGHT, 82 | GLUT_INIT_DISPLAY_MODE, 83 | GLUT_INIT_WINDOW_X, 84 | GLUT_INIT_WINDOW_Y, 85 | GLUT_INIT_WINDOW_WIDTH, 86 | GLUT_INIT_WINDOW_HEIGHT, 87 | GLUT_ELAPSED_TIME, 88 | GLUT_WINDOW_COLORMAP_SIZE 89 | }; 90 | 91 | /* glutDeviceGet */ 92 | enum { 93 | GLUT_HAS_KEYBOARD, 94 | GLUT_HAS_MOUSE, 95 | GLUT_HAS_SPACEBALL, 96 | GLUT_HAS_DIAL_AND_BUTTON_BOX, 97 | GLUT_HAS_TABLET, 98 | GLUT_NUM_MOUSE_BUTTONS, 99 | GLUT_NUM_SPACEBALL_BUTTONS, 100 | GLUT_NUM_BUTTON_BOX_BUTTONS, 101 | GLUT_NUM_DIALS, 102 | GLUT_NUM_TABLET_BUTTONS 103 | }; 104 | 105 | enum { 106 | GLUT_RED, 107 | GLUT_GREEN, 108 | GLUT_BLUE 109 | }; 110 | 111 | enum { 112 | GLUT_KEY_HOME = 0xff50, 113 | GLUT_KEY_LEFT = 0xff51, 114 | GLUT_KEY_UP, 115 | GLUT_KEY_RIGHT, 116 | GLUT_KEY_DOWN, 117 | GLUT_KEY_PAGE_UP, 118 | GLUT_KEY_PAGE_DOWN, 119 | GLUT_KEY_END = 0xff57, 120 | GLUT_KEY_INSERT = 0xff63, 121 | GLUT_KEY_F1 = 0xffbe, 122 | GLUT_KEY_F2, 123 | GLUT_KEY_F3, 124 | GLUT_KEY_F4, 125 | GLUT_KEY_F5, 126 | GLUT_KEY_F6, 127 | GLUT_KEY_F7, 128 | GLUT_KEY_F8, 129 | GLUT_KEY_F9, 130 | GLUT_KEY_F10, 131 | GLUT_KEY_F11, 132 | GLUT_KEY_F12 133 | }; 134 | 135 | /* returned by glutGetModifiers */ 136 | #define GLUT_ACTIVE_SHIFT 1 137 | #define GLUT_ACTIVE_CTRL 4 138 | #define GLUT_ACTIVE_ALT 8 139 | 140 | enum { 141 | GLUT_KEY_REPEAT_OFF, 142 | GLUT_KEY_REPEAT_ON 143 | }; 144 | #define GLUT_KEY_REPEAT_DEFAULT GLUT_KEY_REPEAT_ON 145 | 146 | typedef void (*glut_cb)(void); 147 | typedef void (*glut_cb_reshape)(int x, int y); 148 | typedef void (*glut_cb_state)(int state); 149 | typedef void (*glut_cb_keyb)(unsigned char key, int x, int y); 150 | typedef void (*glut_cb_special)(int key, int x, int y); 151 | typedef void (*glut_cb_mouse)(int bn, int state, int x, int y); 152 | typedef void (*glut_cb_motion)(int x, int y); 153 | typedef void (*glut_cb_sbmotion)(int x, int y, int z); 154 | typedef void (*glut_cb_sbbutton)(int bn, int state); 155 | 156 | #ifdef __cplusplus 157 | extern "C" { 158 | #endif 159 | 160 | void glutInit(int *argc, char **argv); 161 | void glutInitWindowPosition(int x, int y); 162 | void glutInitWindowSize(int xsz, int ysz); 163 | void glutInitDisplayMode(unsigned int mode); 164 | void glutCreateWindow(const char *title); 165 | 166 | void glutExit(void); 167 | void glutMainLoop(void); 168 | void glutMainLoopEvent(void); 169 | 170 | void glutPostRedisplay(void); 171 | void glutSwapBuffers(void); 172 | void glutPositionWindow(int x, int y); 173 | void glutReshapeWindow(int xsz, int ysz); 174 | void glutFullScreen(void); 175 | void glutSetWindowTitle(const char *title); 176 | void glutSetIconTitle(const char *title); 177 | void glutSetCursor(int cursor); 178 | void glutSetColor(int idx, float r, float g, float b); 179 | void glutWarpPointer(int x, int y); 180 | float glutGetColor(int idx, int comp); 181 | 182 | void glutIgnoreKeyRepeat(int ignore); 183 | void glutSetKeyRepeat(int repmode); 184 | 185 | void glutIdleFunc(glut_cb func); 186 | void glutDisplayFunc(glut_cb func); 187 | void glutReshapeFunc(glut_cb_reshape func); 188 | void glutVisibilityFunc(glut_cb_state func); 189 | void glutEntryFunc(glut_cb_state func); 190 | void glutKeyboardFunc(glut_cb_keyb func); 191 | void glutKeyboardUpFunc(glut_cb_keyb func); 192 | void glutSpecialFunc(glut_cb_special func); 193 | void glutSpecialUpFunc(glut_cb_special func); 194 | void glutMouseFunc(glut_cb_mouse func); 195 | void glutMotionFunc(glut_cb_motion func); 196 | void glutPassiveMotionFunc(glut_cb_motion func); 197 | void glutSpaceballMotionFunc(glut_cb_sbmotion func); 198 | void glutSpaceballRotateFunc(glut_cb_sbmotion func); 199 | void glutSpaceballButtonFunc(glut_cb_sbbutton func); 200 | 201 | int glutGet(unsigned int s); 202 | int glutGetModifiers(void); 203 | int glutExtensionSupported(char *ext); 204 | 205 | void glutSolidSphere(float rad, int slices, int stacks); 206 | void glutWireSphere(float rad, int slices, int stacks); 207 | void glutSolidCube(float sz); 208 | void glutWireCube(float sz); 209 | void glutSolidCone(float base, float height, int slices, int stacks); 210 | void glutWireCone(float base, float height, int slices, int stacks); 211 | void glutSolidCylinder(float rad, float height, int slices, int stacks); 212 | void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings); 213 | void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings); 214 | void glutSolidTeapot(float size); 215 | void glutWireTeapot(float size); 216 | 217 | #ifdef __cplusplus 218 | } /* extern "C" */ 219 | #endif 220 | 221 | #endif /* MINIGLUT_H_ */ 222 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "miniglut.h" 5 | 6 | void idle(void); 7 | void display(void); 8 | void reshape(int x, int y); 9 | void keypress(unsigned char key, int x, int y); 10 | void mouse(int bn, int st, int x, int y); 11 | void motion(int x, int y); 12 | void sball_motion(int x, int y, int z); 13 | void sball_rotate(int rx, int ry, int rz); 14 | void sball_button(int bn, int state); 15 | 16 | static void vcross(float *res, const float *a, const float *b); 17 | static void qmul(float *a, const float *b); 18 | static void qrotation(float *q, float angle, float x, float y, float z); 19 | static void qrotate(float *q, float angle, float x, float y, float z); 20 | static void mrotation_quat(float *m, const float *q); 21 | 22 | 23 | float cam_theta, cam_phi = 25, cam_dist = 8; 24 | int mouse_x, mouse_y; 25 | int bnstate[8]; 26 | int anim; 27 | float torus_pos[3], torus_rot[4] = {0, 0, 0, 1}; 28 | int teapot, torus, cone, sphere; 29 | long nframes; 30 | 31 | #ifndef GL_FRAMEBUFFER_SRGB 32 | #define GL_FRAMEBUFFER_SRGB 0x8db9 33 | #endif 34 | 35 | #ifndef GL_MULTISAMPLE 36 | #define GL_MULTISAMPLE 0x809d 37 | #endif 38 | 39 | int main(int argc, char **argv) 40 | { 41 | int i, test_aa = 0, test_srgb = 0; 42 | unsigned int mode = GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE; 43 | 44 | for(i=1; i 90) cam_phi = 90; 247 | glutPostRedisplay(); 248 | } 249 | if(bnstate[2]) { 250 | cam_dist += dy * 0.1; 251 | if(cam_dist < 0) cam_dist = 0; 252 | glutPostRedisplay(); 253 | } 254 | } 255 | 256 | void sball_motion(int x, int y, int z) 257 | { 258 | torus_pos[0] += x * 0.001f; 259 | torus_pos[1] += y * 0.001f; 260 | torus_pos[2] -= z * 0.001f; 261 | glutPostRedisplay(); 262 | } 263 | 264 | static float rsqrt(float number) 265 | { 266 | int i; 267 | float x2, y; 268 | static const float threehalfs = 1.5f; 269 | 270 | x2 = number * 0.5f; 271 | y = number; 272 | i = *(int*)&y; 273 | i = 0x5f3759df - (i >> 1); 274 | y = *(float*)&i; 275 | y *= threehalfs - (x2 * y * y); 276 | y *= threehalfs - (x2 * y * y); 277 | return y; 278 | } 279 | 280 | void sball_rotate(int rx, int ry, int rz) 281 | { 282 | if(rx | ry | rz) { 283 | float s = (float)rsqrt(rx * rx + ry * ry + rz * rz); 284 | qrotate(torus_rot, 0.001f / s, rx * s, ry * s, -rz * s); 285 | glutPostRedisplay(); 286 | } 287 | } 288 | 289 | void sball_button(int bn, int state) 290 | { 291 | if(state == GLUT_DOWN) { 292 | torus_pos[0] = torus_pos[1] = torus_pos[2] = 0; 293 | torus_rot[0] = torus_rot[1] = torus_rot[2] = 0; 294 | torus_rot[3] = 1; 295 | glutPostRedisplay(); 296 | } 297 | } 298 | 299 | 300 | static void vcross(float *res, const float *a, const float *b) 301 | { 302 | res[0] = a[1] * b[2] - a[2] * b[1]; 303 | res[1] = a[2] * b[0] - a[0] * b[2]; 304 | res[2] = a[0] * b[1] - a[1] * b[0]; 305 | } 306 | 307 | static void qmul(float *a, const float *b) 308 | { 309 | float x, y, z, dot; 310 | float cross[3]; 311 | 312 | dot = a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; 313 | vcross(cross, a, b); 314 | 315 | x = a[3] * b[0] + b[3] * a[0] + cross[0]; 316 | y = a[3] * b[1] + b[3] * a[1] + cross[1]; 317 | z = a[3] * b[2] + b[3] * a[2] + cross[2]; 318 | a[3] = a[3] * b[3] - dot; 319 | a[0] = x; 320 | a[1] = y; 321 | a[2] = z; 322 | } 323 | 324 | void mglut_sincos(float angle, float *sptr, float *cptr); 325 | float mglut_tan(float x); 326 | 327 | static void qrotation(float *q, float angle, float x, float y, float z) 328 | { 329 | float sa, ca; 330 | mglut_sincos(angle * 0.5f, &sa, &ca); 331 | q[3] = ca; 332 | q[0] = x * sa; 333 | q[1] = y * sa; 334 | q[2] = z * sa; 335 | } 336 | 337 | static void qrotate(float *q, float angle, float x, float y, float z) 338 | { 339 | float qrot[4]; 340 | qrotation(qrot, angle, x, y, z); 341 | qmul(qrot, q); 342 | q[0] = qrot[0]; 343 | q[1] = qrot[1]; 344 | q[2] = qrot[2]; 345 | q[3] = qrot[3]; 346 | } 347 | 348 | static void mrotation_quat(float *m, const float *q) 349 | { 350 | float xsq2 = 2.0f * q[0] * q[0]; 351 | float ysq2 = 2.0f * q[1] * q[1]; 352 | float zsq2 = 2.0f * q[2] * q[2]; 353 | float sx = 1.0f - ysq2 - zsq2; 354 | float sy = 1.0f - xsq2 - zsq2; 355 | float sz = 1.0f - xsq2 - ysq2; 356 | 357 | m[3] = m[7] = m[11] = m[12] = m[13] = m[14] = 0.0f; 358 | m[15] = 1.0f; 359 | 360 | m[0] = sx; 361 | m[1] = 2.0f * q[0] * q[1] + 2.0f * q[3] * q[2]; 362 | m[2] = 2.0f * q[2] * q[0] - 2.0f * q[3] * q[1]; 363 | m[4] = 2.0f * q[0] * q[1] - 2.0f * q[3] * q[2]; 364 | m[5] = sy; 365 | m[6] = 2.0f * q[1] * q[2] + 2.0f * q[3] * q[0]; 366 | m[8] = 2.0f * q[2] * q[0] + 2.0f * q[3] * q[1]; 367 | m[9] = 2.0f * q[1] * q[2] - 2.0f * q[3] * q[0]; 368 | m[10] = sz; 369 | } 370 | -------------------------------------------------------------------------------- /test.dsp: -------------------------------------------------------------------------------- 1 | # Microsoft Developer Studio Project File - Name="test" - Package Owner=<4> 2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00 3 | # ** DO NOT EDIT ** 4 | 5 | # TARGTYPE "Win32 (x86) Console Application" 0x0103 6 | 7 | CFG=test - Win32 Debug 8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE, 9 | !MESSAGE use the Export Makefile command and run 10 | !MESSAGE 11 | !MESSAGE NMAKE /f "test.mak". 12 | !MESSAGE 13 | !MESSAGE You can specify a configuration when running NMAKE 14 | !MESSAGE by defining the macro CFG on the command line. For example: 15 | !MESSAGE 16 | !MESSAGE NMAKE /f "test.mak" CFG="test - Win32 Debug" 17 | !MESSAGE 18 | !MESSAGE Possible choices for configuration are: 19 | !MESSAGE 20 | !MESSAGE "test - Win32 Release" (based on "Win32 (x86) Console Application") 21 | !MESSAGE "test - Win32 Debug" (based on "Win32 (x86) Console Application") 22 | !MESSAGE 23 | 24 | # Begin Project 25 | # PROP AllowPerConfigDependencies 0 26 | # PROP Scc_ProjName "" 27 | # PROP Scc_LocalPath "" 28 | CPP=cl.exe 29 | RSC=rc.exe 30 | 31 | !IF "$(CFG)" == "test - Win32 Release" 32 | 33 | # PROP BASE Use_MFC 0 34 | # PROP BASE Use_Debug_Libraries 0 35 | # PROP BASE Output_Dir "Release" 36 | # PROP BASE Intermediate_Dir "Release" 37 | # PROP BASE Target_Dir "" 38 | # PROP Use_MFC 0 39 | # PROP Use_Debug_Libraries 0 40 | # PROP Output_Dir "Release" 41 | # PROP Intermediate_Dir "Release" 42 | # PROP Ignore_Export_Lib 0 43 | # PROP Target_Dir "" 44 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c 45 | # ADD CPP /nologo /W2 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c 46 | # ADD BASE RSC /l 0x409 /d "NDEBUG" 47 | # ADD RSC /l 0x409 /d "NDEBUG" 48 | BSC32=bscmake.exe 49 | # ADD BASE BSC32 /nologo 50 | # ADD BSC32 /nologo 51 | LINK32=link.exe 52 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 53 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib miniglut.lib /nologo /subsystem:console /machine:I386 /libpath:"Release" 54 | 55 | !ELSEIF "$(CFG)" == "test - Win32 Debug" 56 | 57 | # PROP BASE Use_MFC 0 58 | # PROP BASE Use_Debug_Libraries 1 59 | # PROP BASE Output_Dir "Debug" 60 | # PROP BASE Intermediate_Dir "Debug" 61 | # PROP BASE Target_Dir "" 62 | # PROP Use_MFC 0 63 | # PROP Use_Debug_Libraries 1 64 | # PROP Output_Dir "Debug" 65 | # PROP Intermediate_Dir "Debug" 66 | # PROP Ignore_Export_Lib 0 67 | # PROP Target_Dir "" 68 | # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c 69 | # ADD CPP /nologo /W2 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c 70 | # ADD BASE RSC /l 0x409 /d "_DEBUG" 71 | # ADD RSC /l 0x409 /d "_DEBUG" 72 | BSC32=bscmake.exe 73 | # ADD BASE BSC32 /nologo 74 | # ADD BSC32 /nologo 75 | LINK32=link.exe 76 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept 77 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib miniglut.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"Debug" 78 | 79 | !ENDIF 80 | 81 | # Begin Target 82 | 83 | # Name "test - Win32 Release" 84 | # Name "test - Win32 Debug" 85 | # Begin Group "src" 86 | 87 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" 88 | # Begin Source File 89 | 90 | SOURCE=test.c 91 | # End Source File 92 | # End Group 93 | # End Target 94 | # End Project 95 | --------------------------------------------------------------------------------