CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Qt/QtMain.cpp
Views: 1401
/*1* Copyright (c) 2012 Sacha Refshauge2*3*/4// Qt 4.7+ / 5.0+ implementation of the framework.5// Currently supports: Android, Linux, Windows, Mac OSX67#include "ppsspp_config.h"8#include <QApplication>9#include <QClipboard>10#include <QDesktopWidget>11#include <QDesktopServices>12#include <QDir>13#include <QFile>14#include <QFileDialog>15#include <QLocale>16#include <QScreen>17#include <QThread>18#include <QUrl>1920#include "ext/glslang/glslang/Public/ShaderLang.h"2122#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)23#include <QStandardPaths>24#ifdef QT_HAS_SYSTEMINFO25#include <QScreenSaver>26#endif27#endif2829#ifdef SDL30#include "SDL/SDLJoystick.h"31#include "SDL_audio.h"32#include "SDL_keyboard.h"33#endif3435#include "Common/System/NativeApp.h"36#include "Common/System/Request.h"37#include "Common/GPU/OpenGL/GLFeatures.h"38#include "Common/Math/math_util.h"39#include "Common/Profiler/Profiler.h"4041#include "QtMain.h"42#include "Qt/mainwindow.h"43#include "Common/Data/Text/I18n.h"44#include "Common/Thread/ThreadUtil.h"45#include "Common/Data/Encoding/Utf8.h"46#include "Common/StringUtils.h"47#include "Common/TimeUtil.h"4849#include "Core/Config.h"50#include "Core/ConfigValues.h"51#include "Core/HW/Camera.h"52#include "Core/Debugger/SymbolMap.h"5354#include <signal.h>55#include <string.h>5657// AUDIO58#define AUDIO_FREQ 4410059#define AUDIO_CHANNELS 260#define AUDIO_SAMPLES 204861#define AUDIO_SAMPLESIZE 1662#define AUDIO_BUFFERS 56364MainUI *emugl = nullptr;65static float refreshRate = 60.f;66static int browseFileEvent = -1;67static int browseFolderEvent = -1;68static int inputBoxEvent = -1;6970QTCamera *qtcamera = nullptr;71MainWindow *g_mainWindow;7273#ifdef SDL74SDL_AudioSpec g_retFmt;7576static SDL_AudioDeviceID audioDev = 0;7778extern void mixaudio(void *userdata, Uint8 *stream, int len) {79NativeMix((short *)stream, len / 4, AUDIO_FREQ);80}8182static void InitSDLAudioDevice() {83SDL_AudioSpec fmt;84memset(&fmt, 0, sizeof(fmt));85fmt.freq = 44100;86fmt.format = AUDIO_S16;87fmt.channels = 2;88fmt.samples = 256;89fmt.callback = &mixaudio;90fmt.userdata = nullptr;9192audioDev = 0;93if (!g_Config.sAudioDevice.empty()) {94audioDev = SDL_OpenAudioDevice(g_Config.sAudioDevice.c_str(), 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);95if (audioDev <= 0) {96WARN_LOG(Log::Audio, "Failed to open preferred audio device %s", g_Config.sAudioDevice.c_str());97}98}99if (audioDev <= 0) {100audioDev = SDL_OpenAudioDevice(nullptr, 0, &fmt, &g_retFmt, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);101}102if (audioDev <= 0) {103ERROR_LOG(Log::Audio, "Failed to open audio: %s", SDL_GetError());104} else {105if (g_retFmt.samples != fmt.samples) // Notify, but still use it106ERROR_LOG(Log::Audio, "Output audio samples: %d (requested: %d)", g_retFmt.samples, fmt.samples);107if (g_retFmt.format != fmt.format || g_retFmt.channels != fmt.channels) {108ERROR_LOG(Log::Audio, "Sound buffer format does not match requested format.");109ERROR_LOG(Log::Audio, "Output audio freq: %d (requested: %d)", g_retFmt.freq, fmt.freq);110ERROR_LOG(Log::Audio, "Output audio format: %d (requested: %d)", g_retFmt.format, fmt.format);111ERROR_LOG(Log::Audio, "Output audio channels: %d (requested: %d)", g_retFmt.channels, fmt.channels);112ERROR_LOG(Log::Audio, "Provided output format does not match requirement, turning audio off");113SDL_CloseAudioDevice(audioDev);114}115SDL_PauseAudioDevice(audioDev, 0);116}117}118119static void StopSDLAudioDevice() {120if (audioDev > 0) {121SDL_PauseAudioDevice(audioDev, 1);122SDL_CloseAudioDevice(audioDev);123}124}125#endif126127std::string System_GetProperty(SystemProperty prop) {128switch (prop) {129case SYSPROP_NAME:130#if defined(__ANDROID__)131return "Qt:Android";132#elif defined(Q_OS_LINUX)133return "Qt:Linux";134#elif defined(_WIN32)135return "Qt:Windows";136#elif defined(Q_OS_MAC)137return "Qt:macOS";138#else139return "Qt";140#endif141case SYSPROP_LANGREGION:142return QLocale::system().name().toStdString();143case SYSPROP_CLIPBOARD_TEXT:144return QApplication::clipboard()->text().toStdString();145#if defined(SDL)146case SYSPROP_AUDIO_DEVICE_LIST:147{148std::string result;149for (int i = 0; i < SDL_GetNumAudioDevices(0); ++i) {150const char *name = SDL_GetAudioDeviceName(i, 0);151if (!name) {152continue;153}154155if (i == 0) {156result = name;157} else {158result.append(1, '\0');159result.append(name);160}161}162return result;163}164#endif165case SYSPROP_BUILD_VERSION:166return PPSSPP_GIT_VERSION;167default:168return "";169}170}171172std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {173std::vector<std::string> result;174switch (prop) {175case SYSPROP_TEMP_DIRS:176if (getenv("TMPDIR") && strlen(getenv("TMPDIR")) != 0)177result.push_back(getenv("TMPDIR"));178if (getenv("TMP") && strlen(getenv("TMP")) != 0)179result.push_back(getenv("TMP"));180if (getenv("TEMP") && strlen(getenv("TEMP")) != 0)181result.push_back(getenv("TEMP"));182return result;183184default:185return result;186}187}188189int64_t System_GetPropertyInt(SystemProperty prop) {190switch (prop) {191#if defined(SDL)192case SYSPROP_AUDIO_SAMPLE_RATE:193return g_retFmt.freq;194case SYSPROP_AUDIO_FRAMES_PER_BUFFER:195return g_retFmt.samples;196case SYSPROP_KEYBOARD_LAYOUT:197{198// TODO: Use Qt APIs for detecting this199char q, w, y;200q = SDL_GetKeyFromScancode(SDL_SCANCODE_Q);201w = SDL_GetKeyFromScancode(SDL_SCANCODE_W);202y = SDL_GetKeyFromScancode(SDL_SCANCODE_Y);203if (q == 'a' && w == 'z' && y == 'y')204return KEYBOARD_LAYOUT_AZERTY;205else if (q == 'q' && w == 'w' && y == 'z')206return KEYBOARD_LAYOUT_QWERTZ;207return KEYBOARD_LAYOUT_QWERTY;208}209#endif210case SYSPROP_DEVICE_TYPE:211#if defined(__ANDROID__)212return DEVICE_TYPE_MOBILE;213#elif defined(Q_OS_LINUX)214return DEVICE_TYPE_DESKTOP;215#elif defined(_WIN32)216return DEVICE_TYPE_DESKTOP;217#elif defined(Q_OS_MAC)218return DEVICE_TYPE_DESKTOP;219#else220return DEVICE_TYPE_DESKTOP;221#endif222case SYSPROP_DISPLAY_COUNT:223return QApplication::screens().size();224default:225return -1;226}227}228229float System_GetPropertyFloat(SystemProperty prop) {230switch (prop) {231case SYSPROP_DISPLAY_REFRESH_RATE:232return refreshRate;233case SYSPROP_DISPLAY_LOGICAL_DPI:234return QApplication::primaryScreen()->logicalDotsPerInch();235case SYSPROP_DISPLAY_DPI:236return QApplication::primaryScreen()->physicalDotsPerInch();237case SYSPROP_DISPLAY_SAFE_INSET_LEFT:238case SYSPROP_DISPLAY_SAFE_INSET_RIGHT:239case SYSPROP_DISPLAY_SAFE_INSET_TOP:240case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM:241return 0.0f;242default:243return -1;244}245}246247bool System_GetPropertyBool(SystemProperty prop) {248switch (prop) {249case SYSPROP_HAS_TEXT_CLIPBOARD:250return true;251case SYSPROP_HAS_BACK_BUTTON:252return true;253case SYSPROP_HAS_IMAGE_BROWSER:254case SYSPROP_HAS_FILE_BROWSER:255case SYSPROP_HAS_FOLDER_BROWSER:256case SYSPROP_HAS_OPEN_DIRECTORY:257case SYSPROP_HAS_TEXT_INPUT_DIALOG:258case SYSPROP_CAN_SHOW_FILE:259return true;260case SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR:261return true; // FileUtil.cpp: OpenFileInEditor262case SYSPROP_APP_GOLD:263#ifdef GOLD264return true;265#else266return false;267#endif268case SYSPROP_CAN_JIT:269return true;270case SYSPROP_HAS_KEYBOARD:271return true;272default:273return false;274}275}276277void System_Notify(SystemNotification notification) {278switch (notification) {279case SystemNotification::BOOT_DONE:280g_symbolMap->SortSymbols();281g_mainWindow->Notify(MainWindowMsg::BOOT_DONE);282break;283case SystemNotification::SYMBOL_MAP_UPDATED:284if (g_symbolMap)285g_symbolMap->SortSymbols();286break;287case SystemNotification::AUDIO_RESET_DEVICE:288#ifdef SDL289StopSDLAudioDevice();290InitSDLAudioDevice();291#endif292break;293default:294break;295}296}297298// TODO: Find a better version to pass parameters to HandleCustomEvent.299static std::string g_param1;300static std::string g_param2;301static int g_param3;302static int g_requestId;303304bool MainUI::HandleCustomEvent(QEvent *e) {305if (e->type() == browseFileEvent) {306BrowseFileType fileType = (BrowseFileType)g_param3;307const char *filter = "All files (*.*)";308switch (fileType) {309case BrowseFileType::BOOTABLE:310filter = "PSP ROMs (*.iso *.cso *.chd *.pbp *.elf *.zip *.ppdmp)";311break;312case BrowseFileType::IMAGE:313filter = "Pictures (*.jpg *.png)";314break;315case BrowseFileType::INI:316filter = "INI files (*.ini)";317break;318case BrowseFileType::DB:319filter = "DB files (*.db)";320break;321case BrowseFileType::SOUND_EFFECT:322filter = "WAVE files (*.wav *.mp3)";323break;324case BrowseFileType::ZIP:325filter = "ZIP files (*.zip)";326break;327case BrowseFileType::ANY:328break;329}330331QString fileName = QFileDialog::getOpenFileName(nullptr, g_param1.c_str(), g_Config.currentDirectory.c_str(), filter);332if (QFile::exists(fileName)) {333g_requestManager.PostSystemSuccess(g_requestId, fileName.toStdString().c_str());334} else {335g_requestManager.PostSystemFailure(g_requestId);336}337} else if (e->type() == browseFolderEvent) {338QString title = QString::fromStdString(g_param1);339QString fileName = QFileDialog::getExistingDirectory(nullptr, title, g_Config.currentDirectory.c_str());340if (QDir(fileName).exists()) {341g_requestManager.PostSystemSuccess(g_requestId, fileName.toStdString().c_str());342} else {343g_requestManager.PostSystemFailure(g_requestId);344}345} else if (e->type() == inputBoxEvent) {346QString title = QString::fromStdString(g_param1);347QString defaultValue = QString::fromStdString(g_param2);348QString text = emugl->InputBoxGetQString(title, defaultValue);349if (text.isEmpty()) {350g_requestManager.PostSystemFailure(g_requestId);351} else {352g_requestManager.PostSystemSuccess(g_requestId, text.toStdString().c_str());353}354} else {355return false;356}357return true;358}359360bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) {361switch (type) {362case SystemRequestType::EXIT_APP:363qApp->exit(0);364return true;365case SystemRequestType::RESTART_APP:366// Should find a way to properly restart the app.367qApp->exit(0);368return true;369case SystemRequestType::COPY_TO_CLIPBOARD:370QApplication::clipboard()->setText(param1.c_str());371return true;372case SystemRequestType::SET_WINDOW_TITLE:373{374std::string title = std::string("PPSSPP ") + PPSSPP_GIT_VERSION;375if (!param1.empty())376title += std::string(" - ") + param1;377#ifdef _DEBUG378title += " (debug)";379#endif380g_mainWindow->SetWindowTitleAsync(title);381return true;382}383case SystemRequestType::INPUT_TEXT_MODAL:384{385g_requestId = requestId;386g_param1 = param1;387g_param2 = param2;388g_param3 = param3;389QCoreApplication::postEvent(emugl, new QEvent((QEvent::Type)inputBoxEvent));390return true;391}392case SystemRequestType::BROWSE_FOR_IMAGE:393// Fall back to file browser.394return System_MakeRequest(SystemRequestType::BROWSE_FOR_FILE, requestId, param1, param2, (int)BrowseFileType::IMAGE, 0);395case SystemRequestType::BROWSE_FOR_FILE:396g_requestId = requestId;397g_param1 = param1;398g_param2 = param2;399g_param3 = param3;400QCoreApplication::postEvent(emugl, new QEvent((QEvent::Type)browseFileEvent));401return true;402case SystemRequestType::BROWSE_FOR_FOLDER:403g_requestId = requestId;404g_param1 = param1;405g_param2 = param2;406QCoreApplication::postEvent(emugl, new QEvent((QEvent::Type)browseFolderEvent));407return true;408case SystemRequestType::CAMERA_COMMAND:409if (!strncmp(param1.c_str(), "startVideo", 10)) {410int width = 0, height = 0;411sscanf(param1.c_str(), "startVideo_%dx%d", &width, &height);412emit(qtcamera->onStartCamera(width, height));413} else if (param1 == "stopVideo") {414emit(qtcamera->onStopCamera());415}416return true;417case SystemRequestType::SHOW_FILE_IN_FOLDER:418QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromUtf8(param1.c_str())));419return true;420default:421return false;422}423}424425void System_Toast(std::string_view text) {}426427void System_AskForPermission(SystemPermission permission) {}428PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; }429430void System_Vibrate(int length_ms) {431if (length_ms == -1 || length_ms == -3)432length_ms = 50;433else if (length_ms == -2)434length_ms = 25;435}436437void System_LaunchUrl(LaunchUrlType urlType, const char *url)438{439QDesktopServices::openUrl(QUrl(url));440}441442static int mainInternal(QApplication &a) {443#ifdef MOBILE_DEVICE444emugl = new MainUI();445emugl->resize(g_display.pixel_xres, g_display.pixel_yres);446emugl->showFullScreen();447#endif448EnableFZ();449// Disable screensaver450#if defined(QT_HAS_SYSTEMINFO)451QScreenSaver ssObject(emugl);452ssObject.setScreenSaverEnabled(false);453#endif454455#ifdef SDL456SDLJoystick joy(true);457joy.registerEventHandler();458SDL_Init(SDL_INIT_AUDIO);459InitSDLAudioDevice();460#else461QScopedPointer<MainAudio> audio(new MainAudio());462audio->run();463#endif464465browseFileEvent = QEvent::registerEventType();466browseFolderEvent = QEvent::registerEventType();467inputBoxEvent = QEvent::registerEventType();468469int retval = a.exec();470delete emugl;471return retval;472}473474void MainUI::EmuThreadFunc() {475SetCurrentThreadName("Emu");476477// There's no real requirement that NativeInit happen on this thread, though it can't hurt...478// We just call the update/render loop here. NativeInitGraphics should be here though.479NativeInitGraphics(graphicsContext);480481emuThreadState = (int)EmuThreadState::RUNNING;482while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) {483updateAccelerometer();484UpdateRunLoop(graphicsContext);485}486emuThreadState = (int)EmuThreadState::STOPPED;487488NativeShutdownGraphics();489graphicsContext->StopThread();490}491492void MainUI::EmuThreadStart() {493emuThreadState = (int)EmuThreadState::START_REQUESTED;494emuThread = std::thread([&]() { this->EmuThreadFunc(); } );495}496497void MainUI::EmuThreadStop() {498emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;499}500501void MainUI::EmuThreadJoin() {502emuThread.join();503emuThread = std::thread();504}505506MainUI::MainUI(QWidget *parent)507: QGLWidget(parent) {508emuThreadState = (int)EmuThreadState::DISABLED;509setAttribute(Qt::WA_AcceptTouchEvents);510#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)511setAttribute(Qt::WA_LockLandscapeOrientation);512#endif513#if defined(MOBILE_DEVICE)514acc = new QAccelerometer(this);515acc->start();516#endif517setFocus();518setFocusPolicy(Qt::StrongFocus);519startTimer(16);520}521522MainUI::~MainUI() {523INFO_LOG(Log::System, "MainUI::Destructor");524if (emuThreadState != (int)EmuThreadState::DISABLED) {525INFO_LOG(Log::System, "EmuThreadStop");526EmuThreadStop();527while (graphicsContext->ThreadFrame()) {528// Need to keep eating frames to allow the EmuThread to exit correctly.529continue;530}531EmuThreadJoin();532}533#if defined(MOBILE_DEVICE)534delete acc;535#endif536graphicsContext->Shutdown();537delete graphicsContext;538graphicsContext = nullptr;539}540541QString MainUI::InputBoxGetQString(QString title, QString defaultValue) {542bool ok;543QString text = QInputDialog::getText(this, title, title, QLineEdit::Normal, defaultValue, &ok);544if (!ok)545text = QString();546return text;547}548549void MainUI::resizeGL(int w, int h) {550if (UpdateScreenScale(w, h)) {551System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);552}553xscale = w / this->width();554yscale = h / this->height();555556PSP_CoreParameter().pixelWidth = g_display.pixel_xres;557PSP_CoreParameter().pixelHeight = g_display.pixel_yres;558}559560void MainUI::timerEvent(QTimerEvent *) {561updateGL();562emit newFrame();563}564565void MainUI::changeEvent(QEvent *e) {566QGLWidget::changeEvent(e);567if (e->type() == QEvent::WindowStateChange)568Core_NotifyWindowHidden(isMinimized());569}570571bool MainUI::event(QEvent *e) {572TouchInput input;573QList<QTouchEvent::TouchPoint> touchPoints;574575switch (e->type()) {576case QEvent::TouchBegin:577case QEvent::TouchUpdate:578case QEvent::TouchEnd:579touchPoints = static_cast<QTouchEvent *>(e)->touchPoints();580foreach (const QTouchEvent::TouchPoint &touchPoint, touchPoints) {581switch (touchPoint.state()) {582case Qt::TouchPointStationary:583break;584case Qt::TouchPointPressed:585case Qt::TouchPointReleased:586input.x = touchPoint.pos().x() * g_display.dpi_scale_x * xscale;587input.y = touchPoint.pos().y() * g_display.dpi_scale_y * yscale;588input.flags = (touchPoint.state() == Qt::TouchPointPressed) ? TOUCH_DOWN : TOUCH_UP;589input.id = touchPoint.id();590NativeTouch(input);591break;592case Qt::TouchPointMoved:593input.x = touchPoint.pos().x() * g_display.dpi_scale_x * xscale;594input.y = touchPoint.pos().y() * g_display.dpi_scale_y * yscale;595input.flags = TOUCH_MOVE;596input.id = touchPoint.id();597NativeTouch(input);598break;599default:600break;601}602}603break;604case QEvent::MouseButtonDblClick:605if (!g_Config.bShowTouchControls || GetUIState() != UISTATE_INGAME)606emit doubleClick();607break;608case QEvent::MouseButtonPress:609case QEvent::MouseButtonRelease:610switch(((QMouseEvent*)e)->button()) {611case Qt::LeftButton:612input.x = ((QMouseEvent*)e)->pos().x() * g_display.dpi_scale_x * xscale;613input.y = ((QMouseEvent*)e)->pos().y() * g_display.dpi_scale_y * yscale;614input.flags = (e->type() == QEvent::MouseButtonPress) ? TOUCH_DOWN : TOUCH_UP;615input.id = 0;616NativeTouch(input);617break;618case Qt::RightButton:619NativeKey(KeyInput(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_2, (e->type() == QEvent::MouseButtonPress) ? KEY_DOWN : KEY_UP));620break;621case Qt::MiddleButton:622NativeKey(KeyInput(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_3, (e->type() == QEvent::MouseButtonPress) ? KEY_DOWN : KEY_UP));623break;624case Qt::ExtraButton1:625NativeKey(KeyInput(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_4, (e->type() == QEvent::MouseButtonPress) ? KEY_DOWN : KEY_UP));626break;627case Qt::ExtraButton2:628NativeKey(KeyInput(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_5, (e->type() == QEvent::MouseButtonPress) ? KEY_DOWN : KEY_UP));629break;630default:631break;632}633break;634case QEvent::MouseMove:635input.x = ((QMouseEvent*)e)->pos().x() * g_display.dpi_scale_x * xscale;636input.y = ((QMouseEvent*)e)->pos().y() * g_display.dpi_scale_y * yscale;637input.flags = TOUCH_MOVE;638input.id = 0;639NativeTouch(input);640break;641case QEvent::Wheel:642NativeKey(KeyInput(DEVICE_ID_MOUSE, ((QWheelEvent*)e)->delta()<0 ? NKCODE_EXT_MOUSEWHEEL_DOWN : NKCODE_EXT_MOUSEWHEEL_UP, KEY_DOWN));643break;644case QEvent::KeyPress:645{646auto qtKeycode = ((QKeyEvent*)e)->key();647auto iter = KeyMapRawQttoNative.find(qtKeycode);648InputKeyCode nativeKeycode = NKCODE_UNKNOWN;649if (iter != KeyMapRawQttoNative.end()) {650nativeKeycode = iter->second;651NativeKey(KeyInput(DEVICE_ID_KEYBOARD, nativeKeycode, KEY_DOWN));652}653654// Also get the unicode value.655QString text = ((QKeyEvent*)e)->text();656std::string str = text.toStdString();657// Now, we don't want CHAR events for non-printable characters. Not quite sure how we'll best658// do that, but here's one attempt....659switch (nativeKeycode) {660case NKCODE_DEL:661case NKCODE_FORWARD_DEL:662case NKCODE_TAB:663break;664default:665if (str.size()) {666int pos = 0;667int unicode = u8_nextchar(str.c_str(), &pos, str.size());668NativeKey(KeyInput(DEVICE_ID_KEYBOARD, unicode));669}670break;671}672}673break;674case QEvent::KeyRelease:675NativeKey(KeyInput(DEVICE_ID_KEYBOARD, KeyMapRawQttoNative.find(((QKeyEvent*)e)->key())->second, KEY_UP));676break;677678default:679// Can't switch on dynamic event types.680if (!HandleCustomEvent(e)) {681return QWidget::event(e);682}683}684e->accept();685return true;686}687688void MainUI::initializeGL() {689if (g_Config.iGPUBackend != (int)GPUBackend::OPENGL) {690INFO_LOG(Log::System, "Only GL supported under Qt - switching.");691g_Config.iGPUBackend = (int)GPUBackend::OPENGL;692}693694bool useCoreContext = format().profile() == QGLFormat::CoreProfile;695696SetGLCoreContext(useCoreContext);697698#ifndef USING_GLES2699// Some core profile drivers elide certain extensions from GL_EXTENSIONS/etc.700// glewExperimental allows us to force GLEW to search for the pointers anyway.701if (useCoreContext) {702glewExperimental = true;703}704glewInit();705// Unfortunately, glew will generate an invalid enum error, ignore.706if (useCoreContext) {707glGetError();708}709#endif710if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL) {711// OpenGL uses a background thread to do the main processing and only renders on the gl thread.712INFO_LOG(Log::System, "Initializing GL graphics context");713graphicsContext = new QtGLGraphicsContext();714INFO_LOG(Log::System, "Using thread, starting emu thread");715EmuThreadStart();716} else {717INFO_LOG(Log::System, "Not using thread, backend=%d", (int)g_Config.iGPUBackend);718}719graphicsContext->ThreadStart();720}721722void MainUI::paintGL() {723#ifdef SDL724SDL_PumpEvents();725#endif726updateAccelerometer();727if (emuThreadState == (int)EmuThreadState::DISABLED) {728UpdateRunLoop(graphicsContext);729} else {730graphicsContext->ThreadFrame();731// Do the rest in EmuThreadFunc732}733}734735void MainUI::updateAccelerometer() {736#if defined(MOBILE_DEVICE)737// TODO: Toggle it depending on whether it is enabled738QAccelerometerReading *reading = acc->reading();739if (reading) {740NativeAccelerometer(reading->x(), reading->y(), reading->z());741}742#endif743}744745#ifndef SDL746747MainAudio::~MainAudio() {748if (feed != nullptr) {749killTimer(timer);750feed->close();751}752if (output) {753output->stop();754delete output;755}756if (mixbuf)757free(mixbuf);758}759760void MainAudio::run() {761QAudioFormat fmt;762fmt.setSampleRate(AUDIO_FREQ);763fmt.setCodec("audio/pcm");764fmt.setChannelCount(AUDIO_CHANNELS);765fmt.setSampleSize(AUDIO_SAMPLESIZE);766fmt.setByteOrder(QAudioFormat::LittleEndian);767fmt.setSampleType(QAudioFormat::SignedInt);768mixlen = sizeof(short)*AUDIO_BUFFERS*AUDIO_CHANNELS*AUDIO_SAMPLES;769mixbuf = (char*)malloc(mixlen);770output = new QAudioOutput(fmt);771output->setBufferSize(mixlen);772feed = output->start();773if (feed != nullptr) {774// buffering has already done in the internal mixed buffer775// use a small interval to copy mixed audio stream from776// internal buffer to audio output buffer as soon as possible777// use 1 instead of 0 to prevent CPU exhausting778timer = startTimer(1);779}780}781782void MainAudio::timerEvent(QTimerEvent *) {783memset(mixbuf, 0, mixlen);784size_t frames = NativeMix((short *)mixbuf, AUDIO_BUFFERS*AUDIO_SAMPLES, AUDIO_FREQ);785if (frames > 0)786feed->write(mixbuf, sizeof(short) * AUDIO_CHANNELS * frames);787}788789#endif790791792void QTCamera::startCamera(int width, int height) {793__qt_startCapture(width, height);794}795796void QTCamera::stopCamera() {797__qt_stopCapture();798}799800#ifndef SDL801Q_DECL_EXPORT802#endif803int main(int argc, char *argv[])804{805TimeInit();806807for (int i = 1; i < argc; i++) {808if (!strcmp(argv[i], "--version")) {809printf("%s\n", PPSSPP_GIT_VERSION);810return 0;811}812}813814// Ignore sigpipe.815if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {816perror("Unable to ignore SIGPIPE");817}818819PROFILE_INIT();820glslang::InitializeProcess();821#if defined(Q_OS_LINUX)822QApplication::setAttribute(Qt::AA_X11InitThreads, true);823#endif824825// Qt would otherwise default to a 3.0 compatibility profile826// except on Nvidia, where Nvidia gives us the highest supported anyway827QGLFormat format;828format.setVersion(4, 6);829format.setProfile(QGLFormat::CoreProfile);830QGLFormat::setDefaultFormat(format);831832QApplication a(argc, argv);833QScreen* screen = a.primaryScreen();834QSizeF res = screen->physicalSize();835836if (res.width() < res.height())837res.transpose();838g_display.pixel_xres = res.width();839g_display.pixel_yres = res.height();840841g_display.dpi_scale_x = screen->logicalDotsPerInchX() / screen->physicalDotsPerInchX();842g_display.dpi_scale_y = screen->logicalDotsPerInchY() / screen->physicalDotsPerInchY();843g_display.dpi_scale_real_x = g_display.dpi_scale_x;844g_display.dpi_scale_real_y = g_display.dpi_scale_y;845g_display.dp_xres = (int)(g_display.pixel_xres * g_display.dpi_scale_x);846g_display.dp_yres = (int)(g_display.pixel_yres * g_display.dpi_scale_y);847848refreshRate = screen->refreshRate();849850qtcamera = new QTCamera;851QObject::connect(qtcamera, SIGNAL(onStartCamera(int, int)), qtcamera, SLOT(startCamera(int, int)));852QObject::connect(qtcamera, SIGNAL(onStopCamera()), qtcamera, SLOT(stopCamera()));853854std::string savegame_dir = ".";855std::string external_dir = ".";856#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)857savegame_dir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString();858external_dir = QStandardPaths::writableLocation(QStandardPaths::DataLocation).toStdString();859#endif860savegame_dir += "/";861external_dir += "/";862863NativeInit(argc, (const char **)argv, savegame_dir.c_str(), external_dir.c_str(), nullptr);864865g_mainWindow = new MainWindow(nullptr, g_Config.UseFullScreen());866g_mainWindow->show();867868// TODO: Support other backends than GL, like Vulkan, in the Qt backend.869g_Config.iGPUBackend = (int)GPUBackend::OPENGL;870871int ret = mainInternal(a);872INFO_LOG(Log::System, "Left mainInternal here.");873874#ifdef SDL875if (audioDev > 0) {876SDL_PauseAudioDevice(audioDev, 1);877SDL_CloseAudioDevice(audioDev);878}879#endif880NativeShutdown();881glslang::FinalizeProcess();882return ret;883}884885886