Development
It's easy to write your own plugin by making a python package and then indicating it's name as the plugin name.
Writing plugins
Plugins can be loaded with full python module path, eg: "mymodule.pyprlandplugin", the loaded module must provide an Extension class.
Check the interface.py file to know the base methods, also have a look at the example below.
To get more details when an error is occurring, use pypr --debug <log file path>, it will also display the log in the console.
NOTE
To quickly get started, you can directly edit the experimental built-in plugin. In order to distribute it, make your own Python package or trigger a pull request. If you prefer to make a separate package, check the examples's package
The Extension interface provides a couple of built-in attributes:
config: object exposing the plugin section inpyprland.tomlnotify,notify_error,notify_info: access to Hyprland's notification systemhyprctl,hyprctl_json: invoke Hyprland's IPC system
IMPORTANT
Contact me to get your extension listed on the home page
TIP
You can set a plugins_paths=["/custom/path/example"] in the hyprland section of the configuration to add extra paths (eg: during development).
NOTE
If your extension is at the root of the plugin (this is not recommended, preferable add a name space, as in johns_pyprland.super_feature, rather than super_feature) you can still import it using the external: prefix when you refer to it in the plugins list.
API Documentation
Run tox run -e doc then visit http://localhost:8080
The most important to know are:
hyprctl_jsonto get a response from an IPC queryhyprctlto trigger general IPC commandson_reloadto be implemented, called when the config is (re)loadedrun_<command_name>to implement a commandevent_<event_name>called when the given event is emitted by Hyprland
All those methods are async
On top of that:
- the first line of a
run_*command's docstring will be used by thehelpcommand self.configin your Extension contains the entry corresponding to your plugin name in the TOML filestatefrom..commonmodule contains ready to use information- there is a
MenuMixinin..adapters.menusto make menu-based plugins easy
Workflow
Just ^C when you make a change and repeat:
pypr exit ; pypr --debug /tmp/output.logCreating a plugin
from .interface import Plugin
class Extension(Plugin):
" My plugin "
async def init(self):
await self.notify("My plugin loaded")Adding a command
Just add a method called run_<name of your command> to your Extension class, eg with "togglezoom" command:
zoomed = False
async def run_togglezoom(self, args):
""" this doc string will show in `help` to document `togglezoom`
But this line will not show in the CLI help
"""
if self.zoomed:
await self.hyprctl('misc:cursor_zoom_factor 1', 'keyword')
else:
await self.hyprctl('misc:cursor_zoom_factor 2', 'keyword')
self.zoomed = not self.zoomedReacting to an event
Similar as a command, implement some async def event_<the event you are interested in> method.
Code safety
Pypr ensures only one run_ or event_ handler runs at a time, allowing the plugins code to stay simple and avoid the need for concurrency handling. However, each plugin can run its handlers in parallel.
Reusable code
from ..common import state, CastBoolMixinstateprovides a couple of handy variables so you don't have to fetch them, allow optimizing the most common operationsMixinsare providing common code, for instance theCastBoolMixinprovides thecast_boolmethod to yourExtension.
If you want to use menus, then the MenuMixin will provide:
menuto show a menuensure_menu_configuredto call before you require a menu in your plugin
Example
You'll find a basic external plugin in the examples folder.
It provides one command: pypr dummy.
Read the plugin code
It's a simple python package. To install it for development without a need to re-install it for testing, you can use pip install -e . in this folder. It's ready to be published using poetry publish, don't forget to update the details in the pyproject.toml file.
Usage
Ensure you added pypr_examples.focus_counter to your plugins list:
[pyprland]
plugins = [
"pypr_examples.focus_counter"
]Optionally you can customize one color:
["pypr_examples.focus_counter"]
color = "FFFF00"