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"
|
||||
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
|
||||
if [[ $1 = stop ]];then
|
||||
if ! cat "$SETTINGS" | grep "TINT2" &>/dev/null;then
|
||||
|
@ -1100,6 +1113,7 @@ function restoreSettings(){
|
|||
#mb-regenerate-menu
|
||||
restoreMenuPanel "$FPATH"
|
||||
restoreSettings
|
||||
setXsettingsd
|
||||
}
|
||||
|
||||
### END FUNCTIONS ######################################################
|
||||
|
|
Loading…
Reference in New Issue