-
Notifications
You must be signed in to change notification settings - Fork 0
/
move_monitor.py
109 lines (89 loc) · 3.45 KB
/
move_monitor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import argparse
from typing import Literal, get_args
import pywintypes
import screeninfo
import win32api
import win32con
from screeninfo import Monitor
Sides = Literal['top', 'left', 'bottom', 'right']
def get_monitors():
primary = None
monitors = []
for monitor in screeninfo.get_monitors():
if monitor.is_primary:
primary = monitor
else:
monitors.append(monitor)
monitors.sort(key=lambda a: a.name)
monitors.insert(0, primary)
return monitors
def flip_args(pos: Sides) -> Sides:
flip = {
'center': 'center',
'top': 'bottom',
'left': 'right'
}
flip.update({v: k for k, v in flip.items()})
return flip[pos]
def calc_fuzzy_pos(base: Monitor, device: Monitor, pos: Sides, align: Sides = 'center'):
x, y = 0, 0
new_mode = pywintypes.DEVMODEType()
if pos in ('top', 'bottom'):
if align == 'left':
x = base.x
elif align == 'right':
x = base.width - device.width
else:
x = (base.width // 2) - (device.width // 2)
y = base.height if pos == 'bottom' else base.y - device.height
else:
if align == 'top':
y = base.y
elif align == 'bottom':
y = base.height - device.height
else:
y = (base.height // 2) - (device.height // 2)
x = base.width + base.x if pos == 'right' else base.x - device.width
new_mode.Position_x = x
new_mode.Position_y = y
new_mode.Fields = win32con.DM_POSITION
return new_mode
if __name__ == '__main__':
parser = argparse.ArgumentParser()
sub = parser.add_subparsers(dest='command', required=True)
sub.add_parser('list', help='list available displays')
move_parser = sub.add_parser('move', help='move a display')
move_parser.add_argument(
'display', type=int, help='the display to move (1 being primary)')
move_parser.add_argument('side', choices=get_args(
Sides), help='Move to this side of the primary display')
move_parser.add_argument('-a', '--align', default='center', choices=get_args(
Sides) + ('center',), help='align to this edge of the chosen side')
move_parser.add_argument('--rel-to', default=None, type=int,
help=('move the monitor relative to the position of this one.'
' Defaults to primary or secondary monitor, whichever is NOT being moved'
))
args = parser.parse_args()
# make sure primary monitor is monitor 0, then sort the rest by "\\DISPLAY1..." str
devices = get_monitors()
if args.command == 'list':
for index, device in enumerate(devices):
print(index + 1, ':', device)
else:
args.display -= 1
args.rel_to -= 1
if args.rel_to is None:
args.rel_to = 1 if args.display == 0 else 0
if args.display == 0:
# dont move the primary, it causes issues. Move the other one relative to
# the primary instead
choice = devices[args.rel_to]
rel_to = devices[0]
pos, align = flip_args(args.side), args.align
else:
choice = devices[args.display]
rel_to = devices[args.rel_to]
pos, align = args.side, args.align
mode = calc_fuzzy_pos(rel_to, choice, pos, align)
print(win32api.ChangeDisplaySettingsEx(
choice.name, mode, win32con.CDS_UPDATEREGISTRY))