-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix some encoding issues (mostly on Windows) #4401
Conversation
I replicated issue #2662 on LinuxMint/Qt4 . It's indeed fixed by this PR. Ready to merge? |
plugins/MidiImport/MidiImport.cpp
Outdated
if (!file.open(QIODevice::ReadOnly)) | ||
{ | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like you're closing and then reopening the file. Is it possible to just not close the file to begin with, and then replace QByteArray arr = file.readAll();
with QByteArray arr = file().readAll();
further down?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote like this because of the const-ness of file()
. I'll add a method to work around.
plugins/vst_base/RemoteVstPlugin.cpp
Outdated
static std::wstring toWString(std::string s) | ||
{ | ||
/* | ||
QString qstr = QString::fromUtf8(s.data(), int(s.size())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see this is commented out, so I'm guessing it doesn't work. Did you try
return qstr.toStdWString();
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beacause it requires including Qt headers(on Windows). If that don't matter (we already do that), I can switch to QString is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. @lukas-w has offered me advice on where it's okay to use Qt stuff in the past. I think things like QString, at least, are fine everywhere, but hopefully he'll chime in here to confirm that this is OK if he's not too busy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be more clear, RemoteVstPlugin
use Qt libraries for IPC on Windows. So I think that would be fine.
Edit: Linux build also needs the function, too. That's why I didn't use Qt function here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PhysSong is right, this is not a good place to use Qt because that would add MinGW Qt as a dependency on Linux builds. We can however use C++11 functions to convert to UTF-16:
#include <locale>
#include <codecvt>
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(s);
This does the same thing, just in fewer lines. I think we already use C++11 for RemoteVstPlugin
on stable-1.2
, but I didn't check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because that would add MinGW Qt as a dependency on Linux builds.
It seems like we can use host version Qt, but I don't think it's a good idea because it adds 32bit Qt as a dependency on 64bit Linux.
We can however use C++11 functions to convert to UTF-16:
That doesn't work with our old MinGW because of missing header files(it should work with newest g++-mingw-w64
). Those functions are deprecated in C++17, too.
plugins/vst_base/RemoteVstPlugin.cpp
Outdated
#define F_OPEN(a, b) _wfopen(toWString(a).c_str(), L##b) | ||
#else | ||
#define F_OPEN(a, b) fopen((a).c_str(), b) | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see you added these F_OPEN
defines, but they don't seem to be used anywhere (?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot to commit that. Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed.
plugins/vst_base/RemoteVstPlugin.cpp
Outdated
@@ -2083,7 +2106,7 @@ int main( int _argc, char * * _argv ) | |||
} | |||
|
|||
// constructor automatically will process messages until it receives | |||
// a IdVstLoadPlugin message and processes it | |||
// a IdVstLoadPlugin message and processes it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ No need to change the indentation here. It was fine before :)
#else | ||
int fd = dup(f.handle()); | ||
#endif | ||
f.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see you're using this to open files by their descriptor instead of the filename. Is it gzopen
that's giving you problems, fopen
, or both? Because if it's only gzopen
that's broken, it might be possible to just always fopen
, and then call gzdopen(fileno(file), options)
if compression is enabled, rather than doing this trickery with duplicating the file handles and such.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both are broken, because C library only supports characters in local code page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, oops. I had typed out that comment and after digging deeper to better understand the problem I meant to remove it before posting the review >.<
Yeah, I agree with your conclusion here. It's not pretty, but I don't see any better solution.
#else | ||
int fd = dup(f.handle()); | ||
#endif | ||
f.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if you could offload this logic to a helper function,
static int fdopenFromQString(QString filename, QIODevice::OpenMode mode)
to avoid duplicating the body in multiple functins, and then leave a comment near that function explaining why the function is necessary (to most readers, this code will look silly and they may be tempted to simplify it back to the way it was before).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestion!
src/core/DrumSynth.cpp
Outdated
QFile f(file); | ||
f.open(QIODevice::ReadOnly); | ||
QByteArray dat = f.readAll().constData(); | ||
is.str(string(dat.constData(), dat.size())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The casual reader will see this and think "we're reading the entire file into a string, and then operating on it only as a stream? Why not directly open the file with ifstream
?" and will be tempted to change this back to how it was previously. I think there should be some [short] comment explaining why we must open the file with QFile
, possibly linking back to this github PR.
src/core/SampleBuffer.cpp
Outdated
@@ -416,7 +412,10 @@ f_cnt_t SampleBuffer::decodeSampleSF( const char * _f, | |||
f_cnt_t frames = 0; | |||
bool sf_rr = false; | |||
|
|||
if( ( snd_file = sf_open( _f, SFM_READ, &sf_info ) ) != NULL ) | |||
|
|||
QFile f(_f); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking these should all be commented with something along the lines of
// Open the file with QFile and pass the descriptor to sf_open_fd instead of directly calling sf_open
// for better filename encoding support. See https://github.com/LMMS/lmms/pull/4401
so that people know not to revert this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
6cd2d26
to
1f09c05
Compare
@PhysSong Thanks for the improvements! I'm marking this as "approved" now, since you've addressed any concerns I had. But it looks like Lukas wanted to review this too, so you should probably wait until that before merging 🎉 |
plugins/stk/mallets/mallets.cpp
Outdated
#else | ||
.toUtf8().constData() ); | ||
#endif | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can (should?) use toLocal8Bit
on Unix as well. It should be equivalent to toUtf8
on most Linux and macOS systems. Correct me if I'm wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think that would be simpler. AFAIK that wouldn't break anything, too.
plugins/vst_base/RemoteVstPlugin.cpp
Outdated
static std::wstring toWString(std::string s) | ||
{ | ||
/* | ||
QString qstr = QString::fromUtf8(s.data(), int(s.size())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PhysSong is right, this is not a good place to use Qt because that would add MinGW Qt as a dependency on Linux builds. We can however use C++11 functions to convert to UTF-16:
#include <locale>
#include <codecvt>
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(s);
This does the same thing, just in fewer lines. I think we already use C++11 for RemoteVstPlugin
on stable-1.2
, but I didn't check.
@@ -193,9 +216,11 @@ int QtXmlWrapper::dosavefile(const char *filename, | |||
int compression, | |||
const char *xmldata) const | |||
{ | |||
int fd = fdopenFromUtf8(filename, QIODevice::WriteOnly); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use the same method here that's used in RemoteVstPlugin.cpp
? We can move the toWString
method and the F_OPEN
defines to a common header and include that in both files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think F_OPEN
macro is an ugly workaround for fopen
. I can do that if needed, but I don't like the macro approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PhysSong You can make it a function instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something tricky about that: "r"
vs L"r"
or something like that. That's why I chose macro approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PhysSong Sounds like you need to convert the mode string as well. So something like this:
std::FILE* openFile(std::string filename, std::string mode)
{
#ifdef LMMS_BUILD_WIN32
return _wfopen(toWString(filename), toWString(mode));
#else
return fopen(filename.data(), mode.data());
#endif
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could, but I don't think there's a need for it, because they're just a couple lines each and operate on an UTF-16 QString
instead of an UTF-8 std::string
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think either lmms_io.h
or IoHelper.h
will be fine. I'll address this point soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or how about putting them in RemotePlugin.h
? I can't use-cases of UTF-8 string filename except in remote plugins.
Edit: It doesn't look like good because Zyn core needs it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it fine for our Zyn to include LMMS' headers at all? I think this will be fine in stable-1.2
, but I'm not sure about master
's submodule. It would be the last concern for me...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it doesn't make sense to build https://github.com/LMMS/zynaddsubfx as a standalone, of course. However, I think it's possible to check if it's building with LMMS or not, though not really needed.
@lukas-w I addressed these issues and pushed to a separate branch. I'll push this change to the original branch when/if you says okay. |
Oops, accidentally pushed them. I can roll back to original commits whenever needed. |
@PhysSong Please roll back to the original commits, all the comment/review history on GitHub is lost otherwise. It's okay to have one extra commit on top of the original ones, there's no need to rewrite history. |
What do you mean? Review comments are still there unless you commented on specific commits, the are just collapsed. Anyway, I agree that it's not necessary to rewrite history. I just wanted to keep things cleaner, but that was all. If you're still unhappy with this force-push, I can roll back. |
Sorry, I was being imprecise. The comments are still there, but the context is lost. Review comments are always on specific commits and you can't view that commits any more, so you can't see the changes the review refers to. Try clicking on the "View changes" button next to a review, this is what you'll get now:
Yes, please do 👍 |
I just noticed even a rebasing can cause such a thing. I just accidentally rebased commits because I didn't know that... I'll revert to the revision before rebasing if needed. |
BTW @lukas-w, do you want any more changes in this pull request? |
@PhysSong Apart from restoring the pre-force-push commits, nope. |
Then do you mind if I merge this(probably with cleaner history)? |
I don't mind, go ahead ❤️ |
Fix plugin loading and setting loading/saving
Still incomplete on Windows due to an upstream issue
Just found a very rare encoding issues when using temporary directory whose name contains some non-ASCII characters on Linux. In that case VST plugin can't be loaded. |
int fd = open( _file.c_str(), O_WRONLY | O_BINARY ); | ||
if ( ::write( fd, chunk, len ) != len ) | ||
FILE* fp = F_OPEN_UTF8( _file, "wb" ); | ||
if ( fwrite( chunk, len, 1, fp ) != len ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only spotted this now: fwrite
returns the count
of elements successfully written, so in this case it will return 1
on success, not len
. Therefore this condition is always leading to a false error message on success (except when coincidentally len == 1
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! Changed to fwrite( chunk, 1, len, fp )
in 0f3b41f.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also synced into master
via 170a46e.
This pull request fixes several encoding issue. Every issue except STK one are Windows-specific.
Issues remaining on Windows:
toLocal8Bit
fails to handle)I'm planning to track remaining issues in #1191.
Fixes #3855, Fixes #2662, and almost #1191.