1# Copyright (c) 2012, Tycho Andersen. All rights reserved. 2# 3# Permission is hereby granted, free of charge, to any person obtaining a copy 4# of this software and associated documentation files (the "Software"), to deal 5# in the Software without restriction, including without limitation the rights 6# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7# copies of the Software, and to permit persons to whom the Software is 8# furnished to do so, subject to the following conditions: 9# 10# The above copyright notice and this permission notice shall be included in 11# all copies or substantial portions of the Software. 12# 13# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19# SOFTWARE. 20 21 22from libqtile import hook 23from libqtile.scratchpad import ScratchPad 24 25 26class QtileState: 27 """Represents the state of the qtile object 28 29 Primarily used for restoring state across restarts; any additional state 30 which doesn't fit nicely into X atoms can go here. 31 """ 32 def __init__(self, qtile): 33 # Note: window state is saved and restored via _NET_WM_STATE, so 34 # the only thing we need to restore here is the layout and screen 35 # configurations. 36 self.groups = [] 37 self.screens = {} 38 self.current_screen = 0 39 self.scratchpads = {} 40 self.orphans = [] 41 42 for group in qtile.groups: 43 if isinstance(group, ScratchPad): 44 self.scratchpads[group.name] = group.get_state() 45 for dd in group.dropdowns.values(): 46 dd.hide() 47 else: 48 self.groups.append((group.name, group.layout.name, group.label)) 49 50 for index, screen in enumerate(qtile.screens): 51 self.screens[index] = screen.group.name 52 if screen == qtile.current_screen: 53 self.current_screen = index 54 55 def apply(self, qtile): 56 """ 57 Rearrange the windows in the specified Qtile object according to this 58 QtileState. 59 """ 60 for (group, layout, label) in self.groups: 61 try: 62 qtile.groups_map[group].layout = layout 63 qtile.groups_map[group].label = label 64 except KeyError: 65 qtile.add_group(group, layout, label=label) 66 67 for (screen, group) in self.screens.items(): 68 try: 69 group = qtile.groups_map[group] 70 qtile.screens[screen].set_group(group) 71 except (KeyError, IndexError): 72 pass # group or screen missing 73 74 for group in qtile.groups: 75 if isinstance(group, ScratchPad) and group.name in self.scratchpads: 76 orphans = group.restore_state(self.scratchpads.pop(group.name)) 77 self.orphans.extend(orphans) 78 for sp_state in self.scratchpads.values(): 79 for _, pid, _ in sp_state: 80 self.orphans.append(pid) 81 if self.orphans: 82 hook.subscribe.client_new(self.handle_orphan_dropdowns) 83 84 qtile.focus_screen(self.current_screen) 85 86 def handle_orphan_dropdowns(self, client): 87 """ 88 Remove any windows from now non-existent scratchpad groups. 89 """ 90 client_pid = client.get_pid() 91 if client_pid in self.orphans: 92 self.orphans.remove(client_pid) 93 client.group = None 94 if not self.orphans: 95 hook.unsubscribe.client_new(self.handle_orphan_dropdowns) 96