This repository has been archived on 2025-07-12. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
shroom_of_doom/lib/system.py
2025-02-26 14:40:25 -05:00

253 lines
11 KiB
Python
Executable File

#!~/.pyenv/versions/3.11.6/bin/python
#
# Copyright (c) 2024 Cutieguwu | Olivia Brooks
#
# -*- coding:utf-8 -*-
# @Title: System utilities.
# @Author: Cutieguwu | Olivia Brooks
# @Email: owen.brooks77@gmail.com | obroo2@ocdsb.ca
# @Description: Utilities for a PyGame game.
#
# @Script: system.py
# @Date Created: 17 Apr, 2024
# @Last Modified: 14 Jun, 2024
# @Last Modified by: Cutieguwu | Olivia Brooks
# ----------------------------------------------------------
"""
Modules that are loaded will always be loaded globally.
Some will only be loaded based on conditions, but made global.
Thus, they can be checked for in globals() and be easily accessible anywhere.
Even if a module were loaded and unloaded, it would remain in cache, so unloading it is not really worth it.
Imports will be loaded as the program needs them, thus reducing the initial memory consumption.
"""
# Although they won't be reloaded and the imports will be skipped if called again,
# avoid wasting processing time by keeping out of import_dependencies()
from pkg_resources import working_set
from subprocess import check_call, CalledProcessError
from sys import executable
def install_dependencies(dependencies:dict):
"""
Tries to install and import any missing dependencies from list.
"""
# Adapted from Serge Stroobandt on https://stackoverflow.com/questions/46419607/how-to-automatically-install-required-packages-from-a-python-script-as-necessary
libsInst = {
pkg.key for pkg in working_set # Find required and installed libraries.
}
libsMiss = dependencies - libsInst # Remove the already installed libraries from the dependency list.
try:
if len(libsMiss) != 0:
check_call([executable, "-m", "pip", "install", "--upgrade", "pip"]) # Ensure pip is up-to-date.
for library in libsMiss:
check_call([executable, "-m", "pip", "install", library]) # Install all missing libraries.
except CalledProcessError:
print("Error | CANNOT FIND OR INSTALL DEPENDENCY:")
print(library)
raise SystemExit
install_dependencies({"icecream", "pygame"})
from icecream import ic
from pygame.version import vernum
from pygame.display import get_driver
from os import path
from sys import version as pyver
from platform import uname
from json import load as jload
DISPLAY = {}
PLATFORM = {"libraries": {}}
PLATFORM["kernel"] = uname().system # `system` is a variable of uname()
PLATFORM["libraries"]["pygame"] = vernum
# Python version cleanup.
versionStr = ""
for c in pyver:
if c == " ":
break
else:
versionStr = versionStr + c
versionTuple = tuple(versionStr.split("."))
versionTuple = (int(versionTuple[0]), int(versionTuple[1]), int(versionTuple[2]))
PLATFORM["libraries"]["python"] = versionTuple
# GCC version cleanup.
versionStr = ""
startFlag = False
for c in pyver:
if startFlag:
if c not in ["G", "C", "]"]:
versionStr = versionStr + c
elif c == "[":
startFlag = True
PLATFORM["libraries"]["GCC"] = versionStr
del pyver, versionStr, startFlag, versionTuple
# Fetch compatability settings.
DIRWORKING = path.dirname(__file__) + "/.."
with open(DIRWORKING + "/config/features.json") as f:
FEATURES = jload(f)
# All features in file default to is_active: false
# Enable supported features.
for feature in FEATURES.items():
is_active = False
for lib in feature[1]["libs"].items():
if PLATFORM["libraries"][lib[0]] > tuple(lib[1]["ver_implemented"]):
is_active = True
FEATURES[feature[0]]["is_active"] = is_active
class Compat():
def linux():
# Adapted from Glyph, Eevee on https://stackoverflow.com/questions/1225057/how-can-i-determine-the-monitor-refresh-rate
"""
if "environ" not in globals(): # Import if not already.
global environ
from os import environ
try:
if environ["WAYLAND_DISPLAY"] != "" and globals["XDG_SESSION_TYPE"] != "x11": # Check for environment variable present on Wayland systems.
print("Sorry, you are running Wayland. Configuring FPS on Wayland is not yet supported.")
except KeyError: # Wayland keys won't exist on X11 systems (mostly).
install_dependencies({"Xlib"})
global display
from Xlib import display
global randr
from Xlib.ext import randr
displayGlobal = display.Display() # Get every display thing. Maybe a WM object?
default_screen = displayGlobal.get_default_screen() # Find default display for some reason.
info = displayGlobal.screen(default_screen) # Use this as a point to get information about all screens.
displayConfigs = randr.get_screen_resources(info.root) # Get every screen's possible configurations.
configsActive = set() # Create an empty set to add configurations to.
for config in displayConfigs.crtcs: # For every display configuration, determine if it's active and add its identifier to the configurations in use.
crtc_info = randr.get_crtc_info(info.root, config, displayConfigs.config_timestamp)
if crtc_info.mode:
configsActive.add(crtc_info.mode)
for config in displayConfigs.modes:
if config.id in configsActive: # For every active display mode, figure out its framerate.
framerate = config.dot_clock / (config.h_total * config.v_total)
"""
if get_driver() == "wayland":
ic("Sorry, you are running Wayland. Configuring FPS on Wayland is not yet supported.")
elif get_driver() == "x11":
install_dependencies({"Xlib"})
global display
from Xlib import display
global randr
from Xlib.ext import randr
displayGlobal = display.Display() # Get every display thing. Maybe a WM object?
default_screen = displayGlobal.get_default_screen() # Find default display for some reason.
info = displayGlobal.screen(default_screen) # Use this as a point to get information about all screens.
displayConfigs = randr.get_screen_resources(info.root) # Get every screen's possible configurations.
configsActive = set() # Create an empty set to add configurations to.
for config in displayConfigs.crtcs: # For every display configuration, determine if it's active and add its identifier to the configurations in use.
crtc_info = randr.get_crtc_info(info.root, config, displayConfigs.config_timestamp)
if crtc_info.mode:
configsActive.add(crtc_info.mode)
framerate = 30
# With multiple displays, get the highest framerate that any one monitor can do.
for config in displayConfigs.modes:
if config.id in configsActive: # For every active display mode, figure out its framerate.
framerateDisplay = (config.dot_clock / (config.h_total * config.v_total))
if framerateDisplay > framerate:
framerate = framerateDisplay
return framerate
def nt():
# Cannot use pygame.display.get_driver for windows or macos in pygame < v2
# Adapted from Anurag Uniyal on https://stackoverflow.com/questions/1225057/how-can-i-determine-the-monitor-refresh-rate
install_dependencies({"pywin32"})
if "EnumDisplayDevices" not in globals(): # Import if not already.
global EnumDisplayDevices
from win32api import EnumDisplayDevices
if "EnumDisplaySettings" not in globals(): # Import if not already.
global EnumDisplaySettings
from win32api import EnumDisplaySettings
display_info = EnumDisplayDevices() # Returns PyDISPLAY_DEVICE object
display_settings = EnumDisplaySettings(display_info.DeviceName, -1) # Picks display that seems to have highest framerate?...
return getattr(display_settings, "DisplayFrequency")
def darwin():
# Cannot use pygame.display.get_driver for windows or macos in pygame < v2
# Adapted from Glyph, Eevee on https://stackoverflow.com/questions/1225057/how-can-i-determine-the-monitor-refresh-rate
install_dependencies({"Appkit"})
if "NSScreen" not in globals(): # Import if not already.
global NSScreen
from AppKit import NSScreen
for screen in NSScreen.screens():
if framerate < screen.maximumFramesPerSecond(): # I the framerate of this display is higher, set its as the new maximum rate.
framerate = screen.maximumFramesPerSecond()
return framerate
def update_framerate():
"""
Used for mock vsync to support Pygame < v2.0.0
"""
if FEATURES["matchcase"]["is_active"]:
match PLATFORM["kernel"]:
case "Linux":
framerate = Compat.linux()
case "Windows":
framerate = Compat.nt()
case "Darwin":
framerate = Compat.darwin()
elif PLATFORM["kernel"] == "Linux":
framerate = Compat.linux()
elif PLATFORM["kernel"] == "Windows":
framerate = Compat.nt()
elif PLATFORM["kernel"] == "Darwin": # I'm trusting that the info I found for MacOS does actually still work this way.
framerate = Compat.darwin()
else:
print("Unsupported or unable to detect system kernel. Defaulting to 30 FPS. This can be overridden in the user settings file.")
framerate = int("%.0f" % framerate)
DISPLAY["framerate"] = framerate