From 6dcff6ae0e1430bf8712b572beb66e301dc9c0c9 Mon Sep 17 00:00:00 2001 From: Vitaly Minko Date: Thu, 3 Oct 2019 08:26:56 +0300 Subject: [PATCH] Implemented 'stop_hidden' configuration option for applications. This option lets user to stop application processes automatically when all the application windows are hidden from the user. A window is considered hidden when it's iconified or shaded or does not belong to the current desktop. This option in intended for minimization of CPU usage and power consumption. Some modern applications (like Web browsers) tend to consume significant amount of CPU resources even when hidden. There are three possible values of the option: 'no' - do not stop application when it hides (default value) 'process' - stop just the process the hidden window belongs to 'group' - stop whole process group the application belongs to; some complex applications (like web browsers) emerge multiple child processed, so in order to stop such applications it's not enough just to stop one process. Example: group Please note that Firefox does not create a new group for its child processes. You need to run it via `setsid firefox` in order to have a separate group for all Firefox processes. --- data/rc.xml | 7 ++++ data/rc.xsd | 8 ++++ doc/rc-mouse-focus.xml | 7 ++++ openbox/client.c | 91 +++++++++++++++++++++++++++++++++++++++++- openbox/client.h | 16 ++++++++ openbox/config.c | 14 +++++++ openbox/config.h | 1 + openbox/session.c | 4 ++ openbox/session.h | 2 +- 9 files changed, 148 insertions(+), 2 deletions(-) diff --git a/data/rc.xml b/data/rc.xml index 3e5554ba..c06d3839 100644 --- a/data/rc.xml +++ b/data/rc.xml @@ -732,6 +732,13 @@ yes # make the window in fullscreen mode when it appears + process + # stop the application when it hides in order to minimize CPU usage + # and power consumption + # 'no' - do not stop application when it hides (default value) + # 'process' - stop just the process the window belongs to + # 'group' - stop whole process group the application belongs to + true # 'Horizontal', 'Vertical' or boolean (yes/no) diff --git a/data/rc.xsd b/data/rc.xsd index c8f5638b..a7c23bd4 100644 --- a/data/rc.xsd +++ b/data/rc.xsd @@ -254,6 +254,7 @@ + @@ -485,6 +486,13 @@ + + + + + + + diff --git a/doc/rc-mouse-focus.xml b/doc/rc-mouse-focus.xml index dc7f2e98..3e29f73b 100644 --- a/doc/rc-mouse-focus.xml +++ b/doc/rc-mouse-focus.xml @@ -625,6 +625,13 @@ yes # make the window in fullscreen mode when it appears + process + # stop the application when it hides in order to minimize CPU usage + # and power consumption + # 'no' - do not stop application when it hides (default value) + # 'process' - stop just the process the window belongs to + # 'group' - stop whole process group the application belongs to + true # 'Horizontal', 'Vertical' or boolean (yes/no) diff --git a/openbox/client.c b/openbox/client.c index 3ff278ae..04b66c08 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -625,6 +625,9 @@ void client_unmanage(ObClient *self) prompt_unref(self->kill_prompt); self->kill_prompt = NULL; + /* if client was stopped, continue executing */ + client_continue_hidden(self); + client_list = g_list_remove(client_list, self); stacking_remove(self); window_remove(self->window); @@ -968,6 +971,8 @@ static ObAppSettings *client_get_settings_state(ObClient *self) self->skip_pager = !!settings->skip_pager; if (settings->skip_taskbar != -1) self->skip_taskbar = !!settings->skip_taskbar; + if (settings->stop_hidden != OB_CLIENT_STOP_MODE_NONE) + self->stop_hidden = settings->stop_hidden; if (settings->max_vert != -1) self->max_vert = !!settings->max_vert; @@ -1043,6 +1048,7 @@ static void client_restore_session_state(ObClient *self) self->max_horz = self->session->max_horz; self->max_vert = self->session->max_vert; self->undecorated = self->session->undecorated; + self->stop_hidden = self->session->stop_hidden; } static gboolean client_restore_session_stacking(ObClient *self) @@ -2510,8 +2516,11 @@ static void client_change_wm_state(ObClient *self) (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop)) { self->wmstate = IconicState; - } else + client_stop_hidden(self); + } else { self->wmstate = NormalState; + client_continue_hidden(self); + } if (old != self->wmstate) { OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE, @@ -2608,6 +2617,24 @@ ObClient *client_search_focus_group_full(ObClient *self) return NULL; } +gboolean client_is_hidden(ObClient *self) +{ + return (self->wmstate == IconicState); +} + +gboolean client_is_group_hidden(ObClient *self) +{ + GSList *it; + + if (self->group) { + for (it = self->group->members; it; it = g_slist_next(it)) { + ObClient *c = it->data; + if (!client_is_hidden(c)) return FALSE; + } + } + return TRUE; +} + gboolean client_has_parent(ObClient *self) { return self->parents != NULL; @@ -3706,6 +3733,68 @@ void client_kill(ObClient *self) } } +void client_stop_hidden(ObClient *self) +{ + if (self->stop_hidden == OB_CLIENT_STOP_MODE_NONE) + return; + + if (client_on_localhost(self) && self->pid) { + /* running on the local host */ + + if (!client_is_group_hidden(self)) { + return; + } + + if (self->stop_hidden == OB_CLIENT_STOP_MODE_PROCESS) { + ob_debug("client_stop_hidden is stopping process with pid %lu", + self->pid); + kill(self->pid, SIGSTOP); + } else if (self->stop_hidden == OB_CLIENT_STOP_MODE_GROUP) { + ob_debug("client_stop_hidden is stopping group with pid %lu", + self->pid); + pid_t pgid = getpgid(self->pid); + if (pgid != -1) { + killpg(self->pid, SIGSTOP); + } + } else { + ob_debug("client_stop_hidden: unknown value of stop_hidden"); + return; + } + } + else { + /* do nothing, running on a remote host */ + } +} + +void client_continue_hidden(ObClient *self) +{ + if (self->stop_hidden == OB_CLIENT_STOP_MODE_NONE) + return; + + if (client_on_localhost(self) && self->pid) { + /* running on the local host */ + if (self->stop_hidden == OB_CLIENT_STOP_MODE_PROCESS) { + ob_debug("client_continue_hidden is continuing process with pid %lu", + self->pid); + kill(self->pid, SIGCONT); + } else if (self->stop_hidden == OB_CLIENT_STOP_MODE_GROUP) { + ob_debug("client_continue_hidden is continuing group with pid %lu", + self->pid); + pid_t pgid = getpgid(self->pid); + if (pgid != -1) { + killpg(self->pid, SIGCONT); + } + } else { + ob_debug("client_continue_hidden: unknown value" + " of stop_hidden"); + return; + } + } + else { + /* do nothing, running on a remote host */ + } +} + void client_hilite(ObClient *self, gboolean hilite) { if (self->demands_attention == hilite) diff --git a/openbox/client.h b/openbox/client.h index 11a01400..d574740a 100644 --- a/openbox/client.h +++ b/openbox/client.h @@ -69,6 +69,14 @@ typedef enum OB_CLIENT_FUNC_UNDECORATE = 1 << 9 /*!< Allow to be undecorated */ } ObFunctions; +/*! The way to stop client when it hides */ +typedef enum +{ + OB_CLIENT_STOP_MODE_NONE, /*!< Do not stop client */ + OB_CLIENT_STOP_MODE_PROCESS, /*!< Stop the client process only */ + OB_CLIENT_STOP_MODE_GROUP, /*!< Stop process group the client belongs to */ +} ObClientStopMode; + struct _ObClient { ObWindow obwin; @@ -272,6 +280,8 @@ struct _ObClient gboolean skip_pager; /*! The window should not be displayed by taskbars */ gboolean skip_taskbar; + /*! How to stop the process when it hides or iconifies */ + ObClientStopMode stop_hidden; /*! The window is a 'fullscreen' window, and should be on top of all others */ gboolean fullscreen; @@ -548,6 +558,12 @@ void client_close(ObClient *self); /*! Kill the client off violently */ void client_kill(ObClient *self); +/*! Stop the client process when all its windows are hidded */ +void client_stop_hidden(ObClient *self); + +/*! Continue executing the client process when one of its windows appears */ +void client_continue_hidden(ObClient *self); + /*! Sends the window to the specified desktop @param donthide If TRUE, the window will not be shown/hidden after its desktop has been changed. Generally this should be FALSE. diff --git a/openbox/config.c b/openbox/config.c index dad5d1bf..4310906b 100644 --- a/openbox/config.c +++ b/openbox/config.c @@ -123,6 +123,7 @@ ObAppSettings* config_create_app_settings(void) settings->fullscreen = -1; settings->max_horz = -1; settings->max_vert = -1; + settings->stop_hidden = OB_CLIENT_STOP_MODE_NONE; return settings; } @@ -148,6 +149,7 @@ void config_app_settings_copy_non_defaults(const ObAppSettings *src, copy_if(fullscreen, -1); copy_if(max_horz, -1); copy_if(max_vert, -1); + copy_if(stop_hidden, OB_CLIENT_STOP_MODE_NONE); if (src->pos_given) { dst->pos_given = TRUE; @@ -328,6 +330,18 @@ static void parse_single_per_app_settings(xmlNodePtr app, if (!obt_xml_node_contains(n, "default")) settings->fullscreen = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(app->children, "stop_hidden"))) + if (!obt_xml_node_contains(n, "default")) { + gchar *s = obt_xml_node_string(n); + if (!g_ascii_strcasecmp(s, "no")) + settings->stop_hidden = OB_CLIENT_STOP_MODE_NONE; + else if (!g_ascii_strcasecmp(s, "process")) + settings->stop_hidden = OB_CLIENT_STOP_MODE_PROCESS; + else if (!g_ascii_strcasecmp(s, "group")) + settings->stop_hidden = OB_CLIENT_STOP_MODE_GROUP; + g_free(s); + } + if ((n = obt_xml_find_node(app->children, "maximized"))) { if (!obt_xml_node_contains(n, "default")) { gchar *s = obt_xml_node_string(n); diff --git a/openbox/config.h b/openbox/config.h index 96a66cf1..567e88e6 100644 --- a/openbox/config.h +++ b/openbox/config.h @@ -42,6 +42,7 @@ struct _ObAppSettings GPatternSpec *group_name; GPatternSpec *title; ObClientType type; + ObClientStopMode stop_hidden; GravityPoint position; gboolean pos_given; diff --git a/openbox/session.c b/openbox/session.c index d6c6f767..96fa066f 100644 --- a/openbox/session.c +++ b/openbox/session.c @@ -603,6 +603,8 @@ static gboolean session_save_to_file(const ObSMSaveData *savedata) fprintf(f, "\t\n"); if (c->undecorated) fprintf(f, "\t\n"); + if (c->stop_hidden) + fprintf(f, "\t\n"); if (savedata->focus_client == c) fprintf(f, "\t\n"); fprintf(f, "\n\n"); @@ -784,6 +786,8 @@ static void session_load_file(const gchar *path) obt_xml_find_node(node->children, "max_vert") != NULL; state->undecorated = obt_xml_find_node(node->children, "undecorated") != NULL; + state->stop_hidden = + obt_xml_find_node(node->children, "stop_hidden") != NULL; state->focused = obt_xml_find_node(node->children, "focused") != NULL; diff --git a/openbox/session.h b/openbox/session.h index f37e2111..a65f8397 100644 --- a/openbox/session.h +++ b/openbox/session.h @@ -34,7 +34,7 @@ struct _ObSessionState { gboolean shaded, iconic, skip_pager, skip_taskbar, fullscreen; gboolean above, below, max_horz, max_vert, undecorated; gboolean focused; - + ObClientStopMode stop_hidden; gboolean matched; }; -- 2.21.0