It is currently March 28th, 2024, 7:53 pm

ActionTimer

Share and get help with Plugins and Addons
User avatar
Brian
Developer
Posts: 2673
Joined: November 24th, 2011, 1:42 am
Location: Utah

ActionTimer

Post by Brian »

ActionTimer.dll

NOTE: As of the Rainmeter 3.3 r2493 beta of December 27, 2015, ActionTimer is included in the Rainmeter distribution, and there is no need to download, install, or distribute ActionTimer. ActionTimer just comes with Rainmeter.

ActionTimer is a plugin that can execute a series of Rainmeter actions independent of the normal skin Update cycle. This can allow the series of actions to be executed faster (or slower) than the rate defined in Update in [Rainmeter] and can be executed as fast as 1 millisecond apart.

Plugin Options:
  • ActionList(N)
    A numbered option, starting with ActionList1. For example ActionList1, ActionList2 ... ActionListN

    ActionListN is a list of plugin actions, separated by the pipe | character, that will be executed in order.

    Wait (N)
    Between each action in ActionListN, a Wait value is inserted, to tell the plugin how many milliseconds to wait before executing the next action in the list. Wait can be set to any positive integer value.

    Repeat (Action), (N0), (N1) (as of version 0.1.0.6)
    This will perform Action followed by a wait of time N0, and repeat N1 times.

    Example: ActionList1=... | Repeat SomeAction, 5, 10 | ...
    In the above example, once the list hits the 'Repeat' command, it will repeat 'SomeAction' 10 times with a wait of 5 milliseconds between each action.

    Note: This does not apply a 'Wait' after the last command. So the amount of 'Waits' will be the same as Actions minus 1.
  • Actions
    The plugin action options, with any name and in any order, that will be executed by the ActionListN.

    Example:

    Code: Select all

    ActionList1=SetFontSmall|Wait 2|SetFontBig
    SetFontSmall=[!SetOption SomeMeter FontSize "10"][!UpdateMeter SomeMeter][!Redraw]
    SetFontBig=[!SetOption SomeMeter FontSize "15"][!UpdateMeter SomeMeter][!Redraw]
    
    When ActionList1 is executed, perform the SetFontSmall action, then wait for 2 milliseconds, and perform the SetFontBig action.
  • IgnoreWarnings (0/1 Default 0)
    Any ActionListN must completely finish all actions in the list before it can be executed again. Any command to execute the list while it is still performing actions will be ignored, and a warning message will be generated in the log.

    If IgnoreWarnings=1, then the warning messages in the log will be suppressed.
Plugin Commands:

Commands are sent to the plugin using [!CommandMeasure MeasureName "Command"]
  • Execute (N)
    The number of the ActionListN to execute.

    Example:
    LeftMouseUpAction=[!CommandMeasure SomeMeasure "Execute 1"]
  • Stop (N)
    The number of the ActionListN to stop.

    As noted above, Any ActionListN must completely finish all actions in the list before it can be executed again. Any command to execute the list while it is still performing actions will be ignored, and a warning message will be generated in the log.

    The Stop N command will immediately terminate and reset the currently running ActionListN, so it may be executed again.

    Example:
    LeftMouseUpAction=[!CommandMeasure SomeMeasure "Stop 1"][!CommandMeasure SomeMeasure "Execute 1"]

Examples

The following is a .rmskin which will install several example skins demonstrating its use.
ActionTimer_3.0.rmskin

For any changes and additions, check the official Rainmeter documentation here: https://docs.rainmeter.net/manual-beta/plugins/actiontimer/



Known Issues

Care should be taken when executing actions back to back. Some sort of a wait, (ie. Wait 1) should be used in between each action.

For example:

Code: Select all

;Don't do this:
ActionList1=Something1|Something2|Wait 10|Something3
Something1=...
Something2=...
Something3=...

;Do this instead:
ActionList1=Something1|Wait 1|Something2|Wait 10|Something3
Something1=...
Something2=...
Something3=...
The reason for adding an extra 'Wait' between actions is give Rainmeter a chance to perform other important functions without blocking.

-Brian
You do not have the required permissions to view the files attached to this post.
User avatar
jsmorley
Developer
Posts: 22628
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: ActionTimer Overview

Post by jsmorley »

ActionTimer Overview

The Update Challenge

To understand the purpose of ActionTimer, it helps to have some understanding of how Rainmeter runs a skin. On each skin Update, the value set in the [Rainmeter] section of the skin, Rainmeter does the following:
  • Update all measures
  • Execute measure actions
  • Update all meters
  • Redraw the skin
Update has a lower limit of 16, or once every 16 milliseconds. This means that any series of separate actions can only execute every 16 milliseconds. In addition, setting a very low Update rate in [Rainmeter] can cause the entire skin to use a lot of CPU resources, when you may only need it to be "fast" for a specific purpose, perhaps some animation or transition effect. Finally, any series of actions you take in the context of the Update cycle are "blocking", in the sense that Rainmeter must complete them before it is able to continue with other normal skin processing.

While you can "stack up" actions in a single action option:

IfTrueAction=[!SetOption SomeMeter X "5"][!SetOption SomeMeter X "10"]

Rainmeter will run both of the actions in the same Update cycle, before it updates the meters and redraws the skin, and so in effect they both visibly happen "at once". The meter won't visibly move to "5" and then "10", but will just jump straight to "10". The only current solution is to have the two actions take place in two Update cycles, and thus the fastest the series can ever execute is every 16 milliseconds.

Now 16 milliseconds seems pretty fast, and it is. However, if you have some animation with 100 "frames" or want to do some "transition" of a meter; fading the opacity, shifting the color or sliding the position, it is in fact visibly quite slow, and in addition quite CPU "expensive".

Don't forget as well, that that 16 millisecond lower limit is the time Rainmeter will have to do everything in the skin. Update all measures, take all actions, update all meters, and redraw the skin. In reality, even a moderately complicated skin isn't really going to update every 16 milliseconds in any case, it will at best "try to".

The ActionTimer Solution

What ActionTimer does is execute the list of actions defined in ActionListN, one after the other, in a separate thread outside the normal Update cycle, as fast as it possibly can. This is REALLY fast. The Wait N values that you put in between each action in the list is what stops them from appearing to just happen all at once, and allows you to completely control the speed of the series of actions. Wait can be any number, from 1 (one millisecond) to whatever you need.

Since the actions in ActionListN are executed in a separate thread from the main Rainmeter process, the skin will carry on updating normally, while the ActionTimer plugin will control the actions in the list.

Say goodbye to the 16 millisecond limit for your animations or meter transitions, and goodbye to unreasonable CPU usage when you have one simple animation in a larger more complex skin.

The examples in the .rmskin in the post above demonstrate some different ways you might use the plugin. However, it is designed with the maximum amount of flexibility we could give it, and the uses you find for it are only limited by your imagination.
toolbar.gif
Important Considerations
  • Generally, you will need to update meters and redraw the skin between actions in the list.

    Remember our example of "stacking up" actions in a single options above:

    IfTrueAction=[!SetOption SomeMeter X "5"][!SetOption SomeMeter X "10"]

    As we noted, those would in effect both happen "at once" visibly. What ActionTimer does to address this is in effect:

    Code: Select all

    ActionList1=[!SetOption SomeMeter X "5"]|Wait 10|[!SetOption SomeMeter X "10"]
    That Wait, which is the key to ActionTimer, will cause a 10 millisecond delay between each action.

    That is a start, but hold on... since it doesn't update [SomeMeter] or redraw the skin in between, the visible effect is still going to be "all at once". What you really want it to do is:

    Code: Select all

    ActionList1=[!SetOption SomeMeter X "5"][!UpdateMeter SomeMeter][!Redraw]|Wait 10|[!SetOption SomeMeter X "10"][!UpdateMeter SomeMeter][!Redraw]
    While that is the effect we want, ActionListN is actually always defined with separate ActionName options, each called in the order they are in the list:

    Code: Select all

    ActionList1=MoveFive|Wait 10|MoveTen
    MoveFive=[!SetOption SomeMeter X "5"][!UpdateMeter SomeMeter][!Redraw]
    MoveTen=[!SetOption SomeMeter X "10"][!UpdateMeter SomeMeter][!Redraw]
    You can use variables to shorten things as well:

    Code: Select all

    [Variables]
    U=[!UpdateMeter SomeMeter][!Redraw]
    
    [SomeMeasure]
    Measure=Plugin
    Plugin=ActionTimer
    ActionList1=MoveFive|Wait 10|MoveTen
    MoveFive=[!SetOption SomeMeter X "5"]#U#
    MoveTen=[!SetOption SomeMeter X "10"]#U#
    
  • Dynamic variables in actions are only updated when the ActionTimer measure is updated.

    Dynamic #Variables# used in a measure in Rainmeter are resolved only when the measure is updated, and only if DynamicVariables=1 is used on the measure. This means that if we want to "increment" a variable during the ActionListN processing, for instance:

    Code: Select all

    [SomeMeasure]
    Measure=Plugin
    Plugin=ActionTimer
    ActionList1=ChangeSizeCalc|Wait 5|ChangeSizeCalc|Wait 5|ChangeSizeCalc
    ChangeSizeCalc=[!SetVariable Size "(#Size#+5)"]#U#
    DynamicVariables=1
    
    Then not only does our #U# have to take care of updating the meters we want to change, and redraw the skin, but we also need to update the [SomeMeasure] measure between each action:

    Code: Select all

    [Variables]
    Size=8
    U=[!UpdateMeasure SomeMeasure][UpdateMeter SomeMeter][!Redraw]
    
    That way when #Size# is used in the actions, it will always have the "current" value of the variable, and can be used to increment itself.

    Hint: Since an ActionTimer measure returns no value, and does no processing unless called with !CommandMeasure, this would be the only reason to ever update the measure, and generally you can just set UpdateDivider=-1 on it. This can be useful to keep the normal skin update from updating the value of variables in the measure, perhaps before you are ready.
  • ActionListN is a "list" and not a "loop".

    Attempts to create a recurring loop of actions, by for instance having the last (or worse yet only) item in an ActionList execute the same ActionList, is not what is intended for this plugin. It will attempt to protect you from creating a "runaway" measure, by not allowing the list to be executed while it is still running, however even that will cause Rainmeter to use excessive CPU or even become unresponsive if the plugin and Rainmeter are having to deal with the error condition every few milliseconds.

    Don't forget that the ActionList, the plugin, is running in a separate thread from the main skin Update cycle. While that can allow for much faster actions than normal, it also removes the automatic "throttle control" that the Update cycle provides. A loop inside the thread can easily and quickly cause the plugin to overwhelm Rainmeter's ability to manage the action messages coming from it.

    Extreme care should be used when trying to find ways to avoid a long "list", by creating loops "inside" the plugin. It will be very tricky to get them to work right, and in fact they are almost certain to cause problems. Any loops should be "outside' the plugin, using Loop, Calc or String measures with IfCondition / IfMatch testing to restart the plugin as needed to have the ActionList run "endlessly" in a safe way.
You do not have the required permissions to view the files attached to this post.
User avatar
jsmorley
Developer
Posts: 22628
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: ActionTimer Example Gifs

Post by jsmorley »

Some .gifs of the examples in the .rmskin.
FadeClock.gif
ShiftColor.gif
SizeFont.gif
SlideApps.gif
ToolBar.gif
SlideToobar.gif
You do not have the required permissions to view the files attached to this post.
User avatar
Brian
Developer
Posts: 2673
Joined: November 24th, 2011, 1:42 am
Location: Utah

Re: ActionTimer 0.1 [Beta]

Post by Brian »

Thanks for posting these docs, jsmorley.

We have been working on this plugin for some time, and we believe it is time for some testing.

If everything goes well, we will likely be adding this to our build and shipped with Rainmeter as soon as we switch over to VS2015 (as soon as it comes out).

I will even try to keep the version current this time. :-)

-Brian
cfixd
Posts: 31
Joined: September 1st, 2014, 1:38 am

Re: ActionTimer 0.1 [Beta]

Post by cfixd »

Glad to see it :) It's really what I want.
By the way, can something like "Loop" be added into ActionList?
e.g.
"ActionList1=Start|Wait 1|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left"
can be written as "ActionList1=Start|Wait 1|Left|Wait #W#|Loop 2,5|Left" to expand "Left|Wait #W#" 5 times inside the plugin.

Also, can there be a command to stop all other actionlist except one?
e.g.
[!CommandMeasure SomeMeasure "XXX 3"]to replace
[!CommandMeasure SomeMeasure "Stop 1"][!CommandMeasure SomeMeasure "Stop 2"][!CommandMeasure SomeMeasure "Execute 3"]

Just some suggestions, hoping the plugin will be better
User avatar
Brian
Developer
Posts: 2673
Joined: November 24th, 2011, 1:42 am
Location: Utah

Re: ActionTimer 0.1 [Beta]

Post by Brian »

cfixd wrote:By the way, can something like "Loop" be added into ActionList?
This is something I would like to see as well, but we will have to evaluate the pros/cons of it. The one major problem with this, is overloading Rainmeter with bangs every few milliseconds. This can already happen with the plugin as it is right now.

It is easy to forget that 99% of Rainmeter runs in 1 thread. All skins, all updates cycles, all lua scripts, all drawing etc. occurs in the same thread. Sending bangs to that one thread every few milliseconds can really slow down Rainmeter - especially on older systems.


cfixd wrote:Also, can there be a command to stop all other actionlist except one?
Right now there is no plans for this since it can already be done with multiple commands at this time. If more users request this, we can deal with it at that time.
Too bad there is no !CommandMeasureGroup bang.

-Brian
Lucidas
Posts: 16
Joined: August 12th, 2014, 10:17 pm

Re: ActionTimer 0.1 [Beta]

Post by Lucidas »

Brian wrote: It is easy to forget that 99% of Rainmeter runs in 1 thread. All skins, all updates cycles, all lua scripts, all drawing etc. occurs in the same thread. Sending bangs to that one thread every few milliseconds can really slow down Rainmeter - especially on older systems.
What if we would have like this:

Code: Select all

[Rainmeter]
Update=1000
AllowActionTimerOperations=1 
If it would be 0 the timer would just skip the wait and just output the final results?
- Lucida Grande
cfixd
Posts: 31
Joined: September 1st, 2014, 1:38 am

Re: ActionTimer 0.1 [Beta]

Post by cfixd »

Brian wrote: This is something I would like to see as well, but we will have to evaluate the pros/cons of it. The one major problem with this, is overloading Rainmeter with bangs every few milliseconds. This can already happen with the plugin as it is right now.

It is easy to forget that 99% of Rainmeter runs in 1 thread. All skins, all updates cycles, all lua scripts, all drawing etc. occurs in the same thread. Sending bangs to that one thread every few milliseconds can really slow down Rainmeter - especially on older systems.
I mean to make the code clearly.
I've seen
"ActionList1=Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left|Wait #W#|Left"
in the SlideApps.ini
I think it's better to expand the code as above when the code is written as "ActionList1=Left|Wait #W#|Loop 2,28|Left" during the Initialize function.It's just more friendly to the skin writters.
Personly I also want a unlimited loop to run when mouse is over the skin, which needs to change how the plugin works and may takes you too much time, it's all depends on you.
Of course the plugin will make rainmeter slower, but I think it's better than all the skin runs in "Update=20".

Do you have any plan to let the rainmeter runs in more thread?
User avatar
jsmorley
Developer
Posts: 22628
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: ActionTimer 0.1 [Beta]

Post by jsmorley »

I think the idea of some kind of "Repeat" parameter has some merit. While it's a bit wonky, it would make for much shorter ActionListN statements. So in my understanding, |Repeat #W#,10|SomeAction would repeat the "SomeAction" command 10 times, with a #W# "wait" between each.

I also think the "Stop All" command is not a bad idea.

I'm less in love with any kind of "endless" parameter. I think that has a huge potential to be accidentally abused, and in a sense "encourage" behavior that will lead to bad performance or worse.

While the fact that this plugin runs in its own thread makes it much "lighter" than trying to execute stuff like this in the context of the skin Update cycle and a really low update rate, that doesn't mean it is "free". Rainmeter is still having to deal with the series of bangs / commands sent to it, and it would not be hard at all to overwhelm Rainmeter's ability to deal with this, causing skipped update cycles, or high CPU use or even having Rainmeter become "unresponsive".

I'm hesitant to in any way encourage behavior where the skin is just constantly animating something in an endless loop.

The intent of this plugin is to provide a way to do short, targeted animations and meter transitions based on some mouse action or a test of a value returned by a measure during the update cycle.

While the actions in the list(s) are run "outside" the update cycle and are really fast, I think "when" the lists are executed should remain "inside" the update cycle, with the protections that provides against runaway processing.

I can fully understand the idea of having some endless animation while the mouse is over some meter, and it only stops when the mouse moves off. There is nothing wrong with that as a concept, but it really is stretching the intent of this plugin almost further than I am comfortable with.

This would be my only concern with the "Repeat" idea to be honest. While I like it as a way to shorten the code needed, I am going to be sorry we did it if I see |Repeat #W#, 10000000|SomeAction. While we don't want to tell authors how to write skins, and it's your CPU to abuse as you see fit, I want to be sure we balance unlimited flexibility with some care that we don't make it too easy to do it wrong. Hanging or crashing Rainmeter is something we take some care to protect even the newest of skin authors from. In addition, while we again want to leave it up to authors how they write skins, there is some splatter effect on the reputation of Rainmeter itself when someone loads a skin that uses 30% of their CPU.

Keep in mind that the "redraw" of the skin window is relatively "expensive", no matter where you do it. Having some endless animation just hammering redraws really fast, and without any chance for Rainmeter to "catch its breath" during the update cycle, is going to lead to problems, the least of which is high CPU usage for the skin.

Let's be clear. Rainmeter is NEVER going to be "really good" at animations. It will never be something that you want to use to re-create the idea of Windows DreamScene. There is a reason why Microsoft ran screaming from DreamScene, and removed it entirely from Windows. They were right to do so. It was a pig, and if anyone should have been able to effectively integrate animations with the OS, it was them. They couldn't...
User avatar
Brian
Developer
Posts: 2673
Joined: November 24th, 2011, 1:42 am
Location: Utah

Re: ActionTimer 0.1 [Beta]

Post by Brian »

Lucidas wrote:What if we would have like this:

Code: Select all

[Rainmeter]
Update=1000
AllowActionTimerOperations=1 
If it would be 0 the timer would just skip the wait and just output the final results?
I'm not quite sure what you are getting at. The purpose of the plugin is to provide short delays between bangs. The 'waits' are critical for that.

If you mean something else, could you be a bit more clear on what you are proposing?


cfixd wrote:I mean to make the code clearly.
...
I think it's better to expand the code as above when the code is written as "ActionList1=Left|Wait #W#|Loop 2,28|Left" during the Initialize function.It's just more friendly to the skin writters.
Personly I also want a unlimited loop to run when mouse is over the skin, which needs to change how the plugin works and may takes you too much time, it's all depends on you.
The more I think about this, the more I like the idea. Although I think 'Repeat' might be a better command. I would actually define it a little differently, maybe something like this: ActionList1=Repeat Left, #W#, 28, which would repeat the 'Left' action 28 times with a wait of #W# in between.

Anyway, give me some time to think it through, and I should have something by next week (I will be away for a few days).


cfixd wrote:Do you have any plan to let the rainmeter runs in more thread?
Unfortunately, not at this time. I am not entirely sure what it would take, but the first step I would like to see is each skin running in its own thread. Some of the other team members would need to chime in on any complications beyond that.

-Brian