Ticket #4995: color handling refactoring#4995
Ticket #4995: color handling refactoring#4995egmontkob wants to merge 2 commits intoMidnightCommander:masterfrom
Conversation
85b624c to
ac6cacb
Compare
zyv
left a comment
There was a problem hiding this comment.
I've had a look at it, and mechanically your rework definitively seems like a much better way to achieve the same goal. My understanding of the whole system, however, is so limited that I can't judge if this approach introduces some new problems or not, and if there is an even better way to do it. I'm sorry, but I would like to defer to Andrew on this...
mc-worker
left a comment
There was a problem hiding this comment.
Cool! Thank you very much!
lib/skin/colors.c
Outdated
| /* | ||
| * Helper for mc_skin_color_cache_init () | ||
| */ | ||
| static void |
lib/tty/tty-internal.c
Outdated
| /* --------------------------------------------------------------------------------------------- */ | ||
|
|
||
| int | ||
| tty_maybe_map_color (int color) |
There was a problem hiding this comment.
Let's move it to color-internal.[ch].
There was a problem hiding this comment.
Obviously, that's a much better place. I didn't realize we had such a file :) Done.
lib/tty/tty-internal.c
Outdated
| { | ||
| if (color >= COLOR_MAP_OFFSET) | ||
| return tty_color_role_to_pair[color - COLOR_MAP_OFFSET]; | ||
| else |
There was a problem hiding this comment.
else after return is unnecessary.
ac6cacb to
742de5e
Compare
lib/tty/color.h
Outdated
| */ | ||
| #define COLOR_MAP_OFFSET 4096 | ||
|
|
||
| enum |
There was a problem hiding this comment.
I think the lib/tty layer should not know which basic widgets and "applications" (editor, viewer, ...) are implemented. Let's move color definitions and related macros back to lib/skin.
There was a problem hiding this comment.
Sorry for the long delay. I was thinking of a truly clean solution, but I didn't find one.
I did the change you requested, but now we need to have two lib/tty/* -> #include "lib/skin.h" dependencies. Not sure if it's better. What do you think?
There was a problem hiding this comment.
"there is no problem in cs that cannot be solved with another layer of indirection" ... 🤣
but more seriously, if you reframe a skin as the color palette, then it becomes obvious that it is intrinsically linked with color resolution. one could avoid that only by making the system way more dynamic, but that would also make it much heavier. so i'm not convinced that andrew's assertion holds up, but i haven't looked into it very deeply.
There was a problem hiding this comment.
I did the change you requested, but now we need to have two
lib/tty/*->#include "lib/skin.h"dependencies. Not sure if it's better. What do you think?
I think I've fixed this: edf652e. What do you think?
There was a problem hiding this comment.
I think I've fixed this: edf652e. What do you think?
I generally like your approach, thanks!
Two nitpicks:
-
You've added a
TTY_prefix toCOLOR_MAP_OFFSET, don't you want to carry it consistently toCOLOR_MAP_NEXTandCOLOR_MAP_SIZE, too? -
With the new g_array business, it's essential that we've called
tty_init_colors()but nottty_colors_done(). Otherwise a color lookup accesses random uncontrolled parts of the memory. What do you think of settingtty_color_role_to_pairtoNULLat its definition/initialization as well as intty_colors_done()? It'd make broken code more obviously broken, accessing aNULLpointer rather than some random address.
And one more question:
I'm not familiar with this "fixup" workflow... is it that you simply manually begin the commiit message with fixup!, and then hitting the big green Merge button in GitHub will automatically squash it? Or what's the exact workflow with them?
Thanks!
There was a problem hiding this comment.
I think I've fixed this
Probably, usage of GArray is overdesign. Simple int * is enough.
There was a problem hiding this comment.
Probably, usage of
GArrayis overdesign. Simpleint *is enough.
Indeed, good ol' malloc/free is just fine.
There was a problem hiding this comment.
I'm not familiar with this "fixup" workflow... is it that you simply manually begin the commiit message with
fixup!, and then hitting the big green Merge button in GitHub will automatically squash it? Or what's the exact workflow with them?
You can edit things and then git add -p and git commit --fixup REF to mark them as belonging to a particular commit. After that, if you do git rebase -i --autosquash, it will automatically reorder marked commits and fuse them together. This is what my rebase bot does among other things. It's useful for self-review when you check your own patch series and make many small adjustments. Or to show you what I want to change more exactly without having you do a range diff on an already rebased patches. Check the manual for more details.
There was a problem hiding this comment.
- You've added a
TTY_prefix toCOLOR_MAP_OFFSET, don't you want to carry it consistently toCOLOR_MAP_NEXTandCOLOR_MAP_SIZE, too?
No. COLOR_MAP_OFFSET is defined in lib/tty, others aren't.
With the new g_array business...
Done.
I'm not familiar with this "fixup" workflow... is it that you simply manually begin the commiit message with fixup!
Such message is generated by git commit --fixup COMMIT_TO_FIX.
and then hitting the big green Merge button in GitHub will automatically squash it?
Can't say. I do everything on git-client side. All "fixup!" commits are squashed with target ones using git rebase -i --autosquash ....
There was a problem hiding this comment.
and then hitting the big green Merge button in GitHub will automatically squash it?
Can't say. I do everything on git-client side. All "fixup!" commits are squashed with target ones using
git rebase -i --autosquash ....
I have added merge checks that should ideally prevent exactly that. You might be able to bypass it as an administrator, but generally the idea is to make sure fixups don't get merged. To get rid of them, one would use the rebase bot or do it manually like Andrew.
742de5e to
617cfc7
Compare
|
(just a rebase; the review comment is yet to be addressed) |
…dget Signed-off-by: Egmont Koblinger <egmont@gmail.com>
617cfc7 to
4677b61
Compare
lib/skin/colors.c
Outdated
| DIFFVIEWER_ERROR_COLOR = mc_skin_color_get ("diffviewer", "error"); | ||
|
|
||
| FILEHIGHLIGHT_DEFAULT_COLOR = mc_skin_color_get ("filehighlight", "_default_"); | ||
| mc_skin_one_color_init (CORE_DEFAULT_COLOR, "skin", "terminal_default_color"); |
There was a problem hiding this comment.
given how uniform this code is, i would recommend a data-driven approach here.
There was a problem hiding this comment.
If I understand correctly what you mean here then I agree, done.
lib/skin.h
Outdated
| #define FILEHIGHLIGHT_DEFAULT_COLOR mc_skin_color__cache[78] | ||
|
|
||
| #define MC_SKIN_COLOR_CACHE_COUNT 79 | ||
| #define COLOR_MAP_OFFSET 4096 |
There was a problem hiding this comment.
you could just put that inside the/an enum as well.
There was a problem hiding this comment.
Could you please be more specific? I can easily do this by "wasting" the first number in the enum, and correspondingly in the array of ints, but I'd prefer not to. Or the code would be fragile because elsewhere we'd need to know the symbolic name of the first entry and it'd break if someone changes the order.
There was a problem hiding this comment.
enum
{
COLOR_MAP_OFFSET = 4096,
/* Basic colors */
CORE_DEFAULT_COLOR = COLOR_MAP_OFFSET,
...
There was a problem hiding this comment.
I didn't know enums could have repeated values :) but Andrew's change is incompatible with your idea, so I guess we'll just leave his version.
lib/tty/color.h
Outdated
| */ | ||
| #define COLOR_MAP_OFFSET 4096 | ||
|
|
||
| enum |
There was a problem hiding this comment.
"there is no problem in cs that cannot be solved with another layer of indirection" ... 🤣
but more seriously, if you reframe a skin as the color palette, then it becomes obvious that it is intrinsically linked with color resolution. one could avoid that only by making the system way more dynamic, but that would also make it much heavier. so i'm not convinced that andrew's assertion holds up, but i haven't looked into it very deeply.
4677b61 to
d9203ed
Compare
Can't figure out how to reply to a comment on a file which is now outdated. (Yes, I know, we should switch to gerrit :)) Your comment makes sense, it might make sense to reposition this part of the source code as "palette" rather than "skin", but at the same time it is also a quite vague direction and I can't see either how the exact details should go, and for that reason preferably I wouldn't spend time on this right now. |
following the link in the notification mail takes you directly to it, where it's possible to reply as usual. but yeah, gerrit is so much better at this ... |
Click on the "Files changed" tab and then on the grey "Comments" button on the right next to the green "Submit review". |
45659bc to
a76a2e3
Compare
|
Thx guys! Just pushed the autosquashed version. I think it's fine to merge; right? Once merged, let's move on to #4976 which was waiting for this one. |
zyv
left a comment
There was a problem hiding this comment.
Fine with me, with one small comment...
lib/skin/colors.c
Outdated
| DIFFVIEWER_ERROR_COLOR = mc_skin_color_get ("diffviewer", "error"); | ||
|
|
||
| FILEHIGHLIGHT_DEFAULT_COLOR = mc_skin_color_get ("filehighlight", "_default_"); | ||
| for (unsigned int i = 0; i < G_N_ELEMENTS (color_keywords); i++) |
There was a problem hiding this comment.
Can unsigned int here be replaced with size_t? I'd stay away from old-style types if possible at all; they always only bring uncertainty, confusion, and portability problems.
There was a problem hiding this comment.
Can unsigned int here be replaced with size_t?
Done.
There was a problem hiding this comment.
"int" is optimized for the machine word size, and unless there is a good reason to actually consider values >4G (mc is 32+ bit only) or require a fixed word size for protocol reasons, using anything else than (unsigned) int is kinda counter-productive.
Variables referring to a color pair may now contain either the lowlevel color pair id, or an identifier of the color role, the latter to be looked up in a map according to the skin. By using the color role id for as long as we can, rather than the resolved color pair id, initializing colors and later changing skins becomes much more straightforward. Signed-off-by: Egmont Koblinger <egmont@gmail.com>
a76a2e3 to
a101d98
Compare
What would you guys think of a color handling refactoring along these lines?
The current code does the resolution from the role (e.g. "dialog's title color") to the ncurses/slang color pair id as soon as possible.
A significant drawback of this design is that initializing some color constant (e.g. the ones used for normal, error, listbox and help dialogs), or later adjusting these values upon a runtime skin change are pretty cumbersome. Either some locally used colors need to be defined in a much more global context (pointed out as unfortunate design twice during code review: #4916 (comment) and #4976 (comment)), or the code that switches the skin needs to dig deep down into the territory of individual widgets to change the color they use.
My idea is to instead carry the color role identifier for as long as possible, and only resolve to the actual color when it comes to painting. This way only the color cache needs to be updated from the new skin; widget's idea on which color (i.e. color role) to use remains the same.
We have those 80-ish uppercase variables like
CORE_NORMAL_COLORetc. Currently it's pretty counterintuitive that despite the uppercase naming, these are variables and do change at a skin change. Yet, for some reason, they aren't declared as individual variables but as members of an array, so we have to maintain their indexing and manually renumber every time a new item is added.With my suggestion, these become constants (actually enum values) as their name suggests, and we no longer have to manually index them.
Now, due to having two places in mc that bypass these variables, namely file highligthing and editor syntax highlighting, the story becomes a bit more complex. Sometimes we know the color role id, yet to be resolved to an ncurses/slang color pair id, but sometimes the latter one is the only thing we have.
Might not be the cleanest design per se, but I just distinguish these two based on how large the value is: if the value is low then it refers directly to the ncurses/slang color pair id; if the value is large (above the arbitrarily picked 4096) then it's a color role id which still needs a hop to resolve. This sort of pattern already exists in mc's file highlighting code where negative vs. positive values have different roles, although it looks to me it's legacy leftover because both are just color pair id, the negative ones will simply be flipped to positive (and, by the way, due to two faulty
ret > 0checks rather thanret >= 0, color pair id 0 (i.e. the terminal's default colors) can't be used for file highlighting; something that probably nobody cares about). I've ported this negative vs. positive split to the new ranges.No more conceptual
tty_setcolor()vs.tty_lowlevel_setcolor()separation (although it was probably a legacy anyway, they were identical, except for a& 0x7Fmask in slang that I don't understand), now by design there's a single method only which decides based on the value (small vs. large) how to handle it.As you can see, the initialization of
command_colorsetc. becomes compile-time rather than runtime, no need to reinitialize at skin change, and easy to move all of them where they really belong.I've moved
CORE_NORMAL_COLORetc. from the upper level skin layer to the lower level tty layer becausetty_setcolor()now needs to know about them, and I didn't want tty to know about skin. This is debatable where it should really go, what the new variables / types / etc. should be named, and so on, suggestions welcome.Let me know please how you like the overall direction.