fix reload GTK23 themes with theme manager
parent
556d92134e
commit
74323a6bab
|
@ -0,0 +1,159 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# bl-reload-gtk23: Make GTK2/3 reload settings file changes
|
||||||
|
# Copyright (C) 2020 2ion <twoion@bunsenlabs.org>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from argparse import ArgumentParser, Namespace
|
||||||
|
import Xlib.display # type: ignore
|
||||||
|
import Xlib.protocol # type: ignore
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import psutil # type: ignore
|
||||||
|
import shutil
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
DESCRIPTION = ("""
|
||||||
|
After changing GTK2 and GTK3 configuration files, notify running GTK2 and GTK3
|
||||||
|
clients to apply those changes. The notification mechanism used by GTK3 requires
|
||||||
|
that xsettingsd is running. If it is installed and not running, the program will
|
||||||
|
launch it. Note that xsettingsd does not read settings from GTK3's settings.ini
|
||||||
|
- if information is changed, it must be changed in xsettingsd config files as well.
|
||||||
|
|
||||||
|
EXIT CODES:
|
||||||
|
0 - both gtk2 and gtk3 clients notified successfully
|
||||||
|
1 - failed to notify gtk2 clients
|
||||||
|
2 - failed to notify gtk3 clients
|
||||||
|
3 - failed to notify gtk2 and gtk3 clients
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
|
LOG_FORMAT = "%(asctime)s %(levelname)s %(module)s %(funcName)s() : %(message)s"
|
||||||
|
|
||||||
|
def getopts() -> Namespace:
|
||||||
|
ap = ArgumentParser(description=DESCRIPTION)
|
||||||
|
ap.add_argument("-d", "--debug", action="store_true", default=False, help="print debug information")
|
||||||
|
ap.add_argument("-f", "--force", action="store_true", default=False, help="ignore all errors")
|
||||||
|
opts = ap.parse_args()
|
||||||
|
if opts.debug:
|
||||||
|
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)
|
||||||
|
else:
|
||||||
|
logging.basicConfig(level=logging.WARN, format=LOG_FORMAT)
|
||||||
|
return opts
|
||||||
|
|
||||||
|
def sync_gtk2() -> None:
|
||||||
|
""" Tell GTK2 X11 clients to reload the GTK RC files and update their
|
||||||
|
appearance/settings if required. This implements this process without GTK/GDK
|
||||||
|
in order to be able to drop the dependency on the obsolete pygtk library.
|
||||||
|
GTK3/pygobject does not support GTK2.
|
||||||
|
|
||||||
|
This function will always fail on non-X11 platforms as the GTK2 client
|
||||||
|
notification mechanism is based on X.
|
||||||
|
|
||||||
|
This implementation is based on the following resources:
|
||||||
|
* From libgtk2 2.24.18:
|
||||||
|
* gdk_event_send_client_message_to_all_recurse()
|
||||||
|
* gdk_screen_broadcast_client_message()
|
||||||
|
* From kde-gtk-config https://github.com/KDE/kde-gtk-config/blob/a5d4ddb3b1a27ec2ee4e1b6957a98a57ad56d39c/gtkproxies/reload.c
|
||||||
|
"""
|
||||||
|
display = Xlib.display.Display(display=os.getenv("DISPLAY"))
|
||||||
|
wm_state_atom = display.intern_atom("WM_STATE", False)
|
||||||
|
gtkrc_atom = display.intern_atom("_GTK_READ_RCFILES", False)
|
||||||
|
|
||||||
|
def send_event(window) -> bool:
|
||||||
|
""" Send a _GTK_READ_RCFILES client message to the given X window.
|
||||||
|
Returns true unless an exception occurs. """
|
||||||
|
window.send_event(
|
||||||
|
Xlib.protocol.event.ClientMessage(
|
||||||
|
window = window,
|
||||||
|
client_type = gtkrc_atom,
|
||||||
|
data = (8, b"\0" * 20),
|
||||||
|
),
|
||||||
|
propagate = 0,
|
||||||
|
event_mask = 0
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def recurse_windows(window, parents) -> bool:
|
||||||
|
""" Given a X window, recurse over all its children and selectively
|
||||||
|
apply the send_event function to them. Returns true if an event got sent
|
||||||
|
to at least one window equal or below the given one. """
|
||||||
|
sent = False
|
||||||
|
wm_state = window.get_property(wm_state_atom, wm_state_atom, 0, 0)
|
||||||
|
name = window.get_wm_name()
|
||||||
|
level = len(parents)
|
||||||
|
if wm_state is not None:
|
||||||
|
sent = send_event(window)
|
||||||
|
else:
|
||||||
|
tree = window.query_tree()
|
||||||
|
for child in tree.children:
|
||||||
|
if not recurse_windows(child, parents + [window.id]) and level == 1:
|
||||||
|
sent = send_event(window)
|
||||||
|
logging.debug("%10s %s %s [%24s] [%s]",
|
||||||
|
hex(window.id),
|
||||||
|
"W" if not not wm_state else " ",
|
||||||
|
"S" if sent else " ",
|
||||||
|
name[:24] if name else "",
|
||||||
|
",".join(map(hex, parents)))
|
||||||
|
return sent
|
||||||
|
|
||||||
|
for sno in range(0, display.screen_count()):
|
||||||
|
screen = display.screen(sno)
|
||||||
|
recurse_windows(screen.root, [])
|
||||||
|
|
||||||
|
def sync_gtk3():
|
||||||
|
""" GTK3 applications can be notified of changes to their theming via
|
||||||
|
xsettingsd. This requires that the GTK3 theming information has been updated
|
||||||
|
in settings.ini as well as the gsettings schema, managed either by a
|
||||||
|
standalone xettingsd implementation or the gnome-settings-daemon. As for now,
|
||||||
|
we only support reloading `xsettingsd`: Send SIGHUP if we find it running, or
|
||||||
|
start it if it is installed and not running.
|
||||||
|
* https://github.com/swaywm/sway/wiki/GTK-3-settings-on-Wayland
|
||||||
|
* https://github.com/KDE/kde-gtk-config/blob/a5d4ddb3b1a27ec2ee4e1b6957a98a57ad56d39c/kded/configeditor.cpp#L285
|
||||||
|
"""
|
||||||
|
found = False
|
||||||
|
for proc in psutil.process_iter():
|
||||||
|
if proc.name() == "xsettingsd":
|
||||||
|
proc.send_signal(signal.SIGHUP)
|
||||||
|
found = True
|
||||||
|
logging.debug("Found xsettingsd process and sent SIGHUP: PID %d", proc.pid)
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
xsettingsd_binary = shutil.which("xsettingsd")
|
||||||
|
if xsettingsd_binary is not None:
|
||||||
|
proc = subprocess.Popen([xsettingsd_binary], cwd="/", start_new_session=True)
|
||||||
|
logging.debug("xsettingsd not running; started from %s; PID: %d", xsettingsd_binary, proc.pid)
|
||||||
|
else:
|
||||||
|
raise RuntimeError("xsettingsd not running and not in PATH, no settings changes propagated")
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
opts = getopts()
|
||||||
|
ret = 0
|
||||||
|
try:
|
||||||
|
sync_gtk2()
|
||||||
|
except Exception as err:
|
||||||
|
logging.warning("Failed to reload GTK2 settings: %s", err)
|
||||||
|
ret |= 1<<0
|
||||||
|
try:
|
||||||
|
sync_gtk3()
|
||||||
|
except Exception as err:
|
||||||
|
logging.warning("Failed to reload GTK3 settings: %s", err)
|
||||||
|
ret |= 1<<1
|
||||||
|
return 0 if opts.force else ret
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
|
@ -617,6 +617,19 @@ function getMenuPanel() {
|
||||||
echo "configdir: $CONFIGDIR"
|
echo "configdir: $CONFIGDIR"
|
||||||
cp "$HOME"/.config/mabox/mabox.conf "$CONFIGDIR"/
|
cp "$HOME"/.config/mabox/mabox.conf "$CONFIGDIR"/
|
||||||
}
|
}
|
||||||
|
function setXsettingsd() {
|
||||||
|
mkdir -p $HOME/.config/xsettingsd
|
||||||
|
|
||||||
|
gtk_theme="$(grep "^[^#]*gtk-theme-name" "${HOME}/.config/gtk-3.0/settings.ini")"
|
||||||
|
gtk_theme="${gtk_theme/gtk-theme-name*=}"
|
||||||
|
echo "Net/ThemeName \"$gtk_theme\"" >> "$HOME/.config/xsettingsd/xsettingsd.conf"
|
||||||
|
|
||||||
|
gtk_theme_icons="$(grep "^[^#]*gtk-icon-theme-name" "${HOME}/.config/gtk-3.0/settings.ini")"
|
||||||
|
gtk_theme_icons="${gtk_theme_icons/gtk-icon-theme-name*=}"
|
||||||
|
echo "Net/IconThemeName \"$gtk_theme_icons\"" > "$HOME/.config/xsettingsd/xsettingsd.conf"
|
||||||
|
|
||||||
|
bl-reload-gtk23
|
||||||
|
}
|
||||||
function checkTint2(){ # kill or restart tint2s for screenshot, if necessary
|
function checkTint2(){ # kill or restart tint2s for screenshot, if necessary
|
||||||
if [[ $1 = stop ]];then
|
if [[ $1 = stop ]];then
|
||||||
if ! cat "$SETTINGS" | grep "TINT2" &>/dev/null;then
|
if ! cat "$SETTINGS" | grep "TINT2" &>/dev/null;then
|
||||||
|
@ -1100,6 +1113,7 @@ function restoreSettings(){
|
||||||
#mb-regenerate-menu
|
#mb-regenerate-menu
|
||||||
restoreMenuPanel "$FPATH"
|
restoreMenuPanel "$FPATH"
|
||||||
restoreSettings
|
restoreSettings
|
||||||
|
setXsettingsd
|
||||||
}
|
}
|
||||||
|
|
||||||
### END FUNCTIONS ######################################################
|
### END FUNCTIONS ######################################################
|
||||||
|
|
Loading…
Reference in New Issue