Environment var resolution with Tab#410
Conversation
…into environmentVar * 'environmentVar' of https://github.com/lobotomy-x/reshade:
Environment var
fixes cases where one could type an envvar and see an empty folder until they resolved the file path
Removed environment variable expansion from path resolution.
crosire
left a comment
There was a problem hiding this comment.
Nice! I just have some stylistic comments =P:
source/runtime.cpp
Outdated
| {"SYSTEM32", g_reshade_base_path.stem().u8string()}, // You can of course still use these macros for stuff like fonts | ||
| {"SYSTEMDRIVE", g_reshade_base_path.stem().u8string()}, // realistically this shouldn't be a concern but the risk is a possible crash due to access rights | ||
| {"SYSTEMROOT", g_reshade_base_path.stem().u8string()}, | ||
| {"WINDIR", g_reshade_base_path.stem().u8string()}, |
There was a problem hiding this comment.
This seems unnecessary. If somebody specifies "%SYSTEM%" in their screenshot path, that's on them. I'd be more confused if that suddenly is redirected to the base path.
There won't be a crash, screenshot saving will simply fail with an error (also shown with a popup in the overlay).
source/imgui_widgets.cpp
Outdated
| } | ||
|
|
||
| std::vector<std::filesystem::path> file_entries; | ||
| std::error_code &ec1 = std::error_code(); |
There was a problem hiding this comment.
This creates a reference to a temporary object, which is invalid. Just use the existng ec, there is no need to create an additional instance here.
tools/archive_examples.py
Outdated
| from os.path import join, getcwd, chdir | ||
| chdir("..")) | ||
| make_archive(f'Examples32.zip', 'zip', join(getcwd(), "bin", "Win32", "Release Examples")) | ||
| make_archive(f'Examples64.zip', 'zip', join(getcwd(), "bin", "x64", "Release Examples")) |
There was a problem hiding this comment.
Looks like an unintended left-over from something else?
There was a problem hiding this comment.
oh yeah that was meant to be called by an action to build and upload the example addons so people who want them without building could grab automated builds. I think I deleted it because there was some oddity with actions on a fork so I'm not sure if it works. No clue why that was still in here, disregard
There was a problem hiding this comment.
That already exists btw., the example add-ons are built by Actions and uploaded here: https://github.com/crosire/reshade-docs/tags (this is how some of them are available via the ReShade setup).
source/runtime_gui.cpp
Outdated
| // This only works if the error occurs later in the path than the correct macro | ||
| // Doesn't communicate to the user that the error has occurred but it should be easy to see what happened | ||
| // And it does move the cursor position to the last real path | ||
| while (!std::filesystem::exists(resolved_path) && !resolved_path.empty() && resolved_path.u8string().find_last_of("\\") != std::string::npos) |
There was a problem hiding this comment.
Don't call std::filesystem::exists without the error code parameter, as it may throw an exception in case of a filesystem error, which will crash because ReShade is built without exceptions.
I'd actually argue that there is no need for existence checks here at all. Just allow macro expansion via tab completion always, regardless of whether the resulting path is valid or not.
There was a problem hiding this comment.
that's totally fine just a couple things to note. You can type out fake folders and the popup file browser will appear as though you're browsing an empty, real folder. Not a problem its just a bit odd. This was already present, but the existence check did address it. As well its worth mentioning that undo and redo will now shift the cursor to the last % sign or right after where it would be. Without patching ImGui's undo handling or modifying the imstb_textedit header it ships with I think the only reasonable way to resolve that would be to use callback always or callback on edit. Probably fine but just making note
source/runtime_gui.cpp
Outdated
| static auto is_macro_filename_element(ImGuiInputTextCallbackData *data) -> int | ||
| { | ||
| // A file name cannot contain any of the following characters | ||
| return is_invalid_path_element(data) || data->EventChar == L'/' || data->EventChar == L'\\'; |
There was a problem hiding this comment.
This is a duplicate of is_invalid_filename_element, but also unused. Maybe left-over from merging?
source/runtime.cpp
Outdated
| break; | ||
| } | ||
| // Allow using env vars alongside macros | ||
| else if (getenv(name.c_str()) != nullptr) |
There was a problem hiding this comment.
This will call getenv for every single defined macro, which is excessive. It would be better to change this to e.g. the following (also avoiding the extra macros empty check, since the loop is just skipped in that case anyway):
std::string value;
for (const std::pair<std::string, std::string> ¯o : macros)
{
if (_stricmp(name.c_str(), macro.first.c_str()) == 0)
{
value = macro.second;
break;
}
}
if (value.empty())
{
value = std::getenv(name.c_str());
}
source/imgui_widgets.cpp
Outdated
| path += std::filesystem::path::preferred_separator; | ||
|
|
||
|
|
||
| std::error_code &ec2 = std::error_code(); |
source/imgui_widgets.cpp
Outdated
| if (path.empty()) | ||
| path = L".\\"; | ||
| if (path.is_relative()) | ||
| const bool is_env_var = !path.empty() && path.generic_u8string()[0] == '%'; |
There was a problem hiding this comment.
Better to avoid the costly UTF-16 to UTF-8 conversion for this trivial check by doing !path.empty() && path[0] == L'%'.
source/runtime.cpp
Outdated
| } | ||
|
|
||
| // part of the screenshot macro functionality. needs to be with the other helpers or forward declared for correct access | ||
| std::string setup_macros(const std::string &input, std::vector<std::pair<std::string, std::string>> macros, std::chrono::system_clock::time_point now) |
There was a problem hiding this comment.
I'd just keep calling this expand_macro_string, making it an overload to the other one above.
source/runtime.cpp
Outdated
| _config_path(config_path), | ||
| _screenshot_path(L".\\"), | ||
| _screenshot_name("%AppName% %Date% %Time%_%TimeMS%"), // Include milliseconds by default because users may request more than one screenshot per second | ||
| _screenshot_name("%AppName% %Date% %Time%_%Count%"), // Use count rather than Ms because its more readable and meaningful |
There was a problem hiding this comment.
The new comment is confusing without knowning what this was before. I'd just adjust the old one slightly: // Include screenshot count by default because users may request more than one screenshot per second.
Removed the is_macro_filename_element and resolve_macros functions to simplify the code.
Refactor environment variable retrieval logic to prioritize macros and simplify the code structure.
Updated screenshot naming logic for clarity and uniqueness.
|
I believe that should be everything but of course let me know. I did also fix a long standing bug due to a missing ec in the file browser so its good you called that out |
source/imgui_widgets.cpp
Outdated
| if (ImGui::InputTextWithHint("##path", hint, buf, sizeof(buf), ImGuiInputTextFlags_EnterReturnsTrue| ImGuiInputTextFlags_CallbackCompletion, &resolve_macros)) | ||
| { | ||
| dialog_path = std::filesystem::u8path(buf); | ||
| std::error_code& ec = std::error_code(); |
There was a problem hiding this comment.
Just one more occurence of an invalid temporary to reference assignment ;)
There was a problem hiding this comment.
maybe I need to start building with warnings as errors...
Hi, this is one of a few features I added last year and have only just found some time to update and merge. This one is pretty small in scope but it does require either moving a few functions to the top of runtime/runtime_gui or adding them to the header. It can be considered as a continuation of the change pushed last year affecting screenshots.
The main functionality here comes from splitting apart and tweaking the macro expansion function to resolve environment variables enclosed in %, e.g. to save to the Pictures folder you would use %USERPROFILE%\Pictures.
WINDIR and a few other vars are not able to used in a screenshot path to avoid write access issues but you can do %WINDIR%\Fonts\Arial.ttf for instance.
This path resolution has been added to the existing resolve_path function meaning that any paths using environment vars will be handled without changing how they are displayed. Screenshot macros do not get resolved here.
While the under the hood handling is nice this feature also uses imgui's input text callbacks to add tab key environment var resolution. This works in any path input field or dialogue box. Some care has been taken to handle cases where nonexistent paths are typed after an environment var (var will resolve and the cursor will move to the last real directory).
Within one session you can of course ctrl-z to reverse the expansion, but there is no realistic method to reverse a path to a macro otherwise. Paths save as displayed so users can save paths as unexpanded macros to make their paths work for other people if sharing a config. I don't think this would affect paths inside .fx files unless that makes use of the same resolve_path function.
In general I think this is a change that should be pretty intuitive. Probably just a patch note stating something like
"enabled tab key autocomplete for environment vars wrapped in %"
Please feel free to make or request any changes you feel are necessary