mirror of
https://github.com/libratbag/piper.git
synced 2026-04-23 15:58:28 -04:00
267 lines
8.8 KiB
Python
Executable File
267 lines
8.8 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# vim: set expandtab shiftwidth=4 tabstop=4
|
|
|
|
from ratbag import *
|
|
|
|
import gi
|
|
gi.require_version('Gtk', '3.0')
|
|
from gi.repository import Gtk
|
|
|
|
class Piper(Gtk.Window):
|
|
|
|
def _show_error(self, message):
|
|
box = self._builder.get_object("piper-error-box")
|
|
|
|
btn = self._builder.get_object("piper-error-button")
|
|
btn.connect("clicked", Gtk.main_quit)
|
|
|
|
error = self._builder.get_object("piper-error-body-label")
|
|
error.set_text(message)
|
|
|
|
self.add(box)
|
|
|
|
def __init__(self):
|
|
Gtk.Window.__init__(self, title="Piper")
|
|
main_window = Gtk.Builder()
|
|
main_window.add_from_file("piper.ui")
|
|
self._builder = main_window;
|
|
|
|
self._ratbag = self._init_ratbag()
|
|
if self._ratbag == None:
|
|
self._show_error("Can't connect to ratbagd on DBus. That's quite unfortunate.")
|
|
return
|
|
if len(self._ratbag.devices) == 0:
|
|
self._show_error("Could not find any devices. Do you have anything vaguely mouse-looking plugged in?")
|
|
return
|
|
|
|
if len(self._ratbag.devices) > 1:
|
|
print("Ooops, can't deal with more than one device. My bad.")
|
|
for d in self._ratbag.devices[1:]:
|
|
print("Ignoring device {}".format(d.description))
|
|
|
|
self._ratbag_device = self._ratbag.devices[0]
|
|
|
|
|
|
self._profile_buttons = []
|
|
self._current_profile = self._ratbag_device.active_profile
|
|
|
|
grid = main_window.get_object("piper-grid")
|
|
self._init_header(self._ratbag_device)
|
|
self.add(grid)
|
|
|
|
# load the right image
|
|
# FIXME: libratbag images need to be scaled into the available
|
|
# space, disable for now
|
|
# svg = self._ratbag_device.svg
|
|
# svg = "/opt/libratbag/share/libratbag/{}".format(svg)
|
|
# img = main_window.get_object("piper-image-device")
|
|
# img.set_from_file(svg)
|
|
|
|
# init the current profile's data
|
|
p = self._current_profile
|
|
self._init_report_rate(main_window, p)
|
|
self._init_resolution(main_window, p)
|
|
self._init_buttons(main_window, self._ratbag_device)
|
|
|
|
def _init_ratbag(self):
|
|
try:
|
|
return Ratbag()
|
|
except RatbagDBusUnavailable:
|
|
return None
|
|
|
|
def _init_header(self, device):
|
|
hb = Gtk.HeaderBar()
|
|
hb.set_show_close_button(True)
|
|
hb.props.title = "{}".format(device.description)
|
|
self.set_titlebar(hb)
|
|
|
|
# apply/reset buttons
|
|
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
|
Gtk.StyleContext.add_class(box.get_style_context(), "linked")
|
|
|
|
button = Gtk.Button()
|
|
icon = Gio.ThemedIcon(name="edit-undo-symbolic")
|
|
image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
|
|
button.add(image)
|
|
button.connect("clicked", self.on_button_reset_clicked)
|
|
box.add(button)
|
|
|
|
button = Gtk.Button()
|
|
icon = Gio.ThemedIcon(name="document-save-symbolic")
|
|
image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
|
|
button.add(image)
|
|
button.connect("clicked", self.on_button_save_clicked)
|
|
box.add(button)
|
|
|
|
hb.pack_end(box)
|
|
|
|
# Profile buttons
|
|
profiles = device.profiles
|
|
if len(profiles) > 1:
|
|
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
|
Gtk.StyleContext.add_class(box.get_style_context(), "linked")
|
|
|
|
for i, p in enumerate(profiles):
|
|
button = Gtk.ToggleButton("Profile {}".format(i))
|
|
button.connect("toggled", self.on_button_profile_toggled, p)
|
|
button.set_active(p == device.active_profile)
|
|
box.add(button)
|
|
self._profile_buttons.append(button)
|
|
hb.pack_start(box)
|
|
|
|
hb.show_all()
|
|
|
|
def _init_resolution(self, builder, profile):
|
|
res = profile.resolutions
|
|
nres = len(profile.resolutions)
|
|
|
|
for i in range(0, 5):
|
|
sb = builder.get_object("piper-xres-spinbutton{}".format(i + 1))
|
|
if i >= nres:
|
|
sb.set_visible(False)
|
|
continue
|
|
|
|
xres = res[i].resolution[0]
|
|
sb.set_value(xres)
|
|
sb.connect("value-changed", self.on_resolutions_changed)
|
|
|
|
nres_spin = builder.get_object("piper-nresolutions-spin")
|
|
nres_spin.connect("value-changed", self.on_nresolutions_changed, builder)
|
|
nres_spin.set_value(nres)
|
|
nres_spin.set_range(1, nres)
|
|
|
|
def _init_report_rate(self, builder, profile):
|
|
# Note: we simplify here, the UI only allows one report rate and it
|
|
# will be applied to all resolutions
|
|
rate = profile.active_resolution.report_rate
|
|
r500 = builder.get_object("piper-report-rate-500")
|
|
r1000 = builder.get_object("piper-report-rate-1000")
|
|
r500.connect("toggled", self.on_resolution_rate_changed, 500)
|
|
r1000.connect("toggled", self.on_resolution_rate_changed, 1000)
|
|
if rate == 500:
|
|
r500.set_active(True)
|
|
if rate == 1000:
|
|
r1000.set_active(True)
|
|
if rate != 500 and rate != 1000:
|
|
print("Ooops, rate is {} and I don't know how to deal with that.".format(rate))
|
|
r500.set_sensitive(False)
|
|
r1000.set_sensitive(False)
|
|
|
|
c = builder.get_object("piper-resolutions-listbox").get_style_context()
|
|
c.set_junction_sides(Gtk.JunctionSides.BOTTOM)
|
|
c.set_junction_sides(Gtk.JunctionSides.TOP)
|
|
|
|
def _init_buttons(self, builder, device):
|
|
lb = builder.get_object("piper-buttons-listbox")
|
|
lb.remove(builder.get_object("piper-button-listboxrow"))
|
|
|
|
for i, b in enumerate(device.buttons):
|
|
lbr = self._init_button_row(i)
|
|
lb.add(lbr)
|
|
|
|
lb.show_all()
|
|
|
|
def _init_button_row(self, button):
|
|
# FIXME: can't I duplicate this from builder?
|
|
lbr = Gtk.ListBoxRow()
|
|
lbr.height_request = 80
|
|
lbr.width_request = 100
|
|
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
|
l1 = Gtk.Label("Button {}".format(button))
|
|
l1.set_margin_left(12)
|
|
l1.set_margin_top(8)
|
|
l1.set_margin_bottom(8)
|
|
l1.height_request = 32
|
|
box.add(l1)
|
|
|
|
l2 = Gtk.Label()
|
|
l2.set_markup("<b>Button 1 click</b>")
|
|
l2.set_hexpand(True)
|
|
box.add(l2)
|
|
|
|
btn = Gtk.Button("...")
|
|
btn.connect("clicked", self.on_button_click, button)
|
|
box.add(btn)
|
|
lbr.add(box)
|
|
return lbr
|
|
|
|
def on_resolution_rate_changed(self, widget, new_rate):
|
|
if not widget.get_active():
|
|
return
|
|
|
|
print("FIXME: set the report rate for all resolutions to {}".format(new_rate))
|
|
|
|
def on_nresolutions_changed(self, widget, builder):
|
|
nres = widget.get_value_as_int()
|
|
for i in range(0, 5):
|
|
sb = builder.get_object("piper-xres-spinbutton{}".format(i + 1))
|
|
sb.set_sensitive(nres > i)
|
|
|
|
self._adjust_sensitivity_ranges()
|
|
|
|
def on_resolutions_changed(self, widget):
|
|
self._adjust_sensitivity_ranges()
|
|
|
|
def on_button_save_clicked(self, widget):
|
|
print("FIXME: I should save this to the device now")
|
|
|
|
def on_button_reset_clicked(self, widget):
|
|
print("FIXME: I should reload from device now")
|
|
|
|
def on_button_profile_toggled(self, widget, profile):
|
|
print("FIXME: I should switch profiles now")
|
|
if not widget.get_active():
|
|
return
|
|
|
|
for b in self._profile_buttons:
|
|
if b != widget:
|
|
b.set_active(False)
|
|
|
|
def on_button_click(self, widget, button):
|
|
print("FIXME: I should pop down the button config window now")
|
|
|
|
def _adjust_sensitivity_ranges(self):
|
|
"""
|
|
Align the five sensitivity ranges so that the right-most one can
|
|
go to 1200, the left-most one to 200. In between they're bound by
|
|
the previous/next one so the order is always ascending
|
|
"""
|
|
builder = self._builder
|
|
widget = builder.get_object("piper-nresolutions-spin")
|
|
nres = widget.get_value_as_int()
|
|
|
|
min, max = 200, 12000
|
|
|
|
while nres > 0:
|
|
a1 = builder.get_object("piper-xres-adjustment{}".format(nres))
|
|
a1.set_upper(max)
|
|
max = int(a1.get_value())
|
|
|
|
a2 = builder.get_object("piper-xres-adjustment{}".format(nres - 1))
|
|
if a2:
|
|
a1.set_lower(a2.get_value())
|
|
nres -= 1
|
|
|
|
|
|
class PiperImage(Gtk.EventBox):
|
|
def __init__(self, path):
|
|
Gtk.EventBox.__init__(self)
|
|
self._image = Gtk.Image()
|
|
self._image.set_from_file(path)
|
|
self.add(self._image)
|
|
self.connect("button-press-event", self.on_button_clicked)
|
|
|
|
def on_button_clicked(self, widget, event):
|
|
print(event.x)
|
|
|
|
def main():
|
|
win = Piper()
|
|
win.connect("delete-event", Gtk.main_quit)
|
|
win.show()
|
|
Gtk.main()
|
|
|
|
if __name__ == '__main__':
|
|
import signal
|
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
main()
|