Compare commits

...

485 Commits

Author SHA1 Message Date
Pietro Gagliardi d80f0e4812 And applied the test changes to the other platforms. Back to focusing on Windows now. 2022-07-30 18:52:04 -04:00
Pietro Gagliardi c241e8676a Restructured the controls tests to allow for more flexibility and implemented that on Windows. Now I can start building ad-hoc controls in tests. 2022-07-30 18:40:10 -04:00
Pietro Gagliardi 79d8eb534e Filled in some allcalls. I'll make the edits to controls.c its own commit. 2022-07-30 17:39:37 -04:00
Pietro Gagliardi eb1250a32b Fixed the build and implemented some of the missing functions I just added. Not too happy with how these tests are structured so far; I might hack on that before continuing... 2022-07-30 12:13:36 -04:00
Pietro Gagliardi 1d7c530c32 More implementation of the new uiWindows control parenting and sizing system. 2022-07-30 04:14:16 -04:00
Pietro Gagliardi 8823ebacfb Drafted an API for Windows resizes. 2022-07-29 23:53:36 -04:00
Pietro Gagliardi c9310e21b7 Implemented parenting on Windows; doing everything through uiControlSetParent() makes this trivial. Now I need to implement sizing, so we can actually test some things with this. 2022-07-29 22:28:38 -04:00
Pietro Gagliardi 7c0113f220 Decided to use uiControlSetParent() to signal that parent handles need to be changed on Windows. Now we can start implementing this model and taking it for a spin. 2022-07-29 00:38:36 -04:00
Pietro Gagliardi f3820ac4b0 Okay new plan: we're going to start with the Windows implementation instead, since that's the one that's going to have to be more complicated. I'll then decide if we should copy this strategy on the other platforms or not. 2022-07-27 21:43:15 -04:00
Pietro Gagliardi 69dbafe6f2 Oops forgot to commit this before midnight: started work on uiWindow children. 2022-07-26 00:18:37 -04:00
Pietro Gagliardi 8b3e2665ed Good night, friend.
I meant to do this a year ago, but didn't, because the pandemic rotted my brain. I'm sorry.

This project is probably not big enough for something like this, but not doing this would be irresponsible, given the circumstances.
2022-07-21 20:42:13 -04:00
Pietro Gagliardi 4057d3e0d3 More notes. 2021-06-16 09:25:04 -04:00
Pietro Gagliardi f6525082a9 More notes. 2021-05-27 20:26:58 -04:00
Pietro Gagliardi 320c92b8eb More notes. I need to work on this again. 2021-05-26 12:27:21 -04:00
Pietro Gagliardi 8f8171550d More notes. 2021-05-22 09:22:19 -04:00
Pietro Gagliardi 7c7da65231 More notes. 2021-02-02 12:02:12 -05:00
Pietro Gagliardi fda1446114 More notes. Yeah, I didn't get to finish remodel by the end of the year; sorry. 2020-12-31 22:59:28 -05:00
Pietro Gagliardi 3005c00376 More notes. 2020-11-23 11:50:25 -05:00
Pietro Gagliardi 76879e7c82 More notes. 2020-11-18 01:04:30 -05:00
Pietro Gagliardi 81fb4ea7f2 More notes. 2020-10-05 23:33:24 -04:00
Pietro Gagliardi 47f2bffd8a Merge branch 'master' into remodel
Conflicts:
	windows/winapi.hpp
(manually resolved by elaborating comment)
2020-10-02 10:30:58 -04:00
Pietro Gagliardi afb53d88c8 Merge branch 'master' into remodel 2020-06-07 21:09:34 -04:00
Pietro Gagliardi 5ca4ccb30b Merge branch 'master' into remodel 2020-06-07 21:06:50 -04:00
Pietro Gagliardi 6f4610f82a Oops 2020-06-07 20:58:11 -04:00
Pietro Gagliardi 1c7718ec58 And updated the tests on Windows. It works! 2020-06-07 14:36:56 -04:00
Pietro Gagliardi 7cdd6ee38c Finished all the necessary bits to implement what little of uiWindow we have now on Windows. Now to fill in the tests. 2020-06-07 14:10:16 -04:00
Pietro Gagliardi 188d9f736f Reintegrated UTF-16 stuff we need immediately and prepared the test suite for it. 2020-06-06 22:30:50 -04:00
Pietro Gagliardi 3f392d04ce Started migrating uiWindow on Windows. Oh boy it's UTF-16 time! 2020-06-06 19:27:05 -04:00
Pietro Gagliardi d5c06108d6 Added the missing noinitwrongthread Unix tests. 2020-06-02 22:52:56 -04:00
Pietro Gagliardi 1dd0372593 Set up a new system for designated static const struct initializers on C++. Also added the missing noinitwrongthread tests on Haiku. 2020-06-02 22:40:54 -04:00
Pietro Gagliardi c07a7861d4 More TODOs. 2020-05-31 19:49:59 -04:00
Pietro Gagliardi 860eb0fae3 And implemented uiWindow stuff on Haiku. 2020-05-31 19:00:03 -04:00
Pietro Gagliardi 218439c215 Did all the uiWindow stuff on GTK+. 2020-05-31 13:37:56 -04:00
Pietro Gagliardi eb1862afe1 Deduplicated noinitwrongthread.c and its OS-specific files. 2020-05-30 21:24:58 -04:00
Pietro Gagliardi 80ddb4df8f Rounded out uiWindow and uiDarwinControl error conditions and error condition documentation. 2020-05-30 21:17:17 -04:00
Pietro Gagliardi d423688def And added (in a somewhat dirty for now way) the noinitwrongthread.c case for uiDarwinControlHandle(). 2020-05-25 22:42:44 -04:00
Pietro Gagliardi de3598df80 Added tests for the uiWindow methods being called after init/on the right thread. To actually implement those, wrote OS-independent wrappers for the uiWindow functions. This will likely become a pattern throughout libui... 2020-05-25 22:35:25 -04:00
Pietro Gagliardi 0dd4bec2af Added uiDarwinControlHandle(), and added it to uiWindow, and added uiWindow handle tests (including underlying string tests) to the test suite. Oof. Also I just remembered we're missing tests of these new functions in noinitwrongthread.c. 2020-05-25 22:13:48 -04:00
Pietro Gagliardi 16d2380adc Added the UTF-8 tests to the uiWindow test. 2020-05-25 19:48:59 -04:00
Pietro Gagliardi 9bc2cccb2d More work on test/utf8.c. Now to integrate this into test/window.c. 2020-05-25 15:43:28 -04:00
Pietro Gagliardi aa954e952a Unicode language lawyering. 2020-05-25 14:19:37 -04:00
Pietro Gagliardi 4c5434d76e More TODOs. 2020-05-25 02:00:16 -04:00
Pietro Gagliardi c71fbe29c2 Wrote some code for helping with UTF-8 test cases. 2020-05-25 01:47:19 -04:00
Pietro Gagliardi 2f9aaeeb62 Started writing tests for uiWindow. 2020-05-24 18:08:48 -04:00
Pietro Gagliardi 0be1273dab Finished our initial implementation of uiWindow on macOS to our previous spec. Now to write tests. 2020-05-24 17:23:52 -04:00
Pietro Gagliardi ba6f2865df Started reintegrating uiWindow on macOS. I didn't think the implData variable naming through... Also made uiWindow a mere typedef to uiControl; I'll need to copy the handle logic from Windows to make this robust, or something to that effect... 2020-05-22 22:43:33 -04:00
Pietro Gagliardi a65bbfa057 Started re-adding uiWindow; this just lays down the bare minimum API we're re-adding. 2020-05-22 18:46:48 -04:00
Pietro Gagliardi 8db1d5474b Included testhooks.h from uipriv.h and test.h, to make things easier to write (we won't have to keep remembering to include it in every file) and to clean up (ditto, but for removal). 2020-05-22 16:32:04 -04:00
Pietro Gagliardi 6fe7c1ef66 Added a UTF-8 sanitization routine. This will be needed for titles on uiWindow. Reintroduced our copy of the utf library. This should really be part of utf; I'll have to decide whether to test this or not. Made that a TODO for now. I'll be testing bad UTF-8 inputs on each function that takes UTF-8 strings anyway, just to be safe. Now we can finally add uiWindow! 2020-05-17 22:18:47 -04:00
Pietro Gagliardi 7aeb937b93 Added methods to uiControl to catch and handle parent changing from the perspective of the individual controls. This will be used to implement the transition from not being backed by an OS handle to being backed by one and back on Windows, and should be all we need to start re-adding controls after uiWindow. 2020-05-13 12:43:40 -04:00
Pietro Gagliardi e01c732760 More documentation notes. 2020-05-12 17:00:40 -04:00
Pietro Gagliardi 150fa7eaa6 Renamed testparent to testrunner. (Whatever this became wasn't going to be testparent anyway; that was me thinking ahead to XCTest for macOS testing, which we're almost to the point of...) 2020-05-12 02:05:30 -04:00
Pietro Gagliardi de35499976 And removed zNEW_test now that everything's integrated back in. 2020-05-12 01:47:14 -04:00
Pietro Gagliardi 1cf87d5c5e And Haiku. 2020-05-12 01:46:36 -04:00
Pietro Gagliardi de5205d594 And with Unix. 2020-05-11 22:37:17 -04:00
Pietro Gagliardi e73c3363ec Reintegrated the Windows control tests. We may be close to getting rid of zNEW_test! 2020-05-11 22:30:30 -04:00
Pietro Gagliardi 22bcf088e9 And reintegrated the macOS tests. Now for the other platforms. 2020-05-11 22:19:07 -04:00
Pietro Gagliardi 76eef19a2b Clarified something in test/errors.c. 2020-05-11 21:03:31 -04:00
Pietro Gagliardi 19a776e446 And integrated the cycle-checking code. Woo it's got tests! Now we need to worry about the OS-side test code, assuming I don't have any more pesky TODOs to fix immediately... 2020-05-11 21:02:04 -04:00
Pietro Gagliardi b1d733e9a2 Reworked programmer error tests to limit what they catch. The cycle checking tests now correctly crash and burn because the programmer errors they throw are elsewhere. Now to actually hook up the cycle checking! Also added some notes to test/errors.c to explain what we're doing and also fixed the build. 2020-05-11 20:55:34 -04:00
Pietro Gagliardi fdf9a1245d Wrote the code to check for parent cycles. I'm not going to actually hook it up yet; I want to resolve the one TODO about limiting the scope of the checkProgrammerError() calls first. 2020-05-11 19:48:02 -04:00
Pietro Gagliardi 1cf4a3df21 Filled in the missing uiControl tests and demoted the non-uiControl requirements to TODOs as I'm not sure how sensible those requirements are. I'll probably bring them back shortly =P Now to implement cycle checking. 2020-05-11 10:32:17 -04:00
Pietro Gagliardi dfd045b3cc Resolved all the TODOs in test/controls.c I care to right now. I still need to write tests for the other failure conditions that aren't tested yet. 2020-05-11 00:52:22 -04:00
Pietro Gagliardi 9b1be0c77a More TODO resolution. 2020-05-10 23:59:09 -04:00
Pietro Gagliardi e71c717b31 Made the test for an invalid control type slightly more robust by not using either 0 or 5 for the fake object (but rather some other value that isn't the constant and that's also conveniently ASCII) and deduplicating the redundant tests. 2020-05-10 22:00:49 -04:00
Pietro Gagliardi c52656f2c2 Explicitly specified 0 as the invalid control type and stopped using 5 as an invalid control type. 2020-05-10 21:54:37 -04:00
Pietro Gagliardi 3689d3c96a More TODOs. 2020-05-10 21:45:33 -04:00
Pietro Gagliardi b710ef6228 Fixed building these tests. 2020-05-10 21:39:44 -04:00
Pietro Gagliardi 436cb567dc Integrated the contents of the old controls_errors.cpp directly into controls.c. We need to rework some test control stuff to get this to compile, and we'll also need to add more test cases for things in the docs that are not in this file. 2020-05-10 21:29:03 -04:00
Pietro Gagliardi d584842628 Started reintegrating control tests. 2020-05-10 17:58:48 -04:00
Pietro Gagliardi e7917500c1 Clarified doc/controls.md a bit, and introduced more requirements. Now to re-introduce its tests. 2020-05-10 16:24:43 -04:00
Pietro Gagliardi 5d17ee6e70 Finished reintegrating test/noinitwrongthread.c. That was easy! 2020-05-10 14:57:03 -04:00
Pietro Gagliardi 21a0550076 Reintegrated test/noinitwrongthread.c into the fold. Now to test. 2020-05-10 14:49:11 -04:00
Pietro Gagliardi f79a0892f2 More TODOs. 2020-05-10 13:09:00 -04:00
Pietro Gagliardi 1daac120af Switched haiku status_t printing to use a constant; also gave the constant a self-descriptive name for readability. 2020-05-10 12:55:47 -04:00
Pietro Gagliardi ecb5558d3d And changed the other OSs to define uiprivSysMain/Quit(). 2020-05-10 12:29:58 -04:00
Pietro Gagliardi 32512c7a5e More TODOs in re previous commit. Okay let's keep going. 2020-05-10 12:15:21 -04:00
Pietro Gagliardi dca16e82b0 Kicking this can down the road because I need to move on (though I may add more TODOs about it) 2020-05-10 11:58:17 -04:00
Pietro Gagliardi 3a31401190 Okay, started writing the interleaving tests. I'm starting to feel like this thread-based model of testing isn't the right approach... 2020-05-10 11:50:05 -04:00
Pietro Gagliardi 4163facc11 Settled most of the remaining non-timer TODOs in test/initmain.c. Just one more (duplicated on two different tests) to go! 2020-05-09 23:10:58 -04:00
Pietro Gagliardi 87e042a309 Wrote in the remaining programmer error tests in test/initmain.c. 2020-05-09 22:34:42 -04:00
Pietro Gagliardi c9339e568c Finished converting the rest of the existing tests. Now we just need to flesh out the remaining tests for this file, as well as adding the error tests for uiMain() and uiQuit(). 2020-05-09 22:14:31 -04:00
Pietro Gagliardi 7a83baefe8 Started reworking all the uiQueueMain() tests to be more robust. 2020-05-09 21:54:26 -04:00
Pietro Gagliardi bbae57ea29 Refined the init tests slightly, started a new model for the QueueMain tests, and allowed tests to run for now. 2020-05-09 20:04:14 -04:00
Pietro Gagliardi 73afdaaeb2 Improved the main QueueMain test. 2020-05-09 16:54:18 -04:00
Pietro Gagliardi c39bd66d38 Cleaned up the init tests. 2020-05-09 16:35:01 -04:00
Pietro Gagliardi a2f0d5c2cd Implemented the rest of the uiInit() tests. A bit repetitive, but it works. 2020-05-09 02:38:07 -04:00
Pietro Gagliardi 9fcc4d500c Adapted test/errors.c to test for the lack of a programmer error. 2020-05-09 02:33:18 -04:00
Pietro Gagliardi 41e62cdce1 Added basic testing for init failing. Now to add support for testing for the lack of a programmer error. 2020-05-09 02:04:51 -04:00
Pietro Gagliardi 761d3a434a Started to fix init/main tests. 2020-05-09 01:45:09 -04:00
Pietro Gagliardi 514037ba6c Modified common and the macOS port to exhibit the new init/main behavior. Now to adjust the tests. 2020-05-09 01:24:40 -04:00
Pietro Gagliardi faaccd0413 Made uiInit() programmer errors actual programmer errors and not normal error returns. This makes the error handling, the documentation, and even the tests much easier, and also makes programmer error handling more consistent. Also defined programmer error cases for uiMain() and uiQuit(). We'll implement all of these next. 2020-05-09 01:01:40 -04:00
Pietro Gagliardi c0e73c67f4 Okay, I won't even bother with the overview until I figure out how events will work in the final API, because I legitimately cannot decide how to write this. 2020-05-09 00:35:38 -04:00
Pietro Gagliardi dea7b52605 Revert "Made thread names consistent."
I don't know if I want to do this yet.

This reverts commit 456fb594c0.
2020-05-09 00:34:51 -04:00
Pietro Gagliardi 456fb594c0 Made thread names consistent. 2020-05-09 00:18:06 -04:00
Pietro Gagliardi 7c4d1ae478 More TODOs in doc/init-main.md. I'll decide these later. I want to rewrite this to not use the term 'event' to avoid confusion with libui's own event system, since these are talking about all the events that can happen, not just the ones libui allows you to handle... 2020-05-08 00:39:37 -04:00
Pietro Gagliardi 69bf71aaa1 Decided to hold off on events for now. I'm still not sure about this design and it's holding me back. 2020-05-08 00:16:14 -04:00
Pietro Gagliardi 74651b347c More notes. 2020-05-05 10:46:56 -04:00
Pietro Gagliardi 63bda503dc More notes. 2020-04-09 20:39:18 -04:00
Pietro Gagliardi e0828a51f1 More notes. 2020-04-07 22:33:41 -04:00
Pietro Gagliardi 3c93d8c9e7 Offloaded some browser tabs to TODOs. 2020-03-22 00:59:12 -04:00
Pietro Gagliardi 0d9f58b5e1 Rewrote test/errors.c as previously planned. checkProgrammerErrors() is no longer self-contained, but rather a header and a footer function. We'll need to change the handling of test/noinitwrongthread.c in the Python appropriately, but this'll be fine. (Plus, we can localize the inThread logic into that file too!) 2020-02-23 18:04:23 -05:00
Pietro Gagliardi 5a7777ffaa Temporarily turning off the files with checkProgrammerError(). Going to rewrite that to be more amenable to our testing system and not need C++ closures. 2020-02-23 17:36:10 -05:00
Pietro Gagliardi 48fd34781e Re-added test/events_errors.cpp. Yay C++ 2020-02-23 15:20:12 -05:00
Pietro Gagliardi e9be6b9502 Finished migrating test/events.c. 2020-02-19 22:44:48 -05:00
Pietro Gagliardi a23bd0f786 Fixed the build. It works so far! 2020-02-18 01:08:38 -05:00
Pietro Gagliardi c269d1f6bc Started re-integrating test/events.c. It doesn't quite work yet, but it appears I need to reboot AGAIN because now both acme and pbcopy/pbpaste are being dumb with the clipboard... 2020-02-17 15:01:20 -05:00
Pietro Gagliardi cdc570082c Added TestDefer(), which we need to bring back the events tests. 2020-02-16 21:54:17 -05:00
Pietro Gagliardi 3f3de363b5 And integrated the new test stuff. All right! 2020-02-16 18:42:02 -05:00
Pietro Gagliardi 18c7eae71e Integrated the new testlist.py into the build. 2020-02-16 18:31:53 -05:00
Pietro Gagliardi f95c87dc96 Wrote in the missing functionality to the new testlist.py. 2020-02-16 13:48:20 -05:00
Pietro Gagliardi cfc4b60ea1 Oops, it was working the whole time; I was being dumb and feeding it the wrong input when testing. 2020-02-16 13:22:45 -05:00
Pietro Gagliardi 27a701c371 Rewrote testlist.py to allow producing an auxiliary file (or auxiliary files) to allow not having to compute the test lookup table in testparent (though we're not doing that yet). It mostly works. 2020-02-16 12:56:55 -05:00
Pietro Gagliardi 4fae662575 Revert "Started restructuring common/errors.c to only produce a single error string (so we can pass it to Haiku's debugger() function without further allocations). In this case, the buffer size is 256 + the size of the prefix + the size of the suffix now."
Okay, this is actually going to be more complicated than I at first thought...

This reverts commit 44e13e53bf.
2020-02-09 20:49:47 -05:00
Pietro Gagliardi 44e13e53bf Started restructuring common/errors.c to only produce a single error string (so we can pass it to Haiku's debugger() function without further allocations). In this case, the buffer size is 256 + the size of the prefix + the size of the suffix now. 2020-02-09 18:27:19 -05:00
Pietro Gagliardi 83129eeef5 Normalized uiprivInternalError() calls to always try to fail gracefully, and make sure errors don't call abort() so we can resume when debugging (and because this isn't really our decision to make...). (The debugger breaks will be removed from release builds when we get to that point.) Finally, refined some TODOs and removed some stale ones (in this case, that were related to the first few things). 2020-02-09 13:37:45 -05:00
Pietro Gagliardi 5952ad368d Fixed Haiku runtime errors. 2020-01-28 21:36:31 -05:00
Pietro Gagliardi cce4419b9a Got Windows working again. 2020-01-28 21:29:45 -05:00
Pietro Gagliardi 9ceed7c62d Fixed errors.c. 2020-01-26 20:06:12 -05:00
Pietro Gagliardi 001e439a9b Started properly reintegrating the core of noinitwrongthread.c (that is, errors.c). 2020-01-26 20:02:03 -05:00
Pietro Gagliardi 8b400f33f7 More initmain.c integration. I think we're now complete to what it used to be for that one file. Now I have to figure out how to readd subtests... 2020-01-23 21:44:45 -05:00
Pietro Gagliardi 7bb67c3e46 More adaptations. 2020-01-23 21:36:50 -05:00
Pietro Gagliardi 042da74cc9 Started restructuring things slightly so as to allow the new test functions to be safely called from subroutines. 2020-01-23 10:52:41 -05:00
Pietro Gagliardi cd2d64c228 Started reintegrating the noinitwrrongthread.cpp cases. Also made it C instead of C++ because maybe Plan 9 one day??? Either way, not ready to test yet. 2020-01-21 15:31:41 -05:00
Pietro Gagliardi b1b60f6077 Reintegrated timerSleep() (now threadSleep() because I'm not bloating things further). initmain.c fully restored! 2020-01-21 15:13:24 -05:00
Pietro Gagliardi b722922428 Reintegrated the test's thread helper functions and the thread test functions in initmain.c. Phew. 2020-01-21 14:19:52 -05:00
Pietro Gagliardi d51a885431 Reintroduced the test type without uiInit() and added uiInit() to most normal tests. Now to start (slowly) reactivating tests. 2020-01-21 01:16:47 -05:00
Pietro Gagliardi da1f9b4875 And fixed compiler errors. It works! 2020-01-20 20:58:19 -05:00
Pietro Gagliardi c014fb5989 Fixed the meson configuration. 2020-01-20 20:55:37 -05:00
Pietro Gagliardi f75875e51b And finished reintegrating enough for one test case. 2020-01-20 20:52:46 -05:00
Pietro Gagliardi 4f2db68520 Reintegrated what I needed in test.h. So much for "overengineered". 2020-01-20 20:49:33 -05:00
Pietro Gagliardi 2ccabe69f2 Started reintegrating initmain.c, sort of. I screwed up and need to redo it. 2020-01-20 20:17:44 -05:00
Pietro Gagliardi 0c66fc3995 Wrote the python script to generate the list of test cases and integrated it into meson.build. This should be fun. 2020-01-20 18:18:58 -05:00
Pietro Gagliardi f4218af3f5 Filled in the rest of test(ing).h and main.c for the new test system. 2020-01-20 17:22:37 -05:00
Pietro Gagliardi c95cefaa1c Started writing the new test parent, which will run tests that do not need to spawn a child process to do UI tests with XCTest. Wow I overengineered testingpriv... Also now that I have Solaris Studio I might as well add __SUNPRO_C support. 2020-01-19 22:42:59 -05:00
Pietro Gagliardi 75db846736 Looks like we're going to need to turn the test suite into a subprocess-based suite now rather than later, because of Haiku not allowing BApplication::Run() to be called more than once. 2020-01-19 18:59:43 -05:00
Pietro Gagliardi 6bc5db4d79 Oops I forgot the soversion. Now to figure out why it crashes. 2020-01-18 21:08:24 -05:00
Pietro Gagliardi de76afd43a And filled in haiku tests. It builds, but doesn't run. 2020-01-18 20:25:49 -05:00
Pietro Gagliardi 4c33e0fb83 Filled up more of the Haiku port, enough to build the library itself. 2020-01-18 20:19:38 -05:00
Pietro Gagliardi 0748d05a19 More notes. 2020-01-13 10:46:45 -05:00
Pietro Gagliardi d0edd9e738 Started a new Haiku port. 2020-01-12 22:48:28 -05:00
Pietro Gagliardi a8d082fa79 Merge branch 'master' into remodel 2020-01-02 22:31:13 -05:00
Pietro Gagliardi 9d4253ad6d Fixed build on FreeBSD. 2020-01-02 22:30:31 -05:00
Pietro Gagliardi 5d072aad58 Merge branch 'master' into remodel 2019-12-31 23:25:23 -05:00
Pietro Gagliardi 01c1691161 More TODOs. 2019-11-08 22:22:03 -05:00
Pietro Gagliardi 736af09557 More notes. 2019-10-25 20:03:36 -04:00
Pietro Gagliardi 21b887a14a More notes. 2019-10-21 22:14:33 -04:00
Pietro Gagliardi ad1644f9a8 More notes. 2019-10-19 22:56:25 -04:00
Pietro Gagliardi c90ea52922 Merge branch 'master' into remodel 2019-09-15 22:54:30 -04:00
Pietro Gagliardi c0c8207a6c misctests cleanup (of files not being committed to git). 2019-09-15 10:52:15 -04:00
Pietro Gagliardi a410f9d5e5 Ooh we mostly figured it out! 2019-09-02 21:06:07 -04:00
Pietro Gagliardi 6801cf5282 More progress with the DWM blur-behind stuff. This is increasingly complicated; I can get rid of the alpha-blending OR keep the Aero animations... 2019-09-01 18:59:19 -04:00
Pietro Gagliardi 5c65e6d7fa More notes. 2019-08-31 19:33:33 -04:00
Pietro Gagliardi 17f9a5eff5 Started writing a test program for DwmEnableBlurBehind() for sidebars. This is gonna be a LOT of work... 2019-08-31 19:22:02 -04:00
Pietro Gagliardi 0264ea9905 BIG OOPS 2019-06-18 20:36:17 -04:00
Pietro Gagliardi c17f854d04 And added uiControlOSVtable on Windows. We're now back to building everywhere! :D 2019-06-18 20:31:28 -04:00
Pietro Gagliardi 80ada0a06b And implemented the bare bones of uiControlOSVtable on GTK+. 2019-06-18 20:19:32 -04:00
Pietro Gagliardi 4649e7d632 Oh wait, we leave that to the individual containers. Never mind! Let's write this on other platforms!! 2019-06-18 11:02:39 -04:00
Pietro Gagliardi 644afdedaf Added uiControlOnFree() checking to test/control.c. Now we can add the tests for uiControlFree() calling Free on children, and then we can finally start implementing these on the other platforms :D 2019-06-18 10:59:56 -04:00
Pietro Gagliardi a1b0979506 Rewrote test/controls.c to be about counting calls. 2019-06-18 10:48:10 -04:00
Pietro Gagliardi dcae8888d2 More controls test work. 2019-06-17 20:03:57 -04:00
Pietro Gagliardi a9145c2f35 Started writing the uiControl functionality tests. Also more TODOs. Let's fix build errors next. 2019-06-16 21:44:49 -04:00
Pietro Gagliardi 6b161efed7 Added uiControlSetParent() to allcalls.h. 2019-06-16 21:28:16 -04:00
Pietro Gagliardi a907353c9c Workaround for now. I should have specified in the bug that I overlooked it because my version of clang seems to use C99 for Objective-C by default, but not for Objective-C++. I'll do that when I'm next online. Anyway, now to write uiControl functionality tests. 2019-06-16 21:26:05 -04:00
Pietro Gagliardi 517791f04e Started writing the macOS control errors file. We have a meson problem now... it's not respecting cpp_std for Objective-C++. 2019-06-16 20:35:36 -04:00
Pietro Gagliardi aa7da7b647 And added those control errors. Back to passing, and more importantly, back to actually writing tests! (Once we get to the uiControlOSVtable tests, that is...) Also more TODOs. 2019-06-16 20:03:37 -04:00
Pietro Gagliardi 3f744de64e Started cleaning things up from zOLD as well. 2019-06-16 19:55:51 -04:00
Pietro Gagliardi edee2b930c Re-added uiControlSetParent() to allow finishing testing uiControlFree() errors. That will come next, alongside its own errors. 2019-06-16 19:52:09 -04:00
Pietro Gagliardi fda6c4a743 And strengthened the uiControlType() test a bit. 2019-06-16 17:32:47 -04:00
Pietro Gagliardi c28adcbd01 Added test hook for checking a malformed uiControl whose type field is invalid. 2019-06-16 17:26:03 -04:00
Pietro Gagliardi f90150a579 More filling in of the uiControl errors tests. 2019-06-16 13:06:28 -04:00
Pietro Gagliardi 0d61f674c5 Filled in some error messages in the previous commit. More TODOs. 2019-06-16 10:28:27 -04:00
Pietro Gagliardi 0bc347dd62 Stubbed out most of the other uiControl errors to test. 2019-06-16 10:20:21 -04:00
Pietro Gagliardi 3e96dafe44 Created the test uiControl type. Now we can start writing the rest of the uiControl tests. 2019-06-16 05:45:16 -04:00
Pietro Gagliardi ed5bbc4dbd And added uiControlVtable NULL method tests. Woo! 2019-06-15 23:38:17 -04:00
Pietro Gagliardi 29309ab040 And also FINALLY added the internal uiEvents to the uiEventFree() test. 2019-06-15 21:45:35 -04:00
Pietro Gagliardi 29ce809772 And FINALLY fixed the NULL name error in uiRegisterControlType(). 2019-06-15 21:38:21 -04:00
Pietro Gagliardi 0550e4bc00 Turned testVtable into a function that returned the pointer to use, made the OSVtable equivalent likewise, and renamed darwin/control.m to darwin/controls.m. 2019-06-15 21:33:53 -04:00
Pietro Gagliardi c1d8c0f5ec Oops, forgot to add the consts to the documentation. 2019-06-15 20:48:55 -04:00
Pietro Gagliardi b6a8d24c3e And fixed const issues and other build errors. 2019-06-15 20:48:20 -04:00
Pietro Gagliardi ad209175c1 Migrated controls_errors.h. Now I need to add consts to uiRegisterControlType() and friends. 2019-06-15 20:38:18 -04:00
Pietro Gagliardi 4d78b5a3ef Migrated noinitwrongthread.c. 2019-06-15 20:17:45 -04:00
Pietro Gagliardi 8c7f1987da And fixed build errors. It works! Now to translate everything else back. 2019-06-15 20:06:12 -04:00
Pietro Gagliardi 83dc1d6065 And removed errors.h. 2019-06-15 19:59:17 -04:00
Pietro Gagliardi c2bace108d And finished rewriting events_errors.h. 2019-06-15 19:58:46 -04:00
Pietro Gagliardi f966b6ee16 Adapted everything to life with C++ and to the new error checking model. 2019-06-15 19:53:50 -04:00
Pietro Gagliardi c74fc09261 Started rewriting the error checks in C++, because ugh. 2019-06-15 19:28:47 -04:00
Pietro Gagliardi 5d63b34904 I should probably just switch all this to C++ at this rate. 2019-06-14 20:15:39 -04:00
Pietro Gagliardi f517742a3c Started rewriting events_errors.c to use a new errors.h that will do all the hard work. We'll need to do slightly more work for this particular case because of the firing stuff, though. 2019-06-13 21:20:39 -04:00
Pietro Gagliardi ae4bfea10b Started rewriting events_errors.c to mention events less; this will turn into an events.h that will do all the heavy lifting. 2019-06-13 20:57:15 -04:00
Pietro Gagliardi ab82278a30 And got things building. Now to clean up the error macro part. 2019-06-13 10:43:44 -04:00
Pietro Gagliardi d54f23c0cc Deduplicated the common defer functions. 2019-06-13 10:39:40 -04:00
Pietro Gagliardi f3b0dc16ab Slight refactoring of the controls test. One more thing to do for now. 2019-06-13 10:31:17 -04:00
Pietro Gagliardi c1ca78c46e Split the events.c errors test into its own file. 2019-06-12 11:12:08 -04:00
Pietro Gagliardi 47a8ccaf98 And filled in the error messages in controls_errors.h. 2019-06-12 11:03:21 -04:00
Pietro Gagliardi 63773f703c Fixed build errors. The test itself will fail at present, but we have some cleanups to do still... 2019-06-11 21:29:25 -04:00
Pietro Gagliardi e30ed33046 Finished rewriting test/controls.c for the initial run. Now to fix errors. 2019-06-11 21:23:56 -04:00
Pietro Gagliardi 28a5cd543a Started writing the uiControl error tests. 2019-06-11 12:01:35 -04:00
Pietro Gagliardi 6903bebe7d Started writing the controls Darwin-specific code and all-platforms tests for real. 2019-06-10 23:09:48 -04:00
Pietro Gagliardi 89f64eb067 Added the documentation for macOS uiControl, or what little of it currently exists. 2019-06-10 20:11:10 -04:00
Pietro Gagliardi 7e3d6d2b1c And fixed build and runtime errors. This works! Woo! We can now move on to uiControl for real this time! (Will resolve the TODOs later.) 2019-06-10 11:36:44 -04:00
Pietro Gagliardi a0fc3187e2 And rewrote events.c to use the new error checking framework. Now to fix build and possibly runtime errors. 2019-06-10 11:24:33 -04:00
Pietro Gagliardi 7b10fa729b More TODOs. 2019-06-10 10:42:11 -04:00
Pietro Gagliardi 70ff47cb06 Wrote a new file to store the uiEvent error test cases. 2019-06-10 10:41:33 -04:00
Pietro Gagliardi afbf5f2d59 And actually did split the new error testing code into its own file. Next is changing both events.c and controls.c to use it (and yes, that means we can finally start continuing work on controls.c). 2019-06-09 22:45:44 -04:00
Pietro Gagliardi b34a427077 Further refactored noinitwrongthread.c; at this point, the actual check runner can be separated into its own file and used elsewhere. 2019-06-09 22:33:45 -04:00
Pietro Gagliardi 05b1e26ee2 Removed some leftover code. 2019-06-09 21:18:31 -04:00
Pietro Gagliardi 97e37e5831 Started defining uiControlOSVtable on macOS. I'll need to write the documentation alongside this. 2019-06-09 21:16:13 -04:00
Pietro Gagliardi 5f454a6114 Some more refactoring in noinitwrongthread.c. The different components are almost isolated! 2019-06-09 21:00:33 -04:00
Pietro Gagliardi ee9df0491f Simplified noinitwrongthread.c further. This will form the basis of a simplified general event handler. 2019-06-09 19:00:01 -04:00
Pietro Gagliardi ef05d40fad Fixed test execution. 2019-06-09 18:19:43 -04:00
Pietro Gagliardi a0fd823328 Just restored TestEventErrors as is for now. There's definitely room for cleanup here. 2019-06-09 18:08:50 -04:00
Pietro Gagliardi 94d638e601 And finished rewriting test/noinitwrongthread.c. 2019-06-09 16:14:18 -04:00
Pietro Gagliardi 91c1e8e517 And rewrote TestFunctionsFailBeforeInit. This should work nicely. 2019-06-09 15:29:03 -04:00
Pietro Gagliardi db3de44432 Collapsed the strsafe*_impl files. strdup_impl is still a mess, but to fix that we'll need to both clean up the static nonsense and other stuff in testingpriv.c. 2019-06-09 14:06:16 -04:00
Pietro Gagliardi 5f011acc0c Somewhat hackily worked around strdup wonk. 2019-06-09 14:03:27 -04:00
Pietro Gagliardi 24320ce784 Disabled the event error tests and started rewriting the no uiInit()/wrong thread checks to use the new programmer error test hooks. I'll also start taking care of the sharedbits cruft now by just providing Strdup and FreeStrdup as utility functions, because bleh. 2019-06-09 13:57:02 -04:00
Pietro Gagliardi 65226ac416 Refactored the programmer error test hook to not needlessly pass in the prefix, suffix, or internal flag, and to allow passing in arbitrary data so we can get rid of the global state in the tests. This will make the tests a LOT easier. 2019-06-09 13:49:53 -04:00
Pietro Gagliardi 7486ff6b25 Changed the programmer error tests to require a strict match. Now I want to restructure them to both be reusable and to not require global state. 2019-06-09 13:44:46 -04:00
Pietro Gagliardi a2b9145931 Did the same rewording to most of the other programmer errors. Only the first two are left, because I can't think of a good rewording... 2019-06-09 13:39:14 -04:00
Pietro Gagliardi 87a2195027 Renamed the uiControl programmer errors to more clearly indicate they are uiControl programmer errors, and reworded their messages to put the function name first. 2019-06-09 13:32:31 -04:00
Pietro Gagliardi 0f41bb2188 Made all programmer error functions take the function name as an argument. 2019-06-09 13:18:18 -04:00
Pietro Gagliardi 9b11ec4a6f Changed the IntID error into a uiEvent-specific one. 2019-06-09 10:30:47 -04:00
Pietro Gagliardi 14c80eefb8 Started organizing common/programmererrors.h. 2019-06-09 10:27:28 -04:00
Pietro Gagliardi e808d85e01 Added function names to the wrong struct size error. 2019-06-09 08:50:12 -04:00
Pietro Gagliardi 9e05efa091 Oh right forgot to clean up error stuff from zOLD. 2019-06-09 08:44:30 -04:00
Pietro Gagliardi e3a4d34196 Started cleaning up zOLD/control stuff now that we are reimplmenting it all. 2019-06-09 08:39:51 -04:00
Pietro Gagliardi c45ab57bce Started writing control tests. This is going to be fun... 2019-06-08 21:38:32 -04:00
Pietro Gagliardi 822572d395 Stubbed out the OS-specific files on macOS so I can actually start writing the uiControl tests. 2019-06-08 19:45:09 -04:00
Pietro Gagliardi 379fc230e5 More compile fixes. sharedbits is starting to get really messy. 2019-06-08 18:39:59 -04:00
Pietro Gagliardi 22cc5ad834 More error fixing. 2019-06-08 18:22:16 -04:00
Pietro Gagliardi 19fc6f674f Oops, forgot to add controls.c to the build. 2019-06-08 18:09:37 -04:00
Pietro Gagliardi 9f776c140d Fixed compile errors. 2019-06-08 18:09:12 -04:00
Pietro Gagliardi 7aa1b0870d And finished filling in controls.c. This is gonna be fun to debug... 2019-06-08 15:31:40 -04:00
Pietro Gagliardi 1189877ca5 Started writing the uiControl implementation. 2019-06-08 13:00:30 -04:00
Pietro Gagliardi e2842bae35 Oops, forgot to put Size in uiControlVtable. Let's go! 2019-06-08 09:58:25 -04:00
Pietro Gagliardi 6f4c65defc Finished the uiEventInvalidateSender() tests. 2019-06-07 21:05:45 -04:00
Pietro Gagliardi 5260d5c90f More reminders for when I write the rest of the uiEventInvalidateSender() tests later. 2019-06-07 11:01:17 -04:00
Pietro Gagliardi 6cf92b2058 Wrote and started testing uiEventInvalidateSender(). The rest of the tests come next. 2019-06-07 10:58:39 -04:00
Pietro Gagliardi 65c88fd1c4 And documented all the other functions we need to start writing common/control.c. 2019-06-06 22:51:37 -04:00
Pietro Gagliardi 582a781171 More uiControl work. 2019-06-05 23:20:53 -04:00
Pietro Gagliardi 4ff61c81b0 More uiControl documentation writing. This new system might work nicely the way I'm designing it... 2019-06-04 20:33:29 -04:00
Pietro Gagliardi 26e492539d Renamed uiFreeEvent() to uiEventFree() for consistency with uiControlFree(). 2019-06-02 22:04:25 -04:00
Pietro Gagliardi 57dc482f2a Oops 2019-06-02 20:50:55 -04:00
Pietro Gagliardi b8f8e1c4f7 Started writing out uiControl's documentation. This new design is going to be rather interesting. 2019-06-02 20:47:27 -04:00
Pietro Gagliardi c6294f163f Okay, added uiFreeEvent() error tests. Now back to uiControl. 2019-06-02 19:11:22 -04:00
Pietro Gagliardi d040bca07e Started layout out the uiControl stuff. I forgot to write tests for uiFreeEvent() errors :| 2019-06-02 18:49:16 -04:00
Pietro Gagliardi 12c01d0d42 Ensured calls to bsearch() have a length of at least 1; UndefinedBehaviorSanitizer will warn otherwise. Also included qsort(), to be safe. 2019-06-02 14:26:25 -04:00
Pietro Gagliardi 6d6cd66046 Freed defers after they run. Apparently this is sufficient to satisfy AddressSanitizer, which... how? I'll take it, but still, there's definitely more unfreed allocations than this... 2019-06-02 14:06:16 -04:00
Pietro Gagliardi ddaa10b7e1 Wrote defers into test/events.c. Woo! Now we need to free the defers when we're done, and I have an idea... 2019-06-02 12:48:06 -04:00
Pietro Gagliardi 9f2796ebac Added uiEventFree(). Now to add it to the tests and see what AddressSanitizer says afterward. 2019-06-02 10:06:50 -04:00
Pietro Gagliardi ece22bdddc Split programmer errors into their own header file for readability and turned the names from string constants into full-fledged uiprivProgrammerError() calls to increase safety. 2019-06-02 08:35:40 -04:00
Pietro Gagliardi 623575f25a Changed uiprivProgrammerError() to use format strings directly, so errors can be more easily caught. We'll make things even more robust when I change the programmer error macros to be functions. 2019-06-02 02:23:12 -04:00
Pietro Gagliardi 671c2031bf TODO in allcalls.h now resolved. 2019-06-02 02:07:33 -04:00
Pietro Gagliardi b5e8b76066 Fixed one mistake. 2019-06-02 02:04:20 -04:00
Pietro Gagliardi 3f4765d116 Renamed diffx() to diff(). 2019-06-02 01:59:08 -04:00
Pietro Gagliardi 0c673acf70 Got rid of all the diff() macros; the replacement, currently called diffx(), just produces the requisite format string. Next up is renaming diffx() to diff() and making sure things work. 2019-06-02 01:51:40 -04:00
Pietro Gagliardi 5d1e6a0cf2 Began handling all the other cases. diff() and diff_2str() go away next. 2019-06-02 01:36:53 -04:00
Pietro Gagliardi 1cf545d369 Started mapping filenames and line numbers back to their appropriate places in the tester. 2019-06-02 01:13:56 -04:00
Pietro Gagliardi 4ae6ab2727 Changed the testing library to allow passing different filename and line numbers through to the various logging functions; cleaned up the macro hell that was by passing the action function directly to the final logging functions in testing.c. Next commit will actually implement this. 2019-06-01 22:03:13 -04:00
Pietro Gagliardi 8980a8663b Cleaned up a TODO. 2019-06-01 20:46:41 -04:00
Pietro Gagliardi bd2877154f Oh, it turns out there IS a warning for shutting up -pedantic. Woo! 2019-06-01 20:26:43 -04:00
Pietro Gagliardi 6ffe0a4c99 Added functionality to mark printf() functions for compilers with warning checks, and marked such. Sadly it appears ms_printf is ignored with -pedantic, so we'll need to stop using I32... 2019-06-01 20:20:11 -04:00
Pietro Gagliardi ef3352c033 Removed the soft librt dependency for now, since we don't appear to need it, at least for Ubuntu 14.4. If that changes (for instance, if it's required by some non-Linux system or some other distro), we can always add it back. 2019-06-01 12:22:35 -04:00
Pietro Gagliardi 9ad77ac57e Rewrote a TODO. Didn't solve it yet, because I'm genuinely indecisive about this one. 2019-06-01 12:17:08 -04:00
Pietro Gagliardi 8531710f03 More TODOs. 2019-06-01 12:11:16 -04:00
Pietro Gagliardi efe118a81d Resolved warning suppression issue in timerDurationString(). It turns out BOTH this AND the solution were both in timer.c, and I somehow did not notice :V 2019-06-01 12:06:09 -04:00
Pietro Gagliardi a8ad49fead Made the names of format string arguments across the board consistent. Only the "public" functions in the testing library are kept expanded for documentation purposes. 2019-06-01 11:40:16 -04:00
Pietro Gagliardi 76fabb37cf Renamed common/init.c to common/main.c. 2019-06-01 11:24:34 -04:00
Pietro Gagliardi dea7436468 Changed LONGTERMs in windows/ to TODOs. If it's not worth doing soon, it should go in another list of things to do. 2019-06-01 10:33:03 -04:00
Pietro Gagliardi f2ce4c8f56 Split the shared and static resources on the Windows tester, for the same reason. 2019-06-01 10:29:48 -04:00
Pietro Gagliardi a8cd121003 Cleaned up resources on Windows. Now to do so for tester. 2019-06-01 10:24:32 -04:00
Pietro Gagliardi ed378d4e0b Cleaned up some easy-to-fix TODOs on Windows, including a LONGTERM that will not be relevant to libui in its current architecture (that'll be handled with a custom build step that converts resources to source code). Next: making resources.rc truly shared-only to reduce the noise further. 2019-06-01 10:18:47 -04:00
Pietro Gagliardi 48cd6e1dd8 Cleaned #include directives so C standard library headers are sorted and so each source file only includes one header file. This makes things clenaer, and alos allows us to more properly strip unneeded headers later. 2019-06-01 09:27:17 -04:00
Pietro Gagliardi a93f5c8f53 Split the strsafe strncpy() into its own header file and simplified its usage a bit. 2019-05-31 22:34:34 -04:00
Pietro Gagliardi 7d0f8403ab Fixed build errors on other platforms. This is a headache and a half :D I should split the files into strsafe_vsnprintf and strsafe_strncpy, but I want to share this file as is in one piece for now. 2019-05-31 22:07:51 -04:00
Pietro Gagliardi a17b7c8c14 Fixed build errors and simplified error handling in test/noinitwrongthread.c. Now to test this at runtime on all platforms. 2019-05-31 11:17:11 -04:00
Pietro Gagliardi 644e188e05 And rewrote strncpy(), since strncpy_s() also exists. Now to test the build. 2019-05-31 10:38:47 -04:00
Pietro Gagliardi 664cf26cdc Changed strcpy() to strncpy(). Let's build on Windows! 2019-05-31 02:58:57 -04:00
Pietro Gagliardi 1814745646 Wrote a wrapper for vsnprintf() that calls _vcsprintf()/vsnprintf_s() on Windows, because lol. I'm going to try switching from strcpy() to strncpy() for the other cases; let's see if this works. 2019-05-31 02:52:51 -04:00
Pietro Gagliardi 3ddaf3052e Fixed build issues on other platforms. That dumb msvcrt macro is next. 2019-05-31 00:24:12 -04:00
Pietro Gagliardi b5a109c063 And wrote array_impl.h. No more code duplication in this respect! Now for some more loose ends before we move on to controls. 2019-05-31 00:12:08 -04:00
Pietro Gagliardi dca2e5f038 Wrote array_header.h. Now for array_impl.h. 2019-05-30 23:47:07 -04:00
Pietro Gagliardi 2dd68fc47f Formalized the macro magic that actually works into sharedbits/{start,end}.h. Now to do arrays! 2019-05-30 23:23:02 -04:00
Pietro Gagliardi 6493faf529 Ah, figured it out. Now to separate these out into a separate header file. 2019-05-30 23:17:50 -04:00
Pietro Gagliardi 3c6cc53a0a Okay something is definitely not right. 2019-05-30 23:14:30 -04:00
Pietro Gagliardi 0f4602ee0f Moved the allocation functions to sharedbits/. Not fully working yet... 2019-05-30 23:11:08 -04:00
Pietro Gagliardi dc8620f9a8 Okay, no more ints that need to be changed. Started splitting out the common allocation and array code into a separate sharedbits/ folder. 2019-05-30 22:46:22 -04:00
Pietro Gagliardi c090dacc4a Switched the test code to use bool wherever appropriate. Will try to do this for the rest of libui, and then I'll deduplicate the allocation and array code. 2019-05-30 22:09:45 -04:00
Pietro Gagliardi b9d445554a Started changing the tests to use bool instead of int. 2019-05-30 10:33:39 -04:00
Pietro Gagliardi 9daef443b2 Moved uiQueueMain() to common/init.c to avoid having multiple initialized variables; the per-OS versions are now uiprivSysQueueMain(). Also more TODOs. 2019-05-30 01:39:43 -04:00
Pietro Gagliardi b0e890ca1d Missed a spot again 2019-05-30 01:18:20 -04:00
Pietro Gagliardi ab5b3076ed Removed uiInitError length checking and just truncated too-long messages with an ellipsis. Also removed the non-printf version of uiprivReportInitError. 2019-05-30 01:16:33 -04:00
Pietro Gagliardi 3049adbca0 Missed a spot 2019-05-29 21:12:49 -04:00
Pietro Gagliardi 19ad0d33a3 Changed uiInit() to return bool instead of int, now that we've settled on using bool. 2019-05-29 21:10:44 -04:00
Pietro Gagliardi edfd5e9157 Place a placeholder controls.md in doc/. Actually, let's do a bit more cleanup first. 2019-05-29 21:01:04 -04:00
Pietro Gagliardi 84a4fd8915 And wrote it on Windows. Okay, now we can FINALLY start working on controls!!!!! 2019-05-29 01:02:15 -04:00
Pietro Gagliardi 4815629eef Implemented the thread checks on GTK+. 2019-05-29 00:54:22 -04:00
Pietro Gagliardi fd770430a9 And properly added init and thread checks to the uiEvent functions. Now we just need to do the same on the other platforms and THEN FINALLY we can move on to controls. 2019-05-29 00:41:36 -04:00
Pietro Gagliardi a632d10701 Indented diff bodies (and all other multi-line test log prints) properly. 2019-05-29 00:35:51 -04:00
Pietro Gagliardi cceae4845e Fixed build issues and added more cases to allcalls.h. I'm going to fix the lack of idnent on the diff()s before continuing. 2019-05-28 22:53:40 -04:00
Pietro Gagliardi d0dd91d8d8 Started moving GTK+ dependency requirements away from being based on what Ubuntu packaged toward what it actually requires. This change just lists all of them in Compatibility.md for now. 2019-05-28 22:29:59 -04:00
Pietro Gagliardi b5927353e0 Started work to test that functions dislike being called before uiInit() or on a different thread; better to do it now than later. 2019-05-28 21:54:13 -04:00
Pietro Gagliardi b3f2214f3e okay what the fuck 2019-05-27 11:36:59 -04:00
Pietro Gagliardi 79ecadb909 Fuck the industry domination of C++ and its standard not forcing compliance in both C and C++ modes, if this is the case here. If not, then fuck MSVC. 2019-05-27 11:18:52 -04:00
Pietro Gagliardi a6c1e1ed17 Fixed build errors. Now to fix runtime errors. 2019-05-27 11:02:23 -04:00
Pietro Gagliardi 2e82d4aad4 Wrote uiprivReportError() on Windows. Now to fix the... lots of compiler errors :| 2019-05-27 10:45:55 -04:00
Pietro Gagliardi 3721bf0c8e Oops, we have to make the tests work on other platforms too. Done for GTK+. 2019-05-26 23:25:08 -04:00
Pietro Gagliardi afb87bda23 And filled in the remainder of the uiEvent error tests. Also more TODOs. We can now FINALLY move on to uiControl :D 2019-05-26 21:59:59 -04:00
Pietro Gagliardi 7cb4f010d4 Cleaned up programmer error testing code. 2019-05-26 20:19:02 -04:00
Pietro Gagliardi 7808b3ee94 Added the hook for checking programmer error responses, and made sure it works. Now to simplify it. 2019-05-26 15:41:22 -04:00
Pietro Gagliardi 9060ef3852 More TODOs. 2019-05-26 15:08:51 -04:00
Pietro Gagliardi e4f5b4f548 And filled in the missing gaps in the non-error cases of the uiEvent tests. Almost ready to move on! 2019-05-26 15:06:28 -04:00
Pietro Gagliardi 07c613b2e1 Stripped the path from test log filenames. 2019-05-26 13:31:55 -04:00
Pietro Gagliardi dbbf84becc Filled in the rest of TestEventBlocksHonored. 2019-05-26 13:26:58 -04:00
Pietro Gagliardi b8b1123027 And rewrote TestEventBlocksHonored. Now we can expand it. 2019-05-25 22:30:08 -04:00
Pietro Gagliardi f77f8d49d3 Restructured TestEventSendersHonored to use the new deinterleaved handler; this also makes it use baseParams and creates events per impl rather than globally. 2019-05-25 22:21:16 -04:00
Pietro Gagliardi a8ab5be01a Started deinterlacing the handler got and want parameters into struct handler. 2019-05-25 22:07:56 -04:00
Pietro Gagliardi 3cd08a5b2d Simplified the event structures. Next step is to deduplicate handlers. 2019-05-25 21:19:22 -04:00
Pietro Gagliardi c41ac17dd4 Rewrote test/events.c to localize the actual uiEventFire() testing code. 2019-05-25 21:11:03 -04:00
Pietro Gagliardi d6cde02825 More event tests. Okay, I definitely need a better way to handle these. 2019-05-23 21:42:52 -04:00
Pietro Gagliardi 5a6c302ae0 Added TestEventSendersHonored. 2019-05-22 11:44:30 -04:00
Pietro Gagliardi bf3492cd9c Cleaned up event test names. 2019-05-22 11:16:15 -04:00
Pietro Gagliardi d346a8ca8e Laid out the other things to test for events.c. 2019-05-21 21:12:01 -04:00
Pietro Gagliardi 43fd636071 And cleaned up the subtest architecture and made both tests properly subtested. Now for the rest of the tests. 2019-05-21 21:01:13 -04:00
Pietro Gagliardi bf1172bc0a Parameterized TestBasicEventsAddDeleteEventHandlers. Now we'll split it into subtests. 2019-05-21 20:16:41 -04:00
Pietro Gagliardi 009a9c25aa Oops (only spotted it when forcing a failure). 2019-05-20 22:26:11 -04:00
Pietro Gagliardi 7be128d5bb Changed TestBasicEventsSingleHandler to use subtests. It works! 2019-05-20 22:20:04 -04:00
Pietro Gagliardi 102dff6489 And added testingprivOutbufString() and enabled testingTRun(). Now to rewrite the events tests to use it. 2019-05-20 22:03:56 -04:00
Pietro Gagliardi 57e4e0d13b Removed the computedName field in testingT; we manage the memory for subtest names in testingTRun() itself. 2019-05-20 21:33:55 -04:00
Pietro Gagliardi 6412d8365f Stored the full options struct in testingT, so options can be passed to subtests; this also avoids the need to pass them into testingprivTRun(). 2019-05-20 21:22:02 -04:00
Pietro Gagliardi 3c9ff9c4bc Added a data parameter to test scaffolds; this is currently NULL, but subtests will use it (the subtest function is called directly, not through a scaffold). 2019-05-20 21:10:29 -04:00
Pietro Gagliardi bd666b8ec9 Started writing subtests. I'll need to make a few more structural changes to the test scaffolding for this. 2019-05-20 20:56:47 -04:00
Pietro Gagliardi 4112630f59 And fixed the remaining kinks with testingprivOutbuf. Now we can add subtests! (Forgot to mention that the previous commit also simplified the indent logic and split the actual function to run a test into its own function; this in particular also contributes to subtests.) 2019-05-20 20:07:59 -04:00
Pietro Gagliardi 9f0bb3aacb Fixed uiprivArray/testingprivArray bugs that led to the crash. Now to fix up the rest of the code. 2019-05-20 11:12:16 -04:00
Pietro Gagliardi a4c6817d33 Rewrote testing.c to use testingprivOutbuf. It doesn't quite work yet... 2019-05-20 10:57:31 -04:00
Pietro Gagliardi 9bec2005a1 Added smprintf() and outbuf to the testingpriv.h functions, introducing a simpler outbuf API along the way. Changing the test suite to actually use this comes next. 2019-05-19 23:15:35 -04:00
Pietro Gagliardi 40508a457c Changed testingSet to use testingprivArray. 2019-05-19 18:06:58 -04:00
Pietro Gagliardi 2d8764f06b Split the testing allocation and print functions into their own file, and added uiprivArray as testingprivArray. Switching everything to use testingprivArray will come next. 2019-05-19 17:55:31 -04:00
Pietro Gagliardi 8e5a12b869 And changed unusedIDs to use uiprivArray. Now we can transplant this into testing. 2019-05-19 15:46:09 -04:00
Pietro Gagliardi 02b61c0156 Changed uiEvent.handlers into a uiprivArray; also fixed the buggy implementations of InsertAt and Delete. 2019-05-19 14:37:32 -04:00
Pietro Gagliardi 745440b1e7 Wrote array manipulation functions. Now to convert events.c to use them. 2019-05-19 12:52:06 -04:00
Pietro Gagliardi 66f1f75992 Started rearranging things to allow for subtests; I should clean up the allocation stuff in libui first, so they can be borrowed. 2019-05-19 11:05:20 -04:00
Pietro Gagliardi efb761f5f2 More notes. 2019-05-19 09:31:32 -04:00
Pietro Gagliardi 35759935ae Added more cases to the AddDelete tests. Now to add subtests so we can more easily pluralize these tests, including handling cases of different senders. 2019-05-19 01:06:43 -04:00
Pietro Gagliardi a81ea6e3fb Fixed events not being deleted properly. 2019-05-19 01:00:25 -04:00
Pietro Gagliardi 973696137f Reduced noise in event tests when a handler that should have run did not run. 2019-05-18 22:21:23 -04:00
Pietro Gagliardi 3b1f5e0e54 Added more event tests. DeleteHandler obviously does not work... and I really need to add subtests. 2019-05-18 21:21:55 -04:00
Pietro Gagliardi 43f7d1a661 Started the events test suite. 2019-05-18 13:26:55 -04:00
Pietro Gagliardi 66247ce73d Fixed building events.c. 2019-05-18 10:00:07 -04:00
Pietro Gagliardi 25afc9b13f And finished writing events.c. Now to write the tests. 2019-05-17 22:40:29 -04:00
Pietro Gagliardi 56156f549b Bah C99 2019-05-16 23:34:33 -04:00
Pietro Gagliardi 96c346c2dd Added uiprivAlloc() and friends and implemented uiNewEvent(). 2019-05-16 23:02:03 -04:00
Pietro Gagliardi bd84da7179 More implementation of uiEvent. Now we'll need to deal with memory allocation. 2019-05-16 12:27:04 -04:00
Pietro Gagliardi caa926feeb Started implementing and deduplicating the error handling in events.c. 2019-05-15 23:11:21 -04:00
Pietro Gagliardi 55a7e3e56e Started implementing events. 2019-05-15 22:40:06 -04:00
Pietro Gagliardi 101df7a469 Added uiEvent-specific programmer errors. Next up: actually implementing uiEvent. 2019-05-14 11:03:03 -04:00
Pietro Gagliardi 7c128e7bcd Added internal errors and refined the error handling somewhat. 2019-05-13 21:30:18 -04:00
Pietro Gagliardi 45f69cf058 Renamed programmererror.c to errors.c so we can add internal errors (which used to be called implementation bugs). 2019-05-13 10:46:42 -04:00
Pietro Gagliardi 7022e6f268 More programmer error refinement. 2019-05-13 06:37:19 -04:00
Pietro Gagliardi 0d21bf8846 Added Debugger() to the programmer error handler on macOS. This will also be used when I add a bug-in-libui function. 2019-05-13 01:00:19 -04:00
Pietro Gagliardi b3049b0a1e Started reintegrating what used to be called user bugs; they're now called programmer errors. We'll create a much more systematic approach to them. Implemented on macOS. 2019-05-12 22:17:24 -04:00
Pietro Gagliardi 781a4117a7 Slightly more documentation. Now to figure out error handling. 2019-05-12 13:59:31 -04:00
Pietro Gagliardi 1a047da08c Fuck it, we're going with <stdbool.h>. I don't like it, but meh. 2019-05-12 13:41:43 -04:00
Pietro Gagliardi d7fa5e63be More events.md stuff. I need to decide if I should just give in and use <stdbool.h>... 2019-05-11 13:40:36 -04:00
Pietro Gagliardi 637c7a7e2c Oops 2019-05-10 21:36:38 -04:00
Pietro Gagliardi 07dc3ee025 Finished the test set implementation, moving the tester itself to use it. This also moves uiInit() into main(); in TestInit, we'll hook into uiInit() to avoid actually doing the initialization. 2019-05-10 21:16:29 -04:00
Pietro Gagliardi 7f986ef073 Started adding more flexible test set functionality. 2019-05-09 22:54:14 -04:00
Pietro Gagliardi f97383f66c Rearranged things so that uiInit() itself is in common/init.c and the OS-specific initialization code is its own standalone internal function. This'll make testing a bit easier, especially after the next few commits, and will allow me to test pre-initialization and cross-thread stuff a bit more easily. 2019-05-09 12:07:28 -04:00
Pietro Gagliardi e1c970bdbd More event interface refining. 2019-05-07 23:12:18 -04:00
Pietro Gagliardi 4aaad25c02 Started writing the documentation for the new event handling interface. At this point I'm tempted to forego the overview sections for the time being, since all the contractual stuff would go in the reference. 2019-05-07 23:05:31 -04:00
Pietro Gagliardi ab42a9ed08 More xvfb-like notes. I'm not sure if I'll actually do this now or not :/ 2019-05-06 23:02:09 -04:00
Pietro Gagliardi ca570868c9 More notes. 2019-05-06 06:02:22 -04:00
Pietro Gagliardi c79ddfd971 More notes. 2019-05-05 23:40:36 -04:00
Pietro Gagliardi 2e562e4946 Merge branch 'master' into remodel 2019-05-05 23:39:48 -04:00
Pietro Gagliardi bc0d3120c8 Added verbosity to the test suite. Now we can move on. 2019-05-05 15:59:59 -04:00
Pietro Gagliardi 3ebc58a5bf Fixed build issues in MinGW-w64. 2019-05-05 15:20:54 -04:00
Pietro Gagliardi b7f06074fb Converted testing.c to buffer test output. Also reordered test logs to be after the status line, like in Go. Now we can add verbosity control, and then maybe later even subtests! 2019-05-05 14:26:38 -04:00
Pietro Gagliardi 8b70e6d247 Cleaned up internal helper functions in testing.c, including adding one for vsnprintf() and snprintf(). This is the first step toward buffering test output and adding verbosity control. 2019-05-05 13:35:07 -04:00
Pietro Gagliardi d49f8ae7e6 Switched the macOS code to use timerprivInt128ToUint64(). Also rearranged the arguments of timerprivInt128ToInt64() and added proper timerDurationMin/Max constants instead of the incorrect-for-that-function timerTimeMin/Max in the Windows code. 2019-05-05 11:16:17 -04:00
Pietro Gagliardi ff803bf792 And implemented 128-bit muldiv on Windows. 2019-05-05 11:03:17 -04:00
Pietro Gagliardi 288c74b026 Implemented the new MulDiv128 stuff on macOS. To make things easier in regards to signedness, this implementation also uses a base, like the Unix one, and the mach_timebase_info() conversion is done in timerMonotonicTime() instead. (Also added the admittedly-redundant-since-the-current(ish)-implementation-cannot-fail error check to mach_timebase_info().) As a result, timerTimeSub() can now be end - start on all platforms. Also found and fixed bugs in the actual muldiv128 algorithm implementation. Next up: Windows. 2019-05-05 03:17:11 -04:00
Pietro Gagliardi 952b36b1c2 Added proper 128-bit muldiv() stuff for macOS and Windows. Will actually use them next. Also fixed compiling on non-Windows. 2019-05-05 02:17:11 -04:00
Pietro Gagliardi 7424a9ea6c Added a note on static Windows linking to the using-libui.md that I overlooked all this time until I realized we needed this now. 2019-05-04 21:49:07 -04:00
Pietro Gagliardi ee3e587ddc Fixed build errors on Windows. 2019-05-04 21:47:04 -04:00
Pietro Gagliardi 98093ed46e Formatted test results across multiple lines in the same way as in Go. Also prettyprinted diffs in initmain.c. 2019-05-04 21:13:47 -04:00
Pietro Gagliardi 4c097f93f7 Implemented error checks in test/initmain.c. 2019-05-04 19:21:57 -04:00
Pietro Gagliardi 09e98af110 Removed a stale TODO caused by inaccurate timer code (that was in response to the old clock()-based Unix code). 2019-05-04 19:04:32 -04:00
Pietro Gagliardi 00411a4d07 Moved all the test infrasturcture files into a subdirectory of test/ called lib/. 2019-05-04 16:53:54 -04:00
Pietro Gagliardi 57abc83fe3 And split all the thread stuff into their own file. Almost done with all this test library cleanup! 2019-05-04 16:47:56 -04:00
Pietro Gagliardi 89e882c4a8 And fixed build errors on non-macOS systems. We're good! 2019-05-04 14:44:29 -04:00
Pietro Gagliardi bfd608cf8e And of course setitimer() doesn't allow us to override the struct sigvalue, so we can't chain out to outer SIGALRM calls. Hooray for global state! 2019-05-04 14:31:33 -04:00
Pietro Gagliardi 5836a2a236 Ugh, macOS doesn't support timer_create() and friends :| (Even up to 10.13, it seems; not sure about 10.14!) Switch back to setitimer(). Also rename oldsig to prevSig and remove a needless (for now???) cast. 2019-05-04 14:24:22 -04:00
Pietro Gagliardi 8655bbf19c Finished porting over the timer functions from testing_darwinunix.c to timer_darwinunix.c. This also includes changing timerRunWIthTimeout() from using setitimer() to using timer_create() and friends, and removing the fallbacks from timerSleep().
Also fixed a possible incorrect use of TlsSetValue() in the Windows code that I spotted while writing an intermediate version of the Unix code.
2019-05-04 12:13:07 -04:00
Pietro Gagliardi 6a25efce63 Started writing the new timer_darwinunix.c file; implemented the monotonic time functions for now. On non-macOS, we now require clock_gettime() to be successful, as GLib also does (at least as far back as the version that ships with Ubuntu 14.04); we also gather the time at first call and use that as the basis for returning a single integer from timerMonotonicNow(), so timerTimeSub() is a simple subtraction now. The logic for this conversion is based on the logic of timevalsub() in macOS's sys/time.h, though this is really simple to understand. 2019-05-03 23:03:38 -04:00
Pietro Gagliardi e9fd8cc878 And made timerRunWithTimeout() use the non-reentrant code on all architectures. Windows code done. 2019-05-03 10:41:27 -04:00
Pietro Gagliardi 020cabc61e Fixed build issues. Now to fix runtime issues: we'll have to use reentrancy everywhere, because rcx is clobbered by GetMessage(). 2019-05-03 10:32:31 -04:00
Pietro Gagliardi dbfea28313 And finished adjusting the tests accordingly. Now to test. 2019-05-03 02:02:20 -04:00
Pietro Gagliardi e2baa5bb5c And finished rewriting timerRunWithTimeout() on Windows, including reentrancy improvements, race condition elimination, setjmp() volatility correctness fixes, and global state reduction. 2019-05-03 01:54:26 -04:00
Pietro Gagliardi 8b5b03794c Started moving testingRunWithTimeout() into timer.h. 2019-05-02 23:15:49 -04:00
Pietro Gagliardi 313f5864f5 Fixed the timer functions on Windows. 2019-05-02 22:03:57 -04:00
Pietro Gagliardi 42623f92e9 And started breaking apart the timer functions. 2019-05-02 12:38:59 -04:00
Pietro Gagliardi 5537e823ef Started splitting out the timer stuff into its own mini-library. Also I decided to drop the fallback stuff, since we run on new enough systems anyway, and nanosleep() is available on every version of macOS we need anyway. 2019-05-02 12:14:08 -04:00
Pietro Gagliardi 99a0f9084c Fixed building on Ubuntu Trusty. 2019-05-02 02:01:42 -04:00
Pietro Gagliardi 796a9cf010 Resolved some TODOs in testing_darwinunix.c, including writing a more elaborate implementation of testingSleep(). 2019-04-30 21:00:52 -04:00
Pietro Gagliardi 7d29c4346d Decided not to make the thread functions report errors to the testingT object; I'm still not a big fan of the thread model here, but I'd like to actually split the thread functions out of the testing library entirely, since they are only used for the tests in initmain.c. 2019-04-30 13:15:36 -04:00
Pietro Gagliardi 25d4021269 Resolved timer/duration variable name TODO. 2019-04-30 09:32:48 -04:00
Pietro Gagliardi 69000cda46 More TODOs. 2019-04-30 02:32:44 -04:00
Pietro Gagliardi 965dec0157 Settled the PostThreadMessage() TODOs in the Windows testing code. 2019-04-30 02:31:08 -04:00
Pietro Gagliardi 848c3813ee Cleaned up the error handling in the Windows testing code by also creating HRESULT functions for whatever it calls. This resolves a bunch of the TODOs in that file. 2019-04-30 01:18:48 -04:00
Pietro Gagliardi db6b6fd97b Provided a mechanism for the testing library to abort on an internal error; redefined memory allocation to do so. This will also be used for resolving many of the TODOs on the Windows testing code. 2019-04-29 23:46:08 -04:00
Pietro Gagliardi 559e4bc139 Changed uiprivHrGetMessageW() to return the BOOL ret value as S_OK and S_FALSE. 2019-04-29 23:12:39 -04:00
Pietro Gagliardi 05af10aade Added ARM64 to the CPU-specific Windows testing code. 2019-04-29 22:34:28 -04:00
Pietro Gagliardi 2282ee2cf8 Removed TODOs about overflow; we really can't handle overflow properly anyway, so eh. 2019-04-29 00:18:26 -04:00
Pietro Gagliardi f948c30a3b Simplified testingTimerNsec() on Windows. _div128() would make this super simple, but that's only available on VS2019, and it seems no one has implemented this in software elsewhere (the most I can find are unsigned ones...). 2019-04-28 23:20:01 -04:00
Pietro Gagliardi 3257710fb7 Fixed bad timers on GTK+ tests. Turns out that clock() doesn't count when the process isn't actively running code (for instance, if it's waiting for I/O) :| 2019-04-28 21:49:54 -04:00
Pietro Gagliardi 74468bb38f And implemented the multithreading stuff on Unix and Darwin. We can finally continue re-adding stuff to libui! 2019-04-28 21:22:11 -04:00
Pietro Gagliardi df8eadb980 And added the multithreaded uiQueueMain() tests. 2019-04-28 20:45:53 -04:00
Pietro Gagliardi 6c41fb712e More notes. 2019-04-28 19:11:52 -04:00
Pietro Gagliardi 74ca863c1b More TODOs. 2019-04-28 19:07:41 -04:00
Pietro Gagliardi 5fb4e4403e And implemented testingRunWithTimeout() on Windows. All tests currently pass on all platforms! Woo! 2019-04-28 19:04:27 -04:00
Pietro Gagliardi 8ffb2b1b1e Fixed build warnings and errors. Now we just need to implement testingRunWithTimeout() on Windows. 2019-04-28 16:35:25 -04:00
Pietro Gagliardi 5548119d8d Big oops 2019-04-28 16:26:00 -04:00
Pietro Gagliardi 812c559b11 Implemented uiMain() and friends on Windows. As a result, started the new HRESULT wrapper stuff. 2019-04-28 16:23:25 -04:00
Pietro Gagliardi 49bde22f81 More TODOs. 2019-04-28 14:54:33 -04:00
Pietro Gagliardi bdf80516c5 Reimplemented uiMain() and friends on GTK+. 2019-04-28 14:52:39 -04:00
Pietro Gagliardi 10ab539ff0 Removed the program used to find the cases for TestQueueMain_Sequence. 2019-04-28 14:41:50 -04:00
Pietro Gagliardi b20bf2d1c9 Wrote TestQueueMain_Sequence. 2019-04-28 14:41:30 -04:00
Pietro Gagliardi 0f6414af6f Added the program I used to determine what to do for uiQueueMain() order testing. I will delete this once the order is actually implemented. 2019-04-28 14:25:18 -04:00
Pietro Gagliardi b8a7b57835 And finally fixed uiQuit() on macOS. 2019-04-28 13:43:55 -04:00
Pietro Gagliardi e03021a350 Piped the actual file/line through to testingRunWithTimeout(). 2019-04-28 13:26:15 -04:00
Pietro Gagliardi 64478bd5b0 Finished cleaning up the TimerNsec naming wonk. 2019-04-28 13:19:04 -04:00
Pietro Gagliardi 1bc2297597 Added a facility to do timeouts in test functions. Also started cleaning up the weird TimerNsec abstractions. 2019-04-28 13:12:40 -04:00
Pietro Gagliardi 9c70782a0f Added uiMain() and friends on macOS and refined the tests. So now we'll need to add a timeout mechanism because that sendEvent: stuff really is still neeeded and I'd like to prevent shenanigans later. 2019-04-28 11:48:21 -04:00
Pietro Gagliardi 2c57498e44 Re-added uiMain(), uiQuit(), and uiQueueMain(). 2019-04-27 21:48:51 -04:00
Pietro Gagliardi aa49da98ba And implemented testingTimer on Windows. 2019-04-23 22:49:28 -04:00
Pietro Gagliardi 7e631879e7 Wrote the Unix clock function. It's primitive, but it works. 2019-04-23 12:26:52 -04:00
Pietro Gagliardi 408b106526 Merge branch 'master' into remodel 2019-04-23 09:10:38 -04:00
Pietro Gagliardi 4bd1ba2fa5 Oops; flipped a boolean. 2019-04-22 23:03:12 -04:00
Pietro Gagliardi bd1ca240e3 Added timers to the testing framework, test timing (so no more of those pesky TODOs), and the OS X implementation of test timing. 2019-04-22 23:01:55 -04:00
Pietro Gagliardi 23591eeefa Executive decision: drop uiUnint(). This was really more for double-checking *my* work, mostly with regards to memory management, and we could probably do that in the test suite instead, or even with AddressSanitizer. 2019-04-21 20:59:36 -04:00
Pietro Gagliardi 80ca6e2fc1 More notes. 2019-04-21 18:00:38 -04:00
Pietro Gagliardi 63952a3e20 ANd integrated the test manifest back into the tester. 2019-04-21 17:59:29 -04:00
Pietro Gagliardi f548f6d4d2 Readded the manifest to libui.dll. The test suite passes! 2019-04-21 17:46:50 -04:00
Pietro Gagliardi b8b3b3df39 Fixed the rest of the build errors and warnings. Woo! Now for the manifests. 2019-04-21 17:28:47 -04:00
Pietro Gagliardi f7867f3427 ALMOST fixed the build on Windows with MSVC. (For the change to test/testing.h: Even in VS2019 inline isn't available in C, ugh. That function doesn't need to be inline anyway, so eh.) 2019-04-21 15:08:09 -04:00
Pietro Gagliardi c6aa8c3324 Pruned windows/init.cpp to a minimal initialization that we can test things with, which includes adding a function to load err->Message with a formatted string.
Also added a better error for if InitCommonControlsEx() returns 0 without setting the last error; this usually means that v5 is loaded.
Fixes #451.
Updates #337.
2019-04-21 14:49:16 -04:00
Pietro Gagliardi b89a18f3dd Don't define DllMain() in a static build. Fixes #433. 2019-04-21 13:58:19 -04:00
Pietro Gagliardi 29c51b6348 Started readding the Windows code; added the Windows version headers to the docs as well now since I'm cleaning up winapi.hpp as well. 2019-04-21 13:54:39 -04:00
Pietro Gagliardi 469484415d Implemented uiInit() on Unix. 2019-04-21 12:24:19 -04:00
Pietro Gagliardi 2652772891 Wrapped the init example in a main() for maximum clarity. 2019-04-20 21:56:50 -04:00
Pietro Gagliardi 484989e925 Implemented uiInit() on macOS. The tests work so far! 2019-04-20 21:38:26 -04:00
Pietro Gagliardi 74b1e2780d Implemented the cross-platform bits of uiInit(). 2019-04-19 12:32:13 -04:00
Pietro Gagliardi 5e45afd0a2 More TODOs. 2019-04-19 12:13:08 -04:00
Pietro Gagliardi 6f3d45b107 Made it so that normal and after tests don't run if before tests failed. 2019-04-19 12:12:13 -04:00
Pietro Gagliardi da36b304f8 Wrote the uiInit() test. 2019-04-19 12:10:45 -04:00
Pietro Gagliardi 668a908e76 Fleshed out init-main.md some more. 2019-04-19 00:56:52 -04:00
Pietro Gagliardi 3001e5dfaf Merge branch 'master' into remodel 2019-04-17 23:17:22 -04:00
Pietro Gagliardi e5e60284fb More desirable sorting of tests: by line number per file, specifically. Also per-file, but that can remain unguaranteed if I ever spin this out into its own library. 2019-04-17 22:58:44 -04:00
Pietro Gagliardi 16c6425200 Reorganized test sets into growing arrays. We can sort them next. 2019-04-17 22:12:32 -04:00
Pietro Gagliardi dcf34e6dab Set up the necessary work for having the list of tests sort by filename/line number instead of init order. 2019-04-17 21:49:47 -04:00
Pietro Gagliardi 2f58c2059e Oops 2019-04-12 22:06:23 -04:00
Pietro Gagliardi 745f2da3b8 Forgot something 2019-04-12 22:05:21 -04:00
Pietro Gagliardi 541f9d892d More documentation of initialization. 2019-04-12 22:02:01 -04:00
Pietro Gagliardi 4c46a25154 Started re-adding the uiInit functions, with a new method of returning errors. 2019-04-12 21:44:39 -04:00
Pietro Gagliardi db9be102e6 Properly defined uiprivExtern in static cases. 2019-04-12 10:56:09 -04:00
Pietro Gagliardi 59b449b920 Added rudimentary test ordering, for the Init and Uninit tests. 2019-04-10 20:17:40 -04:00
Pietro Gagliardi 1b046e763b Fixed the test suite. 2019-04-10 20:11:44 -04:00
Pietro Gagliardi 4f381d04a1 Added some test cases and the build script. 2019-04-10 14:42:17 -04:00
Pietro Gagliardi 759d6d1e46 Allowed deferred functions to access the testingT. If they call FailNow, we act as if nothing happens and the defers keep running. 2019-04-10 14:27:21 -04:00
Pietro Gagliardi 0149639edc Cleaned things up a bit for libui's test suite. 2019-04-10 14:19:17 -04:00
Pietro Gagliardi b26e0677af Okay, I'm not satisfied with the other testing frameworks, so strip testing.h down to just what libui needs. 2019-04-10 13:23:25 -04:00
Pietro Gagliardi ced0820e39 Undo that; this is probably overkill for libui's unit tests... 2019-04-10 12:55:37 -04:00
Pietro Gagliardi 5a0477d46d Moved the testing files back here since we're actually going to start writing proper unit tests now. 2019-04-10 12:18:08 -04:00
Pietro Gagliardi d861de85f6 Fleshed out using-libui.md. 2019-04-09 11:23:39 -04:00
Pietro Gagliardi 7387f08ee9 Added OS-specific header guidance. 2019-04-09 10:56:21 -04:00
Pietro Gagliardi b2cee470ca Oops, I accidentally swapped these two files. 2019-04-09 10:47:36 -04:00
Pietro Gagliardi de3fd3f546 Executive decision: remove menus and dialogs in the rebuild. We'll add them back later. 2019-04-09 10:46:00 -04:00
Pietro Gagliardi d5eb06327d And started the README on the subject. 2019-04-08 23:41:14 -04:00
Pietro Gagliardi 9f4b6507b1 Changed everything to use uiStatic instead of _UI_STATIC. 2019-04-08 23:39:27 -04:00
Pietro Gagliardi c5db824918 Started writing the documentation. Also decided that uiStatic must be defined in order to use libui as a static library. I'll change libui itself next. 2019-04-08 21:23:22 -04:00
Pietro Gagliardi c9643ea5a6 Gutted the existing header files, started trimming things that remain in the backups, and renamed _UI_EXTERN and _UI_ENUM and the include guards and libui_EXPORTS to start with uipriv instead. 2019-04-08 20:55:06 -04:00
Pietro Gagliardi 12d52be921 Oops; missed a spot. 2019-04-08 20:38:17 -04:00
Pietro Gagliardi 9bcb6d0af0 And made copies of the old files so we can properly rebuild them. 2019-04-08 20:37:31 -04:00
Pietro Gagliardi 992d8694a9 Let's start the remodel. Move almost everything out of the way. 2019-04-08 20:36:21 -04:00
389 changed files with 14009 additions and 3848 deletions

97
CODE-OF-CONDUCT Normal file
View File

@ -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
Mozillas 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.

View File

@ -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

View File

@ -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.

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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

View File

@ -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/

View File

@ -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

58
_notes/pieInTheSkyTargets Normal file
View File

@ -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?...

25
_notes/winAcrylic Normal file
View File

@ -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

37
_notes/winAlternateHiDPI Normal file
View File

@ -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 ****

34
_notes/winBlurBehind Normal file
View File

@ -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/

View File

@ -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

4
_wip/dialogs/ui.h Normal file
View File

@ -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);

17
_wip/menus/ui.h Normal file
View File

@ -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);

148
_wip/uninit/main.c Normal file
View File

@ -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);
}

288
_wip/uninit/main.m Normal file
View File

@ -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

9
common/alloc.c Normal file
View File

@ -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

View File

@ -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;
}

290
common/controls.c Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

55
common/errors.c Normal file
View File

@ -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);
}

132
common/main.c Normal file
View File

@ -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;
}

View File

@ -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',
]

93
common/programmererrors.h Normal file
View File

@ -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")
// }

20
common/testhooks.h Normal file
View File

@ -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

View File

@ -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
}

41
common/utf8.c Normal file
View File

@ -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);
}

64
common/window.c Normal file
View File

@ -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);
}

42
darwin/controls.m Normal file
View File

@ -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));
}

View File

@ -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();
}

View File

@ -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.

View File

@ -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 += [

View File

@ -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);

View File

@ -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

12
doc/00_index.md Normal file
View File

@ -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)

142
doc/controls.md Normal file
View File

@ -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?)

45
doc/darwin-controls.md Normal file
View File

@ -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`?

45
doc/haiku-controls.md Normal file
View File

@ -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`?

79
doc/init-main.md Normal file
View File

@ -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.

47
doc/unix-controls.md Normal file
View File

@ -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`?

147
doc/using-libui.md Normal file
View File

@ -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.

81
doc/window.md Normal file
View File

@ -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.

117
doc/windows-controls.md Normal file
View File

@ -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

42
haiku/controls.cpp Normal file
View File

@ -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));
}

91
haiku/main.cpp Normal file
View File

@ -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");
}

16
haiku/meson.build Normal file
View File

@ -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?...

21
haiku/uipriv_haiku.hpp Normal file
View File

@ -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;

115
haiku/window.cpp Normal file
View File

@ -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);
}

View File

@ -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')

4
sharedbits/TODO.md Normal file
View File

@ -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)

17
sharedbits/alloc_header.h Normal file
View File

@ -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"

41
sharedbits/alloc_impl.h Normal file
View File

@ -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"

35
sharedbits/array_header.h Normal file
View File

@ -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"

91
sharedbits/array_impl.h Normal file
View File

@ -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"

5
sharedbits/end.h Normal file
View File

@ -0,0 +1,5 @@
// 30 may 2019
#undef sharedbitsPrefixName
#undef sharedbitsPrefixExpandMakeName
#undef sharedbitsPrefixMakeName

View File

@ -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

8
sharedbits/start.h Normal file
View File

@ -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)

View File

@ -0,0 +1,8 @@
// 8 june 2019
// requires: alloc_header.h
#include "start.h"
extern char *sharedbitsPrefixName(Strdup)(const char *s);
#include "end.h"

17
sharedbits/strdup_impl.h Normal file
View File

@ -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"

View File

@ -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"

73
sharedbits/strsafe_impl.h Normal file
View File

@ -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"

25
test/allcalls.h Normal file
View File

@ -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)

5
test/allcalls_darwin.h Normal file
View File

@ -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)

5
test/allcalls_haiku.hpp Normal file
View File

@ -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)

5
test/allcalls_unix.h Normal file
View File

@ -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)

9
test/allcalls_windows.h Normal file
View File

@ -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)

669
test/controls.c Normal file
View File

@ -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);
}

54
test/controls_darwin.m Normal file
View File

@ -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);
}

58
test/controls_haiku.cpp Normal file
View File

@ -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);
}

54
test/controls_unix.c Normal file
View File

@ -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);
}

130
test/controls_windows.c Normal file
View File

@ -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

109
test/errors.c Normal file
View File

@ -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);
}

597
test/initmain.c Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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

17
test/noinitwrongthread.c Normal file
View File

@ -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

View File

@ -0,0 +1,6 @@
// 28 may 2019
#import "test_darwin.h"
#import "thread.h"
#define allcallsHeader "allcalls_darwin.h"
#include "noinitwrongthreadimpl.h"

View File

@ -0,0 +1,6 @@
// 28 may 2019
#include "test_haiku.hpp"
#include "thread.h"
#define allcallsHeader "allcalls_haiku.hpp"
#include "noinitwrongthreadimpl.h"

View File

@ -0,0 +1,6 @@
// 28 may 2019
#include "test_unix.h"
#include "thread.h"
#define allcallsHeader "allcalls_unix.h"
#include "noinitwrongthreadimpl.h"

View File

@ -0,0 +1,6 @@
// 28 may 2019
#include "test_windows.h"
#include "thread.h"
#define allcallsHeader "allcalls_windows.h"
#include "noinitwrongthreadimpl.h"

View File

@ -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

View File

@ -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

9
test/resources_shared.rc Normal file
View File

@ -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"

8
test/resources_static.rc Normal file
View File

@ -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"

View File

@ -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

5
test/test_darwin.h Normal file
View File

@ -0,0 +1,5 @@
// 10 june 2019
// TODO proper macros and headers
#import <Cocoa/Cocoa.h>
#define libuiOSHeader "../ui_darwin.h"
#import "test.h"

8
test/test_haiku.hpp Normal file
View File

@ -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