Skip to content

Commit 4c6f8b7

Browse files
committed
This new script is using the functionality of display_info in the checkbox support that using dbus to control directly.
1 parent 4cf29f1 commit 4c6f8b7

File tree

2 files changed

+597
-0
lines changed

2 files changed

+597
-0
lines changed

providers/base/bin/randr_cycle.py

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
#!/usr/bin/env python3
2+
#
3+
# This file is part of Checkbox.
4+
#
5+
# Copyright 2025 Canonical Ltd.
6+
# Written by:
7+
# Hanhsuan Lee <[email protected]>
8+
#
9+
# Checkbox is free software: you can redistribute it and/or modify
10+
# it under the terms of the GNU General Public License version 3,
11+
# as published by the Free Software Foundation.
12+
#
13+
# Checkbox is distributed in the hope that it will be useful,
14+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
# GNU General Public License for more details.
17+
#
18+
# You should have received a copy of the GNU General Public License
19+
# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
20+
21+
22+
from checkbox_support.helpers import display_info
23+
from checkbox_support.dbus.gnome_monitor import Mode
24+
from fractions import Fraction
25+
from typing import List
26+
import subprocess
27+
import argparse
28+
import tarfile
29+
import time
30+
import sys
31+
import os
32+
33+
34+
def resolution_filter(modes: List[Mode]):
35+
"""
36+
For filtering resolution then returning needed,
37+
Following will be ignored:
38+
1. aspect is too small
39+
2. the same resoultion
40+
3. smaller width with the same aspect
41+
This function will be called by the cycle method in the
42+
checkbox_support.dbus.gnome_monitor
43+
44+
:param modes: The list of Mode that defined
45+
in checkbox_support.dbus.gnome_monitor
46+
"""
47+
new_modes = []
48+
tmp_resolution = []
49+
sort_modes = sorted(
50+
modes, key=lambda m: int(m.resolution.split("x")[0]), reverse=True
51+
)
52+
top_res_per_aspect = {}
53+
for m in sort_modes:
54+
width, height = [int(x) for x in m.resolution.split("x")]
55+
aspect = Fraction(width, height)
56+
# Igonre the too small one
57+
if width < 675 or width / aspect < 530:
58+
continue
59+
# Igonre the same one
60+
if m.resolution in tmp_resolution:
61+
continue
62+
# Only take the widthest one with the same aspect
63+
if aspect not in top_res_per_aspect:
64+
top_res_per_aspect[aspect] = (m, width)
65+
new_modes.append(m)
66+
else:
67+
pre_m, pre_width = top_res_per_aspect[aspect]
68+
if pre_width < width:
69+
top_res_per_aspect[aspect] = width
70+
new_modes.append(m)
71+
new_modes.remove(pre_m)
72+
tmp_resolution.append(m.resolution)
73+
74+
return new_modes
75+
76+
77+
def action(filename, **kwargs):
78+
"""
79+
For extra steps for each cycle.
80+
The extra steps is typing and moving mouse randomly
81+
then take a screenshot.
82+
This function will be called by the cycle method in the
83+
checkbox_support.dbus.gnome_monitor
84+
85+
:param filename: The string is constructed by
86+
[monitor name]_[resolution]_[transform]_.
87+
"""
88+
print("Test: {}".format(filename))
89+
if "path" in kwargs:
90+
path_and_filename = "{}/{}.jpg".format(kwargs.get("path"), filename)
91+
else:
92+
path_and_filename = "{}.jpg".format(filename)
93+
time.sleep(5)
94+
# subprocess.check_output(["sudo", "keyboard_mouse"])
95+
subprocess.check_output(["gnome-screenshot", "-f", path_and_filename])
96+
97+
98+
class MonitorTest:
99+
def gen_screenshot_path(self, keyword: str, screenshot_dir: str) -> str:
100+
"""
101+
Generate the screenshot path and create the folder.
102+
If the keyword is not defined, it will check the suspend_stats to
103+
decide the keyowrd should be after_suspend or not
104+
105+
:param keyword: the postfix for the path
106+
107+
:param screenshot_dir: the dictionary for screenshot
108+
"""
109+
path = os.path.join(screenshot_dir, "xrandr_screens")
110+
if keyword and keyword != "":
111+
path = path + "_" + keyword
112+
else:
113+
# check the status is before or after suspend
114+
with open("/sys/power/suspend_stats/success", "r") as s:
115+
suspend_count = s.readline().strip("\n")
116+
if suspend_count != "0":
117+
path = "{}_after_suspend".format(path)
118+
os.makedirs(path, exist_ok=True)
119+
120+
return path
121+
122+
def tar_screenshot_dir(self, path: str):
123+
"""
124+
Tar up the screenshots for uploading.
125+
126+
:param path: the dictionary for screenshot
127+
"""
128+
try:
129+
with tarfile.open(path + ".tgz", "w:gz") as screen_tar:
130+
for screen in os.listdir(path):
131+
screen_tar.add(path + "/" + screen, screen)
132+
except (IOError, OSError):
133+
pass
134+
135+
def parse_args(self, args=sys.argv[1:]):
136+
"""
137+
command line arguments parsing
138+
139+
:param args: arguments from sys
140+
:type args: sys.argv
141+
"""
142+
parser = argparse.ArgumentParser(
143+
prog="monitor tester",
144+
description="Test monitor that could rotate and change resoultion",
145+
)
146+
147+
parser.add_argument(
148+
"-c",
149+
"--cycle",
150+
type=str,
151+
default="both",
152+
help="cycling resolution, transform or both(default: %(default)s)",
153+
)
154+
parser.add_argument(
155+
"--keyword",
156+
default="",
157+
help=(
158+
"A keyword to distinguish the screenshots "
159+
"taken in this run of the script(default: %(default)s)"
160+
),
161+
)
162+
parser.add_argument(
163+
"--screenshot_dir",
164+
default=os.environ["HOME"],
165+
help=(
166+
"Specify a directory to store screenshots in. "
167+
"(default: %(default)s)"
168+
),
169+
)
170+
171+
return parser.parse_args(args)
172+
173+
def main(self):
174+
args = self.parse_args()
175+
176+
try:
177+
monitor_config = display_info.get_monitor_config()
178+
except ValueError as e:
179+
raise SystemExit("Current host is not support: {}".format(e))
180+
181+
screenshot_path = self.gen_screenshot_path(
182+
args.keyword, args.screenshot_dir
183+
)
184+
if args.cycle == "resolution":
185+
monitor_config.cycle(
186+
resolution=True,
187+
resolution_filter=resolution_filter,
188+
transform=False,
189+
action=action,
190+
path=screenshot_path,
191+
)
192+
elif args.cycle == "transform":
193+
monitor_config.cycle(
194+
resolution=False,
195+
resolution_filter=resolution_filter,
196+
transform=True,
197+
action=action,
198+
path=screenshot_path,
199+
)
200+
else:
201+
monitor_config.cycle(
202+
resolution=True,
203+
resolution_filter=resolution_filter,
204+
transform=True,
205+
action=action,
206+
path=screenshot_path,
207+
)
208+
self.tar_screenshot_dir(screenshot_path)
209+
210+
211+
if __name__ == "__main__":
212+
MonitorTest().main()

0 commit comments

Comments
 (0)