sway icon indicating copy to clipboard operation
sway copied to clipboard

swaybar shows multiple status lines at once

Open shibe2 opened this issue 6 years ago • 11 comments

  • Sway Version:

sway version 1.2-rc2 (Aug 23 2019, Arch Linux)

  • Configuration File:
bar {
    status_command tail -f ~/sway-status.txt
}

When status command initially immediately outputs multiple lines, they all are shown on the swaybar. When after that the command outputs more lines, only the last line is shown.

Swaybar should always display only the last status line.

To reproduce, create a file ~/sway-status.txt with multiple short lines of text. Set status_command to output that file:

swaymsg bar bar-0 status_command "tail -f ~/sway-status.txt"

shibe2 avatar Aug 25 '19 12:08 shibe2

I cannot seem to reproduce your issue. screenshot of what I managed to reproduce

Also, could we get a little bit more information regarding what you are trying to achieve? It seems a little unorthodox to be creating a status bar in this way (running tail on a text file).

samerickson avatar Aug 25 '19 18:08 samerickson

@samerickson Your screenshot shows exactly the issue. You should only see the last line, "sizth line" as the status.

What I'm trying to achieve

Status command may want to output information as soon as it's available. If shortly after that another piece of information is available, it will output another line. Normally, it works fine. Only if multiple updates happened during the bar startup, both old and new status appear together.

As an example, my program is affected. https://github.com/shibe2/sklt

shibe2 avatar Aug 25 '19 19:08 shibe2

I misunderstood the issue, I thought you were having multiple status bars. Status bars will display newline characters inline. This is useful if you want something like lsblk to be displayed, which is normally a block of text, but in a status bar, you need that information to be in a single line.

In your case, if you are trying to only display the last line of the file, you need to change your tail command. Tail defaults to showing the last 10 lines of a file, to make it display Only the last line, use: tail -n 1.

As for getting your project to function correctly, I have created an issue https://github.com/shibe2/sklt/issues/1, as I cannot seem to make your project function.

samerickson avatar Aug 25 '19 19:08 samerickson

You can change your bar config to link to a script containing the following:

#!/bin/bash
while true;
    printf "%s %s\n" "$(date +%D)" "$(date +%r)"
    sleep 1
done

As for getting it to display the current keyboard layout, you can have a look at this project as well: https://gitlab.com/racy/sway-keyboard-layout/blob/master/sway-keyboard-layout. This makes your code footprint a lot smaller, but I am unsure of the resource consumption differences.

samerickson avatar Aug 25 '19 19:08 samerickson

Thanks for your prompt reply on the sklt issue. Everything appears to work, though for the first minute you have the time listed twice. To circumvent this, it would be best to remove the date in your project its self, rather than trying to remove it using command-line utilities such as tail

samerickson avatar Aug 25 '19 20:08 samerickson

Status bars will display newline characters inline. This is useful if you want something like lsblk to be displayed, which is normally a block of text, but in a status bar, you need that information to be in a single line.

This is not useful with line protocol, where each line is a new status. But even if it was useful, it is currently inconsistent: if the command outputs multiple lines immediately, they appear together; but if it does after some delay, only the last line appears.

In your case, if you are trying to only display the last line of the file, you need to change your tail command.

To be clear, my tail example is purely for reproduction of the issue.

I also have a program that only outputs time. It uses less resources and it is more precise than a simple script. I.e. the time on screen updates almost exactly when the second changes on the computer clock.

As for your suggestion about keyboard layout, it seems to use xkblayout command, and I can't find it in Arch Linux repositories. Also, I tried to use X protocol for getting the current keyboard layout, but the change notification did not work under Wayland. And polling requires a compromise between CPU usage and indicator lag, so I'd like to avoid it. With Sway 1.2 it is possible to get proper notifications about keyboard layout switching, which I used in my program.

shibe2 avatar Aug 25 '19 20:08 shibe2

This seems like an easy fix for anyone interested; just add some logic here: https://github.com/swaywm/sway/blob/master/swaybar/status_line.c#L117 to find the last line that was read.

ianyfan avatar Mar 09 '20 16:03 ianyfan

@ianyfan, for testing purposes, how can I get my built (but not installed) sway branch to stop using the system swaybar? Doesn't seem to respect $PATH

ammgws avatar Mar 13 '20 05:03 ammgws

It should use PATH. You can also set swaybar_command.

emersion avatar Mar 13 '20 09:03 emersion

Or start it manually. For example, if you've compiled it in a directory called build, then you should be able to invoke it like build/swaybar/swaybar -b bar-0 (replacing the bar id appropriately).

ianyfan avatar Mar 13 '20 10:03 ianyfan

:eyes:

haxfn avatar Jul 21 '24 16:07 haxfn

This seems like an easy fix for anyone interested; just add some logic here: https://github.com/swaywm/sway/blob/master/swaybar/status_line.c#L117 to find the last line that was read.

Hello. Based on what I understood from the issue and the comment above, I believe I solved the issue with the following code (starting from https://github.com/swaywm/sway/blob/master/swaybar/status_line.c#L114):

		sway_log(SWAY_DEBUG, "Using text protocol.");
		status->protocol = PROTOCOL_TEXT;
		// Gets starting address (relative to buffer) of last line on file
		size_t i;
		size_t lastline = i = 0;
		while(status->buffer[i] != '\0' && i <= status->buffer_size) {
			if(status->buffer[i] == '\n' && status->buffer[i+1] != '\0') {
				lastline = i + 1;
			}
			i++;
		}
		status->text = status->buffer + lastline; // <== ASSIGN HERE
		// intentional fall-through
	case PROTOCOL_TEXT:
		while (true) {
			if (status->buffer[read_bytes - 1] == '\n') {
				status->buffer[read_bytes - 1] = '\0';
			}
			errno = 0;
			read_bytes = getline(&status->buffer,
					&status->buffer_size, status->read);
			if (errno == EAGAIN) {
				clearerr(status->read);
				return true;
			} else if (errno) {
				status_error(status, "[error reading from status command]");
				return true;
			}
			status->text = status->buffer;    // <== REASSIGN HERE
		}
	case PROTOCOL_I3BAR:
		status->text = status->buffer;  // <== REASSIGN HERE
		return i3bar_handle_readable(status);

Notice I had to reassign status->text three times in the code, otherwise the pointer would get stuck and the output wouldn't update correctly (obviously I would remove the comments in upper case on PR). I believe the logic here is correct, but is the code appropriate for merging? And if not, how would I write the code to be more secure/appropriate to the project? Since this would be my first time contributing, and I didn't find any specifications on how to do so, I figured it would be better to ask than to bother anyone with phony PRs. Thank you!

gabriel-bizioo avatar Jul 01 '25 22:07 gabriel-bizioo