Metadata-Version: 2.1
Name: PyMonCtl
Version: 0.2
Summary: Cross-Platform toolkit to get info on and control monitors connected
Home-page: https://github.com/Kalmat/PyMonCtl
Author: Kalmat
Author-email: palookjones@gmail.com
License: BSD 3
Keywords: screen display monitor control geometry size position frequency scale orientation screen-size mouse-position
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Win32 (MS Windows)
Classifier: Environment :: X11 Applications
Classifier: Environment :: MacOS X
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Description-Content-Type: text/markdown
License-File: LICENSE.txt
License-File: AUTHORS.txt
Requires-Dist: pyobjc (>=8.1) ; sys_platform == "darwin"
Requires-Dist: python-xlib (>=0.21) ; sys_platform == "linux"
Requires-Dist: pywin32 (>=302) ; sys_platform == "win32"
Provides-Extra: dev
Requires-Dist: types-setuptools (>=65.5) ; extra == 'dev'
Requires-Dist: mypy (>=0.990) ; extra == 'dev'
Requires-Dist: typing-extensions (>=4.4.0) ; extra == 'dev'
Requires-Dist: types-pywin32 (>=305.0.0.3) ; extra == 'dev'
Requires-Dist: types-python-xlib (>=0.32) ; extra == 'dev'

# PyMonCtl
[![Type Checking](https://github.com/Kalmat/PyMonCtl/actions/workflows/type-checking.yml/badge.svg)](https://github.com/Kalmat/PyMonCtl/actions/workflows/type-checking.yml)
[![PyPI version](https://badge.fury.io/py/PyMonCtl.svg)](https://badge.fury.io/py/PyMonCtl)

Cross-Platform module which provides a set of features to get info on and control monitors.

Additional tools/extensions/APIs used:
- Linux:
  - Xlib's randr extension
  - xrandr command-line tool
  - xset command-line tool
- Windows:
  - VCP MCCS API interface
- macOS:
  - pmset command-line tool
    
My most sincere thanks and appreciation to the University of Utah Student Computing Labs for their awesome work on 
the [display_manager_lib](https://github.com/univ-of-utah-marriott-library-apple/display_manager) module, for sharing it so generously, and most especially for allowing to be integrated 
into PyMonCtl

## General Features

Functions to get monitor instances, get info and arrange monitors plugged to the system.

| General functions: |
|:------------------:|
|   getAllMonitors   |
| getAllMonitorsDict |
|  getMonitorsCount  |
|     getPrimary     |
|    findMonitor     |
|  findMonitorInfo   |
|  arrangeMonitors   |
|    getMousePos     |


## Monitor Class

Class to access all methods and functions to get info and control a given monitor plugged to the system.

This class is not meant to be directly instantiated. Instead, use convenience functions like `getAllMonitors()`,
`getPrimary()` or `findMonitor(x, y)`.

To instantiate it, you need to pass the monitor handle (OS-dependent). It can raise ValueError exception in case 
the provided handle is not valid.

|                | Windows | Linux | macOS |
|:--------------:|:-------:|:-----:|:-----:|
|      size      |    X    |   X   |   X   |
|    workarea    |    X    |   X   |   X   |
|    position    |    X    |   X   |   X   |
|  setPosition   |    X    |   X   |   X   |
|      box       |    X    |   X   |   X   |
|      rect      |    X    |   X   |   X   |
|     scale      |    X    |   X   |   X   |
|    setScale    |    X    |   X   |   X   |
|      dpi       |    X    |   X   |   X   |
|  orientation   |    X    |   X   |   X   |
| setOrientation |    X    |   X   | X (1) |
|   frequency    |    X    |   X   |   X   |
|   colordepth   |    X    |   X   |   X   |
|   brightness   |  X (2)  |   X   | X (1) |
| setBrightness  |  X (2)  |   X   | X (1) |
|    contrast    |  X (2)  | X (3) | X (3) |
|  setContrast   |  X (2)  | X (3) | X (3) |
|      mode      |    X    |   X   |   X   |
|    setMode     |    X    |   X   |   X   |
|  defaultMode   |    X    |   X   |   X   |
| setDefaultMode |    X    |   X   |   X   |
|    allModes    |    X    |   X   |   X   |
|   isPrimary    |    X    |   X   |   X   |
|   setPrimary   |    X    |   X   |   X   |
|     turnOn     |    X    |   X   |       |
|    turnOff     |  X (4)  |   X   |       |
|    suspend     |  X (4)  | X (5) | X (5) |
|      isOn      |  X (6)  |   X   |   X   |
|  isSuspended   |  X (2)  |   X   |   X   |
|     attach     |    X    |   X   |       |
|     detach     |    X    |   X   |       |
|   isAttached   |    X    |   X   |   X   |


(1) Through display_manager_lib from University of Utah - Marriott Library - Apple Infrastructure (thank you, guys!).

(2) If monitor has no VCP MCCS support, these methods won't likely work.

(3) It doesn't exactly return / change contrast, but gamma values.

(4) If monitor has no VCP MCCS support, it can not be addressed separately, 
    so ALL monitors will be turned off / suspended.
    To address a specific monitor, try using turnOff() / turnOn() / detach() / attach() methods.

(5) It will suspend ALL monitors.

(6) Only if monitor has VCP MCCS support.


#### WARNING: Most of these properties may return ''None'' in case the value can not be obtained

## Monitoring Monitor(s)

You can activate a watchdog, running in a separate Thread, which will allow you to keep monitors 
information updated, without negatively impacting your main process, and define hooks and its callbacks to be  
notified when monitors are plugged / unplugged or their properties change.

|   Watchdog methods:    |
|:----------------------:|
|   isWatchdogEnabled    |
| updateWatchdogInterval |

The watchdog will automatically start while the update information is enabled and / or there are any listeners 
registered, and will automatically stop otherwise.

You can check if the watchdog is working (`isWatchdogEnabled()`) and also change its update interval 
(`updateWatchdogInterval()`) in case you need a custom period (default is 0.5 seconds). Adjust this value to your needs, 
but take into account that higher values will take longer to detect and notify changes; whilst lower values will 
consume more CPU and may produce additional notifications for "intermediate" status.

### Keep Monitors info updated

|    Info update methods:    |
|:--------------------------:|
|      enableUpdateInfo      |
|     disableUpdateInfo      |
|    isUpdateInfoEnabled     |

Enable this only if you need to keep track of monitor-related events like changing its resolution, position, scale,
or if monitors can be dynamically plugged or unplugged in a multi-monitor setup. If you need monitors info updated 
at a given moment, but not continuously updated, just invoke `getAllMonitors()` at your convenience.

If enabled, it will activate a separate thread which will periodically update the list of monitors and
their properties (see `getAllMonitors()` and `getAllMonitorsDict()` function).

### Get notified on Monitors changes

It is possible to register listeners to be invoked in case the number of connected monitors or their 
properties change.

|     Listeners methods:     |
|:--------------------------:|
|    plugListenerRegister    |
|   changeListenerRegister   |
|   plugListenerUnregister   |
|  changeListenerUnregister  |
|  isPlugListenerRegistered  |
| isChangeListenerRegistered |

The information passed to the listeners is as follows:

   - Names of the monitors which have changed (as a list of strings)
   - All monitors info, as returned by `getAllMonitorsDict()`. To access monitors properties, use monitor name/s as dictionary key

Example:

    import pymonctl as pmc
    import time

    def countChanged(names, screensInfo):
        print("MONITOR PLUGGED/UNPLUGGED:", names)
        for name in names:
            print("MONITORS INFO:", screensInfo[name])

    def propsChanged(names, screensInfo):
        print("MONITOR CHANGED:", names)
        for name in names:
            print("MONITORS INFO:", screensInfo[name])

    pmc.plugListenerRegister(countChanged)
    pmc.changeListenerRegister(propsChanged)

    print("Plug/Unplug monitors, or change monitor properties while running")
    print("Press Ctl-C to Quit")
    while True:
        try:
            time.sleep(1)
        except KeyboardInterrupt:
            break

    pmc.plugListenerUnregister(countChanged)
    pmc.changeListenerUnregister(propsChanged)


## INSTALL <a name="install"></a>

To install this module on your system, you can use pip: 

    pip install pymonctl

or

    python3 -m pip install pymonctl

Alternatively, you can download the wheel file (.whl) available in the [Download page](https://pypi.org/project/PyMonCtl/#files) and the [dist folder](https://github.com/Kalmat/PyMonCtl/tree/master/dist), and run this (don't forget to replace 'x.x.xx' with proper version number):

    pip install PyMonCtl-x.x.xx-py3-none-any.whl

You may want to add `--force-reinstall` option to be sure you are installing the right dependencies version.

Then, you can use it on your own projects just importing it:

    import pymonctl

## SUPPORT <a name="support"></a>

In case you have a problem, comments or suggestions, do not hesitate to [open issues](https://github.com/Kalmat/PyMonCtl/issues) on the [project homepage](https://github.com/Kalmat/PyMonCtl)

## USING THIS CODE <a name="using"></a>

If you want to use this code or contribute, you can either:

* Create a fork of the [repository](https://github.com/Kalmat/PyMonCtl), or 
* [Download the repository](https://github.com/Kalmat/PyMonCtl/archive/refs/heads/master.zip), uncompress, and open it on your IDE of choice (e.g. PyCharm)

Be sure you install all dependencies described on `docs/requirements.txt` by using pip
    
    python3 -m pip install -r requirements.txt

## TEST <a name="test"></a>

To test this module on your own system, cd to `tests` folder and run:

    python3 test_pymonctl.py
