This commit is contained in:
boyska 2022-01-25 12:12:06 +01:00
parent 2e8e785616
commit a10aeceab4

View file

@ -21,8 +21,15 @@ import re
import stat import stat
from contextlib import closing from contextlib import closing
from gi import require_version from gi import require_version
require_version("Gtk", "3.0") require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, GLib, GObject # pylint:disable=wrong-import-position from gi.repository import (
Gtk,
Gdk,
GLib,
GObject,
) # pylint:disable=wrong-import-position
try: try:
require_version("Wnck", "3.0") require_version("Wnck", "3.0")
from gi.repository import Wnck from gi.repository import Wnck
@ -35,6 +42,7 @@ if sys.version_info.major == 3:
else: else:
# py 2.x # py 2.x
from ConfigParser import SafeConfigParser # pylint:disable=import-error from ConfigParser import SafeConfigParser # pylint:disable=import-error
# In python 2, ENOENT is sometimes IOError and sometimes OSError. Catch # In python 2, ENOENT is sometimes IOError and sometimes OSError. Catch
# both by catching their immediate superclass exception EnvironmentError. # both by catching their immediate superclass exception EnvironmentError.
FileNotFoundError = EnvironmentError # pylint: disable=redefined-builtin FileNotFoundError = EnvironmentError # pylint: disable=redefined-builtin
@ -78,7 +86,11 @@ class suppress_if_errno(object):
# exactly reproduce the limitations of the CPython interpreter. # exactly reproduce the limitations of the CPython interpreter.
# #
# See http://bugs.python.org/issue12029 for more details # See http://bugs.python.org/issue12029 for more details
return exctype is not None and issubclass(exctype, self._exceptions) and excinst.errno == self._exc_val return (
exctype is not None
and issubclass(exctype, self._exceptions)
and excinst.errno == self._exc_val
)
class ClipsterError(Exception): class ClipsterError(Exception):
@ -88,7 +100,6 @@ class ClipsterError(Exception):
Exception.__init__(self, args) Exception.__init__(self, args)
class Daemon(object): class Daemon(object):
"""Handles clipboard events, client requests, stores history.""" """Handles clipboard events, client requests, stores history."""
@ -104,12 +115,12 @@ class Daemon(object):
self.primary = Gtk.Clipboard.get(Gdk.SELECTION_PRIMARY) self.primary = Gtk.Clipboard.get(Gdk.SELECTION_PRIMARY)
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
self.boards = {"PRIMARY": [], "CLIPBOARD": []} self.boards = {"PRIMARY": [], "CLIPBOARD": []}
self.pid_file = self.config.get('calippo', 'pid_file') self.pid_file = self.config.get("calippo", "pid_file")
self.client_msgs = {} self.client_msgs = {}
# Flag to indicate that the in-memory history should be flushed to disk # Flag to indicate that the in-memory history should be flushed to disk
self.update_history_file = False self.update_history_file = False
# Flag whether next clipboard change should be ignored # Flag whether next clipboard change should be ignored
self.ignore_next = {'PRIMARY': False, 'CLIPBOARD': False} self.ignore_next = {"PRIMARY": False, "CLIPBOARD": False}
self.whitelist_classes = self.blacklist_classes = [] self.whitelist_classes = self.blacklist_classes = []
self.command = command self.command = command
@ -136,10 +147,8 @@ class Daemon(object):
self.window = Gtk.Window(type=Gtk.WindowType.POPUP) self.window = Gtk.Window(type=Gtk.WindowType.POPUP)
# Handle clipboard changes # Handle clipboard changes
self.p_id = self.primary.connect('owner-change', self.p_id = self.primary.connect("owner-change", self.owner_change)
self.owner_change) self.c_id = self.clipboard.connect("owner-change", self.owner_change)
self.c_id = self.clipboard.connect('owner-change',
self.owner_change)
# Handle unix signals # Handle unix signals
GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, self.exit) GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, self.exit)
GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, self.exit) GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, self.exit)
@ -147,14 +156,13 @@ class Daemon(object):
Gtk.main() Gtk.main()
def prepare_files(self): def prepare_files(self):
"""Ensure that all files and sockets used """Ensure that all files and sockets used
by the daemon are available.""" by the daemon are available."""
# Create the calippo dir if necessary # Create the calippo dir if necessary
with suppress_if_errno(FileExistsError, errno.EEXIST): with suppress_if_errno(FileExistsError, errno.EEXIST):
os.makedirs(self.config.get('calippo', 'data_dir')) os.makedirs(self.config.get("calippo", "data_dir"))
# check for existing pid_file, and tidy up if appropriate # check for existing pid_file, and tidy up if appropriate
with suppress_if_errno(FileNotFoundError, errno.ENOENT): with suppress_if_errno(FileNotFoundError, errno.ENOENT):
@ -168,7 +176,9 @@ class Daemon(object):
try: try:
# Do nothing, but raise an error if no such process # Do nothing, but raise an error if no such process
os.kill(pid, 0) os.kill(pid, 0)
raise ClipsterError("Daemon already running: pid {}".format(pid)) raise ClipsterError(
"Daemon already running: pid {}".format(pid)
)
except ProcessLookupError as exc: except ProcessLookupError as exc:
if exc.errno != errno.ESRCH: if exc.errno != errno.ESRCH:
raise raise
@ -182,7 +192,7 @@ class Daemon(object):
logging.debug("owner-change event!") logging.debug("owner-change event!")
selection = str(event.selection) selection = str(event.selection)
logging.debug("selection: %s", selection) logging.debug("selection: %s", selection)
active = self.config.get('calippo', 'active_selections').split(',') active = self.config.get("calippo", "active_selections").split(",")
if selection not in active: if selection not in active:
return return
@ -277,14 +287,21 @@ def find_config():
# Set a default directory for calippo files # Set a default directory for calippo files
# https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
xdg_config_dirs = os.environ.get('XDG_CONFIG_DIRS', '/etc/xdg').split(':') xdg_config_dirs = os.environ.get("XDG_CONFIG_DIRS", "/etc/xdg").split(":")
xdg_config_dirs.insert(0, os.environ.get('XDG_CONFIG_HOME', os.path.join(os.environ.get('HOME'), ".config"))) xdg_config_dirs.insert(
xdg_data_home = os.environ.get('XDG_DATA_HOME', os.path.join(os.environ.get('HOME'), ".local/share")) 0,
os.environ.get(
"XDG_CONFIG_HOME", os.path.join(os.environ.get("HOME"), ".config")
),
)
xdg_data_home = os.environ.get(
"XDG_DATA_HOME", os.path.join(os.environ.get("HOME"), ".local/share")
)
data_dir = os.path.join(xdg_data_home, "calippo") data_dir = os.path.join(xdg_data_home, "calippo")
# Keep trying to define conf_dir, moving from local -> global # Keep trying to define conf_dir, moving from local -> global
for path in xdg_config_dirs: for path in xdg_config_dirs:
conf_dir = os.path.join(path, 'calippo') conf_dir = os.path.join(path, "calippo")
if os.path.exists(conf_dir): if os.path.exists(conf_dir):
return conf_dir, data_dir return conf_dir, data_dir
return "", data_dir return "", data_dir
@ -300,8 +317,10 @@ def main():
args = parse_args() args = parse_args()
# Enable logging # Enable logging
logging.basicConfig(format='%(levelname)s:%(message)s', logging.basicConfig(
level=getattr(logging, args.log_level.upper())) format="%(levelname)s:%(message)s",
level=getattr(logging, args.log_level.upper()),
)
logging.debug("Debugging Enabled.") logging.debug("Debugging Enabled.")
config = parse_config(args, data_dir, conf_dir) config = parse_config(args, data_dir, conf_dir)
@ -314,7 +333,7 @@ def safe_decode(data):
"""Convenience method to ensure everything is utf-8.""" """Convenience method to ensure everything is utf-8."""
try: try:
data = data.decode('utf-8') data = data.decode("utf-8")
except (UnicodeDecodeError, UnicodeEncodeError, AttributeError): except (UnicodeDecodeError, UnicodeEncodeError, AttributeError):
pass pass
return data return data