• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
2
3import urwid
4import pykms
5
6def exit_on_q(key):
7	if key in ('q', 'Q'):
8		raise urwid.ExitMainLoop()
9	elif key == 'a':
10		apply_mode()
11
12alarm_handle = None
13
14def recalc_info(l, d):
15	global alarm_handle
16
17	alarm_handle = None
18
19	for w in recalc_list:
20		w.recalc()
21
22def div_or_zero(n, d):
23	if d == 0:
24		return 0
25	else:
26		return n / d
27
28class MyIntEdit(urwid.IntEdit):
29	_metaclass_ = urwid.signals.MetaSignals
30	signals = ['value_change']
31
32	def __init__(self, caption, calc=None):
33		self._myval = 0
34		self._disable_change = False
35		self._calc = calc
36		self._updlist = None
37
38		super().__init__(caption, 0)
39
40	def set_edit_text(self, text):
41		global alarm_handle
42
43		super().set_edit_text(text)
44		newtext = super().get_edit_text()
45		new_val = int(newtext) if newtext != "" else 0
46		if new_val != self._myval:
47			self._myval = new_val
48			if not self._disable_change:
49				urwid.emit_signal(self, 'value_change', self, self._myval)
50
51				if alarm_handle == None:
52					alarm_handle = loop.set_alarm_in(0, recalc_info)
53
54				if self._updlist != None:
55					for w in self._updlist:
56						w.recalc()
57
58	def recalc(self):
59		self._disable_change = True
60		self.set_val(self._calc())
61		self._disable_change = False
62
63	def set_val(self, val):
64		self.set_edit_text(str(int(val)))
65
66	def get_val(self):
67		return self._myval
68
69	def set_updlist(self, list):
70		self._updlist = list
71
72	def keypress(self, size, key):
73		if key == '+':
74			self.set_edit_text(str(self.value() + 1))
75		elif key == '-':
76			self.set_edit_text(str(self.value() - 1))
77		else:
78			return super().keypress(size, key)
79
80class MyIntText(urwid.Text):
81	def __init__(self, fmt, calc=None):
82		super().__init__("")
83		self._fmt = fmt
84		self._calc = calc
85
86	def recalc(self):
87		val = self._calc()
88		super().set_text(self._fmt.format(val))
89
90def khz_to_ps(khz):
91	if khz == 0:
92		return 0
93	else:
94		return 1.0 / khz * 1000 * 1000 * 1000
95
96def khz_to_us(khz):
97	if khz == 0:
98		return 0
99	else:
100		return 1.0 / khz * 1000
101
102pclk_khz_widget = MyIntEdit(u"pclk (kHz) ")
103pclk_ps_widget = MyIntText(fmt="pclk {:.2f} ps", calc = lambda: khz_to_ps(pclk_khz_widget.get_val()))
104
105pclk_widgets = [pclk_khz_widget, pclk_ps_widget]
106
107pclk_columns = urwid.LineBox(urwid.Columns(pclk_widgets), title = "Pixel clock")
108
109# Horizontal widgets
110
111hdisp_widget = MyIntEdit(u"hdisp ", calc = lambda: hdisp2_widget.get_val())
112hfp_widget = MyIntEdit(u"hfp   ", calc = lambda: hss_widget.get_val() - hdisp_widget.get_val())
113hsw_widget = MyIntEdit(u"hsw   ", calc = lambda: hse_widget.get_val() - hss_widget.get_val())
114hbp_widget = MyIntEdit(u"hbp   ", calc = lambda: htot_widget.get_val() - hse_widget.get_val())
115
116hdisp2_widget = MyIntEdit(u"hdisp ", calc = lambda: hdisp_widget.get_val())
117hss_widget = MyIntEdit(u"hss   ",
118	calc = lambda: hdisp_widget.get_val() + hfp_widget.get_val())
119hse_widget = MyIntEdit(u"hse   ",
120	calc = lambda: hdisp_widget.get_val() + hfp_widget.get_val() + hsw_widget.get_val())
121htot_widget = MyIntEdit(u"htot  ",
122	calc = lambda: hdisp_widget.get_val() + hfp_widget.get_val() + hsw_widget.get_val() + hbp_widget.get_val())
123
124hwidgets1 = [hdisp_widget, hfp_widget, hsw_widget, hbp_widget]
125hwidgets2 = [hdisp2_widget, hss_widget, hse_widget, htot_widget]
126
127horiz_pile1 = urwid.Pile(hwidgets1)
128horiz_pile2 = urwid.Pile(hwidgets2)
129
130h_columns = urwid.LineBox(urwid.Columns([(15, horiz_pile1), (15, horiz_pile2)]), title = "Horizontal")
131
132# Vertical columns
133
134vdisp_widget = MyIntEdit(u"vdisp ", calc = lambda: vdisp2_widget.get_val())
135vfp_widget = MyIntEdit(u"vfp   ", calc = lambda: vss_widget.get_val() - vdisp_widget.get_val())
136vsw_widget = MyIntEdit(u"vsw   ", calc = lambda: vse_widget.get_val() - vss_widget.get_val())
137vbp_widget = MyIntEdit(u"vbp   ", calc = lambda: vtot_widget.get_val() - vse_widget.get_val())
138
139vdisp2_widget = MyIntEdit(u"vdisp ", calc = lambda: vdisp_widget.get_val())
140vss_widget = MyIntEdit(u"vss   ",
141	calc = lambda: vdisp_widget.get_val() + vfp_widget.get_val())
142vse_widget = MyIntEdit(u"vse   ",
143	calc = lambda: vdisp_widget.get_val() + vfp_widget.get_val() + vsw_widget.get_val())
144vtot_widget = MyIntEdit(u"vtot  ",
145	calc = lambda: vdisp_widget.get_val() + vfp_widget.get_val() + vsw_widget.get_val() + vbp_widget.get_val())
146
147vwidgets1 = [vdisp_widget, vfp_widget, vsw_widget, vbp_widget]
148vwidgets2 = [vdisp2_widget, vss_widget, vse_widget, vtot_widget]
149
150vert_pile1 = urwid.Pile(vwidgets1)
151vert_pile2 = urwid.Pile(vwidgets2)
152
153v_columns = urwid.LineBox(urwid.Columns([(15, vert_pile1), (15, vert_pile2)]), title = "Vertical")
154
155# Info widgets
156
157line_us_widget = MyIntText(fmt="line {:.2f} us",
158	calc = lambda: khz_to_us(pclk_khz_widget.get_val()) * htot_widget.get_val())
159line_khz_widget = MyIntText(fmt="line {:.2f} kHz",
160	calc = lambda: div_or_zero(pclk_khz_widget.get_val(), htot_widget.get_val()))
161
162frame_tot_widget = MyIntText(fmt="tot {} pix",
163	calc = lambda: htot_widget.get_val() * vtot_widget.get_val())
164frame_us_widget = MyIntText(fmt="frame {:.2f} ms",
165	calc = lambda: khz_to_us(pclk_khz_widget.get_val()) * htot_widget.get_val() * vtot_widget.get_val() / 1000)
166frame_khz_widget = MyIntText(fmt="frame {:.2f} Hz",
167	calc = lambda: div_or_zero(pclk_khz_widget.get_val() * 1000,  htot_widget.get_val() * vtot_widget.get_val()))
168
169info_box = urwid.LineBox(urwid.Pile([line_us_widget, line_khz_widget, urwid.Divider(), frame_tot_widget, frame_us_widget, frame_khz_widget]), title = "Info")
170
171# Set update lists
172
173recalc_list = [ pclk_ps_widget, line_us_widget, line_khz_widget, frame_tot_widget, frame_us_widget, frame_khz_widget ]
174
175hdisp_widget.set_updlist([hdisp2_widget, hss_widget, hse_widget, htot_widget])
176hfp_widget.set_updlist([hss_widget, hse_widget, htot_widget])
177hsw_widget.set_updlist([hse_widget, htot_widget])
178hbp_widget.set_updlist([htot_widget])
179hdisp2_widget.set_updlist([hdisp_widget, hfp_widget])
180hss_widget.set_updlist([hfp_widget, hsw_widget])
181hse_widget.set_updlist([hsw_widget, hbp_widget])
182htot_widget.set_updlist([hbp_widget])
183
184vdisp_widget.set_updlist([vdisp2_widget, vss_widget, vse_widget, vtot_widget])
185vfp_widget.set_updlist([vss_widget, vse_widget, vtot_widget])
186vsw_widget.set_updlist([vse_widget, vtot_widget])
187vbp_widget.set_updlist([vtot_widget])
188vdisp2_widget.set_updlist([vdisp_widget, vfp_widget])
189vss_widget.set_updlist([vfp_widget, vsw_widget])
190vse_widget.set_updlist([vsw_widget, vbp_widget])
191vtot_widget.set_updlist([vbp_widget])
192
193# Flags
194
195fb = None
196
197DRM_MODE_FLAG_PHSYNC = (1<<0)
198DRM_MODE_FLAG_NHSYNC = (1<<1)
199DRM_MODE_FLAG_PVSYNC = (1<<2)
200DRM_MODE_FLAG_NVSYNC = (1<<3)
201DRM_MODE_FLAG_INTERLACE = (1<<4)
202DRM_MODE_FLAG_DBLCLK = (1<<12)
203
204def mode_is_ilace(mode):
205	return (mode.flags & DRM_MODE_FLAG_INTERLACE) != 0
206
207def apply_mode():
208	global fb
209
210	mode = pykms.Videomode()
211	mode.clock = pclk_khz_widget.get_val()
212
213	mode.hdisplay = hdisp2_widget.get_val()
214	mode.hsync_start = hss_widget.get_val()
215	mode.hsync_end = hse_widget.get_val()
216	mode.htotal = htot_widget.get_val()
217
218	mode.vdisplay = vdisp2_widget.get_val()
219	mode.vsync_start = vss_widget.get_val()
220	mode.vsync_end = vse_widget.get_val()
221	mode.vtotal = vtot_widget.get_val()
222
223	if ilace_box.state:
224		mode.flags |= DRM_MODE_FLAG_INTERLACE
225
226	if dblclk_box.state:
227		mode.flags |= DRM_MODE_FLAG_DBLCLK
228
229	if hsync_pol.state == True:
230		mode.flags |= DRM_MODE_FLAG_PHSYNC
231	elif hsync_pol.state == False:
232		mode.flags |= DRM_MODE_FLAG_NHSYNC
233
234	if vsync_pol.state == True:
235		mode.flags |= DRM_MODE_FLAG_PVSYNC
236	elif vsync_pol.state == False:
237		mode.flags |= DRM_MODE_FLAG_NVSYNC
238
239	fb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24");
240	pykms.draw_test_pattern(fb);
241
242	crtc.set_mode(conn, fb, mode)
243
244def read_mode(mode):
245	pclk_khz_widget.set_val(mode.clock)
246	hdisp2_widget.set_val(mode.hdisplay)
247	hss_widget.set_val(mode.hsync_start)
248	hse_widget.set_val(mode.hsync_end)
249	htot_widget.set_val(mode.htotal)
250
251	vdisp2_widget.set_val(mode.vdisplay)
252	vss_widget.set_val(mode.vsync_start)
253	vse_widget.set_val(mode.vsync_end)
254	vtot_widget.set_val(mode.vtotal)
255
256	ilace_box.set_state(mode_is_ilace(mode))
257	dblclk_box.set_state((mode.flags & DRM_MODE_FLAG_DBLCLK) != 0)
258
259	sync = 'mixed'
260	if (mode.flags & DRM_MODE_FLAG_PHSYNC) != 0:
261		sync = True
262	elif (mode.flags & DRM_MODE_FLAG_NHSYNC) != 0:
263		sync = False
264	hsync_pol.set_state(sync)
265
266	sync = 'mixed'
267	if (mode.flags & DRM_MODE_FLAG_PVSYNC) != 0:
268		sync = True
269	elif (mode.flags & DRM_MODE_FLAG_NVSYNC) != 0:
270		sync = False
271	vsync_pol.set_state(sync)
272
273def apply_press(w):
274	apply_mode()
275
276ilace_box = urwid.CheckBox('interlace')
277hsync_pol = urwid.CheckBox('hsync positive', has_mixed=True)
278vsync_pol = urwid.CheckBox('vsync positive', has_mixed=True)
279dblclk_box = urwid.CheckBox('double clock')
280
281flags_pile = urwid.LineBox(urwid.Pile([ilace_box, hsync_pol, vsync_pol, dblclk_box]), title = "Flags")
282
283apply_button = urwid.LineBox(urwid.Padding(urwid.Button('apply', on_press=apply_press)))
284
285# Main
286
287def mode_press(w, mode):
288	read_mode(mode)
289
290def mode_to_str(mode):
291	return "{}@{}{}".format(mode.name, mode.vrefresh, "i" if mode_is_ilace(mode) else "")
292
293mode_buttons = []
294
295card = pykms.Card()
296conn = card.get_first_connected_connector()
297crtc = conn.get_current_crtc()
298modes = conn.get_modes()
299i = 0
300for m in modes:
301	mode_buttons.append(urwid.Button(mode_to_str(m), on_press=mode_press, user_data=m))
302	i += 1
303
304modes_pile = urwid.LineBox(urwid.Pile(mode_buttons), title = "Video modes")
305
306main_pile = urwid.Pile([modes_pile, pclk_columns, urwid.Columns([ h_columns, v_columns ]), info_box, flags_pile, apply_button])
307
308main_columns = urwid.Filler(main_pile, valign='top')
309
310loop = urwid.MainLoop(main_columns, unhandled_input=exit_on_q, handle_mouse=False)
311
312# select the first mode
313mode_press(None, modes[0])
314
315loop.run()
316
317fb = None
318