Adds Support for Command-Line on Windows (#3699)

* + cli support on windows

- removes the preprocessor macro that prevented arg
  parsing on Windows
- adds windows-cli.cpp as src for a wrapper exe
- adds flameshot-cli target into cmake when building
  on Windows
- updates README

* updates PR

+ support for unicode characters in cli args
+ additional clarification in README re: flameshot-cli
+ new workaround for spaces in _popen path; works with relative output paths

* fix EOL

* updated flameshot.exe path construction

avoids using the hard-coded length of flameshot-cli.exe

* fix whitespace (clang)
This commit is contained in:
Nicholas Morris
2025-05-31 12:39:02 -05:00
committed by GitHub
parent 134238b8eb
commit eefefe187a
4 changed files with 88 additions and 2 deletions

View File

@@ -153,6 +153,14 @@ A systray icon will be in your system's panel while Flameshot is running.
Do a right click on the tray icon and you'll see some menu items to open the configuration window and the information window.
Check out the About window to see all available shortcuts in the graphical capture mode.
### Usage on Windows
On Windows, `flameshot.exe` will behave as expected for all supported command-line arguments,
but it will not output any text to the console. This is problematic if, for example, you are
running `flameshot.exe -h`.
If you require console output, run `flameshot-cli.exe` instead. `flameshot-cli.exe` is a minimal wrapper around `flameshot.exe` that ensures all stdout is captured and output to the console.
### CLI configuration
You can use the graphical menu to configure Flameshot, but alternatively you can use your terminal or scripts to do so.

View File

@@ -73,6 +73,13 @@ if(FLAMESHOT_ICON)
endif()
if (WIN32)
add_executable(flameshot-cli)
target_sources(
flameshot-cli
PRIVATE
windows-cli.cpp)
set_target_properties(flameshot-cli PROPERTIES OUTPUT_NAME "flameshot-cli")
target_link_options(flameshot-cli PRIVATE /SUBSYSTEM:CONSOLE)
set_property(TARGET flameshot PROPERTY WIN32_EXECUTABLE true)
if (MSVC)
target_compile_options(flameshot PRIVATE /source-charset:utf-8)
@@ -322,6 +329,10 @@ install(TARGETS flameshot
EXPORT flameshot-targets
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
if (WIN32)
install(TARGETS flameshot-cli
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif ()
if (UNIX)
# Install desktop files, completion and dbus files

View File

@@ -184,7 +184,6 @@ int main(int argc, char* argv[])
return qApp->exec();
}
#if !defined(Q_OS_WIN)
/*--------------|
* CLI parsing |
* ------------*/
@@ -609,6 +608,5 @@ int main(int argc, char* argv[])
}
finish:
#endif
return 0;
}

69
src/windows-cli.cpp Normal file
View File

@@ -0,0 +1,69 @@
#include <iostream>
#include <windows.h>
std::wstring joinArgs(int argc, wchar_t* argv[])
{
std::wstring result;
for (int i = 1; i < argc; ++i) {
if (i > 1) {
result += L" ";
}
result += argv[i];
}
return result;
}
void CallFlameshot(const std::wstring args, bool wait)
{
// generate full path for flameshot executable
wchar_t path[MAX_PATH];
int pathLength = GetModuleFileNameW(NULL, path, MAX_PATH);
std::wstring pathstring(path);
// Find the last backslash to isolate the filename
size_t lastBackslash = pathstring.find_last_of(L'\\');
std::wstring directory = (lastBackslash != std::wstring::npos)
? pathstring.substr(0, lastBackslash + 1)
: L"";
// generate command string
// note: binary path placed within quotes in case of spaces in path
int cmdSize = 32 + sizeof(directory) + sizeof(args);
wchar_t* cmd = (wchar_t*)malloc(sizeof(wchar_t) * cmdSize);
swprintf(cmd,
cmdSize,
L"\"%s\\flameshot.exe\" %s",
directory.c_str(),
args.c_str());
// call subprocess
FILE* stream = _wpopen(cmd, L"r");
free(cmd);
if (wait) {
if (stream) {
const int MAX_BUFFER = 2048;
char buffer[MAX_BUFFER];
while (!feof(stream)) {
if (fgets(buffer, MAX_BUFFER, stream) != NULL) {
std::cout << buffer;
}
}
}
_pclose(stream);
}
return;
}
// Console 'wrapper' for flameshot on windows
int wmain(int argc, wchar_t* argv[])
{
// if no args, do not wait for stdout
if (argc == 1) {
std::cout << "Starting flameshot in daemon mode" << std::endl;
CallFlameshot(L"", false);
} else {
std::wstring argString = joinArgs(argc, argv);
CallFlameshot(argString, true);
}
std::cout.flush();
return 0;
}