Source code for libqtile.dgroups

import collections

import libqtile.hook
from libqtile.backend.base import Static
from libqtile.config import Group, Key, Rule
from libqtile.lazy import lazy
from libqtile.log_utils import logger


[docs] def simple_key_binder(mod, keynames=None): """Bind keys to mod+group position or to the keys specified as second argument""" def func(dgroup): # unbind all for key in dgroup.keys[:]: dgroup.qtile.ungrab_key(key) dgroup.qtile.config.keys.remove(key) dgroup.keys.remove(key) if keynames: keys = keynames else: # keys 1 to 9 and 0 keys = list(map(str, list(range(1, 10)) + [0])) # bind all keys for keyname, group in zip(keys, dgroup.qtile.groups): name = group.name key = Key([mod], keyname, lazy.group[name].toscreen()) key_s = Key([mod, "shift"], keyname, lazy.window.togroup(name)) key_c = Key([mod, "control"], keyname, lazy.group.switch_groups(name)) dgroup.keys.extend([key, key_s, key_c]) dgroup.qtile.config.keys.extend([key, key_s, key_c]) dgroup.qtile.grab_key(key) dgroup.qtile.grab_key(key_s) dgroup.qtile.grab_key(key_c) return func
class DGroups: """Dynamic Groups""" def __init__(self, qtile, dgroups, key_binder=None, delay=1): self.qtile = qtile self.groups = dgroups self.groups_map = {} self.rules = [] self.rules_map = {} self.last_rule_id = 0 for rule in getattr(qtile.config, "dgroups_app_rules", []): self.add_rule(rule) self.keys = [] self.key_binder = key_binder self._setup_hooks() self._setup_groups() self.delay = delay self.timeout = {} def add_rule(self, rule, last=True): rule_id = self.last_rule_id self.rules_map[rule_id] = rule if last: self.rules.append(rule) else: self.rules.insert(0, rule) self.last_rule_id += 1 return rule_id def remove_rule(self, rule_id): rule = self.rules_map.get(rule_id) if rule: self.rules.remove(rule) del self.rules_map[rule_id] else: logger.warning('Rule "%s" not found', rule_id) def add_dgroup(self, group, start=False): self.groups_map[group.name] = group rule = Rule(group.matches, group=group.name) self.rules.append(rule) if start: self.qtile.add_group( group.name, group.layout, group.layouts, group.label, screen_affinity=group.screen_affinity, ) def _setup_groups(self): def launch_app(app, group): def launcher(): self.qtile.spawn(app, group=group) return launcher for group in self.groups: self.add_dgroup(group, group.init) if group.spawn and not self.qtile.no_spawn: if isinstance(group.spawn, str): spawns = [group.spawn] else: spawns = group.spawn for spawn in spawns: libqtile.hook.subscribe.startup_once(launch_app(spawn, group=group.name)) def _setup_hooks(self): libqtile.hook.subscribe.addgroup(self._addgroup) libqtile.hook.subscribe.client_new(self._add) libqtile.hook.subscribe.client_killed(self._del) if self.key_binder: libqtile.hook.subscribe.setgroup(lambda: self.key_binder(self)) libqtile.hook.subscribe.changegroup(lambda: self.key_binder(self)) def _addgroup(self, group_name): if group_name not in self.groups_map: self.add_dgroup(Group(group_name, persist=False)) def _add(self, client): if client in self.timeout: logger.debug("Remove dgroup source") self.timeout.pop(client).cancel() # ignore static windows if isinstance(client, Static): return # ignore windows whose groups is already set (e.g. from another hook or # when it was set on state restore) if client.group is not None: return group_set = False intrusive = False delete_rules = [] for rule in self.rules: # Matching Rules if rule.matches(client): if rule.group: if rule.group in self.groups_map: layout = self.groups_map[rule.group].layout layouts = self.groups_map[rule.group].layouts label = self.groups_map[rule.group].label else: layout = None layouts = None label = None group_added = self.qtile.add_group(rule.group, layout, layouts, label) client.togroup(rule.group) group_set = True group_obj = self.qtile.groups_map[rule.group] group = self.groups_map.get(rule.group) if group and group_added: for k, v in list(group.layout_opts.items()): if isinstance(v, collections.abc.Callable): v(group_obj.layout) else: setattr(group_obj.layout, k, v) affinity = group.screen_affinity if affinity and len(self.qtile.screens) > affinity: self.qtile.screens[affinity].set_group(group_obj) if rule.float: client.enable_floating() if rule.intrusive: intrusive = rule.intrusive if rule.one_time: delete_rules.append(rule) if rule.break_on_match: break if delete_rules: ids_to_delete = [ rule_id for rule_id, rule in self.rules_map.items() if rule in delete_rules ] for rule_id in ids_to_delete: self.remove_rule(rule_id) # If app doesn't have a group if not group_set: current_group = self.qtile.current_group.name if ( current_group in self.groups_map and self.groups_map[current_group].exclusive and not intrusive ): wm_class = client.get_wm_class() if wm_class: if len(wm_class) > 1: wm_class = wm_class[1] else: wm_class = wm_class[0] group_name = wm_class else: group_name = client.name or "Unnamed" self.add_dgroup(Group(group_name, persist=False), start=True) client.togroup(group_name) self.sort_groups() def sort_groups(self): grps = self.qtile.groups sorted_grps = sorted(grps, key=lambda g: self.groups_map[g.name].position) if grps != sorted_grps: self.qtile.groups = sorted_grps libqtile.hook.fire("changegroup") def _del(self, client): # ignore static windows if isinstance(client, Static): return group = client.group def delete_client(): # Delete group if empty and don't persist if ( group and group.name in self.groups_map and not self.groups_map[group.name].persist and len(group.windows) <= 0 ): self.qtile.delete_group(group.name) self.sort_groups() del self.timeout[client] if group is not None and group.persist: return logger.debug("Deleting %s in %ss", group, self.delay) if client not in self.timeout: self.timeout[client] = self.qtile.call_later(self.delay, delete_client)