mirror of
https://github.com/Llewellynvdm/conky.git
synced 2024-09-29 21:49:07 +00:00
Merge pull request #192 from marcpayne/exec-cb-refactor
Refactor the exec callback system
This commit is contained in:
commit
d2d72e2abd
@ -99,8 +99,8 @@
|
|||||||
<option>default_bar_height</option>
|
<option>default_bar_height</option>
|
||||||
</command>
|
</command>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Specify a default height for bars. This is particularly useful for execbar and
|
<listitem>Specify a default height for bars. If not specified,
|
||||||
execibar as they do not take size arguments.
|
the default value is 6.
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -109,8 +109,13 @@
|
|||||||
<option>default_bar_width</option>
|
<option>default_bar_width</option>
|
||||||
</command>
|
</command>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Specify a default width for bars. This is particularly useful for execbar and
|
<listitem>Specify a default width for bars. If not specified,
|
||||||
execibar as they do not take size arguments.
|
the default value is 0, which causes the bar to expand to
|
||||||
|
fit the width of your Conky window. If you set
|
||||||
|
out_to_console = true, the text version of the bar will
|
||||||
|
actually have no width and you will need to set a
|
||||||
|
sensible default or set the height and width of each bar
|
||||||
|
individually.
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -128,8 +133,8 @@
|
|||||||
<option>default_gauge_height</option>
|
<option>default_gauge_height</option>
|
||||||
</command>
|
</command>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Specify a default height for gauges. This is particularly useful for execgauge
|
<listitem>Specify a default height for gauges. If not specified,
|
||||||
and execigauge as they do not take size arguments
|
the default value is 25.
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -138,8 +143,8 @@
|
|||||||
<option>default_gauge_width</option>
|
<option>default_gauge_width</option>
|
||||||
</command>
|
</command>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Specify a default width for gauges. This is particularly useful for execgauge
|
<listitem>Specify a default width for gauges. If not specified,
|
||||||
and execigauge as they do not take size arguments
|
the default value is 40.
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -148,8 +153,8 @@
|
|||||||
<option>default_graph_height</option>
|
<option>default_graph_height</option>
|
||||||
</command>
|
</command>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Specify a default height for graphs. This is particularly useful for execgraph
|
<listitem>Specify a default height for graphs. If not specified,
|
||||||
and execigraph as they do not take size arguments
|
the default value is 25.
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -158,8 +163,13 @@
|
|||||||
<option>default_graph_width</option>
|
<option>default_graph_width</option>
|
||||||
</command>
|
</command>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Specify a default width for graphs. This is particularly useful for execgraph
|
<listitem>Specify a default width for graphs. If not specified,
|
||||||
and execigraph as they do not take size arguments
|
the default value is 0, which causes the graph to expand to
|
||||||
|
fit the width of your Conky window. If you set
|
||||||
|
out_to_console = true, the text version of the graph will
|
||||||
|
actually have no width and you will need to set a
|
||||||
|
sensible default or set the height and width of each graph
|
||||||
|
individually.
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
@ -1118,8 +1118,8 @@
|
|||||||
<option>command</option>
|
<option>command</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Executes a shell command and displays the output
|
<listitem>Executes a shell command and displays the output
|
||||||
in conky. warning: this takes a lot more resources than
|
in conky. Warning: this takes a lot more resources than
|
||||||
other variables. I'd recommend coding wanted behaviour in C
|
other variables. I'd recommend coding wanted behaviour in C/C++
|
||||||
and posting a patch.
|
and posting a patch.
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -1128,12 +1128,14 @@
|
|||||||
<command>
|
<command>
|
||||||
<option>execbar</option>
|
<option>execbar</option>
|
||||||
</command>
|
</command>
|
||||||
|
<option>(height),(width)</option>
|
||||||
<option>command</option>
|
<option>command</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Same as exec, except if the first value return is
|
<listitem>Same as exec, except if the first value returned is
|
||||||
a value between 0-100, it will use that number for a bar.
|
a value between 0-100, it will use that number to draw a
|
||||||
The size for bars can be controlled via the
|
horizontal bar. The height and width parameters are optional,
|
||||||
default_bar_size config setting.
|
and default to the default_bar_height and default_bar_width
|
||||||
|
config settings, respectively.
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -1141,12 +1143,15 @@
|
|||||||
<command>
|
<command>
|
||||||
<option>execgauge</option>
|
<option>execgauge</option>
|
||||||
</command>
|
</command>
|
||||||
|
<option>(height),(width)</option>
|
||||||
<option>command</option>
|
<option>command</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Same as exec, except if the first value returned
|
<listitem>Same as exec, except if the first value returned is
|
||||||
is a value between 0-100, it will use that number for a
|
a value between 0-100, it will use that number to draw a
|
||||||
gauge. The size for gauges can be controlled via the
|
round gauge (much like a vehicle speedometer). The height and
|
||||||
default_gauge_size config setting.
|
width parameters are optional, and default to the
|
||||||
|
default_gauge_height and default_gauge_width config settings,
|
||||||
|
respectively.
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -1154,33 +1159,61 @@
|
|||||||
<command>
|
<command>
|
||||||
<option>execgraph</option>
|
<option>execgraph</option>
|
||||||
</command>
|
</command>
|
||||||
<option>(-t) (-l) command</option>
|
<option>command</option>
|
||||||
|
<option>(height),(width)</option>
|
||||||
|
<option>(gradient color 1)</option>
|
||||||
|
<option>(gradient color 2)</option>
|
||||||
|
<option>(scale)</option>
|
||||||
|
<option>(-t)</option>
|
||||||
|
<option>(-l)</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Same as execbar, but graphs values. Uses a
|
<listitem>
|
||||||
logaritmic scale when the log option (-l switch) is given
|
<para>Draws a horizontally scrolling graph with values
|
||||||
(to see small numbers). Values still have to be between 0
|
from 0-100 plotted on the vertical axis. All parameters
|
||||||
and 100. The size for graphs can be controlled via the
|
following the command are optional. Gradient colors can
|
||||||
default_graph_size config setting. Takes the switch '-t' to
|
be specified as hexadecimal values with no 0x or #
|
||||||
use a temperature gradient, which makes the gradient values
|
prefix. Use the -t switch to enable a temperature
|
||||||
change depending on the amplitude of a particular graph
|
gradient, so that small values are "cold" with color 1
|
||||||
value (try it and see). If -t or -l is your first argument,
|
and large values are "hot" with color 2. Without the -t
|
||||||
you may need to preceed it by a space (' '). You may also use
|
switch, the colors produce a horizontal gradient
|
||||||
double-quotes around the exec argument should you need to execute a
|
spanning the width of the graph. The scale parameter
|
||||||
command with spaces. For example, ${execgraph "date +'%S'"} to execute
|
defines the maximum value of the graph. Use the -l
|
||||||
`date +'%S'` and graph the result. Without quotes, it would simply
|
switch to enable a logarithmic scale, which helps to see
|
||||||
print the result of `date`.
|
small values. The default size for graphs can be
|
||||||
<para /></listitem>
|
controlled via the default_graph_height and
|
||||||
|
default_graph_width config settings.</para>
|
||||||
|
|
||||||
|
<para>If you need to execute a command with spaces, you
|
||||||
|
have a couple options: 1) put your command into a
|
||||||
|
separate file, such as ~/bin/myscript.sh, and use that
|
||||||
|
as your execgraph command. Remember to make your script
|
||||||
|
executable! 2) Wrap your command in double-quotes. If
|
||||||
|
you go for this option, do not try to pass the extra
|
||||||
|
parameters for height, width, etc., as the results may
|
||||||
|
be unexpected.</para>
|
||||||
|
|
||||||
|
<para>Option 1 is preferred. In the following example, we
|
||||||
|
set up execgraph to display seconds (0-59) on a graph that
|
||||||
|
is 50px high and 200px wide, using a temperature gradient
|
||||||
|
with colors ranging from red for small values (FF0000) to
|
||||||
|
yellow for large values (FFFF00). We set the scale to
|
||||||
|
60.</para>
|
||||||
|
|
||||||
|
<para><command>${execgraph ~/seconds.sh 50,200 FF0000
|
||||||
|
FFFF00 60 -t}</command></para>
|
||||||
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term>
|
<term>
|
||||||
<command>
|
<command>
|
||||||
<option>execi</option>
|
<option>execi</option>
|
||||||
</command>
|
</command>
|
||||||
<option>interval command</option>
|
<option>interval</option>
|
||||||
|
<option>command</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Same as exec but with specific interval. Interval
|
<listitem>Same as exec, but with a specific interval in
|
||||||
can't be less than update_interval in configuration. See
|
seconds. The interval can't be less than the update_interval
|
||||||
also $texeci
|
in your configuration. See also $texeci.
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -1188,9 +1221,11 @@
|
|||||||
<command>
|
<command>
|
||||||
<option>execibar</option>
|
<option>execibar</option>
|
||||||
</command>
|
</command>
|
||||||
<option>interval command</option>
|
<option>interval</option>
|
||||||
|
<option>(height),(width)</option>
|
||||||
|
<option>command</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Same as execbar, except with an interval
|
<listitem>Same as execbar, but with an interval.
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -1198,10 +1233,11 @@
|
|||||||
<command>
|
<command>
|
||||||
<option>execigauge</option>
|
<option>execigauge</option>
|
||||||
</command>
|
</command>
|
||||||
<option>interval command</option>
|
<option>interval</option>
|
||||||
|
<option>(height),(width)</option>
|
||||||
|
<option>command</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Same as execgauge, but takes an interval arg and
|
<listitem>Same as execgauge, but with an interval.
|
||||||
gauges values.
|
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -1209,11 +1245,16 @@
|
|||||||
<command>
|
<command>
|
||||||
<option>execigraph</option>
|
<option>execigraph</option>
|
||||||
</command>
|
</command>
|
||||||
<option>interval (-t) (-l) command</option>
|
<option>interval</option>
|
||||||
|
<option>command</option>
|
||||||
|
<option>(height),(width)</option>
|
||||||
|
<option>(gradient color 1)</option>
|
||||||
|
<option>(gradient color 2)</option>
|
||||||
|
<option>(scale)</option>
|
||||||
|
<option>(-t)</option>
|
||||||
|
<option>(-l)</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Same as execgraph, but takes an interval arg and
|
<listitem>Same as execgraph, but with an interval.
|
||||||
graphs values. If -t or -l is your first argument, you may
|
|
||||||
need to preceed it by a space (' ').
|
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -1224,8 +1265,8 @@
|
|||||||
<option>command</option>
|
<option>command</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Executes a shell command and displays the output
|
<listitem>Executes a shell command and displays the output
|
||||||
in conky. warning: this takes a lot more resources than
|
in conky. Warning: this takes a lot more resources than
|
||||||
other variables. I'd recommend coding wanted behaviour in C
|
other variables. I'd recommend coding wanted behaviour in C/C++
|
||||||
and posting a patch. This differs from $exec in that it
|
and posting a patch. This differs from $exec in that it
|
||||||
parses the output of the command, so you can insert things
|
parses the output of the command, so you can insert things
|
||||||
like ${color red}hi!${color} in your script and have it
|
like ${color red}hi!${color} in your script and have it
|
||||||
@ -1243,12 +1284,12 @@
|
|||||||
<command>
|
<command>
|
||||||
<option>execpi</option>
|
<option>execpi</option>
|
||||||
</command>
|
</command>
|
||||||
<option>interval command</option>
|
<option>interval</option>
|
||||||
|
<option>command</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Same as execp but with specific interval.
|
<listitem>Same as execp, but with an interval. Note that
|
||||||
Interval can't be less than update_interval in
|
the output from the $execpi command is still parsed
|
||||||
configuration. Note that the output from the $execpi
|
and evaluated at every interval.
|
||||||
command is still parsed and evaluated at every interval.
|
|
||||||
<para /></listitem>
|
<para /></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -3769,13 +3810,14 @@
|
|||||||
<command>
|
<command>
|
||||||
<option>texeci</option>
|
<option>texeci</option>
|
||||||
</command>
|
</command>
|
||||||
<option>interval command</option>
|
<option>interval</option>
|
||||||
|
<option>command</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Runs a command at an interval inside a thread and
|
<listitem>Runs a command at an interval inside a thread and
|
||||||
displays the output. Same as $execi, except the command is
|
displays the output. Same as $execi, except the command is
|
||||||
run inside a thread. Use this if you have a slow script to
|
run inside a thread. Use this if you have a slow script to
|
||||||
keep Conky updating. You should make the interval slightly
|
keep Conky updating. You should make the interval slightly
|
||||||
longer then the time it takes your script to execute. For
|
longer than the time it takes your script to execute. For
|
||||||
example, if you have a script that take 5 seconds to
|
example, if you have a script that take 5 seconds to
|
||||||
execute, you should make the interval at least 6 seconds.
|
execute, you should make the interval at least 6 seconds.
|
||||||
See also $execi. This object will clean up the thread when
|
See also $execi. This object will clean up the thread when
|
||||||
@ -3789,7 +3831,8 @@
|
|||||||
<command>
|
<command>
|
||||||
<option>texecpi</option>
|
<option>texecpi</option>
|
||||||
</command>
|
</command>
|
||||||
<option>interval command</option>
|
<option>interval</option>
|
||||||
|
<option>command</option>
|
||||||
</term>
|
</term>
|
||||||
<listitem>Same as execpi, except the command is run inside
|
<listitem>Same as execpi, except the command is run inside
|
||||||
a thread.
|
a thread.
|
||||||
|
@ -275,15 +275,13 @@ conky::simple_config_setting<bool> no_buffers("no_buffers", true, true);
|
|||||||
|
|
||||||
void update_stuff(void)
|
void update_stuff(void)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
|
|
||||||
/* clear speeds, addresses and up status in case device was removed and
|
/* clear speeds, addresses and up status in case device was removed and
|
||||||
* doesn't get updated */
|
* doesn't get updated */
|
||||||
|
|
||||||
#ifdef HAVE_OPENMP
|
#ifdef HAVE_OPENMP
|
||||||
#pragma omp parallel for schedule(dynamic,10)
|
#pragma omp parallel for schedule(dynamic,10)
|
||||||
#endif /* HAVE_OPENMP */
|
#endif /* HAVE_OPENMP */
|
||||||
for (i = 0; i < MAX_NET_INTERFACES; i++) {
|
for (int i = 0; i < MAX_NET_INTERFACES; ++i) {
|
||||||
if (netstats[i].dev) {
|
if (netstats[i].dev) {
|
||||||
netstats[i].up = 0;
|
netstats[i].up = 0;
|
||||||
netstats[i].recv_speed = 0.0;
|
netstats[i].recv_speed = 0.0;
|
||||||
@ -295,8 +293,10 @@ void update_stuff(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* this is a stub on all platforms except solaris */
|
||||||
prepare_update();
|
prepare_update();
|
||||||
|
|
||||||
|
/* if you registered a callback with conky::register_cb, this will run it */
|
||||||
conky::run_all_callbacks();
|
conky::run_all_callbacks();
|
||||||
|
|
||||||
/* XXX: move the following into the update_meminfo() functions? */
|
/* XXX: move the following into the update_meminfo() functions? */
|
||||||
|
12
src/conky.cc
12
src/conky.cc
@ -909,19 +909,17 @@ static void generate_text(void)
|
|||||||
{
|
{
|
||||||
char *p;
|
char *p;
|
||||||
unsigned int i, j, k;
|
unsigned int i, j, k;
|
||||||
|
|
||||||
special_count = 0;
|
special_count = 0;
|
||||||
|
|
||||||
/* update info */
|
|
||||||
|
|
||||||
current_update_time = get_time();
|
current_update_time = get_time();
|
||||||
|
|
||||||
|
/* clears netstats info, calls conky::run_all_callbacks(), and changes
|
||||||
|
* some info.mem entries */
|
||||||
update_stuff();
|
update_stuff();
|
||||||
|
|
||||||
/* add things to the buffer */
|
/* populate the text buffer; generate_text_internal() iterates through
|
||||||
|
* global_root_object (an instance of the text_object struct) and calls
|
||||||
/* generate text */
|
* any callbacks that were set on startup by construct_text_object(). */
|
||||||
|
|
||||||
p = text_buffer;
|
p = text_buffer;
|
||||||
|
|
||||||
generate_text_internal(p, max_user_text.get(*state), global_root_object);
|
generate_text_internal(p, max_user_text.get(*state), global_root_object);
|
||||||
|
95
src/core.cc
95
src/core.cc
@ -742,69 +742,79 @@ struct text_object *construct_text_object(char *s, const char *arg,
|
|||||||
scan_no_update(obj, arg);
|
scan_no_update(obj, arg);
|
||||||
obj->callbacks.print = &print_no_update;
|
obj->callbacks.print = &print_no_update;
|
||||||
obj->callbacks.free = &free_no_update;
|
obj->callbacks.free = &free_no_update;
|
||||||
END OBJ(exec, 0)
|
END OBJ_ARG(exec, 0, "exec needs arguments: <command>")
|
||||||
scan_exec_arg(obj, arg);
|
scan_exec_arg(obj, arg, EF_EXEC);
|
||||||
obj->parse = false;
|
obj->parse = false;
|
||||||
obj->thread = false;
|
obj->thread = false;
|
||||||
|
register_exec(obj);
|
||||||
obj->callbacks.print = &print_exec;
|
obj->callbacks.print = &print_exec;
|
||||||
obj->callbacks.free = &free_exec;
|
obj->callbacks.free = &free_exec;
|
||||||
END OBJ(execp, 0)
|
END OBJ_ARG(execi, 0, "execi needs arguments: <interval> <command>")
|
||||||
scan_exec_arg(obj, arg);
|
scan_exec_arg(obj, arg, EF_EXECI);
|
||||||
|
obj->parse = false;
|
||||||
|
obj->thread = false;
|
||||||
|
register_execi(obj);
|
||||||
|
obj->callbacks.print = &print_exec;
|
||||||
|
obj->callbacks.free = &free_execi;
|
||||||
|
END OBJ_ARG(execp, 0, "execp needs arguments: <command>")
|
||||||
|
scan_exec_arg(obj, arg, EF_EXEC);
|
||||||
obj->parse = true;
|
obj->parse = true;
|
||||||
obj->thread = false;
|
obj->thread = false;
|
||||||
|
register_exec(obj);
|
||||||
obj->callbacks.print = &print_exec;
|
obj->callbacks.print = &print_exec;
|
||||||
obj->callbacks.free = &free_exec;
|
obj->callbacks.free = &free_exec;
|
||||||
END OBJ(execbar, 0)
|
END OBJ_ARG(execpi, 0, "execpi needs arguments: <interval> <command>")
|
||||||
scan_exec_arg(obj, arg);
|
scan_exec_arg(obj, arg, EF_EXECI);
|
||||||
|
obj->parse = true;
|
||||||
|
obj->thread = false;
|
||||||
|
register_execi(obj);
|
||||||
|
obj->callbacks.print = &print_exec;
|
||||||
|
obj->callbacks.free = &free_execi;
|
||||||
|
END OBJ_ARG(execbar, 0, "execbar needs arguments: [height],[width] <command>")
|
||||||
|
scan_exec_arg(obj, arg, EF_EXEC | EF_BAR);
|
||||||
|
register_exec(obj);
|
||||||
obj->callbacks.barval = &execbarval;
|
obj->callbacks.barval = &execbarval;
|
||||||
obj->callbacks.free = &free_exec;
|
obj->callbacks.free = &free_exec;
|
||||||
END OBJ(execgauge, 0)
|
END OBJ_ARG(execibar, 0, "execibar needs arguments: <interval> [height],[width] <command>")
|
||||||
scan_exec_arg(obj, arg);
|
scan_exec_arg(obj, arg, EF_EXECI | EF_BAR);
|
||||||
|
register_execi(obj);
|
||||||
|
obj->callbacks.barval = &execbarval;
|
||||||
|
obj->callbacks.free = &free_execi;
|
||||||
|
#ifdef BUILD_X11
|
||||||
|
END OBJ_ARG(execgauge, 0, "execgauge needs arguments: [height],[width] <command>")
|
||||||
|
scan_exec_arg(obj, arg, EF_EXEC | EF_GAUGE);
|
||||||
|
register_exec(obj);
|
||||||
obj->callbacks.gaugeval = &execbarval;
|
obj->callbacks.gaugeval = &execbarval;
|
||||||
obj->callbacks.free = &free_exec;
|
obj->callbacks.free = &free_exec;
|
||||||
#ifdef BUILD_X11
|
END OBJ_ARG(execigauge, 0, "execigauge needs arguments: <interval> [height],[width] <command>")
|
||||||
END OBJ(execgraph, 0)
|
scan_exec_arg(obj, arg, EF_EXECI | EF_GAUGE);
|
||||||
scan_execgraph_arg(obj, arg);
|
register_execi(obj);
|
||||||
|
obj->callbacks.gaugeval = &execbarval;
|
||||||
|
obj->callbacks.free = &free_execi;
|
||||||
|
END OBJ_ARG(execgraph, 0, "execgraph needs arguments: <command> [height],[width] [color1] [color2] [scale] [-t|-l]")
|
||||||
|
scan_exec_arg(obj, arg, EF_EXEC | EF_GRAPH);
|
||||||
|
register_exec(obj);
|
||||||
obj->callbacks.graphval = &execbarval;
|
obj->callbacks.graphval = &execbarval;
|
||||||
obj->callbacks.free = &free_exec;
|
obj->callbacks.free = &free_exec;
|
||||||
#endif /* BUILD_X11 */
|
END OBJ_ARG(execigraph, 0, "execigraph needs arguments: <interval> <command> [height],[width] [color1] [color2] [scale] [-t|-l]")
|
||||||
END OBJ_ARG(execibar, 0, "execibar needs arguments")
|
scan_exec_arg(obj, arg, EF_EXECI | EF_GRAPH);
|
||||||
scan_execi_bar_arg(obj, arg);
|
register_execi(obj);
|
||||||
obj->callbacks.barval = &execi_barval;
|
obj->callbacks.graphval = &execbarval;
|
||||||
obj->callbacks.free = &free_execi;
|
|
||||||
#ifdef BUILD_X11
|
|
||||||
END OBJ_ARG(execigraph, 0, "execigraph needs arguments")
|
|
||||||
scan_execgraph_arg(obj, arg);
|
|
||||||
obj->callbacks.graphval = &execi_barval;
|
|
||||||
obj->callbacks.free = &free_execi;
|
|
||||||
END OBJ_ARG(execigauge, 0, "execigauge needs arguments")
|
|
||||||
scan_execi_gauge_arg(obj, arg);
|
|
||||||
obj->callbacks.gaugeval = &execi_barval;
|
|
||||||
obj->callbacks.free = &free_execi;
|
obj->callbacks.free = &free_execi;
|
||||||
#endif /* BUILD_X11 */
|
#endif /* BUILD_X11 */
|
||||||
END OBJ_ARG(execi, 0, "execi needs arguments")
|
END OBJ_ARG(texeci, 0, "texeci needs arguments: <interval> <command>")
|
||||||
scan_execi_arg(obj, arg);
|
scan_exec_arg(obj, arg, EF_EXECI);
|
||||||
obj->parse = false;
|
|
||||||
obj->thread = false;
|
|
||||||
obj->callbacks.print = &print_execi;
|
|
||||||
obj->callbacks.free = &free_execi;
|
|
||||||
END OBJ_ARG(execpi, 0, "execpi needs arguments")
|
|
||||||
scan_execi_arg(obj, arg);
|
|
||||||
obj->parse = true;
|
|
||||||
obj->thread = false;
|
|
||||||
obj->callbacks.print = &print_execi;
|
|
||||||
obj->callbacks.free = &free_execi;
|
|
||||||
END OBJ_ARG(texeci, 0, "texeci needs arguments")
|
|
||||||
scan_execi_arg(obj, arg);
|
|
||||||
obj->parse = false;
|
obj->parse = false;
|
||||||
obj->thread = true;
|
obj->thread = true;
|
||||||
obj->callbacks.print = &print_execi;
|
register_execi(obj);
|
||||||
|
obj->callbacks.print = &print_exec;
|
||||||
obj->callbacks.free = &free_execi;
|
obj->callbacks.free = &free_execi;
|
||||||
END OBJ_ARG(texecpi, 0, "texecpi needs arguments")
|
END OBJ_ARG(texecpi, 0, "texecpi needs arguments: <interval> <command>")
|
||||||
scan_execi_arg(obj, arg);
|
scan_exec_arg(obj, arg, EF_EXECI);
|
||||||
obj->parse = true;
|
obj->parse = true;
|
||||||
obj->thread = true;
|
obj->thread = true;
|
||||||
obj->callbacks.print = &print_execi;
|
register_execi(obj);
|
||||||
|
obj->callbacks.print = &print_exec;
|
||||||
obj->callbacks.free = &free_execi;
|
obj->callbacks.free = &free_execi;
|
||||||
END OBJ(fs_bar, &update_fs_stats)
|
END OBJ(fs_bar, &update_fs_stats)
|
||||||
init_fs_bar(obj, arg);
|
init_fs_bar(obj, arg);
|
||||||
@ -2071,7 +2081,6 @@ void free_text_objects(struct text_object *root)
|
|||||||
if(root && root->prev) {
|
if(root && root->prev) {
|
||||||
for (obj = root->prev; obj; obj = root->prev) {
|
for (obj = root->prev; obj; obj = root->prev) {
|
||||||
root->prev = obj->prev;
|
root->prev = obj->prev;
|
||||||
|
|
||||||
if (obj->callbacks.free) {
|
if (obj->callbacks.free) {
|
||||||
(*obj->callbacks.free)(obj);
|
(*obj->callbacks.free)(obj);
|
||||||
}
|
}
|
||||||
|
304
src/exec.cc
304
src/exec.cc
@ -30,30 +30,17 @@
|
|||||||
|
|
||||||
#include "conky.h"
|
#include "conky.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
#include "exec.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "specials.h"
|
#include "specials.h"
|
||||||
#include "text_object.h"
|
#include "text_object.h"
|
||||||
|
#include "update-cb.hh"
|
||||||
|
#include <cmath>
|
||||||
|
#include <mutex>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <cmath>
|
|
||||||
#include <mutex>
|
|
||||||
#include "update-cb.hh"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
class exec_cb: public conky::callback<std::string, std::string> {
|
|
||||||
typedef conky::callback<std::string, std::string> Base;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void work();
|
|
||||||
|
|
||||||
public:
|
|
||||||
exec_cb(uint32_t period, bool wait, const std::string &cmd)
|
|
||||||
: Base(period, wait, Base::Tuple(cmd))
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct execi_data {
|
struct execi_data {
|
||||||
float interval;
|
float interval;
|
||||||
@ -70,14 +57,14 @@ static FILE* pid_popen(const char *command, const char *mode, pid_t *child) {
|
|||||||
|
|
||||||
//by running pipe after the strcmp's we make sure that we don't have to create a pipe
|
//by running pipe after the strcmp's we make sure that we don't have to create a pipe
|
||||||
//and close the ends if mode is something illegal
|
//and close the ends if mode is something illegal
|
||||||
if(strcmp(mode, "r") == 0) {
|
if (strcmp(mode, "r") == 0) {
|
||||||
if(pipe(ends) != 0) {
|
if (pipe(ends) != 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
parentend = ends[0];
|
parentend = ends[0];
|
||||||
childend = ends[1];
|
childend = ends[1];
|
||||||
} else if(strcmp(mode, "w") == 0) {
|
} else if (strcmp(mode, "w") == 0) {
|
||||||
if(pipe(ends) != 0) {
|
if (pipe(ends) != 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
parentend = ends[1];
|
parentend = ends[1];
|
||||||
@ -85,17 +72,18 @@ static FILE* pid_popen(const char *command, const char *mode, pid_t *child) {
|
|||||||
} else {
|
} else {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*child = fork();
|
*child = fork();
|
||||||
if(*child == -1) {
|
if (*child == -1) {
|
||||||
close(parentend);
|
close(parentend);
|
||||||
close(childend);
|
close(childend);
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if(*child > 0) {
|
} else if (*child > 0) {
|
||||||
close(childend);
|
close(childend);
|
||||||
waitpid(*child, NULL, 0);
|
waitpid(*child, NULL, 0);
|
||||||
} else {
|
} else {
|
||||||
//don't read from both stdin and pipe or write to both stdout and pipe
|
//don't read from both stdin and pipe or write to both stdout and pipe
|
||||||
if(childend == ends[0]) {
|
if (childend == ends[0]) {
|
||||||
close(0);
|
close(0);
|
||||||
} else {
|
} else {
|
||||||
close(1);
|
close(1);
|
||||||
@ -110,9 +98,21 @@ static FILE* pid_popen(const char *command, const char *mode, pid_t *child) {
|
|||||||
execl("/bin/sh", "sh", "-c", command, (char *) NULL);
|
execl("/bin/sh", "sh", "-c", command, (char *) NULL);
|
||||||
_exit(EXIT_FAILURE); //child should die here, (normally execl will take care of this but it can fail)
|
_exit(EXIT_FAILURE); //child should die here, (normally execl will take care of this but it can fail)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fdopen(parentend, mode);
|
return fdopen(parentend, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a command and stores the result
|
||||||
|
*
|
||||||
|
* This function is called automatically, either once every update
|
||||||
|
* interval, or at specific intervals in the case of execi commands.
|
||||||
|
* conky::run_all_callbacks() handles this. In order for this magic to
|
||||||
|
* happen, we must register a callback with conky::register_cb<exec_cb>()
|
||||||
|
* and store it somewhere, such as obj->exec_handle. To retrieve the
|
||||||
|
* results, use the stored callback to call get_result_copy(), which
|
||||||
|
* returns a std::string.
|
||||||
|
*/
|
||||||
void exec_cb::work()
|
void exec_cb::work()
|
||||||
{
|
{
|
||||||
pid_t childpid;
|
pid_t childpid;
|
||||||
@ -120,37 +120,45 @@ void exec_cb::work()
|
|||||||
std::shared_ptr<FILE> fp;
|
std::shared_ptr<FILE> fp;
|
||||||
char b[0x1000];
|
char b[0x1000];
|
||||||
|
|
||||||
if(FILE *t = pid_popen(std::get<0>(tuple).c_str(), "r", &childpid))
|
if (FILE *t = pid_popen(std::get<0>(tuple).c_str(), "r", &childpid))
|
||||||
fp.reset(t, fclose);
|
fp.reset(t, fclose);
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while(!feof(fp.get()) && !ferror(fp.get())) {
|
while (!feof(fp.get()) && !ferror(fp.get())) {
|
||||||
int length = fread(b, 1, sizeof b, fp.get());
|
int length = fread(b, 1, sizeof b, fp.get());
|
||||||
buf.append(b, length);
|
buf.append(b, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*buf.rbegin() == '\n')
|
if (*buf.rbegin() == '\n')
|
||||||
buf.resize(buf.size()-1);
|
buf.resize(buf.size()-1);
|
||||||
|
|
||||||
std::lock_guard<std::mutex> l(result_mutex);
|
std::lock_guard<std::mutex> l(result_mutex);
|
||||||
result = buf;
|
result = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
//remove backspaced chars, example: "dog^H^H^Hcat" becomes "cat"
|
//remove backspaced chars, example: "dog^H^H^Hcat" becomes "cat"
|
||||||
//string has to end with \0 and it's length should fit in a int
|
//string has to end with \0 and it's length should fit in a int
|
||||||
#define BACKSPACE 8
|
#define BACKSPACE 8
|
||||||
static void remove_deleted_chars(char *string){
|
static void remove_deleted_chars(char *string){
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while(string[i] != 0){
|
while (string[i] != 0) {
|
||||||
if(string[i] == BACKSPACE){
|
if (string[i] == BACKSPACE) {
|
||||||
if(i != 0){
|
if (i != 0) {
|
||||||
strcpy( &(string[i-1]), &(string[i+1]) );
|
strcpy( &(string[i-1]), &(string[i+1]) );
|
||||||
i--;
|
i--;
|
||||||
}else strcpy( &(string[i]), &(string[i+1]) ); //necessary for ^H's at the start of a string
|
} else strcpy( &(string[i]), &(string[i+1]) ); //necessary for ^H's at the start of a string
|
||||||
}else i++;
|
} else i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses command output to find a number between 0.0 and 100.0.
|
||||||
|
* Used by ${exec[i]{bar,gauge,graph}}.
|
||||||
|
*
|
||||||
|
* @param[in] buf output of a command executed by an exec_cb object
|
||||||
|
* @return number between 0.0 and 100.0
|
||||||
|
*/
|
||||||
static inline double get_barnum(const char *buf)
|
static inline double get_barnum(const char *buf)
|
||||||
{
|
{
|
||||||
double barnum;
|
double barnum;
|
||||||
@ -165,125 +173,177 @@ static inline double get_barnum(const char *buf)
|
|||||||
"therefore it will be ignored");
|
"therefore it will be ignored");
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return barnum;
|
return barnum;
|
||||||
}
|
}
|
||||||
|
|
||||||
void scan_exec_arg(struct text_object *obj, const char *arg)
|
/**
|
||||||
{
|
* Store command output in p. For execp objects, we process the output
|
||||||
/* XXX: do real bar parsing here */
|
* in case it contains special commands like ${color}
|
||||||
scan_bar(obj, "", 100);
|
*
|
||||||
obj->data.s = strndup(arg ? arg : "", text_buffer_size.get(*state));
|
* @param[in] buffer the output of a command
|
||||||
}
|
* @param[in] obj text_object that specifies whether or not to parse
|
||||||
|
* @param[out] p the string in which we store command output
|
||||||
void scan_execi_arg(struct text_object *obj, const char *arg)
|
* @param[in] p_max_size the maximum size of p...
|
||||||
{
|
*/
|
||||||
struct execi_data *ed;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
ed = new execi_data;
|
|
||||||
|
|
||||||
if (sscanf(arg, "%f %n", &ed->interval, &n) <= 0) {
|
|
||||||
NORM_ERR("${execi* <interval> command}");
|
|
||||||
delete ed;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ed->cmd = strndup(arg + n, text_buffer_size.get(*state));
|
|
||||||
obj->data.opaque = ed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scan_execi_bar_arg(struct text_object *obj, const char *arg)
|
|
||||||
{
|
|
||||||
/* XXX: do real bar parsing here */
|
|
||||||
scan_bar(obj, "", 100);
|
|
||||||
scan_execi_arg(obj, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef BUILD_X11
|
|
||||||
void scan_execi_gauge_arg(struct text_object *obj, const char *arg)
|
|
||||||
{
|
|
||||||
/* XXX: do real gauge parsing here */
|
|
||||||
scan_gauge(obj, "", 100);
|
|
||||||
scan_execi_arg(obj, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scan_execgraph_arg(struct text_object *obj, const char *arg)
|
|
||||||
{
|
|
||||||
struct execi_data *ed;
|
|
||||||
char *buf;
|
|
||||||
|
|
||||||
ed = new execi_data;
|
|
||||||
memset(ed, 0, sizeof(struct execi_data));
|
|
||||||
|
|
||||||
buf = scan_graph(obj, arg, 100);
|
|
||||||
if (!buf) {
|
|
||||||
NORM_ERR("missing command argument to execgraph object");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ed->cmd = buf;
|
|
||||||
obj->data.opaque = ed;
|
|
||||||
}
|
|
||||||
#endif /* BUILD_X11 */
|
|
||||||
|
|
||||||
void fill_p(const char *buffer, struct text_object *obj, char *p, int p_max_size) {
|
void fill_p(const char *buffer, struct text_object *obj, char *p, int p_max_size) {
|
||||||
if(obj->parse == true) {
|
if (obj->parse == true) {
|
||||||
evaluate(buffer, p, p_max_size);
|
evaluate(buffer, p, p_max_size);
|
||||||
} else snprintf(p, p_max_size, "%s", buffer);
|
} else {
|
||||||
|
snprintf(p, p_max_size, "%s", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
remove_deleted_chars(p);
|
remove_deleted_chars(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses arg to find the command to be run, as well as special options
|
||||||
|
* like height, width, color, and update interval
|
||||||
|
*
|
||||||
|
* @param[out] obj stores the command and an execi_data structure (if applicable)
|
||||||
|
* @param[in] arg the argument to an ${exec*} object
|
||||||
|
* @param[in] execflag bitwise flag used to specify the exec variant we need to process
|
||||||
|
*/
|
||||||
|
void scan_exec_arg(struct text_object *obj, const char *arg, unsigned int execflag)
|
||||||
|
{
|
||||||
|
const char *cmd = arg;
|
||||||
|
struct execi_data *ed;
|
||||||
|
|
||||||
|
/* in case we have an execi object, we need to parse out the interval */
|
||||||
|
if (execflag & EF_EXECI) {
|
||||||
|
ed = new execi_data;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
/* store the interval in ed->interval */
|
||||||
|
if (sscanf(arg, "%f %n", &ed->interval, &n) <= 0) {
|
||||||
|
NORM_ERR("missing execi interval: ${execi* <interval> command}");
|
||||||
|
delete ed;
|
||||||
|
ed = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set cmd to everything after the interval */
|
||||||
|
cmd = strndup(arg + n, text_buffer_size.get(*state));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse any special options for the graphical exec types */
|
||||||
|
if (execflag & EF_BAR) {
|
||||||
|
cmd = scan_bar(obj, cmd, 100);
|
||||||
|
#ifdef BUILD_X11
|
||||||
|
} else if (execflag & EF_GAUGE) {
|
||||||
|
cmd = scan_gauge(obj, cmd, 100);
|
||||||
|
} else if (execflag & EF_GRAPH) {
|
||||||
|
cmd = scan_graph(obj, cmd, 100);
|
||||||
|
if (!cmd) {
|
||||||
|
NORM_ERR("error parsing arguments to execgraph object");
|
||||||
|
}
|
||||||
|
#endif /* BUILD_X11 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* finally, store the resulting command, or an empty string if something went wrong */
|
||||||
|
if (execflag & EF_EXEC) {
|
||||||
|
obj->data.s = strndup(cmd ? cmd : "", text_buffer_size.get(*state));
|
||||||
|
} else if (execflag & EF_EXECI) {
|
||||||
|
ed->cmd = strndup(cmd ? cmd : "", text_buffer_size.get(*state));
|
||||||
|
obj->data.opaque = ed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an exec_cb object using the command that we have parsed
|
||||||
|
*
|
||||||
|
* @param[out] obj stores the callback handle
|
||||||
|
*/
|
||||||
|
void register_exec(struct text_object *obj)
|
||||||
|
{
|
||||||
|
if (obj->data.s && obj->data.s[0]) {
|
||||||
|
obj->exec_handle = new conky::callback_handle<exec_cb>(
|
||||||
|
conky::register_cb<exec_cb>(1, true, obj->data.s));
|
||||||
|
} else {
|
||||||
|
DBGP("unable to register exec callback");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an exec_cb object using the command that we have parsed.
|
||||||
|
*
|
||||||
|
* This version takes care of execi intervals. Note that we depend on
|
||||||
|
* obj->thread, so be sure to run this function *after* setting obj->thread.
|
||||||
|
*
|
||||||
|
* @param[out] obj stores the callback handle
|
||||||
|
*/
|
||||||
|
void register_execi(struct text_object *obj)
|
||||||
|
{
|
||||||
|
struct execi_data *ed = (struct execi_data *)obj->data.opaque;
|
||||||
|
|
||||||
|
if (ed && ed->cmd && ed->cmd[0]) {
|
||||||
|
uint32_t period = std::max(lround(ed->interval/active_update_interval()), 1l);
|
||||||
|
obj->exec_handle = new conky::callback_handle<exec_cb>(
|
||||||
|
conky::register_cb<exec_cb>(period, !obj->thread, ed->cmd));
|
||||||
|
} else {
|
||||||
|
DBGP("unable to register execi callback");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the results of an exec_cb object (command output)
|
||||||
|
*
|
||||||
|
* @param[in] obj holds an exec_handle, assuming one was registered
|
||||||
|
* @param[out] p the string in which we store command output
|
||||||
|
* @param[in] p_max_size the maximum size of p...
|
||||||
|
*/
|
||||||
void print_exec(struct text_object *obj, char *p, int p_max_size)
|
void print_exec(struct text_object *obj, char *p, int p_max_size)
|
||||||
{
|
{
|
||||||
auto cb = conky::register_cb<exec_cb>(1, true, obj->data.s);
|
if (obj->exec_handle) {
|
||||||
fill_p(cb->get_result_copy().c_str(), obj, p, p_max_size);
|
fill_p((*obj->exec_handle)->get_result_copy().c_str(), obj, p, p_max_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_execi(struct text_object *obj, char *p, int p_max_size)
|
|
||||||
{
|
|
||||||
struct execi_data *ed = (struct execi_data *)obj->data.opaque;
|
|
||||||
|
|
||||||
if (!ed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint32_t period = std::max(lround(ed->interval/active_update_interval()), 1l);
|
|
||||||
|
|
||||||
auto cb = conky::register_cb<exec_cb>(period, !obj->thread, ed->cmd);
|
|
||||||
|
|
||||||
fill_p(cb->get_result_copy().c_str(), obj, p, p_max_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the results of a graphical (bar, gauge, graph) exec_cb object
|
||||||
|
*
|
||||||
|
* @param[in] obj hold an exec_handle, assuming one was registered
|
||||||
|
* @return a value between 0.0 and 100.0
|
||||||
|
*/
|
||||||
double execbarval(struct text_object *obj)
|
double execbarval(struct text_object *obj)
|
||||||
{
|
{
|
||||||
auto cb = conky::register_cb<exec_cb>(1, true, obj->data.s);
|
if (obj->exec_handle) {
|
||||||
return get_barnum(cb->get_result_copy().c_str());
|
return get_barnum((*obj->exec_handle)->get_result_copy().c_str());
|
||||||
}
|
} else {
|
||||||
|
return 0.0;
|
||||||
double execi_barval(struct text_object *obj)
|
}
|
||||||
{
|
|
||||||
struct execi_data *ed = (struct execi_data *)obj->data.opaque;
|
|
||||||
|
|
||||||
if (!ed)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
uint32_t period = std::max(lround(ed->interval/active_update_interval()), 1l);
|
|
||||||
|
|
||||||
auto cb = conky::register_cb<exec_cb>(period, !obj->thread, ed->cmd);
|
|
||||||
|
|
||||||
return get_barnum(cb->get_result_copy().c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free up any dynamically allocated data
|
||||||
|
*
|
||||||
|
* @param[in] obj holds the data that we need to free up
|
||||||
|
*/
|
||||||
void free_exec(struct text_object *obj)
|
void free_exec(struct text_object *obj)
|
||||||
{
|
{
|
||||||
free_and_zero(obj->data.s);
|
free_and_zero(obj->data.s);
|
||||||
|
delete obj->exec_handle;
|
||||||
|
obj->exec_handle = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free up any dynamically allocated data, specifically for execi objects
|
||||||
|
*
|
||||||
|
* @param[in] obj holds the data that we need to free up
|
||||||
|
*/
|
||||||
void free_execi(struct text_object *obj)
|
void free_execi(struct text_object *obj)
|
||||||
{
|
{
|
||||||
struct execi_data *ed = (struct execi_data *)obj->data.opaque;
|
struct execi_data *ed = (struct execi_data *)obj->data.opaque;
|
||||||
|
|
||||||
|
/* if ed is NULL, there is nothing to do */
|
||||||
if (!ed)
|
if (!ed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
delete obj->exec_handle;
|
||||||
|
obj->exec_handle = NULL;
|
||||||
|
|
||||||
free_and_zero(ed->cmd);
|
free_and_zero(ed->cmd);
|
||||||
delete ed;
|
delete ed;
|
||||||
|
ed = NULL;
|
||||||
obj->data.opaque = NULL;
|
obj->data.opaque = NULL;
|
||||||
}
|
}
|
||||||
|
51
src/exec.h
51
src/exec.h
@ -33,15 +33,52 @@
|
|||||||
|
|
||||||
#include "text_object.h"
|
#include "text_object.h"
|
||||||
|
|
||||||
void scan_exec_arg(struct text_object *, const char *);
|
/**
|
||||||
void scan_execi_arg(struct text_object *, const char *);
|
* A callback that executes a command and stores the output as a std::string.
|
||||||
void scan_execi_bar_arg(struct text_object *, const char *);
|
*
|
||||||
void scan_execi_gauge_arg(struct text_object *, const char *);
|
* Important note: if more than one exec callback uses the same command,
|
||||||
void scan_execgraph_arg(struct text_object *, const char *);
|
* then only ONE callback is actually stored. This saves space. However,
|
||||||
|
* suppose we have the following ${exec} objects in our conky.text:
|
||||||
|
*
|
||||||
|
* ${exec ~/bin/foo.sh}
|
||||||
|
* ${execi 10 ~/bin/foo.sh}
|
||||||
|
*
|
||||||
|
* To the callback system, these are identical! Furthermore, the callback
|
||||||
|
* with the smallest period/interval is the one that is stored. So the execi
|
||||||
|
* command will in fact run on every update interval, rather than every
|
||||||
|
* ten seconds as one would expect.
|
||||||
|
*/
|
||||||
|
class exec_cb: public conky::callback<std::string, std::string> {
|
||||||
|
typedef conky::callback<std::string, std::string> Base;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void work();
|
||||||
|
|
||||||
|
public:
|
||||||
|
exec_cb(uint32_t period, bool wait, const std::string &cmd)
|
||||||
|
: Base(period, wait, Base::Tuple(cmd))
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags used to identify the different types of exec commands during
|
||||||
|
* parsing by scan_exec_arg(). These can be used individually or combined.
|
||||||
|
* For example, to parse an ${execgraph} object, we pass EF_EXEC | EF_GRAPH
|
||||||
|
* as the last argument to scan_exec_arg().
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
EF_EXEC = (1 << 0),
|
||||||
|
EF_EXECI = (1 << 1),
|
||||||
|
EF_BAR = (1 << 2),
|
||||||
|
EF_GRAPH = (1 << 3),
|
||||||
|
EF_GAUGE = (1 << 4)
|
||||||
|
};
|
||||||
|
|
||||||
|
void scan_exec_arg(struct text_object *, const char *, unsigned int);
|
||||||
|
void register_exec(struct text_object *);
|
||||||
|
void register_execi(struct text_object *);
|
||||||
void print_exec(struct text_object *, char *, int);
|
void print_exec(struct text_object *, char *, int);
|
||||||
void print_execi(struct text_object *, char *, int);
|
|
||||||
double execbarval(struct text_object *);
|
double execbarval(struct text_object *);
|
||||||
double execi_barval(struct text_object *);
|
|
||||||
void free_exec(struct text_object *);
|
void free_exec(struct text_object *);
|
||||||
void free_execi(struct text_object *);
|
void free_execi(struct text_object *);
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "config.h" /* for the defines */
|
#include "config.h" /* for the defines */
|
||||||
#include "specials.h" /* enum special_types */
|
#include "specials.h" /* enum special_types */
|
||||||
#include "update-cb.hh"
|
#include "update-cb.hh"
|
||||||
|
#include "exec.h"
|
||||||
|
|
||||||
/* text object callbacks */
|
/* text object callbacks */
|
||||||
struct obj_cb {
|
struct obj_cb {
|
||||||
@ -87,12 +88,24 @@ public:
|
|||||||
: Base(period, true, Base::Tuple(fn))
|
: Base(period, true, Base::Tuple(fn))
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
typedef conky::callback_handle<legacy_cb> legacy_cb_handle;
|
|
||||||
|
|
||||||
|
typedef conky::callback_handle<legacy_cb> legacy_cb_handle;
|
||||||
|
typedef conky::callback_handle<exec_cb> exec_cb_handle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is where Conky collects information on the conky.text objects in your config
|
||||||
|
*
|
||||||
|
* During startup and reload, objects are parsed and callbacks are set. Note that
|
||||||
|
* there are currently two types of callback: obj_cb (old style) and
|
||||||
|
* conky::callback (new style). On each update interval, generate_text_internal()
|
||||||
|
* in conky.cc traverses the list of text_objects and calls the old callbacks.
|
||||||
|
* The new style callbacks are run separately by conky::run_all_callbacks().
|
||||||
|
*/
|
||||||
struct text_object {
|
struct text_object {
|
||||||
struct text_object *next, *prev; /* doubly linked list of text objects */
|
struct text_object *next, *prev; /* doubly linked list of text objects */
|
||||||
struct text_object *sub; /* for objects parsing text into objects */
|
struct text_object *sub; /* for objects parsing text into objects */
|
||||||
struct text_object *ifblock_next; /* jump target for ifblock objects */
|
struct text_object *ifblock_next; /* jump target for ifblock objects */
|
||||||
|
|
||||||
union {
|
union {
|
||||||
void *opaque; /* new style generic per object data */
|
void *opaque; /* new style generic per object data */
|
||||||
char *s; /* some string */
|
char *s; /* some string */
|
||||||
@ -102,10 +115,15 @@ struct text_object {
|
|||||||
|
|
||||||
void *special_data;
|
void *special_data;
|
||||||
long line;
|
long line;
|
||||||
struct obj_cb callbacks;
|
bool parse; /* if true then data.s should still be parsed */
|
||||||
bool parse; //if this true then data.s should still be parsed
|
bool thread; /* if true then data.s should be set by a seperate thread */
|
||||||
bool thread; //if this true then data.s should be set by a seperate thread
|
|
||||||
|
|
||||||
|
struct obj_cb callbacks;
|
||||||
|
|
||||||
|
/* Each _cb_handle is a std::shared_ptr with very tight restrictions on
|
||||||
|
* construction. For now, it is necessary to store them here as regular
|
||||||
|
* pointers so we can instantiate them later. */
|
||||||
|
exec_cb_handle *exec_handle;
|
||||||
legacy_cb_handle *cb_handle;
|
legacy_cb_handle *cb_handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,6 +77,12 @@ namespace conky {
|
|||||||
return *a == *b;
|
return *a == *b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If a callback is not successfully inserted into the set, it must have
|
||||||
|
* the same hash as an existing callback. If this is so, merge the incoming
|
||||||
|
* callback with the one that prevented insertion. Keep the smaller of the
|
||||||
|
* two periods.
|
||||||
|
*/
|
||||||
void callback_base::merge(callback_base &&other)
|
void callback_base::merge(callback_base &&other)
|
||||||
{
|
{
|
||||||
if(other.period < period) {
|
if(other.period < period) {
|
||||||
@ -87,10 +93,14 @@ namespace conky {
|
|||||||
unused = 0;
|
unused = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register a callback (i.e. insert it into the callbacks set)
|
||||||
|
*/
|
||||||
callback_base::handle callback_base::do_register_cb(const handle &h)
|
callback_base::handle callback_base::do_register_cb(const handle &h)
|
||||||
{
|
{
|
||||||
const auto &p = callbacks.insert(h);
|
const auto &p = callbacks.insert(h);
|
||||||
|
|
||||||
|
/* insertion failed; callback already exists */
|
||||||
if(not p.second)
|
if(not p.second)
|
||||||
(*p.first)->merge(std::move(*h));
|
(*p.first)->merge(std::move(*h));
|
||||||
|
|
||||||
@ -134,7 +144,10 @@ namespace conky {
|
|||||||
for(auto i = callback_base::callbacks.begin(); i != callback_base::callbacks.end(); ) {
|
for(auto i = callback_base::callbacks.begin(); i != callback_base::callbacks.end(); ) {
|
||||||
callback_base &cb = **i;
|
callback_base &cb = **i;
|
||||||
|
|
||||||
|
/* check whether enough update intervals have elapsed (up to period) */
|
||||||
if(cb.remaining-- == 0) {
|
if(cb.remaining-- == 0) {
|
||||||
|
/* run the callback as long as someone holds a pointer to it;
|
||||||
|
* if no one owns the callback, run it at most UNUSED_MAX times */
|
||||||
if(!i->unique() || ++cb.unused < UNUSED_MAX) {
|
if(!i->unique() || ++cb.unused < UNUSED_MAX) {
|
||||||
cb.remaining = cb.period-1;
|
cb.remaining = cb.period-1;
|
||||||
cb.run();
|
cb.run();
|
||||||
|
@ -56,13 +56,13 @@ namespace conky {
|
|||||||
|
|
||||||
semaphore sem_start;
|
semaphore sem_start;
|
||||||
std::thread *thread;
|
std::thread *thread;
|
||||||
const size_t hash;
|
const size_t hash; /* used to determined callback uniqueness */
|
||||||
uint32_t period;
|
uint32_t period; /* how often to run a callback */
|
||||||
uint32_t remaining;
|
uint32_t remaining; /* update intervals remaining until we can run a callback */
|
||||||
std::pair<int, int> pipefd;
|
std::pair<int, int> pipefd;
|
||||||
const bool wait;
|
const bool wait; /* whether or not to wait for a callback to finish */
|
||||||
bool done;
|
bool done; /* if true, callback is being stopped and destroyed */
|
||||||
uint8_t unused;
|
uint8_t unused; /* number of update intervals during which no one owns a callback */
|
||||||
|
|
||||||
callback_base(const callback_base &) = delete;
|
callback_base(const callback_base &) = delete;
|
||||||
callback_base& operator=(const callback_base &) = delete;
|
callback_base& operator=(const callback_base &) = delete;
|
||||||
@ -159,6 +159,9 @@ namespace conky {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback uniqueness is determined by the hash computed here.
|
||||||
|
*/
|
||||||
namespace priv {
|
namespace priv {
|
||||||
template<size_t pos, typename... Elements>
|
template<size_t pos, typename... Elements>
|
||||||
struct hash_tuple {
|
struct hash_tuple {
|
||||||
|
Loading…
Reference in New Issue
Block a user