diff options
Diffstat (limited to 'py/kmsmodeview.py')
-rwxr-xr-x | py/kmsmodeview.py | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/py/kmsmodeview.py b/py/kmsmodeview.py new file mode 100755 index 0000000..355db02 --- /dev/null +++ b/py/kmsmodeview.py @@ -0,0 +1,317 @@ +#!/usr/bin/python3 + +import urwid +import pykms + +def exit_on_q(key): + if key in ('q', 'Q'): + raise urwid.ExitMainLoop() + elif key == 'a': + apply_mode() + +alarm_handle = None + +def recalc_info(l, d): + global alarm_handle + + alarm_handle = None + + for w in recalc_list: + w.recalc() + +def div_or_zero(n, d): + if d == 0: + return 0 + else: + return n / d + +class MyIntEdit(urwid.IntEdit): + _metaclass_ = urwid.signals.MetaSignals + signals = ['value_change'] + + def __init__(self, caption, calc=None): + self._myval = 0 + self._disable_change = False + self._calc = calc + self._updlist = None + + super().__init__(caption, 0) + + def set_edit_text(self, text): + global alarm_handle + + super().set_edit_text(text) + newtext = super().get_edit_text() + new_val = int(newtext) if newtext != "" else 0 + if new_val != self._myval: + self._myval = new_val + if not self._disable_change: + urwid.emit_signal(self, 'value_change', self, self._myval) + + if alarm_handle == None: + alarm_handle = loop.set_alarm_in(0, recalc_info) + + if self._updlist != None: + for w in self._updlist: + w.recalc() + + def recalc(self): + self._disable_change = True + self.set_val(self._calc()) + self._disable_change = False + + def set_val(self, val): + self.set_edit_text(str(int(val))) + + def get_val(self): + return self._myval + + def set_updlist(self, list): + self._updlist = list + + def keypress(self, size, key): + if key == '+': + self.set_edit_text(str(self.value() + 1)) + elif key == '-': + self.set_edit_text(str(self.value() - 1)) + else: + return super().keypress(size, key) + +class MyIntText(urwid.Text): + def __init__(self, fmt, calc=None): + super().__init__("") + self._fmt = fmt + self._calc = calc + + def recalc(self): + val = self._calc() + super().set_text(self._fmt.format(val)) + +def khz_to_ps(khz): + if khz == 0: + return 0 + else: + return 1.0 / khz * 1000 * 1000 * 1000 + +def khz_to_us(khz): + if khz == 0: + return 0 + else: + return 1.0 / khz * 1000 + +pclk_khz_widget = MyIntEdit(u"pclk (kHz) ") +pclk_ps_widget = MyIntText(fmt="pclk {:.2f} ps", calc = lambda: khz_to_ps(pclk_khz_widget.get_val())) + +pclk_widgets = [pclk_khz_widget, pclk_ps_widget] + +pclk_columns = urwid.LineBox(urwid.Columns(pclk_widgets), title = "Pixel clock") + +# Horizontal widgets + +hdisp_widget = MyIntEdit(u"hdisp ", calc = lambda: hdisp2_widget.get_val()) +hfp_widget = MyIntEdit(u"hfp ", calc = lambda: hss_widget.get_val() - hdisp_widget.get_val()) +hsw_widget = MyIntEdit(u"hsw ", calc = lambda: hse_widget.get_val() - hss_widget.get_val()) +hbp_widget = MyIntEdit(u"hbp ", calc = lambda: htot_widget.get_val() - hse_widget.get_val()) + +hdisp2_widget = MyIntEdit(u"hdisp ", calc = lambda: hdisp_widget.get_val()) +hss_widget = MyIntEdit(u"hss ", + calc = lambda: hdisp_widget.get_val() + hfp_widget.get_val()) +hse_widget = MyIntEdit(u"hse ", + calc = lambda: hdisp_widget.get_val() + hfp_widget.get_val() + hsw_widget.get_val()) +htot_widget = MyIntEdit(u"htot ", + calc = lambda: hdisp_widget.get_val() + hfp_widget.get_val() + hsw_widget.get_val() + hbp_widget.get_val()) + +hwidgets1 = [hdisp_widget, hfp_widget, hsw_widget, hbp_widget] +hwidgets2 = [hdisp2_widget, hss_widget, hse_widget, htot_widget] + +horiz_pile1 = urwid.Pile(hwidgets1) +horiz_pile2 = urwid.Pile(hwidgets2) + +h_columns = urwid.LineBox(urwid.Columns([(15, horiz_pile1), (15, horiz_pile2)]), title = "Horizontal") + +# Vertical columns + +vdisp_widget = MyIntEdit(u"vdisp ", calc = lambda: vdisp2_widget.get_val()) +vfp_widget = MyIntEdit(u"vfp ", calc = lambda: vss_widget.get_val() - vdisp_widget.get_val()) +vsw_widget = MyIntEdit(u"vsw ", calc = lambda: vse_widget.get_val() - vss_widget.get_val()) +vbp_widget = MyIntEdit(u"vbp ", calc = lambda: vtot_widget.get_val() - vse_widget.get_val()) + +vdisp2_widget = MyIntEdit(u"vdisp ", calc = lambda: vdisp_widget.get_val()) +vss_widget = MyIntEdit(u"vss ", + calc = lambda: vdisp_widget.get_val() + vfp_widget.get_val()) +vse_widget = MyIntEdit(u"vse ", + calc = lambda: vdisp_widget.get_val() + vfp_widget.get_val() + vsw_widget.get_val()) +vtot_widget = MyIntEdit(u"vtot ", + calc = lambda: vdisp_widget.get_val() + vfp_widget.get_val() + vsw_widget.get_val() + vbp_widget.get_val()) + +vwidgets1 = [vdisp_widget, vfp_widget, vsw_widget, vbp_widget] +vwidgets2 = [vdisp2_widget, vss_widget, vse_widget, vtot_widget] + +vert_pile1 = urwid.Pile(vwidgets1) +vert_pile2 = urwid.Pile(vwidgets2) + +v_columns = urwid.LineBox(urwid.Columns([(15, vert_pile1), (15, vert_pile2)]), title = "Vertical") + +# Info widgets + +line_us_widget = MyIntText(fmt="line {:.2f} us", + calc = lambda: khz_to_us(pclk_khz_widget.get_val()) * htot_widget.get_val()) +line_khz_widget = MyIntText(fmt="line {:.2f} kHz", + calc = lambda: div_or_zero(pclk_khz_widget.get_val(), htot_widget.get_val())) + +frame_tot_widget = MyIntText(fmt="tot {} pix", + calc = lambda: htot_widget.get_val() * vtot_widget.get_val()) +frame_us_widget = MyIntText(fmt="frame {:.2f} ms", + calc = lambda: khz_to_us(pclk_khz_widget.get_val()) * htot_widget.get_val() * vtot_widget.get_val() / 1000) +frame_khz_widget = MyIntText(fmt="frame {:.2f} Hz", + calc = lambda: div_or_zero(pclk_khz_widget.get_val() * 1000, htot_widget.get_val() * vtot_widget.get_val())) + +info_box = urwid.LineBox(urwid.Pile([line_us_widget, line_khz_widget, urwid.Divider(), frame_tot_widget, frame_us_widget, frame_khz_widget]), title = "Info") + +# Set update lists + +recalc_list = [ pclk_ps_widget, line_us_widget, line_khz_widget, frame_tot_widget, frame_us_widget, frame_khz_widget ] + +hdisp_widget.set_updlist([hdisp2_widget, hss_widget, hse_widget, htot_widget]) +hfp_widget.set_updlist([hss_widget, hse_widget, htot_widget]) +hsw_widget.set_updlist([hse_widget, htot_widget]) +hbp_widget.set_updlist([htot_widget]) +hdisp2_widget.set_updlist([hdisp_widget, hfp_widget]) +hss_widget.set_updlist([hfp_widget, hsw_widget]) +hse_widget.set_updlist([hsw_widget, hbp_widget]) +htot_widget.set_updlist([hbp_widget]) + +vdisp_widget.set_updlist([vdisp2_widget, vss_widget, vse_widget, vtot_widget]) +vfp_widget.set_updlist([vss_widget, vse_widget, vtot_widget]) +vsw_widget.set_updlist([vse_widget, vtot_widget]) +vbp_widget.set_updlist([vtot_widget]) +vdisp2_widget.set_updlist([vdisp_widget, vfp_widget]) +vss_widget.set_updlist([vfp_widget, vsw_widget]) +vse_widget.set_updlist([vsw_widget, vbp_widget]) +vtot_widget.set_updlist([vbp_widget]) + +# Flags + +fb = None + +DRM_MODE_FLAG_PHSYNC = (1<<0) +DRM_MODE_FLAG_NHSYNC = (1<<1) +DRM_MODE_FLAG_PVSYNC = (1<<2) +DRM_MODE_FLAG_NVSYNC = (1<<3) +DRM_MODE_FLAG_INTERLACE = (1<<4) +DRM_MODE_FLAG_DBLCLK = (1<<12) + +def mode_is_ilace(mode): + return (mode.flags & DRM_MODE_FLAG_INTERLACE) != 0 + +def apply_mode(): + global fb + + mode = pykms.Videomode() + mode.clock = pclk_khz_widget.get_val() + + mode.hdisplay = hdisp2_widget.get_val() + mode.hsync_start = hss_widget.get_val() + mode.hsync_end = hse_widget.get_val() + mode.htotal = htot_widget.get_val() + + mode.vdisplay = vdisp2_widget.get_val() + mode.vsync_start = vss_widget.get_val() + mode.vsync_end = vse_widget.get_val() + mode.vtotal = vtot_widget.get_val() + + if ilace_box.state: + mode.flags |= DRM_MODE_FLAG_INTERLACE + + if dblclk_box.state: + mode.flags |= DRM_MODE_FLAG_DBLCLK + + if hsync_pol.state == True: + mode.flags |= DRM_MODE_FLAG_PHSYNC + elif hsync_pol.state == False: + mode.flags |= DRM_MODE_FLAG_NHSYNC + + if vsync_pol.state == True: + mode.flags |= DRM_MODE_FLAG_PVSYNC + elif vsync_pol.state == False: + mode.flags |= DRM_MODE_FLAG_NVSYNC + + fb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); + pykms.draw_test_pattern(fb); + + crtc.set_mode(conn, fb, mode) + +def read_mode(mode): + pclk_khz_widget.set_val(mode.clock) + hdisp2_widget.set_val(mode.hdisplay) + hss_widget.set_val(mode.hsync_start) + hse_widget.set_val(mode.hsync_end) + htot_widget.set_val(mode.htotal) + + vdisp2_widget.set_val(mode.vdisplay) + vss_widget.set_val(mode.vsync_start) + vse_widget.set_val(mode.vsync_end) + vtot_widget.set_val(mode.vtotal) + + ilace_box.set_state(mode_is_ilace(mode)) + dblclk_box.set_state((mode.flags & DRM_MODE_FLAG_DBLCLK) != 0) + + sync = 'mixed' + if (mode.flags & DRM_MODE_FLAG_PHSYNC) != 0: + sync = True + elif (mode.flags & DRM_MODE_FLAG_NHSYNC) != 0: + sync = False + hsync_pol.set_state(sync) + + sync = 'mixed' + if (mode.flags & DRM_MODE_FLAG_PVSYNC) != 0: + sync = True + elif (mode.flags & DRM_MODE_FLAG_NVSYNC) != 0: + sync = False + vsync_pol.set_state(sync) + +def apply_press(w): + apply_mode() + +ilace_box = urwid.CheckBox('interlace') +hsync_pol = urwid.CheckBox('hsync positive', has_mixed=True) +vsync_pol = urwid.CheckBox('vsync positive', has_mixed=True) +dblclk_box = urwid.CheckBox('double clock') + +flags_pile = urwid.LineBox(urwid.Pile([ilace_box, hsync_pol, vsync_pol, dblclk_box]), title = "Flags") + +apply_button = urwid.LineBox(urwid.Padding(urwid.Button('apply', on_press=apply_press))) + +# Main + +def mode_press(w, mode): + read_mode(mode) + +def mode_to_str(mode): + return "{}@{}{}".format(mode.name, mode.vrefresh, "i" if mode_is_ilace(mode) else "") + +mode_buttons = [] + +card = pykms.Card() +conn = card.get_first_connected_connector() +crtc = conn.get_current_crtc() +modes = conn.get_modes() +i = 0 +for m in modes: + mode_buttons.append(urwid.Button(mode_to_str(m), on_press=mode_press, user_data=m)) + i += 1 + +modes_pile = urwid.LineBox(urwid.Pile(mode_buttons), title = "Video modes") + +main_pile = urwid.Pile([modes_pile, pclk_columns, urwid.Columns([ h_columns, v_columns ]), info_box, flags_pile, apply_button]) + +main_columns = urwid.Filler(main_pile, valign='top') + +loop = urwid.MainLoop(main_columns, unhandled_input=exit_on_q, handle_mouse=False) + +# select the first mode +mode_press(None, modes[0]) + +loop.run() + +fb = None |