├── .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 | 
5 |
6 | 
7 | 
8 | 
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 |
--------------------------------------------------------------------------------