2022-02-07 15:00:02 +06:00
/*
2023-01-06 15:36:05 -05:00
* 86 Box 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 86 Box 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
*/
2021-11-25 10:20:56 +01:00
# include <cstdio>
# include <mutex>
2021-12-08 11:05:52 +01:00
# 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>
2022-04-08 00:58:38 +02:00
# include <QStandardPaths>
2021-11-25 10:20:56 +01:00
# include <QCoreApplication>
2021-12-24 11:53:24 +06:00
# include <QDateTime>
2022-02-27 15:30:35 +06:00
# include <QLocalSocket>
2022-05-05 11:55:17 +06:00
# include <QTimer>
2023-08-09 17:12:27 -04:00
# include <QProcess>
2023-08-10 10:40:19 -04:00
# include <QRegularExpression>
2021-11-25 10:20:56 +01:00
# include <QLibrary>
# include <QElapsedTimer>
2023-08-11 04:45:32 +02:00
# include <QScreen>
2022-07-11 03:44:30 +02:00
# include "qt_rendererstack.hpp"
2021-12-24 12:59:10 +06:00
# include "qt_mainwindow.hpp"
2022-01-08 16:39:51 +06:00
# include "qt_progsettings.hpp"
2023-08-11 04:45:32 +02:00
# include "qt_util.hpp"
2021-12-24 12:59:10 +06:00
2021-11-25 10:20:56 +01:00
# ifdef Q_OS_UNIX
2024-01-09 20:13:16 -03:00
# include <pthread.h>
2022-11-19 08:49:04 -05:00
# include <sys / mman.h>
2021-11-25 10:20:56 +01:00
# 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 ;
2021-12-08 11:05:52 +01:00
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>
2021-12-24 14:16:10 +06:00
# 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>
2022-04-06 16:16:25 +06:00
# include <86box/mem.h>
# include <86box/rom.h>
2021-11-25 10:20:56 +01:00
# include <86box/config.h>
# include <86box/ui.h>
2023-02-01 17:17:56 +01:00
# 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
{
2021-12-24 14:16:10 +06: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
{
2021-12-24 14:16:10 +06: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
}
2024-03-16 19:53:11 +05:00
void
do_start ( void )
{
}
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 )
{
2022-10-01 17:39:03 -04:00
# 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 ( ) ;
2022-10-01 17:39:03 -04:00
return fopen ( filename . toUtf8 ( ) . constData ( ) , mode ) ;
# else
2022-01-15 00:07:01 +02:00
return fopen ( QString : : fromUtf8 ( path ) . toLocal8Bit ( ) , mode ) ;
2022-10-01 17:39:03 -04:00
# endif
2021-11-25 10:20:56 +01:00
}
FILE *
plat_fopen64 ( const char * path , const char * mode )
{
2022-08-30 19:38:22 -04:00
# if defined(Q_OS_MACOS) or defined(Q_OS_LINUX)
2022-08-30 17:18:51 -04:00
QFileInfo fi ( path ) ;
2022-11-19 08:49:04 -05:00
QString filename = ( fi . isRelative ( ) & & ! fi . filePath ( ) . isEmpty ( ) ) ? usr_path + fi . filePath ( ) : fi . filePath ( ) ;
2022-08-30 17:18:51 -04:00
return fopen ( filename . toUtf8 ( ) . constData ( ) , mode ) ;
# else
2022-08-11 01:16:56 +06:00
return fopen ( QString : : fromUtf8 ( path ) . toLocal8Bit ( ) , mode ) ;
2022-08-30 17:18:51 -04:00
# 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. */
strncpy ( bufp , exe_path , max ) ;
# 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
{
2022-02-11 00:31:41 +06:00
# ifdef Q_OS_WINDOWS
if ( ( path [ 1 ] = = ' : ' ) | | ( path [ 0 ] = = ' \\ ' ) | | ( path [ 0 ] = = ' / ' ) )
return 1 ;
return 0 ;
# else
return path [ 0 ] = = ' / ' ;
# endif
2021-11-25 10:20:56 +01:00
}
2022-03-04 01:17:38 +06:00
void
2022-11-19 08:49:04 -05:00
path_normalize ( char * path )
2022-03-04 01:17:38 +06:00
{
2022-03-04 14:00:24 +06:00
# ifdef Q_OS_WINDOWS
2022-11-19 08:49:04 -05:00
while ( * path + + ! = 0 ) {
if ( * path = = ' \\ ' )
* path = ' / ' ;
2022-03-04 01:17:38 +06:00
}
2022-03-04 14:00:24 +06:00
# endif
2022-03-04 01:17:38 +06:00
}
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 ) ;
2022-03-04 01:17:38 +06:00
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 *
2023-08-18 18:28:38 +02:00
path_get_slash ( char * path )
{
2023-08-22 15:11:37 -04:00
return QString ( path ) . endsWith ( " / " ) ? " " : " / " ;
2023-08-18 18:28:38 +02:00
}
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 ) ) ;
}
2023-06-22 21:53:15 +05:00
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
2022-01-07 15:00:00 +06:00
void * ret = mmap ( 0 , size , PROT_READ | PROT_WRITE | ( executable ? PROT_EXEC : 0 ) , MAP_ANON | MAP_PRIVATE | ( executable ? MAP_JIT : 0 ) , - 1 , 0 ) ;
2022-11-19 08:49:04 -05:00
# else
2022-01-07 15:00:00 +06:00
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
2021-12-26 15:48:06 +06:00
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
}
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
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 ;
}
2022-08-02 02:57:00 +02:00
2021-12-23 01:56:56 +06:00
if ( ( p = = 0 ) & & ( time_sync & TIME_SYNC_ENABLED ) )
2022-01-09 01:04:59 +06:00
nvr_time_sync ( ) ;
2021-12-23 01:56:56 +06:00
2023-10-31 00:44:04 +01: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 ) ;
2022-08-02 02:57:00 +02:00
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 ) ;
2022-05-24 23:06:28 +02:00
paused_msg [ QObject : : tr ( " - PAUSED " ) . toWCharArray ( paused_msg ) ] = 0 ;
2022-02-13 07:03:37 +05:00
wcscat ( title , paused_msg ) ;
2021-11-25 10:20:56 +01:00
ui_window_title ( title ) ;
} else {
ui_window_title ( oldtitle ) ;
}
2023-02-01 17:17:56 +01:00
# ifdef DISCORD
2022-01-09 01:04:59 +06:00
discord_update_activity ( dopause ) ;
2023-02-01 17:17:56 +01:00
# endif
2022-10-29 09:09:30 -04:00
QTimer : : singleShot ( 0 , main_window , & MainWindow : : updateUiPauseState ) ;
2022-01-22 01:10:11 +02: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-01-22 01:10:11 +02:00
# 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 ) ;
2024-03-01 19:27:46 +05:00
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 = {
2023-08-17 02:46:37 +02:00
{ 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) " } } ,
2023-08-17 02:46:37 +02:00
{ 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) " } } ,
{ 0x0415 , { " pl-PL " , " Polish (Poland) " } } ,
{ 0x0416 , { " pt-BR " , " Portuguese (Brazil) " } } ,
{ 0x0816 , { " pt-PT " , " Portuguese (Portugal) " } } ,
{ 0x0419 , { " ru-RU " , " Russian (Russia) " } } ,
2023-08-17 02:46:37 +02:00
{ 0x041B , { " sk-SK " , " Slovak (Slovakia) " } } ,
2022-11-19 08:49:04 -05:00
{ 0x0424 , { " sl-SI " , " Slovenian (Slovenia) " } } ,
2023-08-17 02:46:37 +02:00
{ 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) " } } ,
{ 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 " } ;
2021-11-30 20:34:07 +01:00
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 + + ) {
2021-11-30 20:34:07 +01:00
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
}
2021-11-30 20:20:53 +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
}
2022-07-24 03:37:37 +02: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
{
2021-12-08 11:05:52 +01:00
blitmx_contention + + ;
2022-08-01 13:26:07 +06:00
if ( blitmx . try_lock ( ) ) {
2021-12-08 11:05:52 +01:00
return ;
}
2022-08-01 13:26:07 +06:00
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
{
2021-12-08 11:05:52 +01:00
blitmx_contention - - ;
2022-08-01 13:26:07 +06:00
blitmx . unlock ( ) ;
2021-12-08 11:05:52 +01:00
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
}
}
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
2021-12-31 12:36:55 +06:00
# ifdef _WIN32
2024-03-17 02:37:46 +05:00
# if defined(__amd64__) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64)
2024-03-16 04:07:27 +05:00
# define LIB_NAME_GS "gsdll64.dll"
# else
# define LIB_NAME_GS "gsdll32.dll"
2024-03-17 02:37:46 +05:00
# endif
2022-11-19 08:49:04 -05:00
# define MOUSE_CAPTURE_KEYSEQ "F8+F12"
2021-12-31 12:36:55 +06:00
# else
2022-11-19 08:49:04 -05:00
# define LIB_NAME_GS "libgs"
# define MOUSE_CAPTURE_KEYSEQ "CTRL-END"
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 ( ) ;
2024-03-19 14:27:19 +05:00
translatedstrings [ STRING_MOUSE_CAPTURE ] = QCoreApplication : : translate ( " " , " Click to capture mouse " ) . toStdWString ( ) ;
translatedstrings [ STRING_MOUSE_RELEASE ] = QCoreApplication : : translate ( " " , " Press F8+F12 to release mouse " ) . replace ( " F8+F12 " , MOUSE_CAPTURE_KEYSEQ ) . replace ( " CTRL-END " , QLocale : : system ( ) . name ( ) = = " de_DE " ? " Strg+Ende " : " CTRL-END " ) . toStdWString ( ) ;
translatedstrings [ STRING_MOUSE_RELEASE_MMB ] = QCoreApplication : : translate ( " " , " Press F8+F12 or middle button to release mouse " ) . replace ( " F8+F12 " , MOUSE_CAPTURE_KEYSEQ ) . replace ( " CTRL-END " , QLocale : : system ( ) . name ( ) = = " de_DE " ? " Strg+Ende " : " CTRL-END " ) . 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 libpcap is installed and that you are on a libpcap-compatible network connection. " ) . toStdWString ( ) ;
translatedstrings [ STRING_GHOSTSCRIPT_ERROR_TITLE ] = QCoreApplication : : translate ( " " , " Unable to initialize Ghostscript " ) . 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_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 ( ) ;
auto gsstr = QCoreApplication : : translate ( " " , " is required for automatic conversion of PostScript files to PDF. \n \n Any documents sent to the generic PostScript printer will be saved as PostScript (.ps) files. " ) ;
2023-07-22 18:14:22 -03:00
if ( gsstr . contains ( " libgs " ) ) {
gsstr . replace ( " libgs " , LIB_NAME_GS ) ;
2022-11-19 08:49:04 -05:00
} else
2023-07-22 18:14:22 -03:00
gsstr . prepend ( LIB_NAME_GS ) ;
2024-03-19 14:27:19 +05:00
translatedstrings [ STRING_GHOSTSCRIPT_ERROR_DESC ] = gsstr . 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-04-06 16:16:25 +06:00
2022-11-06 22:32:58 +06:00
void
plat_get_global_config_dir ( char * strptr )
{
# ifdef __APPLE__
auto dir = QDir ( QStandardPaths : : standardLocations ( QStandardPaths : : GenericConfigLocation ) [ 0 ] + " /net.86Box.86Box/ " ) ;
# else
auto dir = QDir ( QStandardPaths : : standardLocations ( QStandardPaths : : GenericConfigLocation ) [ 0 ] + " /86Box/ " ) ;
# endif
if ( ! dir . exists ( ) ) dir . mkpath ( " . " ) ;
strncpy ( strptr , dir . canonicalPath ( ) . toUtf8 ( ) . constData ( ) , 1024 ) ;
}
2022-04-06 16:16:25 +06:00
void
2023-08-11 04:45:32 +02:00
plat_init_rom_paths ( void )
2022-04-06 16:16:25 +06:00
{
2022-04-08 00:58:38 +02:00
auto paths = QStandardPaths : : standardLocations ( QStandardPaths : : GenericDataLocation ) ;
2022-04-06 16:16:25 +06:00
2022-04-08 00:58:38 +02:00
# ifdef _WIN32
// HACK: The standard locations returned for GenericDataLocation include
// the EXE path and a `data` directory within it as the last two entries.
2022-04-06 16:16:25 +06:00
2022-04-08 00:58:38 +02:00
// Remove the entries as we don't need them.
paths . removeLast ( ) ;
paths . removeLast ( ) ;
2022-04-06 16:16:25 +06:00
# endif
2022-04-08 00:58:38 +02:00
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 ( ) ) ;
# else
2022-04-08 00:46:30 +02:00
rom_add_path ( QDir ( path ) . filePath ( " 86Box/roms " ) . toUtf8 ( ) . constData ( ) ) ;
2022-04-08 10:20:01 +02:00
# endif
2022-04-08 00:58:38 +02:00
}
2022-04-06 16:16:25 +06:00
}
2023-08-09 17:12:27 -04:00
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 ( ) ;
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 ;
}
2023-08-10 10:40:19 -04:00
if ( QRegularExpression ( " model name.*: " ) . match ( line ) . hasMatch ( ) ) {
2023-08-09 17:12:27 -04:00
auto list = line . split ( " : " ) ;
if ( ! list . last ( ) . isEmpty ( ) ) {
cpu_string = list . last ( ) ;
break ;
}
}
}
}
# endif
qstrncpy ( outbuf , cpu_string . toUtf8 ( ) . constData ( ) , len ) ;
2023-08-11 04:45:32 +02:00
}
2024-01-09 20:13:16 -03:00
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 */
2024-01-09 20:13:16 -03:00
pSetThreadDescription = NULL ;
}
}
if ( pSetThreadDescription ) {
size_t len = strlen ( name ) + 1 ;
2024-01-10 10:27:11 -03:00
wchar_t wname [ len + 1 ] ;
2024-01-09 20:13:16 -03:00
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 ;
2024-01-09 20:13:16 -03:00
char truncated [ 64 ] ;
# else
char truncated [ 16 ] ;
# endif
strncpy ( truncated , name , sizeof ( truncated ) - 1 ) ;
# ifdef Q_OS_DARWIN
2024-01-10 10:27:11 -03:00
pthread_setname_np ( truncated ) ;
2024-01-09 20:13:16 -03:00
# else
2024-01-09 20:28:10 -03:00
pthread_setname_np ( thread ? * ( ( pthread_t * ) thread ) : pthread_self ( ) , truncated ) ;
2024-01-09 20:13:16 -03:00
# endif
# endif
}