diff --git a/.gitignore b/.gitignore
index 3516a57..e3c2527 100755
--- a/.gitignore
+++ b/.gitignore
@@ -105,4 +105,6 @@ ENV/
.mypy*
# idea
-.idea*
\ No newline at end of file
+.idea*
+brightness-controller-linux/test.txt
+brightness-controller-linux/testDdc.txt
diff --git a/brightness-controller-linux/brightness_controller_linux/init.py b/brightness-controller-linux/brightness_controller_linux/init.py
index e84468c..e3e6959 100755
--- a/brightness-controller-linux/brightness_controller_linux/init.py
+++ b/brightness-controller-linux/brightness_controller_linux/init.py
@@ -18,8 +18,8 @@
# .
import sys
-import getpass
-from os import path, remove, makedirs
+import getpass, argparse, os, traceback
+from os import path, remove, makedirs, getenv
from qtpy import QtGui, QtCore, QtWidgets
from qtpy.QtCore import QSize, Qt
from qtpy.QtGui import QIcon
@@ -33,42 +33,75 @@
from brightness_controller_linux.util import write_config as WriteConfig
from brightness_controller_linux.util import read_config as ReadConfig
from brightness_controller_linux.util import resource_provider as rp
+
+import brightness_controller_linux.util.log as log
# import util.filepath_handler as Filepath_handler
import subprocess
import threading
+verbosity = 1
+
class MyApplication(QtWidgets.QMainWindow):
ddcutil_Installed = False
+ waylandEnvironment = False
displayMaxes = []
displayValues = []
- displayNames = []
+ # ["connection", "name"] ordered the same as ddcutil lists
+
+ global parser
+
+ def verbose(self, verbosityLevel : int, message : str) -> None:
+ if verbosity>= verbosityLevel:
+ print(message)
def __assign_displays(self):
"""assigns display name """
- self.displays = CDisplay.detect_display_devices()
+ self.displays = CDisplay.extract_display_names() # returns ['connection', 'display name']
self.no_of_displays = len(self.displays)
self.no_of_connected_dev = self.no_of_displays
+ log.info(f"{self.no_of_displays} detected displays:")
+ log.info(str(self.displays))
+ log.info("")
+
+ self.verbose(2, str(self.displays) + " " + str(self.no_of_displays))
+
if self.no_of_displays == 1:
- self.display1 = self.displays[0]
+ self.display1 = self.displays[0][0]
elif self.no_of_displays >= 2:
- self.display1 = self.displays[0]
- self.display2 = self.displays[1]
+ self.display1 = self.displays[0][0]
+ self.display2 = self.displays[1][0]
+
+ def directlySetBrightness(self, displayNum, value):
+ self.verbose(2, f"Updating brightness for display {self.displays[displayNum][1]} with value {value}")
+ log.info(f"Updating brightness for display {displayNum} {self.displays[displayNum][1]} with value {value}")
- def directlySetMaxBrightness(self, displayNum, percentage):
+ if self.displays[displayNum][0].startswith("eDP"):
+ print("ATTEMPTED TO SET LAPTOP DISPLAY: ABORTING")
+ return
- percentage = round(percentage) / 100
+ try:
+ subprocess.run(["ddcutil", "setvcp", "10", str(int(value)), "-d", str(displayNum + 1)])
+ except:
+ print(f"Error while setting display {self.displays[displayNum][1]} with value {value}")
+ log.error(f"Error while setting display {displayNum} {self.displays[displayNum][1]} with value {value}")
- subprocess.run(["ddcutil", "setvcp", "10", str(int(
- self.displayMaxes[displayNum - 1] * percentage)), "-d",
- str(displayNum)])
def __init__(self, parent=None):
"""Initializes"""
QtWidgets.QMainWindow.__init__(self, parent)
+ log.begin()
+
+ # warn if wayland is installed
+ if os.getenv("XDG_SESSION_TYPE") == "wayland":
+ print("Warning: Wayland session detected! Wayland is in experimental support! Expect buggy behavior")
+ self.waylandEnvironment = True
+ log.warning("Wayland session detected!")
+ QtWidgets.QMessageBox.warning(self, "Wayland Environment", "Wayland is in EXPERIMENTAL support. Software brightness is currently broken.")
+
# check if ddcutil is installed
try:
if "ddcutil" in str(
@@ -76,43 +109,37 @@ def __init__(self, parent=None):
if "sudo modprobe" in str(
subprocess.check_output(["ddcutil", "environment"]),
'utf-8'):
- self.ui.ddcutilsNotInstalled.setText(
- "add i2c-dev to etc/modules-load.d")
- else:
- self.ddcutil_Installed = True
- except:
- self.ddcutil_Installed = False
+ self.ui.ddcutilsNotInstalled.setText("add i2c-dev to etc/modules-load.d")
- try:
- getNames = str(subprocess.check_output(["ddcutil", "detect"]),
- 'utf-8').split("\n")
+ envCheck = str(subprocess.check_output(["ddcutil", "environment"]), 'utf-8')
- for i in range(len(getNames)):
- if "Model:" in getNames[i]:
+ if "not a member of group i2c" in envCheck:
- if not getNames[i].split(":")[1].strip() == "":
- self.displayNames.append(
- getNames[i].split(":")[1].strip())
+ log.fatal(f"[DDCUtil] User is not part of i2c group! Run 'sudo usermod -G i2c -a {getpass.getuser()}'")
+ errorBox = QtWidgets.QMessageBox.critical(None,
+ "DDCUtil User config error!",
+ f"User is not part of i2c group! Run 'sudo usermod -G i2c -a {getpass.getuser()}'",
+ QtWidgets.QMessageBox.StandardButton.Close)
+ exit()
+ else:
+ self.ddcutil_Installed = True
+ except Exception:
+ self.ddcutil_Installed = False
- if "Invalid display" in getNames[i]:
- self.displayNames.append(getNames[i].strip())
+ log.info(f"DDCUtils installed: {self.ddcutil_Installed}")
- for i in range(len(self.displayNames)):
- if not self.displayNames[i] == "Invalid display":
- brightnessValue = str(subprocess.check_output(
- ["ddcutil", "getvcp", "10", "-d", str(i + 1)]), 'utf-8')
+ if (not self.ddcutil_Installed) and self.waylandEnvironment:
+ # Just exit entirely if we are on wayland and dont have ddcutil since it just won't do anything
+ log.fatal("Wayland environment detected and ddcutils is not installed! Exiting")
+ errorBox = QtWidgets.QMessageBox.critical(None,
+ "DDCUtil Missing",
+ "Software brightness is broken on wayland and ddcutil doesn't appear to be installed! Please install the package `ddcutil` to use this program on wayland.",
+ QtWidgets.QMessageBox.StandardButton.Close)
+ sys.exit()
+
- self.displayMaxes.append(int(
- brightnessValue.split(",")[1].split("=")[1].strip()))
-
- self.displayValues.append(int(
- brightnessValue.split(',')[0].split('=')[1].strip()))
- else:
- self.displayMaxes.append(1)
- self.displayValues.append(1)
-
- except Exception as e:
- print("error: " + str(e))
+
+ self.updatingMode = False
self.tray_menu = None
self.tray_icon = None
@@ -135,6 +162,43 @@ def __init__(self, parent=None):
self.temperature = 'Default'
self.no_of_connected_dev = 0
self.__assign_displays()
+
+ #moved to directly after __assign_displays to prevent comboboxes having items added in the original order from xrandr
+ if self.ddcutil_Installed:
+ self.displays = CDisplay.match_ddc_order(self.displays)
+
+ self.verbose(2, str(self.displays) + " : reordered displays")
+
+ self.ui.ddcutilsNotInstalled.setVisible(False)
+
+ log.info("Getting display brightness ranges.")
+
+ for i in range(len(self.displays)):
+ self.ui.directControlBox.setEnabled(True)
+
+ brightnessValue = ""
+
+ brightnessValue = subprocess.getoutput(f"ddcutil getvcp 10 -d {i + 1}") # Ignores the return value
+ log.info(brightnessValue)
+ if "Display not found" in brightnessValue:
+ log.error(f"Display wasn't found for command `ddcutil getvcp 10 -d {i + 1}`")
+ self.ui.ddcutilsNotInstalled.setVisible(True)
+ self.ui.ddcutilsNotInstalled.setText("Laptop Displays Not Supported")
+ self.displayMaxes.append(1)
+ self.displayValues.append(1)
+ continue
+
+ self.displayMaxes.append(int(
+ brightnessValue.split(",")[1].split("=")[1].strip()))
+
+ self.displayValues.append(int(
+ brightnessValue.split(',')[0].split('=')[1].strip()))
+
+ log.info(f"current display values {self.displayValues}")
+ log.info(f"display maxes: {self.displayMaxes}")
+
+
+
self.setup_default_directory()
self.generate_dynamic_items()
self.default_config = '/home/{}/.config/' \
@@ -148,21 +212,20 @@ def __init__(self, parent=None):
self.connect_handlers()
self.setup_widgets()
+ if self.ddcutil_Installed and self.waylandEnvironment:
+ self.ui.directControlBox.setChecked(True) # Auto turn on ddc control since xrandr doesnt work on wayland
+ self.ui.directControlBox.setEnabled(False)
+
if path.exists(self.default_config):
self.load_settings(self.default_config)
- if self.ddcutil_Installed:
- res = all(ele == "Invalid display" for ele in self.displayNames)
+ """
+ res = all(ele == "Invalid display" for ele in self.displays)
if not res:
- self.ui.directControlBox.setEnabled(True)
- self.ui.ddcutilsNotInstalled.setVisible(False)
+
else:
- self.ui.ddcutilsNotInstalled.setText(
- "Laptop Displays Not Supported")
-
- print(self.displayNames)
+ """
- print(self.ddcutil_Installed)
self.canCloseToTray = False
@@ -170,6 +233,8 @@ def __init__(self, parent=None):
self.canCloseToTray = True
self.setup_tray(parent)
+ log.info("Init finished!")
+
def setup_default_directory(self):
""" Create default settings directory if it doesnt exist """
directory = '/home/{}/.config/' \
@@ -191,6 +256,7 @@ def closeEvent(self, event):
QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes:
event.accept()
+ log.info("Application Exiting!")
sys.exit(self.APP.exec_())
else:
event.ignore()
@@ -208,6 +274,7 @@ def trayClose(self):
QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes:
+ log.info("Application Exiting!")
sys.exit(self.APP.exec_())
def setup_tray(self, parent):
@@ -277,15 +344,15 @@ def generate_brightness_sources(self):
self.ui.primary_combobox.setEnabled(False)
return
- if self.ddcutil_Installed:
+ """if self.ddcutil_Installed:
for i in range(self.no_of_connected_dev):
self.ui.secondary_combo.addItem(self.displayNames[i])
self.ui.primary_combobox.addItem(self.displayNames[i])
pass
- else:
- for display in self.displays:
- self.ui.secondary_combo.addItem(display)
- self.ui.primary_combobox.addItem(display)
+ else:"""
+ for display in self.displays:
+ self.ui.secondary_combo.addItem(display[1])
+ self.ui.primary_combobox.addItem(display[1])
def connect_handlers(self):
"""Connects the handlers of GUI widgets"""
@@ -327,29 +394,55 @@ def connect_handlers(self):
self.ui.actionLoad.triggered.connect(self.load_settings)
def directControlUpdate(self, value):
+
+ if self.ddcutil_Installed and self.waylandEnvironment and not self.ui.directControlBox.isChecked():
+ self.ui.directControlBox.setChecked(True) # Force wayland users to only use ddc
+
+ log.info(f"ddc mode toggled to: {self.ui.directControlBox.isChecked()}")
+
+ self.updatingMode = True
+
if self.ui.directControlBox.isChecked():
self.ui.primary_brightness.setMaximum(100)
- self.ui.primary_brightness.setValue(int(round(
- (self.displayValues[0] / self.displayMaxes[0]) * 100)))
- self.ui.primary_brightness.setFocusPolicy(Qt.NoFocus)
- self.ui.primary_brightness.setTracking(False)
- print("Update: " + self.ui.primary_combobox.currentText())
+ primaryComboIndex = self.ui.primary_combobox.currentIndex()
+ secondaryComboIndex = self.ui.secondary_combo.currentIndex()
- if self.ui.primary_combobox.currentText() == "Invalid display":
+ # if display has valid max value, set the brightness slider's max value to display max
+ # otherwise we disable the slider since it either errored or is a laptop display and cant be controlled
+ if self.displayMaxes[primaryComboIndex] > 0:
+
+ if not self.displays[primaryComboIndex][0].startswith("eDP"):
+ self.ui.primary_brightness.setEnabled(True)
+ self.ui.primary_brightness.setMaximum(self.displayMaxes[primaryComboIndex])
+ self.ui.primary_brightness.setValue(int(round(
+ (self.displayValues[primaryComboIndex] / self.displayMaxes[primaryComboIndex]) * 100)))
+ else:
+ self.ui.primary_brightness.setEnabled(False)
+
+ self.ui.primary_brightness.setFocusPolicy(Qt.NoFocus)
+ self.ui.primary_brightness.setTracking(False)
+ else:
self.ui.primary_brightness.setEnabled(False)
if self.no_of_displays > 1:
- self.ui.secondary_brightness.setMaximum(100)
- self.ui.secondary_brightness.setValue(int(round(
- (self.displayValues[1] / self.displayMaxes[1]) * 100)))
- self.ui.secondary_brightness.setFocusPolicy(Qt.NoFocus)
- self.ui.secondary_brightness.setTracking(False)
-
- if self.ui.secondary_combo.currentText() == "Invalid display":
+ if self.displayMaxes[secondaryComboIndex] > 0:
+ if not self.displays[secondaryComboIndex][0].startswith("eDP"):
+ self.ui.secondary_brightness.setEnabled(True)
+ self.ui.secondary_brightness.setMaximum(self.displayMaxes[secondaryComboIndex])
+ self.ui.secondary_brightness.setValue(int(round(
+ (self.displayValues[secondaryComboIndex] / self.displayMaxes[secondaryComboIndex]) * 100)))
+ else:
+ self.ui.secondary_brightness.setEnabled(False)
+
+ self.ui.secondary_brightness.setFocusPolicy(Qt.NoFocus)
+ self.ui.secondary_brightness.setTracking(False)
+ else:
self.ui.secondary_brightness.setEnabled(False)
else:
+ self.ui.primary_brightness.setEnabled(True)
+ self.ui.secondary_brightness.setEnabled(True)
self.ui.primary_brightness.setMaximum(99)
self.ui.secondary_brightness.setMaximum(99)
self.ui.primary_brightness.setValue(99)
@@ -359,11 +452,8 @@ def directControlUpdate(self, value):
self.ui.secondary_brightness.setFocusPolicy(Qt.StrongFocus)
self.ui.secondary_brightness.setTracking(True)
- if self.no_of_displays == 1:
- self.ui.primary_brightness.setEnabled(True)
- else:
- self.ui.primary_brightness.setEnabled(True)
- self.ui.secondary_brightness.setEnabled(True)
+ self.updatingMode = False
+
def enable_secondary_widgets(self, boolean):
"""
@@ -390,14 +480,12 @@ def connect_secondary_widgets(self):
def change_value_pbr(self):
"""Changes Primary Display Brightness"""
- if self.ui.directControlBox.isChecked():
+ if self.updatingMode: return
- setValue = threading.Thread(target=self.directlySetMaxBrightness,
- args=(
- self.ui.primary_combobox.currentIndex() + 1,
- self.ui.primary_brightness.value()))
+ if self.ui.directControlBox.isChecked():
- setValue.start()
+ self.directlySetBrightness(self.ui.primary_combobox.currentIndex(),
+ self.ui.primary_brightness.value())
self.displayValues[self.ui.primary_combobox.currentIndex()] = int(
round(self.ui.primary_brightness.value() / 100 *
@@ -461,15 +549,12 @@ def change_value_sbr(self):
"""
Changes Secondary Display Brightness
"""
+ if self.updatingMode: return
if self.ui.directControlBox.isChecked():
- setValue = threading.Thread(target=self.directlySetMaxBrightness,
- args=(
- self.ui.secondary_combo.currentIndex() + 1,
- self.ui.secondary_brightness.value()))
-
- setValue.start()
+ self.directlySetBrightness(self.ui.secondary_combo.currentIndex(),
+ self.ui.secondary_brightness.value())
self.displayValues[self.ui.secondary_combo.currentIndex()] = int(
round((self.ui.secondary_brightness.value() / 100 *
@@ -545,33 +630,30 @@ def secondary_source_combo_activated(self, text):
assigns combo value to display
"""
self.display2 = self.displays[
- self.ui.secondary_combo.currentIndex()] # text
- print(self.ui.secondary_combo.currentText())
- if self.ui.directControlBox.isChecked():
- if self.ui.secondary_combo.currentText() == "Invalid display":
- self.ui.secondary_brightness.setEnabled(False)
- print("secondary disabled")
- else:
- self.ui.secondary_brightness.setEnabled(True)
- print("secondary enabled")
+ self.ui.secondary_combo.currentIndex()][0] # text
+
+ if self.ddcutil_Installed and self.displays[self.ui.secondary_combo.currentIndex()][0].startswith("eDP"):
+ self.ui.primary_brightness.setEnabled(False)
else:
- self.ui.secondary_brightness.setEnabled(True)
+ self.ui.primary_brightness.setEnabled(True)
+
+
+ if self.ui.directControlBox.isChecked():
+ self.directControlUpdate(0)
def primary_source_combo_activated(self, text):
"""assigns combo value to display"""
self.display1 = self.displays[
- self.ui.primary_combobox.currentIndex()] # text
+ self.ui.primary_combobox.currentIndex()][0] # text
- print(self.ui.primary_combobox.currentText())
- if self.ui.directControlBox.isChecked():
- if self.ui.primary_combobox.currentText() == "Invalid display":
- self.ui.primary_brightness.setEnabled(False)
- print("primary disabled")
- else:
- self.ui.primary_brightness.setEnabled(True)
- print("primary enabled")
+ # Disable slider if laptop display
+ if self.ddcutil_Installed and self.displays[self.ui.primary_combobox.currentIndex()][0].startswith("eDP"):
+ self.ui.primary_brightness.setEnabled(False)
else:
- self.ui.secondary_brightness.setEnabled(True)
+ self.ui.primary_brightness.setEnabled(True)
+
+ if self.ui.directControlBox.isChecked():
+ self.directControlUpdate(0)
def combo_activated(self, text):
""" Designates values to display and to sliders """
@@ -895,6 +977,15 @@ def set_main_window(self, main_win):
"""assigns main_win as main_window"""
self.main_window = main_win
+
+parser = argparse.ArgumentParser(prog='ProgramName',
+ description='What the program does',
+ epilog='use --help to show cli arguments')
+parser.add_argument('-v', '--verbose', action='store_const', const=2, default=1)
+
+args = parser.parse_args()
+verbosity = args.verbose
+
def main():
UUID = 'PHIR-HWOH-MEIZ-AHTA'
APP = QtSingleApplication(UUID, sys.argv)
@@ -907,4 +998,7 @@ def main():
sys.exit(APP.exec_())
if __name__ == "__main__":
- main()
+ try:
+ main()
+ except:
+ log.fatal(traceback.format_exc())
diff --git a/brightness-controller-linux/brightness_controller_linux/util/check_displays.py b/brightness-controller-linux/brightness_controller_linux/util/check_displays.py
index d87aaff..c5df6d6 100644
--- a/brightness-controller-linux/brightness_controller_linux/util/check_displays.py
+++ b/brightness-controller-linux/brightness_controller_linux/util/check_displays.py
@@ -16,10 +16,16 @@
# You should have received a copy of the GNU General Public License
# along with Brightness Controller. If not, see .
-import subprocess
+import subprocess, os
import shlex
import re
+debug = False
+try:
+ import brightness_controller_linux.util.log as log
+except:
+ import log #used in testing
+ debug = True
def query_xrandr():
query = "xrandr --query"
@@ -38,6 +44,196 @@ def extract_displays(output):
return connected_displays
+def extract_edid_name(edid_hex):
+ try:
+ display_name = edid_hex[edid_hex.find('fc00') + 4:]
+ display_name = display_name[:display_name.find('0a')]
+
+ return bytes.fromhex(display_name).decode()
+
+ except Exception as e:
+ print("Error:", e)
+ return None
+
+def x11_Monitor_Name_Extractor(xrandr_monitors : list):
+
+ displays = []
+
+ for monitor in xrandr_monitors:
+ monitorInfo = []
+ gettingEDID = False
+ currentEdid = ""
+ for line in monitor:
+
+ if "connected" in line:
+ monitorInfo.append(line[:line.find(' ')])
+
+ if gettingEDID and line.startswith("\t\t"):
+ currentEdid += line[2:]
+ elif gettingEDID:
+ break
+ else:
+ gettingEDID = False
+
+ if line == "\tEDID: ":
+ gettingEDID = True
+
+
+ monitorName = extract_edid_name(currentEdid)
+ if monitorName:
+ monitorInfo.append(monitorName)
+ log.info(f"{monitor[0]} name is {monitorName}")
+ else:
+ print(f"Failed to get display name from monitor {monitor[0]}")
+ log.info(f"Failed to get display name from monitor {monitor[0]}")
+ monitorInfo.append(monitorInfo[0])
+
+ displays.append(monitorInfo)
+
+ log.info(f"[x11] Monitor names extracted: [{displays}]")
+
+ return displays
+
+def wayland_Monitor_Info_Extractor(monitorInfo):
+
+ connectionName = None
+ modelName = None
+
+ for line in monitorInfo:
+ #print(line)
+
+ if line.startswith('\tname:'):
+ connectionName = line.split(':')[1].strip()
+
+ if line.startswith("\tmake:"):
+ model = line.split(',')[1]
+ modelName = model[ model.find("'") + 1 : model.rfind("/") ]
+ #print(modelName)
+
+ if modelName and connectionName:
+ return [connectionName, modelName]
+ else:
+ return None
+
+
+def wayland_Monitor_Name_Extractor():
+
+ waylandInfo = []
+ displays = []
+ currentDisplay = []
+
+ try:
+ waylandInfo = subprocess.check_output(["wayland-info"]).decode().splitlines()
+ except:
+ print("ERROR: Please install the package \"wayland-utils\" for the wayland-info command!")
+ print("Monitor names will not be shown as they can't be labeled")
+ return None
+
+ i = -1
+ for line in waylandInfo:
+ i += 1
+
+ if line.startswith("interface: 'wl_output',") or i == len(waylandInfo) - 1:
+ if currentDisplay[0].startswith("interface: 'wl_output',"):
+ #get display info out
+ monitorInfo = wayland_Monitor_Info_Extractor(currentDisplay)
+ if not monitorInfo[0].startswith("Unknown"):
+ displays.append(monitorInfo)
+ #print(currentDisplay)
+ currentDisplay = []
+
+ currentDisplay.append(line)
+
+ log.info(f"[wayland] Monitor names extracted: {displays}")
+
+ return displays
+
+
+def extract_display_names(testInfo = None):
+ xrandr_output = subprocess.check_output(["xrandr", "--verbose"]).decode().splitlines()
+
+ displayVerboseInfo = []
+ display = []
+
+ #get verbose data for displays
+ i = -1
+ for line in xrandr_output:
+ i += 1
+
+ if line.startswith("Screen"): continue
+
+ if i == len(xrandr_output) - 1:
+ display.append(line)
+ if "disconnected" not in display[0]:
+ if not display[0].startswith("Unknown"):
+ displayVerboseInfo.append(display)
+
+ if not line.startswith("\t") and "connected" in line:
+ if len(display) > 0:
+ if "disconnected" not in display[0]:
+ if not display[0].startswith("Unknown"):
+ displayVerboseInfo.append(display)
+
+ display = []
+ display.append(line)
+ else:
+ display.append(line)
+
+ log.info(f"Display info : {len(displayVerboseInfo)} displays.")
+ log.info(displayVerboseInfo)
+ log.info("")
+
+ if os.getenv("XDG_SESSION_TYPE") == "wayland":
+ waylandDisplayNames = wayland_Monitor_Name_Extractor()
+ if waylandDisplayNames == None:
+ # fall back to old nameing
+ log.warning("Fell back to x11 monitor name extraction!")
+ print("ERROR Falling back to x11 monitor name extractor! Names may not be extracted!")
+ return x11_Monitor_Name_Extractor(displayVerboseInfo)
+
+ # we got display names from wayland!
+ return waylandDisplayNames
+
+ else:
+ return x11_Monitor_Name_Extractor(displayVerboseInfo)
+
+
+def match_ddc_order(monitorNames):
+
+ detectedMonitors = subprocess.check_output(["ddcutil", "detect"]).decode().splitlines()
+
+ log.info("ddcutil detect output:")
+ log.info(detectedMonitors)
+
+ reorderedMonitors = []
+
+ #laptopMonitorNames = [['eDP-1', 'eDP-1'], ['HDMI-1', 'VG279']]
+
+ #laptopTestCase = ['Display 1', ' I2C bus: /dev/i2c-1', ' EDID synopsis:', ' Mfg id: AUS', ' Model: VG279', ' Serial number: Redacted', ' Manufacture year: 2020', ' EDID version: 1.3', ' VCP version: 2.2', '', 'Invalid display', ' I2C bus: /dev/i2c-4', ' EDID synopsis:', ' Mfg id: BOE', ' Model: ', ' Serial number: ', ' Manufacture year: 2015', ' EDID version: 1.4', ' DDC communication failed', ' This is an eDP laptop display. Laptop displays do not support DDC/CI.', '']
+
+ for line in detectedMonitors:
+ if "Model" in line:
+ for monitor in monitorNames:
+ modelName = line.split(":")[1].strip()
+ log.info(f"[ddcReorder] Model name output {modelName}")
+ if modelName == '':
+ if monitor[1].startswith('eDP'):
+ reorderedMonitors.append(monitor)
+ log.info(f"[ddcReorder] added {monitor} from {modelName}")
+ break
+
+ if monitor[1] in modelName:
+ reorderedMonitors.append(monitor)
+ log.info(f"[ddcReorder] added {monitor} from {modelName}")
+ break
+
+ if len(monitorNames) != len(reorderedMonitors):
+ print(f"ERROR IN MONITOR REORDERING please create an issue on the github with your log file at ~/.config/brightness_controller/log.txt")
+ log.error(f"Failed attempting to reorder monitors, input: {monitorNames} attempted output: {reorderedMonitors}")
+ return monitorNames # fall back to unreordered output
+ return reorderedMonitors
+
+
def detect_display_devices():
"""
Detects available displays.
@@ -47,5 +243,9 @@ def detect_display_devices():
return extract_displays(query_xrandr())
-if __name__ == '__main__':
- print(detect_display_devices())
+if debug:
+ #print(detect_display_devices())
+ with open("test.txt", 'r') as file:
+ testInfo = file.readlines()
+
+ extract_display_names(testInfo)
diff --git a/brightness-controller-linux/brightness_controller_linux/util/executor.py b/brightness-controller-linux/brightness_controller_linux/util/executor.py
index 5c109ac..c19ddb5 100644
--- a/brightness-controller-linux/brightness_controller_linux/util/executor.py
+++ b/brightness-controller-linux/brightness_controller_linux/util/executor.py
@@ -20,4 +20,7 @@
def execute_command(string_cmd):
- subprocess.check_output(string_cmd, shell=True)
+ try:
+ subprocess.check_output(string_cmd, shell=True)
+ except:
+ None
\ No newline at end of file
diff --git a/brightness-controller-linux/brightness_controller_linux/util/log.py b/brightness-controller-linux/brightness_controller_linux/util/log.py
new file mode 100644
index 0000000..d2d4bbe
--- /dev/null
+++ b/brightness-controller-linux/brightness_controller_linux/util/log.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+# This file is part of Brightness Controller.
+#
+# Brightness Controller is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Brightness Controller is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Brightness Controller. If not, see
+# .
+
+import os, sys
+from datetime import datetime
+
+logPath = f"/home/{os.getlogin()}/.config/brightness_controller/log.txt"
+
+def write(logString, level):
+ currentTime = datetime.now()
+ with open(logPath, 'a') as file:
+ if type(logString) is list:
+ for line in logString:
+ if type(line) is list:
+ write(line, level)
+ else:
+ file.write(f"[{currentTime}] [{level}] {line}\n")
+ else:
+ file.write(f"[{currentTime}] [{level}] {logString}\n")
+
+def info(logString):
+ write(logString, "info")
+
+def warning(logString):
+ write(logString, "warning")
+
+def error(logString):
+ write(logString, "ERROR")
+
+def fatal(logString):
+ write(logString, "FATAL")
+
+def begin():
+ if not os.path.exists(logPath):
+ try:
+ os.makedirs(f"/home/{os.getlogin()}/.config/brightness_controller")
+ except:
+ None
+ open(logPath, 'w').close()
+
+ info("Application start!")
\ No newline at end of file
diff --git a/brightness-controller-linux/brightness_controller_linux/util/resource_provider.py b/brightness-controller-linux/brightness_controller_linux/util/resource_provider.py
index 3650012..e9eca67 100644
--- a/brightness-controller-linux/brightness_controller_linux/util/resource_provider.py
+++ b/brightness-controller-linux/brightness_controller_linux/util/resource_provider.py
@@ -1,7 +1,7 @@
from importlib import resources
from pathlib import Path
-import brightness_controller_linux.icons as icons
+import icons as icons
def icon_path(module_name=icons):
diff --git a/brightness-controller-linux/poetry.lock b/brightness-controller-linux/poetry.lock
index 58f2a9b..68ad105 100644
--- a/brightness-controller-linux/poetry.lock
+++ b/brightness-controller-linux/poetry.lock
@@ -1,40 +1,54 @@
+# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
+
[[package]]
name = "attrs"
version = "22.1.0"
description = "Classes Without Boilerplate"
-category = "dev"
optional = false
python-versions = ">=3.5"
+files = [
+ {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
+ {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
+]
[package.extras]
dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
-tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
+tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
[[package]]
name = "colorama"
version = "0.4.5"
description = "Cross-platform colored terminal text."
-category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+files = [
+ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
+ {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
+]
[[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
-category = "dev"
optional = false
python-versions = "*"
+files = [
+ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
+ {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
+]
[[package]]
name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
-category = "main"
optional = false
python-versions = ">=3.6"
+files = [
+ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
+ {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
+]
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
@@ -43,9 +57,12 @@ pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
name = "pluggy"
version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
-category = "dev"
optional = false
python-versions = ">=3.6"
+files = [
+ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
+ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
+]
[package.extras]
dev = ["pre-commit", "tox"]
@@ -55,17 +72,23 @@ testing = ["pytest", "pytest-benchmark"]
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
-category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+files = [
+ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
+ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
+]
[[package]]
name = "pyparsing"
version = "3.0.9"
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
-category = "main"
optional = false
python-versions = ">=3.6.8"
+files = [
+ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
+ {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
+]
[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]
@@ -74,9 +97,15 @@ diagrams = ["jinja2", "railroad-diagrams"]
name = "PyQt5"
version = "5.15.7"
description = "Python bindings for the Qt cross platform application toolkit"
-category = "main"
optional = false
python-versions = ">=3.7"
+files = [
+ {file = "PyQt5-5.15.7-cp37-abi3-macosx_10_13_x86_64.whl", hash = "sha256:1a793748c60d5aff3850b7abf84d47c1d41edb11231b7d7c16bef602c36be643"},
+ {file = "PyQt5-5.15.7-cp37-abi3-manylinux1_x86_64.whl", hash = "sha256:e319c9d8639e0729235c1b09c99afdadad96fa3dbd8392ab561b5ab5946ee6ef"},
+ {file = "PyQt5-5.15.7-cp37-abi3-win32.whl", hash = "sha256:08694f0a4c7d4f3d36b2311b1920e6283240ad3b7c09b515e08262e195dcdf37"},
+ {file = "PyQt5-5.15.7-cp37-abi3-win_amd64.whl", hash = "sha256:232fe5b135a095cbd024cf341d928fc672c963f88e6a52b0c605be8177c2fdb5"},
+ {file = "PyQt5-5.15.7.tar.gz", hash = "sha256:755121a52b3a08cb07275c10ebb96576d36e320e572591db16cfdbc558101594"},
+]
[package.dependencies]
PyQt5-Qt5 = ">=5.15.0"
@@ -86,25 +115,55 @@ PyQt5-sip = ">=12.11,<13"
name = "PyQt5-Qt5"
version = "5.15.2"
description = "The subset of a Qt installation needed by PyQt5."
-category = "main"
optional = false
python-versions = "*"
+files = [
+ {file = "PyQt5_Qt5-5.15.2-py3-none-macosx_10_13_intel.whl", hash = "sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962"},
+]
[[package]]
name = "PyQt5-sip"
version = "12.11.0"
description = "The sip module support for PyQt5"
-category = "main"
optional = false
python-versions = ">=3.7"
+files = [
+ {file = "PyQt5_sip-12.11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f1f9e312ff8284d6dfebc5366f6f7d103f84eec23a4da0be0482403933e68660"},
+ {file = "PyQt5_sip-12.11.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:4031547dfb679be309094bfa79254f5badc5ddbe66b9ad38e319d84a7d612443"},
+ {file = "PyQt5_sip-12.11.0-cp310-cp310-win32.whl", hash = "sha256:ad21ca0ee8cae2a41b61fc04949dccfab6fe008749627d94e8c7078cb7a73af1"},
+ {file = "PyQt5_sip-12.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:3126c84568ab341c12e46ded2230f62a9a78752a70fdab13713f89a71cd44f73"},
+ {file = "PyQt5_sip-12.11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bd733667098cac70e89279d9c239106d543fb480def62a44e6366ccb8f68510b"},
+ {file = "PyQt5_sip-12.11.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec1d8ce50be76c5c1d1c86c6dc0ccacc2446172dde98b663a17871f532f9bd44"},
+ {file = "PyQt5_sip-12.11.0-cp311-cp311-win32.whl", hash = "sha256:43dfe6dd409e713edeb67019b85419a7a0dc9923bfc451d6cf3778471c122532"},
+ {file = "PyQt5_sip-12.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:afa4ffffc54e306669bf2b66ea37abbc56c5cdda4f3f474d20324e3634302b12"},
+ {file = "PyQt5_sip-12.11.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0f77655c62ec91d47c2c99143f248624d44dd2d8a12d016e7c020508ad418aca"},
+ {file = "PyQt5_sip-12.11.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ec5e9ef78852e1f96f86d7e15c9215878422b83dde36d44f1539a3062942f19c"},
+ {file = "PyQt5_sip-12.11.0-cp37-cp37m-win32.whl", hash = "sha256:d12b81c3a08abf7657a2ebc7d3649852a1f327eb2146ebadf45930486d32e920"},
+ {file = "PyQt5_sip-12.11.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b69a1911f768b489846335e31e49eb34795c6b5a038ca24d894d751e3b0b44da"},
+ {file = "PyQt5_sip-12.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:51e377789d59196213eddf458e6927f33ba9d217b614d17d20df16c9a8b2c41c"},
+ {file = "PyQt5_sip-12.11.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4e5c1559311515291ea0ab0635529f14536954e3b973a7c7890ab7e4de1c2c23"},
+ {file = "PyQt5_sip-12.11.0-cp38-cp38-win32.whl", hash = "sha256:9bca450c5306890cb002fe36bbca18f979dd9e5b810b766dce8e3ce5e66ba795"},
+ {file = "PyQt5_sip-12.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:f6b72035da4e8fecbb0bc4a972e30a5674a9ad5608dbddaa517e983782dbf3bf"},
+ {file = "PyQt5_sip-12.11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9356260d4feb60dbac0ab66f8a791a0d2cda1bf98c9dec8e575904a045fbf7c5"},
+ {file = "PyQt5_sip-12.11.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:205f3e1b3eea3597d8e878936c1a06e04bd23a59e8b179ee806465d72eea3071"},
+ {file = "PyQt5_sip-12.11.0-cp39-cp39-win32.whl", hash = "sha256:686071be054e5be6ca5aaaef7960931d4ba917277e839e2e978c7cbe3f43bb6e"},
+ {file = "PyQt5_sip-12.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:42320e7a94b1085ed85d49794ed4ccfe86f1cae80b44a894db908a8aba2bc60e"},
+ {file = "PyQt5_sip-12.11.0.tar.gz", hash = "sha256:b4710fd85b57edef716cc55fae45bfd5bfac6fc7ba91036f1dcc3f331ca0eb39"},
+]
[[package]]
name = "pytest"
version = "7.1.3"
description = "pytest: simple powerful testing with Python"
-category = "dev"
optional = false
python-versions = ">=3.7"
+files = [
+ {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"},
+ {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"},
+]
[package.dependencies]
attrs = ">=19.2.0"
@@ -122,9 +181,12 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.
name = "QtPy"
version = "2.2.0"
description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)."
-category = "main"
optional = false
python-versions = ">=3.7"
+files = [
+ {file = "QtPy-2.2.0-py3-none-any.whl", hash = "sha256:d283cfba378b0dbe36a55b68aea8ee2f86cd6ccf06c023af25bbe705ffbb29e5"},
+ {file = "QtPy-2.2.0.tar.gz", hash = "sha256:d85f1b121f24a41ad26c55c446e66abdb7c528839f8c4f11f156ec4541903914"},
+]
[package.dependencies]
packaging = "*"
@@ -136,85 +198,14 @@ test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
-category = "dev"
optional = false
python-versions = ">=3.7"
-
-[metadata]
-lock-version = "1.1"
-python-versions = "^3.8"
-content-hash = "6cb55fbae7c75eef595c187aa6c8b5487a0875c8feddee2d8d9697dd55a5748e"
-
-[metadata.files]
-attrs = [
- {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
- {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
-]
-colorama = [
- {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
- {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
-]
-iniconfig = [
- {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
- {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
-]
-packaging = [
- {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
- {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
-]
-pluggy = [
- {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
- {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
-]
-py = [
- {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
- {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
-]
-pyparsing = [
- {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
- {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
-]
-PyQt5 = [
- {file = "PyQt5-5.15.7-cp37-abi3-macosx_10_13_x86_64.whl", hash = "sha256:1a793748c60d5aff3850b7abf84d47c1d41edb11231b7d7c16bef602c36be643"},
- {file = "PyQt5-5.15.7-cp37-abi3-manylinux1_x86_64.whl", hash = "sha256:e319c9d8639e0729235c1b09c99afdadad96fa3dbd8392ab561b5ab5946ee6ef"},
- {file = "PyQt5-5.15.7-cp37-abi3-win32.whl", hash = "sha256:08694f0a4c7d4f3d36b2311b1920e6283240ad3b7c09b515e08262e195dcdf37"},
- {file = "PyQt5-5.15.7-cp37-abi3-win_amd64.whl", hash = "sha256:232fe5b135a095cbd024cf341d928fc672c963f88e6a52b0c605be8177c2fdb5"},
- {file = "PyQt5-5.15.7.tar.gz", hash = "sha256:755121a52b3a08cb07275c10ebb96576d36e320e572591db16cfdbc558101594"},
-]
-PyQt5-Qt5 = [
- {file = "PyQt5_Qt5-5.15.2-py3-none-macosx_10_13_intel.whl", hash = "sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154"},
- {file = "PyQt5_Qt5-5.15.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a"},
- {file = "PyQt5_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"},
- {file = "PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962"},
-]
-PyQt5-sip = [
- {file = "PyQt5_sip-12.11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f1f9e312ff8284d6dfebc5366f6f7d103f84eec23a4da0be0482403933e68660"},
- {file = "PyQt5_sip-12.11.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:4031547dfb679be309094bfa79254f5badc5ddbe66b9ad38e319d84a7d612443"},
- {file = "PyQt5_sip-12.11.0-cp310-cp310-win32.whl", hash = "sha256:ad21ca0ee8cae2a41b61fc04949dccfab6fe008749627d94e8c7078cb7a73af1"},
- {file = "PyQt5_sip-12.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:3126c84568ab341c12e46ded2230f62a9a78752a70fdab13713f89a71cd44f73"},
- {file = "PyQt5_sip-12.11.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0f77655c62ec91d47c2c99143f248624d44dd2d8a12d016e7c020508ad418aca"},
- {file = "PyQt5_sip-12.11.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ec5e9ef78852e1f96f86d7e15c9215878422b83dde36d44f1539a3062942f19c"},
- {file = "PyQt5_sip-12.11.0-cp37-cp37m-win32.whl", hash = "sha256:d12b81c3a08abf7657a2ebc7d3649852a1f327eb2146ebadf45930486d32e920"},
- {file = "PyQt5_sip-12.11.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b69a1911f768b489846335e31e49eb34795c6b5a038ca24d894d751e3b0b44da"},
- {file = "PyQt5_sip-12.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:51e377789d59196213eddf458e6927f33ba9d217b614d17d20df16c9a8b2c41c"},
- {file = "PyQt5_sip-12.11.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4e5c1559311515291ea0ab0635529f14536954e3b973a7c7890ab7e4de1c2c23"},
- {file = "PyQt5_sip-12.11.0-cp38-cp38-win32.whl", hash = "sha256:9bca450c5306890cb002fe36bbca18f979dd9e5b810b766dce8e3ce5e66ba795"},
- {file = "PyQt5_sip-12.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:f6b72035da4e8fecbb0bc4a972e30a5674a9ad5608dbddaa517e983782dbf3bf"},
- {file = "PyQt5_sip-12.11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9356260d4feb60dbac0ab66f8a791a0d2cda1bf98c9dec8e575904a045fbf7c5"},
- {file = "PyQt5_sip-12.11.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:205f3e1b3eea3597d8e878936c1a06e04bd23a59e8b179ee806465d72eea3071"},
- {file = "PyQt5_sip-12.11.0-cp39-cp39-win32.whl", hash = "sha256:686071be054e5be6ca5aaaef7960931d4ba917277e839e2e978c7cbe3f43bb6e"},
- {file = "PyQt5_sip-12.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:42320e7a94b1085ed85d49794ed4ccfe86f1cae80b44a894db908a8aba2bc60e"},
- {file = "PyQt5_sip-12.11.0.tar.gz", hash = "sha256:b4710fd85b57edef716cc55fae45bfd5bfac6fc7ba91036f1dcc3f331ca0eb39"},
-]
-pytest = [
- {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"},
- {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"},
-]
-QtPy = [
- {file = "QtPy-2.2.0-py3-none-any.whl", hash = "sha256:d283cfba378b0dbe36a55b68aea8ee2f86cd6ccf06c023af25bbe705ffbb29e5"},
- {file = "QtPy-2.2.0.tar.gz", hash = "sha256:d85f1b121f24a41ad26c55c446e66abdb7c528839f8c4f11f156ec4541903914"},
-]
-tomli = [
+files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.8"
+content-hash = "f9db7aab91a3ce3666d74a685330a389ce052d2aadc35266d12518d9edd4f71d"
diff --git a/brightness-controller-linux/pyproject.toml b/brightness-controller-linux/pyproject.toml
index 05dcfbb..642d5cd 100644
--- a/brightness-controller-linux/pyproject.toml
+++ b/brightness-controller-linux/pyproject.toml
@@ -3,7 +3,7 @@ name = "brightness-controller-linux"
version = "2.4"
description = "Using Brightness Controller, you can control brightness of both primary and external displays in Linux. Check it out!"
authors = ["Amit "]
-readme = "readme.md"
+readme = "README.md"
homepage = "https://github.com/LordAmit/Brightness"
repository = "https://github.com/LordAmit/Brightness"
keywords = [