mirror of
https://github.com/mupen64plus/mupen64plus-ui-python.git
synced 2025-04-02 10:51:53 -04:00
Update for v2.6.0
This commit is contained in:
parent
101b504fdd
commit
234b811bd2
13 changed files with 249 additions and 169 deletions
|
@ -36,7 +36,7 @@ except ImportError as err:
|
|||
sys.exit(1)
|
||||
|
||||
try:
|
||||
from m64py.core.defs import FRONTEND_VERSION, LOGO
|
||||
from m64py.core.defs import FRONTEND_VERSION
|
||||
except ImportError as err:
|
||||
sys.stderr.write("Can't import m64py modules%sError:%s%s" % (
|
||||
os.linesep, str(err), os.linesep))
|
||||
|
@ -51,8 +51,6 @@ def handle_exception(exc_type, exc_value, exc_traceback):
|
|||
sys.excepthook = handle_exception
|
||||
|
||||
if __name__ == "__main__":
|
||||
QApplication.setAttribute(Qt.AA_X11InitThreads)
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
from m64py.opts import opts, args
|
||||
|
||||
|
@ -61,8 +59,8 @@ if __name__ == "__main__":
|
|||
if translator.load(":i18n/m64py_" + locale, ":/"):
|
||||
app.installTranslator(translator)
|
||||
|
||||
sys.stderr.write("%s\nM64Py - A frontend for Mupen64Plus version %s\n\n" % (
|
||||
LOGO, FRONTEND_VERSION))
|
||||
sys.stderr.write("M64Py - A frontend for Mupen64Plus version %s\n\n" % (
|
||||
FRONTEND_VERSION))
|
||||
|
||||
from m64py.frontend.mainwindow import MainWindow
|
||||
window = MainWindow((opts, args))
|
||||
|
|
|
@ -16,10 +16,8 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
import ctypes as C
|
||||
import subprocess
|
||||
import binascii
|
||||
import ctypes as C
|
||||
|
||||
from m64py.core.defs import *
|
||||
from m64py.core.config import Config
|
||||
|
@ -71,8 +69,8 @@ class Core:
|
|||
self.plugins = {}
|
||||
self.rom_type = None
|
||||
self.rom_length = None
|
||||
self.rom_header = m64p_rom_header()
|
||||
self.rom_settings = m64p_rom_settings()
|
||||
self.rom_header = M64pRomHeader()
|
||||
self.rom_settings = M64pRomSettings()
|
||||
self.core_path = ""
|
||||
self.core_name = "Mupen64Plus Core"
|
||||
self.core_version = "Unknown"
|
||||
|
@ -127,6 +125,13 @@ class Core:
|
|||
os.path.basename(self.core_path),
|
||||
version_split(config_ver),
|
||||
version_split(CONFIG_API_VERSION)))
|
||||
if vidext_ver & 0xffff0000 != VIDEXT_API_VERSION & 0xffff0000:
|
||||
raise Exception(
|
||||
"emulator core '%s' is incompatible, "
|
||||
"vidext API major version %s doesn't match application: (%s)" % (
|
||||
os.path.basename(self.core_path),
|
||||
version_split(config_ver),
|
||||
version_split(CONFIG_API_VERSION)))
|
||||
|
||||
self.core_name = plugin_name
|
||||
self.core_version = plugin_version
|
||||
|
@ -291,7 +296,7 @@ class Core:
|
|||
if rval != M64ERR_SUCCESS:
|
||||
log.debug("detach_plugins()")
|
||||
log.warn(self.error_message(rval))
|
||||
log.warn("core failed to dettach %s plugin." % (
|
||||
log.warn("core failed to detach %s plugin." % (
|
||||
plugin_name))
|
||||
|
||||
def rom_open(self, romfile):
|
||||
|
@ -478,7 +483,7 @@ class Core:
|
|||
def get_rom_settings(self, crc1, crc2):
|
||||
"""Searches through the data in the ini file for given crc hashes,
|
||||
if found, fills in the RomSettings structure with the data."""
|
||||
rom_settings = m64p_rom_settings()
|
||||
rom_settings = M64pRomSettings()
|
||||
rval = self.m64p.CoreGetRomSettings(
|
||||
C.byref(rom_settings),
|
||||
C.c_int(C.sizeof(rom_settings)),
|
||||
|
|
|
@ -20,9 +20,10 @@ from m64py.platform import DLL_EXT
|
|||
|
||||
CORE_NAME = "mupen64plus"
|
||||
CORE_API_VERSION = 0x20001
|
||||
CONFIG_API_VERSION = 0x20000
|
||||
MINIMUM_CORE_VERSION = 0x016300
|
||||
FRONTEND_VERSION = "0.2.5"
|
||||
CONFIG_API_VERSION = 0x20302
|
||||
VIDEXT_API_VERSION = 0x030300
|
||||
MINIMUM_CORE_VERSION = 0x020600
|
||||
FRONTEND_VERSION = "0.2.6"
|
||||
|
||||
SIZE_1X = (320, 240)
|
||||
SIZE_2X = (640, 480)
|
||||
|
@ -69,6 +70,8 @@ M64VIDEO_NONE = 1
|
|||
M64VIDEO_WINDOWED = 2
|
||||
M64VIDEO_FULLSCREEN = 3
|
||||
|
||||
M64VIDEOFLAG_SUPPORT_RESIZING = 1
|
||||
|
||||
M64CORE_EMU_STATE = 1
|
||||
M64CORE_VIDEO_MODE = 2
|
||||
M64CORE_SAVESTATE_SLOT = 3
|
||||
|
@ -80,6 +83,7 @@ M64CORE_AUDIO_MUTE = 8
|
|||
M64CORE_INPUT_GAMESHARK = 9
|
||||
M64CORE_STATE_LOADCOMPLETE = 10
|
||||
M64CORE_STATE_SAVECOMPLETE = 11
|
||||
M64CORE_SCREENSHOT_CAPTURED = 12
|
||||
|
||||
M64CMD_NOP = 0
|
||||
M64CMD_ROM_OPEN = 1
|
||||
|
@ -102,6 +106,15 @@ M64CMD_CORE_STATE_SET = 17
|
|||
M64CMD_READ_SCREEN = 18
|
||||
M64CMD_RESET = 19
|
||||
M64CMD_ADVANCE_FRAME = 20
|
||||
M64CMD_SET_MEDIA_LOADER = 21
|
||||
M64CMD_NETPLAY_INIT = 22
|
||||
M64CMD_NETPLAY_CONTROL_PLAYER = 23
|
||||
M64CMD_NETPLAY_GET_VERSION = 24
|
||||
M64CMD_NETPLAY_CLOSE = 25
|
||||
M64CMD_PIF_OPEN = 26
|
||||
M64CMD_ROM_SET_SETTINGS = 27
|
||||
M64CMD_DISK_OPEN = 28
|
||||
M64CMD_DISK_CLOSE = 29
|
||||
|
||||
M64P_GL_DOUBLEBUFFER = 1
|
||||
M64P_GL_BUFFER_SIZE = 2
|
||||
|
@ -117,6 +130,10 @@ M64P_GL_CONTEXT_MAJOR_VERSION = 11
|
|||
M64P_GL_CONTEXT_MINOR_VERSION = 12
|
||||
M64P_GL_CONTEXT_PROFILE_MASK = 13
|
||||
|
||||
M64P_GL_CONTEXT_PROFILE_CORE = 0
|
||||
M64P_GL_CONTEXT_PROFILE_COMPATIBILITY = 1
|
||||
M64P_GL_CONTEXT_PROFILE_ES = 2
|
||||
|
||||
M64TYPE_INT = 1
|
||||
M64TYPE_FLOAT = 2
|
||||
M64TYPE_BOOL = 3
|
||||
|
@ -166,27 +183,28 @@ m64p_error = C.c_int
|
|||
m64p_GLattr = C.c_int
|
||||
|
||||
|
||||
class m64p_rom_header(C.Structure):
|
||||
class M64pRomHeader(C.Structure):
|
||||
_fields_ = [
|
||||
('init_PI_BSB_DOM1_LAT_REG', C.c_ubyte),
|
||||
('init_PI_BSB_DOM1_PGS_REG', C.c_ubyte),
|
||||
('init_PI_BSB_DOM1_PWD_REG', C.c_ubyte),
|
||||
('init_PI_BSB_DOM1_PGS_REG2', C.c_ubyte),
|
||||
('ClockRate', C.c_uint),
|
||||
('PC', C.c_uint),
|
||||
('Release', C.c_uint),
|
||||
('CRC1', C.c_uint),
|
||||
('CRC2', C.c_uint),
|
||||
('Unknown', C.c_uint * 2),
|
||||
('init_PI_BSB_DOM1_LAT_REG', C.c_uint8),
|
||||
('init_PI_BSB_DOM1_PGS_REG', C.c_uint8),
|
||||
('init_PI_BSB_DOM1_PWD_REG', C.c_uint8),
|
||||
('init_PI_BSB_DOM1_PGS_REG2', C.c_uint8),
|
||||
('ClockRate', C.c_uint32),
|
||||
('PC', C.c_uint32),
|
||||
('Release', C.c_uint32),
|
||||
('CRC1', C.c_uint32),
|
||||
('CRC2', C.c_uint32),
|
||||
('Unknown', C.c_uint32 * 2),
|
||||
('Name', C.c_char * 20),
|
||||
('unknown', C.c_uint),
|
||||
('Manufacturer_ID', C.c_uint),
|
||||
('Cartridge_ID', C.c_ushort),
|
||||
('Country_code', C.c_ushort)
|
||||
('unknown', C.c_uint32),
|
||||
('Manufacturer_ID', C.c_uint32),
|
||||
('Cartridge_ID', C.c_uint16),
|
||||
('Country_code', C.c_uint8),
|
||||
('Version', C.c_uint8)
|
||||
]
|
||||
|
||||
|
||||
class m64p_rom_settings(C.Structure):
|
||||
class M64pRomSettings(C.Structure):
|
||||
_fields_ = [
|
||||
('goodname', C.c_char * 256),
|
||||
('MD5', C.c_char * 33),
|
||||
|
@ -196,18 +214,22 @@ class m64p_rom_settings(C.Structure):
|
|||
('rumble', C.c_ubyte),
|
||||
('transferpak', C.c_ubyte),
|
||||
('mempak', C.c_ubyte),
|
||||
('biopak', C.c_ubyte)
|
||||
('biopak', C.c_ubyte),
|
||||
('disableextramem', C.c_ubyte),
|
||||
('countperop', C.c_uint),
|
||||
('sidmaduration', C.c_uint),
|
||||
('aidmamodifier', C.c_uint)
|
||||
]
|
||||
|
||||
|
||||
class m64p_cheat_code(C.Structure):
|
||||
class M64pCheatCode(C.Structure):
|
||||
_fields_ = [
|
||||
('address', C.c_uint),
|
||||
('address', C.c_uint32),
|
||||
('value', C.c_int),
|
||||
]
|
||||
|
||||
|
||||
class m64p_2d_size(C.Structure):
|
||||
class M64p2dSize(C.Structure):
|
||||
_fields_ = [
|
||||
('uiWidth', C.c_uint),
|
||||
('uiHeight', C.c_uint)
|
||||
|
@ -215,8 +237,10 @@ class m64p_2d_size(C.Structure):
|
|||
|
||||
FuncInit = C.CFUNCTYPE(m64p_error)
|
||||
FuncQuit = C.CFUNCTYPE(m64p_error)
|
||||
FuncListModes = C.CFUNCTYPE(m64p_error, C.POINTER(m64p_2d_size), C.POINTER(C.c_int))
|
||||
FuncSetMode = C.CFUNCTYPE(m64p_error, C.c_int, C.c_int, C.c_int, C.c_int)
|
||||
FuncListModes = C.CFUNCTYPE(m64p_error, C.POINTER(M64p2dSize), C.POINTER(C.c_int))
|
||||
FuncListRates = C.CFUNCTYPE(m64p_error, M64p2dSize, C.POINTER(C.c_int), C.POINTER(C.c_int))
|
||||
FuncSetMode = C.CFUNCTYPE(m64p_error, C.c_int, C.c_int, C.c_int, C.c_int, C.c_int)
|
||||
FuncSetModeWithRate = C.CFUNCTYPE(m64p_error, C.c_int, C.c_int, C.c_int, C.c_int, C.c_int, C.c_int)
|
||||
FuncGLGetProc = C.CFUNCTYPE(C.c_void_p, C.c_char_p)
|
||||
FuncGLSetAttr = C.CFUNCTYPE(m64p_error, m64p_GLattr, C.c_int)
|
||||
FuncGLGetAttr = C.CFUNCTYPE(m64p_error, m64p_GLattr, C.POINTER(C.c_int))
|
||||
|
@ -224,16 +248,21 @@ FuncGLSwapBuf = C.CFUNCTYPE(m64p_error)
|
|||
FuncSetCaption = C.CFUNCTYPE(m64p_error, C.c_char_p)
|
||||
FuncToggleFS = C.CFUNCTYPE(m64p_error)
|
||||
FuncResizeWindow = C.CFUNCTYPE(m64p_error, C.c_int, C.c_int)
|
||||
FuncGLGetDefaultFramebuffer = C.CFUNCTYPE(C.c_uint)
|
||||
FuncGLGetDefaultFramebuffer = C.CFUNCTYPE(C.c_uint32)
|
||||
FuncInitWithRenderMode = C.CFUNCTYPE(m64p_error, C.c_int)
|
||||
FuncVKGetSurface = C.CFUNCTYPE(m64p_error, C.POINTER(C.c_void_p), C.c_void_p)
|
||||
FuncVKGetInstanceExtensions = C.CFUNCTYPE(m64p_error, C.POINTER(C.c_char_p), C.POINTER(C.c_uint32))
|
||||
|
||||
|
||||
class m64p_video_extension_functions(C.Structure):
|
||||
class M64pVideoExtensionFunctions(C.Structure):
|
||||
_fields_ = [
|
||||
('Functions', C.c_uint),
|
||||
('VidExtFuncInit', FuncInit),
|
||||
('VidExtFuncQuit', FuncQuit),
|
||||
('VidExtFuncListModes', FuncListModes),
|
||||
('VidExtFuncListRates', FuncListRates),
|
||||
('VidExtFuncSetMode', FuncSetMode),
|
||||
('VidExtFuncSetModeWithRate', FuncSetModeWithRate),
|
||||
('VidExtFuncGLGetProc', FuncGLGetProc),
|
||||
('VidExtFuncGLSetAttr', FuncGLSetAttr),
|
||||
('VidExtFuncGLGetAttr', FuncGLGetAttr),
|
||||
|
@ -241,12 +270,8 @@ class m64p_video_extension_functions(C.Structure):
|
|||
('VidExtFuncSetCaption', FuncSetCaption),
|
||||
('VidExtFuncToggleFS', FuncToggleFS),
|
||||
('VidExtFuncResizeWindow', FuncResizeWindow),
|
||||
('VidExtFuncGLGetDefaultFramebuffer', FuncGLGetDefaultFramebuffer)
|
||||
('VidExtFuncGLGetDefaultFramebuffer', FuncGLGetDefaultFramebuffer),
|
||||
('VidExtFuncInitWithRenderMode', FuncInitWithRenderMode),
|
||||
('VidExtFuncVKGetSurface', FuncVKGetSurface),
|
||||
('VidExtFuncVKGetInstanceExtensions', FuncVKGetInstanceExtensions)
|
||||
]
|
||||
|
||||
LOGO = " __ __ __ _ _ ____ _ \n"
|
||||
LOGO += "| \/ |_ _ _ __ ___ _ __ / /_ | || | | _ \| |_ _ ___ \n"
|
||||
LOGO += "| |\/| | | | | '_ \ / _ \ '_ \| '_ \| || |_| |_) | | | | / __| \n"
|
||||
LOGO += "| | | | |_| | |_) | __/ | | | (_) |__ _| __/| | |_| \__ \ \n"
|
||||
LOGO += "|_| |_|\__,_| .__/ \___|_| |_|\___/ |_| |_| |_|\__,_|___/ \n"
|
||||
LOGO += " |_| \n"
|
||||
|
|
|
@ -20,10 +20,11 @@ try:
|
|||
# nvidia hack
|
||||
from OpenGL import GL
|
||||
glimport = True
|
||||
except:
|
||||
except ModuleNotFoundError:
|
||||
glimport = False
|
||||
|
||||
from PyQt5.QtOpenGL import QGLFormat
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from sdl2 import SDL_WasInit, SDL_InitSubSystem, SDL_QuitSubSystem, SDL_INIT_VIDEO
|
||||
from sdl2 import SDL_GetNumDisplayModes, SDL_DisplayMode, SDL_GetDisplayMode
|
||||
|
@ -35,18 +36,20 @@ try:
|
|||
if not SDL_WasInit(SDL_INIT_VIDEO):
|
||||
SDL_InitSubSystem(SDL_INIT_VIDEO)
|
||||
MODES = []
|
||||
RATES = []
|
||||
display = SDL_DisplayMode()
|
||||
for mode in range(SDL_GetNumDisplayModes(0)):
|
||||
ret = SDL_GetDisplayMode(0, mode, ctypes.byref(display))
|
||||
for m in range(SDL_GetNumDisplayModes(0)):
|
||||
ret = SDL_GetDisplayMode(0, m, ctypes.byref(display))
|
||||
if (display.w, display.h) not in MODES:
|
||||
MODES.append((display.w, display.h))
|
||||
RATES.append(display.refresh_rate)
|
||||
if SDL_WasInit(SDL_INIT_VIDEO):
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO)
|
||||
except Exception as err:
|
||||
log.warn(str(err))
|
||||
|
||||
|
||||
class Video():
|
||||
class Video:
|
||||
"""Mupen64Plus video extension"""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -58,10 +61,10 @@ class Video():
|
|||
self.major = None
|
||||
self.minor = None
|
||||
|
||||
def set_widget(self, parent):
|
||||
def set_widget(self, parent, widget):
|
||||
"""Sets GL widget."""
|
||||
self.parent = parent
|
||||
self.widget = self.parent.glwidget
|
||||
self.widget = widget
|
||||
|
||||
def init(self):
|
||||
"""Initialize GL context."""
|
||||
|
@ -70,16 +73,18 @@ class Video():
|
|||
self.glcontext = self.widget.context()
|
||||
self.glcontext.setFormat(self.glformat)
|
||||
self.glcontext.create()
|
||||
self.parent.vidext_init.emit(self.glcontext)
|
||||
return M64ERR_SUCCESS
|
||||
|
||||
def quit(self):
|
||||
"""Shuts down the video system."""
|
||||
if self.glcontext:
|
||||
self.glcontext.doneCurrent()
|
||||
self.glcontext.moveToThread(QApplication.instance().thread())
|
||||
self.glcontext = None
|
||||
return M64ERR_SUCCESS
|
||||
|
||||
def list_fullscreen_modes(self, size_array, num_sizes):
|
||||
def list_modes(self, size_array, num_sizes):
|
||||
"""Enumerates the available resolutions
|
||||
for fullscreen video modes."""
|
||||
num_sizes.contents.value = len(MODES)
|
||||
|
@ -89,19 +94,31 @@ class Video():
|
|||
size_array[num].uiHeight = height
|
||||
return M64ERR_SUCCESS
|
||||
|
||||
def set_video_mode(self, width, height, bits, mode):
|
||||
def list_rates(self, size_array, num_rates, rates):
|
||||
"""Enumerates the available rates
|
||||
for fullscreen video modes."""
|
||||
num_rates.contents.value = len(RATES)
|
||||
for num, rate in enumerate(RATES):
|
||||
rates[num] = rate
|
||||
return M64ERR_SUCCESS
|
||||
|
||||
def set_mode(self, width, height, bits, mode, flags):
|
||||
"""Creates a rendering window."""
|
||||
self.glcontext.makeCurrent()
|
||||
if self.glcontext.isValid():
|
||||
self.widget.makeCurrent()
|
||||
if self.widget.isValid():
|
||||
if glimport:
|
||||
GL.glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
|
||||
GL.glClearColor(0.0, 0.0, 0.0, 1.0)
|
||||
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
|
||||
self.widget.swapBuffers()
|
||||
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
|
||||
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
|
||||
return M64ERR_SUCCESS
|
||||
else:
|
||||
return M64ERR_SYSTEM_FAIL
|
||||
|
||||
def set_mode_with_rate(self, width, height, rate, bits, mode, flags):
|
||||
"""Creates a rendering window."""
|
||||
return self.set_mode(width, height, bits, mode, flags)
|
||||
|
||||
def set_caption(self, title):
|
||||
"""Sets the caption text of the
|
||||
emulator rendering window. """
|
||||
|
@ -112,7 +129,7 @@ class Video():
|
|||
def toggle_fs(self):
|
||||
"""Toggles between fullscreen and
|
||||
windowed rendering modes. """
|
||||
self.widget.toggle_fs.emit()
|
||||
self.parent.toggle_fs.emit()
|
||||
return M64ERR_SUCCESS
|
||||
|
||||
def gl_get_proc(self, proc):
|
||||
|
@ -128,8 +145,8 @@ class Video():
|
|||
"""Sets OpenGL attributes."""
|
||||
attr_map = {
|
||||
M64P_GL_DOUBLEBUFFER: self.glformat.setDoubleBuffer,
|
||||
M64P_GL_BUFFER_SIZE: self.glformat.setDepthBufferSize,
|
||||
M64P_GL_DEPTH_SIZE: self.glformat.setDepth,
|
||||
M64P_GL_BUFFER_SIZE: None,
|
||||
M64P_GL_DEPTH_SIZE: self.glformat.setDepthBufferSize,
|
||||
M64P_GL_RED_SIZE: self.glformat.setRedBufferSize,
|
||||
M64P_GL_GREEN_SIZE: self.glformat.setGreenBufferSize,
|
||||
M64P_GL_BLUE_SIZE: self.glformat.setBlueBufferSize,
|
||||
|
@ -139,21 +156,25 @@ class Video():
|
|||
M64P_GL_MULTISAMPLESAMPLES: self.glformat.setSamples,
|
||||
M64P_GL_CONTEXT_MAJOR_VERSION: self.set_major,
|
||||
M64P_GL_CONTEXT_MINOR_VERSION: self.set_minor,
|
||||
M64P_GL_CONTEXT_PROFILE_MASK: self.glformat.setProfile
|
||||
M64P_GL_CONTEXT_PROFILE_MASK: self.set_profile
|
||||
}
|
||||
|
||||
set_attr = attr_map[attr]
|
||||
set_attr(value)
|
||||
if set_attr:
|
||||
set_attr(value)
|
||||
|
||||
if attr == M64P_GL_CONTEXT_MAJOR_VERSION or attr == M64P_GL_CONTEXT_MINOR_VERSION:
|
||||
if self.major and self.minor:
|
||||
self.glformat.setVersion(self.major, self.minor)
|
||||
|
||||
return M64ERR_SUCCESS
|
||||
|
||||
def gl_get_attr(self, attr, value):
|
||||
"""Gets OpenGL attributes."""
|
||||
attr_map = {
|
||||
M64P_GL_DOUBLEBUFFER: self.glformat.doubleBuffer,
|
||||
M64P_GL_BUFFER_SIZE: self.glformat.depthBufferSize,
|
||||
M64P_GL_DEPTH_SIZE: self.glformat.depth,
|
||||
M64P_GL_BUFFER_SIZE: None,
|
||||
M64P_GL_DEPTH_SIZE: self.glformat.depthBufferSize,
|
||||
M64P_GL_RED_SIZE: self.glformat.redBufferSize,
|
||||
M64P_GL_GREEN_SIZE: self.glformat.greenBufferSize,
|
||||
M64P_GL_BLUE_SIZE: self.glformat.blueBufferSize,
|
||||
|
@ -163,19 +184,23 @@ class Video():
|
|||
M64P_GL_MULTISAMPLESAMPLES: self.glformat.samples,
|
||||
M64P_GL_CONTEXT_MAJOR_VERSION: self.glformat.majorVersion,
|
||||
M64P_GL_CONTEXT_MINOR_VERSION: self.glformat.minorVersion,
|
||||
M64P_GL_CONTEXT_PROFILE_MASK: self.glformat.profile
|
||||
M64P_GL_CONTEXT_PROFILE_MASK: self.get_profile
|
||||
}
|
||||
|
||||
get_attr = attr_map[attr]
|
||||
new_value = int(get_attr())
|
||||
if new_value == value.contents.value:
|
||||
return M64ERR_SUCCESS
|
||||
else:
|
||||
return M64ERR_SYSTEM_FAIL
|
||||
if get_attr:
|
||||
new_value = int(get_attr())
|
||||
value.contents.value = new_value
|
||||
if new_value != value.contents.value:
|
||||
return M64ERR_SYSTEM_FAIL
|
||||
|
||||
return M64ERR_SUCCESS
|
||||
|
||||
def gl_swap_buf(self):
|
||||
"""Swaps the front/back buffers after
|
||||
rendering an output video frame. """
|
||||
self.widget.swapBuffers()
|
||||
if self.widget.isValid():
|
||||
self.widget.swapBuffers()
|
||||
return M64ERR_SUCCESS
|
||||
|
||||
def resize_window(self, width, height):
|
||||
|
@ -187,19 +212,47 @@ class Video():
|
|||
"""Gets default framebuffer."""
|
||||
return 0
|
||||
|
||||
def init_with_render_mode(self, mode):
|
||||
return self.init()
|
||||
|
||||
def vk_get_surface(self, a, b):
|
||||
return M64ERR_SUCCESS
|
||||
|
||||
def vk_get_instance_extensions(self, a, b):
|
||||
return M64ERR_SUCCESS
|
||||
|
||||
def set_major(self, major):
|
||||
self.major = major
|
||||
|
||||
def set_minor(self, minor):
|
||||
self.minor = minor
|
||||
|
||||
def set_profile(self, value):
|
||||
if value == M64P_GL_CONTEXT_PROFILE_CORE:
|
||||
self.glformat.setProfile(QGLFormat.CoreProfile)
|
||||
elif value == M64P_GL_CONTEXT_PROFILE_COMPATIBILITY:
|
||||
self.glformat.setProfile(QGLFormat.CompatibilityProfile)
|
||||
else:
|
||||
self.glformat.setProfile(QGLFormat.CompatibilityProfile)
|
||||
|
||||
def get_profile(self):
|
||||
profile = self.glformat.profile()
|
||||
if profile == QGLFormat.CoreProfile:
|
||||
return M64P_GL_CONTEXT_PROFILE_CORE
|
||||
elif profile == QGLFormat.CompatibilityProfile:
|
||||
return M64P_GL_CONTEXT_PROFILE_COMPATIBILITY
|
||||
else:
|
||||
return M64P_GL_CONTEXT_PROFILE_COMPATIBILITY
|
||||
|
||||
video = Video()
|
||||
vidext = m64p_video_extension_functions()
|
||||
vidext.Functions = 12
|
||||
vidext = M64pVideoExtensionFunctions()
|
||||
vidext.Functions = 17
|
||||
vidext.VidExtFuncInit = FuncInit(video.init)
|
||||
vidext.VidExtFuncQuit = FuncQuit(video.quit)
|
||||
vidext.VidExtFuncListModes = FuncListModes(video.list_fullscreen_modes)
|
||||
vidext.VidExtFuncSetMode = FuncSetMode(video.set_video_mode)
|
||||
vidext.VidExtFuncListModes = FuncListModes(video.list_modes)
|
||||
vidext.VidExtFuncListRates = FuncListRates(video.list_rates)
|
||||
vidext.VidExtFuncSetMode = FuncSetMode(video.set_mode)
|
||||
vidext.VidExtFuncSetModeWithRate = FuncSetModeWithRate(video.set_mode_with_rate)
|
||||
vidext.VidExtFuncGLGetProc = FuncGLGetProc(video.gl_get_proc)
|
||||
vidext.VidExtFuncGLSetAttr = FuncGLSetAttr(video.gl_set_attr)
|
||||
vidext.VidExtFuncGLGetAttr = FuncGLGetAttr(video.gl_get_attr)
|
||||
|
@ -208,3 +261,6 @@ vidext.VidExtFuncSetCaption = FuncSetCaption(video.set_caption)
|
|||
vidext.VidExtFuncToggleFS = FuncToggleFS(video.toggle_fs)
|
||||
vidext.VidExtFuncResizeWindow = FuncResizeWindow(video.resize_window)
|
||||
vidext.VidExtFuncGLGetDefaultFramebuffer = FuncGLGetDefaultFramebuffer(video.gl_get_default_framebuffer)
|
||||
vidext.VidExtFuncInitWithRenderMode = FuncInitWithRenderMode(video.init_with_render_mode)
|
||||
vidext.VidExtFuncVKGetSurface = FuncVKGetSurface(video.vk_get_surface)
|
||||
vidext.VidExtFuncVKGetInstanceExtensions = FuncVKGetInstanceExtensions(video.vk_get_instance_extensions)
|
||||
|
|
|
@ -18,7 +18,7 @@ import os
|
|||
import re
|
||||
from collections import defaultdict
|
||||
|
||||
from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QListWidgetItem
|
||||
from PyQt5.QtWidgets import QDialog, QTreeWidgetItem, QListWidgetItem, QTreeWidgetItemIterator
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
from m64py.core.defs import *
|
||||
|
@ -127,7 +127,7 @@ class Cheat(QDialog, Ui_CheatDialog):
|
|||
name = "%s\\%s" % (parent.text(column), name)
|
||||
data = item.data(column, Qt.UserRole)
|
||||
if state == Qt.Checked:
|
||||
codes_type = m64p_cheat_code * len(data)
|
||||
codes_type = M64pCheatCode * len(data)
|
||||
codes = codes_type()
|
||||
for num, cheat in enumerate(data):
|
||||
cd, address, value, choices = cheat
|
||||
|
|
|
@ -14,26 +14,17 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, QMargins
|
||||
from PyQt5.QtCore import Qt, QMargins
|
||||
from PyQt5.QtOpenGL import QGLWidget
|
||||
|
||||
from m64py.core.defs import *
|
||||
from m64py.frontend.keymap import QT2SDL2
|
||||
|
||||
|
||||
class GLWidget(QGLWidget):
|
||||
|
||||
toggle_fs = pyqtSignal()
|
||||
|
||||
def __init__(self, parent):
|
||||
def __init__(self, parent=None):
|
||||
QGLWidget.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.worker = parent.worker
|
||||
self.setAttribute(Qt.WA_NativeWindow, True)
|
||||
self.setContentsMargins(QMargins())
|
||||
self.setFocusPolicy(Qt.StrongFocus)
|
||||
self.setFocus(True)
|
||||
self.toggle_fs.connect(self.toggle_fullscreen)
|
||||
|
||||
def showEvent(self, event):
|
||||
self.setFocus(True)
|
||||
|
@ -44,43 +35,3 @@ class GLWidget(QGLWidget):
|
|||
|
||||
def paintEvent(self, event):
|
||||
pass
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
self.toggle_fs.emit()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if self.worker.state == M64EMU_RUNNING:
|
||||
key = event.key()
|
||||
modifiers = event.modifiers()
|
||||
if modifiers & Qt.AltModifier and \
|
||||
(key == Qt.Key_Enter or key == Qt.Key_Return):
|
||||
self.toggle_fs.emit()
|
||||
elif key == Qt.Key_F3:
|
||||
self.worker.save_title()
|
||||
elif key == Qt.Key_F4:
|
||||
self.worker.save_snapshot()
|
||||
else:
|
||||
try:
|
||||
sdl_key = QT2SDL2[key]
|
||||
self.worker.send_sdl_keydown(sdl_key)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def keyReleaseEvent(self, event):
|
||||
if self.worker.state == M64EMU_RUNNING:
|
||||
key = event.key()
|
||||
try:
|
||||
sdl_key = QT2SDL2[key]
|
||||
self.worker.send_sdl_keyup(sdl_key)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def toggle_fullscreen(self):
|
||||
window = self.window()
|
||||
if window.isFullScreen():
|
||||
self.parent.menubar.show()
|
||||
self.parent.statusbar.show()
|
||||
else:
|
||||
self.parent.menubar.hide()
|
||||
self.parent.statusbar.hide()
|
||||
window.setWindowState(window.windowState() ^ Qt.WindowFullScreen)
|
||||
|
|
|
@ -18,9 +18,9 @@ from PyQt5.QtCore import Qt
|
|||
|
||||
from m64py.frontend.keycodes import *
|
||||
|
||||
QT2SDL2 = {}
|
||||
SCANCODE2KEYCODE = {}
|
||||
KEYCODE2SCANCODE = {}
|
||||
QT2SDL2 = dict()
|
||||
SCANCODE2KEYCODE = dict()
|
||||
KEYCODE2SCANCODE = dict()
|
||||
|
||||
QT2SDL2[Qt.Key_A] = SDL_SCANCODE_A
|
||||
QT2SDL2[Qt.Key_B] = SDL_SCANCODE_B
|
||||
|
|
|
@ -35,6 +35,10 @@ class Log:
|
|||
if self.logview:
|
||||
self.logview.msg_written.emit(msg)
|
||||
|
||||
def flush(self):
|
||||
if self.out:
|
||||
self.out.flush()
|
||||
|
||||
|
||||
class LogView(QDialog, Ui_LogView):
|
||||
msg_written = pyqtSignal(str)
|
||||
|
|
|
@ -17,10 +17,11 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
from PyQt5.QtOpenGL import QGLContext
|
||||
from PyQt5.QtGui import QKeySequence, QPixmap
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem
|
||||
from PyQt5.QtWidgets import QAction, QLabel, QFileDialog, QStackedWidget, QActionGroup, QSizePolicy
|
||||
from PyQt5.QtCore import Qt, QTimer, QFileInfo, QEvent, QMargins, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import QAction, QLabel, QFileDialog, QStackedWidget, QActionGroup, QSizePolicy, QDialog
|
||||
from PyQt5.QtCore import Qt, QTimer, QFileInfo, QEvent, QMargins, pyqtSignal, pyqtSlot, QThread
|
||||
|
||||
from m64py.core.defs import *
|
||||
from m64py.frontend.dialogs import *
|
||||
|
@ -34,6 +35,7 @@ from m64py.frontend.settings import Settings
|
|||
from m64py.frontend.glwidget import GLWidget
|
||||
from m64py.ui.mainwindow_ui import Ui_MainWindow
|
||||
from m64py.frontend.recentfiles import RecentFiles
|
||||
from m64py.frontend.keymap import QT2SDL2
|
||||
|
||||
|
||||
class MainWindow(QMainWindow, Ui_MainWindow):
|
||||
|
@ -48,6 +50,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
save_image = pyqtSignal(bool)
|
||||
info_dialog = pyqtSignal(str)
|
||||
archive_dialog = pyqtSignal(list)
|
||||
vidext_init = pyqtSignal(QGLContext)
|
||||
toggle_fs = pyqtSignal()
|
||||
|
||||
def __init__(self, optparse):
|
||||
"""Constructor"""
|
||||
|
@ -55,6 +59,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
self.setupUi(self)
|
||||
self.opts, self.args = optparse
|
||||
|
||||
self._initialize_attempt = 0
|
||||
|
||||
logview.setParent(self)
|
||||
logview.setWindowFlags(Qt.Dialog)
|
||||
|
||||
|
@ -71,7 +77,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
SIZE_3X: self.action3X}
|
||||
|
||||
self.slots = {}
|
||||
self.view = None
|
||||
self.stack = None
|
||||
self.glwidget = None
|
||||
self.cheats = None
|
||||
|
@ -97,16 +102,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
if event.type() == QEvent.WindowStateChange:
|
||||
if event.oldState() == Qt.WindowMaximized:
|
||||
self.maximized = False
|
||||
elif event.oldState() == Qt.WindowNoState and \
|
||||
self.windowState() == Qt.WindowMaximized:
|
||||
elif event.oldState() == Qt.WindowNoState and self.windowState() == Qt.WindowMaximized:
|
||||
self.maximized = True
|
||||
|
||||
def resizeEvent(self, event):
|
||||
event.ignore()
|
||||
size = event.size()
|
||||
width, height = size.width(), size.height()
|
||||
if self.widgets_height:
|
||||
width, height = size.width(), size.height() - self.widgets_height
|
||||
width, height = size.width(), size.height()
|
||||
self.window_size_triggered((width, height))
|
||||
else:
|
||||
width, height = size.width(), size.height()
|
||||
|
@ -122,11 +125,40 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
self.create_size_actions()
|
||||
self.center_widget()
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
self.toggle_fs.emit()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if self.worker.state == M64EMU_RUNNING:
|
||||
key = event.key()
|
||||
modifiers = event.modifiers()
|
||||
if modifiers & Qt.AltModifier and \
|
||||
(key == Qt.Key_Enter or key == Qt.Key_Return):
|
||||
self.toggle_fs.emit()
|
||||
elif key == Qt.Key_F3:
|
||||
self.worker.save_title()
|
||||
elif key == Qt.Key_F4:
|
||||
self.worker.save_snapshot()
|
||||
else:
|
||||
try:
|
||||
sdl_key = QT2SDL2[key]
|
||||
self.worker.send_sdl_keydown(sdl_key)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def keyReleaseEvent(self, event):
|
||||
if self.worker.state == M64EMU_RUNNING:
|
||||
key = event.key()
|
||||
try:
|
||||
sdl_key = QT2SDL2[key]
|
||||
self.worker.send_sdl_keyup(sdl_key)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def window_size_triggered(self, size):
|
||||
window_width, window_height = size
|
||||
if self.vidext and self.worker.core.get_handle():
|
||||
fullscreen = self.window().isFullScreen()
|
||||
game_height = window_height
|
||||
game_height = window_height - self.widgets_height
|
||||
game_width = window_width
|
||||
|
||||
if not sys.platform == "win32":
|
||||
|
@ -137,18 +169,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
self.worker.core.config.set_parameter("ScreenWidth", game_width)
|
||||
self.worker.core.config.set_parameter("ScreenHeight", game_height)
|
||||
|
||||
if not fullscreen:
|
||||
video_size = (game_width << 16) + game_height
|
||||
else:
|
||||
video_size = (game_width << 16) + (game_height + self.widgets_height)
|
||||
video_size = (game_width << 16) + game_height
|
||||
if self.worker.state in (M64EMU_RUNNING, M64EMU_PAUSED):
|
||||
self.worker.core_state_set(M64CORE_VIDEO_SIZE, video_size)
|
||||
|
||||
self.glwidget.move(int((window_width - game_width) / 2), 0)
|
||||
|
||||
self.set_sizes((window_width, window_height))
|
||||
self.settings.qset.setValue("size", (window_width, window_height))
|
||||
self.resize(window_width, window_height + self.widgets_height)
|
||||
self.set_sizes((window_width, window_height - self.widgets_height))
|
||||
self.settings.qset.setValue("size", (window_width, window_height - self.widgets_height))
|
||||
self.resize(window_width, window_height)
|
||||
|
||||
def set_sizes(self, size):
|
||||
"""Sets 'Window Size' radio buttons on resize event."""
|
||||
|
@ -194,23 +223,25 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
self.save_image.connect(self.on_save_image)
|
||||
self.info_dialog.connect(self.on_info_dialog)
|
||||
self.archive_dialog.connect(self.on_archive_dialog)
|
||||
self.toggle_fs.connect(self.on_toggle_fs)
|
||||
self.vidext_init.connect(self.on_vidext_init)
|
||||
|
||||
def create_widgets(self):
|
||||
"""Creates central widgets."""
|
||||
self.stack = QStackedWidget(self)
|
||||
self.stack = QStackedWidget(None)
|
||||
palette = self.stack.palette()
|
||||
palette.setColor(self.stack.backgroundRole(), Qt.black)
|
||||
self.stack.setPalette(palette)
|
||||
self.stack.setAutoFillBackground(True)
|
||||
|
||||
self.view = View(self)
|
||||
self.glwidget = GLWidget(self)
|
||||
self.worker.video.set_widget(self)
|
||||
|
||||
|
||||
self.worker.video.set_widget(self, self.glwidget)
|
||||
self.setCentralWidget(self.stack)
|
||||
self.stack.addWidget(self.view)
|
||||
|
||||
self.stack.addWidget(View(self))
|
||||
self.stack.addWidget(self.glwidget)
|
||||
self.stack.setCurrentWidget(self.view)
|
||||
self.stack.setCurrentIndex(0)
|
||||
|
||||
def create_state_slots(self):
|
||||
"""Creates state slot actions."""
|
||||
|
@ -241,6 +272,18 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
action.setToolTip("%sx%s" % (width, height))
|
||||
action.triggered.connect(lambda t, wi=w, he=h: self.resize(wi, he))
|
||||
|
||||
def on_vidext_init(self, context):
|
||||
context.moveToThread(self.worker)
|
||||
|
||||
def on_toggle_fs(self):
|
||||
if self.isFullScreen():
|
||||
self.menubar.show()
|
||||
self.statusbar.show()
|
||||
else:
|
||||
self.menubar.hide()
|
||||
self.statusbar.hide()
|
||||
self.setWindowState(self.windowState() ^ Qt.WindowFullScreen)
|
||||
|
||||
def on_file_open(self, filepath=None, filename=None):
|
||||
"""Opens ROM file."""
|
||||
if not filepath:
|
||||
|
@ -325,19 +368,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
|
||||
def on_rom_opened(self):
|
||||
if self.vidext:
|
||||
self.stack.setCurrentWidget(self.glwidget)
|
||||
self.glwidget.setFocus(True)
|
||||
self.stack.setCurrentIndex(1)
|
||||
if not self.cheats:
|
||||
self.cheats = Cheat(self)
|
||||
self.update_status(self.worker.core.rom_settings.goodname.decode())
|
||||
"""Use QTimer to wait for core initialization before toggling UI actions"""
|
||||
self._initialize_attempt = 0
|
||||
QTimer.singleShot(1000, self.wait_for_initialize)
|
||||
|
||||
def on_rom_closed(self):
|
||||
if self.vidext and self.isFullScreen():
|
||||
self.glwidget.toggle_fs.emit()
|
||||
self.stack.setCurrentWidget(self.view)
|
||||
self.toggle_fs.emit()
|
||||
self.stack.setCurrentIndex(0)
|
||||
self.actionMute.setChecked(False)
|
||||
self.actionPause.setChecked(False)
|
||||
self.actionLimitFPS.setChecked(True)
|
||||
|
@ -505,7 +546,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||
class View(QGraphicsView):
|
||||
def __init__(self, parent=None):
|
||||
QGraphicsView.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.setContentsMargins(QMargins())
|
||||
self.setStyleSheet("QGraphicsView {border:0px solid;margin:0px;}")
|
||||
self.setResizeAnchor(QGraphicsView.AnchorViewCenter)
|
||||
|
|
|
@ -140,6 +140,7 @@ class Plugin(QDialog, Ui_PluginDialog):
|
|||
def save_items(self):
|
||||
for param_name, item in self.widgets.items():
|
||||
widget, widget_class, opts = item
|
||||
param_value = None
|
||||
if widget_class == QLineEdit:
|
||||
param_value = widget.text().encode()
|
||||
elif widget_class == QSpinBox:
|
||||
|
|
|
@ -21,7 +21,7 @@ import fnmatch
|
|||
from PyQt5.QtCore import QThread
|
||||
|
||||
from m64py.utils import sl
|
||||
from m64py.core.defs import m64p_rom_header
|
||||
from m64py.core.defs import M64pRomHeader
|
||||
from m64py.frontend.log import log
|
||||
from m64py.archive import Archive, EXT_FILTER
|
||||
|
||||
|
@ -55,7 +55,7 @@ class ROMReader(QThread):
|
|||
return files
|
||||
|
||||
def get_rom_crc(self, archive, fname):
|
||||
rom_header = m64p_rom_header()
|
||||
rom_header = M64pRomHeader()
|
||||
ctypes.memmove(
|
||||
ctypes.byref(rom_header),
|
||||
archive.read(fname, ctypes.sizeof(rom_header)),
|
||||
|
|
|
@ -105,7 +105,6 @@ class Worker(QThread):
|
|||
self.core.core_startup(
|
||||
str(self.library_path), self.parent.vidext)
|
||||
|
||||
|
||||
def core_shutdown(self):
|
||||
"""Shutdowns core library."""
|
||||
if self.core.get_handle():
|
||||
|
@ -335,6 +334,7 @@ class Worker(QThread):
|
|||
"""Toggles actions state."""
|
||||
self.state = self.core_state_query(M64CORE_EMU_STATE)
|
||||
cheat = bool(self.parent.cheats.cheats) if self.parent.cheats else False
|
||||
(load, pause, action, cheats) = True, False, False, False
|
||||
if self.state == M64EMU_STOPPED:
|
||||
(load, pause, action, cheats) = True, False, False, False
|
||||
elif self.state == M64EMU_PAUSED:
|
||||
|
|
|
@ -18,8 +18,8 @@ import re
|
|||
|
||||
|
||||
def which(prog):
|
||||
def is_exe(fpath):
|
||||
return os.path.exists(fpath) and os.access(fpath, os.X_OK)
|
||||
def is_exe(fp):
|
||||
return os.path.exists(fp) and os.access(fp, os.X_OK)
|
||||
fpath, fname = os.path.split(prog)
|
||||
if fpath:
|
||||
if is_exe(prog):
|
||||
|
@ -36,7 +36,7 @@ def version_split(ver):
|
|||
return "%d.%d.%d" % (
|
||||
((ver >> 16) & 0xffff),
|
||||
((ver >> 8) & 0xff),
|
||||
((ver & 0xff)))
|
||||
(ver & 0xff))
|
||||
|
||||
|
||||
def sl(mot):
|
||||
|
@ -71,7 +71,7 @@ def format_options(param_help):
|
|||
if not param_help:
|
||||
return None
|
||||
items = re.findall(
|
||||
"(\d+|[\d,-]+)\s?=\s?([\w/ %-]+)", param_help)
|
||||
r"(\d+|[\d,-]+)\s?=\s?([\w/ %-]+)", param_help)
|
||||
for item in items:
|
||||
key, value = item
|
||||
if '-' in key[1:] or ',' in key:
|
||||
|
|
Loading…
Add table
Reference in a new issue