Skip to content

Commit a0df61b

Browse files
Mickaël SchoentgenBoboTiG
authored andcommitted
Windows: Ready for high-DPI displays (fix BoboTiG#20)
1 parent 434be44 commit a0df61b

File tree

5 files changed

+59
-15
lines changed

5 files changed

+59
-15
lines changed

CHANGELOG

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dev 2017/xx/xx
99
- MSS: possibility to use custom class to handle screen shot data
1010
- Mac: handle screenshot's width not divisible by 16 (fix #14, #19, #21)
1111
- Linux: handle bad display value
12+
- Windows: Take into account zoom factor for high-DPI displays (fix #20)
1213

1314
3.0.1 2017/07/06
1415
- fix examples links

CHANGES.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ darwin.py
99
---------
1010
- Removed `get_infinity()` function
1111

12+
windows.py
13+
----------
14+
- Added `scale_factor` property to `MSS` class
15+
- Added `scale()` method to `MSS` class
16+
1217

1318
3.0.0 (2017-07-06)
1419
==================

mss/__main__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,21 @@
66

77
from __future__ import print_function
88

9+
from .exception import ScreenShotError
910
from .factory import mss
1011

1112

1213
def main():
1314
# type: () -> int
1415
""" Main logic. """
1516

16-
with mss() as sct:
17-
for file_name in sct.save():
18-
print(file_name)
19-
return 0
20-
21-
return 1
17+
try:
18+
with mss() as sct:
19+
for file_name in sct.save():
20+
print(file_name)
21+
return 0
22+
except ScreenShotError:
23+
return 1
2224

2325

2426
if __name__ == '__main__':

mss/darwin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def monitors(self):
152152
# Update AiO monitor's values
153153
all_monitors = self.core.CGRectUnion(all_monitors, rect)
154154

155-
# Update AiO monitor's values
155+
# Set the AiO monitor's values
156156
self._monitors[0] = {
157157
'left': int(all_monitors.origin.x),
158158
'top': int(all_monitors.origin.y),

mss/windows.py

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
Source: https://github.com/BoboTiG/python-mss
55
"""
66

7+
from __future__ import division
8+
79
import ctypes
810
import ctypes.wintypes
911

@@ -41,6 +43,8 @@ class BITMAPINFO(ctypes.Structure):
4143
class MSS(MSSBase):
4244
""" Multiple ScreenShots implementation for Microsoft Windows. """
4345

46+
__scale_factor = None # type: float
47+
4448
def __init__(self):
4549
# type: () -> None
4650
""" Windows initialisations. """
@@ -55,6 +59,32 @@ def __init__(self):
5559
set_argtypes(self.monitorenumproc)
5660
set_restypes()
5761

62+
@property
63+
def scale_factor(self):
64+
""" Compute the scale factor. """
65+
66+
if not self.__scale_factor:
67+
display = None
68+
try:
69+
display = ctypes.windll.user32.GetWindowDC(0)
70+
width = ctypes.windll.gdi32.GetDeviceCaps(display, 8)
71+
width_orig = ctypes.windll.gdi32.GetDeviceCaps(display, 118)
72+
scale = (100 + 100 - width * 100 // width_orig) / 100
73+
self.__scale_factor = round(scale * 4) / 4
74+
finally:
75+
if display:
76+
ctypes.windll.gdi32.DeleteObject(display)
77+
78+
return self.__scale_factor
79+
80+
def scale(self, value):
81+
# type: (float) -> int
82+
""" Compute a monitor value at scale, rounded to 2. """
83+
84+
if self.scale_factor == 1.0:
85+
return int(value)
86+
return int(value * self.scale_factor * 2 + 0.5) // 2
87+
5888
@property
5989
def monitors(self):
6090
# type: () -> List[Dict[str, int]]
@@ -69,10 +99,11 @@ def monitors(self):
6999
top = ctypes.windll.user32.GetSystemMetrics(sm_yvirtualscreen)
70100
bottom = ctypes.windll.user32.GetSystemMetrics(sm_cyvirtualscreen)
71101
self._monitors.append({
72-
'left': int(left),
73-
'top': int(top),
74-
'width': int(right - left),
75-
'height': int(bottom - top),
102+
'left': self.scale(left),
103+
'top': self.scale(top),
104+
'width': self.scale(right - left),
105+
'height': self.scale(bottom - top),
106+
'scale': self.scale_factor,
76107
})
77108

78109
# Each monitors
@@ -86,10 +117,11 @@ def _callback(monitor, data, rect, dc_):
86117
del monitor, data, dc_
87118
rct = rect.contents
88119
self._monitors.append({
89-
'left': int(rct.left),
90-
'top': int(rct.top),
91-
'width': int(rct.right - rct.left),
92-
'height': int(rct.bottom - rct.top),
120+
'left': self.scale(rct.left),
121+
'top': self.scale(rct.top),
122+
'width': self.scale(rct.right - rct.left),
123+
'height': self.scale(rct.bottom - rct.top),
124+
'scale': self.scale_factor,
93125
})
94126
return 1
95127

@@ -145,6 +177,7 @@ def grab(self, monitor):
145177
buf_len = monitor['width'] * monitor['height'] * 4 # See [2]
146178
data = ctypes.create_string_buffer(buf_len)
147179
srcdc = ctypes.windll.user32.GetWindowDC(0)
180+
148181
memdc = ctypes.windll.gdi32.CreateCompatibleDC(srcdc)
149182
bmp = ctypes.windll.gdi32.CreateCompatibleBitmap(
150183
srcdc, monitor['width'], monitor['height'])
@@ -184,6 +217,8 @@ def set_argtypes(callback):
184217
callback,
185218
ctypes.wintypes.LPARAM]
186219
ctypes.windll.user32.GetWindowDC.argtypes = [ctypes.wintypes.HWND]
220+
ctypes.windll.gdi32.GetDeviceCaps.argtypes = [ctypes.wintypes.HWND,
221+
ctypes.wintypes.INT]
187222
ctypes.windll.gdi32.CreateCompatibleDC.argtypes = [ctypes.wintypes.HDC]
188223
ctypes.windll.gdi32.CreateCompatibleBitmap.argtypes = [
189224
ctypes.wintypes.HDC,
@@ -219,6 +254,7 @@ def set_restypes():
219254
ctypes.windll.user32.GetSystemMetrics.restype = ctypes.wintypes.INT
220255
ctypes.windll.user32.EnumDisplayMonitors.restype = ctypes.wintypes.BOOL
221256
ctypes.windll.user32.GetWindowDC.restype = ctypes.wintypes.HDC
257+
ctypes.windll.gdi32.GetDeviceCaps.restype = ctypes.wintypes.INT
222258
ctypes.windll.gdi32.CreateCompatibleDC.restype = ctypes.wintypes.HDC
223259
ctypes.windll.gdi32.CreateCompatibleBitmap.restype = \
224260
ctypes.wintypes.HBITMAP

0 commit comments

Comments
 (0)