Skip to content

Instantly share code, notes, and snippets.

@leonmax
Last active November 14, 2022 04:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leonmax/bc42bb4a8450313ca97507871e46514a to your computer and use it in GitHub Desktop.
Save leonmax/bc42bb4a8450313ca97507871e46514a to your computer and use it in GitHub Desktop.
Swap displays position
#!/usr/bin/env python
import os
import sys
import subprocess
import re
import json
import pickle
from dataclasses import dataclass
from typing import Tuple, List
CONF_DIR = os.path.expanduser("~/.config/displayplacer")
P_CONF = re.compile(r'"([^"]+)"', re.IGNORECASE)
P_ATTR = re.compile(r'":"', re.IGNORECASE)
@dataclass
class Config:
id_: str
res: Tuple[int, int]
origin: Tuple[int, int] = (0,0)
hz: int = -1
color_depth: int = -1
scaling: bool = False
degree: int = 0
def to_conf(self):
result = f"id:{self.id_}"
result += f" degree:{self.degree}"
result += f" res:{self.res}"
result += f" hz:{self.hz}"
result += f" scaling:{'on' if self.scaling else 'off'}"
result += f" origin:{self.origin}"
return result
@staticmethod
def parse(value):
kv = dict(attr.split(':', 1) for attr in value.split(' '))
kv['id_'] = kv.pop('id')
return Config(**kv)
@dataclass
class Layout:
configs: List[Config]
@staticmethod
def parse(value):
configs = [Config.parse(m.group(1))
for line in value.split('\n')
if line.startswith("displayplacer")
for m in P_CONF.finditer(line)]
return Layout(configs)
@property
def footprint(self):
return [(c.id_, c.origin) for c in sorted(self.configs, key=lambda x: hash(x.id_))]
def to_args(self):
return "displayplacer " + ' '.join([f"\"{c.to_conf()}\"" for c in self.configs])
def run(self):
subprocess.run(self.to_args(), shell=True, check=True)
def switch(self, templates):
if self.footprint == templates[0].footprint:
print("switch to 1")
self.configs = templates[1].configs
else:
print("switch to 0")
self.configs = templates[0].configs
def load_templates(folder):
with open(f'{folder}/templates.txt', 'r') as f:
return [Layout.parse(l)
for l in f.readlines()
if l.strip()]
def main():
result = subprocess.run(['displayplacer', 'list'], capture_output=True, text=True)
layout = Layout.parse(result.stdout)
templates = load_templates(CONF_DIR)
layout.switch(templates)
print(layout.to_args())
layout.run()
if __name__ == "__main__":
main()

This script swap the two displays in case their order got reversed. It relies on the jakehilborn/displayplacer

  1. Install displayplacer
  2. Create config folder
  3. Copy the swap_display.py to a directory on your $PATH
brew install jakehilborn/jakehilborn/displayplacer
mkdir -p ~/.config/displayplacer
# now edit your ~/.config/displayplacer/templates.txt
cp <swap_display.py> ~/.local/bin/swap_display
chmod +x swap_display

And now you can just run swap_display. Also you can make a workflow in alfred like I do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment