~emersion/public-inbox

kanshi: Do not allow substring matching of output names, only exact matching v2 APPLIED

Ivan Oleynikov: 2
 Upgrade wlr-output-management-unstable-v1.xml to version 2
 Improve the make-model-serial matching of outputs

 3 files changed, 125 insertions(+), 10 deletions(-)
#728974 .build.yml success
kanshi/patches/.build.yml: SUCCESS in 24s

[Do not allow substring matching of output names, only exact matching][0] v2 from [Ivan Oleynikov][1]

[0]: https://lists.sr.ht/~emersion/public-inbox/patches/30772
[1]: mailto:ivan.oleynikov95@gmail.com

✓ #728974 SUCCESS kanshi/patches/.build.yml https://builds.sr.ht/~emersion/job/728974
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~emersion/public-inbox/patches/30772/mbox | git am -3
Learn more about email & git

[PATCH kanshi v2 1/2] Upgrade wlr-output-management-unstable-v1.xml to version 2 Export this patch

---
 .../wlr-output-management-unstable-v1.xml     | 83 +++++++++++++++++--
 1 file changed, 77 insertions(+), 6 deletions(-)

diff --git a/protocol/wlr-output-management-unstable-v1.xml b/protocol/wlr-output-management-unstable-v1.xml
index 35f7ca4..bf0cc93 100644
--- a/protocol/wlr-output-management-unstable-v1.xml
+++ b/protocol/wlr-output-management-unstable-v1.xml
@@ -39,7 +39,7 @@
    interface version number is reset.
  </description>

  <interface name="zwlr_output_manager_v1" version="1">
  <interface name="zwlr_output_manager_v1" version="2">
    <description summary="output device configuration manager">
      This interface is a manager that allows reading and writing the current
      output device configuration.
@@ -115,7 +115,7 @@
      </description>
    </request>

    <event name="finished">
    <event name="finished" type="destructor">
      <description summary="the compositor has finished with the manager">
        This event indicates that the compositor is done sending manager events.
        The compositor will destroy the object immediately after sending this
@@ -125,7 +125,7 @@
    </event>
  </interface>

  <interface name="zwlr_output_head_v1" version="1">
  <interface name="zwlr_output_head_v1" version="2">
    <description summary="output device">
      A head is an output device. The difference between a wl_output object and
      a head is that heads are advertised even if they are turned off. A head
@@ -257,9 +257,80 @@
        resources associated with it.
      </description>
    </event>

    <!-- Version 2 additions -->
    <event name="make" since="2">
      <description summary="head manufacturer">
        This event describes the manufacturer of the head.

        This must report the same make as the wl_output interface does in its
        geometry event.

        Together with the model and serial_number events the purpose is to
        allow clients to recognize heads from previous sessions and for example
        load head-specific configurations back.

        It is not guaranteed this event will be ever sent. A reason for that
        can be that the compositor does not have information about the make of
        the head or the definition of a make is not sensible in the current
        setup, for example in a virtual session. Clients can still try to
        identify the head by available information from other events but should
        be aware that there is an increased risk of false positives.

        It is not recommended to display the make string in UI to users. For
        that the string provided by the description event should be preferred.
      </description>
      <arg name="make" type="string"/>
    </event>

    <event name="model" since="2">
      <description summary="head model">
        This event describes the model of the head.

        This must report the same model as the wl_output interface does in its
        geometry event.

        Together with the make and serial_number events the purpose is to
        allow clients to recognize heads from previous sessions and for example
        load head-specific configurations back.

        It is not guaranteed this event will be ever sent. A reason for that
        can be that the compositor does not have information about the model of
        the head or the definition of a model is not sensible in the current
        setup, for example in a virtual session. Clients can still try to
        identify the head by available information from other events but should
        be aware that there is an increased risk of false positives.

        It is not recommended to display the model string in UI to users. For
        that the string provided by the description event should be preferred.
      </description>
      <arg name="model" type="string"/>
    </event>

    <event name="serial_number" since="2">
      <description summary="head serial number">
        This event describes the serial number of the head.

        Together with the make and model events the purpose is to allow clients
        to recognize heads from previous sessions and for example load head-
        specific configurations back.

        It is not guaranteed this event will be ever sent. A reason for that
        can be that the compositor does not have information about the serial
        number of the head or the definition of a serial number is not sensible
        in the current setup. Clients can still try to identify the head by
        available information from other events but should be aware that there
        is an increased risk of false positives.

        It is not recommended to display the serial_number string in UI to
        users. For that the string provided by the description event should be
        preferred.
      </description>
      <arg name="serial_number" type="string"/>
    </event>
  </interface>

  <interface name="zwlr_output_mode_v1" version="1">
  <interface name="zwlr_output_mode_v1" version="2">
    <description summary="output mode">
      This object describes an output mode.

@@ -305,7 +376,7 @@
    </event>
  </interface>

  <interface name="zwlr_output_configuration_v1" version="1">
  <interface name="zwlr_output_configuration_v1" version="2">
    <description summary="output configuration">
      This object is used by the client to describe a full output configuration.

@@ -423,7 +494,7 @@
    </request>
  </interface>

  <interface name="zwlr_output_configuration_head_v1" version="1">
  <interface name="zwlr_output_configuration_head_v1" version="2">
    <description summary="head configuration">
      This object is used by the client to update a single head's configuration.

-- 
2.35.1

[PATCH kanshi v2 2/2] Improve the make-model-serial matching of outputs Export this patch

Previously, the matching algorithm was looking for user-configured
output name as a substring in "$output_description ($output_name)". This
lead to unexpected behaviour when you have two outputs where the one's
description is a substring of the other's.

For example, say, you have monitors "hello (DP-1)" and "hello world
(DP-2)" and you're trying to match them in your Kanshi config using

profile {
  output "hello" …
  output "hello world" …
}

Substring matching allows "hello" to be matched with either of the
outputs, and the profile may end up being applied or rejected depending
on the order in which the outputs are discovered: if "hello world
(DP-2)" is discovered first, it gets matched with "hello", while "hello
world" fails to match with "hello (DP-1)".

This patch makes Kanshi discover the actual values of output's make,
model and serial; and then compares the user-supplied matching
string with "$make $model $serial" instead of looking for it in the
description. This is exactly how swaymsg forms the output descriptions
[1], so the user can simply copy-paste the output descriptions from
swaymsg into Kanshi config and have it uniquely identified.

To implement that, this patch takes advantage of the version 2 of
wlr-output-management-unstable-v1.xml protocol.

This patch fixes my issue [2] that I reported (I don't know if it fixes
the issues of others reported there).

[1]: https://github.com/swaywm/sway/blob/master/swaymsg/main.c#L218
[2]: https://todo.sr.ht/~emersion/kanshi/54
---
 include/kanshi.h |  1 +
 main.c           | 51 ++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/include/kanshi.h b/include/kanshi.h
index 22b8f47..2fc6a8a 100644
--- a/include/kanshi.h
+++ b/include/kanshi.h
@@ -25,6 +25,7 @@ struct kanshi_head {
	struct wl_list link;

	char *name, *description;
	char *make, *model, *serial_number;
	int32_t phys_width, phys_height; // mm
	struct wl_list modes;

diff --git a/main.c b/main.c
index db34789..3cfb53a 100644
--- a/main.c
+++ b/main.c
@@ -22,11 +22,23 @@

static bool match_profile_output(struct kanshi_profile_output *output,
		struct kanshi_head *head) {
	// TODO: improve vendor/model/serial matching

	// Assume the field is empty if it's not set.
	const char *make = head->make ? head->make : "";
	const char *model = head->model ? head->model : "";
	const char *serial_number =
		head->serial_number ? head->serial_number : "";

	char match[strlen(make) + strlen(model) + strlen(serial_number) + 3];
	strcpy(match, make);
	strcat(match, " ");
	strcat(match, model);
	strcat(match, " ");
	strcat(match, serial_number);

	return strcmp(output->name, "*") == 0 ||
		strcmp(output->name, head->name) == 0 ||
		(strchr(output->name, ' ') != NULL &&
		strstr(head->description, output->name) != NULL);
		strcmp(output->name, match) == 0;
}

static bool match_profile(struct kanshi_state *state,
@@ -390,9 +402,37 @@ static void head_handle_finished(void *data,
	zwlr_output_head_v1_destroy(head->wlr_head);
	free(head->name);
	free(head->description);
	if (head->make)
		free(head->make);
	if (head->model)
		free(head->model);
	if (head->serial_number)
		free(head->serial_number);
	free(head);
}

void head_handle_make(void *data,
  struct zwlr_output_head_v1 *zwlr_output_head_v1,
  const char *make) {
  struct kanshi_head *head = data;
  head->make = strdup(make);
}


void head_handle_model(void *data,
  struct zwlr_output_head_v1 *zwlr_output_head_v1,
  const char *model) {
  struct kanshi_head *head = data;
  head->model = strdup(model);
}

void head_handle_serial_number(void *data,
    struct zwlr_output_head_v1 *zwlr_output_head_v1,
    const char *serial_number) {
  struct kanshi_head *head = data;
  head->serial_number = strdup(serial_number);
}

static const struct zwlr_output_head_v1_listener head_listener = {
	.name = head_handle_name,
	.description = head_handle_description,
@@ -404,6 +444,9 @@ static const struct zwlr_output_head_v1_listener head_listener = {
	.transform = head_handle_transform,
	.scale = head_handle_scale,
	.finished = head_handle_finished,
	.make = head_handle_make,
	.model = head_handle_model,
	.serial_number = head_handle_serial_number,
};

static void output_manager_handle_head(void *data,
@@ -459,7 +502,7 @@ static void registry_handle_global(void *data, struct wl_registry *registry,

	if (strcmp(interface, zwlr_output_manager_v1_interface.name) == 0) {
		state->output_manager = wl_registry_bind(registry, name,
			&zwlr_output_manager_v1_interface, 1);
			&zwlr_output_manager_v1_interface, 2);
		zwlr_output_manager_v1_add_listener(state->output_manager,
			&output_manager_listener, state);
	}
-- 
2.35.1
kanshi/patches/.build.yml: SUCCESS in 24s

[Do not allow substring matching of output names, only exact matching][0] v2 from [Ivan Oleynikov][1]

[0]: https://lists.sr.ht/~emersion/public-inbox/patches/30772
[1]: mailto:ivan.oleynikov95@gmail.com

✓ #728974 SUCCESS kanshi/patches/.build.yml https://builds.sr.ht/~emersion/job/728974