diff --git a/modules/application.py b/modules/application.py index 2b840a6..4ab267e 100644 --- a/modules/application.py +++ b/modules/application.py @@ -121,11 +121,11 @@ class Application(GroupedControl): super().cleanup() - def respond_to(self, command): + def respond_to_ex(self, command): if command == '': return False logger.info('Received command %s', command) - return super().respond_to(command) + return super().respond_to_ex(command) def handle_signal(self, signal, tb): raise Exception("Received signal %s"%signal) @@ -134,6 +134,7 @@ class Application(GroupedControl): def run(self): parser = argparse.ArgumentParser(description='Action manager for xmobar') + self.set_name_ex('') self.configure(parser) self.bind_arguments(parser.parse_args()) @@ -143,7 +144,7 @@ class Application(GroupedControl): try: self.args.output_pipe.writelines(str(self)+"\n") while True: - if self.respond_to(str.rstrip(self.args.command_pipe.readline())) or self.periodic(): + if self.respond_to_ex(str.rstrip(self.args.command_pipe.readline())) or self.periodic(): self.args.output_pipe.writelines(str(self) + "\n") else: time.sleep(1) diff --git a/modules/core.py b/modules/core.py index 9b383dd..1b6e1b6 100644 --- a/modules/core.py +++ b/modules/core.py @@ -19,6 +19,7 @@ class AbstractControl(metaclass=abc.ABCMeta): Creates a new control """ self.args = None + self.__name = None @property def visible(self): @@ -94,6 +95,31 @@ class AbstractControl(metaclass=abc.ABCMeta): """ return False + def respond_to_ex(self, command: str): + """ + Responds to a non-cleaned user command + + Namespaced commands still contain the namespace of this class, which has to be + removed from the command before it is passed on to respond_to() + + :param command: The uncleaned command from the user + :return: bool Whether the displayed information is changed by the executed operations. + """ + logger.debug('%s.respond_to_ex: %s', self.__class__.__name__, command) + if command[0] == ':': + split_command = command.split(':', 2) + if len(split_command) == 3: + if split_command[1] != self.get_namespace(): + logger.error('%s.respond_to_ex: Unsollicited command (mismatch %s <-> %s)', self.__class__.__name__, split_command[1], self.get_namespace()) + return False + command = ':' + split_command[2] + logger.debug('%s.respond_to: %s', self.__class__.__name__, command) + return self.respond_to(command) + logger.warning('%s.respond_to_ex: Could not split into full command.', self.__class__.__name__) + else: + logger.debug('%s.respond_to: %s', self.__class__.__name__, command) + return self.respond_to(command) + def __str__(self): """ Creates the string representation of the module to show on the action bar @@ -153,6 +179,32 @@ class AbstractControl(metaclass=abc.ABCMeta): """ return {self.__class__.__name__: self.dump_state()} + def get_namespace(self): + """ + :return: The class-specific part of the namespace to use for namespaced commands. It cannot contain colons + """ + return self.__class__.__name__ + + def set_name(self, name: str): + """ + Sets the namespace to use for namespaced commands + + This method may be overridden to customize the namespace used for the class + It must call parent().set_name() with the desired name + :param name: The namespace to use for namespaced commands + """ + self.__name = name + logger.debug('%s.set_name: Set name to %s', self.__class__.__name__, name) + + def set_name_ex(self, name: str): + """ + Sets the namespace to use for namespaced commands + + This method must not be overridden + :param name: Namespace used by the object one up the hierarchy + """ + self.set_name('%s:%s' % (name, self.get_namespace())) + def create_pipe_command(self, command: str): """ Creates a shell command that will pass :command to the daemon through the controlpipe @@ -160,6 +212,8 @@ class AbstractControl(metaclass=abc.ABCMeta): :param command: The command to pass :return: str Shell command that will pass the given command through the controlpipe """ + if command[0] == ':': + command = self.__name + command return '{}/command.sh {} {}'.format(os.path.abspath(sys.path[0]), command, os.path.abspath(self.args.command_pipe.name)) @@ -202,7 +256,12 @@ class GroupedControl(AbstractControl): [m.cleanup() for m in self.__modules if m.enabled] def respond_to(self, command): - return any([m.respond_to(command) for m in self.__modules if m.enabled]) + if command[0] != ':': + return any([m.respond_to(command) for m in self.__modules if m.enabled]) + split_command = command.split(':', maxsplit=2) + if len(split_command) == 3: + index = int(split_command[1]) + return self.__modules[index].respond_to_ex(':' + split_command[2]) def periodic(self): return any([m.periodic() for m in self.__modules if m.enabled]) @@ -214,6 +273,10 @@ class GroupedControl(AbstractControl): data.update(m.dump_state_ex()) return data + def set_name(self, name: str): + super().set_name(name) + [m.set_name_ex('%s:%d' % (name, i)) for i, m in enumerate(self.__modules)] + def __passthrough_log(self, fn, s): logger.debug('%s.%s(): %s', self.__class__.__name__, fn, s) return s @@ -262,7 +325,7 @@ class WrappingControl(AbstractControl): self.child.load_state(state) def respond_to(self, command): - return self.child.respond_to(command) + return self.child.respond_to_ex(command) @property def enabled(self): @@ -288,6 +351,10 @@ class WrappingControl(AbstractControl): def load_state_ex(self, state): self.child.load_state_ex(state) + def set_name(self, name: str): + super().set_name(name) + self.child.set_name_ex(name) + def __str__(self): return self.child.__str__() diff --git a/modules/toggle.py b/modules/toggle.py index 97bfa06..ebfcf19 100644 --- a/modules/toggle.py +++ b/modules/toggle.py @@ -1,6 +1,5 @@ from .core import AbstractControl, WrappingControl, action import abc -import functools class ToggleAction(AbstractControl, metaclass=abc.ABCMeta): @@ -46,16 +45,13 @@ class ToggleControl(WrappingControl): super().__init__(toggle_action) self.__letter = letter - def __get_control_command(self): - return '%s:%s:%s' % (self.__class__.__name__, self.__letter, self.child.__class__.__name__) - def respond_to(self, command): - if command == self.__get_control_command(): + if command == ':toggle': self.child.toggle() return True def __str__(self): return action( - self.create_pipe_command(self.__get_control_command()), + self.create_pipe_command(':toggle'), self.__letter.upper() if self.child.state else self.__letter.lower() )