2022-02-07 15:00:02 +06: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 .
*
* This file is part of the 86 Box distribution .
*
* Program settings UI module .
*
*
*
* Authors : Joakim L . Gilje < jgilje @ jgilje . net >
* Cacodemon345
* Teemu Korhonen
*
* Copyright 2021 Joakim L . Gilje
* Copyright 2021 - 2021 Teemu Korhonen
* Copyright 2021 - 2022 Cacodemon345
*/
2021-12-04 21:33:04 +01:00
# include "qt_rendererstack.hpp"
# include "ui_qt_rendererstack.h"
# include "qt_hardwarerenderer.hpp"
2022-02-28 08:43:14 +02:00
# include "qt_openglrenderer.hpp"
2022-02-27 14:56:51 +02:00
# include "qt_softwarerenderer.hpp"
2022-04-21 16:32:46 +06:00
# include "qt_vulkanwindowrenderer.hpp"
2021-12-04 21:33:04 +01:00
2021-12-29 23:49:09 +06:00
# include "qt_mainwindow.hpp"
2022-02-10 15:30:39 +06:00
# include "qt_util.hpp"
2021-12-29 23:49:09 +06:00
2021-12-10 01:03:20 +06:00
# include "evdev_mouse.hpp"
2021-12-08 17:02:28 +06:00
# include <QScreen>
2022-04-21 16:32:48 +06:00
# include <QMessageBox>
2021-12-08 17:02:28 +06:00
2021-11-30 16:26:49 +06:00
# ifdef __APPLE__
2022-02-27 14:56:51 +02:00
# include <CoreGraphics / CoreGraphics.h>
2021-11-30 16:26:49 +06:00
# endif
2022-02-27 14:56:51 +02:00
extern " C " {
2021-11-30 16:26:49 +06:00
# include <86box/mouse.h>
# include <86box/plat.h>
# include <86box/video.h>
}
2022-04-21 13:56:39 -03:00
extern " C " void macos_poll_mouse ( ) ;
2022-02-27 14:56:51 +02:00
extern MainWindow * main_window ;
RendererStack : : RendererStack ( QWidget * parent )
: QStackedWidget ( parent )
, ui ( new Ui : : RendererStack )
2021-12-04 21:33:04 +01:00
{
ui - > setupUi ( this ) ;
2021-12-15 00:37:48 +02:00
2022-03-11 12:03:54 +06:00
# if defined __unix__ && !defined __HAIKU__
2022-04-20 20:54:41 -03:00
char * mouse_type = getenv ( " EMU86BOX_MOUSE " ) , auto_mouse_type [ 16 ] ;
if ( ! mouse_type | | ( mouse_type [ 0 ] = = ' \0 ' ) | | ! stricmp ( mouse_type , " auto " ) ) {
if ( QApplication : : platformName ( ) . contains ( " wayland " ) )
strcpy ( auto_mouse_type , " wayland " ) ;
else if ( QApplication : : platformName ( ) = = " eglfs " )
strcpy ( auto_mouse_type , " evdev " ) ;
else if ( QApplication : : platformName ( ) = = " xcb " )
strcpy ( auto_mouse_type , " xinput2 " ) ;
else
auto_mouse_type [ 0 ] = ' \0 ' ;
mouse_type = auto_mouse_type ;
2021-12-10 01:03:20 +06:00
}
2022-04-20 20:54:41 -03:00
# ifdef WAYLAND
if ( ! stricmp ( mouse_type , " wayland " ) ) {
2022-04-21 13:56:39 -03:00
wl_init ( ) ;
this - > mouse_poll_func = wl_mouse_poll ;
this - > mouse_capture_func = wl_mouse_capture ;
this - > mouse_uncapture_func = wl_mouse_uncapture ;
2022-04-20 20:54:41 -03:00
} else
2022-02-27 14:56:51 +02:00
# endif
# ifdef EVDEV_INPUT
2022-04-20 20:54:41 -03:00
if ( ! stricmp ( mouse_type , " evdev " ) ) {
2022-04-21 13:56:39 -03:00
evdev_init ( ) ;
this - > mouse_poll_func = evdev_mouse_poll ;
2022-04-20 20:54:41 -03:00
} else
2022-02-27 14:56:51 +02:00
# endif
2022-04-20 20:54:41 -03:00
if ( ! stricmp ( mouse_type , " xinput2 " ) ) {
2022-02-15 02:34:13 +06:00
extern void xinput2_init ( ) ;
2022-04-20 20:54:41 -03:00
extern void xinput2_poll ( ) ;
2022-04-20 21:38:57 -03:00
extern void xinput2_exit ( ) ;
2022-04-21 13:56:39 -03:00
xinput2_init ( ) ;
this - > mouse_poll_func = xinput2_poll ;
this - > mouse_exit_func = xinput2_exit ;
2022-02-15 02:34:13 +06:00
}
2021-12-10 01:03:20 +06:00
# endif
2022-04-21 13:56:39 -03:00
# ifdef __APPLE__
this - > mouse_poll_func = macos_poll_mouse ;
# endif
2021-12-04 21:33:04 +01:00
}
RendererStack : : ~ RendererStack ( )
{
delete ui ;
}
2021-11-30 16:26:49 +06:00
void
qt_mouse_capture ( int on )
{
2022-02-27 14:56:51 +02:00
if ( ! on ) {
2021-11-30 16:26:49 +06:00
mouse_capture = 0 ;
QApplication : : setOverrideCursor ( Qt : : ArrowCursor ) ;
# ifdef __APPLE__
CGAssociateMouseAndMouseCursorPosition ( true ) ;
# endif
return ;
}
mouse_capture = 1 ;
QApplication : : setOverrideCursor ( Qt : : BlankCursor ) ;
# ifdef __APPLE__
CGAssociateMouseAndMouseCursorPosition ( false ) ;
# endif
return ;
}
2022-02-27 14:56:51 +02:00
void
RendererStack : : mousePoll ( )
2021-11-30 16:26:49 +06:00
{
2022-04-21 13:56:39 -03:00
# ifndef __APPLE__
2022-02-27 14:56:51 +02:00
mouse_x = mousedata . deltax ;
mouse_y = mousedata . deltay ;
mouse_z = mousedata . deltaz ;
2021-11-30 16:26:49 +06:00
mousedata . deltax = mousedata . deltay = mousedata . deltaz = 0 ;
2022-04-21 13:56:39 -03:00
mouse_buttons = mousedata . mousebuttons ;
2022-02-15 02:34:13 +06:00
2022-04-21 13:56:39 -03:00
if ( this - > mouse_poll_func )
# endif
this - > mouse_poll_func ( ) ;
2021-11-30 16:26:49 +06:00
}
2021-12-09 00:01:22 +06:00
int ignoreNextMouseEvent = 1 ;
2022-02-27 14:56:51 +02:00
void
RendererStack : : mouseReleaseEvent ( QMouseEvent * event )
2021-11-30 16:26:49 +06:00
{
2022-02-27 14:56:51 +02:00
if ( this - > geometry ( ) . contains ( event - > pos ( ) ) & & event - > button ( ) = = Qt : : LeftButton & & ! mouse_capture & & ( isMouseDown & 1 ) ) {
2021-12-01 01:11:06 +06:00
plat_mouse_capture ( 1 ) ;
2021-12-02 16:26:33 +06:00
this - > setCursor ( Qt : : BlankCursor ) ;
2022-02-27 14:56:51 +02:00
if ( ! ignoreNextMouseEvent )
ignoreNextMouseEvent + + ; // Avoid jumping cursor when moved.
2022-02-05 01:01:06 +05:00
isMouseDown & = ~ 1 ;
2021-11-30 16:26:49 +06:00
return ;
}
2022-02-27 14:56:51 +02:00
if ( mouse_capture & & event - > button ( ) = = Qt : : MiddleButton & & mouse_get_buttons ( ) < 3 ) {
2021-12-01 01:11:06 +06:00
plat_mouse_capture ( 0 ) ;
2021-12-02 16:26:33 +06:00
this - > setCursor ( Qt : : ArrowCursor ) ;
2022-02-05 01:01:06 +05:00
isMouseDown & = ~ 1 ;
2021-11-30 16:26:49 +06:00
return ;
}
2022-02-27 14:56:51 +02:00
if ( mouse_capture ) {
2021-11-30 16:26:49 +06:00
mousedata . mousebuttons & = ~ event - > button ( ) ;
}
2022-02-05 01:01:06 +05:00
isMouseDown & = ~ 1 ;
2021-11-30 16:26:49 +06:00
}
2022-02-27 14:56:51 +02:00
void
RendererStack : : mousePressEvent ( QMouseEvent * event )
2021-11-30 16:26:49 +06:00
{
2022-02-05 01:01:06 +05:00
isMouseDown | = 1 ;
2022-02-27 14:56:51 +02:00
if ( mouse_capture ) {
2021-11-30 16:26:49 +06:00
mousedata . mousebuttons | = event - > button ( ) ;
}
2021-12-02 16:26:33 +06:00
event - > accept ( ) ;
2021-11-30 16:26:49 +06:00
}
2022-02-27 14:56:51 +02:00
void
RendererStack : : wheelEvent ( QWheelEvent * event )
2021-11-30 16:26:49 +06:00
{
2022-02-27 14:56:51 +02:00
if ( mouse_capture ) {
2021-12-06 16:33:25 +06:00
mousedata . deltaz + = event - > pixelDelta ( ) . y ( ) ;
2021-11-30 16:26:49 +06:00
}
}
2022-02-27 14:56:51 +02:00
void
RendererStack : : mouseMoveEvent ( QMouseEvent * event )
2021-11-30 16:26:49 +06:00
{
2022-02-27 14:56:51 +02:00
if ( QApplication : : platformName ( ) . contains ( " wayland " ) ) {
2021-12-02 16:26:33 +06:00
event - > accept ( ) ;
return ;
}
2022-02-27 14:56:51 +02:00
if ( ! mouse_capture ) {
event - > ignore ( ) ;
return ;
}
2022-04-08 01:51:32 +06:00
# if defined __APPLE__ || defined _WIN32
2021-11-30 16:26:49 +06:00
event - > accept ( ) ;
return ;
# else
static QPoint oldPos = QCursor : : pos ( ) ;
2022-02-27 14:56:51 +02:00
if ( ignoreNextMouseEvent ) {
oldPos = event - > pos ( ) ;
ignoreNextMouseEvent - - ;
event - > accept ( ) ;
return ;
}
2021-11-30 16:26:49 +06:00
mousedata . deltax + = event - > pos ( ) . x ( ) - oldPos . x ( ) ;
mousedata . deltay + = event - > pos ( ) . y ( ) - oldPos . y ( ) ;
2022-02-27 14:56:51 +02:00
if ( QApplication : : platformName ( ) = = " eglfs " ) {
leaveEvent ( ( QEvent * ) event ) ;
2021-12-09 00:01:22 +06:00
ignoreNextMouseEvent - - ;
}
2022-02-15 02:34:13 +06:00
QCursor : : setPos ( mapToGlobal ( QPoint ( width ( ) / 2 , height ( ) / 2 ) ) ) ;
ignoreNextMouseEvent = 2 ;
2022-02-27 14:56:51 +02:00
oldPos = event - > pos ( ) ;
2021-11-30 16:26:49 +06:00
# endif
}
2022-02-27 14:56:51 +02:00
void
RendererStack : : leaveEvent ( QEvent * event )
2021-12-08 16:27:58 +06:00
{
2022-02-27 14:56:51 +02:00
if ( QApplication : : platformName ( ) . contains ( " wayland " ) ) {
2021-12-08 16:27:58 +06:00
event - > accept ( ) ;
return ;
}
2022-02-27 14:56:51 +02:00
if ( ! mouse_capture )
return ;
2022-03-11 12:03:54 +06:00
QCursor : : setPos ( mapToGlobal ( QPoint ( width ( ) / 2 , height ( ) / 2 ) ) ) ;
2021-12-08 16:27:58 +06:00
ignoreNextMouseEvent = 2 ;
event - > accept ( ) ;
}
2022-02-27 14:56:51 +02:00
void
RendererStack : : switchRenderer ( Renderer renderer )
{
2021-12-15 19:58:09 +01:00
startblit ( ) ;
2021-12-15 20:02:35 +01:00
if ( current ) {
2022-02-27 14:56:51 +02:00
rendererWindow - > finalize ( ) ;
2021-12-15 20:02:35 +01:00
removeWidget ( current . get ( ) ) ;
2022-02-27 14:56:51 +02:00
disconnect ( this , & RendererStack : : blitToRenderer , nullptr , nullptr ) ;
/* Create new renderer only after previous is destroyed! */
connect ( current . get ( ) , & QObject : : destroyed , [ this , renderer ] ( QObject * ) { createRenderer ( renderer ) ; } ) ;
current . release ( ) - > deleteLater ( ) ;
} else {
createRenderer ( renderer ) ;
2021-12-15 19:58:09 +01:00
}
2022-02-27 14:56:51 +02:00
}
2021-12-15 19:58:09 +01:00
2022-02-27 14:56:51 +02:00
void
RendererStack : : createRenderer ( Renderer renderer )
{
2021-12-15 19:58:09 +01:00
switch ( renderer ) {
2022-02-27 14:56:51 +02:00
case Renderer : : Software :
{
auto sw = new SoftwareRenderer ( this ) ;
rendererWindow = sw ;
connect ( this , & RendererStack : : blitToRenderer , sw , & SoftwareRenderer : : onBlit , Qt : : QueuedConnection ) ;
2022-03-11 12:03:54 +06:00
# ifdef __HAIKU__
current . reset ( sw ) ;
# else
2022-02-27 14:56:51 +02:00
current . reset ( this - > createWindowContainer ( sw , this ) ) ;
2022-03-11 12:03:54 +06:00
# endif
2022-02-27 14:56:51 +02:00
}
break ;
case Renderer : : OpenGL :
{
this - > createWinId ( ) ;
auto hw = new HardwareRenderer ( this ) ;
rendererWindow = hw ;
connect ( this , & RendererStack : : blitToRenderer , hw , & HardwareRenderer : : onBlit , Qt : : QueuedConnection ) ;
current . reset ( this - > createWindowContainer ( hw , this ) ) ;
break ;
}
case Renderer : : OpenGLES :
{
this - > createWinId ( ) ;
auto hw = new HardwareRenderer ( this , HardwareRenderer : : RenderType : : OpenGLES ) ;
rendererWindow = hw ;
connect ( this , & RendererStack : : blitToRenderer , hw , & HardwareRenderer : : onBlit , Qt : : QueuedConnection ) ;
current . reset ( this - > createWindowContainer ( hw , this ) ) ;
break ;
}
case Renderer : : OpenGL3 :
{
this - > createWinId ( ) ;
auto hw = new OpenGLRenderer ( this ) ;
rendererWindow = hw ;
connect ( this , & RendererStack : : blitToRenderer , hw , & OpenGLRenderer : : onBlit , Qt : : QueuedConnection ) ;
connect ( hw , & OpenGLRenderer : : initialized , [ = ] ( ) {
2022-04-21 16:32:48 +06:00
/* Buffers are available only after initialization. */
2022-02-27 14:56:51 +02:00
imagebufs = rendererWindow - > getBuffers ( ) ;
endblit ( ) ;
emit rendererChanged ( ) ;
} ) ;
connect ( hw , & OpenGLRenderer : : errorInitializing , [ = ] ( ) {
2022-04-21 16:32:48 +06:00
/* Renderer not could initialize, fallback to software. */
2022-03-05 02:57:57 +02:00
imagebufs = { } ;
2022-02-27 14:56:51 +02:00
endblit ( ) ;
QTimer : : singleShot ( 0 , this , [ this ] ( ) { switchRenderer ( Renderer : : Software ) ; } ) ;
} ) ;
current . reset ( this - > createWindowContainer ( hw , this ) ) ;
break ;
}
2022-04-21 16:32:46 +06:00
case Renderer : : Vulkan :
{
this - > createWinId ( ) ;
2022-04-21 16:32:48 +06:00
VulkanWindowRenderer * hw = nullptr ;
try {
hw = new VulkanWindowRenderer ( this ) ;
} catch ( std : : runtime_error & e ) {
auto msgBox = new QMessageBox ( QMessageBox : : Critical , " 86Box " , e . what ( ) + QString ( " \n Falling back to software rendering. " ) , QMessageBox : : Ok ) ;
msgBox - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
msgBox - > show ( ) ;
imagebufs = { } ;
endblit ( ) ;
QTimer : : singleShot ( 0 , this , [ this ] ( ) { switchRenderer ( Renderer : : Software ) ; } ) ;
break ;
} ;
2022-04-21 16:32:46 +06:00
rendererWindow = hw ;
connect ( this , & RendererStack : : blitToRenderer , hw , & VulkanWindowRenderer : : onBlit , Qt : : QueuedConnection ) ;
connect ( hw , & VulkanWindowRenderer : : rendererInitialized , [ = ] ( ) {
2022-04-21 16:32:48 +06:00
/* Buffers are available only after initialization. */
2022-04-21 16:32:46 +06:00
imagebufs = rendererWindow - > getBuffers ( ) ;
endblit ( ) ;
emit rendererChanged ( ) ;
} ) ;
2022-04-21 16:32:48 +06:00
connect ( hw , & VulkanWindowRenderer : : errorInitializing , [ = ] ( ) {
/* Renderer could not initialize, fallback to software. */
auto msgBox = new QMessageBox ( QMessageBox : : Critical , " 86Box " , QString ( " Failed to initialize Vulkan renderer. \n Falling back to software rendering. " ) , QMessageBox : : Ok ) ;
msgBox - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
msgBox - > show ( ) ;
imagebufs = { } ;
endblit ( ) ;
QTimer : : singleShot ( 0 , this , [ this ] ( ) { switchRenderer ( Renderer : : Software ) ; } ) ;
} ) ;
2022-04-21 16:32:46 +06:00
current . reset ( this - > createWindowContainer ( hw , this ) ) ;
break ;
}
2021-12-15 19:58:09 +01:00
}
2022-01-15 21:45:34 +02:00
2021-12-15 19:58:09 +01:00
current - > setFocusPolicy ( Qt : : NoFocus ) ;
2021-12-18 00:37:30 +06:00
current - > setFocusProxy ( this ) ;
2021-12-15 20:02:35 +01:00
addWidget ( current . get ( ) ) ;
2021-12-15 21:19:46 +02:00
2021-12-25 15:34:00 +06:00
this - > setStyleSheet ( " background-color: black " ) ;
2021-12-15 21:19:46 +02:00
2022-02-27 14:56:51 +02:00
currentBuf = 0 ;
2022-04-21 16:32:46 +06:00
if ( renderer ! = Renderer : : OpenGL3 & & renderer ! = Renderer : : Vulkan ) {
2022-02-27 14:56:51 +02:00
imagebufs = rendererWindow - > getBuffers ( ) ;
endblit ( ) ;
emit rendererChanged ( ) ;
}
2021-12-15 19:58:09 +01:00
}
2021-12-04 21:33:04 +01:00
// called from blitter thread
2022-02-27 14:56:51 +02:00
void
RendererStack : : blit ( int x , int y , int w , int h )
2021-11-30 16:26:49 +06:00
{
2022-03-05 02:57:57 +02:00
if ( ( w < = 0 ) | | ( h < = 0 ) | | ( w > 2048 ) | | ( h > 2048 ) | | ( buffer32 = = NULL ) | | imagebufs . empty ( ) | | std : : get < std : : atomic_flag * > ( imagebufs [ currentBuf ] ) - > test_and_set ( ) ) {
2021-11-30 16:26:49 +06:00
video_blit_complete ( ) ;
2021-12-04 21:33:04 +01:00
return ;
}
sx = x ;
sy = y ;
sw = this - > w = w ;
2022-02-27 14:56:51 +02:00
sh = this - > h = h ;
uint8_t * imagebits = std : : get < uint8_t * > ( imagebufs [ currentBuf ] ) ;
for ( int y1 = y ; y1 < ( y + h ) ; y1 + + ) {
2022-04-21 16:32:46 +06:00
auto scanline = imagebits + ( y1 * rendererWindow - > getBytesPerRow ( ) ) + ( x * 4 ) ;
2022-01-14 12:39:49 +06:00
video_copy ( scanline , & ( buffer32 - > line [ y1 ] [ x ] ) , w * 4 ) ;
}
2021-12-04 21:33:04 +01:00
2022-02-27 14:56:51 +02:00
if ( screenshots ) {
video_screenshot ( ( uint32_t * ) imagebits , x , y , 2048 ) ;
2021-12-04 21:33:04 +01:00
}
video_blit_complete ( ) ;
2022-01-15 21:45:34 +02:00
emit blitToRenderer ( currentBuf , sx , sy , sw , sh ) ;
currentBuf = ( currentBuf + 1 ) % imagebufs . size ( ) ;
2021-12-04 21:33:04 +01:00
}