diff --git a/doc/dbus.rst b/doc/dbus.rst index d68596ba..2972e396 100644 --- a/doc/dbus.rst +++ b/doc/dbus.rst @@ -80,6 +80,19 @@ org.freedesktop.ratbag1.Manager The **org.freedesktop.ratbag1.Manager** interface is the entry point to interact with ratbagd. +.. attribute:: APIVersion + + :type: i + :flags: read-only,constant + + The DBus API version. This is a stopgap feature while the DBus API + is still in flux. The version has no backwards or + forward-compatibilty guarantees - your client must understand + **exactly** the same version, it is not enough to support an older + or a newer version. + + This API will be removed once the DBus API is declared stable. + .. attribute:: Devices :type: ao diff --git a/meson.build b/meson.build index b2e792b1..ae40bf24 100644 --- a/meson.build +++ b/meson.build @@ -4,6 +4,12 @@ project('libratbag', 'c', default_options : [ 'c_std=gnu99', 'warning_level=2' ], meson_version : '>= 0.40.0') +# The DBus API version. Increase this every time the DBus API changes. +# No backwards/forwards guarantee, clients are expected to understand +# whatever ratbagd speaks or bail out. This should be removed if we ever +# finish the API and declare it stable. +ratbagd_api_version = 1 + # We use libtool-version numbers because it's easier to understand. # Before making a release, the libratbag_so_* and liblur_so_* # numbers should be modified. The components are of the form C:R:A. @@ -35,6 +41,7 @@ add_project_arguments(cflags, language: 'c') config_h = configuration_data() config_h.set('_GNU_SOURCE', '1') config_h.set_quoted('RATBAG_VERSION', meson.project_version()) +config_h.set('RATBAGD_API_VERSION', ratbagd_api_version) libratbag_data_dir = join_paths(get_option('prefix'), get_option('datadir'), 'libratbag') @@ -520,15 +527,22 @@ else py3 = pymod.find_installation() endif +config_ratbagctl = configuration_data() +config_ratbagctl.set('RATBAGD_API_VERSION', ratbagd_api_version) +config_ratbagctl.set('version', meson.project_version()) + +ratbagctl_in = configure_file(input : 'tools/ratbagctl.in.in', + output : 'ratbagctl.in', + configuration: config_ratbagctl) + # ratbagctl is the commandline tool to interact with ratbagd over DBus. custom_target('ratbagctl', output : 'ratbagctl', - input : ['tools/ratbagctl.in', 'tools/ratbagd.py'], + input : [ratbagctl_in, 'tools/ratbagd.py'], build_by_default : true, command : [py3, join_paths(meson.source_root(), 'tools', 'merge_ratbagd.py'), '@INPUT@', - '--output', '@OUTPUT@', - '--version', meson.project_version()], + '--output', '@OUTPUT@'], install: true, install_dir: get_option('bindir')) @@ -543,6 +557,7 @@ man_ratbagctl = configure_file ( config_ratbagctl_devel = configuration_data() config_ratbagctl_devel.set('MESON_BUILD_ROOT', meson.build_root()) config_ratbagctl_devel.set('LIBRATBAG_DATA_DIR', libratbag_data_dir_devel) +config_ratbagctl_devel.set('RATBAGD_API_VERSION', ratbagd_api_version) configure_file(input : 'tools/toolbox.py', output : 'toolbox.py', @@ -610,12 +625,11 @@ ratbagc_py = configure_file(input: 'tools/ratbagc.py.in', # development tool for protocol debugging etc. custom_target('ratbag-command', output : 'ratbag-command', - input : ['tools/ratbagctl.in', ratbagc_py], + input : [ratbagctl_in, ratbagc_py], build_by_default : true, command : [py3, join_paths(meson.source_root(), 'tools', 'merge_ratbagd.py'), '@INPUT@', - '--output', '@OUTPUT@', - '--version', meson.project_version()], + '--output', '@OUTPUT@'], install: false) #### output files #### diff --git a/ratbagd/ratbagd.c b/ratbagd/ratbagd.c index 8bf7101c..3012202c 100644 --- a/ratbagd/ratbagd.c +++ b/ratbagd/ratbagd.c @@ -166,6 +166,7 @@ static int ratbagd_get_devices(sd_bus *bus, static const sd_bus_vtable ratbagd_vtable[] = { SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("APIVersion", "i", 0, offsetof(struct ratbagd, api_version), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Devices", "ao", ratbagd_get_devices, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), #ifdef RATBAG_DEVELOPER_EDITION SD_BUS_METHOD("LoadTestDevice", "s", "i", ratbagd_load_test_device, SD_BUS_VTABLE_UNPRIVILEGED), @@ -345,6 +346,7 @@ static int ratbagd_new(struct ratbagd **out) int r; ctx = zalloc(sizeof(*ctx)); + ctx->api_version = RATBAGD_API_VERSION; r = sd_event_default(&ctx->event); if (r < 0) diff --git a/ratbagd/ratbagd.h b/ratbagd/ratbagd.h index a980db54..17bfa5cb 100644 --- a/ratbagd/ratbagd.h +++ b/ratbagd/ratbagd.h @@ -218,6 +218,8 @@ struct ratbagd_device *ratbagd_device_next(struct ratbagd_device *device); */ struct ratbagd { + int api_version; + sd_event *event; struct ratbag *lib_ctx; struct udev_monitor *monitor; diff --git a/tools/ratbagctl.in b/tools/ratbagctl.in.in similarity index 99% rename from tools/ratbagctl.in rename to tools/ratbagctl.in.in index f3244afc..bfea5ab3 100755 --- a/tools/ratbagctl.in +++ b/tools/ratbagctl.in.in @@ -1465,7 +1465,7 @@ def on_device_added(ratbagd, device): def open_ratbagd(ratbagd_process=None, verbose=0): try: - r = Ratbagd() + r = Ratbagd(@RATBAGD_API_VERSION@) r.verbose = verbose try: r.connect('device-added', on_device_added) diff --git a/tools/ratbagd.py b/tools/ratbagd.py index 3991bda7..d6db205d 100644 --- a/tools/ratbagd.py +++ b/tools/ratbagd.py @@ -62,6 +62,18 @@ class RatbagErrorCode(IntEnum): IMPLEMENTATION = -1004 +class RatbagdIncompatible(Exception): + """ratbagd is incompatible with this client""" + def __init__(self, ratbagd_version, required_version): + super().__init__() + self.ratbagd_version = ratbagd_version + self.required_version = required_version + self.message = "ratbagd API version is {} but we require {}".format(ratbagd_version, required_version) + + def __str__(self): + return self.message + + class RatbagdUnavailable(Exception): """Signals DBus is unavailable or the ratbagd daemon is not available.""" pass @@ -238,11 +250,13 @@ class Ratbagd(_RatbagdDBus): (GObject.SignalFlags.RUN_FIRST, None, ()), } - def __init__(self): + def __init__(self, api_version): super().__init__("Manager", None) result = self._get_dbus_property("Devices") or [] self._devices = [RatbagdDevice(objpath) for objpath in result] self._proxy.connect("notify::g-name-owner", self._on_name_owner_changed) + if self.api_version != api_version: + raise RatbagdIncompatible(self.api_version or -1, api_version) def _on_name_owner_changed(self, *kwargs): self.emit("daemon-disappeared") @@ -261,6 +275,10 @@ class Ratbagd(_RatbagdDBus): self.emit("device-removed", device) self.notify("devices") + @GObject.Property + def api_version(self): + return self._get_dbus_property("APIVersion") + @GObject.Property def devices(self): """A list of RatbagdDevice objects supported by ratbagd."""