Files
86Box/src/qt/qt_platform.cpp

845 lines
24 KiB
C++
Raw Normal View History

2022-02-07 15:00:02 +06:00
/*
2023-01-06 15:36:05 -05:00
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
2022-02-07 15:00:02 +06:00
*
2023-01-06 15:36:05 -05:00
* This file is part of the 86Box distribution.
2022-02-07 15:00:02 +06:00
*
2023-01-06 15:36:05 -05:00
* Common platform functions.
2022-02-07 15:00:02 +06:00
*
*
2023-01-06 15:36:05 -05:00
*
* Authors: Joakim L. Gilje <jgilje@jgilje.net>
2022-02-07 15:00:02 +06:00
* Cacodemon345
* Teemu Korhonen
*
2023-01-06 15:36:05 -05:00
* Copyright 2021 Joakim L. Gilje
* Copyright 2021-2022 Cacodemon345
* Copyright 2021-2022 Teemu Korhonen
2022-02-07 15:00:02 +06:00
*/
2025-03-14 21:24:36 +06:00
#ifdef __HAIKU__
#include <OS.h>
#endif
2021-11-25 10:20:56 +01:00
#include <cstdio>
#include <mutex>
#include <thread>
2021-11-25 10:20:56 +01:00
#include <memory>
#include <algorithm>
2021-12-31 12:36:55 +06:00
#include <map>
2021-11-25 10:20:56 +01:00
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QTemporaryFile>
#include <QStandardPaths>
2021-11-25 10:20:56 +01:00
#include <QCoreApplication>
2021-12-24 11:53:24 +06:00
#include <QDateTime>
#include <QLocalSocket>
#include <QTimer>
#include <QProcess>
#include <QRegularExpression>
2021-11-25 10:20:56 +01:00
#include <QLibrary>
#include <QElapsedTimer>
#include <QScreen>
2022-07-11 03:44:30 +02:00
#include "qt_rendererstack.hpp"
#include "qt_mainwindow.hpp"
2022-01-08 16:39:51 +06:00
#include "qt_progsettings.hpp"
#include "qt_util.hpp"
2021-11-25 10:20:56 +01:00
#ifdef Q_OS_UNIX
# include <pthread.h>
2022-11-19 08:49:04 -05:00
# include <sys/mman.h>
2021-11-25 10:20:56 +01:00
#endif
#ifdef Q_OS_OPENBSD
# include <pthread_np.h>
#endif
2023-05-29 01:30:51 -04:00
#if 0
static QByteArray buf;
#endif
2021-11-25 10:20:56 +01:00
extern QElapsedTimer elapsed_timer;
2022-11-19 08:49:04 -05:00
extern MainWindow *main_window;
QElapsedTimer elapsed_timer;
2022-11-19 08:49:04 -05:00
static std::atomic_int blitmx_contention = 0;
2022-07-04 01:50:42 +06:00
static std::recursive_mutex blitmx;
2021-11-25 10:20:56 +01:00
class CharPointer {
public:
2022-11-19 08:49:04 -05:00
CharPointer(char *buf, int size)
: b(buf)
, s(size)
{
}
CharPointer &operator=(const QByteArray &ba)
{
2021-11-25 10:20:56 +01:00
if (s > 0) {
2022-11-19 08:49:04 -05:00
strncpy(b, ba.data(), s - 1);
2021-11-25 10:20:56 +01:00
b[s] = 0;
} else {
// if we haven't been told the length of b, just assume enough
// because we didn't get it from emulator code
strcpy(b, ba.data());
b[ba.size()] = 0;
}
return *this;
}
2022-11-19 08:49:04 -05:00
2021-11-25 10:20:56 +01:00
private:
2022-11-19 08:49:04 -05:00
char *b;
int s;
2021-11-25 10:20:56 +01:00
};
extern "C" {
2021-12-02 19:35:08 +02:00
#ifdef Q_OS_WINDOWS
2022-03-07 21:47:48 +02:00
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <windows.h>
# include <86box/win.h>
#else
2022-03-07 21:47:48 +02:00
# include <strings.h>
2021-12-02 19:35:08 +02:00
#endif
2021-11-25 10:20:56 +01:00
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/gameport.h>
2021-12-23 01:56:56 +06:00
#include <86box/timer.h>
#include <86box/nvr.h>
2022-04-19 23:06:39 +02:00
#include <86box/path.h>
2021-11-25 10:20:56 +01:00
#include <86box/plat_dynld.h>
#include <86box/mem.h>
#include <86box/rom.h>
2021-11-25 10:20:56 +01:00
#include <86box/config.h>
#include <86box/ui.h>
#ifdef DISCORD
# include <86box/discord.h>
#endif
2021-11-25 10:20:56 +01:00
#include "../cpu/cpu.h"
#include <86box/plat.h>
2022-11-19 08:49:04 -05:00
volatile int cpu_thread_run = 1;
int mouse_capture = 0;
int fixed_size_x = 640;
int fixed_size_y = 480;
int rctrl_is_lalt = 0;
int update_icons = 1;
int kbd_req_capture = 0;
int hide_status_bar = 0;
int hide_tool_bar = 0;
uint32_t lang_id = 0x0409, lang_sys = 0x0409; // Multilangual UI variables, for now all set to LCID of en-US
2021-11-25 10:20:56 +01:00
2022-11-19 08:49:04 -05:00
int
stricmp(const char *s1, const char *s2)
2021-11-25 10:20:56 +01:00
{
#ifdef Q_OS_WINDOWS
return _stricmp(s1, s2);
#else
return strcasecmp(s1, s2);
#endif
2021-11-25 10:20:56 +01:00
}
2022-11-19 08:49:04 -05:00
int
strnicmp(const char *s1, const char *s2, size_t n)
2021-11-25 10:20:56 +01:00
{
#ifdef Q_OS_WINDOWS
return _strnicmp(s1, s2, n);
#else
return strncasecmp(s1, s2, n);
#endif
2021-11-25 10:20:56 +01:00
}
void
do_start(void)
{
2025-01-26 15:15:53 -05:00
//
}
2021-11-25 10:20:56 +01:00
void
do_stop(void)
{
2022-02-02 22:08:19 +06:00
cpu_thread_run = 0;
2023-05-29 01:30:51 -04:00
#if 0
main_window->close();
#endif
2021-11-25 10:20:56 +01:00
}
2022-11-19 08:49:04 -05:00
void
plat_get_exe_name(char *s, int size)
2021-11-25 10:20:56 +01:00
{
2021-12-02 19:35:08 +02:00
QByteArray exepath_temp = QCoreApplication::applicationDirPath().toLocal8Bit();
2022-11-19 08:49:04 -05:00
memcpy(s, exepath_temp.data(), std::min((qsizetype) exepath_temp.size(), (qsizetype) size));
2021-12-02 19:35:08 +02:00
2022-04-19 23:06:39 +02:00
path_slash(s);
2021-11-25 10:20:56 +01:00
}
uint32_t
plat_get_ticks(void)
{
return elapsed_timer.elapsed();
}
uint64_t
plat_timer_read(void)
{
return elapsed_timer.elapsed();
}
FILE *
plat_fopen(const char *path, const char *mode)
{
#if defined(Q_OS_MACOS) or defined(Q_OS_LINUX)
QFileInfo fi(path);
2022-11-19 08:49:04 -05:00
QString filename = (fi.isRelative() && !fi.filePath().isEmpty()) ? usr_path + fi.filePath() : fi.filePath();
return fopen(filename.toUtf8().constData(), mode);
#else
2022-01-15 00:07:01 +02:00
return fopen(QString::fromUtf8(path).toLocal8Bit(), mode);
#endif
2021-11-25 10:20:56 +01:00
}
FILE *
plat_fopen64(const char *path, const char *mode)
{
#if defined(Q_OS_MACOS) or defined(Q_OS_LINUX)
QFileInfo fi(path);
2022-11-19 08:49:04 -05:00
QString filename = (fi.isRelative() && !fi.filePath().isEmpty()) ? usr_path + fi.filePath() : fi.filePath();
return fopen(filename.toUtf8().constData(), mode);
#else
return fopen(QString::fromUtf8(path).toLocal8Bit(), mode);
#endif
2021-11-25 10:20:56 +01:00
}
int
plat_dir_create(char *path)
{
return QDir().mkdir(path) ? 0 : -1;
}
int
plat_dir_check(char *path)
{
QFileInfo fi(path);
return fi.isDir() ? 1 : 0;
}
int
plat_getcwd(char *bufp, int max)
{
2022-04-20 13:35:14 -03:00
#ifdef __APPLE__
/* Working directory for .app bundles is undefined. */
#ifdef USE_EXE_PATH
2022-04-20 13:35:14 -03:00
strncpy(bufp, exe_path, max);
#else
CharPointer(bufp, max) = QDir::homePath().toUtf8();
2024-09-19 10:34:10 +02:00
path_append_filename(bufp, bufp, "Library/86Box");
#endif
2022-04-20 13:35:14 -03:00
#else
2021-11-25 10:20:56 +01:00
CharPointer(bufp, max) = QDir::currentPath().toUtf8();
2022-04-20 13:35:14 -03:00
#endif
2021-11-25 10:20:56 +01:00
return 0;
}
void
2022-04-19 23:06:39 +02:00
path_get_dirname(char *dest, const char *path)
2021-11-25 10:20:56 +01:00
{
QFileInfo fi(path);
CharPointer(dest, -1) = fi.dir().path().toUtf8();
}
char *
2022-04-19 23:06:39 +02:00
path_get_extension(char *s)
2021-11-25 10:20:56 +01:00
{
auto len = strlen(s);
auto idx = QByteArray::fromRawData(s, len).lastIndexOf('.');
if (idx >= 0) {
2022-11-19 08:49:04 -05:00
return s + idx + 1;
2021-11-25 10:20:56 +01:00
}
2022-11-19 08:49:04 -05:00
return s + len;
2021-11-25 10:20:56 +01:00
}
char *
2022-04-19 23:06:39 +02:00
path_get_filename(char *s)
2021-11-25 10:20:56 +01:00
{
2021-12-02 19:35:08 +02:00
#ifdef Q_OS_WINDOWS
int c = strlen(s) - 1;
while (c > 0) {
2022-11-19 08:49:04 -05:00
if (s[c] == '/' || s[c] == '\\')
return (&s[c + 1]);
c--;
2021-12-02 19:35:08 +02:00
}
2023-05-29 01:30:51 -04:00
return s;
2021-12-02 19:35:08 +02:00
#else
2022-11-19 08:49:04 -05:00
auto idx = QByteArray::fromRawData(s, strlen(s)).lastIndexOf(QDir::separator().toLatin1());
2021-11-25 10:20:56 +01:00
if (idx >= 0) {
2022-11-19 08:49:04 -05:00
return s + idx + 1;
2021-11-25 10:20:56 +01:00
}
return s;
2021-12-02 19:35:08 +02:00
#endif
2021-11-25 10:20:56 +01:00
}
int
2022-04-19 23:06:39 +02:00
path_abs(char *path)
2021-11-25 10:20:56 +01:00
{
#ifdef Q_OS_WINDOWS
2024-05-19 21:17:57 +02:00
if ((path[1] == ':') || (path[0] == '\\') || (path[0] == '/') || (strstr(path, "ioctl://") == path))
return 1;
return 0;
#else
return path[0] == '/';
#endif
2021-11-25 10:20:56 +01:00
}
void
2022-11-19 08:49:04 -05:00
path_normalize(char *path)
{
#ifdef Q_OS_WINDOWS
2024-05-19 21:17:57 +02:00
if (strstr(path, "ioctl://") != path) {
while (*path++ != 0) {
if (*path == '\\')
*path = '/';
}
} else
path[8] = path[9] = path[11] = '\\';
#endif
}
2021-11-25 10:20:56 +01:00
void
2022-04-19 23:06:39 +02:00
path_slash(char *path)
2021-11-25 10:20:56 +01:00
{
2022-11-19 08:49:04 -05:00
auto len = strlen(path);
auto separator = '/';
2022-11-19 08:49:04 -05:00
if (path[len - 1] != separator) {
path[len] = separator;
path[len + 1] = 0;
2021-11-25 10:20:56 +01:00
}
2022-04-19 23:06:39 +02:00
path_normalize(path);
2021-11-25 10:20:56 +01:00
}
2023-08-22 15:11:37 -04:00
const char *
path_get_slash(char *path)
{
2023-08-22 15:11:37 -04:00
return QString(path).endsWith("/") ? "" : "/";
}
2021-11-25 10:20:56 +01:00
void
2022-04-19 23:06:39 +02:00
path_append_filename(char *dest, const char *s1, const char *s2)
2021-11-25 10:20:56 +01:00
{
strcpy(dest, s1);
2022-04-19 23:06:39 +02:00
path_slash(dest);
2021-11-25 10:20:56 +01:00
strcat(dest, s2);
}
void
plat_tempfile(char *bufp, char *prefix, char *suffix)
{
QString name;
if (prefix != nullptr) {
name.append(QString("%1-").arg(prefix));
}
name.append(QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss-zzz"));
2022-11-19 08:49:04 -05:00
if (suffix)
name.append(suffix);
strcpy(bufp, name.toUtf8().data());
2021-11-25 10:20:56 +01:00
}
2022-11-19 08:49:04 -05:00
void
plat_remove(char *path)
2021-11-25 10:20:56 +01:00
{
QFile(path).remove();
}
void *
plat_mmap(size_t size, uint8_t executable)
{
#if defined Q_OS_WINDOWS
return VirtualAlloc(NULL, size, MEM_COMMIT, executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
#elif defined Q_OS_UNIX
2022-11-19 08:49:04 -05:00
# if defined Q_OS_DARWIN && defined MAP_JIT
void *ret = mmap(0, size, PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0), MAP_ANON | MAP_PRIVATE | (executable ? MAP_JIT : 0), -1, 0);
2024-11-20 01:42:32 +01:00
# elif defined(PROT_MPROTECT)
void *ret = mmap(0, size, PROT_MPROTECT(PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0)), MAP_ANON | MAP_PRIVATE, -1, 0);
2025-03-11 01:20:00 +06:00
if (ret)
mprotect(ret, size, PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0));
2022-11-19 08:49:04 -05:00
# else
void *ret = mmap(0, size, PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0), MAP_ANON | MAP_PRIVATE, -1, 0);
2022-11-19 08:49:04 -05:00
# endif
return (ret == MAP_FAILED) ? nullptr : ret;
2021-11-25 10:20:56 +01:00
#endif
}
void
plat_munmap(void *ptr, size_t size)
{
2021-12-02 19:35:08 +02:00
#if defined Q_OS_WINDOWS
VirtualFree(ptr, 0, MEM_RELEASE);
#else
2021-11-25 10:20:56 +01:00
munmap(ptr, size);
2021-12-02 19:35:08 +02:00
#endif
2021-11-25 10:20:56 +01:00
}
extern bool cpu_thread_running;
2021-11-25 10:20:56 +01:00
void
plat_pause(int p)
{
static wchar_t oldtitle[512];
2023-05-29 01:30:51 -04:00
wchar_t title[1024];
wchar_t paused_msg[512];
2021-11-25 10:20:56 +01:00
if (!cpu_thread_running && p == 1) {
p = 2;
}
2023-12-21 15:06:04 +01:00
if ((!!p) == dopause) {
2022-02-13 19:28:28 +06:00
#ifdef Q_OS_WINDOWS
if (source_hwnd)
2022-11-19 08:49:04 -05:00
PostMessage((HWND) (uintptr_t) source_hwnd, WM_SENDSTATUS, (WPARAM) !!p, (LPARAM) (HWND) main_window->winId());
2022-02-13 19:28:28 +06:00
#endif
return;
}
2021-12-23 01:56:56 +06:00
if ((p == 0) && (time_sync & TIME_SYNC_ENABLED))
nvr_time_sync();
2021-12-23 01:56:56 +06:00
do_pause(p);
2021-11-25 10:20:56 +01:00
if (p) {
2022-11-19 08:49:04 -05:00
if (mouse_capture)
plat_mouse_capture(0);
2021-11-25 10:20:56 +01:00
wcsncpy(oldtitle, ui_window_title(NULL), sizeof_w(oldtitle) - 1);
2022-02-13 14:42:36 +01:00
wcscpy(title, oldtitle);
paused_msg[QObject::tr(" - PAUSED").toWCharArray(paused_msg)] = 0;
wcscat(title, paused_msg);
2021-11-25 10:20:56 +01:00
ui_window_title(title);
} else {
ui_window_title(oldtitle);
}
#ifdef DISCORD
discord_update_activity(dopause);
#endif
QTimer::singleShot(0, main_window, &MainWindow::updateUiPauseState);
#ifdef Q_OS_WINDOWS
if (source_hwnd)
2022-11-19 08:49:04 -05:00
PostMessage((HWND) (uintptr_t) source_hwnd, WM_SENDSTATUS, (WPARAM) !!p, (LPARAM) (HWND) main_window->winId());
#endif
2021-11-25 10:20:56 +01:00
}
void
plat_power_off(void)
{
2022-02-17 11:51:24 +06:00
plat_mouse_capture(0);
confirm_exit_cmdl = 0;
2021-11-25 10:20:56 +01:00
nvr_save();
config_save();
/* Deduct a sufficiently large number of cycles that no instructions will
run before the main thread is terminated */
cycles -= 99999999;
cpu_thread_run = 0;
2022-07-11 03:44:30 +02:00
QTimer::singleShot(0, (const QWidget *) main_window, &QMainWindow::close);
2021-11-25 10:20:56 +01:00
}
2022-11-19 08:49:04 -05:00
extern "C++" {
QMap<uint32_t, QPair<QString, QString>> ProgSettings::lcid_langcode = {
{ 0x0403, { "ca-ES", "Catalan (Spain)" } },
{ 0x0804, { "zh-CN", "Chinese (Simplified)" } },
{ 0x0404, { "zh-TW", "Chinese (Traditional)" } },
{ 0x041A, { "hr-HR", "Croatian (Croatia)" } },
{ 0x0405, { "cs-CZ", "Czech (Czech Republic)" } },
2022-11-19 08:49:04 -05:00
{ 0x0407, { "de-DE", "German (Germany)" } },
{ 0x0809, { "en-GB", "English (United Kingdom)" }},
{ 0x0409, { "en-US", "English (United States)" } },
2022-11-19 08:49:04 -05:00
{ 0x040B, { "fi-FI", "Finnish (Finland)" } },
{ 0x040C, { "fr-FR", "French (France)" } },
{ 0x040E, { "hu-HU", "Hungarian (Hungary)" } },
{ 0x0410, { "it-IT", "Italian (Italy)" } },
{ 0x0411, { "ja-JP", "Japanese (Japan)" } },
{ 0x0412, { "ko-KR", "Korean (Korea)" } },
2024-11-22 15:05:31 +01:00
{ 0x0413, { "nl-NL", "Dutch (Netherlands)" } },
2022-11-19 08:49:04 -05:00
{ 0x0415, { "pl-PL", "Polish (Poland)" } },
{ 0x0416, { "pt-BR", "Portuguese (Brazil)" } },
{ 0x0816, { "pt-PT", "Portuguese (Portugal)" } },
{ 0x0419, { "ru-RU", "Russian (Russia)" } },
{ 0x041B, { "sk-SK", "Slovak (Slovakia)" } },
2022-11-19 08:49:04 -05:00
{ 0x0424, { "sl-SI", "Slovenian (Slovenia)" } },
{ 0x0C0A, { "es-ES", "Spanish (Spain, Modern Sort)" } },
2022-11-19 08:49:04 -05:00
{ 0x041F, { "tr-TR", "Turkish (Turkey)" } },
{ 0x0422, { "uk-UA", "Ukrainian (Ukraine)" } },
2024-05-03 01:22:36 +05:00
{ 0x042A, { "vi-VN", "Vietnamese (Vietnam)" } },
2022-11-19 08:49:04 -05:00
{ 0xFFFF, { "system", "(System Default)" } },
};
2022-01-08 13:40:45 +02:00
}
2022-01-08 16:39:51 +06:00
2021-11-25 10:20:56 +01:00
/* Sets up the program language before initialization. */
2022-11-19 08:49:04 -05:00
uint32_t
plat_language_code(char *langcode)
{
for (auto &curKey : ProgSettings::lcid_langcode.keys()) {
if (ProgSettings::lcid_langcode[curKey].first == langcode) {
2022-01-08 16:39:51 +06:00
return curKey;
}
}
return 0xFFFF;
2021-11-25 10:20:56 +01:00
}
/* Converts back the language code to LCID */
2022-11-19 08:49:04 -05:00
void
plat_language_code_r(uint32_t lcid, char *outbuf, int len)
{
if (!ProgSettings::lcid_langcode.contains(lcid)) {
2022-01-08 16:39:51 +06:00
qstrncpy(outbuf, "system", len);
return;
}
qstrncpy(outbuf, ProgSettings::lcid_langcode[lcid].first.toUtf8().constData(), len);
2021-11-25 10:20:56 +01:00
return;
}
2022-07-24 03:54:47 +02:00
#ifndef Q_OS_WINDOWS
2022-11-19 08:49:04 -05:00
void *
dynld_module(const char *name, dllimp_t *table)
2021-11-25 10:20:56 +01:00
{
2022-11-19 08:49:04 -05:00
QString libraryName = name;
QFileInfo fi(libraryName);
QStringList removeSuffixes = { "dll", "dylib", "so" };
if (removeSuffixes.contains(fi.suffix())) {
libraryName = fi.completeBaseName();
}
auto lib = std::unique_ptr<QLibrary>(new QLibrary(libraryName));
2021-11-25 10:20:56 +01:00
if (lib->load()) {
2022-11-19 08:49:04 -05:00
for (auto imp = table; imp->name != nullptr; imp++) {
auto ptr = lib->resolve(imp->name);
if (ptr == nullptr) {
2021-11-25 10:20:56 +01:00
return nullptr;
}
2022-11-19 08:49:04 -05:00
auto imp_ptr = reinterpret_cast<void **>(imp->func);
*imp_ptr = reinterpret_cast<void *>(ptr);
2021-11-25 10:20:56 +01:00
}
} else {
return nullptr;
2021-11-25 10:20:56 +01:00
}
return lib.release();
}
2022-11-19 08:49:04 -05:00
void
dynld_close(void *handle)
2021-11-25 10:20:56 +01:00
{
2022-11-19 08:49:04 -05:00
delete reinterpret_cast<QLibrary *>(handle);
2021-11-25 10:20:56 +01:00
}
#endif
2021-11-25 10:20:56 +01:00
2022-11-19 08:49:04 -05:00
void
startblit()
2021-11-25 10:20:56 +01:00
{
blitmx_contention++;
if (blitmx.try_lock()) {
return;
}
blitmx.lock();
2021-11-25 10:20:56 +01:00
}
2022-11-19 08:49:04 -05:00
void
endblit()
2021-11-25 10:20:56 +01:00
{
blitmx_contention--;
blitmx.unlock();
if (blitmx_contention > 0) {
// a deadlock has been observed on linux when toggling via video_toggle_option
// because the mutex is typically unfair on linux
// => sleep if there's contention
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
2021-11-25 10:20:56 +01:00
}
2025-01-26 15:15:53 -05:00
} /*extern "C" */
2021-12-02 19:35:08 +02:00
#ifdef Q_OS_WINDOWS
2022-11-19 08:49:04 -05:00
size_t
mbstoc16s(uint16_t dst[], const char src[], int len)
2021-12-02 19:35:08 +02:00
{
2022-11-19 08:49:04 -05:00
if (src == NULL)
return 0;
if (len < 0)
return 0;
2021-12-02 19:35:08 +02:00
size_t ret = MultiByteToWideChar(CP_UTF8, 0, src, -1, reinterpret_cast<LPWSTR>(dst), dst == NULL ? 0 : len);
if (!ret) {
2022-11-19 08:49:04 -05:00
return -1;
2021-12-02 19:35:08 +02:00
}
return ret;
}
2022-11-19 08:49:04 -05:00
size_t
c16stombs(char dst[], const uint16_t src[], int len)
2021-12-02 19:35:08 +02:00
{
2022-11-19 08:49:04 -05:00
if (src == NULL)
return 0;
if (len < 0)
return 0;
2021-12-02 19:35:08 +02:00
size_t ret = WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<LPCWCH>(src), -1, dst, dst == NULL ? 0 : len, NULL, NULL);
if (!ret) {
2022-11-19 08:49:04 -05:00
return -1;
2021-12-02 19:35:08 +02:00
}
return ret;
}
#endif
# define MOUSE_CAPTURE_KEYSEQ "F8+F12"
2021-12-31 12:36:55 +06:00
#ifdef _WIN32
# if defined(__amd64__) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64)
2024-06-21 15:36:28 +06:00
# define LIB_NAME_GS "gsdll64.dll"
# define LIB_NAME_GPCL "gpcl6dll64.dll"
# else
2024-06-21 15:36:28 +06:00
# define LIB_NAME_GS "gsdll32.dll"
# define LIB_NAME_GPCL "gpcl6dll32.dll"
# endif
# define LIB_NAME_PCAP "Npcap"
2021-12-31 12:36:55 +06:00
#else
2022-11-19 08:49:04 -05:00
# define LIB_NAME_GS "libgs"
2024-06-21 15:36:28 +06:00
# define LIB_NAME_GPCL "libgpcl6"
# define LIB_NAME_PCAP "libpcap"
2021-12-31 12:36:55 +06:00
#endif
2022-01-08 16:39:51 +06:00
QMap<int, std::wstring> ProgSettings::translatedstrings;
2021-12-31 12:36:55 +06:00
2022-11-19 08:49:04 -05:00
void
ProgSettings::reloadStrings()
2021-12-31 12:36:55 +06:00
{
translatedstrings.clear();
translatedstrings[STRING_MOUSE_CAPTURE] = QCoreApplication::translate("", "Click to capture mouse").toStdWString();
translatedstrings[STRING_MOUSE_RELEASE] = QCoreApplication::translate("", "Press %1 to release mouse").arg(QCoreApplication::translate("", MOUSE_CAPTURE_KEYSEQ)).toStdWString();
translatedstrings[STRING_MOUSE_RELEASE_MMB] = QCoreApplication::translate("", "Press %1 or middle button to release mouse").arg(QCoreApplication::translate("", MOUSE_CAPTURE_KEYSEQ)).toStdWString();
translatedstrings[STRING_INVALID_CONFIG] = QCoreApplication::translate("", "Invalid configuration").toStdWString();
translatedstrings[STRING_NO_ST506_ESDI_CDROM] = QCoreApplication::translate("", "MFM/RLL or ESDI CD-ROM drives never existed").toStdWString();
translatedstrings[STRING_PCAP_ERROR_NO_DEVICES] = QCoreApplication::translate("", "No PCap devices found").toStdWString();
translatedstrings[STRING_PCAP_ERROR_INVALID_DEVICE] = QCoreApplication::translate("", "Invalid PCap device").toStdWString();
translatedstrings[STRING_PCAP_ERROR_DESC] = QCoreApplication::translate("", "Make sure %1 is installed and that you are on a %1-compatible network connection.").arg(LIB_NAME_PCAP).toStdWString();
translatedstrings[STRING_GHOSTSCRIPT_ERROR_TITLE] = QCoreApplication::translate("", "Unable to initialize Ghostscript").toStdWString();
translatedstrings[STRING_GHOSTSCRIPT_ERROR_DESC] = QCoreApplication::translate("", "%1 is required for automatic conversion of PostScript files to PDF.\n\nAny documents sent to the generic PostScript printer will be saved as PostScript (.ps) files.").arg(LIB_NAME_GS).toStdWString();
2024-06-21 15:36:28 +06:00
translatedstrings[STRING_GHOSTPCL_ERROR_TITLE] = QCoreApplication::translate("", "Unable to initialize GhostPCL").toStdWString();
translatedstrings[STRING_GHOSTPCL_ERROR_DESC] = QCoreApplication::translate("", "%1 is required for automatic conversion of PCL files to PDF.\n\nAny documents sent to the generic PCL printer will be saved as Printer Command Language (.pcl) files.").arg(LIB_NAME_GPCL).toStdWString();
translatedstrings[STRING_HW_NOT_AVAILABLE_MACHINE] = QCoreApplication::translate("", "Machine \"%hs\" is not available due to missing ROMs in the roms/machines directory. Switching to an available machine.").toStdWString();
translatedstrings[STRING_HW_NOT_AVAILABLE_VIDEO] = QCoreApplication::translate("", "Video card \"%hs\" is not available due to missing ROMs in the roms/video directory. Switching to an available video card.").toStdWString();
translatedstrings[STRING_HW_NOT_AVAILABLE_VIDEO2] = QCoreApplication::translate("", "Video card #2 \"%hs\" is not available due to missing ROMs in the roms/video directory. Disabling the second video card.").toStdWString();
translatedstrings[STRING_HW_NOT_AVAILABLE_DEVICE] = QCoreApplication::translate("", "Device \"%hs\" is not available due to missing ROMs. Ignoring the device.").toStdWString();
translatedstrings[STRING_HW_NOT_AVAILABLE_TITLE] = QCoreApplication::translate("", "Hardware not available").toStdWString();
translatedstrings[STRING_MONITOR_SLEEP] = QCoreApplication::translate("", "Monitor in sleep mode").toStdWString();
translatedstrings[STRING_NET_ERROR] = QCoreApplication::translate("", "Failed to initialize network driver").toStdWString();
translatedstrings[STRING_NET_ERROR_DESC] = QCoreApplication::translate("", "The network configuration will be switched to the null driver").toStdWString();
translatedstrings[STRING_ESCP_ERROR_TITLE] = QCoreApplication::translate("", "Unable to find Dot-Matrix fonts").toStdWString();
2025-03-11 10:40:40 +01:00
translatedstrings[STRING_ESCP_ERROR_DESC] = QCoreApplication::translate("", "TrueType fonts in the \"roms/printer/fonts\" directory are required for the emulation of the Generic ESC/P Dot-Matrix Printer.").toStdWString();
2021-12-31 12:36:55 +06:00
}
2022-11-19 08:49:04 -05:00
wchar_t *
plat_get_string(int i)
2021-12-31 12:36:55 +06:00
{
2022-11-19 08:49:04 -05:00
if (ProgSettings::translatedstrings.empty())
ProgSettings::reloadStrings();
2022-01-08 16:39:51 +06:00
return ProgSettings::translatedstrings[i].data();
2021-12-31 12:36:55 +06:00
}
2021-12-02 19:35:08 +02:00
int
plat_chdir(char *path)
{
return QDir::setCurrent(QString(path)) ? 0 : -1;
}
2022-11-06 22:32:58 +06:00
void
plat_get_global_config_dir(char *outbuf, const uint8_t len)
2022-11-06 22:32:58 +06:00
{
const auto dir = QDir(QStandardPaths::standardLocations(QStandardPaths::AppConfigLocation)[0]);
if (!dir.exists()) {
if (!dir.mkpath(".")) {
qWarning("Failed to create global configuration directory %s", dir.absolutePath().toUtf8().constData());
}
}
strncpy(outbuf, dir.canonicalPath().toUtf8().constData(), len);
}
void
plat_get_global_data_dir(char *outbuf, const uint8_t len)
{
const auto dir = QDir(QStandardPaths::standardLocations(QStandardPaths::AppDataLocation)[0]);
if (!dir.exists()) {
if (!dir.mkpath(".")) {
qWarning("Failed to create global data directory %s", dir.absolutePath().toUtf8().constData());
}
}
strncpy(outbuf, dir.canonicalPath().toUtf8().constData(), len);
}
void
plat_get_temp_dir(char *outbuf, const uint8_t len)
{
const auto dir = QDir(QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0]);
strncpy(outbuf, dir.canonicalPath().toUtf8().constData(), len);
2022-11-06 22:32:58 +06:00
}
void
plat_init_rom_paths(void)
{
auto paths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
#ifdef _WIN32
// HACK: The standard locations returned for GenericDataLocation include
// the EXE path and a `data` directory within it as the last two entries.
// Remove the entries as we don't need them.
paths.removeLast();
paths.removeLast();
#endif
2022-11-19 08:49:04 -05:00
for (auto &path : paths) {
2022-04-08 10:20:01 +02:00
#ifdef __APPLE__
rom_add_path(QDir(path).filePath("net.86Box.86Box/roms").toUtf8().constData());
rom_add_path(QDir(path).filePath("86Box/roms").toUtf8().constData());
2022-04-08 10:20:01 +02:00
#else
rom_add_path(QDir(path).filePath("86Box/roms").toUtf8().constData());
2022-04-08 10:20:01 +02:00
#endif
}
}
void
plat_get_cpu_string(char *outbuf, uint8_t len) {
auto cpu_string = QString("Unknown");
/* Write the default string now in case we have to exit early from an error */
qstrncpy(outbuf, cpu_string.toUtf8().constData(), len);
#if defined(Q_OS_MACOS)
auto *process = new QProcess(nullptr);
QStringList arguments;
QString program = "/usr/sbin/sysctl";
arguments << "machdep.cpu.brand_string";
process->start(program, arguments);
if (!process->waitForStarted()) {
return;
}
if (!process->waitForFinished()) {
return;
}
QByteArray result = process->readAll();
auto command_result = QString(result).split(": ").last().trimmed();
if(!command_result.isEmpty()) {
cpu_string = command_result;
}
#elif defined(Q_OS_WINDOWS)
const LPCSTR keyName = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
const LPCSTR valueName = "ProcessorNameString";
unsigned char buf[32768];
DWORD bufSize;
HKEY hKey;
bufSize = 32768;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName, 0, 1, &hKey) == ERROR_SUCCESS) {
if (RegQueryValueExA(hKey, valueName, NULL, NULL, buf, &bufSize) == ERROR_SUCCESS) {
cpu_string = reinterpret_cast<const char*>(buf);
}
RegCloseKey(hKey);
}
#elif defined(Q_OS_LINUX)
auto cpuinfo = QString("/proc/cpuinfo");
auto cpuinfo_fi = QFileInfo(cpuinfo);
if(!cpuinfo_fi.isReadable()) {
return;
}
QFile file(cpuinfo);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream textStream(&file);
while(true) {
QString line = textStream.readLine();
if (line.isNull()) {
break;
}
if(QRegularExpression("model name.*:").match(line).hasMatch()) {
auto list = line.split(": ");
if(!list.last().isEmpty()) {
cpu_string = list.last();
break;
}
}
}
}
#endif
qstrncpy(outbuf, cpu_string.toUtf8().constData(), len);
}
void
plat_set_thread_name(void *thread, const char *name)
{
#ifdef Q_OS_WINDOWS
/* SetThreadDescription was added in 14393. Revisit if we ever start requiring 10. */
static void *kernel32_handle = NULL;
static HRESULT(WINAPI *pSetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription) = NULL;
static dllimp_t kernel32_imports[] = {
// clang-format off
{ "SetThreadDescription", &pSetThreadDescription },
{ NULL, NULL }
// clang-format on
};
if (!kernel32_handle) {
kernel32_handle = dynld_module("kernel32.dll", kernel32_imports);
if (!kernel32_handle) {
2024-01-10 10:27:11 -03:00
kernel32_handle = kernel32_imports; /* store dummy pointer to avoid trying again */
pSetThreadDescription = NULL;
}
}
if (pSetThreadDescription) {
size_t len = strlen(name) + 1;
2024-01-10 10:27:11 -03:00
wchar_t wname[len + 1];
mbstowcs(wname, name, len);
pSetThreadDescription(thread ? (HANDLE) thread : GetCurrentThread(), wname);
}
#else
# ifdef Q_OS_DARWIN
2024-01-10 10:27:11 -03:00
if (thread) /* Apple pthread can only set self's name */
return;
char truncated[64];
2024-11-20 01:36:29 +01:00
# elif defined(Q_OS_NETBSD)
char truncated[64];
# else
char truncated[16];
# endif
strncpy(truncated, name, sizeof(truncated) - 1);
# if defined(Q_OS_DARWIN)
2024-01-10 10:27:11 -03:00
pthread_setname_np(truncated);
2024-11-20 01:36:29 +01:00
# elif defined(Q_OS_NETBSD)
pthread_setname_np(thread ? *((pthread_t *) thread) : pthread_self(), truncated, "%s");
2025-03-14 21:24:36 +06:00
# elif defined(__HAIKU__)
rename_thread(find_thread(NULL), truncated);
# elif defined(Q_OS_OPENBSD)
pthread_set_name_np(thread ? *((pthread_t *) thread) : pthread_self(), truncated);
# else
2024-01-09 20:28:10 -03:00
pthread_setname_np(thread ? *((pthread_t *) thread) : pthread_self(), truncated);
# endif
#endif
}