That will make negative numbers very large and discard them as a result.
The bug was caused by a wrong assumption that individual write_sgr functions are there for optimization purposes. They are not. Their purpose is to make TB_DEFAULT work. Therefore TB_DEFAULT != lastfg/lastbg. In fact the send_attr puts SGR0 as soon as it detects deviation from lastfg or lastbg, which means the actual attributes are equal to terminal defaults from that point. The fix restores the previous logic for NORMAL mode and forces a complete update for other output modes. Again, complete doesn't mean less optimized, it was complete in most of the cases before as well. The update happens partially only if the TB_DEFAULT is involved.
As a side note, it makes sense to convert the preprocessor defines to enums eventually. Better for parsing tools.
Yes, having _MODE_ as part of the constant name is better, but in order to be consistent, I'm changing these names to ugly ones. I would rather add _MODE_ part to the input mode constants, but it's a major API breakage, while breaking new API isn't as painful. At some point transition to termbox2 will happen, where I'll adjust API as I want, including better constant names. For now I choose consistency over prettiness.
Output mode functions/constants. Realign things using spaces instead of tabs.
Additionally, make cosmetic change by transforming if() into switch() statements.
Well, actually there are two translation units, but only because utf8 functions are kind of pure and separate anyway. I would like to remove them, but the demo uses them and in most cases the end user needs them. It is sad that C standard library still doesn't have proper utf8 functions.