scummvm/backends/platform/sdl/macosx/macosx.cpp
Eugene Sandulenko e844981cd0 BACKENDS: MACOS: Query number of MIDI devices at the start of ScummVM in a thread
The call to MIDIGetNumberOfDestinations() starts the MIDI server which takes
3-4l seconds even on the modern hardware. We are querying this in the GUI Options
which then lags on the first launch.

Thus, we are creating a separate thread and make this call at the start of
ScummVM, so by the time the user gets to the GUI, the server is already launched
and there is no lag.

Of course, that would mean that we will launch MIDI even for the games which are
not using it, so this is a tradeoff for the better UX.
2021-03-29 20:25:37 +02:00

270 lines
8.5 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "common/scummsys.h"
#ifdef MACOSX
#include "backends/audiocd/macosx/macosx-audiocd.h"
#include "backends/platform/sdl/macosx/appmenu_osx.h"
#include "backends/platform/sdl/macosx/macosx.h"
#include "backends/updates/macosx/macosx-updates.h"
#include "backends/taskbar/macosx/macosx-taskbar.h"
#include "backends/text-to-speech/macosx/macosx-text-to-speech.h"
#include "backends/dialogs/macosx/macosx-dialogs.h"
#include "backends/platform/sdl/macosx/macosx_wrapper.h"
#include "backends/fs/posix/posix-fs.h"
#include "common/archive.h"
#include "common/config-manager.h"
#include "common/fs.h"
#include "common/translation.h"
#include "ApplicationServices/ApplicationServices.h" // for LSOpenFSRef
#include "CoreFoundation/CoreFoundation.h" // for CF* stuff
// For querying number of MIDI devices
#include <pthread.h>
#include <CoreMIDI/CoreMIDI.h>
void *coreMIDIthread(void *threadarg) {
(void)MIDIGetNumberOfDestinations();
pthread_exit(NULL);
return NULL;
}
OSystem_MacOSX::~OSystem_MacOSX() {
releaseMenu();
}
void OSystem_MacOSX::init() {
// Use an iconless window on OS X, as we use a nicer external icon there.
initSDL();
_window = new SdlIconlessWindow();
#if defined(USE_TASKBAR)
// Initialize taskbar manager
_taskbarManager = new MacOSXTaskbarManager();
#endif
#if defined(USE_SYSDIALOGS)
// Initialize dialog manager
_dialogManager = new MacOSXDialogManager();
#endif
// The call to query the number of MIDI devices is ubiquitously slow
// on the first run. This is apparent when opening Options in GUI,
// which takes 2-3 secs.
//
// Thus, we are launching it now, in a separate thread, so
// the subsequent calls are instantaneous
pthread_t thread;
pthread_create(&thread, NULL, coreMIDIthread, NULL);
// Invoke parent implementation of this method
OSystem_POSIX::init();
}
void OSystem_MacOSX::initBackend() {
#ifdef USE_TRANSLATION
// We need to initialize the translataion manager here for the following
// call to replaceApplicationMenuItems() work correctly
TransMan.setLanguage(ConfMan.get("gui_language").c_str());
#endif // USE_TRANSLATION
// Replace the SDL generated menu items with our own translated ones on Mac OS X
replaceApplicationMenuItems();
#ifdef USE_SPARKLE
// Initialize updates manager
_updateManager = new MacOSXUpdateManager();
#endif
#ifdef USE_TTS
// Initialize Text to Speech manager
_textToSpeechManager = new MacOSXTextToSpeechManager();
#endif
// Invoke parent implementation of this method
OSystem_POSIX::initBackend();
}
void OSystem_MacOSX::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
// Invoke parent implementation of this method
OSystem_POSIX::addSysArchivesToSearchSet(s, priority);
// Get URL of the Resource directory of the .app bundle
CFURLRef fileUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
if (fileUrl) {
// Try to convert the URL to an absolute path
UInt8 buf[MAXPATHLEN];
if (CFURLGetFileSystemRepresentation(fileUrl, true, buf, sizeof(buf))) {
// Success: Add it to the search path
Common::String bundlePath((const char *)buf);
// Search with a depth of 2 so the shaders are found
s.add("__OSX_BUNDLE__", new Common::FSDirectory(bundlePath, 2), priority);
}
CFRelease(fileUrl);
}
}
bool OSystem_MacOSX::hasFeature(Feature f) {
if (f == kFeatureDisplayLogFile || f == kFeatureClipboardSupport || f == kFeatureOpenUrl)
return true;
#ifdef USE_SYSDIALOGS
if (f == kFeatureSystemBrowserDialog)
return true;
#endif
return OSystem_POSIX::hasFeature(f);
}
bool OSystem_MacOSX::displayLogFile() {
// Use LaunchServices to open the log file, if possible.
if (_logFilePath.empty())
return false;
CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)_logFilePath.c_str(), _logFilePath.size(), false);
OSStatus err = LSOpenCFURLRef(url, NULL);
CFRelease(url);
return err != noErr;
}
bool OSystem_MacOSX::hasTextInClipboard() {
return hasTextInClipboardMacOSX();
}
Common::U32String OSystem_MacOSX::getTextFromClipboard() {
return getTextFromClipboardMacOSX();
}
bool OSystem_MacOSX::setTextInClipboard(const Common::U32String &text) {
return setTextInClipboardMacOSX(text);
}
bool OSystem_MacOSX::openUrl(const Common::String &url) {
CFURLRef urlRef = CFURLCreateWithBytes (NULL, (const UInt8*)url.c_str(), url.size(), kCFStringEncodingASCII, NULL);
OSStatus err = LSOpenCFURLRef(urlRef, NULL);
CFRelease(urlRef);
return err == noErr;
}
Common::String OSystem_MacOSX::getSystemLanguage() const {
#if defined(USE_DETECTLANG) && defined(USE_TRANSLATION)
CFArrayRef availableLocalizations = CFBundleCopyBundleLocalizations(CFBundleGetMainBundle());
if (availableLocalizations) {
CFArrayRef preferredLocalizations = CFBundleCopyPreferredLocalizationsFromArray(availableLocalizations);
CFRelease(availableLocalizations);
if (preferredLocalizations) {
CFIndex localizationsSize = CFArrayGetCount(preferredLocalizations);
// Since we have a list of sorted preferred localization, I would like here to
// check that they are supported by the TranslationManager and take the first
// one that is supported. The listed localizations are taken from the Bundle
// plist file, so they should all be supported, unless the plist file is not
// synchronized with the translations.dat file. So this is not really a big
// issue. And because getSystemLanguage() is called from the constructor of
// TranslationManager (therefore before the instance pointer is set), calling
// TransMan here results in an infinite loop and creation of a lot of TransMan
// instances.
/*
for (CFIndex i = 0 ; i < localizationsSize ; ++i) {
CFStringRef language = (CFStringRef)CFArrayGetValueAtIndex(preferredLocalizations, i);
char buffer[10];
CFStringGetCString(language, buffer, sizeof(buffer), kCFStringEncodingASCII);
int32 languageId = TransMan.findMatchingLanguage(buffer);
if (languageId != -1) {
CFRelease(preferredLocalizations);
return TransMan.getLangById(languageId);
}
}
*/
if (localizationsSize > 0) {
CFStringRef language = (CFStringRef)CFArrayGetValueAtIndex(preferredLocalizations, 0);
char buffer[10];
CFStringGetCString(language, buffer, sizeof(buffer), kCFStringEncodingASCII);
CFRelease(preferredLocalizations);
return buffer;
}
CFRelease(preferredLocalizations);
}
}
// Falback to POSIX implementation
return OSystem_POSIX::getSystemLanguage();
#else // USE_DETECTLANG
return OSystem_POSIX::getSystemLanguage();
#endif // USE_DETECTLANG
}
Common::String OSystem_MacOSX::getDefaultConfigFileName() {
const Common::String baseConfigName = "Library/Preferences/ScummVM Preferences";
Common::String configFile;
Common::String prefix = getenv("HOME");
if (!prefix.empty() && (prefix.size() + 1 + baseConfigName.size()) < MAXPATHLEN) {
configFile = prefix;
configFile += '/';
configFile += baseConfigName;
} else {
configFile = baseConfigName;
}
return configFile;
}
Common::String OSystem_MacOSX::getDefaultLogFileName() {
const char *prefix = getenv("HOME");
if (prefix == nullptr) {
return Common::String();
}
if (!Posix::assureDirectoryExists("Library/Logs", prefix)) {
return Common::String();
}
return Common::String(prefix) + "/Library/Logs/scummvm.log";
}
Common::String OSystem_MacOSX::getScreenshotsPath() {
Common::String path = ConfMan.get("screenshotpath");
if (path.empty())
path = getDesktopPathMacOSX();
if (!path.empty() && !path.hasSuffix("/"))
path += "/";
return path;
}
AudioCDManager *OSystem_MacOSX::createAudioCDManager() {
return createMacOSXAudioCDManager();
}
#endif