Update for v2.6.0

This commit is contained in:
Milan Nikolic 2024-10-09 16:39:46 +02:00
parent 101b504fdd
commit 234b811bd2
No known key found for this signature in database
GPG key ID: 9229D0EAA3AA4E75
13 changed files with 249 additions and 169 deletions

View file

@ -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))

View file

@ -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)),

View file

@ -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"

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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:

View file

@ -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)),

View file

@ -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:

View file

@ -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: