Initial Commit
This commit is contained in:
252
lib/system.py
Executable file
252
lib/system.py
Executable file
@@ -0,0 +1,252 @@
|
||||
#!~/.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
|
||||
Reference in New Issue
Block a user