Compare commits
485 Commits
Author | SHA1 | Date |
---|---|---|
Pietro Gagliardi | d80f0e4812 | |
Pietro Gagliardi | c241e8676a | |
Pietro Gagliardi | 79d8eb534e | |
Pietro Gagliardi | eb1250a32b | |
Pietro Gagliardi | 1d7c530c32 | |
Pietro Gagliardi | 8823ebacfb | |
Pietro Gagliardi | c9310e21b7 | |
Pietro Gagliardi | 7c0113f220 | |
Pietro Gagliardi | f3820ac4b0 | |
Pietro Gagliardi | 69dbafe6f2 | |
Pietro Gagliardi | 8b3e2665ed | |
Pietro Gagliardi | 4057d3e0d3 | |
Pietro Gagliardi | f6525082a9 | |
Pietro Gagliardi | 320c92b8eb | |
Pietro Gagliardi | 8f8171550d | |
Pietro Gagliardi | 7c7da65231 | |
Pietro Gagliardi | fda1446114 | |
Pietro Gagliardi | 3005c00376 | |
Pietro Gagliardi | 76879e7c82 | |
Pietro Gagliardi | 81fb4ea7f2 | |
Pietro Gagliardi | 47f2bffd8a | |
Pietro Gagliardi | afb53d88c8 | |
Pietro Gagliardi | 5ca4ccb30b | |
Pietro Gagliardi | 6f4610f82a | |
Pietro Gagliardi | 1c7718ec58 | |
Pietro Gagliardi | 7cdd6ee38c | |
Pietro Gagliardi | 188d9f736f | |
Pietro Gagliardi | 3f392d04ce | |
Pietro Gagliardi | d5c06108d6 | |
Pietro Gagliardi | 1dd0372593 | |
Pietro Gagliardi | c07a7861d4 | |
Pietro Gagliardi | 860eb0fae3 | |
Pietro Gagliardi | 218439c215 | |
Pietro Gagliardi | eb1862afe1 | |
Pietro Gagliardi | 80ddb4df8f | |
Pietro Gagliardi | d423688def | |
Pietro Gagliardi | de3598df80 | |
Pietro Gagliardi | 0dd4bec2af | |
Pietro Gagliardi | 16d2380adc | |
Pietro Gagliardi | 9bc2cccb2d | |
Pietro Gagliardi | aa954e952a | |
Pietro Gagliardi | 4c5434d76e | |
Pietro Gagliardi | c71fbe29c2 | |
Pietro Gagliardi | 2f9aaeeb62 | |
Pietro Gagliardi | 0be1273dab | |
Pietro Gagliardi | ba6f2865df | |
Pietro Gagliardi | a65bbfa057 | |
Pietro Gagliardi | 8db1d5474b | |
Pietro Gagliardi | 6fe7c1ef66 | |
Pietro Gagliardi | 7aeb937b93 | |
Pietro Gagliardi | e01c732760 | |
Pietro Gagliardi | 150fa7eaa6 | |
Pietro Gagliardi | de35499976 | |
Pietro Gagliardi | 1cf87d5c5e | |
Pietro Gagliardi | de5205d594 | |
Pietro Gagliardi | e73c3363ec | |
Pietro Gagliardi | 22bcf088e9 | |
Pietro Gagliardi | 76eef19a2b | |
Pietro Gagliardi | 19a776e446 | |
Pietro Gagliardi | b1d733e9a2 | |
Pietro Gagliardi | fdf9a1245d | |
Pietro Gagliardi | 1cf4a3df21 | |
Pietro Gagliardi | dfd045b3cc | |
Pietro Gagliardi | 9b1be0c77a | |
Pietro Gagliardi | e71c717b31 | |
Pietro Gagliardi | c52656f2c2 | |
Pietro Gagliardi | 3689d3c96a | |
Pietro Gagliardi | b710ef6228 | |
Pietro Gagliardi | 436cb567dc | |
Pietro Gagliardi | d584842628 | |
Pietro Gagliardi | e7917500c1 | |
Pietro Gagliardi | 5d17ee6e70 | |
Pietro Gagliardi | 21a0550076 | |
Pietro Gagliardi | f79a0892f2 | |
Pietro Gagliardi | 1daac120af | |
Pietro Gagliardi | ecb5558d3d | |
Pietro Gagliardi | 32512c7a5e | |
Pietro Gagliardi | dca16e82b0 | |
Pietro Gagliardi | 3a31401190 | |
Pietro Gagliardi | 4163facc11 | |
Pietro Gagliardi | 87e042a309 | |
Pietro Gagliardi | c9339e568c | |
Pietro Gagliardi | 7a83baefe8 | |
Pietro Gagliardi | bbae57ea29 | |
Pietro Gagliardi | 73afdaaeb2 | |
Pietro Gagliardi | c39bd66d38 | |
Pietro Gagliardi | a2f0d5c2cd | |
Pietro Gagliardi | 9fcc4d500c | |
Pietro Gagliardi | 41e62cdce1 | |
Pietro Gagliardi | 761d3a434a | |
Pietro Gagliardi | 514037ba6c | |
Pietro Gagliardi | faaccd0413 | |
Pietro Gagliardi | c0e73c67f4 | |
Pietro Gagliardi | dea7b52605 | |
Pietro Gagliardi | 456fb594c0 | |
Pietro Gagliardi | 7c4d1ae478 | |
Pietro Gagliardi | 69bf71aaa1 | |
Pietro Gagliardi | 74651b347c | |
Pietro Gagliardi | 63bda503dc | |
Pietro Gagliardi | e0828a51f1 | |
Pietro Gagliardi | 3c93d8c9e7 | |
Pietro Gagliardi | 0d9f58b5e1 | |
Pietro Gagliardi | 5a7777ffaa | |
Pietro Gagliardi | 48fd34781e | |
Pietro Gagliardi | e9be6b9502 | |
Pietro Gagliardi | a23bd0f786 | |
Pietro Gagliardi | c269d1f6bc | |
Pietro Gagliardi | cdc570082c | |
Pietro Gagliardi | 3f3de363b5 | |
Pietro Gagliardi | 18c7eae71e | |
Pietro Gagliardi | f95c87dc96 | |
Pietro Gagliardi | cfc4b60ea1 | |
Pietro Gagliardi | 27a701c371 | |
Pietro Gagliardi | 4fae662575 | |
Pietro Gagliardi | 44e13e53bf | |
Pietro Gagliardi | 83129eeef5 | |
Pietro Gagliardi | 5952ad368d | |
Pietro Gagliardi | cce4419b9a | |
Pietro Gagliardi | 9ceed7c62d | |
Pietro Gagliardi | 001e439a9b | |
Pietro Gagliardi | 8b400f33f7 | |
Pietro Gagliardi | 7bb67c3e46 | |
Pietro Gagliardi | 042da74cc9 | |
Pietro Gagliardi | cd2d64c228 | |
Pietro Gagliardi | b1b60f6077 | |
Pietro Gagliardi | b722922428 | |
Pietro Gagliardi | d51a885431 | |
Pietro Gagliardi | da1f9b4875 | |
Pietro Gagliardi | c014fb5989 | |
Pietro Gagliardi | f75875e51b | |
Pietro Gagliardi | 4f2db68520 | |
Pietro Gagliardi | 2ccabe69f2 | |
Pietro Gagliardi | 0c66fc3995 | |
Pietro Gagliardi | f4218af3f5 | |
Pietro Gagliardi | c95cefaa1c | |
Pietro Gagliardi | 75db846736 | |
Pietro Gagliardi | 6bc5db4d79 | |
Pietro Gagliardi | de76afd43a | |
Pietro Gagliardi | 4c33e0fb83 | |
Pietro Gagliardi | 0748d05a19 | |
Pietro Gagliardi | d0edd9e738 | |
Pietro Gagliardi | a8d082fa79 | |
Pietro Gagliardi | 9d4253ad6d | |
Pietro Gagliardi | 5d072aad58 | |
Pietro Gagliardi | 01c1691161 | |
Pietro Gagliardi | 736af09557 | |
Pietro Gagliardi | 21b887a14a | |
Pietro Gagliardi | ad1644f9a8 | |
Pietro Gagliardi | c90ea52922 | |
Pietro Gagliardi | c0c8207a6c | |
Pietro Gagliardi | a410f9d5e5 | |
Pietro Gagliardi | 6801cf5282 | |
Pietro Gagliardi | 5c65e6d7fa | |
Pietro Gagliardi | 17f9a5eff5 | |
Pietro Gagliardi | 0264ea9905 | |
Pietro Gagliardi | c17f854d04 | |
Pietro Gagliardi | 80ada0a06b | |
Pietro Gagliardi | 4649e7d632 | |
Pietro Gagliardi | 644afdedaf | |
Pietro Gagliardi | a1b0979506 | |
Pietro Gagliardi | dcae8888d2 | |
Pietro Gagliardi | a9145c2f35 | |
Pietro Gagliardi | 6b161efed7 | |
Pietro Gagliardi | a907353c9c | |
Pietro Gagliardi | 517791f04e | |
Pietro Gagliardi | aa7da7b647 | |
Pietro Gagliardi | 3f744de64e | |
Pietro Gagliardi | edee2b930c | |
Pietro Gagliardi | fda6c4a743 | |
Pietro Gagliardi | c28adcbd01 | |
Pietro Gagliardi | f90150a579 | |
Pietro Gagliardi | 0d61f674c5 | |
Pietro Gagliardi | 0bc347dd62 | |
Pietro Gagliardi | 3e96dafe44 | |
Pietro Gagliardi | ed5bbc4dbd | |
Pietro Gagliardi | 29309ab040 | |
Pietro Gagliardi | 29ce809772 | |
Pietro Gagliardi | 0550e4bc00 | |
Pietro Gagliardi | c1d8c0f5ec | |
Pietro Gagliardi | b6a8d24c3e | |
Pietro Gagliardi | ad209175c1 | |
Pietro Gagliardi | 4d78b5a3ef | |
Pietro Gagliardi | 8c7f1987da | |
Pietro Gagliardi | 83dc1d6065 | |
Pietro Gagliardi | c2bace108d | |
Pietro Gagliardi | f966b6ee16 | |
Pietro Gagliardi | c74fc09261 | |
Pietro Gagliardi | 5d63b34904 | |
Pietro Gagliardi | f517742a3c | |
Pietro Gagliardi | ae4bfea10b | |
Pietro Gagliardi | ab82278a30 | |
Pietro Gagliardi | d54f23c0cc | |
Pietro Gagliardi | f3b0dc16ab | |
Pietro Gagliardi | c1ca78c46e | |
Pietro Gagliardi | 47a8ccaf98 | |
Pietro Gagliardi | 63773f703c | |
Pietro Gagliardi | e30ed33046 | |
Pietro Gagliardi | 28a5cd543a | |
Pietro Gagliardi | 6903bebe7d | |
Pietro Gagliardi | 89f64eb067 | |
Pietro Gagliardi | 7e3d6d2b1c | |
Pietro Gagliardi | a0fc3187e2 | |
Pietro Gagliardi | 7b10fa729b | |
Pietro Gagliardi | 70ff47cb06 | |
Pietro Gagliardi | afbf5f2d59 | |
Pietro Gagliardi | b34a427077 | |
Pietro Gagliardi | 05b1e26ee2 | |
Pietro Gagliardi | 97e37e5831 | |
Pietro Gagliardi | 5f454a6114 | |
Pietro Gagliardi | ee9df0491f | |
Pietro Gagliardi | ef05d40fad | |
Pietro Gagliardi | a0fd823328 | |
Pietro Gagliardi | 94d638e601 | |
Pietro Gagliardi | 91c1e8e517 | |
Pietro Gagliardi | db3de44432 | |
Pietro Gagliardi | 5f011acc0c | |
Pietro Gagliardi | 24320ce784 | |
Pietro Gagliardi | 65226ac416 | |
Pietro Gagliardi | 7486ff6b25 | |
Pietro Gagliardi | a2b9145931 | |
Pietro Gagliardi | 87a2195027 | |
Pietro Gagliardi | 0f41bb2188 | |
Pietro Gagliardi | 9b11ec4a6f | |
Pietro Gagliardi | 14c80eefb8 | |
Pietro Gagliardi | e808d85e01 | |
Pietro Gagliardi | 9e05efa091 | |
Pietro Gagliardi | e3a4d34196 | |
Pietro Gagliardi | c45ab57bce | |
Pietro Gagliardi | 822572d395 | |
Pietro Gagliardi | 379fc230e5 | |
Pietro Gagliardi | 22cc5ad834 | |
Pietro Gagliardi | 19fc6f674f | |
Pietro Gagliardi | 9f776c140d | |
Pietro Gagliardi | 7aa1b0870d | |
Pietro Gagliardi | 1189877ca5 | |
Pietro Gagliardi | e2842bae35 | |
Pietro Gagliardi | 6f4c65defc | |
Pietro Gagliardi | 5260d5c90f | |
Pietro Gagliardi | 6cf92b2058 | |
Pietro Gagliardi | 65c88fd1c4 | |
Pietro Gagliardi | 582a781171 | |
Pietro Gagliardi | 4ff61c81b0 | |
Pietro Gagliardi | 26e492539d | |
Pietro Gagliardi | 57dc482f2a | |
Pietro Gagliardi | b8f8e1c4f7 | |
Pietro Gagliardi | c6294f163f | |
Pietro Gagliardi | d040bca07e | |
Pietro Gagliardi | 12c01d0d42 | |
Pietro Gagliardi | 6d6cd66046 | |
Pietro Gagliardi | ddaa10b7e1 | |
Pietro Gagliardi | 9f2796ebac | |
Pietro Gagliardi | ece22bdddc | |
Pietro Gagliardi | 623575f25a | |
Pietro Gagliardi | 671c2031bf | |
Pietro Gagliardi | b5e8b76066 | |
Pietro Gagliardi | 3f4765d116 | |
Pietro Gagliardi | 0c673acf70 | |
Pietro Gagliardi | 5d1e6a0cf2 | |
Pietro Gagliardi | 1cf545d369 | |
Pietro Gagliardi | 4ae6ab2727 | |
Pietro Gagliardi | 8980a8663b | |
Pietro Gagliardi | bd2877154f | |
Pietro Gagliardi | 6ffe0a4c99 | |
Pietro Gagliardi | ef3352c033 | |
Pietro Gagliardi | 9ad77ac57e | |
Pietro Gagliardi | 8531710f03 | |
Pietro Gagliardi | efe118a81d | |
Pietro Gagliardi | a8ad49fead | |
Pietro Gagliardi | 76fabb37cf | |
Pietro Gagliardi | dea7436468 | |
Pietro Gagliardi | f2ce4c8f56 | |
Pietro Gagliardi | a8cd121003 | |
Pietro Gagliardi | ed378d4e0b | |
Pietro Gagliardi | 48cd6e1dd8 | |
Pietro Gagliardi | a93f5c8f53 | |
Pietro Gagliardi | 7d0f8403ab | |
Pietro Gagliardi | a17b7c8c14 | |
Pietro Gagliardi | 644e188e05 | |
Pietro Gagliardi | 664cf26cdc | |
Pietro Gagliardi | 1814745646 | |
Pietro Gagliardi | 3ddaf3052e | |
Pietro Gagliardi | b5a109c063 | |
Pietro Gagliardi | dca2e5f038 | |
Pietro Gagliardi | 2dd68fc47f | |
Pietro Gagliardi | 6493faf529 | |
Pietro Gagliardi | 3c6cc53a0a | |
Pietro Gagliardi | 0f4602ee0f | |
Pietro Gagliardi | dc8620f9a8 | |
Pietro Gagliardi | c090dacc4a | |
Pietro Gagliardi | b9d445554a | |
Pietro Gagliardi | 9daef443b2 | |
Pietro Gagliardi | b0e890ca1d | |
Pietro Gagliardi | ab5b3076ed | |
Pietro Gagliardi | 3049adbca0 | |
Pietro Gagliardi | 19ad0d33a3 | |
Pietro Gagliardi | edfd5e9157 | |
Pietro Gagliardi | 84a4fd8915 | |
Pietro Gagliardi | 4815629eef | |
Pietro Gagliardi | fd770430a9 | |
Pietro Gagliardi | a632d10701 | |
Pietro Gagliardi | cceae4845e | |
Pietro Gagliardi | d0dd91d8d8 | |
Pietro Gagliardi | b5927353e0 | |
Pietro Gagliardi | b3f2214f3e | |
Pietro Gagliardi | 79ecadb909 | |
Pietro Gagliardi | a6c1e1ed17 | |
Pietro Gagliardi | 2e82d4aad4 | |
Pietro Gagliardi | 3721bf0c8e | |
Pietro Gagliardi | afb87bda23 | |
Pietro Gagliardi | 7cb4f010d4 | |
Pietro Gagliardi | 7808b3ee94 | |
Pietro Gagliardi | 9060ef3852 | |
Pietro Gagliardi | e4f5b4f548 | |
Pietro Gagliardi | 07c613b2e1 | |
Pietro Gagliardi | dbbf84becc | |
Pietro Gagliardi | b8b1123027 | |
Pietro Gagliardi | f77f8d49d3 | |
Pietro Gagliardi | a8ab5be01a | |
Pietro Gagliardi | 3cd08a5b2d | |
Pietro Gagliardi | c41ac17dd4 | |
Pietro Gagliardi | d6cde02825 | |
Pietro Gagliardi | 5a6c302ae0 | |
Pietro Gagliardi | bf3492cd9c | |
Pietro Gagliardi | d346a8ca8e | |
Pietro Gagliardi | 43fd636071 | |
Pietro Gagliardi | bf1172bc0a | |
Pietro Gagliardi | 009a9c25aa | |
Pietro Gagliardi | 7be128d5bb | |
Pietro Gagliardi | 102dff6489 | |
Pietro Gagliardi | 57e4e0d13b | |
Pietro Gagliardi | 6412d8365f | |
Pietro Gagliardi | 3c9ff9c4bc | |
Pietro Gagliardi | bd666b8ec9 | |
Pietro Gagliardi | 4112630f59 | |
Pietro Gagliardi | 9f0bb3aacb | |
Pietro Gagliardi | a4c6817d33 | |
Pietro Gagliardi | 9bec2005a1 | |
Pietro Gagliardi | 40508a457c | |
Pietro Gagliardi | 2d8764f06b | |
Pietro Gagliardi | 8e5a12b869 | |
Pietro Gagliardi | 02b61c0156 | |
Pietro Gagliardi | 745440b1e7 | |
Pietro Gagliardi | 66f1f75992 | |
Pietro Gagliardi | efb761f5f2 | |
Pietro Gagliardi | 35759935ae | |
Pietro Gagliardi | a81ea6e3fb | |
Pietro Gagliardi | 973696137f | |
Pietro Gagliardi | 3b1f5e0e54 | |
Pietro Gagliardi | 43f7d1a661 | |
Pietro Gagliardi | 66247ce73d | |
Pietro Gagliardi | 25afc9b13f | |
Pietro Gagliardi | 56156f549b | |
Pietro Gagliardi | 96c346c2dd | |
Pietro Gagliardi | bd84da7179 | |
Pietro Gagliardi | caa926feeb | |
Pietro Gagliardi | 55a7e3e56e | |
Pietro Gagliardi | 101df7a469 | |
Pietro Gagliardi | 7c128e7bcd | |
Pietro Gagliardi | 45f69cf058 | |
Pietro Gagliardi | 7022e6f268 | |
Pietro Gagliardi | 0d21bf8846 | |
Pietro Gagliardi | b3049b0a1e | |
Pietro Gagliardi | 781a4117a7 | |
Pietro Gagliardi | 1a047da08c | |
Pietro Gagliardi | d7fa5e63be | |
Pietro Gagliardi | 637c7a7e2c | |
Pietro Gagliardi | 07dc3ee025 | |
Pietro Gagliardi | 7f986ef073 | |
Pietro Gagliardi | f97383f66c | |
Pietro Gagliardi | e1c970bdbd | |
Pietro Gagliardi | 4aaad25c02 | |
Pietro Gagliardi | ab42a9ed08 | |
Pietro Gagliardi | ca570868c9 | |
Pietro Gagliardi | c79ddfd971 | |
Pietro Gagliardi | 2e562e4946 | |
Pietro Gagliardi | bc0d3120c8 | |
Pietro Gagliardi | 3ebc58a5bf | |
Pietro Gagliardi | b7f06074fb | |
Pietro Gagliardi | 8b70e6d247 | |
Pietro Gagliardi | d49f8ae7e6 | |
Pietro Gagliardi | ff803bf792 | |
Pietro Gagliardi | 288c74b026 | |
Pietro Gagliardi | 952b36b1c2 | |
Pietro Gagliardi | 7424a9ea6c | |
Pietro Gagliardi | ee3e587ddc | |
Pietro Gagliardi | 98093ed46e | |
Pietro Gagliardi | 4c097f93f7 | |
Pietro Gagliardi | 09e98af110 | |
Pietro Gagliardi | 00411a4d07 | |
Pietro Gagliardi | 57abc83fe3 | |
Pietro Gagliardi | 89e882c4a8 | |
Pietro Gagliardi | bfd608cf8e | |
Pietro Gagliardi | 5836a2a236 | |
Pietro Gagliardi | 8655bbf19c | |
Pietro Gagliardi | 6a25efce63 | |
Pietro Gagliardi | e9fd8cc878 | |
Pietro Gagliardi | 020cabc61e | |
Pietro Gagliardi | dbfea28313 | |
Pietro Gagliardi | e2baa5bb5c | |
Pietro Gagliardi | 8b5b03794c | |
Pietro Gagliardi | 313f5864f5 | |
Pietro Gagliardi | 42623f92e9 | |
Pietro Gagliardi | 5537e823ef | |
Pietro Gagliardi | 99a0f9084c | |
Pietro Gagliardi | 796a9cf010 | |
Pietro Gagliardi | 7d29c4346d | |
Pietro Gagliardi | 25d4021269 | |
Pietro Gagliardi | 69000cda46 | |
Pietro Gagliardi | 965dec0157 | |
Pietro Gagliardi | 848c3813ee | |
Pietro Gagliardi | db6b6fd97b | |
Pietro Gagliardi | 559e4bc139 | |
Pietro Gagliardi | 05af10aade | |
Pietro Gagliardi | 2282ee2cf8 | |
Pietro Gagliardi | f948c30a3b | |
Pietro Gagliardi | 3257710fb7 | |
Pietro Gagliardi | 74468bb38f | |
Pietro Gagliardi | df8eadb980 | |
Pietro Gagliardi | 6c41fb712e | |
Pietro Gagliardi | 74ca863c1b | |
Pietro Gagliardi | 5fb4e4403e | |
Pietro Gagliardi | 8ffb2b1b1e | |
Pietro Gagliardi | 5548119d8d | |
Pietro Gagliardi | 812c559b11 | |
Pietro Gagliardi | 49bde22f81 | |
Pietro Gagliardi | bdf80516c5 | |
Pietro Gagliardi | 10ab539ff0 | |
Pietro Gagliardi | b20bf2d1c9 | |
Pietro Gagliardi | 0f6414af6f | |
Pietro Gagliardi | b8a7b57835 | |
Pietro Gagliardi | e03021a350 | |
Pietro Gagliardi | 64478bd5b0 | |
Pietro Gagliardi | 1bc2297597 | |
Pietro Gagliardi | 9c70782a0f | |
Pietro Gagliardi | 2c57498e44 | |
Pietro Gagliardi | aa49da98ba | |
Pietro Gagliardi | 7e631879e7 | |
Pietro Gagliardi | 408b106526 | |
Pietro Gagliardi | 4bd1ba2fa5 | |
Pietro Gagliardi | bd1ca240e3 | |
Pietro Gagliardi | 23591eeefa | |
Pietro Gagliardi | 80ca6e2fc1 | |
Pietro Gagliardi | 63952a3e20 | |
Pietro Gagliardi | f548f6d4d2 | |
Pietro Gagliardi | b8b3b3df39 | |
Pietro Gagliardi | f7867f3427 | |
Pietro Gagliardi | c6aa8c3324 | |
Pietro Gagliardi | b89a18f3dd | |
Pietro Gagliardi | 29c51b6348 | |
Pietro Gagliardi | 469484415d | |
Pietro Gagliardi | 2652772891 | |
Pietro Gagliardi | 484989e925 | |
Pietro Gagliardi | 74b1e2780d | |
Pietro Gagliardi | 5e45afd0a2 | |
Pietro Gagliardi | 6f3d45b107 | |
Pietro Gagliardi | da36b304f8 | |
Pietro Gagliardi | 668a908e76 | |
Pietro Gagliardi | 3001e5dfaf | |
Pietro Gagliardi | e5e60284fb | |
Pietro Gagliardi | 16c6425200 | |
Pietro Gagliardi | dcf34e6dab | |
Pietro Gagliardi | 2f58c2059e | |
Pietro Gagliardi | 745f2da3b8 | |
Pietro Gagliardi | 541f9d892d | |
Pietro Gagliardi | 4c46a25154 | |
Pietro Gagliardi | db9be102e6 | |
Pietro Gagliardi | 59b449b920 | |
Pietro Gagliardi | 1b046e763b | |
Pietro Gagliardi | 4f381d04a1 | |
Pietro Gagliardi | 759d6d1e46 | |
Pietro Gagliardi | 0149639edc | |
Pietro Gagliardi | b26e0677af | |
Pietro Gagliardi | ced0820e39 | |
Pietro Gagliardi | 5a0477d46d | |
Pietro Gagliardi | d861de85f6 | |
Pietro Gagliardi | 7387f08ee9 | |
Pietro Gagliardi | b2cee470ca | |
Pietro Gagliardi | de3fd3f546 | |
Pietro Gagliardi | d5eb06327d | |
Pietro Gagliardi | 9f4b6507b1 | |
Pietro Gagliardi | c5db824918 | |
Pietro Gagliardi | c9643ea5a6 | |
Pietro Gagliardi | 12d52be921 | |
Pietro Gagliardi | 9bcb6d0af0 | |
Pietro Gagliardi | 992d8694a9 |
|
@ -0,0 +1,97 @@
|
|||
Contributor Covenant Code of Conduct
|
||||
Our Pledge
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
Our Standards
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others’ private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
Enforcement Responsibilities
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
Scope
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
Enforcement
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
wherever Pietro Gagliardi may be contacted privately.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
Enforcement Guidelines
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
1. Correction
|
||||
Community Impact: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
Consequence: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
2. Warning
|
||||
Community Impact: A violation through a single incident or series of
|
||||
actions.
|
||||
Consequence: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
3. Temporary Ban
|
||||
Community Impact: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
Consequence: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
4. Permanent Ban
|
||||
Community Impact: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
Consequence: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
Attribution
|
||||
This Code of Conduct is adapted from the Contributor Covenant,
|
||||
version 2.1, available at
|
||||
https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
|
||||
Community Impact Guidelines were inspired by
|
||||
Mozilla’s code of conduct enforcement ladder.
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
|
107
Compatibility.md
107
Compatibility.md
|
@ -19,24 +19,18 @@ TODO look up PDC 2008 talk "new shell user interface"
|
|||
## GTK+
|
||||
TODO what ships with Ubuntu Quantal (12.10)?
|
||||
|
||||
### GTK+ 3.6
|
||||
ships with: Ubuntu Raring (13.04)
|
||||
|
||||
- GtkEntry and GtkTextView have input purposes and input hints for external input methods but do not change input themselves
|
||||
- according to Company, we connect to insert-text for that
|
||||
- GtkLevelBar
|
||||
- GtkMenuButton
|
||||
- **GtkSearchEntry**
|
||||
|
||||
### GTK+ 3.8
|
||||
ships with: Ubuntu Saucy (13.10)
|
||||
|
||||
Not many interesting new things to us here, unless you count widget-internal tickers and single-click instead of double-click to select list items (a la KDE)... and oh yeah, also widget opacity.
|
||||
|
||||
### GTK+ 3.10
|
||||
ships with: **Ubuntu Trusty (14.04 LTS)**
|
||||
<br>GLib version: 2.40
|
||||
|
||||
minimum versions (see `configure.ac` for the relevant tagged 3.x.0 versions):
|
||||
- GLib: 2.37.5
|
||||
- ATK: 2.7.5
|
||||
- cairo: 1.12.0
|
||||
- pango: 1.32.4
|
||||
- gdk-pixbuf: 2.27.1
|
||||
|
||||
features:
|
||||
- tab character stops in GtkEntry
|
||||
- GtkHeaderBar
|
||||
- intended for titlebar overrides; GtkInfoBar is what I keep thinking GtkHeaderBar is
|
||||
|
@ -50,6 +44,14 @@ ships with: **Ubuntu Trusty (14.04 LTS)**
|
|||
ships with: Ubuntu Utopic (14.10)
|
||||
<br>GLib version: 2.42
|
||||
|
||||
minimum versions:
|
||||
- GLib: 2.39.5
|
||||
- ATK: 2.7.5
|
||||
- cairo: 1.12.0
|
||||
- pango: 1.32.4
|
||||
- gdk-pixbuf: 2.27.1
|
||||
|
||||
features:
|
||||
- GtkActionBar (basically like the bottom-of-the-window toolbars in Mac programs)
|
||||
- gtk_get_locale_direction(), for internationalization
|
||||
- more control over GtkHeaderBar
|
||||
|
@ -62,6 +64,14 @@ ships with: Ubuntu Utopic (14.10)
|
|||
ships with: **Debian Jessie**, Ubuntu Vivid (15.04)
|
||||
<br>GLib version: Debian: 2.42, Ubuntu: 2.44
|
||||
|
||||
minimum versions:
|
||||
- GLib: 2.41.2
|
||||
- ATK: 2.12.0
|
||||
- cairo: 1.12.0
|
||||
- pango: 1.36.7
|
||||
- gdk-pixbuf: 2.30.0
|
||||
|
||||
features:
|
||||
- gestures
|
||||
- better GtkListbox selection handling
|
||||
- more style classes (TODO also prior?)
|
||||
|
@ -71,6 +81,14 @@ ships with: **Debian Jessie**, Ubuntu Vivid (15.04)
|
|||
ships with: Ubuntu Wily (15.10)
|
||||
<br>GLib version: 2.46
|
||||
|
||||
minimum versions:
|
||||
- GLib: 2.43.4
|
||||
- ATK: 2.15.1
|
||||
- cairo: 1.14.0
|
||||
- pango: 1.36.7
|
||||
- gdk-pixbuf: 2.30.0
|
||||
|
||||
features:
|
||||
- gtk_clipboard_get_default() (???)
|
||||
- **GtkGLArea**
|
||||
- proper xalign and yalign for GtkLabel; should get rid of runtime deprecation warnings
|
||||
|
@ -86,8 +104,69 @@ ships with: Ubuntu Wily (15.10)
|
|||
- GtkTextView: font fallbacks
|
||||
|
||||
### GTK+ 3.18
|
||||
ships with: TODO
|
||||
|
||||
minimum versions:
|
||||
- GLib: 2.45.8 (min_required 2.44 max_allowed 2.46 ?)
|
||||
- ATK: 2.15.1
|
||||
- cairo: 1.14.0
|
||||
- pango: 1.37.3
|
||||
- gdk-pixbuf: 2.30.0
|
||||
|
||||
features:
|
||||
- TODO
|
||||
|
||||
### GTK+ 3.20
|
||||
ships with: TODO
|
||||
|
||||
minimum versions (TODO double-check these):
|
||||
- GLib: 2.45.8 (min_required 2.44 max_allowed 2.46 ?)
|
||||
- ATK: 2.15.1
|
||||
- cairo: 1.14.0
|
||||
- pango: 1.37.3
|
||||
- gdk-pixbuf: 2.30.0
|
||||
|
||||
features:
|
||||
- TODO
|
||||
|
||||
### GTK+ 3.22
|
||||
ships with: TODO
|
||||
|
||||
minimum versions:
|
||||
- GLib: 2.49.4 (min_required 2.48 max_allowed 2.50 ?)
|
||||
- ATK: 2.15.1
|
||||
- cairo: 1.14.0
|
||||
- pango: 1.37.3
|
||||
- gdk-pixbuf: 2.30.0
|
||||
|
||||
features:
|
||||
- TODO
|
||||
|
||||
### GTK+ 3.24
|
||||
ships with: TODO
|
||||
|
||||
minimum versions:
|
||||
- GLib: 2.49.4 (min_required 2.48 max_allowed 2.50 ?)
|
||||
- ATK: 2.15.1
|
||||
- cairo: 1.14.0
|
||||
- pango: 1.41.0
|
||||
- gdk-pixbuf: 2.30.0
|
||||
|
||||
features:
|
||||
- TODO
|
||||
|
||||
### TODO greater versions
|
||||
ships with: TODO
|
||||
|
||||
minimum versions:
|
||||
- GLib: TODO (min_required TODO max_allowed TODO ?)
|
||||
- ATK: TODO
|
||||
- cairo: TODO
|
||||
- pango: TODO
|
||||
- gdk-pixbuf: TODO
|
||||
|
||||
features:
|
||||
- TODO
|
||||
|
||||
## Cocoa
|
||||
### Mac OS X 10.8
|
||||
|
|
|
@ -30,6 +30,10 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t
|
|||
|
||||
*Note that today's entry (Eastern Time) may be updated later today.*
|
||||
|
||||
* **<codedate**
|
||||
* **BIG CHANGES TODO INCLUDING DOCUMENTATION** TODO TODO TODO TODO You can read the documentation for more details, but the most important notes to know:
|
||||
* Static builds now require users of libui to define `uiStatic` before including `ui.h`.
|
||||
|
||||
* **7 April 2019**
|
||||
* **The build system has been switched to Meson.** See below for instructions. This change was made because the previous build system, CMake, caused countless headaches over trivial issues. Meson was chosen due to how unproblematic setting up libui's build just right was, as well as having design goals that are by coincidence closely aligned with what libui wants.
|
||||
* Travis CI has been replaced with Azure Pipelines and much of the AppVeyor CI configuration was integrated into the Azure Pipelines configuration. This shouldn't affect most developers.
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// cl winbuttonexplorertheme.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib windows.res
|
||||
// cl winbuttonexplorertheme.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib _res\windows.res
|
||||
|
||||
void diele(const char *func)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,492 @@
|
|||
// 31 august 2019
|
||||
#define UNICODE
|
||||
#define _UNICODE
|
||||
#define STRICT
|
||||
#define STRICT_TYPED_ITEMIDS
|
||||
#define WINVER 0x0600 /* from Microsoft's winnls.h */
|
||||
#define _WIN32_WINNT 0x0600
|
||||
#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */
|
||||
#define _WIN32_IE 0x0700
|
||||
#define NTDDI_VERSION 0x06000000
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
#include <uxtheme.h>
|
||||
#include <vsstyle.h>
|
||||
#include <vssym32.h>
|
||||
#include <windowsx.h>
|
||||
#include <dwmapi.h>
|
||||
#include "_res/detours.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// cl windwmblurbehindtest.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib dwmapi.lib _res\detours.lib _res\windows.res
|
||||
|
||||
void diele(const char *func)
|
||||
{
|
||||
DWORD le;
|
||||
|
||||
le = GetLastError();
|
||||
fprintf(stderr, "%s: %I32u\n", func, le);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void diehr(const char *func, HRESULT hr)
|
||||
{
|
||||
fprintf(stderr, "%s: 0x%08I32X\n", func, hr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// TODO if we merge this into libui proper, this will need to be deduplicated
|
||||
static inline HRESULT lastErrorToHRESULT(DWORD lastError)
|
||||
{
|
||||
if (lastError == 0)
|
||||
return E_FAIL;
|
||||
return HRESULT_FROM_WIN32(lastError);
|
||||
}
|
||||
|
||||
static void paintIntoBuffer(HWND hwnd, UINT uMsg, HDC dc, RECT *r)
|
||||
{
|
||||
HPAINTBUFFER bbuf;
|
||||
HDC bdc;
|
||||
HRESULT hr;
|
||||
|
||||
// TODO begin check errors
|
||||
bbuf = BeginBufferedPaint(dc, r, BPBF_TOPDOWNDIB, NULL, &bdc);
|
||||
if (uMsg == WM_PAINT)
|
||||
SendMessageW(hwnd, WM_PRINTCLIENT, (WPARAM) bdc, PRF_CLIENT | PRF_ERASEBKGND);
|
||||
else
|
||||
SendMessageW(hwnd, WM_PRINT, (WPARAM) bdc, PRF_ERASEBKGND | PRF_NONCLIENT);
|
||||
hr = BufferedPaintSetAlpha(bbuf, NULL, 255);
|
||||
hr = EndBufferedPaint(bbuf, TRUE);
|
||||
// TODO end check errors
|
||||
}
|
||||
|
||||
static HWND bpHWND = NULL;
|
||||
static HDC bpDC = NULL;
|
||||
static HDC bpPSDC = NULL;
|
||||
static HDC bpBufDC = NULL;
|
||||
static HPAINTBUFFER bpBuf = NULL;
|
||||
|
||||
static HDC (WINAPI *origBeginPaint)(HWND hwnd, LPPAINTSTRUCT ps) = BeginPaint;
|
||||
static HDC WINAPI ourBeginPaint(HWND hwnd, LPPAINTSTRUCT ps)
|
||||
{
|
||||
HDC dc, bdc;
|
||||
HPAINTBUFFER bbuf;
|
||||
|
||||
if (bpHWND == NULL) {
|
||||
dc = (*origBeginPaint)(hwnd, ps);
|
||||
if (dc == NULL)
|
||||
return NULL;
|
||||
bbuf = BeginBufferedPaint(dc, &(ps->rcPaint), BPBF_TOPDOWNDIB, NULL, &bdc);
|
||||
if (bbuf == NULL) // just draw normally, not much else we can do but deal with the graphical glitches
|
||||
return dc;
|
||||
bpHWND = hwnd;
|
||||
bpDC = dc;
|
||||
bpPSDC = ps->hdc;
|
||||
ps->hdc = bdc;
|
||||
bpBufDC = bdc;
|
||||
bpBuf = bbuf;
|
||||
return bdc;
|
||||
}
|
||||
return (*origBeginPaint)(hwnd, ps);
|
||||
}
|
||||
|
||||
static HANIMATIONBUFFER bpHAB = NULL;
|
||||
static HDC bpFromDC = NULL;
|
||||
static HDC bpFromBufDC = NULL;
|
||||
static HPAINTBUFFER bpFromBuf = NULL;
|
||||
static HDC bpToDC = NULL;
|
||||
static HDC bpToBufDC = NULL;
|
||||
static HPAINTBUFFER bpToBuf = NULL;
|
||||
|
||||
static HANIMATIONBUFFER (STDAPICALLTYPE *origBeginBufferedAnimation)(HWND hwnd, HDC hdcTarget, const RECT *prcTarget, BP_BUFFERFORMAT dwFormat, BP_PAINTPARAMS *pPaintParams, BP_ANIMATIONPARAMS *pAnimationParams, HDC *phdcFrom, HDC *phdcTo) = BeginBufferedAnimation;
|
||||
static HANIMATIONBUFFER STDAPICALLTYPE ourBeginBufferedAnimation(HWND hwnd, HDC hdcTarget, const RECT *prcTarget, BP_BUFFERFORMAT dwFormat, BP_PAINTPARAMS *pPaintParams, BP_ANIMATIONPARAMS *pAnimationParams, HDC *phdcFrom, HDC *phdcTo)
|
||||
{
|
||||
HANIMATIONBUFFER hab;
|
||||
HDC bdc;
|
||||
HPAINTBUFFER bbuf;
|
||||
bool doit;
|
||||
|
||||
doit = false;
|
||||
if (bpHWND != NULL && bpHWND == hwnd && bpBufDC != NULL && bpBufDC == hdcTarget) {
|
||||
doit = true;
|
||||
hdcTarget = bpDC;
|
||||
}
|
||||
hab = (*origBeginBufferedAnimation)(hwnd, hdcTarget, prcTarget, dwFormat, pPaintParams, pAnimationParams, phdcFrom, phdcTo);
|
||||
if (!doit)
|
||||
return hab;
|
||||
if (hab == NULL)
|
||||
return NULL;
|
||||
bpHAB = hab;
|
||||
if (phdcFrom != NULL && *phdcFrom != NULL) {
|
||||
bbuf = BeginBufferedPaint(*phdcFrom, prcTarget, BPBF_TOPDOWNDIB, NULL, &bdc);
|
||||
if (bbuf != NULL) { // otherwise, just draw normally, not much else we can do but deal with the graphical glitches
|
||||
bpFromDC = *phdcFrom;
|
||||
*phdcFrom = bdc;
|
||||
bpFromBufDC = bdc;
|
||||
bpFromBuf = bbuf;
|
||||
}
|
||||
}
|
||||
if (phdcTo != NULL && *phdcTo != NULL) {
|
||||
bbuf = BeginBufferedPaint(*phdcTo, prcTarget, BPBF_TOPDOWNDIB, NULL, &bdc);
|
||||
if (bbuf != NULL) {
|
||||
bpToDC = *phdcTo;
|
||||
*phdcTo = bdc;
|
||||
bpToBufDC = bdc;
|
||||
bpToBuf = bbuf;
|
||||
}
|
||||
}
|
||||
return hab;
|
||||
}
|
||||
|
||||
static BOOL (STDAPICALLTYPE *origBufferedPaintRenderAnimation)(HWND hwnd, HDC hdcTarget) = BufferedPaintRenderAnimation;
|
||||
static BOOL STDAPICALLTYPE ourBufferedPaintRenderAnimation(HWND hwnd, HDC hdcTarget)
|
||||
{
|
||||
if (bpHWND != NULL && bpHWND == hwnd && bpBufDC != NULL && bpBufDC == hdcTarget)
|
||||
hdcTarget = bpDC;
|
||||
return (*origBufferedPaintRenderAnimation)(hwnd, hdcTarget);
|
||||
}
|
||||
|
||||
static HRESULT (STDAPICALLTYPE *origEndBufferedAnimation)(HANIMATIONBUFFER hbpAnimation, BOOL fUpdateTarget) = EndBufferedAnimation;
|
||||
static HRESULT STDAPICALLTYPE ourEndBufferedAnimation(HANIMATIONBUFFER hbpAnimation, BOOL fUpdateTarget)
|
||||
{
|
||||
HRESULT hrFrom1 = S_OK;
|
||||
HRESULT hrFrom2 = S_OK;
|
||||
HRESULT hrTo1 = S_OK;
|
||||
HRESULT hrTo2 = S_OK;
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (bpHAB != NULL && bpHAB == hbpAnimation) {
|
||||
if (bpFromDC != NULL) {
|
||||
hrFrom1 = BufferedPaintSetAlpha(bpFromBuf, NULL, 255);
|
||||
hrFrom2 = EndBufferedPaint(bpFromBuf, TRUE);
|
||||
bpFromDC = NULL;
|
||||
bpFromBufDC = NULL;
|
||||
bpFromBuf = NULL;
|
||||
}
|
||||
if (bpToDC != NULL) {
|
||||
hrTo1 = BufferedPaintSetAlpha(bpToBuf, NULL, 255);
|
||||
hrTo2 = EndBufferedPaint(bpToBuf, TRUE);
|
||||
bpToDC = NULL;
|
||||
bpToBufDC = NULL;
|
||||
bpToBuf = NULL;
|
||||
}
|
||||
bpHAB = NULL;
|
||||
}
|
||||
hr = (*origEndBufferedAnimation)(hbpAnimation, fUpdateTarget);
|
||||
if (hrFrom1 != S_OK)
|
||||
return hrFrom1;
|
||||
if (hrFrom2 != S_OK)
|
||||
return hrFrom2;
|
||||
if (hrTo1 != S_OK)
|
||||
return hrTo1;
|
||||
if (hrTo2 != S_OK)
|
||||
return hrTo2;
|
||||
return hr;
|
||||
}
|
||||
|
||||
static BOOL (WINAPI *origEndPaint)(HWND hwnd, CONST PAINTSTRUCT *ps) = EndPaint;
|
||||
static BOOL WINAPI ourEndPaint(HWND hwnd, CONST PAINTSTRUCT *ps)
|
||||
{
|
||||
HRESULT hr1, hr2;
|
||||
BOOL ret;
|
||||
DWORD lasterr;
|
||||
|
||||
if (bpHWND == NULL || bpHWND != hwnd)
|
||||
return (*origEndPaint)(hwnd, ps);
|
||||
// TODO don't const this
|
||||
((PAINTSTRUCT *) ps)->hdc = bpPSDC;
|
||||
hr1 = BufferedPaintSetAlpha(bpBuf, NULL, 255);
|
||||
hr2 = EndBufferedPaint(bpBuf, TRUE);
|
||||
ret = (*origEndPaint)(hwnd, ps);
|
||||
lasterr = GetLastError();
|
||||
bpHWND = NULL;
|
||||
bpDC = NULL;
|
||||
bpPSDC = NULL;
|
||||
bpBufDC = NULL;
|
||||
bpBuf = NULL;
|
||||
if (hr1 != S_OK) {
|
||||
SetLastError(ERROR_GEN_FAILURE);
|
||||
if (HRESULT_FACILITY(hr1) == FACILITY_WIN32)
|
||||
SetLastError(HRESULT_CODE(hr1));
|
||||
return 0;
|
||||
}
|
||||
if (hr2 != S_OK) {
|
||||
SetLastError(ERROR_GEN_FAILURE);
|
||||
if (HRESULT_FACILITY(hr2) == FACILITY_WIN32)
|
||||
SetLastError(HRESULT_CODE(hr2));
|
||||
return 0;
|
||||
}
|
||||
SetLastError(lasterr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK buttonSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC dc;
|
||||
RECT r;
|
||||
|
||||
switch (uMsg) {
|
||||
case WM_PAINT:
|
||||
// TODO begin check errors
|
||||
#if 0
|
||||
dc = BeginPaint(hwnd, &ps);
|
||||
// paintIntoBuffer(hwnd, uMsg, dc, &(ps.rcPaint));
|
||||
DefSubclassProc(hwnd, uMsg, (WPARAM) dc, lParam);
|
||||
EndPaint(hwnd, &ps);
|
||||
#elif 1
|
||||
if (wParam != 0)
|
||||
break;
|
||||
|
||||
DetourTransactionBegin();
|
||||
DetourUpdateThread(GetCurrentThread());
|
||||
DetourAttach(&((PVOID &) origBeginPaint), ourBeginPaint);
|
||||
DetourAttach(&((PVOID &) origBeginBufferedAnimation), ourBeginBufferedAnimation);
|
||||
//TODO DetourAttach(&((PVOID &) origBufferedPaintRenderAnimation), ourBufferedPaintRenderAnimation);
|
||||
DetourAttach(&((PVOID &) origEndBufferedAnimation), ourEndBufferedAnimation);
|
||||
DetourAttach(&((PVOID &) origEndPaint), ourEndPaint);
|
||||
DetourTransactionCommit();
|
||||
|
||||
DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||
|
||||
DetourTransactionBegin();
|
||||
DetourUpdateThread(GetCurrentThread());
|
||||
DetourDetach(&((PVOID &) origBeginPaint), ourBeginPaint);
|
||||
DetourDetach(&((PVOID &) origBeginBufferedAnimation), ourBeginBufferedAnimation);
|
||||
//TODO DetourDetach(&((PVOID &) origBufferedPaintRenderAnimation), ourBufferedPaintRenderAnimation);
|
||||
DetourDetach(&((PVOID &) origEndBufferedAnimation), ourEndBufferedAnimation);
|
||||
DetourDetach(&((PVOID &) origEndPaint), ourEndPaint);
|
||||
DetourTransactionCommit();
|
||||
#else
|
||||
DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||
if (0) {
|
||||
RECT r;
|
||||
|
||||
GetClientRect(hwnd, &r);
|
||||
MapWindowRect(hwnd, GetParent(hwnd), &r);
|
||||
InvalidateRect(GetParent(hwnd), &r, TRUE);
|
||||
}
|
||||
#endif
|
||||
// TODO end check errors
|
||||
return 0;
|
||||
//TODO case WM_NCPAINT:
|
||||
// TODO begin check errors
|
||||
dc = GetDCEx(hwnd, (HRGN) wParam, DCX_WINDOW | DCX_INTERSECTRGN);
|
||||
GetRgnBox((HRGN) wParam, &r);
|
||||
paintIntoBuffer(hwnd, uMsg, dc, &r);
|
||||
ReleaseDC(hwnd, dc);
|
||||
// TODO end check errors
|
||||
return 0;
|
||||
case WM_NCDESTROY:
|
||||
if (RemoveWindowSubclass(hwnd, buttonSubProc, uIdSubclass) == FALSE)
|
||||
diele("RemoveWindowSubclass()");
|
||||
break;
|
||||
}
|
||||
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
HINSTANCE hInstance;
|
||||
HRGN rgn;
|
||||
BOOL created = FALSE;
|
||||
HBRUSH opaquebrush;
|
||||
|
||||
#define OURWIDTH 320
|
||||
#define OURHEIGHT 240
|
||||
|
||||
void onWM_CREATE(HWND hwnd)
|
||||
{
|
||||
DWM_BLURBEHIND dbb;
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
rgn = CreateRectRgn(0, 0, OURWIDTH / 2, OURHEIGHT);
|
||||
if (rgn == NULL)
|
||||
diele("CreateRectRgn()");
|
||||
ZeroMemory(&dbb, sizeof (DWM_BLURBEHIND));
|
||||
dbb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
|
||||
dbb.fEnable = TRUE;
|
||||
dbb.hRgnBlur = NULL;//rgn;
|
||||
hr = DwmEnableBlurBehindWindow(hwnd, &dbb);
|
||||
if (hr != S_OK)
|
||||
diehr("DwmEnableBlurBehindWindow()", hr);
|
||||
HWND w1=CreateWindowExW(0,
|
||||
L"BUTTON",L"Hello",
|
||||
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
|
||||
3*OURWIDTH/4,150,
|
||||
OURWIDTH/6,25,
|
||||
hwnd,NULL,hInstance,NULL);
|
||||
SetWindowSubclass(w1, buttonSubProc, 0, 0);
|
||||
HWND w2=CreateWindowExW(0,
|
||||
L"BUTTON",L"Hello",
|
||||
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
|
||||
3*OURWIDTH/4,200,
|
||||
OURWIDTH/6,25,
|
||||
hwnd,NULL,hInstance,NULL);
|
||||
SetWindowTheme(w2,L"",L"");
|
||||
SetWindowSubclass(w2, buttonSubProc, 0, 0);
|
||||
}
|
||||
|
||||
void doPaint(HWND hwnd, HDC dc, RECT *rcPaint)
|
||||
{
|
||||
RECT r, r2;
|
||||
HPAINTBUFFER bbuf;
|
||||
HDC bdc;
|
||||
HRESULT hr;
|
||||
|
||||
// First, fill with BLACK_BRUSH to satisfy the DWM
|
||||
r.left = 0;
|
||||
r.top = 0;
|
||||
r.right = OURWIDTH;
|
||||
r.bottom = OURHEIGHT;
|
||||
// TODO check error
|
||||
FillRect(dc, &r, (HBRUSH) GetStockObject(BLACK_BRUSH));
|
||||
|
||||
r.left = OURWIDTH / 2;
|
||||
// TODO check error
|
||||
IntersectRect(&r2, &r, rcPaint);
|
||||
bbuf = BeginBufferedPaint(dc, &r, BPBF_TOPDOWNDIB, NULL, &bdc);
|
||||
if (bbuf == NULL)
|
||||
diele("BeginBufferedPaint()");
|
||||
// TODO start check errors
|
||||
FillRect(bdc, &r, GetSysColorBrush(COLOR_BTNFACE));
|
||||
r.left = 3 * OURWIDTH / 4;
|
||||
r.top = 100;
|
||||
r.right = 7 * OURWIDTH / 8;
|
||||
r.bottom = 25;
|
||||
auto m = SetBkMode(bdc,TRANSPARENT);
|
||||
TextOutW(bdc, r.left, r.top, L"Hello", 5);
|
||||
SetBkMode(bdc, m);
|
||||
// TODO end check errors
|
||||
hr = BufferedPaintSetAlpha(bbuf, NULL, 255);
|
||||
if (hr != S_OK)
|
||||
diehr("BufferedPaintSetAlpha()", hr);
|
||||
hr = EndBufferedPaint(bbuf, TRUE);
|
||||
if (hr != S_OK)
|
||||
diehr("EndBufferedPaint()", hr);
|
||||
}
|
||||
|
||||
void onWM_PAINT(HWND hwnd, WPARAM wParam)
|
||||
{
|
||||
HDC dc;
|
||||
PAINTSTRUCT ps;
|
||||
RECT rcPaint;
|
||||
|
||||
if (wParam != 0) {
|
||||
// TODO check errors
|
||||
GetClientRect(hwnd, &rcPaint);
|
||||
doPaint(hwnd, (HDC) wParam, &rcPaint);
|
||||
return;
|
||||
}
|
||||
|
||||
ZeroMemory(&ps, sizeof (PAINTSTRUCT));
|
||||
dc = BeginPaint(hwnd, &ps);
|
||||
if (dc == NULL)
|
||||
diele("BeginPaint()");
|
||||
doPaint(hwnd, dc, &(ps.rcPaint));
|
||||
EndPaint(hwnd, &ps);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg) {
|
||||
case WM_CREATE:
|
||||
onWM_CREATE(hwnd);
|
||||
break;
|
||||
case WM_PAINT:
|
||||
// case WM_PRINTCLIENT:
|
||||
// case WM_ERASEBKGND:
|
||||
onWM_PAINT(hwnd, wParam);
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
}
|
||||
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
STARTUPINFOW si;
|
||||
int nCmdShow;
|
||||
INITCOMMONCONTROLSEX icc;
|
||||
HICON hDefaultIcon;
|
||||
HCURSOR hDefaultCursor;
|
||||
DWORD dwStyle, dwExStyle;
|
||||
RECT r;
|
||||
WNDCLASSW wc;
|
||||
HWND mainwin;
|
||||
MSG msg;
|
||||
HRESULT hr;
|
||||
|
||||
hInstance = (HINSTANCE) (&__ImageBase);
|
||||
nCmdShow = SW_SHOWDEFAULT;
|
||||
GetStartupInfoW(&si);
|
||||
if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0)
|
||||
nCmdShow = si.wShowWindow;
|
||||
|
||||
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
|
||||
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
|
||||
icc.dwICC = ICC_STANDARD_CLASSES | ICC_BAR_CLASSES | ICC_COOL_CLASSES;
|
||||
if (InitCommonControlsEx(&icc) == 0)
|
||||
diele("InitCommonControlsEx()");
|
||||
|
||||
hDefaultIcon = LoadIconW(NULL, IDI_APPLICATION);
|
||||
if (hDefaultIcon == NULL)
|
||||
diele("LoadIconW(IDI_APPLICATION)");
|
||||
hDefaultCursor = LoadCursorW(NULL, IDC_ARROW);
|
||||
if (hDefaultCursor == NULL)
|
||||
diele("LoadCursorW(IDC_ARROW)");
|
||||
|
||||
hr = BufferedPaintInit();
|
||||
if (hr != S_OK)
|
||||
diehr("BufferedPaintInit()", hr);
|
||||
|
||||
ZeroMemory(&wc, sizeof (WNDCLASSW));
|
||||
wc.lpszClassName = L"mainwin";
|
||||
wc.lpfnWndProc = wndproc;
|
||||
wc.hInstance = hInstance;
|
||||
wc.hIcon = hDefaultIcon;
|
||||
wc.hCursor = hDefaultCursor;
|
||||
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
|
||||
if (RegisterClassW(&wc) == 0)
|
||||
diele("RegisterClassW()");
|
||||
|
||||
dwStyle = WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
|
||||
dwExStyle = 0;
|
||||
r.left = 0;
|
||||
r.top = 0;
|
||||
r.right = OURWIDTH;
|
||||
r.bottom = OURHEIGHT;
|
||||
if (AdjustWindowRectEx(&r, dwStyle, FALSE, dwExStyle) == 0)
|
||||
diele("AdjustWindowRectEx()");
|
||||
mainwin = CreateWindowExW(dwExStyle,
|
||||
L"mainwin", L"Main Window",
|
||||
dwStyle,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
r.right - r.left, r.bottom - r.top,
|
||||
NULL, NULL, hInstance, NULL);
|
||||
if (mainwin == NULL)
|
||||
diele("CreateWindowExW(L\"mainwin\")");
|
||||
|
||||
ShowWindow(mainwin, nCmdShow);
|
||||
if (UpdateWindow(mainwin) == 0)
|
||||
diele("UpdateWindow()");
|
||||
|
||||
for (;;) {
|
||||
int res;
|
||||
|
||||
res = GetMessageW(&msg, NULL, 0, 0);
|
||||
if (res < 0)
|
||||
diele("GetMessageW()");
|
||||
if (res == 0)
|
||||
break;
|
||||
if (IsDialogMessageW(mainwin, &msg) == 0) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
}
|
||||
BufferedPaintUnInit();
|
||||
return 0;
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// cl winrebarexplorertheme.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib windows.res
|
||||
// cl winrebarexplorertheme.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib _res\windows.res
|
||||
|
||||
void diele(const char *func)
|
||||
{
|
||||
|
|
|
@ -23,3 +23,4 @@ alphablending:
|
|||
http://www.os2voice.org/vnewsarc/bn2007122.html
|
||||
http://www.mozillazine.org/talkback.html?article=194
|
||||
https://books.google.com/books?id=9cpU5uYCzq4C&pg=PA202&lpg=PA202&dq=%22OS/2%22+alphablending&source=bl&ots=uatEop2jAL&sig=HAa_ofQSKsk6-8tBR6YZ6MRJG_0&hl=en&sa=X&ved=0ahUKEwiDq5HukLbaAhUk8IMKHR7aCw4Q6AEIWTAI#v=onepage&q=%22OS%2F2%22%20alphablending&f=false
|
||||
investigate eicons.dll
|
||||
|
|
|
@ -21,3 +21,19 @@ https://medium.com/@ivan.ha/how-to-mimic-a-2k-monitor-as-retina-display-in-macos
|
|||
https://www.google.com/search?client=firefox-b-1-d&q=IOFramebuffer+10.13
|
||||
https://www.quora.com/What-is-the-underlying-reason-if-DisplayLink-is-no-longer-working-properly-on-Mac-OS
|
||||
https://github.com/Siguza/iokit-utils
|
||||
https://developer.apple.com/library/archive/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/BaseClasses/BaseClasses.html#//apple_ref/doc/uid/TP0000016-BAJEEGAF
|
||||
https://github.com/andreacremaschi/EWProxyFramebuffer
|
||||
https://github.com/practicalswift/osx/blob/master/src/iographics/IOGraphicsFamily/IOAccelerator.cpp
|
||||
https://github.com/practicalswift/osx/tree/master/src/iographics/IOGraphicsFamily
|
||||
https://www.google.com/search?q=macos+monitor+driver&client=firefox-b-1-d&ei=zh3QXLOqHcbI_QaQtISABg&start=10&sa=N&ved=0ahUKEwizyb_W6YbiAhVGZN8KHRAaAWAQ8tMDCKgB&biw=1083&bih=606
|
||||
https://www.google.com/search?client=firefox-b-1-d&q=macos+10.13+monitor+driver
|
||||
https://www.google.com/search?client=firefox-b-1-d&q=macos+10.13+monitor+driver+development
|
||||
https://www.kensington.com/news/docking-connectivity-blog/Mac-OS-10.13.4-and-OS-10.14-DisplayLink-Issue-Resolved/
|
||||
https://www.kensington.com/news/docking-connectivity-blog/Mac-OS-10.13.4-and-OS-10.14-DisplayLink-Issue-Resolved/
|
||||
https://www.displaylink.com/downloads
|
||||
https://www.google.com/search?client=firefox-b-1-d&q=macos+10.13+monitor+driver+github
|
||||
https://apple.stackexchange.com/questions/335610/where-do-i-find-macos-graphics-drivers
|
||||
http://www.cgl.ucsf.edu/chimera/graphics/updatemac.html
|
||||
https://support.apple.com/downloads/driver-macbook
|
||||
https://support.apple.com/en_US/downloads/macbookpro
|
||||
https://www.kensington.com/news/docking-connectivity-blog/resolving-mac-os-x-10.13.4-displaylink-issue/
|
||||
|
|
50
_notes/misc
50
_notes/misc
|
@ -211,3 +211,53 @@ whatever this is seems to have no documentation... it appears to be related to
|
|||
unreachable code optimizations
|
||||
https://docs.microsoft.com/en-us/cpp/intrinsics/assume?view=vs-2017
|
||||
https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
|
||||
|
||||
|
||||
(#gtk) [01:57:47] <gwutz2[m]> I have a question about blocking signal handlers from emission when using a GtkSearchEntry. As i did it in the past with signal emissions is to block the handler, set anything on the Entry, unblock the handler. But the search entry emits after 150 ms therefore this does not work for GtkSearchEntrys. Is there a good way to archieve the same?
|
||||
|
||||
|
||||
https://devblogs.microsoft.com/oldnewthing/20191010-00/?p=102978
|
||||
TODO put this in the right file
|
||||
|
||||
https://devblogs.microsoft.com/oldnewthing/20191011-00/?p=102989
|
||||
https://devblogs.microsoft.com/oldnewthing/20191015-00/?p=102996
|
||||
TODO put this one in the right file too?
|
||||
the first one in this block isn't part of that TODO
|
||||
|
||||
https://devblogs.microsoft.com/oldnewthing/20191021-00/?p=103014
|
||||
https://devblogs.microsoft.com/oldnewthing/20191025-00/?p=103025
|
||||
|
||||
|
||||
https://devblogs.microsoft.com/oldnewthing/20191108-00/?p=103080
|
||||
|
||||
https://devblogs.microsoft.com/oldnewthing/20200110-00/?p=103316
|
||||
ribbon https://github.com/MicrosoftDocs/Contribute/issues/267#issuecomment-610717385
|
||||
|
||||
https://devblogs.microsoft.com/oldnewthing/20200730-00/?p=104021
|
||||
https://devblogs.microsoft.com/oldnewthing/20200807-00/?p=104056
|
||||
https://devblogs.microsoft.com/oldnewthing/20200821-00/?p=104112
|
||||
|
||||
https://devblogs.microsoft.com/oldnewthing/20201030-00/?p=104409
|
||||
https://devblogs.microsoft.com/oldnewthing/20201109-00/?p=104431
|
||||
https://devblogs.microsoft.com/oldnewthing/20201123-00/?p=104476
|
||||
|
||||
https://devblogs.microsoft.com/oldnewthing/20201231-00/?p=104627
|
||||
|
||||
autoscroll on drag on windows {
|
||||
https://devblogs.microsoft.com/oldnewthing/20210125-00/?p=104757
|
||||
https://devblogs.microsoft.com/oldnewthing/20210126-00/?p=104759
|
||||
https://devblogs.microsoft.com/oldnewthing/20210127-00/?p=104764
|
||||
https://devblogs.microsoft.com/oldnewthing/20210128-00/?p=104768
|
||||
https://devblogs.microsoft.com/oldnewthing/20210129-00/?p=104773
|
||||
}
|
||||
|
||||
https://devblogs.microsoft.com/oldnewthing/20210202-00/?p=104785
|
||||
|
||||
https://gankra.github.io/blah/text-hates-you/
|
||||
https://lord.io/text-editing-hates-you-too/
|
||||
|
||||
https://devblogs.microsoft.com/oldnewthing/20210526-00/?p=105252
|
||||
|
||||
https://devblogs.microsoft.com/oldnewthing/20210527-00/?p=105255 inclusion and support of icu in newer win10
|
||||
|
||||
https://gitlab.gnome.org/jadahl/libdecoration
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
SGI IRIS 2000
|
||||
sgi iris 2500T
|
||||
sgi iris 3130
|
||||
https://en.wikipedia.org/wiki/Sun-1
|
||||
https://en.wikipedia.org/wiki/Sun-2
|
||||
https://en.wikipedia.org/wiki/Sun-3
|
||||
https://en.wikipedia.org/wiki/HP_9000
|
||||
https://en.wikipedia.org/wiki/HP_series_80
|
||||
https://en.wikipedia.org/wiki/HP_9845C
|
||||
|
||||
note that some of these are 68000-based systems, and a few of those I want to look up for my own interests — especially since those would probably will just suffice with the GTK+ 3 port assuming a modern unix-like system can be run on them
|
||||
|
||||
IRIX {
|
||||
007-2006-120.pdf
|
||||
http://irix7.com/techpubs/007-2006-120.pdf
|
||||
|
||||
007-2006-130.pdf
|
||||
http://irix7.com/techpubs/007-2006-130.pdf
|
||||
|
||||
007-2167-005.pdf
|
||||
http://irix7.com/techpubs/007-2167-005.pdf
|
||||
|
||||
007-2167-006.pdf
|
||||
http://irix7.com/techpubs/007-2167-006.pdf
|
||||
|
||||
007-2006-080.pdf
|
||||
http://irix7.com/techpubs/007-2006-080.pdf
|
||||
|
||||
007-2006-090.pdf
|
||||
http://irix7.com/techpubs/007-2006-090.pdf
|
||||
|
||||
007-2006-100.pdf
|
||||
http://irix7.com/techpubs/007-2006-100.pdf
|
||||
|
||||
007-2167-001.pdf
|
||||
http://irix7.com/techpubs/007-2167-001.pdf
|
||||
|
||||
007-2167-002.pdf
|
||||
http://irix7.com/techpubs/007-2167-002.pdf
|
||||
|
||||
007-2167-003.pdf
|
||||
http://irix7.com/techpubs/007-2167-003.pdf
|
||||
|
||||
007-2851-001.pdf
|
||||
http://irix7.com/techpubs/007-2851-001.pdf
|
||||
|
||||
007-1472-020.pdf
|
||||
http://irix7.com/techpubs/007-1472-020.pdf
|
||||
|
||||
007-1797-020.pdf
|
||||
http://irix7.com/techpubs/007-1797-020.pdf
|
||||
|
||||
007-1797-030.pdf
|
||||
http://irix7.com/techpubs/007-1797-030.pdf
|
||||
|
||||
007-3951-001.pdf
|
||||
http://irix7.com/techpubs/007-3951-001.pdf
|
||||
} GTK+ is not an option because of no dbus, but maybe we could get away with mixing Motif and some 2D library that's in IRIX itself? but which one?...
|
|
@ -0,0 +1,25 @@
|
|||
[16:28:35] <+andlabs> https://github.com/microsoft/microsoft-ui-xaml/blob/d883cf3593912ded1a1fcc73f38768fda8ee3a45/dev/Materials/Acrylic/AcrylicBrush.cpp
|
||||
[16:28:47] <+andlabs> oh, so the acrylic effect in windows 10 uwp IS open source now after all
|
||||
[16:29:05] <+andlabs> and I bet most of it is possible in non-UWP apps with DirectComposition and Direct2D
|
||||
[16:29:46] <+andlabs> the only problem left is Get(Host)BackgroundBrush(), which are not in the open-source components and are still uWP only, and are how the actual content to blend with is grabbed
|
||||
Pietro Gagliardi, [19.05.19 00:12]
|
||||
[In reply to Pietro Gagliardi]
|
||||
https://support.microsoft.com/en-us/help/2670838/platform-update-for-windows-7-sp1-and-windows-server-2008-r2-sp1 oh, directcomposition isn't part of the windows 7 platform update, so it's a hard win8 only :|
|
||||
Pietro Gagliardi, [19.05.19 00:54]
|
||||
https://docs.microsoft.com/en-us/windows/desktop/api/dxgi1_2/nf-dxgi1_2-idxgifactory2-createswapchainforcomposition yeah, windows 7 doesn't have a flexible composition API; boo 😐
|
||||
|
||||
Pietro Gagliardi, [19.05.19 00:54]
|
||||
I guess I can use DwmEnableBlurBehindWindow on the older platforms...
|
||||
|
||||
Pietro Gagliardi, [19.05.19 00:54]
|
||||
won't work for blur-behind window-content, only blur-behind everything-underneath-the-window
|
||||
|
||||
|
||||
|
||||
https://github.com/microsoft/microsoft-ui-xaml/blob/d883cf3593912ded1a1fcc73f38768fda8ee3a45/dev/Materials/Acrylic/AcrylicBrush.cpp
|
||||
https://github.com/microsoft/WindowsCompositionSamples/issues/219
|
||||
https://docs.microsoft.com/en-us/windows/desktop/directcomp/directcomposition-portal
|
||||
https://msdn.microsoft.com/en-us/magazine/mt590968.aspx
|
||||
https://kennykerr.ca/2014/09/02/directcomposition/
|
||||
https://stackoverflow.com/questions/44000217/mimicking-acrylic-in-a-win32-app
|
||||
https://docs.microsoft.com/en-us/windows/desktop/api/dxgi1_2/nf-dxgi1_2-idxgifactory2-createswapchainforcomposition
|
|
@ -0,0 +1,37 @@
|
|||
https://marketplace.visualstudio.com/items?itemName=ms-autotest.screen-resolution-utility-task
|
||||
https://devblogs.microsoft.com/oldnewthing/20180510-00/?p=98725
|
||||
https://blogs.windows.com/buildingapps/2017/05/19/improving-high-dpi-experience-gdi-based-desktop-apps/
|
||||
https://technet.microsoft.com/en-us/evalcenter/dn469266(v=vs.90)
|
||||
https://www.windowscentral.com/how-change-high-dpi-settings-classic-apps-windows-10-april-2018-update
|
||||
https://stackoverflow.com/questions/44398075/can-dpi-scaling-be-enabled-disabled-programmatically-on-a-per-session-basis
|
||||
https://stackoverflow.com/questions/13858665/disable-dpi-awareness-for-wpf-application
|
||||
https://stackoverflow.com/questions/16571568/scaling-ui-with-dpi-change-of-non-mfc-application
|
||||
https://stackoverflow.com/questions/4075802/creating-a-dpi-aware-application
|
||||
https://kynosarges.org/WindowsDpi.html ***
|
||||
http://www.drdobbs.com/windows/coding-for-high-dpi-displays-in-windows/240168736
|
||||
https://www.tenforums.com/tutorials/5990-change-dpi-scaling-level-displays-windows-10-a.html
|
||||
https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dpi-related-apis-and-registry-settings ????
|
||||
https://stackoverflow.com/questions/35233182/how-can-i-change-windows-10-display-scaling-programmatically-using-c-sharp ****
|
||||
https://stackoverflow.com/questions/5977445/how-to-get-windows-display-settings
|
||||
https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/high-dpi-support-for-it-professionals ????
|
||||
actually ****
|
||||
https://docs.microsoft.com/en-us/windows-hardware/drivers/display/index
|
||||
https://docs.microsoft.com/en-us/windows-hardware/drivers/display/display-samples
|
||||
https://www.google.com/search?client=firefox-b-1-d&ei=M0DQXP_vNYKm_Qb_z5P4Cw&q=winapi+fake+monitor+driver&oq=winapi+fake+monitor+driver&gs_l=psy-ab.3..33i299l2j33i160.446439.447219..447312...0.0..0.173.795.0j6......0....1..gws-wiz.......0i71j33i22i29i30.LY8Gclmd9X4
|
||||
https://docs.microsoft.com/en-us/windows/desktop/gdi/the-virtual-screen
|
||||
https://www.autoitscript.com/forum/topic/134534-_desktopdimensions-details-about-the-primary-and-secondary-monitors/
|
||||
https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/um/MultiMon.h
|
||||
https://stackoverflow.com/questions/20367371/how-do-i-utilize-the-functionality-of-a-multi-monitor-setup-without-physical-har
|
||||
????
|
||||
https://stackoverflow.com/questions/16510930/creating-a-virtual-monitor-virtual-display-device ????
|
||||
https://superuser.com/questions/91786/where-can-i-find-vista-win7-virtual-display-drivers ????
|
||||
http://virtualmonitor.github.io/
|
||||
https://docs.microsoft.com/en-us/windows-hardware/drivers/display/overriding-monitor-edids ????
|
||||
https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/index
|
||||
https://docs.microsoft.com/en-us/windows-hardware/drivers/display/windows-vista-display-driver-model-design-guide
|
||||
https://docs.microsoft.com/en-us/windows-hardware/drivers/display/monitor-class-function-driver
|
||||
https://docs.microsoft.com/en-us/windows-hardware/drivers/display/monitor-hot-plug-detection
|
||||
https://docs.microsoft.com/en-us/windows-hardware/drivers/display/indirect-display-driver-model-overview ****
|
||||
https://docs.microsoft.com/en-us/windows-hardware/drivers/display/iddcx-objects ****
|
||||
https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/iddcx/ns-iddcx-idarg_in_adapter_init ****
|
||||
https://github.com/kfroe/IndirectDisplay/blob/master/IddSampleDriver/Driver.cpp#L380-L389 ****
|
|
@ -0,0 +1,34 @@
|
|||
https://stackoverflow.com/questions/27787966/winapi-dwmapi-blur-behind-window-with-irregular-shape
|
||||
https://archive.codeplex.com/?p=glassui
|
||||
https://stackoverflow.com/questions/17246206/wpf-glass-window-with-no-border-and-no-resize-out-of-focus
|
||||
https://github.com/rust-windowing/winit/issues/260
|
||||
https://stackoverflow.com/questions/31778017/blur-behind-transparent-wpf-window
|
||||
https://web.archive.org/web/20170701081532/http://withinrafael.com/adding-the-aero-glass-blur-to-your-windows-10-apps/
|
||||
https://stackoverflow.com/questions/32335945/blur-behind-window-with-titlebar-in-windows-10-stopped-working-after-windows-up
|
||||
https://stackoverflow.com/questions/32335945/blur-behind-window-with-titlebar-in-windows-10-stopped-working-after-windows-up
|
||||
https://web.archive.org/web/20170628221023/https://blogs.windows.com/windowsexperience/2015/04/29/new-windows-10-insider-preview-build-10074-now-available/
|
||||
http://undoc.airesoft.co.uk/user32.dll/SetWindowCompositionAttribute.php
|
||||
https://gist.github.com/ysc3839/b08d2bff1c7dacde529bed1d37e85ccf
|
||||
https://gist.github.com/ethanhs/0e157e4003812e99bf5bc7cb6f73459f
|
||||
http://undoc.airesoft.co.uk/user32.dll/GetWindowCompositionAttribute.php
|
||||
https://withinrafael.com/2015/07/08/adding-the-aero-glass-blur-to-your-windows-10-apps/
|
||||
http://www.classicshell.net/forum/viewtopic.php?f=10&t=6444
|
||||
https://www.autoitscript.com/forum/topic/183488-_winapi_dwmenableblurbehindwindow-in-windows-10/
|
||||
https://stackoverflow.com/questions/31151539/native-aero-blur-without-glass-effect-on-borderless-wpf-window
|
||||
https://stackoverflow.com/questions/35736399/uwp-app-with-background-blurred-setwindowcompositionattribute
|
||||
https://www.autoitscript.com/forum/topic/189638-win10-dwm-blur-with-rounded-corners-help/
|
||||
https://www.reddit.com/r/Windows10/comments/5xa13h/is_there_any_way_i_can_make_my_command_prompt/
|
||||
https://social.msdn.microsoft.com/Forums/vstudio/en-US/ff875100-718f-473b-acf3-e2724339999f/blur-behind-option-for-wpf-controls-within-the-application?forum=wpf
|
||||
https://docs.microsoft.com/en-us/windows/win32/dwm/composition-ovw
|
||||
https://github.com/rust-windowing/winit/issues/538
|
||||
http://winclassic.boards.net/thread/39/extended-glass-borders-fix
|
||||
https://stackoverflow.com/questions/27787966/winapi-dwmapi-blur-behind-window-with-irregular-shape
|
||||
https://coldjackle.wordpress.com/2012/07/17/c-aero-glass/
|
||||
https://stackoverflow.com/questions/52735048/rounded-window-corners-with-transparent-blurred-form
|
||||
https://books.google.com/books?id=Z3g8PuyLsTMC&pg=PT149&lpg=PT149&dq=aero+glass+guide&source=bl&ots=Fg99AzeDai&sig=ACfU3U0vKunGz2-l3hykLmX6iyhMrzNv6w&hl=en&sa=X&ved=2ahUKEwjRzJz7l67kAhURYcAKHe1HDYEQ6AEwG3oECAgQAQ#v=onepage&q=aero%20glass%20guide&f=false
|
||||
https://stackoverflow.com/questions/3822609/documentation-and-api-samples-for-drawing-on-windows-aero-glass-dwm-gdi-gdi
|
||||
https://stackoverflow.com/questions/1870139/windows-aero-what-color-to-paint-to-make-glass-appear
|
||||
http://forums.codeguru.com/showthread.php?499073-Win32-Aero-glass-and-drawing-transparent-gdi-shapes
|
||||
https://archive.msdn.microsoft.com/VistaBridge/Release/ProjectReleases.aspx?ReleaseId=2230
|
||||
http://blog.delphi-jedi.net/2008/05/01/translucent-windows-with-aero/
|
||||
https://delphihaven.wordpress.com/2010/08/19/custom-drawing-on-glass-1/
|
|
@ -0,0 +1,14 @@
|
|||
http://www.danielmoth.com/Blog/Vista-Glass-Answers-And-DwmEnableBlurBehindWindow.aspx
|
||||
https://weblogs.asp.net/kennykerr/Windows-Vista-for-Developers-_1320_-Part-3-_1320_-The-Desktop-Window-Manager
|
||||
https://stackoverflow.com/questions/7981322/strategy-for-creating-a-layered-window-with-child-windows-controls
|
||||
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-printwindow
|
||||
http://www.danielmoth.com/Blog/glass-nugget.aspx
|
||||
https://weblogs.asp.net/kennykerr/controls-and-the-desktop-window-manager
|
||||
https://docs.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-bufferedpaintinit
|
||||
https://docs.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-beginbufferedpaint
|
||||
https://docs.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-bufferedpaintsetalpha
|
||||
https://docs.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-endbufferedpaint
|
||||
https://docs.microsoft.com/en-us/windows/win32/api/uxtheme/nf-uxtheme-bufferedpaintuninit
|
||||
https://weblogs.asp.net/kennykerr/Windows-Vista-for-Developers-_1320_-Part-1-_1320_-Aero-Wizards
|
||||
https://weblogs.asp.net/kennykerr/Windows-Vista-for-Developers-_1320_-Part-2-_1320_-Task-Dialogs-in-Depth
|
||||
https://weblogs.asp.net/kennykerr/Windows-Vista-for-Developers-_1320_-Part-6-_1320_-The-New-File-Dialogs
|
|
@ -0,0 +1,4 @@
|
|||
uiprivExtern char *uiOpenFile(uiWindow *parent);
|
||||
uiprivExtern char *uiSaveFile(uiWindow *parent);
|
||||
uiprivExtern void uiMsgBox(uiWindow *parent, const char *title, const char *description);
|
||||
uiprivExtern void uiMsgBoxError(uiWindow *parent, const char *title, const char *description);
|
|
@ -0,0 +1,17 @@
|
|||
typedef struct uiMenuItem uiMenuItem;
|
||||
#define uiMenuItem(this) ((uiMenuItem *) (this))
|
||||
uiprivExtern void uiMenuItemEnable(uiMenuItem *m);
|
||||
uiprivExtern void uiMenuItemDisable(uiMenuItem *m);
|
||||
uiprivExtern void uiMenuItemOnClicked(uiMenuItem *m, void (*f)(uiMenuItem *sender, uiWindow *window, void *data), void *data);
|
||||
uiprivExtern int uiMenuItemChecked(uiMenuItem *m);
|
||||
uiprivExtern void uiMenuItemSetChecked(uiMenuItem *m, int checked);
|
||||
|
||||
typedef struct uiMenu uiMenu;
|
||||
#define uiMenu(this) ((uiMenu *) (this))
|
||||
uiprivExtern uiMenuItem *uiMenuAppendItem(uiMenu *m, const char *name);
|
||||
uiprivExtern uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name);
|
||||
uiprivExtern uiMenuItem *uiMenuAppendQuitItem(uiMenu *m);
|
||||
uiprivExtern uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m);
|
||||
uiprivExtern uiMenuItem *uiMenuAppendAboutItem(uiMenu *m);
|
||||
uiprivExtern void uiMenuAppendSeparator(uiMenu *m);
|
||||
uiprivExtern uiMenu *uiNewMenu(const char *name);
|
|
@ -0,0 +1,148 @@
|
|||
// 6 april 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
uiInitOptions uiprivOptions;
|
||||
|
||||
static GHashTable *timers;
|
||||
|
||||
const char *uiInit(uiInitOptions *o)
|
||||
{
|
||||
GError *err = NULL;
|
||||
const char *msg;
|
||||
|
||||
uiprivOptions = *o;
|
||||
if (gtk_init_with_args(NULL, NULL, NULL, NULL, NULL, &err) == FALSE) {
|
||||
msg = g_strdup(err->message);
|
||||
g_error_free(err);
|
||||
return msg;
|
||||
}
|
||||
uiprivInitAlloc();
|
||||
uiprivLoadFutures();
|
||||
timers = g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct timer; // TODO get rid of forward declaration
|
||||
|
||||
static void uninitTimer(gpointer key, gpointer value, gpointer data)
|
||||
{
|
||||
uiprivFree((struct timer *) key);
|
||||
}
|
||||
|
||||
void uiUninit(void)
|
||||
{
|
||||
g_hash_table_foreach(timers, uninitTimer, NULL);
|
||||
g_hash_table_destroy(timers);
|
||||
uiprivUninitMenus();
|
||||
uiprivUninitAlloc();
|
||||
}
|
||||
|
||||
void uiFreeInitError(const char *err)
|
||||
{
|
||||
g_free((gpointer) err);
|
||||
}
|
||||
|
||||
static gboolean (*iteration)(gboolean) = NULL;
|
||||
|
||||
void uiMain(void)
|
||||
{
|
||||
iteration = gtk_main_iteration_do;
|
||||
gtk_main();
|
||||
}
|
||||
|
||||
static gboolean stepsQuit = FALSE;
|
||||
|
||||
// the only difference is we ignore the return value from gtk_main_iteration_do(), since it will always be TRUE if gtk_main() was never called
|
||||
// gtk_main_iteration_do() will still run the main loop regardless
|
||||
static gboolean stepsIteration(gboolean block)
|
||||
{
|
||||
gtk_main_iteration_do(block);
|
||||
return stepsQuit;
|
||||
}
|
||||
|
||||
void uiMainSteps(void)
|
||||
{
|
||||
iteration = stepsIteration;
|
||||
}
|
||||
|
||||
int uiMainStep(int wait)
|
||||
{
|
||||
gboolean block;
|
||||
|
||||
block = FALSE;
|
||||
if (wait)
|
||||
block = TRUE;
|
||||
return (*iteration)(block) == FALSE;
|
||||
}
|
||||
|
||||
// gtk_main_quit() may run immediately, or it may wait for other pending events; "it depends" (thanks mclasen in irc.gimp.net/#gtk+)
|
||||
// PostQuitMessage() on Windows always waits, so we must do so too
|
||||
// we'll do it by using an idle callback
|
||||
static gboolean quit(gpointer data)
|
||||
{
|
||||
if (iteration == stepsIteration)
|
||||
stepsQuit = TRUE;
|
||||
// TODO run a gtk_main() here just to do the cleanup steps of syncing the clipboard and other stuff gtk_main() does before it returns
|
||||
else
|
||||
gtk_main_quit();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void uiQuit(void)
|
||||
{
|
||||
gdk_threads_add_idle(quit, NULL);
|
||||
}
|
||||
|
||||
struct queued {
|
||||
void (*f)(void *);
|
||||
void *data;
|
||||
};
|
||||
|
||||
static gboolean doqueued(gpointer data)
|
||||
{
|
||||
struct queued *q = (struct queued *) data;
|
||||
|
||||
(*(q->f))(q->data);
|
||||
g_free(q);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void uiQueueMain(void (*f)(void *data), void *data)
|
||||
{
|
||||
struct queued *q;
|
||||
|
||||
// we have to use g_new0()/g_free() because uiprivAlloc() is only safe to call on the main thread
|
||||
// for some reason it didn't affect me, but it did affect krakjoe
|
||||
q = g_new0(struct queued, 1);
|
||||
q->f = f;
|
||||
q->data = data;
|
||||
gdk_threads_add_idle(doqueued, q);
|
||||
}
|
||||
|
||||
struct timer {
|
||||
int (*f)(void *);
|
||||
void *data;
|
||||
};
|
||||
|
||||
static gboolean doTimer(gpointer data)
|
||||
{
|
||||
struct timer *t = (struct timer *) data;
|
||||
|
||||
if (!(*(t->f))(t->data)) {
|
||||
g_hash_table_remove(timers, t);
|
||||
uiprivFree(t);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void uiTimer(int milliseconds, int (*f)(void *data), void *data)
|
||||
{
|
||||
struct timer *t;
|
||||
|
||||
t = uiprivNew(struct timer);
|
||||
t->f = f;
|
||||
t->data = data;
|
||||
g_timeout_add(milliseconds, doTimer, t);
|
||||
g_hash_table_add(timers, t);
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
// 6 april 2015
|
||||
#import "uipriv_darwin.h"
|
||||
#import "attrstr.h"
|
||||
|
||||
static BOOL canQuit = NO;
|
||||
static NSAutoreleasePool *globalPool;
|
||||
static uiprivApplicationClass *app;
|
||||
static uiprivAppDelegate *delegate;
|
||||
|
||||
static BOOL (^isRunning)(void);
|
||||
static BOOL stepsIsRunning;
|
||||
|
||||
@implementation uiprivApplicationClass
|
||||
|
||||
- (void)sendEvent:(NSEvent *)e
|
||||
{
|
||||
if (uiprivSendAreaEvents(e) != 0)
|
||||
return;
|
||||
[super sendEvent:e];
|
||||
}
|
||||
|
||||
// NSColorPanel always sends changeColor: to the first responder regardless of whether there's a target set on it
|
||||
// we can override it here (see colorbutton.m)
|
||||
// thanks to mikeash in irc.freenode.net/#macdev for informing me this is how the first responder chain is initiated
|
||||
// it turns out NSFontManager also sends changeFont: through this; let's inhibit that here too (see fontbutton.m)
|
||||
- (BOOL)sendAction:(SEL)sel to:(id)to from:(id)from
|
||||
{
|
||||
if (uiprivColorButtonInhibitSendAction(sel, from, to))
|
||||
return NO;
|
||||
if (uiprivFontButtonInhibitSendAction(sel, from, to))
|
||||
return NO;
|
||||
return [super sendAction:sel to:to from:from];
|
||||
}
|
||||
|
||||
// likewise, NSFontManager also sends NSFontPanelValidation messages to the first responder, however it does NOT use sendAction:from:to:!
|
||||
// instead, it uses this one (thanks swillits in irc.freenode.net/#macdev)
|
||||
// we also need to override it (see fontbutton.m)
|
||||
- (id)targetForAction:(SEL)sel to:(id)to from:(id)from
|
||||
{
|
||||
id override;
|
||||
|
||||
if (uiprivFontButtonOverrideTargetForAction(sel, from, to, &override))
|
||||
return override;
|
||||
return [super targetForAction:sel to:to from:from];
|
||||
}
|
||||
|
||||
// hey look! we're overriding terminate:!
|
||||
// we're going to make sure we can go back to main() whether Cocoa likes it or not!
|
||||
// and just how are we going to do that, hm?
|
||||
// (note: this is called after applicationShouldTerminate:)
|
||||
- (void)terminate:(id)sender
|
||||
{
|
||||
// yes that's right folks: DO ABSOLUTELY NOTHING.
|
||||
// the magic is [NSApp run] will just... stop.
|
||||
|
||||
// well let's not do nothing; let's actually quit our graceful way
|
||||
NSEvent *e;
|
||||
|
||||
if (!canQuit)
|
||||
uiprivImplBug("call to [NSApp terminate:] when not ready to terminate; definitely contact andlabs");
|
||||
|
||||
[uiprivNSApp() stop:uiprivNSApp()];
|
||||
// stop: won't register until another event has passed; let's synthesize one
|
||||
e = [NSEvent otherEventWithType:NSApplicationDefined
|
||||
location:NSZeroPoint
|
||||
modifierFlags:0
|
||||
timestamp:[[NSProcessInfo processInfo] systemUptime]
|
||||
windowNumber:0
|
||||
context:[NSGraphicsContext currentContext]
|
||||
subtype:0
|
||||
data1:0
|
||||
data2:0];
|
||||
[uiprivNSApp() postEvent:e atStart:NO]; // let pending events take priority (this is what PostQuitMessage() on Windows does so we have to do it here too for parity; thanks to mikeash in irc.freenode.net/#macdev for confirming that this parameter should indeed be NO)
|
||||
|
||||
// and in case uiMainSteps() was called
|
||||
stepsIsRunning = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation uiprivAppDelegate
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// Apple docs: "Don't Use Accessor Methods in Initializer Methods and dealloc"
|
||||
[_menuManager release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
|
||||
{
|
||||
// for debugging
|
||||
NSLog(@"in applicationShouldTerminate:");
|
||||
if (uiprivShouldQuit()) {
|
||||
canQuit = YES;
|
||||
// this will call terminate:, which is the same as uiQuit()
|
||||
return NSTerminateNow;
|
||||
}
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiInitOptions uiprivOptions;
|
||||
|
||||
const char *uiInit(uiInitOptions *o)
|
||||
{
|
||||
@autoreleasepool {
|
||||
uiprivOptions = *o;
|
||||
app = [[uiprivApplicationClass sharedApplication] retain];
|
||||
// don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy!
|
||||
// see https://github.com/andlabs/ui/issues/6
|
||||
[uiprivNSApp() setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
delegate = [uiprivAppDelegate new];
|
||||
[uiprivNSApp() setDelegate:delegate];
|
||||
|
||||
uiprivInitAlloc();
|
||||
uiprivLoadFutures();
|
||||
uiprivLoadUndocumented();
|
||||
|
||||
// always do this so we always have an application menu
|
||||
uiprivAppDelegate().menuManager = [[uiprivMenuManager new] autorelease];
|
||||
[uiprivNSApp() setMainMenu:[uiprivAppDelegate().menuManager makeMenubar]];
|
||||
|
||||
uiprivSetupFontPanel();
|
||||
|
||||
uiprivInitUnderlineColors();
|
||||
}
|
||||
|
||||
globalPool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void uiUninit(void)
|
||||
{
|
||||
if (!globalPool)
|
||||
uiprivUserBug("You must call uiInit() first!");
|
||||
[globalPool release];
|
||||
|
||||
@autoreleasepool {
|
||||
uiprivUninitUnderlineColors();
|
||||
[delegate release];
|
||||
[uiprivNSApp() setDelegate:nil];
|
||||
[app release];
|
||||
uiprivUninitAlloc();
|
||||
}
|
||||
}
|
||||
|
||||
void uiFreeInitError(const char *err)
|
||||
{
|
||||
}
|
||||
|
||||
void uiMain(void)
|
||||
{
|
||||
isRunning = ^{
|
||||
return [uiprivNSApp() isRunning];
|
||||
};
|
||||
[uiprivNSApp() run];
|
||||
}
|
||||
|
||||
void uiMainSteps(void)
|
||||
{
|
||||
// SDL does this and it seems to be necessary for the menubar to work (see #182)
|
||||
[uiprivNSApp() finishLaunching];
|
||||
isRunning = ^{
|
||||
return stepsIsRunning;
|
||||
};
|
||||
stepsIsRunning = YES;
|
||||
}
|
||||
|
||||
int uiMainStep(int wait)
|
||||
{
|
||||
uiprivNextEventArgs nea;
|
||||
|
||||
nea.mask = NSAnyEventMask;
|
||||
|
||||
// ProPuke did this in his original PR requesting this
|
||||
// I'm not sure if this will work, but I assume it will...
|
||||
nea.duration = [NSDate distantPast];
|
||||
if (wait) // but this is normal so it will work
|
||||
nea.duration = [NSDate distantFuture];
|
||||
|
||||
nea.mode = NSDefaultRunLoopMode;
|
||||
nea.dequeue = YES;
|
||||
|
||||
return uiprivMainStep(&nea, ^(NSEvent *e) {
|
||||
return NO;
|
||||
});
|
||||
}
|
||||
|
||||
// see also:
|
||||
// - http://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html
|
||||
// - https://github.com/gnustep/gui/blob/master/Source/NSApplication.m
|
||||
int uiprivMainStep(uiprivNextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *e))
|
||||
{
|
||||
NSDate *expire;
|
||||
NSEvent *e;
|
||||
NSEventType type;
|
||||
|
||||
@autoreleasepool {
|
||||
if (!isRunning())
|
||||
return 0;
|
||||
|
||||
e = [uiprivNSApp() nextEventMatchingMask:nea->mask
|
||||
untilDate:nea->duration
|
||||
inMode:nea->mode
|
||||
dequeue:nea->dequeue];
|
||||
if (e == nil)
|
||||
return 1;
|
||||
|
||||
type = [e type];
|
||||
if (!interceptEvent(e))
|
||||
[uiprivNSApp() sendEvent:e];
|
||||
[uiprivNSApp() updateWindows];
|
||||
|
||||
// GNUstep does this
|
||||
// it also updates the Services menu but there doesn't seem to be a public API for that so
|
||||
if (type != NSPeriodic && type != NSMouseMoved)
|
||||
[[uiprivNSApp() mainMenu] update];
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void uiQuit(void)
|
||||
{
|
||||
canQuit = YES;
|
||||
[uiprivNSApp() terminate:uiprivNSApp()];
|
||||
}
|
||||
|
||||
// thanks to mikeash in irc.freenode.net/#macdev for suggesting the use of Grand Central Dispatch for this
|
||||
// LONGTERM will dispatch_get_main_queue() break after _CFRunLoopSetCurrent()?
|
||||
void uiQueueMain(void (*f)(void *data), void *data)
|
||||
{
|
||||
// dispatch_get_main_queue() is a serial queue so it will not execute multiple uiQueueMain() functions concurrently
|
||||
// the signature of f matches dispatch_function_t
|
||||
dispatch_async_f(dispatch_get_main_queue(), data, f);
|
||||
}
|
||||
|
||||
@interface uiprivTimerDelegate : NSObject {
|
||||
int (*f)(void *data);
|
||||
void *data;
|
||||
}
|
||||
- (id)initWithCallback:(int (*)(void *))callback data:(void *)callbackData;
|
||||
- (void)doTimer:(NSTimer *)timer;
|
||||
@end
|
||||
|
||||
@implementation uiprivTimerDelegate
|
||||
|
||||
- (id)initWithCallback:(int (*)(void *))callback data:(void *)callbackData
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self->f = callback;
|
||||
self->data = callbackData;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)doTimer:(NSTimer *)timer
|
||||
{
|
||||
if (!(*(self->f))(self->data))
|
||||
[timer invalidate];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void uiTimer(int milliseconds, int (*f)(void *data), void *data)
|
||||
{
|
||||
uiprivTimerDelegate *delegate;
|
||||
|
||||
delegate = [[uiprivTimerDelegate alloc] initWithCallback:f data:data];
|
||||
[NSTimer scheduledTimerWithTimeInterval:(milliseconds / 1000.0)
|
||||
target:delegate
|
||||
selector:@selector(doTimer:)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
[delegate release];
|
||||
}
|
||||
|
||||
// TODO figure out the best way to clean the above up in uiUninit(), if it's even necessary
|
||||
// TODO that means figure out if timers can still fire without the main loop
|
|
@ -0,0 +1,9 @@
|
|||
// 16 may 2019
|
||||
#include "uipriv.h"
|
||||
|
||||
#define sharedbitsPrefix uipriv
|
||||
#include "../sharedbits/alloc_impl.h"
|
||||
#include "../sharedbits/array_impl.h"
|
||||
#include "../sharedbits/strsafe_impl.h"
|
||||
#include "../sharedbits/strdup_impl.h"
|
||||
#undef sharedbitsPrefix
|
101
common/control.c
101
common/control.c
|
@ -1,101 +0,0 @@
|
|||
// 26 may 2015
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiControlDestroy(uiControl *c)
|
||||
{
|
||||
(*(c->Destroy))(c);
|
||||
}
|
||||
|
||||
uintptr_t uiControlHandle(uiControl *c)
|
||||
{
|
||||
return (*(c->Handle))(c);
|
||||
}
|
||||
|
||||
uiControl *uiControlParent(uiControl *c)
|
||||
{
|
||||
return (*(c->Parent))(c);
|
||||
}
|
||||
|
||||
void uiControlSetParent(uiControl *c, uiControl *parent)
|
||||
{
|
||||
(*(c->SetParent))(c, parent);
|
||||
}
|
||||
|
||||
int uiControlToplevel(uiControl *c)
|
||||
{
|
||||
return (*(c->Toplevel))(c);
|
||||
}
|
||||
|
||||
int uiControlVisible(uiControl *c)
|
||||
{
|
||||
return (*(c->Visible))(c);
|
||||
}
|
||||
|
||||
void uiControlShow(uiControl *c)
|
||||
{
|
||||
(*(c->Show))(c);
|
||||
}
|
||||
|
||||
void uiControlHide(uiControl *c)
|
||||
{
|
||||
(*(c->Hide))(c);
|
||||
}
|
||||
|
||||
int uiControlEnabled(uiControl *c)
|
||||
{
|
||||
return (*(c->Enabled))(c);
|
||||
}
|
||||
|
||||
void uiControlEnable(uiControl *c)
|
||||
{
|
||||
(*(c->Enable))(c);
|
||||
}
|
||||
|
||||
void uiControlDisable(uiControl *c)
|
||||
{
|
||||
(*(c->Disable))(c);
|
||||
}
|
||||
|
||||
#define uiprivControlSignature 0x7569436F
|
||||
|
||||
uiControl *uiAllocControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr)
|
||||
{
|
||||
uiControl *c;
|
||||
|
||||
c = (uiControl *) uiprivAlloc(size, typenamestr);
|
||||
c->Signature = uiprivControlSignature;
|
||||
c->OSSignature = OSsig;
|
||||
c->TypeSignature = typesig;
|
||||
return c;
|
||||
}
|
||||
|
||||
void uiFreeControl(uiControl *c)
|
||||
{
|
||||
if (uiControlParent(c) != NULL)
|
||||
uiprivUserBug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c);
|
||||
uiprivFree(c);
|
||||
}
|
||||
|
||||
void uiControlVerifySetParent(uiControl *c, uiControl *parent)
|
||||
{
|
||||
uiControl *curParent;
|
||||
|
||||
if (uiControlToplevel(c))
|
||||
uiprivUserBug("You cannot give a toplevel uiControl a parent. (control: %p)", c);
|
||||
curParent = uiControlParent(c);
|
||||
if (parent != NULL && curParent != NULL)
|
||||
uiprivUserBug("You cannot give a uiControl a parent while it already has one. (control: %p; current parent: %p; new parent: %p)", c, curParent, parent);
|
||||
if (parent == NULL && curParent == NULL)
|
||||
uiprivImplBug("attempt to double unparent uiControl %p", c);
|
||||
}
|
||||
|
||||
int uiControlEnabledToUser(uiControl *c)
|
||||
{
|
||||
while (c != NULL) {
|
||||
if (!uiControlEnabled(c))
|
||||
return 0;
|
||||
c = uiControlParent(c);
|
||||
}
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
// 8 june 2019
|
||||
#include "uipriv.h"
|
||||
|
||||
struct controlType {
|
||||
uint32_t id;
|
||||
char *name;
|
||||
uiControlVtable vtable;
|
||||
uiControlOSVtable *osVtable;
|
||||
size_t implDataSize;
|
||||
};
|
||||
|
||||
static int controlTypeCmp(const void *a, const void *b)
|
||||
{
|
||||
const struct controlType *ca = (const struct controlType *) a;
|
||||
const struct controlType *cb = (const struct controlType *) b;
|
||||
|
||||
if (ca->id < cb->id)
|
||||
return -1;
|
||||
if (ca->id > cb->id)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct uiControl {
|
||||
uint32_t controlID;
|
||||
uint32_t typeID;
|
||||
struct controlType *type;
|
||||
void *implData;
|
||||
uiControl *parent;
|
||||
};
|
||||
|
||||
static uiprivArray controlTypes = uiprivArrayStaticInit(struct controlType, 32, "uiControl type information");
|
||||
|
||||
#define controlTypeID UINT32_C(0x1F2E3C4D)
|
||||
|
||||
uint32_t uiControlType(void)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return 0;
|
||||
return controlTypeID;
|
||||
}
|
||||
|
||||
static uint32_t nextControlID = UINT32_C(0x80000000);
|
||||
|
||||
uint32_t uiRegisterControlType(const char *name, const uiControlVtable *vtable, const uiControlOSVtable *osVtable, size_t implDataSize)
|
||||
{
|
||||
struct controlType *ct;
|
||||
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return 0;
|
||||
if (name == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("name", uiprivFunc);
|
||||
return 0;
|
||||
}
|
||||
if (vtable == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiControlVtable", uiprivFunc);
|
||||
return 0;
|
||||
}
|
||||
if (vtable->Size != sizeof (uiControlVtable)) {
|
||||
uiprivProgrammerErrorWrongStructSize(vtable->Size, "uiControlVtable", uiprivFunc);
|
||||
return 0;
|
||||
}
|
||||
#define checkMethod(method) \
|
||||
if (vtable->method == NULL) { \
|
||||
uiprivProgrammerErrorRequiredControlMethodMissing(name, "uiControlVtable", #method, uiprivFunc); \
|
||||
return 0; \
|
||||
}
|
||||
checkMethod(Init)
|
||||
checkMethod(Free)
|
||||
checkMethod(ParentChanging)
|
||||
checkMethod(ParentChanged)
|
||||
#undef checkMethod
|
||||
|
||||
if (osVtable == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiControlOSVtable", uiprivFunc);
|
||||
return 0;
|
||||
}
|
||||
if (!uiprivOSVtableValid(name, osVtable, uiprivFunc))
|
||||
return 0;
|
||||
|
||||
ct = (struct controlType *) uiprivArrayAppend(&controlTypes, 1);
|
||||
ct->id = nextControlID;
|
||||
nextControlID++;
|
||||
ct->name = uiprivStrdup(name);
|
||||
ct->vtable = *vtable;
|
||||
ct->osVtable = uiprivCloneOSVtable(osVtable);
|
||||
ct->implDataSize = implDataSize;
|
||||
return ct->id;
|
||||
}
|
||||
|
||||
void *uiCheckControlType(void *c, uint32_t type)
|
||||
{
|
||||
uiControl *cc = (uiControl *) c;
|
||||
struct controlType *got, *want;
|
||||
struct controlType key;
|
||||
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return NULL;
|
||||
if (c == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiControl", uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
if (cc->controlID != controlTypeID) {
|
||||
uiprivProgrammerErrorNotAControl(uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// now grab the type information for c itself
|
||||
// do this even if we were asked if this is a uiControl; we want to make absolutely sure this is a *real* uiControl
|
||||
memset(&key, 0, sizeof (struct controlType));
|
||||
key.id = cc->typeID;
|
||||
got = (struct controlType *) uiprivArrayBsearch(&controlTypes, &key, controlTypeCmp);
|
||||
if (got == NULL) {
|
||||
uiprivProgrammerErrorUnknownControlTypeUsed(cc->typeID, uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (type == controlTypeID)
|
||||
// this is a real uiControl; no need to check further
|
||||
return c;
|
||||
|
||||
// type isn't uiControlType(); make sure it is valid too
|
||||
memset(&key, 0, sizeof (struct controlType));
|
||||
key.id = type;
|
||||
want = (struct controlType *) uiprivArrayBsearch(&controlTypes, &key, controlTypeCmp);
|
||||
if (want == NULL) {
|
||||
uiprivProgrammerErrorUnknownControlTypeRequested(type, uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cc->typeID != type) {
|
||||
uiprivProgrammerErrorWrongControlType(got->name, want->name, uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
#define callVtable(method, ...) ((*(method))(__VA_ARGS__))
|
||||
|
||||
uiControl *uiNewControl(uint32_t type, void *initData)
|
||||
{
|
||||
uiControl *c;
|
||||
struct controlType *ct;
|
||||
struct controlType key;
|
||||
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return NULL;
|
||||
if (type == controlTypeID) {
|
||||
uiprivProgrammerErrorCannotCreateBaseControl(uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
memset(&key, 0, sizeof (struct controlType));
|
||||
key.id = type;
|
||||
ct = (struct controlType *) uiprivArrayBsearch(&controlTypes, &key, controlTypeCmp);
|
||||
if (ct == NULL) {
|
||||
uiprivProgrammerErrorUnknownControlTypeRequested(type, uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
c = uiprivNew(uiControl);
|
||||
c->controlID = controlTypeID;
|
||||
c->typeID = type;
|
||||
c->type = ct;
|
||||
if (ct->implDataSize != 0)
|
||||
c->implData = uiprivAlloc(ct->implDataSize, "uiControl implementation data");
|
||||
if (!callVtable(c->type->vtable.Init, c, c->implData, initData)) {
|
||||
uiprivProgrammerErrorInvalidControlInitData(ct->name, uiprivFunc);
|
||||
uiprivFree(c->implData);
|
||||
uiprivFree(c);
|
||||
return NULL;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void uiControlFree(uiControl *c)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return;
|
||||
if (c == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiControl", uiprivFunc);
|
||||
return;
|
||||
}
|
||||
if (c->parent != NULL) {
|
||||
uiprivProgrammerErrorControlHasParent(uiprivFunc);
|
||||
return;
|
||||
}
|
||||
|
||||
callVtable(c->type->vtable.Free, c, c->implData);
|
||||
|
||||
uiprivFree(c->implData);
|
||||
uiprivFree(c);
|
||||
}
|
||||
|
||||
static bool parentHasCycle(uiControl *c, uiControl *parent)
|
||||
{
|
||||
// TODO remember if this is the correct way to use a local uiprivArray
|
||||
uiprivArray parents;
|
||||
size_t i;
|
||||
|
||||
if (parent == NULL)
|
||||
return false;
|
||||
if (parent == c) // easy case
|
||||
return true;
|
||||
|
||||
uiprivArrayInit(parents, uiControl *, 16, "uiControl parent list");
|
||||
// add these now, as they are counted as part of any cycles
|
||||
*((uiControl **) uiprivArrayAppend(&parents, 1)) = c;
|
||||
*((uiControl **) uiprivArrayAppend(&parents, 1)) = parent;
|
||||
for (c = parent->parent; c != NULL; c = c->parent) {
|
||||
// TODO this doesn't need to be sequential, but I don't imagine this list will ever be long enough to make it matter... yet
|
||||
for (i = 0; i < parents.len; i++)
|
||||
if (c == *uiprivArrayAt(parents, uiControl *, i)) {
|
||||
uiprivArrayFree(parents);
|
||||
return true;
|
||||
}
|
||||
// new parent; mark it as visited
|
||||
*((uiControl **) uiprivArrayAppend(&parents, 1)) = c;
|
||||
}
|
||||
|
||||
uiprivArrayFree(parents);
|
||||
return false;
|
||||
}
|
||||
|
||||
void uiControlSetParent(uiControl *c, uiControl *parent)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return;
|
||||
if (c == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiControl", uiprivFunc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (c->parent == NULL && parent == NULL) {
|
||||
uiprivProgrammerErrorReparenting("no", "no", uiprivFunc);
|
||||
return;
|
||||
}
|
||||
if (c->parent != NULL && parent != NULL) {
|
||||
uiprivProgrammerErrorReparenting("a", "another", uiprivFunc);
|
||||
return;
|
||||
}
|
||||
if (parentHasCycle(c, parent)) {
|
||||
uiprivProgrammerErrorControlParentCycle(uiprivFunc);
|
||||
return;
|
||||
}
|
||||
|
||||
callVtable(c->type->vtable.ParentChanging, c, c->implData, c->parent);
|
||||
c->parent = parent;
|
||||
callVtable(c->type->vtable.ParentChanged, c, c->implData, c->parent);
|
||||
}
|
||||
|
||||
void *uiControlImplData(uiControl *c)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return NULL;
|
||||
if (c == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiControl", uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
return c->implData;
|
||||
}
|
||||
|
||||
uiControlOSVtable *uiprivControlOSVtable(uiControl *c)
|
||||
{
|
||||
return c->type->osVtable;
|
||||
}
|
||||
|
||||
uiControl *uiprivControlParent(uiControl *c)
|
||||
{
|
||||
return c->parent;
|
||||
}
|
||||
|
||||
static uiControl testHookControlWithInvalidControlMarker = {
|
||||
// use something other than 0 here to make it look like accidental real data
|
||||
.controlID = UINT32_C(0x5A5A5A5A),
|
||||
};
|
||||
|
||||
uiControl *uiprivTestHookControlWithInvalidControlMarker(void)
|
||||
{
|
||||
return &testHookControlWithInvalidControlMarker;
|
||||
}
|
||||
|
||||
static uiControl testHookControlWithInvalidType = {
|
||||
.controlID = controlTypeID,
|
||||
.typeID = 0,
|
||||
};
|
||||
|
||||
uiControl *uiprivTestHookControlWithInvalidType(void)
|
||||
{
|
||||
return &testHookControlWithInvalidType;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// 24 april 2016
|
||||
|
||||
// LONGTERM if I don't decide to remove these outright, should they be renamed uiprivTypeNameSignature? these aren't real symbols, so...
|
||||
|
||||
#define uiAreaSignature 0x41726561
|
||||
#define uiBoxSignature 0x426F784C
|
||||
#define uiButtonSignature 0x42746F6E
|
||||
#define uiCheckboxSignature 0x43686B62
|
||||
#define uiColorButtonSignature 0x436F6C42
|
||||
#define uiComboboxSignature 0x436F6D62
|
||||
#define uiDateTimePickerSignature 0x44545069
|
||||
#define uiEditableComboboxSignature 0x45644362
|
||||
#define uiEntrySignature 0x456E7472
|
||||
#define uiFontButtonSignature 0x466F6E42
|
||||
#define uiFormSignature 0x466F726D
|
||||
#define uiGridSignature 0x47726964
|
||||
#define uiGroupSignature 0x47727062
|
||||
#define uiLabelSignature 0x4C61626C
|
||||
#define uiMultilineEntrySignature 0x4D6C6E45
|
||||
#define uiProgressBarSignature 0x50426172
|
||||
#define uiRadioButtonsSignature 0x5264696F
|
||||
#define uiSeparatorSignature 0x53657061
|
||||
#define uiSliderSignature 0x536C6964
|
||||
#define uiSpinboxSignature 0x5370696E
|
||||
#define uiTabSignature 0x54616273
|
||||
#define uiTableSignature 0x5461626C
|
||||
#define uiWindowSignature 0x57696E64
|
|
@ -1,21 +0,0 @@
|
|||
// 13 may 2016
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiprivDoImplBug(const char *file, const char *line, const char *func, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
uiprivRealBug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void uiprivDoUserBug(const char *file, const char *line, const char *func, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
uiprivRealBug(file, line, func, "You have a bug: ", format, ap);
|
||||
va_end(ap);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// 12 may 2019
|
||||
#include "uipriv.h"
|
||||
|
||||
#define internalErrorPrefix "libui internal error"
|
||||
// TODO add debugging advice?
|
||||
#define internalErrorSuffix "This likely means there is a bug in libui itself. Contact the libui authors."
|
||||
|
||||
void uiprivInternalError(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[256];
|
||||
int n;
|
||||
|
||||
va_start(ap, fmt);
|
||||
n = uiprivVsnprintf(buf, 256, fmt, ap);
|
||||
va_end(ap);
|
||||
if (n < 0)
|
||||
uiprivReportError(internalErrorPrefix, "internal error string has encoding error", internalErrorSuffix, true);
|
||||
if (n >= 256)
|
||||
uiprivReportError(internalErrorPrefix, "internal error string too long", internalErrorSuffix, true);
|
||||
uiprivReportError(internalErrorPrefix, buf, internalErrorSuffix, true);
|
||||
}
|
||||
|
||||
#define programmerErrorPrefix "libui programmer error"
|
||||
// TODO add debugging advice?
|
||||
#define programmerErrorSuffix "This likely means you are using libui incorrectly. Check your source code and try again. If you have received this warning in error, contact the libui authors."
|
||||
|
||||
static uiprivTestHookReportProgrammerErrorFunc reportProgrammerErrorTestHook = NULL;
|
||||
static void *reportProgrammerErrorTestHookData = NULL;
|
||||
|
||||
void uiprivTestHookReportProgrammerError(uiprivTestHookReportProgrammerErrorFunc f, void *data)
|
||||
{
|
||||
reportProgrammerErrorTestHook = f;
|
||||
reportProgrammerErrorTestHookData = data;
|
||||
}
|
||||
|
||||
void uiprivProgrammerError(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int n;
|
||||
char buf[256];
|
||||
|
||||
va_start(ap, fmt);
|
||||
n = uiprivVsnprintf(buf, 256, fmt, ap);
|
||||
if (n < 0)
|
||||
uiprivInternalError("programmer error has encoding error");
|
||||
if (n >= 256)
|
||||
uiprivInternalError("programmer error string too long (%d)", n);
|
||||
va_end(ap);
|
||||
if (reportProgrammerErrorTestHook != NULL) {
|
||||
(*reportProgrammerErrorTestHook)(buf, reportProgrammerErrorTestHookData);
|
||||
return;
|
||||
}
|
||||
uiprivReportError(programmerErrorPrefix, buf, programmerErrorSuffix, false);
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
// 19 april 2019
|
||||
#include "uipriv.h"
|
||||
|
||||
enum {
|
||||
stateUninitialized,
|
||||
stateBeforeMain,
|
||||
stateInMain,
|
||||
stateQuitting,
|
||||
stateAfterMain,
|
||||
stateError,
|
||||
};
|
||||
|
||||
static int state = stateUninitialized;
|
||||
|
||||
#define initialized() (state != stateUninitialized && state != stateError)
|
||||
|
||||
bool testHookInitShouldFail = false;
|
||||
|
||||
void uiprivTestHookSetInitShouldFailArtificially(bool shouldFail)
|
||||
{
|
||||
testHookInitShouldFail = shouldFail;
|
||||
}
|
||||
|
||||
bool uiInit(void *options, uiInitError *err)
|
||||
{
|
||||
if (state != stateUninitialized) {
|
||||
uiprivProgrammerErrorMultipleCalls(uiprivFunc);
|
||||
state = stateError;
|
||||
return false;
|
||||
}
|
||||
if (options != NULL) {
|
||||
uiprivProgrammerErrorBadInitOptions(uiprivFunc);
|
||||
state = stateError;
|
||||
return false;
|
||||
}
|
||||
if (err == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiInitError", uiprivFunc);
|
||||
state = stateError;
|
||||
return false;
|
||||
}
|
||||
if (err->Size != sizeof (uiInitError)) {
|
||||
uiprivProgrammerErrorWrongStructSize(err->Size, "uiInitError", uiprivFunc);
|
||||
state = stateError;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (testHookInitShouldFail) {
|
||||
state = stateError;
|
||||
return uiprivInitReturnErrorf(err, "general failure");
|
||||
}
|
||||
if (!uiprivSysInit(options, err)) {
|
||||
state = stateError;
|
||||
return false;
|
||||
}
|
||||
state = stateBeforeMain;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uiprivInitReturnErrorf(uiInitError *err, const char *fmt, ...)
|
||||
{
|
||||
int n;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
n = uiprivVsnprintf(err->Message, 256, fmt, ap);
|
||||
va_end(ap);
|
||||
if (n < 0) {
|
||||
uiprivInternalError("encoding error returning initialization error; this means something is very very wrong with libui itself");
|
||||
abort(); // TODO handle this scenario more gracefully
|
||||
}
|
||||
if (n >= 256) {
|
||||
// the formatted message is too long; truncate it
|
||||
err->Message[252] = '.';
|
||||
err->Message[253] = '.';
|
||||
err->Message[254] = '.';
|
||||
err->Message[255] = '\0';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void uiMain(void)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return;
|
||||
if (state != stateBeforeMain) {
|
||||
uiprivProgrammerErrorMultipleCalls(uiprivFunc);
|
||||
return;
|
||||
}
|
||||
state = stateInMain;
|
||||
uiprivSysMain();
|
||||
state = stateAfterMain;
|
||||
}
|
||||
|
||||
void uiQuit(void)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return;
|
||||
if (state == stateQuitting || state == stateAfterMain) {
|
||||
uiprivProgrammerErrorMultipleCalls(uiprivFunc);
|
||||
return;
|
||||
}
|
||||
if (state != stateInMain) {
|
||||
// the above handle the other states, so stateBeforeMain is what's left
|
||||
uiprivProgrammerErrorQuitBeforeMain(uiprivFunc);
|
||||
return;
|
||||
}
|
||||
state = stateQuitting;
|
||||
uiprivSysQuit();
|
||||
}
|
||||
|
||||
void uiQueueMain(void (*f)(void *data), void *data)
|
||||
{
|
||||
if (!initialized()) {
|
||||
uiprivProgrammerErrorNotInitialized(uiprivFunc);
|
||||
return;
|
||||
}
|
||||
uiprivSysQueueMain(f, data);
|
||||
}
|
||||
|
||||
bool uiprivCheckInitializedAndThreadImpl(const char *func)
|
||||
{
|
||||
// While it does seem risky to not lock this, if this changes during the execution of this function it means only that it was changed from a different thread, and since it can only change from false to true, an error will be reported anyway.
|
||||
if (!initialized()) {
|
||||
uiprivProgrammerErrorNotInitialized(func);
|
||||
return false;
|
||||
}
|
||||
if (!uiprivSysCheckThread()) {
|
||||
uiprivProgrammerErrorWrongThread(func);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -1,17 +1,12 @@
|
|||
# 23 march 2019
|
||||
|
||||
libui_sources += [
|
||||
'common/attribute.c',
|
||||
'common/attrlist.c',
|
||||
'common/attrstr.c',
|
||||
'common/areaevents.c',
|
||||
'common/control.c',
|
||||
'common/debug.c',
|
||||
'common/matrix.c',
|
||||
'common/opentype.c',
|
||||
'common/shouldquit.c',
|
||||
'common/tablemodel.c',
|
||||
'common/tablevalue.c',
|
||||
'common/userbugs.c',
|
||||
'common/utf.c',
|
||||
'common/alloc.c',
|
||||
'common/controls.c',
|
||||
'common/errors.c',
|
||||
'common/main.c',
|
||||
'common/utf8.c',
|
||||
'common/window.c',
|
||||
|
||||
'common/third_party/utf.c',
|
||||
]
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
// 2 june 2019
|
||||
|
||||
// common {
|
||||
|
||||
#define uiprivProgrammerErrorNotInitialized(func) \
|
||||
uiprivProgrammerError("attempt to call %s() before uiInit()", \
|
||||
func)
|
||||
|
||||
#define uiprivProgrammerErrorWrongThread(func) \
|
||||
uiprivProgrammerError("attempt to call %s() on a thread other than the GUI thread", \
|
||||
func)
|
||||
|
||||
#define uiprivProgrammerErrorWrongStructSize(badSize, structName, func) \
|
||||
uiprivProgrammerError("%s(): wrong size %" uiprivSizetPrintf " for %s", \
|
||||
func, badSize, structName)
|
||||
|
||||
#define uiprivProgrammerErrorIndexOutOfRange(badIndex, func) \
|
||||
uiprivProgrammerError("%s(): index %d out of range", \
|
||||
func, badIndex)
|
||||
|
||||
#define uiprivProgrammerErrorNullPointer(paramDesc, func) \
|
||||
uiprivProgrammerError("%s(): invalid null pointer for %s", \
|
||||
func, paramDesc)
|
||||
|
||||
// }
|
||||
|
||||
// main {
|
||||
|
||||
#define uiprivProgrammerErrorMultipleCalls(func) \
|
||||
uiprivProgrammerError("%s(): attempt to call more than once", func)
|
||||
|
||||
#define uiprivProgrammerErrorBadInitOptions(func) \
|
||||
uiprivProgrammerError("%s(): invalid uiInitOptions passed", func)
|
||||
|
||||
#define uiprivProgrammerErrorQuitBeforeMain(func) \
|
||||
uiprivProgrammerError("%s(): attempt to call before uiMain()", func)
|
||||
|
||||
// }
|
||||
|
||||
// controls {
|
||||
|
||||
#define uiprivProgrammerErrorRequiredControlMethodMissing(typeName, tableType, methodName, func) \
|
||||
uiprivProgrammerError("%s(): required %s method %s() missing for uiControl type %s", \
|
||||
func, tableType, methodName, typeName)
|
||||
|
||||
#define uiprivProgrammerErrorNotAControl(func) \
|
||||
uiprivProgrammerError("%s(): object passed in not a uiControl", \
|
||||
func)
|
||||
|
||||
#define uiprivProgrammerErrorUnknownControlTypeUsed(type, func) \
|
||||
uiprivProgrammerError("%s(): unknown uiControl type %" PRIu32 " found in uiControl (this is likely not a real uiControl or some data is corrupt)", \
|
||||
func, type)
|
||||
|
||||
#define uiprivProgrammerErrorUnknownControlTypeRequested(type, func) \
|
||||
uiprivProgrammerError("%s(): unknown uiControl type %" PRIu32 " requested", \
|
||||
func, type)
|
||||
|
||||
#define uiprivProgrammerErrorWrongControlType(got, want, func) \
|
||||
uiprivProgrammerError("%s(): wrong uiControl type passed: got %s, want %s", \
|
||||
func, got, want)
|
||||
|
||||
#define uiprivProgrammerErrorCannotCreateBaseControl(func) \
|
||||
uiprivProgrammerError("%s(): uiControlType() passed in when specific control type needed", \
|
||||
func)
|
||||
|
||||
#define uiprivProgrammerErrorInvalidControlInitData(type, func) \
|
||||
uiprivProgrammerError("%s(): invalid init data for %s", \
|
||||
func, type)
|
||||
|
||||
#define uiprivProgrammerErrorControlHasParent(func) \
|
||||
uiprivProgrammerError("%s(): cannot be called on a control with has a parent", \
|
||||
func)
|
||||
|
||||
#define uiprivProgrammerErrorReparenting(current, next, func) \
|
||||
uiprivProgrammerError("%s(): cannot set a control with %s parent to have %s parent", \
|
||||
func, current, next)
|
||||
|
||||
#define uiprivProgrammerErrorControlParentCycle(func) \
|
||||
uiprivProgrammerError("%s(): cannot create a parent cycle", func)
|
||||
|
||||
// }
|
||||
|
||||
// windows {
|
||||
|
||||
// TODO have any parameters, such as what the window is and what the parent is? to add func we'll need to carry that out from uiControlSetParent() (which means exposing that in the API)
|
||||
#define uiprivProgrammerErrorCannotHaveWindowsAsChildren() \
|
||||
uiprivProgrammerError("cannot set a uiWindow as the child of another uiControl")
|
||||
|
||||
// for Windows only
|
||||
#define uiprivProgrammerErrorCannotCallSetControlPosOnWindow() \
|
||||
uiprivProgrammerError("cannot call uiWindowsControlSetControlPos() on a uiWindow")
|
||||
|
||||
// }
|
|
@ -0,0 +1,20 @@
|
|||
// 26 may 2019
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// main.c
|
||||
uiprivExtern void uiprivTestHookSetInitShouldFailArtificially(bool shouldFail);
|
||||
|
||||
// errors.c
|
||||
typedef void (*uiprivTestHookReportProgrammerErrorFunc)(const char *msg, void *data);
|
||||
uiprivExtern void uiprivTestHookReportProgrammerError(uiprivTestHookReportProgrammerErrorFunc f, void *data);
|
||||
|
||||
// controls.c
|
||||
uiprivExtern uiControl *uiprivTestHookControlWithInvalidControlMarker(void);
|
||||
uiprivExtern uiControl *uiprivTestHookControlWithInvalidType(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
126
common/uipriv.h
126
common/uipriv.h
|
@ -1,66 +1,94 @@
|
|||
// 6 april 2015
|
||||
// note: this file should not include ui.h, as the OS-specific ui_*.h files are included between that one and this one in the OS-specific uipriv_*.h* files
|
||||
// 19 april 2019
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "controlsigs.h"
|
||||
#include "utf.h"
|
||||
#include "ui.h"
|
||||
#ifdef uiprivOSHeader
|
||||
#include uiprivOSHeader
|
||||
#endif
|
||||
#include "testhooks.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// OS-specific init.* or main.* files
|
||||
extern uiInitOptions uiprivOptions;
|
||||
|
||||
// OS-specific alloc.* files
|
||||
extern void *uiprivAlloc(size_t, const char *);
|
||||
#define uiprivNew(T) ((T *) uiprivAlloc(sizeof (T), #T))
|
||||
extern void *uiprivRealloc(void *, size_t, const char *);
|
||||
extern void uiprivFree(void *);
|
||||
|
||||
// debug.c and OS-specific debug.* files
|
||||
// TODO get rid of this mess...
|
||||
// ugh, __func__ was only introduced in MSVC 2015...
|
||||
// TODO figure out why this is needed despite what https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2013/b0084kay(v=vs.120) says
|
||||
#ifdef _MSC_VER
|
||||
#define uiprivMacro__func__ __FUNCTION__
|
||||
#define uiprivFunc __FUNCTION__
|
||||
#else
|
||||
#define uiprivMacro__func__ __func__
|
||||
#define uiprivFunc __func__
|
||||
#endif
|
||||
extern void uiprivRealBug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap);
|
||||
#define uiprivMacro_ns2(s) #s
|
||||
#define uiprivMacro_ns(s) uiprivMacro_ns2(s)
|
||||
extern void uiprivDoImplBug(const char *file, const char *line, const char *func, const char *format, ...);
|
||||
#define uiprivImplBug(...) uiprivDoImplBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__)
|
||||
extern void uiprivDoUserBug(const char *file, const char *line, const char *func, const char *format, ...);
|
||||
#define uiprivUserBug(...) uiprivDoUserBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__)
|
||||
|
||||
// shouldquit.c
|
||||
extern int uiprivShouldQuit(void);
|
||||
// TODO {
|
||||
// WHY IS THIS NEEDED?!?!?!?!!?!??!Q https://stackoverflow.com/questions/15610053/correct-printf-format-specifier-for-size-t-zu-or-iu SAYS THAT VS2013 DOES SUPPORT %zu
|
||||
// AND WHY IS MINGW AFFECTED?!?!?!?!
|
||||
// Oh and even better: the -Wno-pedantic-ms-printf stuff doesn't result in a warning about this either...
|
||||
// }
|
||||
#ifdef _WIN32
|
||||
#define uiprivSizetPrintf "Iu"
|
||||
#else
|
||||
#define uiprivSizetPrintf "zu"
|
||||
#endif
|
||||
|
||||
// areaevents.c
|
||||
typedef struct uiprivClickCounter uiprivClickCounter;
|
||||
// you should call Reset() to zero-initialize a new instance
|
||||
// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly
|
||||
struct uiprivClickCounter {
|
||||
int curButton;
|
||||
int rectX0;
|
||||
int rectY0;
|
||||
int rectX1;
|
||||
int rectY1;
|
||||
uintptr_t prevTime;
|
||||
int count;
|
||||
};
|
||||
extern int uiprivClickCounterClick(uiprivClickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist);
|
||||
extern void uiprivClickCounterReset(uiprivClickCounter *);
|
||||
extern int uiprivFromScancode(uintptr_t, uiAreaKeyEvent *);
|
||||
#include "../sharedbits/printfwarn_header.h"
|
||||
#define uiprivPrintfFunc(decl, fmtpos, appos) sharedbitsPrintfFunc(decl, fmtpos, appos)
|
||||
|
||||
// matrix.c
|
||||
extern void uiprivFallbackSkew(uiDrawMatrix *, double, double, double, double);
|
||||
extern void uiprivScaleCenter(double, double, double *, double *);
|
||||
extern void uiprivFallbackTransformSize(uiDrawMatrix *, double *, double *);
|
||||
// main.c
|
||||
extern bool uiprivSysInit(void *options, uiInitError *err);
|
||||
uiprivPrintfFunc(
|
||||
extern bool uiprivInitReturnErrorf(uiInitError *err, const char *fmt, ...),
|
||||
2, 3);
|
||||
extern void uiprivSysMain(void);
|
||||
extern void uiprivSysQuit(void);
|
||||
extern void uiprivSysQueueMain(void (*f)(void *data), void *data);
|
||||
extern bool uiprivCheckInitializedAndThreadImpl(const char *func);
|
||||
#define uiprivCheckInitializedAndThread() uiprivCheckInitializedAndThreadImpl(uiprivFunc)
|
||||
extern bool uiprivSysCheckThread(void);
|
||||
|
||||
// OS-specific text.* files
|
||||
extern int uiprivStricmp(const char *a, const char *b);
|
||||
// alloc.c
|
||||
#define sharedbitsPrefix uipriv
|
||||
// TODO determine if we need the ../ or not, and if not, figure out if we should use it everywhere (including ui.h) or not
|
||||
#include "../sharedbits/alloc_header.h"
|
||||
#define uiprivNew(T) ((T *) uiprivAlloc(sizeof (T), #T))
|
||||
#include "../sharedbits/array_header.h"
|
||||
#define uiprivArrayStaticInit(T, grow, whatstr) { NULL, 0, 0, sizeof (T), grow, whatstr }
|
||||
#define uiprivArrayInit(arr, T, nGrow, what) uiprivArrayInitFull(&(arr), sizeof (T), nGrow, what)
|
||||
#define uiprivArrayFree(arr) uiprivArrayFreeFull(&(arr))
|
||||
#define uiprivArrayAt(arr, T, n) (((T *) (arr.buf)) + (n))
|
||||
#include "../sharedbits/strsafe_header.h"
|
||||
#include "../sharedbits/strdup_header.h"
|
||||
#undef sharedbitsPrefix
|
||||
|
||||
// errors.c
|
||||
uiprivPrintfFunc(
|
||||
extern void uiprivInternalError(const char *fmt, ...),
|
||||
1, 2);
|
||||
uiprivPrintfFunc(
|
||||
extern void uiprivProgrammerError(const char *fmt, ...),
|
||||
1, 2);
|
||||
#include "programmererrors.h"
|
||||
extern void uiprivReportError(const char *prefix, const char *msg, const char *suffix, bool internal);
|
||||
|
||||
// controls.c
|
||||
extern bool uiprivOSVtableValid(const char *name, const uiControlOSVtable *osVtable, const char *func);
|
||||
extern uiControlOSVtable *uiprivCloneOSVtable(const uiControlOSVtable *osVtable);
|
||||
extern uiControlOSVtable *uiprivControlOSVtable(uiControl *c);
|
||||
extern uiControl *uiprivControlParent(uiControl *c);
|
||||
|
||||
// utf8.c
|
||||
extern char *uiprivSanitizeUTF8(const char *str);
|
||||
extern void uiprivFreeUTF8(char *sanitized);
|
||||
|
||||
// window.c
|
||||
extern uint32_t uiprivSysWindowType(void);
|
||||
extern uiWindow *uiprivSysNewWindow(void);
|
||||
extern const char *uiprivSysWindowTitle(uiWindow *w);
|
||||
extern void uiprivSysWindowSetTitle(uiWindow *w, const char *title);
|
||||
extern uiControl *uiprivSysWindowChild(uiWindow *w);
|
||||
extern void uiprivSysWindowSetChild(uiWindow *w, uiControl *child);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
// 17 may 2020
|
||||
#include "uipriv.h"
|
||||
#include "third_party/utf.h"
|
||||
|
||||
// TODO write separate tests for this file?
|
||||
// TODO ideally this functionality should really be part of utf itself, in some form or another (for instance, via utf8SanitizedLen() + the requisite loop)
|
||||
|
||||
#define nGrow 32
|
||||
|
||||
char *uiprivSanitizeUTF8(const char *str)
|
||||
{
|
||||
size_t len;
|
||||
char *out;
|
||||
const char *s;
|
||||
size_t i;
|
||||
uint32_t rune;
|
||||
char encoded[4];
|
||||
size_t n;
|
||||
|
||||
// TODO can we even use strlen() with UTF-8 strings? or is '\0' == 0 == actual memory zero just a source code connection (and thus the last one isn't necessarily true)?
|
||||
len = strlen(str);
|
||||
out = (char *) uiprivAlloc((len + 1) * sizeof (char), "sanitized UTF-8 string");
|
||||
s = str;
|
||||
i = 0;
|
||||
while (*s != '\0') {
|
||||
s = uiprivUTF8DecodeRune(s, 0, &rune);
|
||||
n = uiprivUTF8EncodeRune(rune, encoded);
|
||||
if ((i + n) >= len) {
|
||||
out = (char *) uiprivRealloc(out, (len + 1) * sizeof (char), (len + nGrow + 1) * sizeof (char), "sanitized UTF-8 string");
|
||||
len += nGrow;
|
||||
}
|
||||
memcpy(out + i, encoded, n);
|
||||
i += n;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void uiprivFreeUTF8(char *sanitized)
|
||||
{
|
||||
uiprivFree(sanitized);
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// 25 may 2020
|
||||
#include "uipriv.h"
|
||||
|
||||
uint32_t uiWindowType(void)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return 0;
|
||||
return uiprivSysWindowType();
|
||||
}
|
||||
|
||||
uiWindow *uiNewWindow(void)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return NULL;
|
||||
return uiprivSysNewWindow();
|
||||
}
|
||||
|
||||
const char *uiWindowTitle(uiWindow *w)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return NULL;
|
||||
if (w == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiWindow", uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
return uiprivSysWindowTitle(w);
|
||||
}
|
||||
|
||||
void uiWindowSetTitle(uiWindow *w, const char *title)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return;
|
||||
if (w == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiWindow", uiprivFunc);
|
||||
return;
|
||||
}
|
||||
if (title == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("title", uiprivFunc);
|
||||
return;
|
||||
}
|
||||
uiprivSysWindowSetTitle(w, title);
|
||||
}
|
||||
|
||||
uiControl *uiWindowChild(uiWindow *w)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return NULL;
|
||||
if (w == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiWindow", uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
return uiprivSysWindowChild(w);
|
||||
}
|
||||
|
||||
void uiWindowSetChild(uiWindow *w, uiControl *child)
|
||||
{
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return;
|
||||
if (w == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiWindow", uiprivFunc);
|
||||
return;
|
||||
}
|
||||
uiprivSysWindowSetChild(w, child);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// 8 june 2019
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
bool uiprivOSVtableValid(const char *name, const uiControlOSVtable *osVtable, const char *func)
|
||||
{
|
||||
if (osVtable->Size != sizeof (uiControlOSVtable)) {
|
||||
uiprivProgrammerErrorWrongStructSize(osVtable->Size, "uiControlOSVtable", func);
|
||||
return false;
|
||||
}
|
||||
#define checkMethod(method) \
|
||||
if (osVtable->method == NULL) { \
|
||||
uiprivProgrammerErrorRequiredControlMethodMissing(name, "uiControlOSVtable", #method, func); \
|
||||
return 0; \
|
||||
}
|
||||
checkMethod(Handle)
|
||||
return true;
|
||||
}
|
||||
|
||||
uiControlOSVtable *uiprivCloneOSVtable(const uiControlOSVtable *osVtable)
|
||||
{
|
||||
uiControlOSVtable *v2;
|
||||
|
||||
v2 = uiprivNew(uiControlOSVtable);
|
||||
*v2 = *osVtable;
|
||||
return v2;
|
||||
}
|
||||
|
||||
#define callVtable(method, ...) ((*(method))(__VA_ARGS__))
|
||||
|
||||
id uiDarwinControlHandle(uiControl *c)
|
||||
{
|
||||
uiControlOSVtable *osVtable;
|
||||
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return nil;
|
||||
if (c == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiControl", uiprivFunc);
|
||||
return nil;
|
||||
}
|
||||
osVtable = uiprivControlOSVtable(c);
|
||||
return callVtable(osVtable->Handle, c, uiControlImplData(c));
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// 13 may 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// LONGTERM don't halt on release builds
|
||||
|
||||
void uiprivRealBug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap)
|
||||
{
|
||||
NSMutableString *str;
|
||||
NSString *formatted;
|
||||
|
||||
str = [NSMutableString new];
|
||||
[str appendString:[NSString stringWithFormat:@"[libui] %s:%s:%s() %s", file, line, func, prefix]];
|
||||
formatted = [[NSString alloc] initWithFormat:[NSString stringWithUTF8String:format] arguments:ap];
|
||||
[str appendString:formatted];
|
||||
[formatted release];
|
||||
NSLog(@"%@", str);
|
||||
[str release];
|
||||
__builtin_trap();
|
||||
}
|
331
darwin/main.m
331
darwin/main.m
|
@ -1,92 +1,21 @@
|
|||
// 6 april 2015
|
||||
// 20 april 2019
|
||||
#import "uipriv_darwin.h"
|
||||
#import "attrstr.h"
|
||||
|
||||
static BOOL canQuit = NO;
|
||||
static NSAutoreleasePool *globalPool;
|
||||
static uiprivApplicationClass *app;
|
||||
static uiprivAppDelegate *delegate;
|
||||
|
||||
static BOOL (^isRunning)(void);
|
||||
static BOOL stepsIsRunning;
|
||||
|
||||
@implementation uiprivApplicationClass
|
||||
|
||||
- (void)sendEvent:(NSEvent *)e
|
||||
{
|
||||
if (uiprivSendAreaEvents(e) != 0)
|
||||
return;
|
||||
[super sendEvent:e];
|
||||
}
|
||||
|
||||
// NSColorPanel always sends changeColor: to the first responder regardless of whether there's a target set on it
|
||||
// we can override it here (see colorbutton.m)
|
||||
// thanks to mikeash in irc.freenode.net/#macdev for informing me this is how the first responder chain is initiated
|
||||
// it turns out NSFontManager also sends changeFont: through this; let's inhibit that here too (see fontbutton.m)
|
||||
- (BOOL)sendAction:(SEL)sel to:(id)to from:(id)from
|
||||
{
|
||||
if (uiprivColorButtonInhibitSendAction(sel, from, to))
|
||||
return NO;
|
||||
if (uiprivFontButtonInhibitSendAction(sel, from, to))
|
||||
return NO;
|
||||
return [super sendAction:sel to:to from:from];
|
||||
}
|
||||
|
||||
// likewise, NSFontManager also sends NSFontPanelValidation messages to the first responder, however it does NOT use sendAction:from:to:!
|
||||
// instead, it uses this one (thanks swillits in irc.freenode.net/#macdev)
|
||||
// we also need to override it (see fontbutton.m)
|
||||
- (id)targetForAction:(SEL)sel to:(id)to from:(id)from
|
||||
{
|
||||
id override;
|
||||
|
||||
if (uiprivFontButtonOverrideTargetForAction(sel, from, to, &override))
|
||||
return override;
|
||||
return [super targetForAction:sel to:to from:from];
|
||||
}
|
||||
|
||||
// hey look! we're overriding terminate:!
|
||||
// we're going to make sure we can go back to main() whether Cocoa likes it or not!
|
||||
// and just how are we going to do that, hm?
|
||||
// (note: this is called after applicationShouldTerminate:)
|
||||
- (void)terminate:(id)sender
|
||||
{
|
||||
// yes that's right folks: DO ABSOLUTELY NOTHING.
|
||||
// the magic is [NSApp run] will just... stop.
|
||||
|
||||
// well let's not do nothing; let's actually quit our graceful way
|
||||
NSEvent *e;
|
||||
|
||||
if (!canQuit)
|
||||
uiprivImplBug("call to [NSApp terminate:] when not ready to terminate; definitely contact andlabs");
|
||||
|
||||
[uiprivNSApp() stop:uiprivNSApp()];
|
||||
// stop: won't register until another event has passed; let's synthesize one
|
||||
e = [NSEvent otherEventWithType:NSApplicationDefined
|
||||
location:NSZeroPoint
|
||||
modifierFlags:0
|
||||
timestamp:[[NSProcessInfo processInfo] systemUptime]
|
||||
windowNumber:0
|
||||
context:[NSGraphicsContext currentContext]
|
||||
subtype:0
|
||||
data1:0
|
||||
data2:0];
|
||||
[uiprivNSApp() postEvent:e atStart:NO]; // let pending events take priority (this is what PostQuitMessage() on Windows does so we have to do it here too for parity; thanks to mikeash in irc.freenode.net/#macdev for confirming that this parameter should indeed be NO)
|
||||
|
||||
// and in case uiMainSteps() was called
|
||||
stepsIsRunning = NO;
|
||||
}
|
||||
|
||||
@interface uiprivApplication : NSApplication
|
||||
@end
|
||||
|
||||
@implementation uiprivAppDelegate
|
||||
@implementation uiprivApplication
|
||||
@end
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// Apple docs: "Don't Use Accessor Methods in Initializer Methods and dealloc"
|
||||
[_menuManager release];
|
||||
[super dealloc];
|
||||
}
|
||||
@interface uiprivApplicationDelegate : NSObject<NSApplicationDelegate>
|
||||
@end
|
||||
|
||||
static uiprivApplication *uiprivApp;
|
||||
static uiprivApplicationDelegate *uiprivAppDelegate;
|
||||
|
||||
@implementation uiprivApplicationDelegate
|
||||
|
||||
#if 0
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
|
||||
{
|
||||
// for debugging
|
||||
|
@ -98,6 +27,7 @@ static BOOL stepsIsRunning;
|
|||
}
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
|
||||
{
|
||||
|
@ -106,183 +36,104 @@ static BOOL stepsIsRunning;
|
|||
|
||||
@end
|
||||
|
||||
uiInitOptions uiprivOptions;
|
||||
static pthread_t mainThread;
|
||||
|
||||
const char *uiInit(uiInitOptions *o)
|
||||
bool uiprivSysInit(void *options, uiInitError *err)
|
||||
{
|
||||
uiprivApp = [uiprivApplication sharedApplication];
|
||||
if (![NSApp isKindOfClass:[uiprivApplication class]])
|
||||
// TODO verify if Info.plist (both in a proper .app bundle and embedded into a standalone binary) can override this (or if that only happens if you use NSApplicationMain() instead), and, if so, add that tidbit to this message
|
||||
return uiprivInitReturnErrorf(err, "NSApp is not of type uiprivApplication; was likely already initialized beforehand");
|
||||
|
||||
// don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy!
|
||||
// see https://github.com/andlabs/ui/issues/6
|
||||
[uiprivApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
|
||||
uiprivAppDelegate = [uiprivApplicationDelegate new];
|
||||
[uiprivApp setDelegate:uiprivAppDelegate];
|
||||
|
||||
mainThread = pthread_self();
|
||||
return true;
|
||||
}
|
||||
|
||||
void uiprivSysMain(void)
|
||||
{
|
||||
[uiprivApp run];
|
||||
}
|
||||
|
||||
void uiprivSysQuit(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
uiprivOptions = *o;
|
||||
app = [[uiprivApplicationClass sharedApplication] retain];
|
||||
// don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy!
|
||||
// see https://github.com/andlabs/ui/issues/6
|
||||
[uiprivNSApp() setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
delegate = [uiprivAppDelegate new];
|
||||
[uiprivNSApp() setDelegate:delegate];
|
||||
NSEvent *e;
|
||||
|
||||
uiprivInitAlloc();
|
||||
uiprivLoadFutures();
|
||||
uiprivLoadUndocumented();
|
||||
|
||||
// always do this so we always have an application menu
|
||||
uiprivAppDelegate().menuManager = [[uiprivMenuManager new] autorelease];
|
||||
[uiprivNSApp() setMainMenu:[uiprivAppDelegate().menuManager makeMenubar]];
|
||||
|
||||
uiprivSetupFontPanel();
|
||||
|
||||
uiprivInitUnderlineColors();
|
||||
[uiprivApp stop:uiprivApp];
|
||||
// stop: won't register until another event has passed; let's synthesize one
|
||||
// TODO instead of using NSApplicationDefined, create a private event type for libui internal use only; if we can't, set up data1 and data2 so that we can intercept and discard this event if it was accidentally triggered
|
||||
e = [NSEvent otherEventWithType:NSApplicationDefined
|
||||
location:NSZeroPoint
|
||||
modifierFlags:0
|
||||
timestamp:[[NSProcessInfo processInfo] systemUptime]
|
||||
windowNumber:0
|
||||
context:[NSGraphicsContext currentContext]
|
||||
subtype:0
|
||||
data1:0
|
||||
data2:0];
|
||||
[uiprivApp postEvent:e atStart:NO]; // let pending events take priority (this is what PostQuitMessage() on Windows does so we have to do it here too for parity; thanks to mikeash in irc.freenode.net/#macdev for confirming that this parameter should indeed be NO)
|
||||
}
|
||||
|
||||
globalPool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void uiUninit(void)
|
||||
{
|
||||
if (!globalPool)
|
||||
uiprivUserBug("You must call uiInit() first!");
|
||||
[globalPool release];
|
||||
|
||||
@autoreleasepool {
|
||||
uiprivUninitUnderlineColors();
|
||||
[delegate release];
|
||||
[uiprivNSApp() setDelegate:nil];
|
||||
[app release];
|
||||
uiprivUninitAlloc();
|
||||
}
|
||||
}
|
||||
|
||||
void uiFreeInitError(const char *err)
|
||||
{
|
||||
}
|
||||
|
||||
void uiMain(void)
|
||||
{
|
||||
isRunning = ^{
|
||||
return [uiprivNSApp() isRunning];
|
||||
};
|
||||
[uiprivNSApp() run];
|
||||
}
|
||||
|
||||
void uiMainSteps(void)
|
||||
{
|
||||
// SDL does this and it seems to be necessary for the menubar to work (see #182)
|
||||
[uiprivNSApp() finishLaunching];
|
||||
isRunning = ^{
|
||||
return stepsIsRunning;
|
||||
};
|
||||
stepsIsRunning = YES;
|
||||
}
|
||||
|
||||
int uiMainStep(int wait)
|
||||
{
|
||||
uiprivNextEventArgs nea;
|
||||
|
||||
nea.mask = NSAnyEventMask;
|
||||
|
||||
// ProPuke did this in his original PR requesting this
|
||||
// I'm not sure if this will work, but I assume it will...
|
||||
nea.duration = [NSDate distantPast];
|
||||
if (wait) // but this is normal so it will work
|
||||
nea.duration = [NSDate distantFuture];
|
||||
|
||||
nea.mode = NSDefaultRunLoopMode;
|
||||
nea.dequeue = YES;
|
||||
|
||||
return uiprivMainStep(&nea, ^(NSEvent *e) {
|
||||
return NO;
|
||||
});
|
||||
}
|
||||
|
||||
// see also:
|
||||
// - http://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html
|
||||
// - https://github.com/gnustep/gui/blob/master/Source/NSApplication.m
|
||||
int uiprivMainStep(uiprivNextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *e))
|
||||
{
|
||||
NSDate *expire;
|
||||
NSEvent *e;
|
||||
NSEventType type;
|
||||
|
||||
@autoreleasepool {
|
||||
if (!isRunning())
|
||||
return 0;
|
||||
|
||||
e = [uiprivNSApp() nextEventMatchingMask:nea->mask
|
||||
untilDate:nea->duration
|
||||
inMode:nea->mode
|
||||
dequeue:nea->dequeue];
|
||||
if (e == nil)
|
||||
return 1;
|
||||
|
||||
type = [e type];
|
||||
if (!interceptEvent(e))
|
||||
[uiprivNSApp() sendEvent:e];
|
||||
[uiprivNSApp() updateWindows];
|
||||
|
||||
// GNUstep does this
|
||||
// it also updates the Services menu but there doesn't seem to be a public API for that so
|
||||
if (type != NSPeriodic && type != NSMouseMoved)
|
||||
[[uiprivNSApp() mainMenu] update];
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void uiQuit(void)
|
||||
{
|
||||
canQuit = YES;
|
||||
[uiprivNSApp() terminate:uiprivNSApp()];
|
||||
}
|
||||
|
||||
// thanks to mikeash in irc.freenode.net/#macdev for suggesting the use of Grand Central Dispatch for this
|
||||
// LONGTERM will dispatch_get_main_queue() break after _CFRunLoopSetCurrent()?
|
||||
void uiQueueMain(void (*f)(void *data), void *data)
|
||||
void uiprivSysQueueMain(void (*f)(void *data), void *data)
|
||||
{
|
||||
// dispatch_get_main_queue() is a serial queue so it will not execute multiple uiQueueMain() functions concurrently
|
||||
// the signature of f matches dispatch_function_t
|
||||
dispatch_async_f(dispatch_get_main_queue(), data, f);
|
||||
}
|
||||
|
||||
@interface uiprivTimerDelegate : NSObject {
|
||||
int (*f)(void *data);
|
||||
void *data;
|
||||
}
|
||||
- (id)initWithCallback:(int (*)(void *))callback data:(void *)callbackData;
|
||||
- (void)doTimer:(NSTimer *)timer;
|
||||
@end
|
||||
|
||||
@implementation uiprivTimerDelegate
|
||||
|
||||
- (id)initWithCallback:(int (*)(void *))callback data:(void *)callbackData
|
||||
bool uiprivSysCheckThread(void)
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self->f = callback;
|
||||
self->data = callbackData;
|
||||
}
|
||||
return self;
|
||||
return pthread_equal(pthread_self(), mainThread);
|
||||
}
|
||||
|
||||
- (void)doTimer:(NSTimer *)timer
|
||||
// Debugger() was deprecated in macOS 10.8 (as part of the larger CarbonCore deprecation), but they did not provide a replacement.
|
||||
// Though some people say inline asm, I'd rather make this work automatically everywhere.
|
||||
// Others say raise(SIGTRAP) and others still say __builtin_trap(), but I can't confirm these do what I want (some sources, including documentation, claim they either cause a core dump or send a SIGILL instead).
|
||||
// I've also heard of some new clang intrinsics, __builtin_debugtrap(), but this is totally undocumented and the original patch for this suggested it would be identical to __builtin_trap(), so...
|
||||
// Also I cannot figure out how to manually raise EXC_BREAKPOINT.
|
||||
// So that leaves us with one option: just use Debugger(), turning off the deprecation warnings.
|
||||
// Also, while we could turn off the deprecation warning temporarily in gcc >= 4.6 and any clang, I'm not sure what minimum version of gcc we need, and I'm not sure if "any clang" is even accurate.
|
||||
// So instead of juggling version macros, just turn off deprecation warnings at the bottom of this file.
|
||||
// References:
|
||||
// - https://stackoverflow.com/questions/37299/xcode-equivalent-of-asm-int-3-debugbreak-halt
|
||||
// - https://stackoverflow.com/questions/2622017/suppressing-deprecated-warnings-in-xcode
|
||||
// - https://stackoverflow.com/questions/28166565/detect-gcc-as-opposed-to-msvc-clang-with-macro
|
||||
// - https://stackoverflow.com/questions/16555585/why-pragma-gcc-diagnostic-push-pop-warning-in-gcc-c
|
||||
// - possibly others, all on stackoverflow.com (and maybe once on Apple's own forums?); I forget now
|
||||
static void debugBreak(void);
|
||||
|
||||
void uiprivReportError(const char *prefix, const char *msg, const char *suffix, bool internal)
|
||||
{
|
||||
if (!(*(self->f))(self->data))
|
||||
[timer invalidate];
|
||||
NSExceptionName exceptionName;
|
||||
|
||||
NSLog(@"*** %s: %s. %s", prefix, msg, suffix);
|
||||
exceptionName = NSInternalInconsistencyException;
|
||||
if (!internal)
|
||||
// TODO either find an appropriate exception for each possible message or use a custom exception name
|
||||
exceptionName = NSInvalidArgumentException;
|
||||
[NSException raise:exceptionName
|
||||
format:@"%s: %s", prefix, msg];
|
||||
debugBreak();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void uiTimer(int milliseconds, int (*f)(void *data), void *data)
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
static void debugBreak(void)
|
||||
{
|
||||
uiprivTimerDelegate *delegate;
|
||||
|
||||
delegate = [[uiprivTimerDelegate alloc] initWithCallback:f data:data];
|
||||
[NSTimer scheduledTimerWithTimeInterval:(milliseconds / 1000.0)
|
||||
target:delegate
|
||||
selector:@selector(doTimer:)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
[delegate release];
|
||||
Debugger();
|
||||
}
|
||||
|
||||
// TODO figure out the best way to clean the above up in uiUninit(), if it's even necessary
|
||||
// TODO that means figure out if timers can still fire without the main loop
|
||||
// DO NOT ADD NEW CODE HERE. IT WILL NOT BE SUBJECT TO DEPRECATION WARNINGS.
|
||||
// I am making an exception here with Debugger(); see the large comment above.
|
||||
|
|
|
@ -1,55 +1,9 @@
|
|||
# 23 march 2019
|
||||
|
||||
libui_sources += [
|
||||
'darwin/aat.m',
|
||||
'darwin/alloc.m',
|
||||
'darwin/area.m',
|
||||
'darwin/areaevents.m',
|
||||
'darwin/attrstr.m',
|
||||
'darwin/autolayout.m',
|
||||
'darwin/box.m',
|
||||
'darwin/button.m',
|
||||
'darwin/checkbox.m',
|
||||
'darwin/colorbutton.m',
|
||||
'darwin/combobox.m',
|
||||
'darwin/control.m',
|
||||
'darwin/datetimepicker.m',
|
||||
'darwin/debug.m',
|
||||
'darwin/draw.m',
|
||||
'darwin/drawtext.m',
|
||||
'darwin/editablecombo.m',
|
||||
'darwin/entry.m',
|
||||
'darwin/fontbutton.m',
|
||||
'darwin/fontmatch.m',
|
||||
'darwin/fonttraits.m',
|
||||
'darwin/fontvariation.m',
|
||||
'darwin/form.m',
|
||||
'darwin/future.m',
|
||||
'darwin/graphemes.m',
|
||||
'darwin/grid.m',
|
||||
'darwin/group.m',
|
||||
'darwin/image.m',
|
||||
'darwin/label.m',
|
||||
'darwin/controls.m',
|
||||
'darwin/main.m',
|
||||
'darwin/map.m',
|
||||
'darwin/menu.m',
|
||||
'darwin/multilineentry.m',
|
||||
'darwin/opentype.m',
|
||||
'darwin/progressbar.m',
|
||||
'darwin/radiobuttons.m',
|
||||
'darwin/scrollview.m',
|
||||
'darwin/separator.m',
|
||||
'darwin/slider.m',
|
||||
'darwin/spinbox.m',
|
||||
'darwin/stddialogs.m',
|
||||
'darwin/tab.m',
|
||||
'darwin/table.m',
|
||||
'darwin/tablecolumn.m',
|
||||
'darwin/text.m',
|
||||
'darwin/undocumented.m',
|
||||
'darwin/util.m',
|
||||
'darwin/window.m',
|
||||
'darwin/winmoveresize.m',
|
||||
]
|
||||
|
||||
libui_deps += [
|
||||
|
|
|
@ -4,160 +4,16 @@
|
|||
#define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_8
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <dlfcn.h> // see future.m
|
||||
#import "../ui.h"
|
||||
#import "../ui_darwin.h"
|
||||
#import <pthread.h>
|
||||
#import <stdlib.h>
|
||||
#define uiprivOSHeader "../ui_darwin.h"
|
||||
#import "../common/uipriv.h"
|
||||
|
||||
// TODO should we rename the uiprivMk things or not
|
||||
// TODO what about renaming class wrapper functions that return the underlying class (like uiprivNewLabel())
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
#error Sorry, libui cannot be compiled with ARC.
|
||||
#endif
|
||||
|
||||
#define uiprivToNSString(str) [NSString stringWithUTF8String:(str)]
|
||||
#define uiprivFromNSString(str) [(str) UTF8String]
|
||||
|
||||
// TODO find a better place for this
|
||||
#ifndef NSAppKitVersionNumber10_9
|
||||
#define NSAppKitVersionNumber10_9 1265
|
||||
#endif
|
||||
|
||||
// map.m
|
||||
typedef struct uiprivMap uiprivMap;
|
||||
extern uiprivMap *uiprivNewMap(void);
|
||||
extern void uiprivMapDestroy(uiprivMap *m);
|
||||
extern void *uiprivMapGet(uiprivMap *m, void *key);
|
||||
extern void uiprivMapSet(uiprivMap *m, void *key, void *value);
|
||||
extern void uiprivMapDelete(uiprivMap *m, void *key);
|
||||
extern void uiprivMapWalk(uiprivMap *m, void (*f)(void *key, void *value));
|
||||
extern void uiprivMapReset(uiprivMap *m);
|
||||
|
||||
// menu.m
|
||||
@interface uiprivMenuManager : NSObject {
|
||||
uiprivMap *items;
|
||||
BOOL hasQuit;
|
||||
BOOL hasPreferences;
|
||||
BOOL hasAbout;
|
||||
}
|
||||
@property (strong) NSMenuItem *quitItem;
|
||||
@property (strong) NSMenuItem *preferencesItem;
|
||||
@property (strong) NSMenuItem *aboutItem;
|
||||
// NSMenuValidation is only informal
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)item;
|
||||
- (NSMenu *)makeMenubar;
|
||||
@end
|
||||
extern void uiprivFinalizeMenus(void);
|
||||
extern void uiprivUninitMenus(void);
|
||||
|
||||
// main.m
|
||||
@interface uiprivApplicationClass : NSApplication
|
||||
@end
|
||||
// this is needed because NSApp is of type id, confusing clang
|
||||
#define uiprivNSApp() ((uiprivApplicationClass *) NSApp)
|
||||
@interface uiprivAppDelegate : NSObject<NSApplicationDelegate>
|
||||
@property (strong) uiprivMenuManager *menuManager;
|
||||
@end
|
||||
#define uiprivAppDelegate() ((uiprivAppDelegate *) [uiprivNSApp() delegate])
|
||||
typedef struct uiprivNextEventArgs uiprivNextEventArgs;
|
||||
struct uiprivNextEventArgs {
|
||||
NSEventMask mask;
|
||||
NSDate *duration;
|
||||
// LONGTERM no NSRunLoopMode?
|
||||
NSString *mode;
|
||||
BOOL dequeue;
|
||||
};
|
||||
extern int uiprivMainStep(uiprivNextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *));
|
||||
|
||||
// util.m
|
||||
extern void uiprivDisableAutocorrect(NSTextView *);
|
||||
|
||||
// entry.m
|
||||
extern void uiprivFinishNewTextField(NSTextField *, BOOL);
|
||||
extern NSTextField *uiprivNewEditableTextField(void);
|
||||
|
||||
// window.m
|
||||
@interface uiprivNSWindow : NSWindow
|
||||
- (void)uiprivDoMove:(NSEvent *)initialEvent;
|
||||
- (void)uiprivDoResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge;
|
||||
@end
|
||||
extern uiWindow *uiprivWindowFromNSWindow(NSWindow *);
|
||||
|
||||
// alloc.m
|
||||
extern NSMutableArray *uiprivDelegates;
|
||||
extern void uiprivInitAlloc(void);
|
||||
extern void uiprivUninitAlloc(void);
|
||||
|
||||
// autolayout.m
|
||||
extern NSLayoutConstraint *uiprivMkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc);
|
||||
extern void uiprivJiggleViewLayout(NSView *view);
|
||||
typedef struct uiprivSingleChildConstraints uiprivSingleChildConstraints;
|
||||
struct uiprivSingleChildConstraints {
|
||||
NSLayoutConstraint *leadingConstraint;
|
||||
NSLayoutConstraint *topConstraint;
|
||||
NSLayoutConstraint *trailingConstraintGreater;
|
||||
NSLayoutConstraint *trailingConstraintEqual;
|
||||
NSLayoutConstraint *bottomConstraintGreater;
|
||||
NSLayoutConstraint *bottomConstraintEqual;
|
||||
};
|
||||
extern void uiprivSingleChildConstraintsEstablish(uiprivSingleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc);
|
||||
extern void uiprivSingleChildConstraintsRemove(uiprivSingleChildConstraints *c, NSView *cv);
|
||||
extern void uiprivSingleChildConstraintsSetMargined(uiprivSingleChildConstraints *c, int margined);
|
||||
|
||||
// area.m
|
||||
extern int uiprivSendAreaEvents(NSEvent *);
|
||||
|
||||
// areaevents.m
|
||||
extern BOOL uiprivFromKeycode(unsigned short keycode, uiAreaKeyEvent *ke);
|
||||
extern BOOL uiprivKeycodeModifier(unsigned short keycode, uiModifiers *mod);
|
||||
|
||||
// draw.m
|
||||
extern uiDrawContext *uiprivDrawNewContext(CGContextRef, CGFloat);
|
||||
extern void uiprivDrawFreeContext(uiDrawContext *);
|
||||
|
||||
// fontbutton.m
|
||||
extern BOOL uiprivFontButtonInhibitSendAction(SEL sel, id from, id to);
|
||||
extern BOOL uiprivFontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override);
|
||||
extern void uiprivSetupFontPanel(void);
|
||||
|
||||
// colorbutton.m
|
||||
extern BOOL uiprivColorButtonInhibitSendAction(SEL sel, id from, id to);
|
||||
|
||||
// scrollview.m
|
||||
typedef struct uiprivScrollViewCreateParams uiprivScrollViewCreateParams;
|
||||
struct uiprivScrollViewCreateParams {
|
||||
// TODO MAYBE fix these identifiers
|
||||
NSView *DocumentView;
|
||||
NSColor *BackgroundColor;
|
||||
BOOL DrawsBackground;
|
||||
BOOL Bordered;
|
||||
BOOL HScroll;
|
||||
BOOL VScroll;
|
||||
};
|
||||
typedef struct uiprivScrollViewData uiprivScrollViewData;
|
||||
extern NSScrollView *uiprivMkScrollView(uiprivScrollViewCreateParams *p, uiprivScrollViewData **dout);
|
||||
extern void uiprivScrollViewSetScrolling(NSScrollView *sv, uiprivScrollViewData *d, BOOL hscroll, BOOL vscroll);
|
||||
extern void uiprivScrollViewFreeData(NSScrollView *sv, uiprivScrollViewData *d);
|
||||
|
||||
// label.m
|
||||
extern NSTextField *uiprivNewLabel(NSString *str);
|
||||
|
||||
// image.m
|
||||
extern NSImage *uiprivImageNSImage(uiImage *);
|
||||
|
||||
// winmoveresize.m
|
||||
extern void uiprivDoManualMove(NSWindow *w, NSEvent *initialEvent);
|
||||
extern void uiprivDoManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge);
|
||||
|
||||
// future.m
|
||||
extern CFStringRef *uiprivFUTURE_kCTFontOpenTypeFeatureTag;
|
||||
extern CFStringRef *uiprivFUTURE_kCTFontOpenTypeFeatureValue;
|
||||
extern CFStringRef *uiprivFUTURE_kCTBackgroundColorAttributeName;
|
||||
extern void uiprivLoadFutures(void);
|
||||
extern void uiprivFUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier);
|
||||
extern BOOL uiprivFUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent);
|
||||
|
||||
// undocumented.m
|
||||
extern CFStringRef uiprivUNDOC_kCTFontPreferredSubFamilyNameKey;
|
||||
extern CFStringRef uiprivUNDOC_kCTFontPreferredFamilyNameKey;
|
||||
extern void uiprivLoadUndocumented(void);
|
||||
|
|
160
darwin/window.m
160
darwin/window.m
|
@ -3,10 +3,11 @@
|
|||
|
||||
#define defaultStyleMask (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
|
||||
|
||||
struct uiWindow {
|
||||
uiDarwinControl c;
|
||||
struct windowImplData {
|
||||
NSWindow *window;
|
||||
char *title;
|
||||
uiControl *child;
|
||||
#if 0
|
||||
int margined;
|
||||
int (*onClosing)(uiWindow *, void *);
|
||||
void *onClosingData;
|
||||
|
@ -16,8 +17,12 @@ struct uiWindow {
|
|||
BOOL suppressSizeChanged;
|
||||
int fullscreen;
|
||||
int borderless;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if 0
|
||||
// skip {
|
||||
|
||||
@implementation uiprivNSWindow
|
||||
|
||||
- (void)uiprivDoMove:(NSEvent *)initialEvent
|
||||
|
@ -148,8 +153,6 @@ static void uiWindowDestroy(uiControl *c)
|
|||
uiFreeControl(uiControl(w));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiWindow, window)
|
||||
|
||||
uiControl *uiWindowParent(uiControl *c)
|
||||
{
|
||||
return NULL;
|
||||
|
@ -245,16 +248,6 @@ static void uiWindowChildVisibilityChanged(uiDarwinControl *c)
|
|||
windowRelayout(w);
|
||||
}
|
||||
|
||||
char *uiWindowTitle(uiWindow *w)
|
||||
{
|
||||
return uiDarwinNSStringToText([w->window title]);
|
||||
}
|
||||
|
||||
void uiWindowSetTitle(uiWindow *w, const char *title)
|
||||
{
|
||||
[w->window setTitle:uiprivToNSString(title)];
|
||||
}
|
||||
|
||||
void uiWindowContentSize(uiWindow *w, int *width, int *height)
|
||||
{
|
||||
NSRect r;
|
||||
|
@ -367,24 +360,27 @@ static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
|
|||
// do nothing
|
||||
}
|
||||
|
||||
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
||||
// } skip
|
||||
#endif
|
||||
|
||||
static bool windowInit(uiControl *c, void *implData, void *initData)
|
||||
{
|
||||
uiWindow *w;
|
||||
struct windowImplData *wi = (struct windowImplData *) implData;
|
||||
|
||||
uiprivFinalizeMenus();
|
||||
|
||||
uiDarwinNewControl(uiWindow, w);
|
||||
|
||||
w->window = [[uiprivNSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height)
|
||||
// TODO grab the exact values from Interface Builder
|
||||
wi->window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
|
||||
styleMask:defaultStyleMask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:YES];
|
||||
[w->window setTitle:uiprivToNSString(title)];
|
||||
|
||||
wi->title = NULL;
|
||||
[wi->window setTitle:@""];
|
||||
|
||||
// do NOT release when closed
|
||||
// we manually do this in uiWindowDestroy() above
|
||||
[w->window setReleasedWhenClosed:NO];
|
||||
// we manually do this in Free() below
|
||||
[wi->window setReleasedWhenClosed:NO];
|
||||
|
||||
#if 0
|
||||
if (windowDelegate == nil) {
|
||||
windowDelegate = [[windowDelegateClass new] autorelease];
|
||||
[uiprivDelegates addObject:windowDelegate];
|
||||
|
@ -392,10 +388,122 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
|||
[windowDelegate registerWindow:w];
|
||||
uiWindowOnClosing(w, defaultOnClosing, NULL);
|
||||
uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||
#endif
|
||||
|
||||
return w;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void connectChild(struct windowImplData *wi)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void disconnectChild(struct windowImplData *wi)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void windowFree(uiControl *c, void *implData)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) implData;
|
||||
|
||||
if (wi->child != NULL) {
|
||||
disconnectChild(wi);
|
||||
uiControlFree(wi->child);
|
||||
wi->child = NULL;
|
||||
}
|
||||
if (wi->title != NULL) {
|
||||
uiprivFreeUTF8(wi->title);
|
||||
wi->title = NULL;
|
||||
}
|
||||
[wi->window release];
|
||||
wi->window = nil;
|
||||
}
|
||||
|
||||
static void windowParentChanging(uiControl *c, void *implData, uiControl *oldParent)
|
||||
{
|
||||
uiprivProgrammerErrorCannotHaveWindowsAsChildren();
|
||||
}
|
||||
|
||||
static void windowParentChanged(uiControl *c, void *implData, uiControl *newParent)
|
||||
{
|
||||
uiprivProgrammerErrorCannotHaveWindowsAsChildren();
|
||||
}
|
||||
|
||||
static id windowHandle(uiControl *c, void *implData)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) implData;
|
||||
|
||||
return wi->window;
|
||||
}
|
||||
|
||||
static const uiControlVtable windowVtable = {
|
||||
.Size = sizeof (uiControlVtable),
|
||||
.Init = windowInit,
|
||||
.Free = windowFree,
|
||||
.ParentChanging = windowParentChanging,
|
||||
.ParentChanged = windowParentChanged,
|
||||
};
|
||||
|
||||
static const uiControlOSVtable windowOSVtable = {
|
||||
.Size = sizeof (uiControlOSVtable),
|
||||
.Handle = windowHandle,
|
||||
};
|
||||
|
||||
static uint32_t windowType = 0;
|
||||
|
||||
uint32_t uiprivSysWindowType(void)
|
||||
{
|
||||
if (windowType == 0)
|
||||
windowType = uiRegisterControlType("uiWindow", &windowVtable, &windowOSVtable, sizeof (struct windowImplData));
|
||||
return windowType;
|
||||
}
|
||||
|
||||
uiWindow *uiprivSysNewWindow(void)
|
||||
{
|
||||
return (uiWindow *) uiNewControl(uiWindowType(), NULL);
|
||||
}
|
||||
|
||||
const char *uiprivSysWindowTitle(uiWindow *w)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) uiControlImplData(uiControl(w));
|
||||
|
||||
if (wi->title == NULL)
|
||||
// TODO replace this with a dedicated UTF-8 empty string object
|
||||
return "";
|
||||
return wi->title;
|
||||
}
|
||||
|
||||
void uiprivSysWindowSetTitle(uiWindow *w, const char *title)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) uiControlImplData(uiControl(w));
|
||||
|
||||
if (wi->title != NULL)
|
||||
uiprivFreeUTF8(wi->title);
|
||||
wi->title = uiprivSanitizeUTF8(title);
|
||||
[wi->window setTitle:[NSString stringWithUTF8String:wi->title]];
|
||||
}
|
||||
|
||||
uiControl *uiprivSysWindowChild(uiWindow *w)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) uiControlImplData(uiControl(w));
|
||||
|
||||
return wi->child;
|
||||
}
|
||||
|
||||
void uiprivSysWindowSetChild(uiWindow *w, uiControl *child)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) uiControlImplData(uiControl(w));
|
||||
|
||||
if (wi->child != NULL)
|
||||
disconnectChild(wi);
|
||||
wi->child = child;
|
||||
if (wi->child != NULL)
|
||||
connectChild(wi);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
// utility function for menus
|
||||
uiWindow *uiprivWindowFromNSWindow(NSWindow *w)
|
||||
{
|
||||
|
@ -405,3 +513,5 @@ uiWindow *uiprivWindowFromNSWindow(NSWindow *w)
|
|||
return NULL;
|
||||
return [windowDelegate lookupWindow:w];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<!-- 8 april 2019 -->
|
||||
|
||||
* [Using libui](using-libui.md)
|
||||
* [Initialization and the Main Loop](init-main.md)
|
||||
* [Controls](controls.md)
|
||||
** [Window](window.md)
|
||||
* Windows
|
||||
** [Controls](windows-controls.md)
|
||||
* Unix
|
||||
** [Controls](unix-controls.md)
|
||||
* macOS
|
||||
** [Controls](darwin-controls.md)
|
|
@ -0,0 +1,142 @@
|
|||
<!-- 29 may 2019 -->
|
||||
|
||||
# Controls
|
||||
|
||||
## Overview
|
||||
|
||||
TODO
|
||||
|
||||
## Reference
|
||||
|
||||
### `uiControl`
|
||||
|
||||
```c
|
||||
typedef struct uiControl uiControl;
|
||||
uint32_t uiControlType(void);
|
||||
#define uiControl(obj) ((uiControl *) uiCheckControlType((obj), uiControlType()))
|
||||
```
|
||||
|
||||
`uiControl` is an opaque type that describes a control.
|
||||
|
||||
`uiControlType()` is the type identifier of a `uiControl` as passed to `uiCheckControlType()`. You rarely need to call this directly; the `uiControl()` conversion macro does this for you. A control type identifier of 0 is always invalid; this can be used to initialize the variables that hold the returned identifiers from `uiRegisterControlType()`.
|
||||
|
||||
### `uiControlVtable`
|
||||
|
||||
```c
|
||||
typedef struct uiControlVtable uiControlVtable;
|
||||
struct uiControlVtable {
|
||||
size_t Size;
|
||||
bool (*Init)(uiControl *c, void *implData, void *initData);
|
||||
void (*Free)(uiControl *c, void *implData);
|
||||
void (*ParentChanging)(uiControl *c, void *implData, uiControl *oldParent);
|
||||
void (*ParentChanged)(uiControl *c, void *implData, uiControl *newParent);
|
||||
};
|
||||
```
|
||||
|
||||
`uiControlVtable` describes the set of functions that control implementations need to implement. When registering your control type, you pass this in as a parameter to `uiRegisterControlType()`. Each method here is required.
|
||||
|
||||
You are responsible for allocating and initializing this struct. To do so, you simply zero the memory for this struct and set its `Size` field to `sizeof (uiControlVtable)`. (TODO put this in a common place)
|
||||
|
||||
Each method takes at least two parameters. The first, `c`, is the `uiControl` itself. The second, `implData`, is the implementation data pointer; it is the same as the pointer returned by `uiControlImplData(c)`, and is provided here as a convenience.
|
||||
|
||||
Each method is named for the `uiControl` function that it implements. As such, details on how to implement these methods are documented alongside those functions. For instance, instructions on implementing `Free()` are given under the documentation for `uiControlFree()`. The only exception is `Init()`, which is discussed under `uiNewControl()` below.
|
||||
|
||||
### `uiRegisterControlType()`
|
||||
|
||||
```c
|
||||
uint32_t uiRegisterControlType(const char *name, const uiControlVtable *vtable, const uiControlOSVtable *osVtable, size_t implDataSize);
|
||||
```
|
||||
|
||||
`uiRegisterControlType()` registers a new `uiControl` type with the given vtables and returns its ID as passed to `uiNewControl()`. `implDataSize` is the size of the implementation data struct that is created by `uiNewControl()`.
|
||||
|
||||
Each type has a name, passed in the `name` parameter. The type name is only used for debugging and error reporting purposes. The type name should be a standard C string using the system encoding (TODO proper terminology), rather than a UTF-8 string as with the rest of libui. The type name is copied into libui-internal memory; the `name` pointer passed to `uiRegisterControlType()` is not used after it returns. It is a programmer error to specify `NULL` for `name`. (TODO anything else? Empty string? Duplicate name?) (TODO reserved name rules)
|
||||
|
||||
`uiControlVtable` describes the functions of a `uiControl` common between platforms, and is discussed above. `uiControlOSVtable` describes functionst hat vary from OS to OS, and are described in the respective OS-specific uiControl implementation pages. The two vtables are copied into libui-internal memory; the vtable pointers passed to `uiRegisterControlType()` are not used after it returns.
|
||||
|
||||
It is a programmer error to specify `NULL` for either vtable. It is also a programmer error to specify `NULL` for any of the methods in either vtable — that is, all methods are required. It is also a programmer error to pass the wrong value to the `Size` field of either vtable.
|
||||
|
||||
An `implDataSize` of 0 is legal; the implementation data pointer will be `NULL`. This is not particularly useful, however.
|
||||
|
||||
### `uiCheckControlType()`
|
||||
|
||||
```c
|
||||
void *uiCheckControlType(void *c, uint32_t type);
|
||||
```
|
||||
|
||||
`uiCheckControlType()` checks whether `c` is a `uiControl`, and if so, whether it is of the type specified by `type`. If `c` is `NULL`, or if either of the above conditions is false, a programmer error is raised. If the conditions are met, the function returns `c` unchanged.
|
||||
|
||||
This function is intended to be used to implement a macro that converts an arbitrary `uiControl` pointer into a specific type. For instance, `uiButton` exposes its type ID as a function `uiButtonType()`, and provides the macro `uiButton()` that does the actual conversion as so:
|
||||
|
||||
```c
|
||||
#define uiButton(c) ((uiButton *) uiCheckControlType((c), uiButtonType()))
|
||||
```
|
||||
|
||||
(TODO document passing uiControlType() to this, or even make doing so unnecessary)
|
||||
|
||||
### `uiNewControl()`
|
||||
|
||||
```c
|
||||
uiControl *uiNewControl(uint32_t type, void *initData);
|
||||
```
|
||||
|
||||
`uiNewControl()` creates a new `uiControl` of the given type.
|
||||
|
||||
This function is meant for control implementations to use in the implementation of dedicated creation functions; for instance, `uiNewButton()` calls `uiNewControl()`, passing in the appropriate values for `initData`. `initData` is, in turn, passed to the control's `Init()` method, and its format is generally internal to the control. Normal users should not call this function.
|
||||
|
||||
It is a programmer error to pass an invalid value for either `type` or `initData`.
|
||||
|
||||
**For control implementations**: This function allocates both the `uiControl` and the memory for the implementation data, and then passes both of these allocations as well as the value of `initData` into your `Init()` method. Before calling `Init()`, libui will clear the `implData` memory, as with `memset(0)`. Return `false` from the `Init()` method if `initData` is invalid; if it is valid, initialize the control and return `true`. To discourage direct use of `uiNewControl()`, you should generally not allow `initData` to be `NULL`, even if there are no parameters. Do **not** return `false` for any other reason, including other forms of initialization failures; see [Error handling](error-handling.md) for details on what to do instead.
|
||||
TODO is this whole spiel about the return value even necessary? shouldn't the outer library be responsible for handling errors instead?
|
||||
|
||||
### `uiControlFree()`
|
||||
|
||||
```c
|
||||
void uiControlFree(uiControl *c);
|
||||
```
|
||||
|
||||
`uiControlFree()` frees the given control.
|
||||
|
||||
If `c` has children, those children are also freed. It is a programmer error to free a control that is itself a child of another control.
|
||||
|
||||
It is a programmer error to specify `NULL` for `c`.
|
||||
|
||||
**For control implementations**: This function calls your vtable's `Free()` method. Parameter validity checks are already performed. Your `Free()` should call `uiControlFree()` on all your control's children and free dynamically allocated memory that is part of your implementation data. Once your `Free()` method returns, libui will take care of freeing the implementation data memory block itself.
|
||||
|
||||
## `uiControlSetParent()`
|
||||
|
||||
```c
|
||||
uiprivExtern void uiControlSetParent(uiControl *c, uiControl *parent);
|
||||
```
|
||||
|
||||
`uiControlSetParent()` marks `parent` as the parent of `c`. `parent` may be `NULL`, in which case the control has no parent.
|
||||
|
||||
This function is used by the implementation of a container control to actually establish a parent-child relationship from libui's point of view. This function is only intended to be called by control implementations. You should not call it directly; instead, use the methods provided by your container control to add children.
|
||||
|
||||
This function can only be used to set the parent of an unparented control or to remove its parent. It may not be used to change the parent of an already parented control. It is a programmer error to set the parent of a control that already has a parent to something other than `NULL` (even if to the same parent), or to set the parent of a control with no parent to `NULL`. (The idea here is to reinforce the concept of container implementations being responsible for setting their children properly, not the user.)
|
||||
|
||||
It is a programmer error to pass `NULL` for `c`. (TODO non-uiControl for either c or parent?)
|
||||
|
||||
It is a programmer error to introduce a cycle when changing the parent of a control. By extension, it is a programmer error to make a control its own parent.
|
||||
|
||||
TODO top-levels and parenting
|
||||
|
||||
**For control implementations**: You would call this when adding a control to your container, preferably before actually doing any internal bookkeeping. Likewise, call this when removing a child, preferably after doing any internal bookkeeping.
|
||||
|
||||
The child will receive a call to its `ParentChanging()` method before the actual change happens, and a call to `ParentChanged()` afterward, receiving the old and new parent, respectively, as an argument. This is where OS-specific parenting code would go; specific instructions can be found in each OS's uiControl implementation page.
|
||||
|
||||
TODO do things this way to avoid needing to check if reparenting from a container implementation, or do that manually each time? we used to have uiControlVerifySetParent()...
|
||||
TODO I forgot what this meant
|
||||
|
||||
Container implementations are free to un-parent and then immediately re-parent a child should some platform-specific need to recreate any existing OS-level relations arise. For example, on Windows, this would be how to signal that a new parent handle is available, and the child would respond by destroying its current window handles and creating new ones. Refer to each individual platform documentation for details. In particular, this means that implemenations of `ParentChanging()` and `ParentChanged()` should not make any assumptions as to why the parent is changing.
|
||||
|
||||
## `uiControlImplData()`
|
||||
|
||||
```c
|
||||
void *uiControlImplData(uiControl *c);
|
||||
```
|
||||
|
||||
`uiControlImplData()` returns the pointer to the implementation data for `c`. The returned pointer is valid for the lifetime of `c`.
|
||||
|
||||
This function is meant to be used by control implementations only. There is in general no guarantee as to the size or format of this pointer. Normal users should not call `uiControlImplData()`.
|
||||
|
||||
It is a programmer error to pass `NULL` for `c`. (TODO non-uiControl?)
|
|
@ -0,0 +1,45 @@
|
|||
<!-- 10 june 2019 -->
|
||||
|
||||
# Controls on macOS
|
||||
|
||||
## Overview
|
||||
|
||||
TODO
|
||||
|
||||
## Reference
|
||||
|
||||
### `uiControlOSVtable`
|
||||
|
||||
```objective-c
|
||||
typedef struct uiControlOSVtable uiControlOSVtable;
|
||||
struct uiControlOSVtable {
|
||||
size_t Size;
|
||||
id (*Handle)(uiControl *c, void *implData);
|
||||
};
|
||||
```
|
||||
|
||||
`uiControlOSVtable` describes the set of functions that control implementations on macOS need to implement. When registering your control type, you pass this in as a parameter to `uiRegisterControlType()`. Each method here is required.
|
||||
|
||||
You are responsible for allocating and initializing this struct. To do so, you simply zero the memory for this struct and set its `Size` field to `sizeof (uiControlOSVtable)`. (TODO put this in a common place)
|
||||
|
||||
Each method takes at least two parameters. The first, `c`, is the `uiControl` itself. The second, `implData`, is the implementation data pointer; it is the same as the pointer returned by `uiControlImplData(c)`, and is provided here as a convenience.
|
||||
|
||||
Each method is named for the `uiDarwinControl` function that it implements. As such, details on how to implement these methods are documented alongside those functions. For instance, instructions on implementing `Handle()` are given under the documentation for `uiDarwinControlHandle()`.
|
||||
|
||||
### `uiDarwinControlHandle()`
|
||||
|
||||
```objective-c
|
||||
uiprivExtern id uiDarwinControlHandle(uiControl *c);
|
||||
```
|
||||
|
||||
`uiDarwinControlHandle()` returns the Objective-C object that underpins `c`, or `nil` if `c` does not have any underlying object associated with it when called.
|
||||
|
||||
The object returned by `uiDarwinControlHandle()` is owned by `c`; you do not receive a reference to it at all. The object is valid until either `c` is destroyed or until `c` decides to destroy the object; you can handle the latter by catching TODO. In general, you should not store the returned object pointer directly for later use, nor should you attempt to retain the returned object. Instead, use the returned handle immediately if you have to, or follow TODO if you need to adjust properties of the handle that should persist across handle destruction/creation.
|
||||
|
||||
`uiWindow`s have a single handle of type `NSWindow` that is created when the `uiWindow` is created and destroyed when the `uiWindow` is destroyed. Despite this, you should still follow the best practices described above.
|
||||
|
||||
For all other `uiControl`s defined by libui, the returned object is of the appropriate `NSView` subclass:
|
||||
|
||||
* TODO
|
||||
|
||||
It is a programmer error to pass `NULL` for `c`. TODO a non-`uiControl`?
|
|
@ -0,0 +1,45 @@
|
|||
<!-- 10 june 2019 -->
|
||||
|
||||
# Controls on Haiku
|
||||
|
||||
## Overview
|
||||
|
||||
TODO
|
||||
|
||||
## Reference
|
||||
|
||||
### `uiControlOSVtable`
|
||||
|
||||
```objective-c
|
||||
typedef struct uiControlOSVtable uiControlOSVtable;
|
||||
struct uiControlOSVtable {
|
||||
size_t Size;
|
||||
void *(*Handle)(uiControl *c, void *implData);
|
||||
};
|
||||
```
|
||||
|
||||
`uiControlOSVtable` describes the set of functions that control implementations on Haiku need to implement. When registering your control type, you pass this in as a parameter to `uiRegisterControlType()`. Each method here is required.
|
||||
|
||||
You are responsible for allocating and initializing this struct. To do so, you simply zero the memory for this struct and set its `Size` field to `sizeof (uiControlOSVtable)`. (TODO put this in a common place)
|
||||
|
||||
Each method takes at least two parameters. The first, `c`, is the `uiControl` itself. The second, `implData`, is the implementation data pointer; it is the same as the pointer returned by `uiControlImplData(c)`, and is provided here as a convenience.
|
||||
|
||||
Each method is named for the `uiHaikuControl` function that it implements. As such, details on how to implement these methods are documented alongside those functions. For instance, instructions on implementing `Handle()` are given under the documentation for `uiHaikuControlHandle()`.
|
||||
|
||||
### `uiHaikuControlHandle()`
|
||||
|
||||
```c
|
||||
uiprivExtern void *uiHaikuControlHandle(uiControl *c);
|
||||
```
|
||||
|
||||
`uiHaikuControlHandle()` returns the Objective-C object that underpins `c`, or `NULL` if `c` does not have any underlying object associated with it when called.
|
||||
|
||||
The object returned by `uiHaikuControlHandle()` is owned by `c`; you do not receive a reference to it at all. The object is valid until either `c` is destroyed or until `c` decides to destroy the object; you can handle the latter by catching TODO. In general, you should not store the returned object pointer directly for later use. Instead, use the returned handle immediately if you have to, or follow TODO if you need to adjust properties of the handle that should persist across handle destruction/creation.
|
||||
|
||||
`uiWindow`s have a single handle of type `BWindow` that is created when the `uiWindow` is created and destroyed when the `uiWindow` is destroyed. Despite this, you should still follow the best practices described above.
|
||||
|
||||
For all other `uiControl`s defined by libui, the returned object is of the appropriate class:
|
||||
|
||||
* TODO
|
||||
|
||||
It is a programmer error to pass `NULL` for `c`. TODO a non-`uiControl`?
|
|
@ -0,0 +1,79 @@
|
|||
<!-- 12 april 2019 -->
|
||||
|
||||
# Initialization and the Main Loop
|
||||
|
||||
## Overview
|
||||
|
||||
TODO
|
||||
|
||||
## Reference
|
||||
|
||||
### `uiInit()`
|
||||
|
||||
```c
|
||||
bool uiInit(void *options, uiInitError *err);
|
||||
```
|
||||
|
||||
`uiInit()` initializes libui. It returns `true` on success and `false` on failure; in the event of a failure, `err` is filled with relevant information explaining the failure.
|
||||
|
||||
`err` is required and must be properly initialized. It is a programmer error for `err` to be `NULL` or for `err->Size` to not match `sizeof (uiError)`, `uiInit()`. If any of the other fields of `err` are not zero-initialized as with `memset(0)`, the behavior is undefined.
|
||||
|
||||
`options` must be `NULL`; no options are currently defined. It is a programmer error for `options` to not be `NULL`.
|
||||
|
||||
It is a programmer error to call `uiInit()` more than once, even if `uiMain()` has already returned (meaning you cannot re-initialize libui). It is a programmer error to call libui functions before `uiInit()` has been called.
|
||||
|
||||
As part of initialization, the thread that `uiInit()` is called on becomes the GUI thread. All of the functions of libui except `uiQueueMain()` **must** be called from this thread. On platforms where which thread you run GUI code on is restricted, libui will behave in an undefined way if it is called from the wrong thread; to avoid this, you should always call `uiInit()` (and by extension all libui functions) from the thread that `main()` itself is called from.
|
||||
|
||||
If `uiInit()` fails, it will be a programmer error to call any other libui function. This means that you cannot use any of libui's message box functions to report the error.
|
||||
|
||||
**Notes for language binding authors**: Your language will likely provide its own preferred mechanism for reporting errors. You should wrap `uiInit()` to return errors this way, creating and managing the memory for `uiInitError` yourself and transforming the returned error according to both the format of `uiInitError` described below and the rules for encoding errors in your language of choice.
|
||||
|
||||
### `uiInitError`
|
||||
|
||||
```c
|
||||
typedef struct uiInitError uiInitError;
|
||||
struct uiInitError {
|
||||
size_t Size;
|
||||
char Message[256];
|
||||
};
|
||||
```
|
||||
|
||||
`uiInitError` describes an error returned by `uiInit()`.
|
||||
|
||||
You are responsible for allocating and initializing this struct. To do so, you simply zero the memory for this struct and set its `Size` field to `sizeof (uiInitError)`. The example in the main section of this page demonstrates how to do this.
|
||||
|
||||
In the event of an error, `Message` will contain a NUL-terminated C string in the encoding expected by `fprintf()`. This is in contrast to the rest of libui, which uses UTF-8 strings.
|
||||
|
||||
### `uiMain()`
|
||||
|
||||
```c
|
||||
void uiMain(void);
|
||||
```
|
||||
|
||||
`uiMain()` runs the main event loop. It does not return until `uiQuit()` is called.
|
||||
|
||||
It is a programmer error to call `uiMain()` more than once.
|
||||
|
||||
### `uiQuit()`
|
||||
|
||||
```c
|
||||
void uiQuit(void);
|
||||
```
|
||||
|
||||
`uiQuit()` causes `uiMain()` to return once the current event has finished processing.
|
||||
|
||||
It is an error to call `uiQuit()` before `uiMain()` is called or after `uiMain()` returns. It is a programmer error to call `uiQuit()` more than once.
|
||||
|
||||
### `uiQueueMain()`
|
||||
|
||||
```c
|
||||
void uiQueueMain(void (*f)(void *data), void *data);
|
||||
```
|
||||
|
||||
`uiQueueMain()` schedules `f` to be called with `data` as a parameter on the next iteration of the main loop when no event is pending. It returns immediately.
|
||||
|
||||
`uiQueueMain()` can safely be called from any thread, not just the UI thread.
|
||||
|
||||
If you call `uiQueueMain()` in sequence, the functions will execute in that order; if other threads are calling `uiQueueMain()` at the same time, there is no guarantee as to whether the calls are interleaved, but the order per-thread will be maintained.
|
||||
|
||||
If `uiQuit()` is called, it is undefined whether any still-queued functions will be called.
|
|
@ -0,0 +1,47 @@
|
|||
<!-- 10 june 2019 -->
|
||||
|
||||
# Controls on Unix systems other than macOS
|
||||
|
||||
## Overview
|
||||
|
||||
TODO
|
||||
|
||||
TODO talk about how each uiControl on Unix should have one GTK+ widget and that GTK+ composite widgets should be used directly for anything more complex
|
||||
|
||||
## Reference
|
||||
|
||||
### `uiControlOSVtable`
|
||||
|
||||
```c
|
||||
typedef struct uiControlOSVtable uiControlOSVtable;
|
||||
struct uiControlOSVtable {
|
||||
size_t Size;
|
||||
GtkWidget *(*Handle)(uiControl *c, void *implData);
|
||||
};
|
||||
```
|
||||
|
||||
`uiControlOSVtable` describes the set of functions that control implementations on Unix need to implement. When registering your control type, you pass this in as a parameter to `uiRegisterControlType()`. Each method here is required.
|
||||
|
||||
You are responsible for allocating and initializing this struct. To do so, you simply zero the memory for this struct and set its `Size` field to `sizeof (uiControlOSVtable)`. (TODO put this in a common place)
|
||||
|
||||
Each method takes at least two parameters. The first, `c`, is the `uiControl` itself. The second, `implData`, is the implementation data pointer; it is the same as the pointer returned by `uiControlImplData(c)`, and is provided here as a convenience.
|
||||
|
||||
Each method is named for the `uiUnixControl` function that it implements. As such, details on how to implement these methods are documented alongside those functions. For instance, instructions on implementing `Handle()` are given under the documentation for `uiUnixControlHandle()`.
|
||||
|
||||
### `uiUnixControlHandle()`
|
||||
|
||||
```objective-c
|
||||
uiprivExtern GtkWidget *uiUnixControlHandle(uiControl *c);
|
||||
```
|
||||
|
||||
`uiUnixControlHandle()` returns the GtkWidget that underpins `c`, or `NULL` if `c` does not have any underlying widget associated with it when called.
|
||||
|
||||
The widget returned by `uiUnixControlHandle()` is owned by `c`; you do not receive a reference to it at all. The widget is valid until either `c` is destroyed or until `c` decides to destroy the widget; you can handle the latter by catching TODO. In general, you should not store the returned widget pointer directly for later use, nor should you attempt to acquire a reference to the returned object. Instead, use the returned handle immediately if you have to, or follow TODO if you need to adjust properties of the handle that should persist across handle destruction/creation.
|
||||
|
||||
`uiWindow`s have a single handle of type `GtkWindow` that is created when the `uiWindow` is created and destroyed when the `uiWindow` is destroyed. Despite this, you should still follow the best practices described above.
|
||||
|
||||
For all other `uiControl`s defined by libui, the returned object is of the appropriate `GtkWidget` subclass:
|
||||
|
||||
* TODO
|
||||
|
||||
It is a programmer error to pass `NULL` for `c`. TODO a non-`uiControl`?
|
|
@ -0,0 +1,147 @@
|
|||
<!-- 8 april 2019 -->
|
||||
|
||||
# Using libui
|
||||
|
||||
>**Note**: This page discusses instructions for compiling and linking libui, and does so in the most general way possible. Your build system may provide better options for doing any of the following than what is described on this page; consult its manual.
|
||||
|
||||
## Shared vs. Static
|
||||
|
||||
In order to properly use libui, you first need to know whether you are using it as a shared library (also called a dynamically-linked library) or as a static library.
|
||||
|
||||
With a shared library, your application will need to ship with the libui shared library object, or require it at runtime (if it is to be installed via a system package manager). However, your program will only need to directly depend on libui itself.
|
||||
|
||||
With a static library, your application will ship with libui embedded within it, so no additional dependencies will be needed at runtime (apart from ones that require installation via a system package manager, such as GTK+). However, you need to do slightly more work to be able to actually build your application; exactly what is discussed on this page.
|
||||
|
||||
If you are using a language binding, the language binding will likely have already decided how it will use libui, and will provide its own instructions on what to do based on that decision.
|
||||
|
||||
## Including libui in Source Code
|
||||
|
||||
libui ships as a single header file, `ui.h`. Therefore, to include it in a C or C++ source file, all you need to do is
|
||||
|
||||
```c
|
||||
// If the libui headers are loaded in the same way as other system headers
|
||||
#include <ui.h>
|
||||
// OR
|
||||
// If the libui headers are included directly
|
||||
#include "path/to/ui.h"
|
||||
```
|
||||
|
||||
If you are using libui as a static library, you'll need to add the line
|
||||
|
||||
```c
|
||||
#define uiStatic
|
||||
```
|
||||
|
||||
*before* including `ui.h`, as that informs `ui.h` to tell the compiler that the functions in `ui.h` are not dynamically loaded. (You may, of course, also do this on your compiler's command line.)
|
||||
|
||||
### OS-Specific Headers
|
||||
|
||||
For most purposes, the above will be sufficient. However, if you need to do any OS-specific work with libui, there are a few more steps to take. Typically, this would be done if you either wanted to create a new control or access the underlying OS handles behind a control.
|
||||
|
||||
Each OS has a special OS-specific header that provides the necessary additional functions and constants. These must be included *after* `ui.h`. These must also be included *after* any OS headers:
|
||||
|
||||
#### Windows
|
||||
|
||||
The OS-specific header is `ui_windows.h`. The only OS header that is necessary is `<windows.h>`. You should also specify the Unicode, type-strictness, and minimum-version macros before including `<windows.h>`:
|
||||
|
||||
```c
|
||||
#define UNICODE
|
||||
#define _UNICODE
|
||||
#define STRICT
|
||||
#define STRICT_TYPED_ITEMIDS
|
||||
// This sets the minimum Windows API version to the minimum for libui: Windows Vista.
|
||||
// Feel free to make this higher if your code will not run on all versions libui supports.
|
||||
#define WINVER 0x0600
|
||||
#define _WIN32_WINNT 0x0600
|
||||
#define _WIN32_WINDOWS 0x0600
|
||||
#define _WIN32_IE 0x0700
|
||||
#define NTDDI_VERSION 0x06000000
|
||||
#include <windows.h>
|
||||
#include "ui.h"
|
||||
#include "ui_windows.h"
|
||||
```
|
||||
|
||||
#### Unix
|
||||
|
||||
The OS-specific header is `ui_unix.h`. Only `<gtk/gtk.h>` needs to be included beforehand:
|
||||
|
||||
```c
|
||||
#include <gtk/gtk.h>
|
||||
#include "ui.h"
|
||||
#include "ui_unix.h"
|
||||
```
|
||||
|
||||
TODO(andlabs): version constants
|
||||
|
||||
When actually running your compiler, you'll need to tell it where to look for GTK+. The preferred way to do so is via `pkg-config`. For instance:
|
||||
|
||||
```shell
|
||||
$ gcc -c -o program.o program.c `pkg-config --cflags gtk+-3.0`
|
||||
```
|
||||
|
||||
#### macOS
|
||||
|
||||
The OS-specific header is `ui_darwin.h`. Only `<Cocoa/Cocoa.h>` needs to be included beforehand:
|
||||
|
||||
```objective-c
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "ui.h"
|
||||
#import "ui_darwin.h"
|
||||
```
|
||||
|
||||
TODO(andlabs): is this really sufficient, or is only AppKit/Foundation necessary? Or just Foundation?
|
||||
|
||||
## Linking Against libui
|
||||
|
||||
How to link against libui depends on how you are using libui.
|
||||
|
||||
### Linking Against libui as a Shared Library
|
||||
|
||||
When linking a shared library, all you need is the shared library itself, and the compiler-specific mechanism for linking shared libraries applies. Note that for MSVC, this means you link against `libui.lib`, not `libui.dll`!
|
||||
|
||||
### Linking Against libui as a Static Library
|
||||
|
||||
Because static libraries are merely collections of object files, in order to link against libui as a static library, you will also need to link against libui's dependencies.
|
||||
|
||||
#### Windows
|
||||
|
||||
libui needs to be linked against a set of standard Windows DLLs, preferably in the order listed here. The syntax to use depends on the compiler; examples are listed for the first one, and all follow the same pattern.
|
||||
|
||||
- user32.dll (MSVC: `user32.lib`, MinGW: `-luser32`)
|
||||
- kernel32.dll
|
||||
- gdi32.dll
|
||||
- comctl32.dll
|
||||
- TODO
|
||||
|
||||
**Furthermore**, because libui requires Common Controls v6, you will also need to provide a manifest file that specifies Common Controls v6, and ideally also speciifes the versions of Windows your executable runs on. For instance, the one used by libui.dll is a good place to start:
|
||||
|
||||
```xml
|
||||
TODO
|
||||
```
|
||||
|
||||
Ideally, you should provide this as a resource that's linked into your program:
|
||||
|
||||
```rc
|
||||
// Extra #defines above elided for brevity; these should be included too.
|
||||
#include <windows.h>
|
||||
|
||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "manifest.xml"
|
||||
```
|
||||
|
||||
Pass this file into `rc` (MSVC)/`windres` (MinGW) and then pass the output into your linker.
|
||||
|
||||
With MinGW-w64, you must link as C++ (for instance, with `g++`), as libui is written in C++ on Windows.
|
||||
|
||||
#### Unix
|
||||
|
||||
At a minimum, you'll need to link against GTK+ itself. As with compiling, the preferred way to get the correct linker flags is with `pkg-config`. For instance:
|
||||
|
||||
```shell
|
||||
$ gcc -o program program.o `pkg-config --libs gtk+-3.0`
|
||||
```
|
||||
|
||||
Depending on your system, you'll also need to link against libm and libdl; your linker will complain in some way. For instance, if you see something like `DSO missing from command line`, add `-ldl` to make libdl explicit.
|
||||
|
||||
#### macOS
|
||||
|
||||
You'll need to link against the `Foundation` and `AppKit` frameworks. To do so, simply pass `-framework Foundation` and `-framework AppKit` to your linker command.
|
|
@ -0,0 +1,81 @@
|
|||
<!-- 22 may 2020 -->
|
||||
|
||||
# Window controls
|
||||
|
||||
## Overview
|
||||
|
||||
TODO
|
||||
|
||||
## Reference
|
||||
|
||||
### `uiWindow`
|
||||
|
||||
```c
|
||||
typedef uiControl uiWindow;
|
||||
uiprivExtern uint32_t uiWindowType(void);
|
||||
#define uiWindow(obj) ((uiWindow *) uiCheckControlType((obj), uiWindowType()))
|
||||
```
|
||||
|
||||
`uiWindow` is a `uiControl` that represents a window.
|
||||
|
||||
Windows are the highest level of a control hierarchy that is visibile on screen. All controls that are current visible are contained within a `uiWindow`, and it is the size and position of the window that ultimately decides their size and position (though a control's minimum size may contribute to this). Windows also have titles, which are used to identify the window to the user.
|
||||
|
||||
Windows are themselves controls, but behave slightly differently from other controls: they cannot be added as children of other controls (it is a programmer error to do so), they are hidden by default, and OS-level resources are created alongside the `uiWindow` object and survive for the duration of that object (see the OS-specific `uiControl` implementation notes for details).
|
||||
|
||||
`uiWindowType()` is the type identifier of a `uiWindow` as passed to `uiCheckControlType()`. You rarely need to call this directly; the `uiWindow()` conversion macro does this for you.
|
||||
|
||||
### `uiNewWindow()`
|
||||
|
||||
```c
|
||||
uiWindow *uiNewWindow(void);
|
||||
```
|
||||
|
||||
`uiWindow` creates a new window. The window will have the empty string as its title and will not have a child.
|
||||
|
||||
TODO other parameters?
|
||||
|
||||
### `uiWindowTitle()`
|
||||
|
||||
```c
|
||||
const char *uiWindowTitle(uiWindow *w);
|
||||
```
|
||||
|
||||
`uiWindowTitle()` returns `w`'s current title as a UTF-8 string.
|
||||
|
||||
The memory storing the title is owned by libui and should not be modified. The returned pointer is valid until the title is changed or `w` is destroyed; in general, you should not store the returned string pointer directly for later use.
|
||||
|
||||
It is a programmer error to pass `NULL` for `w`. TODO for this and all other functions: either don't bother doing this check or do a redundant uiControl type check as well...
|
||||
|
||||
### `uiWindowSetTitle()`
|
||||
|
||||
```c
|
||||
void uiWindowSetTitle(uiWindow *w, const char *title);
|
||||
```
|
||||
|
||||
`uiWindowSetTitle()` changes `w`'s title to `title`.
|
||||
|
||||
It is a programmer error to pass `NULL` for `title`. If `title` is not valid UTF-8, `U+FFFD` characters will be used to sanitize the string.
|
||||
|
||||
It is a programmer error to pass `NULL` for `w`.
|
||||
|
||||
### `uiWindowChild()`
|
||||
|
||||
```c
|
||||
uiprivExtern uiControl *uiWindowChild(uiWindow *w);
|
||||
```
|
||||
|
||||
`uiWindowChild()` returns the current child of `w`, or `NULL` if there is none.
|
||||
|
||||
It is a programmer error to pass `NULL` for `w`.
|
||||
|
||||
### `uiWindowSetChild()`
|
||||
|
||||
```c
|
||||
uiprivExtern void uiWindowSetChild(uiWindow *w, uiControl *child);
|
||||
```
|
||||
|
||||
`uiWindowSetChild()` sets the child control of `w` to `child`. If `child` is `NULL`, `w` will have its child removed, and the window will be empty. If `w` already has a child, then the child is seamlessly swapped out, and the current child will be free to add to another parent.
|
||||
|
||||
A window can only have one child control at a time, and that child will be given the entire area of the window. Multiple controls within a window are handled using the other container control types.
|
||||
|
||||
It is a programmer error to pass `NULL` for `w`. It is also a programmer error to pass a control for `child` that is already the child of some other control.
|
|
@ -0,0 +1,117 @@
|
|||
<!-- 10 june 2019 -->
|
||||
|
||||
# Controls on Windows
|
||||
|
||||
## Overview
|
||||
|
||||
TODO
|
||||
|
||||
## Reference
|
||||
|
||||
### `uiControlOSVtable`
|
||||
|
||||
```c
|
||||
typedef struct uiControlOSVtable uiControlOSVtable;
|
||||
struct uiControlOSVtable {
|
||||
size_t Size;
|
||||
HWND (*Handle)(uiControl *c, void *implData);
|
||||
HWND (*ParentHandleForChild)(uiControl *c, void *implData, uiControl *child);
|
||||
HRESULT (*SetControlPos)(uiControl *c, void *implData, const RECT *r);
|
||||
};
|
||||
```
|
||||
|
||||
`uiControlOSVtable` describes the set of functions that control implementations on Windows need to implement. When registering your control type, you pass this in as a parameter to `uiRegisterControlType()`. Each method here is required.
|
||||
|
||||
You are responsible for allocating and initializing this struct. To do so, you simply zero the memory for this struct and set its `Size` field to `sizeof (uiControlOSVtable)`. (TODO put this in a common place)
|
||||
|
||||
Each method takes at least two parameters. The first, `c`, is the `uiControl` itself. The second, `implData`, is the implementation data pointer; it is the same as the pointer returned by `uiControlImplData(c)`, and is provided here as a convenience.
|
||||
|
||||
Each method is named for the `uiWindowsControl` function that it implements. As such, details on how to implement these methods are documented alongside those functions. For instance, instructions on implementing `Handle()` are given under the documentation for `uiWindowsControlHandle()`.
|
||||
|
||||
### `uiWindowsControlHandle()`
|
||||
|
||||
```c
|
||||
uiprivExtern HWND uiWindowsControlHandle(uiControl *c);
|
||||
```
|
||||
|
||||
`uiWindowsControlHandle()` returns the window handle that underpins `c`, or `NULL` if `c` does not have any underlying window handle associated with it when called.
|
||||
|
||||
The window returned by `uiWindowsControlHandle()` is owned by `c`; you do not receive a reference to it at all. The window is valid until either `c` is destroyed or until `c` decides to destroy the window; you can handle the latter by catching TODO. In general, you should not store the returned window handle directly for later use. Instead, use the returned handle immediately if you have to, or follow TODO if you need to adjust properties of the handle that should persist across handle destruction/creation.
|
||||
|
||||
`uiWindow`s have a single window that is created when the `uiWindow` is created and destroyed when the `uiWindow` is destroyed. Despite this, you should still follow the best practices described above. The window is of a special libui-internal window class.
|
||||
|
||||
For all other `uiControl`s defined by libui, the returned window is of the appropriate window class:
|
||||
|
||||
* TODO
|
||||
|
||||
It is a programmer error to pass `NULL` for `c`. TODO a non-`uiControl`?
|
||||
|
||||
**For control implementations**: This function does the above programmer error checks and then calls your `Handle()` method. You do not need to repeat the check yourself.
|
||||
|
||||
### `uiWindowsControlParentHandle()`
|
||||
|
||||
```c
|
||||
uiprivExtern HWND uiWindowsControlParentHandle(uiControl *c);
|
||||
```
|
||||
|
||||
`uiWindowsControlParentHandle()` returns the parent handle for `c`, or `NULL` if there currently is no such handle (either because `c` has no parent control or because none of its parent controls have a handle).
|
||||
|
||||
This is the parent from the point of view of the Windows API. When creating the window handle for a uiControl, this is the handle to use as the `hwndParent`.
|
||||
|
||||
The value returned by this function is valid until the control's parent changes for any reason. TODO should not be stored anyway TODO use `ParentChanging()`/`ParentChanged()` to destroy/create (respectively) window handles TODO refer to the top of this page for the control model
|
||||
|
||||
It is a programmer error to pass `NULL` for `c`. TODO a non-`uiControl`?
|
||||
|
||||
**For control implementations**: This function does the above programmer error checks; you do not need to repeat the checks yourself.
|
||||
|
||||
Unlike the other functions that operate on a `uiControl`, this function actually calls the `ParentHandleForChild()` method of the *parent control* of `c`, passing `c` as the `child` argument. If your parent control is a container that has window handles to use as the parent handles of its children, you should return the approprpiate window handle. (As an example of a case where knowing the child is important, `uiTab` has a separate parent handle for each of its tab pages.)
|
||||
|
||||
If your parent control is a container that does not have window handles of its own, you should call `uiWindowsControlParentHandle()` on the parent control itself, which will cause libui to chain up until it has reached the top level:
|
||||
|
||||
```c
|
||||
static HWND controlParentHandleForChild(uiControl *c, void *implData, uiControl *child)
|
||||
{
|
||||
return uiWindowsControlParentHandle(c);
|
||||
}
|
||||
```
|
||||
|
||||
If your parent control is not a container, return `NULL`. TODO programmer error?
|
||||
|
||||
As libui ensures that the arguments to `ParentHandleForChild()` are actually related, you do not need to check that `child` is actually your child yourself.
|
||||
|
||||
### `uiWindowsControlSetControlPos()`
|
||||
|
||||
```c
|
||||
uiprivExtern HRESULT uiWindowsControlSetControlPos(uiControl *c, const RECT *r);
|
||||
```
|
||||
|
||||
`uiWindowsControlSetControlPos()` causes `c` to be moved and resized to fill `r`. `r` must be in the *client* coordinates of `c`'s parent handle.
|
||||
|
||||
This function should be called by container implementations to reposition its children, either in response to a window being resized or when children need to be laid out due to some change (such as visibility). Users should not call this function directly.
|
||||
|
||||
It returns `S_OK` if the resize succeeded or some error if the resize failed *from the perspective of the OS*. It will not return an error in the event of a libui-specific programmer or internal error of some other sort. This error return is only intended for libui-internal use; see the control implementation details below.
|
||||
|
||||
It is a programmer error to pass `NULL` for `c` or `r`. It is also a programmer error to call `uiWindowsControlSetControlPos()` on a `uiWindow`.
|
||||
|
||||
**For control implementations**: This function calls your `SetControlPos()` method. For a simple control with a single window handle, the method should do nothing but call `uiWindowsSetControlHandlePos()` and return its return value:
|
||||
|
||||
```c
|
||||
static HWND controlSetControlPos(uiControl *c, void *implData, RECT *r)
|
||||
{
|
||||
controlImplData *ci = (controlImplData *) implData;
|
||||
|
||||
return uiWindowsSetControlHandlePos(ci->hwnd, r);
|
||||
}
|
||||
```
|
||||
|
||||
If your control is a container, then your method should call this function recursively, passing an appropriate `RECT` for each of its children, returning the first error it finds (if any). If those children have a different parent handle from your own, then you will need ot make sure those `RECT`s are in the *client* coordinates of those parent handles. Return any error as soon as possible, to minimize ill effects (and to prepare for a potential switch to using `DeferWindowPos()` and friends instead).
|
||||
|
||||
You can also use this function to initiate a reposition of any children in the even that their layout has changed. In this case, TODO how to handle errors TODO something here about saving r
|
||||
|
||||
### `uiWindowsSetControlHandlePos()`
|
||||
|
||||
```c
|
||||
uiprivExtern HRESULT uiWindowsSetControlHandlePos(HWND hwnd, const RECT *r);
|
||||
```
|
||||
|
||||
TODO
|
|
@ -0,0 +1,42 @@
|
|||
// 18 january 2020
|
||||
#include "uipriv_haiku.hpp"
|
||||
|
||||
bool uiprivOSVtableValid(const char *name, const uiControlOSVtable *osVtable, const char *func)
|
||||
{
|
||||
if (osVtable->Size != sizeof (uiControlOSVtable)) {
|
||||
uiprivProgrammerErrorWrongStructSize(osVtable->Size, "uiControlOSVtable", func);
|
||||
return false;
|
||||
}
|
||||
#define checkMethod(method) \
|
||||
if (osVtable->method == NULL) { \
|
||||
uiprivProgrammerErrorRequiredControlMethodMissing(name, "uiControlOSVtable", #method, func); \
|
||||
return 0; \
|
||||
}
|
||||
checkMethod(Handle)
|
||||
return true;
|
||||
}
|
||||
|
||||
uiControlOSVtable *uiprivCloneOSVtable(const uiControlOSVtable *osVtable)
|
||||
{
|
||||
uiControlOSVtable *v2;
|
||||
|
||||
v2 = uiprivNew(uiControlOSVtable);
|
||||
*v2 = *osVtable;
|
||||
return v2;
|
||||
}
|
||||
|
||||
#define callVtable(method, ...) ((*(method))(__VA_ARGS__))
|
||||
|
||||
void *uiHaikuControlHandle(uiControl *c)
|
||||
{
|
||||
uiControlOSVtable *osVtable;
|
||||
|
||||
if (!uiprivCheckInitializedAndThread())
|
||||
return NULL;
|
||||
if (c == NULL) {
|
||||
uiprivProgrammerErrorNullPointer("uiControl", uiprivFunc);
|
||||
return NULL;
|
||||
}
|
||||
osVtable = uiprivControlOSVtable(c);
|
||||
return callVtable(osVtable->Handle, c, uiControlImplData(c));
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// 12 january 2020
|
||||
#include "uipriv_haiku.hpp"
|
||||
|
||||
uiprivApplication *uiprivApp;
|
||||
|
||||
#define uiprivInitReturnStatus(err, msg, status) uiprivInitReturnErrorf(err, "%s: %" uiprivStatustFmt, msg, status)
|
||||
|
||||
static thread_id mainThread;
|
||||
|
||||
bool uiprivSysInit(void *options, uiInitError *err)
|
||||
{
|
||||
status_t status;
|
||||
|
||||
uiprivApp = new uiprivApplication("application/libui.TODO", &status);
|
||||
if (status != B_OK)
|
||||
return uiprivInitReturnStatus(err, "error creating BApplication", status);
|
||||
mainThread = find_thread(NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
void uiprivSysMain(void)
|
||||
{
|
||||
uiprivApp->Run();
|
||||
}
|
||||
|
||||
void uiprivSysQuit(void)
|
||||
{
|
||||
uiprivApp->Quit();
|
||||
}
|
||||
|
||||
struct queueMainArgs {
|
||||
void (*f)(void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
// note: msg is owned by the BLooper base class and should not be freed by us
|
||||
void uiprivApplication::MessageReceived(BMessage *msg)
|
||||
{
|
||||
const void *data;
|
||||
const struct queueMainArgs *args;
|
||||
ssize_t size;
|
||||
status_t status;
|
||||
|
||||
switch (msg->what) {
|
||||
case uiprivMsgQueueMain:
|
||||
status = msg->FindData("args", B_ANY_TYPE,
|
||||
&data, &size);
|
||||
if (status != B_OK) {
|
||||
uiprivInternalError("BMessage::FindData() failed in uiprivApplication::MessageReceived() for uiQueueMain(): %" uiprivStatustFmt, status);
|
||||
return;
|
||||
}
|
||||
args = (const struct queueMainArgs *) data;
|
||||
(*(args->f))(args->data);
|
||||
return;
|
||||
}
|
||||
BApplication::MessageReceived(msg);
|
||||
}
|
||||
|
||||
void uiprivSysQueueMain(void (*f)(void *data), void *data)
|
||||
{
|
||||
BMessage *msg;
|
||||
struct queueMainArgs args;
|
||||
status_t status;
|
||||
|
||||
args.f = f;
|
||||
args.data = data;
|
||||
msg = new BMessage(uiprivMsgQueueMain);
|
||||
status = msg->AddData("args", B_RAW_TYPE,
|
||||
&args, sizeof (struct queueMainArgs), true, 1);
|
||||
if (status != B_OK) {
|
||||
uiprivInternalError("BMessage::AddData() failed in uiQueueMain(): %" uiprivStatustFmt, status);
|
||||
delete msg;
|
||||
return;
|
||||
}
|
||||
status = uiprivApp->PostMessage(msg);
|
||||
// msg is copied by PostMessage() so we can delete it here
|
||||
delete msg;
|
||||
if (status != B_OK)
|
||||
uiprivInternalError("BApplication::PostMessage() failed in uiQueueMain(): %" uiprivStatustFmt, status);
|
||||
}
|
||||
|
||||
bool uiprivSysCheckThread(void)
|
||||
{
|
||||
return find_thread(NULL) == mainThread;
|
||||
}
|
||||
|
||||
void uiprivReportError(const char *prefix, const char *msg, const char *suffix, bool internal)
|
||||
{
|
||||
fprintf(stderr, "*** %s: %s. %s\n", prefix, msg, suffix);
|
||||
debugger("TODO");
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
# 12 january 2020
|
||||
|
||||
libui_sources += [
|
||||
'haiku/controls.cpp',
|
||||
'haiku/main.cpp',
|
||||
'haiku/window.cpp',
|
||||
]
|
||||
|
||||
libui_deps += [
|
||||
meson.get_compiler('cpp').find_library('root',
|
||||
required: true),
|
||||
meson.get_compiler('cpp').find_library('be',
|
||||
required: true),
|
||||
]
|
||||
libui_soversion = '0'
|
||||
# TODO possible meson bug? Haiku shouldn't have an rpath, right?...
|
|
@ -0,0 +1,21 @@
|
|||
// 12 january 2020
|
||||
#include <os/AppKit.h>
|
||||
#include <os/InterfaceKit.h>
|
||||
#include <os/KernelKit.h>
|
||||
#include <os/SupportKit.h>
|
||||
#define uiprivOSHeader "../ui_haiku.h"
|
||||
#include "../common/uipriv.h"
|
||||
|
||||
// B_PRId32 is the correct format for status_t; see, for instance, https://review.haiku-os.org/c/haiku/+/2171/3/src/system/boot/platform/u-boot/start.cpp
|
||||
// TODO consider using strings instead (we're limited to POSIX's ill-designed strerror() APIs unless we provide a forever-truncating version of strerror_r())
|
||||
#define uiprivStatustFmt B_PRId32
|
||||
|
||||
constexpr uint32 uiprivMsgQueueMain = 'uiQM';
|
||||
|
||||
// main.cpp
|
||||
class uiprivApplication : public BApplication {
|
||||
public:
|
||||
using BApplication::BApplication;
|
||||
virtual void MessageReceived(BMessage *msg) override;
|
||||
};
|
||||
extern uiprivApplication *uiprivApp;
|
|
@ -0,0 +1,115 @@
|
|||
// 31 may 2020
|
||||
#include "uipriv_haiku.hpp"
|
||||
|
||||
uiControl *uiprivSysWindowChild(uiWindow *w)
|
||||
{
|
||||
// TODO
|
||||
return NULL;
|
||||
}
|
||||
void uiprivSysWindowSetChild(uiWindow *w, uiControl *child)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
struct windowImplData {
|
||||
BWindow *window;
|
||||
char *title;
|
||||
};
|
||||
|
||||
static bool windowInit(uiControl *c, void *implData, void *initData)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) implData;
|
||||
|
||||
// TODO figure out what the correct BRect is
|
||||
wi->window = new BWindow(BRect(0, 0, 0, 0), "",
|
||||
B_TITLED_WINDOW,
|
||||
// TODO B_AUTO_UPDATE_SIZE_LIMITS?
|
||||
B_ASYNCHRONOUS_CONTROLS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void windowFree(uiControl *c, void *implData)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) implData;
|
||||
|
||||
if (wi->title != NULL) {
|
||||
uiprivFreeUTF8(wi->title);
|
||||
wi->title = NULL;
|
||||
}
|
||||
delete wi->window;
|
||||
}
|
||||
|
||||
static void windowParentChanging(uiControl *c, void *implData, uiControl *oldParent)
|
||||
{
|
||||
uiprivProgrammerErrorCannotHaveWindowsAsChildren();
|
||||
}
|
||||
|
||||
static void windowParentChanged(uiControl *c, void *implData, uiControl *newParent)
|
||||
{
|
||||
uiprivProgrammerErrorCannotHaveWindowsAsChildren();
|
||||
}
|
||||
|
||||
static void *windowHandle(uiControl *c, void *implData)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) implData;
|
||||
|
||||
return wi->window;
|
||||
}
|
||||
|
||||
// gotta do this because of lack of C99-style initializers in C++11
|
||||
// see also https://stackoverflow.com/questions/11516657/c-structure-initialization
|
||||
static const uiControlVtable windowVtable = [](void) {
|
||||
uiControlVtable vt;
|
||||
|
||||
memset(&vt, 0, sizeof (uiControlVtable));
|
||||
vt.Size = sizeof (uiControlVtable);
|
||||
vt.Init = windowInit;
|
||||
vt.Free = windowFree;
|
||||
vt.ParentChanging = windowParentChanging;
|
||||
vt.ParentChanged = windowParentChanged;
|
||||
return vt;
|
||||
}();
|
||||
|
||||
static const uiControlOSVtable windowOSVtable = [](void) {
|
||||
uiControlOSVtable vt;
|
||||
|
||||
memset(&vt, 0, sizeof (uiControlOSVtable));
|
||||
vt.Size = sizeof (uiControlOSVtable);
|
||||
vt.Handle = windowHandle;
|
||||
return vt;
|
||||
}();
|
||||
|
||||
static uint32_t windowType = 0;
|
||||
|
||||
uint32_t uiprivSysWindowType(void)
|
||||
{
|
||||
if (windowType == 0)
|
||||
windowType = uiRegisterControlType("uiWindow", &windowVtable, &windowOSVtable, sizeof (struct windowImplData));
|
||||
return windowType;
|
||||
}
|
||||
|
||||
uiWindow *uiprivSysNewWindow(void)
|
||||
{
|
||||
return (uiWindow *) uiNewControl(uiWindowType(), NULL);
|
||||
}
|
||||
|
||||
const char *uiprivSysWindowTitle(uiWindow *w)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) uiControlImplData(uiControl(w));
|
||||
|
||||
if (wi->title == NULL)
|
||||
// TODO replace this with a dedicated UTF-8 empty string object
|
||||
return "";
|
||||
return wi->title;
|
||||
}
|
||||
|
||||
void uiprivSysWindowSetTitle(uiWindow *w, const char *title)
|
||||
{
|
||||
struct windowImplData *wi = (struct windowImplData *) uiControlImplData(uiControl(w));
|
||||
|
||||
if (wi->title != NULL)
|
||||
uiprivFreeUTF8(wi->title);
|
||||
wi->title = uiprivSanitizeUTF8(title);
|
||||
wi->window->SetTitle(wi->title);
|
||||
}
|
22
meson.build
22
meson.build
|
@ -2,7 +2,7 @@
|
|||
|
||||
# TODO I'm not sure how to allow building 32-bit instead of 64-bit with meson
|
||||
|
||||
# TODO remove cpp from this list once https://github.com/mesonbuild/meson/issues/5181 is settled; move it to the OS checks and under cpp-multithread
|
||||
# TODO remove cpp from this list once https://github.com/mesonbuild/meson/issues/5181 is settled; move it to the OS checks and under test and cpp-multithread
|
||||
project('libui', ['c', 'cpp'],
|
||||
meson_version: '>=0.48.0',
|
||||
default_options: [
|
||||
|
@ -96,6 +96,11 @@ else
|
|||
]
|
||||
|
||||
if libui_OS == 'windows'
|
||||
# -pedantic causes warnings without this, even if a function is explicitly marked __attribute__((format(ms_printf)))
|
||||
# references: https://curl.haxx.se/mail/lib-2012-06/0433.html
|
||||
libui_project_compile_args += [
|
||||
'-Wno-pedantic-ms-format',
|
||||
]
|
||||
# don't require shipping the MinGW-w64 DLLs
|
||||
libui_project_link_args += [
|
||||
'-static',
|
||||
|
@ -109,8 +114,8 @@ endif
|
|||
# TODO (after the above TODO is resolved) move that below the part below that actually adds these arguments
|
||||
libui_manifest_args = []
|
||||
if libui_mode == 'static'
|
||||
libui_project_compile_args += ['-D_UI_STATIC']
|
||||
libui_manifest_args = ['-D_UI_STATIC']
|
||||
libui_project_compile_args += ['-DuiStatic']
|
||||
libui_manifest_args = ['-DuiStatic']
|
||||
endif
|
||||
|
||||
add_project_arguments(libui_project_compile_args,
|
||||
|
@ -147,6 +152,9 @@ if libui_OS == 'windows'
|
|||
elif libui_OS == 'darwin'
|
||||
subdir('darwin')
|
||||
install_headers('ui_darwin.h')
|
||||
elif libui_OS == 'haiku'
|
||||
subdir('haiku')
|
||||
install_headers('ui_haiku.h')
|
||||
else
|
||||
subdir('unix')
|
||||
install_headers('ui_unix.h')
|
||||
|
@ -158,9 +166,9 @@ libui_libui = library('ui', libui_sources,
|
|||
name_prefix: 'lib', # always call it libui, even in Windows DLLs
|
||||
install: true,
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
c_args: ['-Dlibui_EXPORTS'],
|
||||
cpp_args: ['-Dlibui_EXPORTS'],
|
||||
objc_args: ['-Dlibui_EXPORTS'],
|
||||
c_args: ['-DuiprivBuildingLibui'],
|
||||
cpp_args: ['-DuiprivBuildingLibui'],
|
||||
objc_args: ['-DuiprivBuildingLibui'],
|
||||
link_args: libui_libui_link_args,
|
||||
soversion: libui_soversion,
|
||||
darwin_versions: []) # TODO
|
||||
|
@ -173,4 +181,4 @@ if libui_mode == 'static'
|
|||
libui_binary_deps = libui_deps
|
||||
endif
|
||||
subdir('test')
|
||||
subdir('examples')
|
||||
#subdir('examples')
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
- figure out how to tell meson to look here for changes, if I need to
|
||||
- document this properly (I should also write development documentation for all of libui in general...)
|
||||
- note that none of these files should have include guards
|
||||
- figure out if we can macro-ize macro names (likely not)
|
|
@ -0,0 +1,17 @@
|
|||
// 30 may 2019
|
||||
|
||||
#include "start.h"
|
||||
|
||||
extern void *sharedbitsPrefixName(Alloc)(size_t n, const char *what);
|
||||
extern void *sharedbitsPrefixName(Realloc)(void *p, size_t nOld, size_t nNew, const char *what);
|
||||
extern void sharedbitsPrefixName(Free)(void *p);
|
||||
|
||||
/*
|
||||
you may also want to define the following:
|
||||
|
||||
#define sharedbitsPrefixName(New)(T) ((T *) sharedbitsPrefix ## Alloc(sizeof (T), #T))
|
||||
#define sharedbitsPrefixName(NewArray)(T, n) ((T *) sharedbitsPrefix ## Alloc(n * sizeof (T), #T "[]"))
|
||||
#define sharedbitsPrefixName(ResizeArray)(x, T, old, new) ((T *) sharedbitsPrefix ## Realloc(x, old * sizeof (T), new * sizeof (T), #T "[]"))
|
||||
*/
|
||||
|
||||
#include "end.h"
|
|
@ -0,0 +1,41 @@
|
|||
// 30 may 2019
|
||||
// requires: alloc_header.h
|
||||
|
||||
#include "start.h"
|
||||
|
||||
#include "printfwarn_header.h"
|
||||
sharedbitsPrintfFunc(
|
||||
extern void sharedbitsPrefixName(InternalError)(const char *fmt, ...),
|
||||
1, 2);
|
||||
|
||||
void *sharedbitsPrefixName(Alloc)(size_t n, const char *what)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = malloc(n);
|
||||
if (p == NULL) {
|
||||
sharedbitsPrefixName(InternalError)("memory exhausted allocating %s", what);
|
||||
abort(); // TODO handle more gracefully somehow
|
||||
}
|
||||
memset(p, 0, n);
|
||||
return p;
|
||||
}
|
||||
|
||||
void *sharedbitsPrefixName(Realloc)(void *p, size_t nOld, size_t nNew, const char *what)
|
||||
{
|
||||
p = realloc(p, nNew);
|
||||
if (p == NULL) {
|
||||
sharedbitsPrefixName(InternalError)("memory exhausted reallocating %s", what);
|
||||
abort(); // TODO handle more gracefully somehow
|
||||
}
|
||||
if (nNew > nOld)
|
||||
memset(((uint8_t *) p) + nOld, 0, nNew - nOld);
|
||||
return p;
|
||||
}
|
||||
|
||||
void sharedbitsPrefixName(Free)(void *p)
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
|
||||
#include "end.h"
|
|
@ -0,0 +1,35 @@
|
|||
// 30 may 2019
|
||||
// requires: alloc_header.h
|
||||
|
||||
#include "start.h"
|
||||
|
||||
typedef struct sharedbitsPrefixName(Array) sharedbitsPrefixName(Array);
|
||||
|
||||
struct sharedbitsPrefixName(Array) {
|
||||
void *buf;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
size_t elemsize;
|
||||
size_t nGrow;
|
||||
const char *what;
|
||||
};
|
||||
|
||||
extern void sharedbitsPrefixName(ArrayInitFull)(sharedbitsPrefixName(Array) *arr, size_t elemsize, size_t nGrow, const char *what);
|
||||
extern void sharedbitsPrefixName(ArrayFreeFull)(sharedbitsPrefixName(Array) *arr);
|
||||
extern void *sharedbitsPrefixName(ArrayAppend)(sharedbitsPrefixName(Array) *arr, size_t n);
|
||||
extern void *sharedbitsPrefixName(ArrayInsertAt)(sharedbitsPrefixName(Array) *arr, size_t pos, size_t n);
|
||||
extern void sharedbitsPrefixName(ArrayDelete)(sharedbitsPrefixName(Array) *arr, size_t pos, size_t n);
|
||||
extern void sharedbitsPrefixName(ArrayDeleteItem)(sharedbitsPrefixName(Array) *arr, void *p, size_t n);
|
||||
extern void *sharedbitsPrefixName(ArrayBsearch)(const sharedbitsPrefixName(Array) *arr, const void *key, int (*compare)(const void *, const void *));
|
||||
extern void sharedbitsPrefixName(ArrayQsort)(sharedbitsPrefixName(Array) *arr, int (*compare)(const void *, const void *));
|
||||
|
||||
/*
|
||||
you may also want to define the following:
|
||||
|
||||
#define sharedbitsPrefixName(StaticInit)(T, nGrow, what) { NULL, 0, 0, sizeof (T), nGrow, what }
|
||||
#define sharedbitsPrefixName(Init)(arr, T, nGrow, what) sharedbitsPrefixName(InitFull)(&(arr), sizeof (T), nGrow, what)
|
||||
#define sharedbitsPrefixName(ArrayFree)(arr) sharedbitsPrefixName(ArrayFreeFull)(&(arr))
|
||||
#define sharedbitsPrefixName(ArrayAt)(arr, T, n) (((T *) (arr.buf)) + (n))
|
||||
*/
|
||||
|
||||
#include "end.h"
|
|
@ -0,0 +1,91 @@
|
|||
// 30 may 2019
|
||||
// requires: array_header.h
|
||||
|
||||
#include "start.h"
|
||||
|
||||
void sharedbitsPrefixName(ArrayInitFull)(sharedbitsPrefixName(Array) *arr, size_t elemsize, size_t nGrow, const char *what)
|
||||
{
|
||||
memset(arr, 0, sizeof (sharedbitsPrefixName(Array)));
|
||||
arr->elemsize = elemsize;
|
||||
arr->nGrow = nGrow;
|
||||
arr->what = what;
|
||||
}
|
||||
|
||||
void sharedbitsPrefixName(ArrayFreeFull)(sharedbitsPrefixName(Array) *arr)
|
||||
{
|
||||
sharedbitsPrefixName(Free)(arr->buf);
|
||||
memset(arr, 0, sizeof (sharedbitsPrefixName(Array)));
|
||||
}
|
||||
|
||||
void *sharedbitsPrefixName(ArrayAppend)(sharedbitsPrefixName(Array) *arr, size_t n)
|
||||
{
|
||||
return sharedbitsPrefixName(ArrayInsertAt)(arr, arr->len, n);
|
||||
}
|
||||
|
||||
void *sharedbitsPrefixName(ArrayInsertAt)(sharedbitsPrefixName(Array) *arr, size_t pos, size_t n)
|
||||
{
|
||||
uint8_t *old, *new;
|
||||
size_t nBytesAdded, nBytesRemaining;
|
||||
|
||||
if ((arr->len + n) >= arr->cap) {
|
||||
size_t nGrow;
|
||||
|
||||
// always grow by a perfect multiple of arr->nGrow
|
||||
nGrow = n + (arr->nGrow - (n % arr->nGrow));
|
||||
arr->buf = sharedbitsPrefixName(Realloc)(arr->buf,
|
||||
arr->cap * arr->elemsize,
|
||||
(arr->cap + nGrow) * arr->elemsize,
|
||||
arr->what);
|
||||
arr->cap += nGrow;
|
||||
}
|
||||
|
||||
nBytesRemaining = (arr->len - pos) * arr->elemsize;
|
||||
nBytesAdded = n * arr->elemsize;
|
||||
new = ((uint8_t *) (arr->buf)) + (pos * arr->elemsize);
|
||||
old = new + nBytesAdded;
|
||||
memmove(old, new, nBytesRemaining);
|
||||
memset(new, 0, nBytesAdded);
|
||||
|
||||
arr->len += n;
|
||||
return new;
|
||||
}
|
||||
|
||||
void sharedbitsPrefixName(ArrayDelete)(sharedbitsPrefixName(Array) *arr, size_t pos, size_t n)
|
||||
{
|
||||
uint8_t *src, *dest;
|
||||
size_t nBytesDeleted, nBytesRemaining;
|
||||
|
||||
nBytesDeleted = n * arr->elemsize;
|
||||
nBytesRemaining = (arr->len - pos - n) * arr->elemsize;
|
||||
dest = ((uint8_t *) (arr->buf)) + (pos * arr->elemsize);
|
||||
src = dest + nBytesDeleted;
|
||||
memmove(dest, src, nBytesRemaining);
|
||||
src = dest + nBytesRemaining;
|
||||
memset(src, 0, nBytesDeleted);
|
||||
arr->len -= n;
|
||||
}
|
||||
|
||||
void sharedbitsPrefixName(ArrayDeleteItem)(sharedbitsPrefixName(Array) *arr, void *p, size_t n)
|
||||
{
|
||||
uint8_t *p8, *buf8;
|
||||
|
||||
p8 = (uint8_t *) p;
|
||||
buf8 = (uint8_t *) (arr->buf);
|
||||
// TODO write this in a way that doesn't mix ptrdiff_t and size_t
|
||||
sharedbitsPrefixName(ArrayDelete)(arr, (p8 - buf8) / arr->elemsize, n);
|
||||
}
|
||||
|
||||
void *sharedbitsPrefixName(ArrayBsearch)(const sharedbitsPrefixName(Array) *arr, const void *key, int (*compare)(const void *, const void *))
|
||||
{
|
||||
if (arr->len == 0)
|
||||
return NULL;
|
||||
return bsearch(key, arr->buf, arr->len, arr->elemsize, compare);
|
||||
}
|
||||
|
||||
void sharedbitsPrefixName(ArrayQsort)(sharedbitsPrefixName(Array) *arr, int (*compare)(const void *, const void *))
|
||||
{
|
||||
if (arr->len != 0)
|
||||
qsort(arr->buf, arr->len, arr->elemsize, compare);
|
||||
}
|
||||
|
||||
#include "end.h"
|
|
@ -0,0 +1,5 @@
|
|||
// 30 may 2019
|
||||
|
||||
#undef sharedbitsPrefixName
|
||||
#undef sharedbitsPrefixExpandMakeName
|
||||
#undef sharedbitsPrefixMakeName
|
|
@ -0,0 +1,14 @@
|
|||
// 1 june 2019
|
||||
|
||||
#ifdef __GNUC__
|
||||
#ifdef _WIN32
|
||||
#define sharedbitsPrintfFunc(decl, fmtpos, appos) \
|
||||
decl __attribute__((format(ms_printf, fmtpos, appos)))
|
||||
#else
|
||||
#define sharedbitsPrintfFunc(decl, fmtpos, appos) \
|
||||
decl __attribute__((format(printf, fmtpos, appos)))
|
||||
#endif
|
||||
#else
|
||||
#define sharedbitsPrintfFunc(decl, fmtpos, appos) \
|
||||
decl
|
||||
#endif
|
|
@ -0,0 +1,8 @@
|
|||
// 30 may 2019
|
||||
|
||||
#ifndef sharedbitsPrefix
|
||||
#error you must define sharedbitsPrefix before including this
|
||||
#endif
|
||||
#define sharedbitsPrefixMakeName(x, y) x ## y
|
||||
#define sharedbitsPrefixExpandMakeName(x, y) sharedbitsPrefixMakeName(x, y)
|
||||
#define sharedbitsPrefixName(Name) sharedbitsPrefixExpandMakeName(sharedbitsPrefix, Name)
|
|
@ -0,0 +1,8 @@
|
|||
// 8 june 2019
|
||||
// requires: alloc_header.h
|
||||
|
||||
#include "start.h"
|
||||
|
||||
extern char *sharedbitsPrefixName(Strdup)(const char *s);
|
||||
|
||||
#include "end.h"
|
|
@ -0,0 +1,17 @@
|
|||
// 8 june 2019
|
||||
// requires alloc_header.h and any of the strsafe_* files
|
||||
|
||||
#include "start.h"
|
||||
|
||||
char *sharedbitsPrefixName(Strdup)(const char *s)
|
||||
{
|
||||
char *t;
|
||||
size_t n;
|
||||
|
||||
n = strlen(s);
|
||||
t = (char *) sharedbitsPrefixName(Alloc)((n + 1) * sizeof (char), "char[]");
|
||||
sharedbitsPrefixName(Strncpy)(t, s, n + 1);
|
||||
return t;
|
||||
}
|
||||
|
||||
#include "end.h"
|
|
@ -0,0 +1,8 @@
|
|||
// 31 may 2019
|
||||
|
||||
#include "start.h"
|
||||
|
||||
extern int sharedbitsPrefixName(Vsnprintf)(char *s, size_t n, const char *fmt, va_list ap);
|
||||
extern char *sharedbitsPrefixName(Strncpy)(char *dest, const char *src, size_t n);
|
||||
|
||||
#include "end.h"
|
|
@ -0,0 +1,73 @@
|
|||
// 31 may 2019
|
||||
// only requires strsafe_header.h if you don't define sharedbitsStatic as static
|
||||
|
||||
#include "start.h"
|
||||
|
||||
#ifdef sharedbitsStatic
|
||||
sharedbitsStatic
|
||||
#endif
|
||||
int sharedbitsPrefixName(Vsnprintf)(char *s, size_t n, const char *fmt, va_list ap)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int ret;
|
||||
|
||||
if (s == NULL && n == 0)
|
||||
return _vscprintf(fmt, ap);
|
||||
// TODO figure out how to disambiguate between encoding errors (returns negative value; does not have documented errno values), other errors (returns negative value; errno == EINVAL), and truncations (returns -1; does not have documented errno values)
|
||||
ret = vsnprintf_s(s, n, _TRUNCATE, fmt, ap);
|
||||
if (ret == -1)
|
||||
// TODO make this safe (by having these functions return size_t? I forget now...)
|
||||
return (int) n;
|
||||
return ret;
|
||||
#else
|
||||
return vsnprintf(s, n, fmt, ap);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef sharedbitsInternalError
|
||||
#define sharedbitsprivInternalError sharedbitsInternalError
|
||||
#else
|
||||
#define sharedbitsprivInternalError sharedbitsPrefixName(InternalError)
|
||||
#include "printfwarn_header.h"
|
||||
#ifdef sharedbitsStatic
|
||||
sharedbitsStatic
|
||||
#else
|
||||
extern
|
||||
#endif
|
||||
sharedbitsPrintfFunc(
|
||||
void sharedbitsprivInternalError(const char *fmt, ...),
|
||||
1, 2);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef sharedbitsStatic
|
||||
sharedbitsStatic
|
||||
#endif
|
||||
char *sharedbitsPrefixName(Strncpy)(char *dest, const char *src, size_t n)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
errno_t err;
|
||||
|
||||
// because strncpy_s() doesn't do this
|
||||
memset(dest, '\0', n * sizeof (char));
|
||||
err = strncpy_s(dest, n, src, _TRUNCATE);
|
||||
if (err != 0 && err != STRUNCATE) {
|
||||
// Yes folks, apparently strerror() is unsafe (it's not reentrant, but that's not the point of the MSVC security functions; that's about buffer overflows, and as you'll soon see there really is no need for what the "safe' version is given reentrancy concerns), and not only that, but the replacement, strerror_s(), requires copying and allocation! it's almost like they were TRYING to shove as many error conditions as possible in!
|
||||
// Oh, and you can't just use _sys_errlist[] to bypass this, because even that has a deprecation warning, telling you to use strerror() instead, which in turn sends you back to strerror_s()!
|
||||
// Of course, the fact _sys_errlist[] is a thing and that it's deprecated out of security and not reentrancy shows that the error strings returned by strerror()/strerror_s() are static and unchanging throughout the lifetime of the program, so a truly reentrant strerror_s() would just return the raw const string array directly, or a placeholder like "unknown error" otherwise, but that would be too easy!
|
||||
// And even better, there's no way to get the length of the error message, so you can't even dynamically allocate a large enough buffer if you wanted to!
|
||||
// (Furthermore, cppreference.com says there's strerrorlen_s(), but a) fuck C11, and b) MSDN does not concur.)
|
||||
// So, alas, you'll have to live with just having the error code; sorry.
|
||||
sharedbitsprivInternalError("error calling strncpy_s(): %d", err);
|
||||
return 0;
|
||||
}
|
||||
return dest;
|
||||
#else
|
||||
return strncpy(dest, src, n);
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef sharedbitsprivInternalError
|
||||
|
||||
#include "end.h"
|
|
@ -0,0 +1,25 @@
|
|||
// 28 may 2019
|
||||
|
||||
// This file should NOT have include guards as it is intended to be included more than once; see noinitwrongthread.c for details.
|
||||
|
||||
allcallsCase(uiMain, /* no arguments */)
|
||||
allcallsCase(uiQuit, /* no arguments */)
|
||||
// uiQueueMain() is defined in all files explicitly since it isn't instantiated for all possible allcalls tests
|
||||
|
||||
allcallsCase(uiControlType, /* no arguments */)
|
||||
|
||||
allcallsCase(uiRegisterControlType, NULL, NULL, NULL, 0)
|
||||
allcallsCase(uiCheckControlType, NULL, 0)
|
||||
|
||||
allcallsCase(uiNewControl, 0, NULL)
|
||||
allcallsCase(uiControlFree, NULL)
|
||||
allcallsCase(uiControlSetParent, NULL, NULL)
|
||||
allcallsCase(uiControlImplData, NULL)
|
||||
|
||||
allcallsCase(uiWindowType, /* no arguments */)
|
||||
|
||||
allcallsCase(uiNewWindow, /* no arguments */)
|
||||
allcallsCase(uiWindowTitle, NULL)
|
||||
allcallsCase(uiWindowSetTitle, NULL, NULL)
|
||||
allcallsCase(uiWindowChild, NULL)
|
||||
allcallsCase(uiWindowSetChild, NULL, NULL)
|
|
@ -0,0 +1,5 @@
|
|||
// 28 may 2019
|
||||
|
||||
// This file should NOT have include guards as it is intended to be included more than once; see noinitwrongthread_darwin.m for details.
|
||||
|
||||
allcallsCase(uiDarwinControlHandle, NULL)
|
|
@ -0,0 +1,5 @@
|
|||
// 28 may 2019
|
||||
|
||||
// This file should NOT have include guards as it is intended to be included more than once; see noinitwrongthread_haiku.cpp for details.
|
||||
|
||||
allcallsCase(uiHaikuControlHandle, NULL)
|
|
@ -0,0 +1,5 @@
|
|||
// 28 may 2019
|
||||
|
||||
// This file should NOT have include guards as it is intended to be included more than once; see noinitwrongthread_unix.c for details.
|
||||
|
||||
allcallsCase(uiUnixControlHandle, NULL)
|
|
@ -0,0 +1,9 @@
|
|||
// 28 may 2019
|
||||
|
||||
// This file should NOT have include guards as it is intended to be included more than once; see noinitwrongthread_windows.c for details.
|
||||
|
||||
allcallsCase(uiWindowsControlHandle, NULL)
|
||||
allcallsCase(uiWindowsControlParentHandle, NULL)
|
||||
allcallsCase(uiWindowsControlSetControlPos, NULL, NULL)
|
||||
|
||||
allcallsCase(uiWindowsSetControlHandlePos, NULL, NULL)
|
|
@ -0,0 +1,669 @@
|
|||
// 8 june 2019
|
||||
#include "test.h"
|
||||
|
||||
static bool testControlInit(uiControl *c, void *implData, void *initData)
|
||||
{
|
||||
struct testControlImplData *ti = (struct testControlImplData *) implData;
|
||||
struct testControlImplData *tinit = (struct testControlImplData *) initData;
|
||||
|
||||
if (tinit == NULL)
|
||||
return true;
|
||||
*ti = *tinit;
|
||||
if (ti->realVtable != NULL && ti->realVtable->Init != NULL)
|
||||
return (*(ti->realVtable->Init))(c, ti->realImplData, initData);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void testControlFree(uiControl *c, void *implData)
|
||||
{
|
||||
struct testControlImplData *ti = (struct testControlImplData *) implData;
|
||||
|
||||
if (ti->realVtable != NULL && ti->realVtable->Free != NULL)
|
||||
(*(ti->realVtable->Free))(c, ti->realImplData);
|
||||
}
|
||||
|
||||
static void testControlParentChanging(uiControl *c, void *implData, uiControl *oldParent)
|
||||
{
|
||||
struct testControlImplData *ti = (struct testControlImplData *) implData;
|
||||
|
||||
if (ti->realVtable != NULL && ti->realVtable->ParentChanging != NULL)
|
||||
(*(ti->realVtable->ParentChanging))(c, ti->realImplData, oldParent);
|
||||
}
|
||||
|
||||
static void testControlParentChanged(uiControl *c, void *implData, uiControl *newParent)
|
||||
{
|
||||
struct testControlImplData *ti = (struct testControlImplData *) implData;
|
||||
|
||||
if (ti->realVtable != NULL && ti->realVtable->ParentChanged != NULL)
|
||||
(*(ti->realVtable->ParentChanged))(c, ti->realImplData, newParent);
|
||||
}
|
||||
|
||||
static const uiControlVtable vtable = {
|
||||
.Size = sizeof (uiControlVtable),
|
||||
.Init = testControlInit,
|
||||
.Free = testControlFree,
|
||||
.ParentChanging = testControlParentChanging,
|
||||
.ParentChanged = testControlParentChanged,
|
||||
};
|
||||
|
||||
const uiControlVtable *testControlVtable(void)
|
||||
{
|
||||
return &vtable;
|
||||
}
|
||||
|
||||
// the following are kludges for just these first two tests
|
||||
static bool nopInit(uiControl *c, void *implData, void *initData)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nopFree(uiControl *c, void *implData)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
static void testControlVtableWithNopInitFree(uiControlVtable *vt)
|
||||
{
|
||||
*vt = vtable;
|
||||
vt->Init = nopInit;
|
||||
vt->Free = nopFree;
|
||||
}
|
||||
|
||||
// TODO we'll have to eventually find out for real if memset(0) is sufficient to set pointers to NULL or not; C99 doesn't seem to say
|
||||
Test(ControlImplDataIsClearedOnNewControl)
|
||||
{
|
||||
char memory[32];
|
||||
uiControlVtable vt;
|
||||
uint32_t type;
|
||||
uiControl *c;
|
||||
char *implData;
|
||||
|
||||
testControlVtableWithNopInitFree(&vt);
|
||||
type = uiRegisterControlType("TestControl", &vt, testControlOSVtable(), sizeof (memory));
|
||||
c = uiNewControl(type, NULL);
|
||||
implData = (char *) uiControlImplData(c);
|
||||
memset(memory, 0, sizeof (memory));
|
||||
if (memcmp(implData, memory, sizeof (memory)) != 0)
|
||||
TestErrorf("control impl data memory not properly cleared on creation");
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
Test(ZeroSizeImplDataIsNULL)
|
||||
{
|
||||
uiControlVtable vt;
|
||||
uint32_t type;
|
||||
uiControl *c;
|
||||
|
||||
testControlVtableWithNopInitFree(&vt);
|
||||
type = uiRegisterControlType("TestControl", &vt, testControlOSVtable(), 0);
|
||||
c = uiNewControl(type, NULL);
|
||||
if (uiControlImplData(c) != NULL)
|
||||
TestErrorf("control impl data is non-NULL despite being of size 0");
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
uint32_t testControlType(void)
|
||||
{
|
||||
static uint32_t type = 0;
|
||||
|
||||
if (type == 0)
|
||||
type = uiRegisterControlType("TestControl", testControlVtable(), testControlOSVtable(), sizeof (struct testControlImplData));
|
||||
return type;
|
||||
}
|
||||
|
||||
static uint32_t testControlType2(void)
|
||||
{
|
||||
static uint32_t type = 0;
|
||||
|
||||
if (type == 0)
|
||||
type = uiRegisterControlType("TestControl2", testControlVtable(), testControlOSVtable(), sizeof (struct testControlImplData));
|
||||
return type;
|
||||
}
|
||||
|
||||
struct counts {
|
||||
unsigned int countInit;
|
||||
unsigned int countFree;
|
||||
unsigned int countParentChanging;
|
||||
unsigned int countParentChanged;
|
||||
uiControl *oldParent;
|
||||
uiControl *newParent;
|
||||
};
|
||||
|
||||
static bool countsInit(uiControl *c, void *implData, void *initData)
|
||||
{
|
||||
struct counts *counts = (struct counts *) implData;
|
||||
|
||||
counts->countInit++;
|
||||
if (counts->countInit > 2)
|
||||
counts->countInit = 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void countsFree(uiControl *c, void *implData)
|
||||
{
|
||||
struct counts *counts = (struct counts *) implData;
|
||||
|
||||
counts->countFree++;
|
||||
if (counts->countFree > 2)
|
||||
counts->countFree = 2;
|
||||
}
|
||||
|
||||
static void countsParentChanging(uiControl *c, void *implData, uiControl *oldParent)
|
||||
{
|
||||
struct counts *counts = (struct counts *) implData;
|
||||
|
||||
counts->oldParent = oldParent;
|
||||
counts->countParentChanging++;
|
||||
if (counts->countParentChanging > 3)
|
||||
counts->countParentChanging = 3;
|
||||
}
|
||||
|
||||
static void countsParentChanged(uiControl *c, void *implData, uiControl *newParent)
|
||||
{
|
||||
struct counts *counts = (struct counts *) implData;
|
||||
|
||||
counts->newParent = newParent;
|
||||
counts->countParentChanged++;
|
||||
if (counts->countParentChanged > 3)
|
||||
counts->countParentChanged = 3;
|
||||
}
|
||||
|
||||
static const uiControlVtable countsVtable = {
|
||||
.Init = countsInit,
|
||||
.Free = countsFree,
|
||||
.ParentChanging = countsParentChanging,
|
||||
.ParentChanged = countsParentChanged,
|
||||
};
|
||||
|
||||
// TODO do this but for the OS-specific methods
|
||||
Test(ControlMethodsCalled)
|
||||
{
|
||||
uiControl *c, *d;
|
||||
struct testControlImplData initData;
|
||||
struct counts counts;
|
||||
|
||||
memset(&counts, 0, sizeof (struct counts));
|
||||
|
||||
initData.realVtable = &countsVtable;
|
||||
initData.realOSVtable = NULL;
|
||||
initData.realImplData = &counts;
|
||||
c = uiNewControl(testControlType(), &initData);
|
||||
switch (counts.countInit) {
|
||||
case 0:
|
||||
TestErrorf("Init() was not called");
|
||||
break;
|
||||
case 1:
|
||||
// do nothing; this is the expected case
|
||||
break;
|
||||
default:
|
||||
TestErrorf("Init() called more than once");
|
||||
}
|
||||
if (counts.countFree != 0)
|
||||
TestErrorf("Free() called unexpectedly by uiNewControl()");
|
||||
// yes, the casts to void * are necessary, because the "equivalence" of data pointers to void * is really just the compiler doing conversions for you and this does not (and cannot) extend to the parameter lists of varargs functions (https://stackoverflow.com/questions/34723062, https://stackoverflow.com/questions/9053658)
|
||||
if (counts.countParentChanging != 0)
|
||||
TestErrorf("ParentChanging() called unexpectedly by uiNewControl(); most recent oldParent = %p", (void *) (counts.oldParent));
|
||||
if (counts.countParentChanged != 0)
|
||||
TestErrorf("ParentChanged() called unexpectedly by uiNewControl(); most recent newParent = %p", (void *) (counts.newParent));
|
||||
|
||||
d = uiNewControl(testControlType(), NULL);
|
||||
|
||||
uiControlSetParent(c, d);
|
||||
switch (counts.countParentChanging) {
|
||||
case 0:
|
||||
TestErrorf("ParentChanging() was not called by SetParent(non-NULL)");
|
||||
break;
|
||||
case 1:
|
||||
// do nothing; this is the expected case
|
||||
break;
|
||||
default:
|
||||
TestErrorf("ParentChanging() called more than once by SetParent(non-NULL)");
|
||||
}
|
||||
if (counts.oldParent != NULL)
|
||||
TestErrorf("ParentChanging() called with wrong oldParent by SetParent(non-NULL) (if called more than once, this is the most recent call):" diff("%p"),
|
||||
(void *) (counts.oldParent), (void *) NULL);
|
||||
switch (counts.countParentChanged) {
|
||||
case 0:
|
||||
TestErrorf("ParentChanged() was not called by SetParent(non-NULL)");
|
||||
break;
|
||||
case 1:
|
||||
// do nothing; this is the expected case
|
||||
break;
|
||||
default:
|
||||
TestErrorf("ParentChanged() called more than once by SetParent(non-NULL)");
|
||||
}
|
||||
if (counts.newParent != d)
|
||||
TestErrorf("ParentChanged() called with wrong newParent by SetParent(non-NULL) (if called more than once, this is the most recent call):" diff("%p"),
|
||||
(void *) (counts.newParent), (void *) d);
|
||||
if (counts.countInit != 1)
|
||||
TestErrorf("Init() called unexpectedly by uiControlSetParent(non-NULL)");
|
||||
if (counts.countFree != 0)
|
||||
TestErrorf("Free() called unexpectedly by uiControlSetParent(non-NULL)");
|
||||
|
||||
uiControlSetParent(c, NULL);
|
||||
switch (counts.countParentChanging) {
|
||||
case 0:
|
||||
case 1:
|
||||
TestErrorf("ParentChanging() was not called by SetParent(NULL)");
|
||||
break;
|
||||
case 2:
|
||||
// do nothing; this is the expected case
|
||||
break;
|
||||
default:
|
||||
TestErrorf("ParentChanging() called more than once by SetParent(NULL)");
|
||||
}
|
||||
if (counts.oldParent != d)
|
||||
TestErrorf("ParentChanging() called with wrong oldParent by SetParent(NULL) (if called more than once, this is the most recent call):" diff("%p"),
|
||||
(void *) (counts.oldParent), (void *) d);
|
||||
switch (counts.countParentChanged) {
|
||||
case 0:
|
||||
case 1:
|
||||
TestErrorf("ParentChanged() was not called by SetParent(NULL)");
|
||||
break;
|
||||
case 2:
|
||||
// do nothing; this is the expected case
|
||||
break;
|
||||
default:
|
||||
TestErrorf("ParentChanged() called more than once by SetParent(NULL)");
|
||||
}
|
||||
if (counts.newParent != NULL)
|
||||
TestErrorf("ParentChanged() called with wrong newParent by SetParent(NULL) (if called more than once, this is the most recent call):" diff("%p"),
|
||||
(void *) (counts.newParent), (void *) NULL);
|
||||
if (counts.countInit != 1)
|
||||
TestErrorf("Init() called unexpectedly by uiControlSetParent(NULL)");
|
||||
if (counts.countFree != 0)
|
||||
TestErrorf("Free() called unexpectedly by uiControlSetParent(NULL)");
|
||||
|
||||
uiControlFree(d);
|
||||
|
||||
uiControlFree(c);
|
||||
switch (counts.countFree) {
|
||||
case 0:
|
||||
TestErrorf("Free() was not called");
|
||||
break;
|
||||
case 1:
|
||||
// do nothing; this is the expected case
|
||||
break;
|
||||
default:
|
||||
TestErrorf("Free() called more than once");
|
||||
}
|
||||
if (counts.countInit != 1)
|
||||
TestErrorf("Init() called unexpectedly by uiControlFree()");
|
||||
if (counts.countParentChanging != 2)
|
||||
TestErrorf("ParentChanging() called unexpectedly by uiNewControl(); most recent oldParent = %p", (void *) (counts.oldParent));
|
||||
if (counts.countParentChanged != 2)
|
||||
TestErrorf("ParentChanged() called unexpectedly by uiNewControl(); most recent newParent = %p", (void *) (counts.newParent));
|
||||
}
|
||||
|
||||
Test(NullControlTypeNameIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): invalid null pointer for name");
|
||||
uiRegisterControlType(NULL, NULL, NULL, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(NullControlVtableIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): invalid null pointer for uiControlVtable");
|
||||
uiRegisterControlType("name", NULL, NULL, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(WrongControlVtableSizeIsProgrammerError)
|
||||
{
|
||||
uiControlVtable vtable;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): wrong size 1 for uiControlVtable");
|
||||
memset(&vtable, 0, sizeof (uiControlVtable));
|
||||
vtable.Size = 1;
|
||||
uiRegisterControlType("name", &vtable, NULL, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(ControlVtableWithMissingInitMethodIsProgrammerError)
|
||||
{
|
||||
uiControlVtable vt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlVtable method Init() missing for uiControl type name");
|
||||
vt = vtable;
|
||||
vt.Init = NULL;
|
||||
uiRegisterControlType("name", &vt, NULL, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(ControlVtableWithMissingFreeMethodIsProgrammerError)
|
||||
{
|
||||
uiControlVtable vt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlVtable method Free() missing for uiControl type name");
|
||||
vt = vtable;
|
||||
vt.Free = NULL;
|
||||
uiRegisterControlType("name", &vt, NULL, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(ControlVtableWithMissingParentChangingMethodIsProgrammerError)
|
||||
{
|
||||
uiControlVtable vt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlVtable method ParentChanging() missing for uiControl type name");
|
||||
vt = vtable;
|
||||
vt.ParentChanging = NULL;
|
||||
uiRegisterControlType("name", &vt, NULL, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(ControlVtableWithMissingParentChangedMethodIsProgrammerError)
|
||||
{
|
||||
uiControlVtable vt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlVtable method ParentChanged() missing for uiControl type name");
|
||||
vt = vtable;
|
||||
vt.ParentChanged = NULL;
|
||||
uiRegisterControlType("name", &vt, NULL, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(NullControlOSVtableIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): invalid null pointer for uiControlOSVtable");
|
||||
uiRegisterControlType("name", &vtable, NULL, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(CheckingNullControlIsProgrammerError)
|
||||
{
|
||||
uint32_t ctrlType;
|
||||
void *ctx;
|
||||
|
||||
ctrlType = uiControlType();
|
||||
ctx = beginCheckProgrammerError("uiCheckControlType(): invalid null pointer for uiControl");
|
||||
uiCheckControlType(NULL, ctrlType);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(CheckingNonControlIsProgrammerError)
|
||||
{
|
||||
uiControl *c;
|
||||
uint32_t ctrlType;
|
||||
void *ctx;
|
||||
|
||||
c = uiprivTestHookControlWithInvalidControlMarker();
|
||||
ctrlType = uiControlType();
|
||||
ctx = beginCheckProgrammerError("uiCheckControlType(): object passed in not a uiControl");
|
||||
uiCheckControlType(c, ctrlType);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(CheckingControlWithAnUnknownTypeIsProgrammerError)
|
||||
{
|
||||
uiControl *c;
|
||||
uint32_t ctrlType;
|
||||
void *ctx;
|
||||
|
||||
c = uiprivTestHookControlWithInvalidType();
|
||||
ctrlType = testControlType();
|
||||
ctx = beginCheckProgrammerError("uiCheckControlType(): unknown uiControl type 0 found in uiControl (this is likely not a real uiControl or some data is corrupt)");
|
||||
uiCheckControlType(c, ctrlType);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(CheckingControlWithAnUnknownTypeIsProgrammerErrorEvenIfCheckingAgainstuiControlType)
|
||||
{
|
||||
uiControl *c;
|
||||
uint32_t ctrlType;
|
||||
void *ctx;
|
||||
|
||||
c = uiprivTestHookControlWithInvalidType();
|
||||
ctrlType = uiControlType();
|
||||
ctx = beginCheckProgrammerError("uiCheckControlType(): unknown uiControl type 0 found in uiControl (this is likely not a real uiControl or some data is corrupt)");
|
||||
uiCheckControlType(c, ctrlType);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(CheckingForUnknownControlTypeIsProgrammerError)
|
||||
{
|
||||
uiControl *c;
|
||||
void *ctx;
|
||||
|
||||
c = uiNewControl(testControlType(), NULL);
|
||||
ctx = beginCheckProgrammerError("uiCheckControlType(): unknown uiControl type 0 requested");
|
||||
uiCheckControlType(c, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
Test(CheckControlTypeFailsCorrectly)
|
||||
{
|
||||
uiControl *c;
|
||||
uint32_t ctrlType;
|
||||
void *ctx;
|
||||
|
||||
c = uiNewControl(testControlType(), NULL);
|
||||
ctrlType = testControlType2();
|
||||
ctx = beginCheckProgrammerError("uiCheckControlType(): wrong uiControl type passed: got TestControl, want TestControl2");
|
||||
uiCheckControlType(c, ctrlType);
|
||||
endCheckProgrammerError(ctx);
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
Test(NewControlOfTypeControlIsProgrammerError)
|
||||
{
|
||||
uint32_t ctrlType;
|
||||
void *ctx;
|
||||
|
||||
ctrlType = uiControlType();
|
||||
ctx = beginCheckProgrammerError("uiNewControl(): uiControlType() passed in when specific control type needed");
|
||||
uiNewControl(ctrlType, NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(NewControlOfUnknownTypeIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiNewControl(): unknown uiControl type 0 requested");
|
||||
uiNewControl(0, NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
static bool alwaysFailInit(uiControl *c, void *implData, void *initData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Test(NewControlWithInvalidInitDataIsProgrammerError)
|
||||
{
|
||||
uint32_t ctrlType;
|
||||
struct testControlImplData initData;
|
||||
uiControlVtable vtable;
|
||||
void *ctx;
|
||||
|
||||
ctrlType = testControlType();
|
||||
ctx = beginCheckProgrammerError("uiNewControl(): invalid init data for TestControl");
|
||||
memset(&vtable, 0, sizeof (uiControlVtable));
|
||||
vtable.Init = alwaysFailInit;
|
||||
initData.realVtable = &vtable;
|
||||
uiNewControl(ctrlType, &initData);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(FreeingNullControlIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiControlFree(): invalid null pointer for uiControl");
|
||||
uiControlFree(NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(FreeingParentedControlIsProgrammerError)
|
||||
{
|
||||
uiControl *c, *d;
|
||||
void *ctx;
|
||||
|
||||
c = uiNewControl(testControlType(), NULL);
|
||||
d = uiNewControl(testControlType(), NULL);
|
||||
|
||||
uiControlSetParent(c, d);
|
||||
ctx = beginCheckProgrammerError("uiControlFree(): cannot be called on a control with has a parent");
|
||||
uiControlFree(c);
|
||||
endCheckProgrammerError(ctx);
|
||||
|
||||
// cleanup
|
||||
uiControlSetParent(c, NULL);
|
||||
uiControlFree(d);
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
Test(SetParentWithNullControlIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiControlSetParent(): invalid null pointer for uiControl");
|
||||
uiControlSetParent(NULL, NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(RemovingParentFromInitiallyParentlessControlIsProgrammerError)
|
||||
{
|
||||
uiControl *c;
|
||||
void *ctx;
|
||||
|
||||
c = uiNewControl(testControlType(), NULL);
|
||||
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot set a control with no parent to have no parent");
|
||||
uiControlSetParent(c, NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
Test(RemovingParentFromExplicitlyParentlessControlIsProgrammerError)
|
||||
{
|
||||
uiControl *c, *d;
|
||||
void *ctx;
|
||||
|
||||
c = uiNewControl(testControlType(), NULL);
|
||||
d = uiNewControl(testControlType(), NULL);
|
||||
uiControlSetParent(c, d);
|
||||
uiControlSetParent(c, NULL);
|
||||
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot set a control with no parent to have no parent");
|
||||
uiControlSetParent(c, NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
uiControlFree(d);
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
Test(ReparentingAlreadyParentedControlToDifferentParentIsProgrammerError)
|
||||
{
|
||||
uiControl *c, *d, *e;
|
||||
void *ctx;
|
||||
|
||||
c = uiNewControl(testControlType(), NULL);
|
||||
d = uiNewControl(testControlType(), NULL);
|
||||
e = uiNewControl(testControlType(), NULL);
|
||||
|
||||
uiControlSetParent(c, d);
|
||||
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot set a control with a parent to have another parent");
|
||||
uiControlSetParent(c, e);
|
||||
endCheckProgrammerError(ctx);
|
||||
|
||||
// cleanup
|
||||
uiControlSetParent(c, NULL);
|
||||
uiControlFree(e);
|
||||
uiControlFree(d);
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
Test(ReparentingAlreadyParentedControlToSameParentIsProgrammerError)
|
||||
{
|
||||
uiControl *c, *d;
|
||||
void *ctx;
|
||||
|
||||
c = uiNewControl(testControlType(), NULL);
|
||||
d = uiNewControl(testControlType(), NULL);
|
||||
|
||||
uiControlSetParent(c, d);
|
||||
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot set a control with a parent to have another parent");
|
||||
uiControlSetParent(c, d);
|
||||
endCheckProgrammerError(ctx);
|
||||
|
||||
// cleanup
|
||||
uiControlSetParent(c, NULL);
|
||||
uiControlFree(d);
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
Test(ControlParentCyclesDisallowed_TwoControls)
|
||||
{
|
||||
uiControl *c, *d;
|
||||
void *ctx;
|
||||
|
||||
c = uiNewControl(testControlType(), NULL);
|
||||
d = uiNewControl(testControlType(), NULL);
|
||||
|
||||
uiControlSetParent(c, d);
|
||||
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot create a parent cycle");
|
||||
uiControlSetParent(d, c);
|
||||
endCheckProgrammerError(ctx);
|
||||
|
||||
// cleanup
|
||||
// TODO reformat all the other tests to have clear init, test, and cleanup sections, and also maybe remove these "// cleanup" comments
|
||||
uiControlSetParent(c, NULL);
|
||||
uiControlFree(d);
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
Test(ControlParentCyclesDisallowed_ThreeControls)
|
||||
{
|
||||
uiControl *c, *d, *e;
|
||||
void *ctx;
|
||||
|
||||
c = uiNewControl(testControlType(), NULL);
|
||||
d = uiNewControl(testControlType(), NULL);
|
||||
e = uiNewControl(testControlType(), NULL);
|
||||
|
||||
uiControlSetParent(c, d);
|
||||
uiControlSetParent(d, e);
|
||||
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot create a parent cycle");
|
||||
uiControlSetParent(e, c);
|
||||
endCheckProgrammerError(ctx);
|
||||
|
||||
// cleanup
|
||||
uiControlSetParent(d, NULL);
|
||||
uiControlSetParent(c, NULL);
|
||||
uiControlFree(e);
|
||||
uiControlFree(d);
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
Test(ControlCannotBeItsOwnParent)
|
||||
{
|
||||
uiControl *c;
|
||||
void *ctx;
|
||||
|
||||
c = uiNewControl(testControlType(), NULL);
|
||||
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot create a parent cycle");
|
||||
uiControlSetParent(c, c);
|
||||
endCheckProgrammerError(ctx);
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
Test(GettingImplDataOfNullControlIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiControlImplData(): invalid null pointer for uiControl");
|
||||
uiControlImplData(NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// 10 june 2019
|
||||
#import "test_darwin.h"
|
||||
|
||||
static id testControlHandle(uiControl *c, void *implData)
|
||||
{
|
||||
struct testControlImplData *ti = (struct testControlImplData *) implData;
|
||||
|
||||
if (ti->realOSVtable != NULL && ti->realOSVtable->Handle != NULL)
|
||||
return (*(ti->realOSVtable->Handle))(c, ti->realImplData);
|
||||
return nil;
|
||||
}
|
||||
|
||||
static const uiControlOSVtable osVtable = {
|
||||
.Size = sizeof (uiControlOSVtable),
|
||||
.Handle = testControlHandle,
|
||||
};
|
||||
|
||||
const uiControlOSVtable *testControlOSVtable(void)
|
||||
{
|
||||
return &osVtable;
|
||||
}
|
||||
|
||||
Test(WrongControlOSVtableSizeIsProgrammerError)
|
||||
{
|
||||
uiControlOSVtable osvt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): wrong size 1 for uiControlOSVtable");
|
||||
memset(&osvt, 0, sizeof (uiControlOSVtable));
|
||||
osvt.Size = 1;
|
||||
uiRegisterControlType("name", testControlVtable(), &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(ControlOSVtableWithMissingHandleMethodIsProgrammerError)
|
||||
{
|
||||
uiControlOSVtable osvt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlOSVtable method Handle() missing for uiControl type name");
|
||||
osvt = osVtable;
|
||||
osvt.Handle = NULL;
|
||||
uiRegisterControlType("name", testControlVtable(), &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(GettingDarwinHandleOfNullControlIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiDarwinControlHandle(): invalid null pointer for uiControl");
|
||||
uiDarwinControlHandle(NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// 18 january 2020
|
||||
#include "test_haiku.hpp"
|
||||
|
||||
static void *testControlHandle(uiControl *c, void *implData)
|
||||
{
|
||||
struct testControlImplData *ti = (struct testControlImplData *) implData;
|
||||
|
||||
if (ti->realOSVtable != NULL && ti->realOSVtable->Handle != NULL)
|
||||
return (*(ti->realOSVtable->Handle))(c, ti->realImplData);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const uiControlOSVtable osVtable = [](void) {
|
||||
uiControlOSVtable vt;
|
||||
|
||||
memset(&vt, 0, sizeof (uiControlOSVtable));
|
||||
vt.Size = sizeof (uiControlOSVtable);
|
||||
vt.Handle = testControlHandle;
|
||||
return vt;
|
||||
}();
|
||||
|
||||
const uiControlOSVtable *testControlOSVtable(void)
|
||||
{
|
||||
return &osVtable;
|
||||
}
|
||||
|
||||
Test(WrongControlOSVtableSizeIsProgrammerError)
|
||||
{
|
||||
uiControlOSVtable osvt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): wrong size 1 for uiControlOSVtable");
|
||||
memset(&osvt, 0, sizeof (uiControlOSVtable));
|
||||
osvt.Size = 1;
|
||||
uiRegisterControlType("name", testControlVtable(), &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(ControlOSVtableWithMissingHandleMethodIsProgrammerError)
|
||||
{
|
||||
uiControlOSVtable osvt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlOSVtable method Handle() missing for uiControl type name");
|
||||
osvt = osVtable;
|
||||
osvt.Handle = NULL;
|
||||
uiRegisterControlType("name", testControlVtable(), &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(GettingHaikuHandleOfNullControlIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiHaikuControlHandle(): invalid null pointer for uiControl");
|
||||
uiHaikuControlHandle(NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// 10 june 2019
|
||||
#include "test_unix.h"
|
||||
|
||||
static GtkWidget *testControlHandle(uiControl *c, void *implData)
|
||||
{
|
||||
struct testControlImplData *ti = (struct testControlImplData *) implData;
|
||||
|
||||
if (ti->realOSVtable != NULL && ti->realOSVtable->Handle != NULL)
|
||||
return (*(ti->realOSVtable->Handle))(c, ti->realImplData);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const uiControlOSVtable osVtable = {
|
||||
.Size = sizeof (uiControlOSVtable),
|
||||
.Handle = testControlHandle,
|
||||
};
|
||||
|
||||
const uiControlOSVtable *testControlOSVtable(void)
|
||||
{
|
||||
return &osVtable;
|
||||
}
|
||||
|
||||
Test(WrongControlOSVtableSizeIsProgrammerError)
|
||||
{
|
||||
uiControlOSVtable osvt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): wrong size 1 for uiControlOSVtable");
|
||||
memset(&osvt, 0, sizeof (uiControlOSVtable));
|
||||
osvt.Size = 1;
|
||||
uiRegisterControlType("name", testControlVtable(), &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(ControlOSVtableWithMissingHandleMethodIsProgrammerError)
|
||||
{
|
||||
uiControlOSVtable osvt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlOSVtable method Handle() missing for uiControl type name");
|
||||
osvt = osVtable;
|
||||
osvt.Handle = NULL;
|
||||
uiRegisterControlType("name", testControlVtable(), &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(GettingUnixHandleOfNullControlIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiUnixControlHandle(): invalid null pointer for uiControl");
|
||||
uiUnixControlHandle(NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
// 10 june 2019
|
||||
#include "test_windows.h"
|
||||
|
||||
static HWND testControlHandle(uiControl *c, void *implData)
|
||||
{
|
||||
struct testControlImplData *ti = (struct testControlImplData *) implData;
|
||||
|
||||
if (ti->realOSVtable != NULL && ti->realOSVtable->Handle != NULL)
|
||||
return (*(ti->realOSVtable->Handle))(c, ti->realImplData);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HWND testControlParentHandleForChild(uiControl *c, void *implData, uiControl *child)
|
||||
{
|
||||
struct testControlImplData *ti = (struct testControlImplData *) implData;
|
||||
|
||||
if (ti->realOSVtable != NULL && ti->realOSVtable->ParentHandleForChild != NULL)
|
||||
return (*(ti->realOSVtable->ParentHandleForChild))(c, ti->realImplData, child);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static HRESULT testControlSetControlPos(uiControl *c, void *implData, const RECT *r)
|
||||
{
|
||||
struct testControlImplData *ti = (struct testControlImplData *) implData;
|
||||
|
||||
if (ti->realOSVtable != NULL && ti->realOSVtable->SetControlPos != NULL)
|
||||
return (*(ti->realOSVtable->SetControlPos))(c, ti->realImplData, r);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static const uiControlOSVtable osVtable = {
|
||||
.Size = sizeof (uiControlOSVtable),
|
||||
.Handle = testControlHandle,
|
||||
.ParentHandleForChild = testControlParentHandleForChild,
|
||||
.SetControlPos = testControlSetControlPos,
|
||||
};
|
||||
|
||||
const uiControlOSVtable *testControlOSVtable(void)
|
||||
{
|
||||
return &osVtable;
|
||||
}
|
||||
|
||||
Test(WrongControlOSVtableSizeIsProgrammerError)
|
||||
{
|
||||
uiControlOSVtable osvt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): wrong size 1 for uiControlOSVtable");
|
||||
memset(&osvt, 0, sizeof (uiControlOSVtable));
|
||||
osvt.Size = 1;
|
||||
uiRegisterControlType("name", testControlVtable(), &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(ControlOSVtableWithMissingHandleMethodIsProgrammerError)
|
||||
{
|
||||
uiControlOSVtable osvt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlOSVtable method Handle() missing for uiControl type name");
|
||||
osvt = osVtable;
|
||||
osvt.Handle = NULL;
|
||||
uiRegisterControlType("name", testControlVtable(), &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(ControlOSVtableWithMissingParentHandleForChildMethodIsProgrammerError)
|
||||
{
|
||||
uiControlOSVtable osvt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlOSVtable method ParentHandleForChild() missing for uiControl type name");
|
||||
osvt = osVtable;
|
||||
osvt.ParentHandleForChild = NULL;
|
||||
uiRegisterControlType("name", testControlVtable(), &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(ControlOSVtableWithMissingSetControlPosMethodIsProgrammerError)
|
||||
{
|
||||
uiControlOSVtable osvt;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlOSVtable method SetControlPos() missing for uiControl type name");
|
||||
osvt = osVtable;
|
||||
osvt.SetControlPos = NULL;
|
||||
uiRegisterControlType("name", testControlVtable(), &osvt, 0);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(GettingWindowsHandleOfNullControlIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiWindowsControlHandle(): invalid null pointer for uiControl");
|
||||
uiWindowsControlHandle(NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(GettingWindowsParentHandleOfNullControlIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiWindowsControlParentHandle(): invalid null pointer for uiControl");
|
||||
uiWindowsControlParentHandle(NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(SettingWindowsControlPosOfNullControlIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiWindowsControlSetControlPos(): invalid null pointer for uiControl");
|
||||
uiWindowsControlSetControlPos(NULL, NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(SettingWindowsControlPosToNullRectIsProgrammerError)
|
||||
{
|
||||
uiControl *c;
|
||||
void *ctx;
|
||||
|
||||
c = uiNewControl(testControlType(), NULL);
|
||||
ctx = beginCheckProgrammerError("uiWindowsControlSetControlPos(): invalid null pointer for RECT");
|
||||
uiWindowsControlSetControlPos(c, NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
uiControlFree(c);
|
||||
}
|
||||
|
||||
// TODO uiWindowsSetControlHandlePos errors
|
|
@ -0,0 +1,109 @@
|
|||
// 28 may 2019
|
||||
#include "test.h"
|
||||
#include "thread.h"
|
||||
|
||||
// Do not put any test cases in this file; they will not be run.
|
||||
|
||||
// Notes on these functions:
|
||||
// - Try to wrap them as tightly around the specific calls being tested as possible, to avoid accidentally catching something else. Ideally, only one libui function call should be made between a begin/end pair, even indirectly.
|
||||
// - I don't know if these are thread-safe yet (TODO potentially make them so so this first part can be made tighter).
|
||||
|
||||
struct checkProgrammerErrorParams {
|
||||
bool caught;
|
||||
char *msgGot;
|
||||
const char *msgWant; // NULL if should not be caught
|
||||
};
|
||||
|
||||
static char *ourStrdup(const char *s)
|
||||
{
|
||||
char *t;
|
||||
size_t n;
|
||||
|
||||
n = (strlen(s) + 1) * sizeof (char);
|
||||
t = (char *) malloc(n);
|
||||
if (t == NULL)
|
||||
TestFatalf("memory exhausted in ourStrdup() copying %s", s);
|
||||
memcpy(t, s, n);
|
||||
return t;
|
||||
}
|
||||
|
||||
static void handleProgrammerError(const char *msg, void *data)
|
||||
{
|
||||
struct checkProgrammerErrorParams *p = (struct checkProgrammerErrorParams *) data;
|
||||
|
||||
p->caught = true;
|
||||
if (strcmp(msg, p->msgWant) != 0)
|
||||
p->msgGot = ourStrdup(msg);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TODO
|
||||
static void checkProgrammerErrorThreadProc(void *data)
|
||||
{
|
||||
struct checkProgrammerErrorParams *p = (struct checkProgrammerErrorParams *) data;
|
||||
|
||||
(*(p->f))();
|
||||
}
|
||||
|
||||
static void checkProgrammerErrorSubtestImpl(struct checkProgrammerErrorParams *p)
|
||||
{
|
||||
if (p->inThread) {
|
||||
threadThread *thread;
|
||||
threadSysError err;
|
||||
|
||||
err = threadNewThread(checkProgrammerErrorThreadProc, p, &thread);
|
||||
if (err != 0)
|
||||
// TODO these should only fatal out of the subtest (see TODO below)
|
||||
TestFatalfFull(p->file, p->line, "error creating thread: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
err = threadThreadWaitAndFree(thread);
|
||||
if (err != 0)
|
||||
TestFatalfFull(p->file, p->line, "error waiting for thread to finish: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void *beginCheckProgrammerError(const char *want)
|
||||
{
|
||||
struct checkProgrammerErrorParams *p;
|
||||
|
||||
p = (struct checkProgrammerErrorParams *) malloc(sizeof (struct checkProgrammerErrorParams));
|
||||
if (p == NULL)
|
||||
TestFatalf("memory exhausted allocating beginCheckProgrammerError() context");
|
||||
memset(p, 0, sizeof (struct checkProgrammerErrorParams));
|
||||
p->msgWant = want;
|
||||
uiprivTestHookReportProgrammerError(handleProgrammerError, p);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void checkWantProgrammerError(const char *file, long line, struct checkProgrammerErrorParams *p)
|
||||
{
|
||||
if (!p->caught)
|
||||
TestErrorfFull(file, line, "did not throw a programmer error; should have");
|
||||
if (p->msgGot != NULL) {
|
||||
TestErrorfFull(file, line, "message doesn't match expected string:" diff("%s"),
|
||||
p->msgGot, p->msgWant);
|
||||
free(p->msgGot);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkWantNoProgrammerError(const char *file, long line, struct checkProgrammerErrorParams *p)
|
||||
{
|
||||
if (p->caught)
|
||||
TestErrorfFull(file, line, "threw a programmer error; should not have");
|
||||
if (p->msgGot != NULL) {
|
||||
TestErrorfFull(file, line, "got unexpected programmer error message when none was expected: %s", p->msgGot);
|
||||
free(p->msgGot);
|
||||
}
|
||||
}
|
||||
|
||||
void endCheckProgrammerErrorFull(const char *file, long line, void *context)
|
||||
{
|
||||
struct checkProgrammerErrorParams *p = (struct checkProgrammerErrorParams *) context;
|
||||
|
||||
if (p->msgWant != NULL)
|
||||
checkWantProgrammerError(file, line, p);
|
||||
else
|
||||
checkWantNoProgrammerError(file, line, p);
|
||||
free(p);
|
||||
uiprivTestHookReportProgrammerError(NULL, NULL);
|
||||
}
|
|
@ -0,0 +1,597 @@
|
|||
// 10 april 2019
|
||||
#include "test.h"
|
||||
#include "thread.h"
|
||||
|
||||
static void testImplInitFailureFull(const char *file, long line)
|
||||
{
|
||||
uiInitError err;
|
||||
void *ctx;
|
||||
|
||||
uiprivTestHookSetInitShouldFailArtificially(true);
|
||||
ctx = beginCheckProgrammerError(NULL);
|
||||
memset(&err, 0, sizeof (uiInitError));
|
||||
err.Size = sizeof (uiInitError);
|
||||
if (uiInit(NULL, &err))
|
||||
TestErrorfFull(file, line, "uiInit() succeeded; expected failure");
|
||||
else if (strcmp(err.Message, "general failure") != 0)
|
||||
TestErrorfFull(file, line, "uiInit() failed with wrong message:" diff("%s"),
|
||||
err.Message, "general failure");
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
#define testImplInitFailure() testImplInitFailureFull(__FILE__, __LINE__)
|
||||
|
||||
TestNoInit(InitFailure)
|
||||
{
|
||||
testImplInitFailure();
|
||||
}
|
||||
|
||||
static void testImplNonNullOptionsFull(const char *file, long line)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiInit(): invalid uiInitOptions passed");
|
||||
if (uiInit(ctx, NULL))
|
||||
TestErrorfFull(file, line, "uiInit() with non-NULL options succeeded; expected failure");
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
#define testImplNonNullOptions() testImplNonNullOptionsFull(__FILE__, __LINE__)
|
||||
|
||||
TestNoInit(InitWithNonNullOptionsIsProgrammerError)
|
||||
{
|
||||
testImplNonNullOptions();
|
||||
}
|
||||
|
||||
TestNoInit(InitWithNullErrorIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiInit(): invalid null pointer for uiInitError");
|
||||
if (uiInit(NULL, NULL))
|
||||
TestErrorf("uiInit() with NULL error succeeded; expected failure");
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
TestNoInit(InitWithWrongErrorSizeIsProgrammerError)
|
||||
{
|
||||
uiInitError err;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiInit(): wrong size 2 for uiInitError");
|
||||
memset(&err, 0, sizeof (uiInitError));
|
||||
err.Size = 2;
|
||||
if (uiInit(NULL, &err))
|
||||
TestErrorf("uiInit() with error with invalid size succeeded; expected failure");
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(InitCorrectlyAfterInitializedSuccessfully)
|
||||
{
|
||||
uiInitError err;
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiInit(): attempt to call more than once");
|
||||
memset(&err, 0, sizeof (uiInitError));
|
||||
err.Size = sizeof (uiInitError);
|
||||
if (uiInit(NULL, &err))
|
||||
TestFatalf("uiInit() after a previous successful call succeeded; expected failure");
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(InitIncorrectlyAfterInitializedSuccessfully)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiInit(): attempt to call more than once");
|
||||
if (uiInit(NULL, NULL))
|
||||
TestFatalf("bad uiInit() after a previous successful call succeeded; expected failure");
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
TestNoInit(InitCorrectlyAfterFailureToInitialize)
|
||||
{
|
||||
uiInitError err;
|
||||
void *ctx;
|
||||
|
||||
testImplInitFailure();
|
||||
ctx = beginCheckProgrammerError("uiInit(): attempt to call more than once");
|
||||
memset(&err, 0, sizeof (uiInitError));
|
||||
err.Size = sizeof (uiInitError);
|
||||
if (uiInit(NULL, &err))
|
||||
TestFatalf("uiInit() after a previous failed call succeeded; expected failure");
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
TestNoInit(InitIncorrectlyAfterFailureToInitialize)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
testImplInitFailure();
|
||||
ctx = beginCheckProgrammerError("uiInit(): attempt to call more than once");
|
||||
if (uiInit(NULL, NULL))
|
||||
TestFatalf("bad uiInit() after a previous failed call succeeded; expected failure");
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
TestNoInit(InitCorrectlyAfterIncorrectInitialization)
|
||||
{
|
||||
uiInitError err;
|
||||
void *ctx;
|
||||
|
||||
testImplNonNullOptions();
|
||||
ctx = beginCheckProgrammerError("uiInit(): attempt to call more than once");
|
||||
memset(&err, 0, sizeof (uiInitError));
|
||||
err.Size = sizeof (uiInitError);
|
||||
if (uiInit(NULL, &err))
|
||||
TestFatalf("uiInit() after a previous erroneous call succeeded; expected failure");
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
TestNoInit(InitIncorrectlyAfterIncorrectInitialization)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
testImplNonNullOptions();
|
||||
ctx = beginCheckProgrammerError("uiInit(): attempt to call more than once");
|
||||
if (uiInit(NULL, NULL))
|
||||
TestFatalf("bad uiInit() after a previous erroneous call succeeded; expected failure");
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
static void done(void *data)
|
||||
{
|
||||
uiQuit();
|
||||
}
|
||||
|
||||
Test(MainCalledTwiceIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
uiQueueMain(done, NULL);
|
||||
uiMain();
|
||||
ctx = beginCheckProgrammerError("uiMain(): attempt to call more than once");
|
||||
uiMain();
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
static void mainAndQuit(void *data)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiMain(): attempt to call more than once");
|
||||
uiMain();
|
||||
endCheckProgrammerError(ctx);
|
||||
uiQuit();
|
||||
}
|
||||
|
||||
Test(MainCalledRecursivelyIsProgrammerError)
|
||||
{
|
||||
uiQueueMain(mainAndQuit, NULL);
|
||||
uiMain();
|
||||
}
|
||||
|
||||
// largely redundant due to InitCorrectlyAfterInitializedSuccessfully, but include it anyway just to be safe
|
||||
Test(InitAfterMainIsProgrammerError)
|
||||
{
|
||||
uiInitError err;
|
||||
void *ctx;
|
||||
|
||||
uiQueueMain(done, NULL);
|
||||
uiMain();
|
||||
ctx = beginCheckProgrammerError("uiInit(): attempt to call more than once");
|
||||
memset(&err, 0, sizeof (uiInitError));
|
||||
err.Size = sizeof (uiInitError);
|
||||
if (uiInit(NULL, &err))
|
||||
TestFatalf("uiInit() after a previous successful call succeeded; expected failure");
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(QuitBeforeMainIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("uiQuit(): attempt to call before uiMain()");
|
||||
uiQuit();
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
static void quitTwice(void *data)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
uiQuit();
|
||||
ctx = beginCheckProgrammerError("uiQuit(): attempt to call more than once");
|
||||
uiQuit();
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
Test(QuitCalledTwiceIsProgrammerError)
|
||||
{
|
||||
uiQueueMain(quitTwice, NULL);
|
||||
uiMain();
|
||||
}
|
||||
|
||||
Test(QuitAfterMainIsProgrammerError)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
uiQueueMain(done, NULL);
|
||||
uiMain();
|
||||
ctx = beginCheckProgrammerError("uiQuit(): attempt to call more than once");
|
||||
uiQuit();
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
// TODO I'm not convinced the rest of this file constitutes meaningful tests of uiQueueMain() behavior, especially since it's more than likely that all my threads are running so quickly the tests might as well be single-threaded and/or non-interleaved, but at the same time it's thorough enough that I'm not sure what would
|
||||
|
||||
struct simpleTestParams {
|
||||
unsigned int n;
|
||||
threadThread *thread;
|
||||
threadSysError createErr;
|
||||
threadSysError sleepErr;
|
||||
};
|
||||
|
||||
static void queueSimple(void *data)
|
||||
{
|
||||
struct simpleTestParams *p = (struct simpleTestParams *) data;
|
||||
|
||||
p->n++;
|
||||
if (p->n > 2)
|
||||
p->n = 2;
|
||||
uiQuit();
|
||||
}
|
||||
|
||||
Test(QueueMain)
|
||||
{
|
||||
struct simpleTestParams p;
|
||||
|
||||
memset(&p, 0, sizeof (struct simpleTestParams));
|
||||
p.n = 0;
|
||||
uiQueueMain(queueSimple, &p);
|
||||
uiMain();
|
||||
switch (p.n) {
|
||||
case 0:
|
||||
TestErrorf("uiQueueMain() function was not called");
|
||||
break;
|
||||
case 1:
|
||||
// do nothing; this is the expected case
|
||||
break;
|
||||
default:
|
||||
TestErrorf("uiQueueMain() called more than once");
|
||||
}
|
||||
}
|
||||
|
||||
struct queuedOrder {
|
||||
int calls[4];
|
||||
int i;
|
||||
unsigned int extraCalls;
|
||||
};
|
||||
|
||||
static void queueOrder(struct queuedOrder *q, int n)
|
||||
{
|
||||
if (q->i < 4) {
|
||||
q->calls[q->i] = n;
|
||||
q->i++;
|
||||
return;
|
||||
}
|
||||
q->extraCalls++;
|
||||
if (q->extraCalls > 9)
|
||||
q->extraCalls = 9;
|
||||
}
|
||||
|
||||
static void queueCheckOrderFull(const char *file, long line, struct queuedOrder *got, int wantI, int wantA, int wantB, int wantC, int wantD)
|
||||
{
|
||||
int wantarr[4];
|
||||
int i;
|
||||
|
||||
if (got->i != wantI || got->extraCalls != 0) {
|
||||
const char *orMore;
|
||||
|
||||
orMore = "";
|
||||
if (got->extraCalls >= 9)
|
||||
orMore = " or more";
|
||||
TestErrorfFull(file, line, "wrong number of queued function calls:" diff("%d%s"),
|
||||
got->i + got->extraCalls, orMore,
|
||||
wantI, "");
|
||||
}
|
||||
wantarr[0] = wantA;
|
||||
wantarr[1] = wantB;
|
||||
wantarr[2] = wantC;
|
||||
wantarr[3] = wantD;
|
||||
for (i = 0; i < 4; i++)
|
||||
if (got->calls[i] != wantarr[i])
|
||||
TestErrorfFull(file, line, "wrong value for call %d in sequence:" diff("%d"),
|
||||
i + 1, got->calls[i], wantarr[i]);
|
||||
}
|
||||
|
||||
#define queueCheckOrder(got, i, a, b, c, d) queueCheckOrderFull(__FILE__, __LINE__, got, i, a, b, c, d)
|
||||
|
||||
struct queueTestParams {
|
||||
struct queuedOrder order1;
|
||||
struct queuedOrder order2;
|
||||
unsigned int n;
|
||||
threadThread *thread;
|
||||
threadSysError createErr;
|
||||
threadSysError sleepErr1;
|
||||
threadSysError sleepErr2;
|
||||
};
|
||||
|
||||
#define queueStep(name, type, field, n) \
|
||||
static void name(void *data) \
|
||||
{ \
|
||||
type *p = (type *) data; \
|
||||
queueOrder(&(p->field), n); \
|
||||
}
|
||||
queueStep(step11, struct queueTestParams, order1, 1)
|
||||
queueStep(step12, struct queueTestParams, order1, 2)
|
||||
queueStep(step13, struct queueTestParams, order1, 3)
|
||||
queueStep(step14, struct queueTestParams, order1, 4)
|
||||
queueStep(step21, struct queueTestParams, order2, 1)
|
||||
queueStep(step22, struct queueTestParams, order2, 2)
|
||||
queueStep(step23, struct queueTestParams, order2, 3)
|
||||
queueStep(step24, struct queueTestParams, order2, 4)
|
||||
|
||||
static void queueOrder1(struct queueTestParams *p)
|
||||
{
|
||||
uiQueueMain(step11, p);
|
||||
uiQueueMain(step12, p);
|
||||
uiQueueMain(step13, p);
|
||||
uiQueueMain(step14, p);
|
||||
uiQueueMain(done, NULL);
|
||||
}
|
||||
|
||||
Test(QueueMain_Sequence)
|
||||
{
|
||||
struct queueTestParams p;
|
||||
|
||||
memset(&p, 0, sizeof (struct queueTestParams));
|
||||
queueOrder1(&p);
|
||||
uiMain();
|
||||
queueCheckOrder(&(p.order1), 4, 1, 2, 3, 4);
|
||||
}
|
||||
|
||||
static void queueOrder1ExceptStep2(void *data)
|
||||
{
|
||||
struct queueTestParams *p = (struct queueTestParams *) data;
|
||||
|
||||
uiQueueMain(step11, p);
|
||||
uiQueueMain(step13, p);
|
||||
uiQueueMain(step14, p);
|
||||
uiQueueMain(done, NULL);
|
||||
}
|
||||
|
||||
Test(QueueMain_SequenceWorksEvenWithFunctionsAlreadyQueued)
|
||||
{
|
||||
struct queueTestParams p;
|
||||
|
||||
memset(&p, 0, sizeof (struct queueTestParams));
|
||||
uiQueueMain(queueOrder1ExceptStep2, &p);
|
||||
uiQueueMain(step12, &p);
|
||||
uiMain();
|
||||
queueCheckOrder(&(p.order1), 4, 2, 1, 3, 4);
|
||||
}
|
||||
|
||||
static void queueThread(void *data)
|
||||
{
|
||||
struct simpleTestParams *p = (struct simpleTestParams *) data;
|
||||
|
||||
p->sleepErr = threadSleep(1250 * threadMillisecond);
|
||||
uiQueueMain(queueSimple, p);
|
||||
}
|
||||
|
||||
Test(QueueMain_DifferentThread)
|
||||
{
|
||||
threadThread *thread;
|
||||
threadSysError err;
|
||||
struct simpleTestParams p;
|
||||
|
||||
memset(&p, 0, sizeof (struct simpleTestParams));
|
||||
p.n = 0;
|
||||
err = threadNewThread(queueThread, &p, &thread);
|
||||
if (err != 0)
|
||||
TestFatalf("error creating thread: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
uiMain();
|
||||
err = threadThreadWaitAndFree(thread);
|
||||
if (err != 0)
|
||||
TestFatalf("error waiting for thread to finish: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
if (p.sleepErr != 0)
|
||||
// TODO is this really a good thing to do? we have a separate set of tests for launching the thread from within uiMain() now, so...
|
||||
TestErrorf("error sleeping in thread to ensure a high likelihood the uiQueueMain() is run after uiMain() starts: " threadSysErrorFmt, threadSysErrorFmtArg(p.sleepErr));
|
||||
switch (p.n) {
|
||||
case 0:
|
||||
TestErrorf("uiQueueMain() function was not called");
|
||||
break;
|
||||
case 1:
|
||||
// do nothing; this is the expected case
|
||||
break;
|
||||
default:
|
||||
TestErrorf("uiQueueMain() called more than once");
|
||||
}
|
||||
}
|
||||
|
||||
static void queueCreateThread(void *data)
|
||||
{
|
||||
struct simpleTestParams *p = (struct simpleTestParams *) data;
|
||||
|
||||
p->createErr = threadNewThread(queueThread, p, &(p->thread));
|
||||
if (p->createErr != 0)
|
||||
uiQuit();
|
||||
}
|
||||
|
||||
Test(QueueMain_DifferentThreadStartedByQueuedFunction)
|
||||
{
|
||||
threadSysError err;
|
||||
struct simpleTestParams p;
|
||||
|
||||
memset(&p, 0, sizeof (struct simpleTestParams));
|
||||
p.n = 0;
|
||||
uiQueueMain(queueCreateThread, &p);
|
||||
uiMain();
|
||||
if (p.createErr != 0)
|
||||
TestFatalf("error creating thread: " threadSysErrorFmt, threadSysErrorFmtArg(p.createErr));
|
||||
err = threadThreadWaitAndFree(p.thread);
|
||||
if (err != 0)
|
||||
TestFatalf("error waiting for thread to finish: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
if (p.sleepErr != 0)
|
||||
TestErrorf("error sleeping in thread to ensure a high likelihood the uiQueueMain() is run after uiMain() starts: " threadSysErrorFmt, threadSysErrorFmtArg(p.sleepErr));
|
||||
switch (p.n) {
|
||||
case 0:
|
||||
TestErrorf("uiQueueMain() function was not called");
|
||||
break;
|
||||
case 1:
|
||||
// do nothing; this is the expected case
|
||||
break;
|
||||
default:
|
||||
TestErrorf("uiQueueMain() called more than once");
|
||||
}
|
||||
}
|
||||
|
||||
static void queueOrder2(struct queueTestParams *p)
|
||||
{
|
||||
uiQueueMain(step23, p);
|
||||
uiQueueMain(step22, p);
|
||||
uiQueueMain(step24, p);
|
||||
uiQueueMain(step21, p);
|
||||
uiQueueMain(done, NULL);
|
||||
}
|
||||
|
||||
static void queueOrderThread(void *data)
|
||||
{
|
||||
struct queueTestParams *p = (struct queueTestParams *) data;
|
||||
|
||||
p->sleepErr1 = threadSleep(1250 * threadMillisecond);
|
||||
queueOrder2(p);
|
||||
}
|
||||
|
||||
Test(QueueMain_DifferentThreadSequence)
|
||||
{
|
||||
threadThread *thread;
|
||||
threadSysError err;
|
||||
struct queueTestParams p;
|
||||
|
||||
memset(&p, 0, sizeof (struct queueTestParams));
|
||||
err = threadNewThread(queueOrderThread, &p, &thread);
|
||||
if (err != 0)
|
||||
TestFatalf("error creating thread: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
uiMain();
|
||||
err = threadThreadWaitAndFree(thread);
|
||||
if (err != 0)
|
||||
TestFatalf("error waiting for thread to finish: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
if (p.sleepErr1 != 0)
|
||||
TestErrorf("error sleeping in thread to ensure a high likelihood the uiQueueMain() is run after uiMain() starts: " threadSysErrorFmt, threadSysErrorFmtArg(p.sleepErr1));
|
||||
queueCheckOrder(&(p.order2), 4, 3, 2, 4, 1);
|
||||
}
|
||||
|
||||
static void doneInterleaved(void *data)
|
||||
{
|
||||
struct queueTestParams *p = (struct queueTestParams *) data;
|
||||
|
||||
p->n++;
|
||||
if (p->n == 2)
|
||||
uiQuit();
|
||||
}
|
||||
|
||||
static void queueOrder1Interleaved(struct queueTestParams *p)
|
||||
{
|
||||
uiQueueMain(step13, p);
|
||||
uiQueueMain(step11, p);
|
||||
uiQueueMain(step12, p);
|
||||
uiQueueMain(step14, p);
|
||||
uiQueueMain(doneInterleaved, p);
|
||||
}
|
||||
|
||||
static void queueOrder2Interleaved(struct queueTestParams *p)
|
||||
{
|
||||
uiQueueMain(step24, p);
|
||||
uiQueueMain(step22, p);
|
||||
uiQueueMain(step21, p);
|
||||
uiQueueMain(step23, p);
|
||||
uiQueueMain(doneInterleaved, p);
|
||||
}
|
||||
|
||||
static void queueOrderThread1Interleaved(void *data)
|
||||
{
|
||||
struct queueTestParams *p = (struct queueTestParams *) data;
|
||||
|
||||
p->sleepErr1 = threadSleep(1250 * threadMillisecond);
|
||||
queueOrder1Interleaved(p);
|
||||
}
|
||||
|
||||
static void queueOrderThread2Interleaved(void *data)
|
||||
{
|
||||
struct queueTestParams *p = (struct queueTestParams *) data;
|
||||
|
||||
p->sleepErr2 = threadSleep(1250 * threadMillisecond);
|
||||
queueOrder2Interleaved(p);
|
||||
}
|
||||
|
||||
Test(QueueMain_DifferentThreadSequenceInterleaved)
|
||||
{
|
||||
threadThread *thread1, *thread2;
|
||||
threadSysError err;
|
||||
struct queueTestParams p;
|
||||
|
||||
memset(&p, 0, sizeof (struct queueTestParams));
|
||||
err = threadNewThread(queueOrderThread1Interleaved, &p, &thread1);
|
||||
if (err != 0)
|
||||
TestFatalf("error creating thread 1: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
err = threadNewThread(queueOrderThread2Interleaved, &p, &thread2);
|
||||
if (err != 0)
|
||||
TestFatalf("error creating thread 2: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
uiMain();
|
||||
err = threadThreadWaitAndFree(thread1);
|
||||
if (err != 0)
|
||||
TestFatalf("error waiting for thread 1 to finish: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
if (p.sleepErr1 != 0)
|
||||
TestErrorf("error sleeping in thread 1 to ensure a high likelihood the uiQueueMain() is run after uiMain() starts: " threadSysErrorFmt, threadSysErrorFmtArg(p.sleepErr1));
|
||||
err = threadThreadWaitAndFree(thread2);
|
||||
if (err != 0)
|
||||
TestFatalf("error waiting for thread 2 to finish: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
if (p.sleepErr2 != 0)
|
||||
TestErrorf("error sleeping in thread 2 to ensure a high likelihood the uiQueueMain() is run after uiMain() starts: " threadSysErrorFmt, threadSysErrorFmtArg(p.sleepErr2));
|
||||
queueCheckOrder(&(p.order1), 4, 3, 1, 2, 4);
|
||||
queueCheckOrder(&(p.order2), 4, 4, 2, 1, 3);
|
||||
}
|
||||
|
||||
static void queueCreateQueueThread(void *data)
|
||||
{
|
||||
struct queueTestParams *p = (struct queueTestParams *) data;
|
||||
|
||||
p->createErr = threadNewThread(queueOrderThread, p, &(p->thread));
|
||||
if (p->createErr != 0)
|
||||
uiQuit();
|
||||
}
|
||||
|
||||
// TODO make an Interleaved version of this test too
|
||||
Test(QueueMain_DifferentThreadSequenceStartedByQueuedFunction)
|
||||
{
|
||||
threadSysError err;
|
||||
struct queueTestParams p;
|
||||
|
||||
memset(&p, 0, sizeof (struct queueTestParams));
|
||||
uiQueueMain(queueCreateQueueThread, &p);
|
||||
uiMain();
|
||||
if (p.createErr != 0)
|
||||
TestFatalf("error creating thread: " threadSysErrorFmt, threadSysErrorFmtArg(p.createErr));
|
||||
err = threadThreadWaitAndFree(p.thread);
|
||||
if (err != 0)
|
||||
TestFatalf("error waiting for thread to finish: " threadSysErrorFmt, threadSysErrorFmtArg(err));
|
||||
if (p.sleepErr1 != 0)
|
||||
TestErrorf("error sleeping in thread to ensure a high likelihood the uiQueueMain() is run after uiMain() starts: " threadSysErrorFmt, threadSysErrorFmtArg(p.sleepErr1));
|
||||
queueCheckOrder(&(p.order2), 4, 3, 2, 4, 1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void timer(void *data)
|
||||
{
|
||||
int *n = (int *) data;
|
||||
|
||||
// TODO return stop if n == 5, continue otherwise
|
||||
*n++;
|
||||
}
|
||||
|
||||
testingTest(Timer)
|
||||
{
|
||||
}
|
||||
#endif
|
259
test/main.c
259
test/main.c
|
@ -1,181 +1,112 @@
|
|||
// 22 april 2015
|
||||
// 19 january 2020
|
||||
#include "test.h"
|
||||
|
||||
// TODOs
|
||||
// - blank page affects menus negatively on Windows
|
||||
// Do not put any test cases in this file; they will not be run.
|
||||
|
||||
void die(const char *fmt, ...)
|
||||
static int testcmp(const void *aa, const void *bb)
|
||||
{
|
||||
const struct testingprivCase *a = (const struct testingprivCase *) aa;
|
||||
const struct testingprivCase *b = (const struct testingprivCase *) bb;
|
||||
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
||||
|
||||
struct defer {
|
||||
void (*f)(void *data);
|
||||
void *data;
|
||||
struct defer *next;
|
||||
};
|
||||
|
||||
static struct defer *defers = NULL;
|
||||
|
||||
static void runDefers(void)
|
||||
{
|
||||
struct defer *d;
|
||||
|
||||
for (d = defers; d != NULL; d = d->next)
|
||||
(*(d->f))(d->data);
|
||||
}
|
||||
|
||||
static int testingprivRet = 0;
|
||||
|
||||
void TestFail(void)
|
||||
{
|
||||
testingprivRet = 1;
|
||||
}
|
||||
|
||||
void TestFailNow(void)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void TestSkipNow(void)
|
||||
{
|
||||
// see https://mesonbuild.com/Unit-tests.html#skipped-tests-and-hard-errors
|
||||
exit(77);
|
||||
}
|
||||
|
||||
void TestDefer(void (*f)(void *data), void *data)
|
||||
{
|
||||
struct defer *d;
|
||||
|
||||
d = (struct defer *) malloc(sizeof (struct defer));
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "** internal error: memory exhausted in TestDefer()\n");
|
||||
abort();
|
||||
}
|
||||
memset(d, 0, sizeof (struct defer));
|
||||
d->f = f;
|
||||
d->data = data;
|
||||
d->next = defers;
|
||||
defers = d;
|
||||
}
|
||||
|
||||
static const char *basename(const char *file)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
for (;;) {
|
||||
p = strpbrk(file, "/\\");
|
||||
if (p == NULL)
|
||||
break;
|
||||
file = p + 1;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
void testingprivLogfFullThen(FILE *f, void (*then)(void), const char *filename, long line, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "[test program] ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(f, "%s:%ld: ", basename(filename), line);
|
||||
vfprintf(f, fmt, ap);
|
||||
fprintf(f, "\n");
|
||||
va_end(ap);
|
||||
abort();
|
||||
if (then != NULL)
|
||||
(*then)();
|
||||
}
|
||||
|
||||
int onClosing(uiWindow *w, void *data)
|
||||
{
|
||||
printf("in onClosing()\n");
|
||||
uiQuit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int onShouldQuit(void *data)
|
||||
{
|
||||
printf("in onShouldQuit()\n");
|
||||
if (uiMenuItemChecked(shouldQuitItem)) {
|
||||
uiControlDestroy(uiControl(data));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uiBox *mainBox;
|
||||
uiTab *mainTab;
|
||||
|
||||
uiBox *(*newhbox)(void);
|
||||
uiBox *(*newvbox)(void);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
uiInitOptions o;
|
||||
int i;
|
||||
const char *err;
|
||||
uiWindow *w;
|
||||
uiBox *page2, *page3, *page4, *page5;
|
||||
uiBox *page6, *page7, *page8, *page9, *page10;
|
||||
uiBox *page11, *page12, *page13;
|
||||
uiTab *page14;
|
||||
uiBox *page15;
|
||||
uiBox *page16;
|
||||
uiTab *outerTab;
|
||||
uiTab *innerTab;
|
||||
int nomenus = 0;
|
||||
int startspaced = 0;
|
||||
int steps = 0;
|
||||
struct testingprivCase *t;
|
||||
struct testingprivCase want;
|
||||
|
||||
newhbox = uiNewHorizontalBox;
|
||||
newvbox = uiNewVerticalBox;
|
||||
|
||||
memset(&o, 0, sizeof (uiInitOptions));
|
||||
for (i = 1; i < argc; i++)
|
||||
if (strcmp(argv[i], "nomenus") == 0)
|
||||
nomenus = 1;
|
||||
else if (strcmp(argv[i], "startspaced") == 0)
|
||||
startspaced = 1;
|
||||
else if (strcmp(argv[i], "swaphv") == 0) {
|
||||
newhbox = uiNewVerticalBox;
|
||||
newvbox = uiNewHorizontalBox;
|
||||
} else if (strcmp(argv[i], "steps") == 0)
|
||||
steps = 1;
|
||||
else {
|
||||
fprintf(stderr, "%s: unrecognized option %s\n", argv[0], argv[i]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = uiInit(&o);
|
||||
if (err != NULL) {
|
||||
fprintf(stderr, "error initializing ui: %s\n", err);
|
||||
uiFreeInitError(err);
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s TestName\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!nomenus)
|
||||
initMenus();
|
||||
|
||||
w = newWindow("Main Window", 320, 240, 1);
|
||||
uiWindowOnClosing(w, onClosing, NULL);
|
||||
printf("main window %p\n", (void *) w);
|
||||
|
||||
uiOnShouldQuit(onShouldQuit, w);
|
||||
|
||||
mainBox = newHorizontalBox();
|
||||
uiWindowSetChild(w, uiControl(mainBox));
|
||||
|
||||
outerTab = newTab();
|
||||
uiBoxAppend(mainBox, uiControl(outerTab), 1);
|
||||
|
||||
mainTab = newTab();
|
||||
uiTabAppend(outerTab, "Pages 1-5", uiControl(mainTab));
|
||||
|
||||
// page 1 uses page 2's uiGroup
|
||||
page2 = makePage2();
|
||||
|
||||
makePage1(w);
|
||||
uiTabAppend(mainTab, "Page 1", uiControl(page1));
|
||||
|
||||
uiTabAppend(mainTab, "Page 2", uiControl(page2));
|
||||
|
||||
uiTabAppend(mainTab, "Empty Page", uiControl(uiNewHorizontalBox()));
|
||||
|
||||
page3 = makePage3();
|
||||
uiTabAppend(mainTab, "Page 3", uiControl(page3));
|
||||
|
||||
page4 = makePage4();
|
||||
uiTabAppend(mainTab, "Page 4", uiControl(page4));
|
||||
|
||||
page5 = makePage5(w);
|
||||
uiTabAppend(mainTab, "Page 5", uiControl(page5));
|
||||
|
||||
innerTab = newTab();
|
||||
uiTabAppend(outerTab, "Pages 6-10", uiControl(innerTab));
|
||||
|
||||
page6 = makePage6();
|
||||
uiTabAppend(innerTab, "Page 6", uiControl(page6));
|
||||
|
||||
page7 = makePage7();
|
||||
uiTabAppend(innerTab, "Page 7", uiControl(page7));
|
||||
|
||||
/* page8 = makePage8();
|
||||
uiTabAppend(innerTab, "Page 8", uiControl(page8));
|
||||
|
||||
page9 = makePage9();
|
||||
uiTabAppend(innerTab, "Page 9", uiControl(page9));
|
||||
|
||||
page10 = makePage10();
|
||||
uiTabAppend(innerTab, "Page 10", uiControl(page10));
|
||||
*/
|
||||
innerTab = newTab();
|
||||
uiTabAppend(outerTab, "Pages 11-15", uiControl(innerTab));
|
||||
|
||||
// page11 = makePage11();
|
||||
// uiTabAppend(innerTab, "Page 11", uiControl(page11));
|
||||
|
||||
page12 = makePage12();
|
||||
uiTabAppend(innerTab, "Page 12", uiControl(page12));
|
||||
|
||||
page13 = makePage13();
|
||||
uiTabAppend(innerTab, "Page 13", uiControl(page13));
|
||||
|
||||
page14 = makePage14();
|
||||
uiTabAppend(innerTab, "Page 14", uiControl(page14));
|
||||
|
||||
page15 = makePage15(w);
|
||||
uiTabAppend(innerTab, "Page 15", uiControl(page15));
|
||||
|
||||
innerTab = newTab();
|
||||
uiTabAppend(outerTab, "Pages 16-?", uiControl(innerTab));
|
||||
|
||||
page16 = makePage16();
|
||||
uiTabAppend(innerTab, "Page 16", uiControl(page16));
|
||||
|
||||
if (startspaced)
|
||||
setSpaced(1);
|
||||
|
||||
uiControlShow(uiControl(w));
|
||||
if (!steps)
|
||||
uiMain();
|
||||
else {
|
||||
uiMainSteps();
|
||||
while (uiMainStep(1))
|
||||
;
|
||||
want.name = argv[1];
|
||||
t = (struct testingprivCase *) bsearch(&want, testingprivCases, testingprivNumCases, sizeof (struct testingprivCase), testcmp);
|
||||
if (t == NULL) {
|
||||
fprintf(stderr, "%s: no such test\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
printf("after uiMain()\n");
|
||||
freePage16();
|
||||
uiUninit();
|
||||
printf("after uiUninit()\n");
|
||||
return 0;
|
||||
testingprivRet = 0;
|
||||
if (atexit(runDefers) != 0) {
|
||||
fprintf(stderr, "atexit(runDefers) failed (errno %d); can't test\n", errno);
|
||||
return 1;
|
||||
}
|
||||
(*(t->f))();
|
||||
return testingprivRet;
|
||||
}
|
||||
|
|
179
test/meson.build
179
test/meson.build
|
@ -1,48 +1,151 @@
|
|||
# 23 march 2019
|
||||
|
||||
libui_test_sources = [
|
||||
'drawtests.c',
|
||||
'images.c',
|
||||
'main.c',
|
||||
'menus.c',
|
||||
'page1.c',
|
||||
'page2.c',
|
||||
'page3.c',
|
||||
'page4.c',
|
||||
'page5.c',
|
||||
'page6.c',
|
||||
'page7.c',
|
||||
'page7a.c',
|
||||
'page7b.c',
|
||||
'page7c.c',
|
||||
# 'page8.c',
|
||||
# 'page9.c',
|
||||
# 'page10.c',
|
||||
'page11.c',
|
||||
'page12.c',
|
||||
'page13.c',
|
||||
'page14.c',
|
||||
'page15.c',
|
||||
'page16.c',
|
||||
'spaced.c',
|
||||
]
|
||||
# 19 january 2020
|
||||
|
||||
# Using files() is the cleanest way to ensure the python script below gets the right filenames regardless of how meson sandboxes the command it's running.
|
||||
libui_test_sources = files([
|
||||
'controls.c',
|
||||
'initmain.c',
|
||||
'noinitwrongthread.c',
|
||||
'window.c',
|
||||
])
|
||||
libui_allcalls_headers = files([
|
||||
'allcalls.h',
|
||||
])
|
||||
if libui_OS == 'windows'
|
||||
libui_test_manifest = 'test.manifest'
|
||||
if libui_mode == 'static'
|
||||
libui_test_manifest = 'test.static.manifest'
|
||||
endif
|
||||
libui_test_sources += [
|
||||
windows.compile_resources('resources.rc',
|
||||
libui_test_sources += files([
|
||||
'controls_windows.c',
|
||||
'noinitwrongthread_windows.c',
|
||||
'window_windows.c',
|
||||
])
|
||||
libui_allcalls_headers += files([
|
||||
'allcalls_windows.h',
|
||||
])
|
||||
elif libui_OS == 'darwin'
|
||||
libui_test_sources += files([
|
||||
'controls_darwin.m',
|
||||
'noinitwrongthread_darwin.m',
|
||||
'window_darwin.m',
|
||||
])
|
||||
libui_allcalls_headers += files([
|
||||
'allcalls_darwin.h',
|
||||
])
|
||||
elif libui_OS == 'haiku'
|
||||
libui_test_sources += files([
|
||||
'controls_haiku.cpp',
|
||||
'noinitwrongthread_haiku.cpp',
|
||||
'window_haiku.cpp',
|
||||
])
|
||||
libui_allcalls_headers += files([
|
||||
'allcalls_haiku.hpp',
|
||||
])
|
||||
else
|
||||
libui_test_sources += files([
|
||||
'controls_unix.c',
|
||||
'noinitwrongthread_unix.c',
|
||||
'window_unix.c',
|
||||
])
|
||||
libui_allcalls_headers += files([
|
||||
'allcalls_unix.h',
|
||||
])
|
||||
endif
|
||||
|
||||
libui_test_sources_without_cases = [
|
||||
'errors.c',
|
||||
'main.c',
|
||||
'utf8.c',
|
||||
]
|
||||
if libui_OS == 'windows'
|
||||
libui_test_sources_without_cases += [
|
||||
'thread_windows.c',
|
||||
'utf8_windows.c',
|
||||
windows.compile_resources('resources_' + libui_mode + '.rc',
|
||||
args: libui_manifest_args,
|
||||
depend_files: [libui_test_manifest]),
|
||||
depend_files: ['test_' + libui_mode + '.manifest']),
|
||||
]
|
||||
else
|
||||
libui_test_sources_without_cases += [
|
||||
'thread_notwindows.c',
|
||||
]
|
||||
endif
|
||||
|
||||
# TODO meson doesn't let us name this target test, but also doesn't seem to provide a way to override the executable name???? we'll probably need to file a feature request for this
|
||||
libui_test_deps = [
|
||||
dependency('threads',
|
||||
required: true),
|
||||
]
|
||||
# TODO deduplicate these
|
||||
if libui_OS == 'windows'
|
||||
# static mode already gives us these dependencies
|
||||
if libui_mode != 'static'
|
||||
libui_test_deps += [
|
||||
meson.get_compiler('c').find_library('kernel32',
|
||||
required: true),
|
||||
meson.get_compiler('c').find_library('user32',
|
||||
required: true),
|
||||
]
|
||||
endif
|
||||
elif libui_OS == 'darwin'
|
||||
# static mode already gives us these dependencies
|
||||
if libui_mode != 'static'
|
||||
libui_test_deps += [
|
||||
meson.get_compiler('objc').find_library('objc',
|
||||
required: true),
|
||||
dependency('appleframeworks',
|
||||
modules: ['Foundation', 'AppKit'],
|
||||
required: true),
|
||||
]
|
||||
endif
|
||||
elif libui_OS == 'haiku'
|
||||
# static mode already gives us these dependencies
|
||||
if libui_mode != 'static'
|
||||
libui_test_deps += [
|
||||
meson.get_compiler('cpp').find_library('root',
|
||||
required: true),
|
||||
meson.get_compiler('cpp').find_library('be',
|
||||
required: true),
|
||||
]
|
||||
endif
|
||||
else
|
||||
# static mode already gives us these dependencies
|
||||
if libui_mode != 'static'
|
||||
libui_test_deps += [
|
||||
dependency('gtk+-3.0',
|
||||
version: '>=3.10.0',
|
||||
method: 'pkg-config',
|
||||
required: true),
|
||||
]
|
||||
endif
|
||||
endif
|
||||
|
||||
pymod = import('python')
|
||||
python = pymod.find_installation()
|
||||
# Likewise, use files() here to make sure the Python script itself will always have the right path.
|
||||
libui_testlist_py = files(['testlist.py'])
|
||||
|
||||
libui_testlist_h = custom_target(
|
||||
'testlist.h',
|
||||
input: libui_test_sources + libui_allcalls_headers,
|
||||
output: ['testlist.h'],
|
||||
command: [python, libui_testlist_py, 'header', '@OUTPUT@', '@INPUT@'])
|
||||
libui_testlist_c = custom_target(
|
||||
'testlist.c',
|
||||
input: libui_test_sources + libui_allcalls_headers,
|
||||
output: ['testlist.c'],
|
||||
command: [python, libui_testlist_py, 'source', '@OUTPUT@', '@INPUT@'])
|
||||
|
||||
# TODO once we upgrade to 0.49.0, add pie: true
|
||||
executable('tester', libui_test_sources,
|
||||
dependencies: libui_binary_deps,
|
||||
# TODO once we upgrade to 0.50.0, add protocol: 'exitcode'
|
||||
libui_testrunner = executable('testrunner', libui_test_sources + libui_test_sources_without_cases + [libui_testlist_c, libui_testlist_h],
|
||||
dependencies: libui_binary_deps + libui_test_deps,
|
||||
link_with: libui_libui,
|
||||
gui_app: false,
|
||||
install: false)
|
||||
|
||||
runresult = run_command(python, libui_testlist_py + ['list'] + libui_test_sources + libui_allcalls_headers)
|
||||
if runresult.returncode() != 0
|
||||
error('testlist.py failed; cannot compute list of test cases. Exit code @0@; stderr:\n@1@'.format(runresult.returncode(), runresult.stderr()))
|
||||
endif
|
||||
foreach casename : runresult.stdout().split()
|
||||
test(casename, libui_testrunner,
|
||||
args: [casename],
|
||||
is_parallel: false,
|
||||
should_fail: false)
|
||||
endforeach
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
// 28 may 2019
|
||||
#include "test.h"
|
||||
#include "thread.h"
|
||||
|
||||
#define allcallsHeader "allcalls.h"
|
||||
#include "noinitwrongthreadimpl.h"
|
||||
|
||||
TestNoInit(CallBeforeInitIsProgrammerError_uiQueueMain)
|
||||
{
|
||||
void *ctx;
|
||||
|
||||
ctx = beginCheckProgrammerError("attempt to call uiQueueMain() before uiInit()");
|
||||
uiQueueMain(NULL, NULL);
|
||||
endCheckProgrammerError(ctx);
|
||||
}
|
||||
|
||||
// no uiQueueMain() test for the wrong thread; it's supposed to be callable from any thread
|
|
@ -0,0 +1,6 @@
|
|||
// 28 may 2019
|
||||
#import "test_darwin.h"
|
||||
#import "thread.h"
|
||||
|
||||
#define allcallsHeader "allcalls_darwin.h"
|
||||
#include "noinitwrongthreadimpl.h"
|
|
@ -0,0 +1,6 @@
|
|||
// 28 may 2019
|
||||
#include "test_haiku.hpp"
|
||||
#include "thread.h"
|
||||
|
||||
#define allcallsHeader "allcalls_haiku.hpp"
|
||||
#include "noinitwrongthreadimpl.h"
|
|
@ -0,0 +1,6 @@
|
|||
// 28 may 2019
|
||||
#include "test_unix.h"
|
||||
#include "thread.h"
|
||||
|
||||
#define allcallsHeader "allcalls_unix.h"
|
||||
#include "noinitwrongthreadimpl.h"
|
|
@ -0,0 +1,6 @@
|
|||
// 28 may 2019
|
||||
#include "test_windows.h"
|
||||
#include "thread.h"
|
||||
|
||||
#define allcallsHeader "allcalls_windows.h"
|
||||
#include "noinitwrongthreadimpl.h"
|
|
@ -0,0 +1,37 @@
|
|||
// 28 may 2019
|
||||
// define allcallsHeader and include test.h and thread.h before including this
|
||||
|
||||
// TODO rename to FunctionsFailBeforeInit?
|
||||
#define allcallsCase(f, ...) \
|
||||
TestNoInit(CallBeforeInitIsProgrammerError_ ## f) \
|
||||
{ \
|
||||
void *ctx; \
|
||||
ctx = beginCheckProgrammerError("attempt to call " #f "() before uiInit()"); \
|
||||
f(__VA_ARGS__); \
|
||||
endCheckProgrammerError(ctx); \
|
||||
}
|
||||
#include allcallsHeader
|
||||
#undef allcallsCase
|
||||
|
||||
// TODO rename to FunctionsFailOnWrongThread?
|
||||
#define allcallsCase(f, ...) \
|
||||
static void threadTest ## f(void *data) \
|
||||
{ \
|
||||
f(__VA_ARGS__); \
|
||||
} \
|
||||
Test(CallOnWrongThreadIsProgrammerError_ ## f) \
|
||||
{ \
|
||||
threadThread *thread; \
|
||||
threadSysError err; \
|
||||
void *ctx; \
|
||||
ctx = beginCheckProgrammerError("attempt to call " #f "() on a thread other than the GUI thread"); \
|
||||
err = threadNewThread(threadTest ## f, NULL, &thread); \
|
||||
if (err != 0) \
|
||||
TestFatalf("error creating thread: " threadSysErrorFmt, threadSysErrorFmtArg(err)); \
|
||||
err = threadThreadWaitAndFree(thread); \
|
||||
if (err != 0) \
|
||||
TestFatalf("error waiting for thread to finish: " threadSysErrorFmt, threadSysErrorFmtArg(err)); \
|
||||
endCheckProgrammerError(ctx); \
|
||||
}
|
||||
#include allcallsHeader
|
||||
#undef allcallsCase
|
|
@ -1,13 +0,0 @@
|
|||
// 30 may 2015
|
||||
|
||||
// this is a UTF-8 file
|
||||
#pragma code_page(65001)
|
||||
|
||||
// this is the Common Controls 6 manifest
|
||||
// TODO set up the string values here
|
||||
// 1 is the value of CREATEPROCESS_MANIFEST_RESOURCE_ID and 24 is the value of RT_MANIFEST; we use it directly to avoid needing to share winapi.h with the tests and examples
|
||||
#ifndef _UI_STATIC
|
||||
1 24 "test.manifest"
|
||||
#else
|
||||
1 24 "test.static.manifest"
|
||||
#endif
|
|
@ -0,0 +1,9 @@
|
|||
// 30 may 2015
|
||||
|
||||
// This is a UTF-8 file.
|
||||
#pragma code_page(65001)
|
||||
|
||||
// This is the Common Controls 6 manifest.
|
||||
// 1 is the value of CREATEPROCESS_MANIFEST_RESOURCE_ID and 24 is the value of RT_MANIFEST; we use it directly to avoid needing to share winapi.h with the tests and examples.
|
||||
// We also don't define Common Controls v6 in the test program's shared manifest to ensure that the isolation awareness actually does work.
|
||||
1 24 "test_shared.manifest"
|
|
@ -0,0 +1,8 @@
|
|||
// 30 may 2015
|
||||
|
||||
// This is a UTF-8 file.
|
||||
#pragma code_page(65001)
|
||||
|
||||
// This is the Common Controls 6 manifest.
|
||||
// 1 is the value of CREATEPROCESS_MANIFEST_RESOURCE_ID and 24 is the value of RT_MANIFEST; we use it directly to avoid needing to share winapi.h with the tests and examples.
|
||||
1 24 "test_static.manifest"
|
181
test/test.h
181
test/test.h
|
@ -1,98 +1,121 @@
|
|||
// 22 april 2015
|
||||
// 27 february 2018
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include "../ui.h"
|
||||
#ifdef libuiOSHeader
|
||||
#include libuiOSHeader
|
||||
#endif
|
||||
#include "../common/testhooks.h"
|
||||
|
||||
// main.c
|
||||
extern void die(const char *, ...);
|
||||
extern uiBox *mainBox;
|
||||
extern uiTab *mainTab;
|
||||
extern uiBox *(*newhbox)(void);
|
||||
extern uiBox *(*newvbox)(void);
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// spaced.c
|
||||
extern void setSpaced(int);
|
||||
extern void querySpaced(char[12]);
|
||||
extern uiWindow *newWindow(const char *title, int width, int height, int hasMenubar);
|
||||
extern uiBox *newHorizontalBox(void);
|
||||
extern uiBox *newVerticalBox(void);
|
||||
extern uiTab *newTab(void);
|
||||
extern uiGroup *newGroup(const char *);
|
||||
extern uiForm *newForm(void);
|
||||
extern uiGrid *newGrid(void);
|
||||
#define testingprivImplName(basename) testingprivImpl ## basename
|
||||
#define testingprivScaffoldName(basename) testingprivScaffold ## basename
|
||||
|
||||
// menus.c
|
||||
extern uiMenuItem *shouldQuitItem;
|
||||
extern void initMenus(void);
|
||||
#define Test(Name) \
|
||||
static void testingprivImplName(Test ## Name)(void); \
|
||||
void testingprivScaffoldName(Test ## Name)(void) \
|
||||
{ \
|
||||
uiInitError err; \
|
||||
memset(&err, 0, sizeof (uiInitError)); \
|
||||
err.Size = sizeof (uiInitError); \
|
||||
if (!uiInit(NULL, &err)) { \
|
||||
fprintf(stderr, "error initializing libui for Test" #Name ": %s\n", err.Message); \
|
||||
TestFailNow(); \
|
||||
} \
|
||||
testingprivImplName(Test ## Name)(); \
|
||||
} \
|
||||
static void testingprivImplName(Test ## Name)(void)
|
||||
|
||||
// page1.c
|
||||
extern uiBox *page1;
|
||||
extern void makePage1(uiWindow *);
|
||||
#define TestNoInit(Name) \
|
||||
static void testingprivImplName(Test ## Name)(void); \
|
||||
void testingprivScaffoldName(Test ## Name)(void) \
|
||||
{ \
|
||||
testingprivImplName(Test ## Name)(); \
|
||||
} \
|
||||
static void testingprivImplName(Test ## Name)(void)
|
||||
|
||||
// page2.c
|
||||
extern uiGroup *page2group;
|
||||
extern uiBox *makePage2(void);
|
||||
extern void TestFail(void);
|
||||
extern void TestFailNow(void);
|
||||
extern void TestSkipNow(void);
|
||||
|
||||
// page3.c
|
||||
extern uiBox *makePage3(void);
|
||||
#define TestLogf(...) \
|
||||
(testingprivLogfFullThen(stdout, NULL, __FILE__, __LINE__, __VA_ARGS__))
|
||||
#define TestLogfFull(f, n, ...) \
|
||||
(testingprivLogfFullThen(stdout, NULL, f, n, __VA_ARGS__))
|
||||
#define TestErrorf(...) \
|
||||
(testingprivLogfFullThen(stderr, TestFail, __FILE__, __LINE__, __VA_ARGS__))
|
||||
#define TestErrorfFull(f, n, ...) \
|
||||
(testingprivLogfFullThen(stderr, TestFail, f, n, __VA_ARGS__))
|
||||
#define TestFatalf(...) \
|
||||
(testingprivLogfFullThen(stderr, TestFailNow, __FILE__, __LINE__, __VA_ARGS__))
|
||||
#define TestFatalfFull(f, n, ...) \
|
||||
(testingprivLogfFullThen(stderr, TestFailNow, f, n, __VA_ARGS__))
|
||||
// TODO remember if this needs to go to stdout or to stderr
|
||||
#define TestSkipf(...) \
|
||||
(testingprivLogfFullThen(stderr, TestSkipNow, __FILE__, __LINE__, __VA_ARGS__))
|
||||
// TODO TestSkipfFull (after resolving above TODO)
|
||||
|
||||
// page4.c
|
||||
extern uiBox *makePage4(void);
|
||||
extern void TestDefer(void (*f)(void *data), void *data);
|
||||
|
||||
// page5.c
|
||||
extern uiBox *makePage5(uiWindow *);
|
||||
#include "../sharedbits/printfwarn_header.h"
|
||||
sharedbitsPrintfFunc(
|
||||
extern void testingprivLogfFullThen(FILE *f, void (*then)(void), const char *filename, long line, const char *fmt, ...),
|
||||
5, 6);
|
||||
#undef sharedbitsPrintfFunc
|
||||
|
||||
// page6.c
|
||||
extern uiBox *makePage6(void);
|
||||
#include "testlist.h"
|
||||
|
||||
// drawtests.c
|
||||
extern void runDrawTest(int, uiAreaDrawParams *);
|
||||
extern void populateComboboxWithTests(uiCombobox *);
|
||||
// end of test framework definitions
|
||||
|
||||
// page7.c
|
||||
extern uiBox *makePage7(void);
|
||||
#define diff(fmt) "\ngot " fmt "\nwant " fmt
|
||||
|
||||
// page7a.c
|
||||
extern uiGroup *makePage7a(void);
|
||||
// errors.c
|
||||
extern void *beginCheckProgrammerError(const char *want);
|
||||
extern void endCheckProgrammerErrorFull(const char *file, long line, void *context);
|
||||
#define endCheckProgrammerError(context) endCheckProgrammerErrorFull(__FILE__, __LINE__, context)
|
||||
|
||||
// page7b.c
|
||||
extern uiGroup *makePage7b(void);
|
||||
// controls.c
|
||||
struct testControlImplData {
|
||||
const uiControlVtable *realVtable;
|
||||
const uiControlOSVtable *realOSVtable;
|
||||
void *realImplData;
|
||||
};
|
||||
extern const uiControlVtable *testControlVtable(void);
|
||||
extern const uiControlOSVtable *testControlOSVtable(void);
|
||||
extern uint32_t testControlType(void);
|
||||
|
||||
// page7c.c
|
||||
extern uiGroup *makePage7c(void);
|
||||
// utf8.c
|
||||
extern const char testUTF8Empty[];
|
||||
extern const char testUTF8ASCIIOnly[];
|
||||
extern const char testUTF8WithTwoByte[];
|
||||
extern const char testUTF8WithThreeByte[];
|
||||
extern const char testUTF8WithFourByte[];
|
||||
extern const char testUTF8Combined[];
|
||||
extern const char testUTF8InvalidInput[];
|
||||
extern const char testUTF8InvalidOutput[];
|
||||
extern const uint16_t testUTF16Empty[];
|
||||
extern const uint16_t testUTF16ASCIIOnly[];
|
||||
extern const uint16_t testUTF16WithTwoByte[];
|
||||
extern const uint16_t testUTF16WithThreeByte[];
|
||||
extern const uint16_t testUTF16WithFourByte[];
|
||||
extern const uint16_t testUTF16Combined[];
|
||||
extern const uint16_t testUTF16InvalidOutput[];
|
||||
extern bool utf8equal(const char *s, const char *t);
|
||||
extern void utf8diffErrorFull(const char *file, long line, const char *msg, const char *got, const char *want);
|
||||
#define utf8diffError(msg, got, want) utf8diffErrorFull(__FILE__, __LINE__, msg, got, want)
|
||||
extern bool utf16equal(const uint16_t *s, const uint16_t *t);
|
||||
extern void utf16diffErrorFull(const char *file, long line, const char *msg, const uint16_t *got, const uint16_t *want);
|
||||
#define utf16diffError(msg, got, want) utf16diffErrorFull(__FILE__, __LINE__, msg, got, want)
|
||||
|
||||
// page8.c
|
||||
extern uiBox *makePage8(void);
|
||||
|
||||
// page9.c
|
||||
extern uiBox *makePage9(void);
|
||||
|
||||
// page10.c
|
||||
extern uiBox *makePage10(void);
|
||||
|
||||
// page11.c
|
||||
extern uiBox *makePage11(void);
|
||||
|
||||
// page12.c
|
||||
extern uiBox *makePage12(void);
|
||||
|
||||
// page13.c
|
||||
extern uiBox *makePage13(void);
|
||||
|
||||
// page14.c
|
||||
extern uiTab *makePage14(void);
|
||||
|
||||
// page15.c
|
||||
extern uiBox *makePage15(uiWindow *);
|
||||
|
||||
// page16.c
|
||||
extern uiBox *makePage16(void);
|
||||
extern void freePage16(void);
|
||||
|
||||
// images.c
|
||||
extern void appendImageNamed(uiImage *img, const char *name);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
// 10 june 2019
|
||||
// TODO proper macros and headers
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#define libuiOSHeader "../ui_darwin.h"
|
||||
#import "test.h"
|
|
@ -0,0 +1,8 @@
|
|||
// 18 january 2020
|
||||
// TODO proper macros and headers
|
||||
#include <os/AppKit.h>
|
||||
#include <os/InterfaceKit.h>
|
||||
#include <os/KernelKit.h>
|
||||
#include <os/SupportKit.h>
|
||||
#define libuiOSHeader "../ui_haiku.h"
|
||||
#include "test.h"
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue