Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom button refresh time #70

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ All attributes are optional, so the most basic plugins simply print lines consis

Lines containing only dashes (`---`) are *separators*.

Lines above the first separator belong to the button itself. If there are multiple such lines, they are displayed in succession, each of them for 3 seconds before switching to the next. Additionally, all button lines get a dropdown menu item, except if their `dropdown` attribute is set to `false`.
Lines above the first separator belong to the button itself. If there are multiple such lines, they are displayed in succession, each of them for 3 seconds before switching to the next (timeout can be configured with `timeout` attribute). Additionally, all button lines get a dropdown menu item, except if their `dropdown` attribute is set to `false`.

Lines below the first separator are rendered as dropdown menu items. Further separators create graphical separator menu items.

Expand Down
30 changes: 15 additions & 15 deletions [email protected]/button.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var ArgosButton = new Lang.Class({
this._isDestroyed = false;

this._updateTimeout = null;
this._cycleTimeout = null;
this._cycleTimeouts = [];

this.connect("destroy", Lang.bind(this, this._onDestroy));

Expand All @@ -59,9 +59,8 @@ var ArgosButton = new Lang.Class({

if (this._updateTimeout !== null)
Mainloop.source_remove(this._updateTimeout);
if (this._cycleTimeout !== null)
Mainloop.source_remove(this._cycleTimeout);

this._cycleTimeouts.forEach(cycle => Mainloop.source_remove(cycle));
this.menu.removeAll();
},

Expand Down Expand Up @@ -131,25 +130,26 @@ var ArgosButton = new Lang.Class({

this.menu.removeAll();

if (this._cycleTimeout !== null) {
Mainloop.source_remove(this._cycleTimeout);
this._cycleTimeout = null;
}
this._cycleTimeouts.forEach(cycle => Mainloop.source_remove(cycle));
this._cycleTimeouts = [];

if (buttonLines.length === 0) {
this._lineView.setMarkup(GLib.markup_escape_text(this._file.get_basename(), -1));
} else if (buttonLines.length === 1) {
this._lineView.setLine(buttonLines[0]);
} else {
this._lineView.setLine(buttonLines[0]);
let i = 0;
this._cycleTimeout = Mainloop.timeout_add_seconds(3, Lang.bind(this, function() {
i++;
this._lineView.setLine(buttonLines[i % buttonLines.length]);
return true;
}));

let fullsec = 0;
for (let j = 0; j < buttonLines.length; j++) {
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, fullsec, Lang.bind(this, function() {
this._cycleTimeouts.push(Mainloop.timeout_add_seconds(fullsec, Lang.bind(this, function() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are there two nested timeouts here? I don't understand this part at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's the tricky part... But after some tries, it is the easiest way I found.

The first wrapping GLib.timeout_add_seconds is just to emulate a "window.setTimeout" event. It only runs once (returning false at line 154) setting the "current" line once. It takes the (current) sum of seconds as a Timeout, so next strings appear after the correct timeout.

Then the second timeout always has a fixed fullsec since the initial iteration over the lines is done and the full sum of seconds is known. This "full sum of seconds" is the timeout for EACH of the lines. Image:

  • Line A, 1 Sec.
  • Line B, 3 Sec.
  • Line C, 2 Sec.

Line A is shown directly (because fullsec is 0 on first run), Line B is shown 0+1 seconds later, Line C is shown 0+1+3 seconds later. After this first launch of the lines, the timeout does not change anymore for any of the lines, because we used the different timeouts in the first loop, so every message is displayed every 0+1+3+2 seconds from now on.

As a native German speaker I hope that I was able to describe the idea good enough to make it understandable. If not so, just keep asking... ;)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So to summarize, each line has its own independent periodic timeout with interval equal to the total timeout of all lines, and each of those timeouts is offset by another timeout equal to the sum of timeouts of all lines before it.

That's a nice idea, but it doesn't quite work. The problem is that Desktop Linux is not a realtime operating system, and functions like timeout_add_seconds don't guarantee anything with respect to wall clock time (this is actually mentioned in the GLib documentation). So in the long run, the individual _cycleTimeouts can get out of sync, to the point where the order in which the lines are displayed is changed. This can't be allowed to happen.

See the _update function in the same file for an approach that solves this problem. At the end of each cycle, a timeout is set that recursively invokes the function. The length of the timeout can depend on the cycle, and thus on the line. This also means that only one timeout is active at any given time, which saves resources.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So to summarize, each line has its own independent periodic timeout with interval equal to the total timeout of all lines, and each of those timeouts is offset by another timeout equal to the sum of timeouts of all lines before it.

check!

[...] this is actually mentioned in the GLib documentation.

check! I got the "recalculation" thing from the docs wrong... 😞

(So the) order in which the lines are displayed is changed. This can't be allowed to happen.

check!

I'll continue optimizing. Thanks for taking the time to respond.

this._lineView.setLine(buttonLines[j]);
return true;
})));
this._lineView.setLine(buttonLines[j]);
return false;
}));
fullsec += +buttonLines[j].timeout || 3;

if (buttonLines[j].dropdown !== "false")
this.menu.addMenuItem(new ArgosMenuItem(this, buttonLines[j]));
}
Expand Down