def cmd_ungrab_all_chords(self) -> None: """Leave all chord modes and grab the root bindings""" hook.fire("leave_chord") self.ungrab_keys() self.chord_stack.clear() for key in self.config.keys: self.grab_key(key)
def _reconfigure_floating(self, new_float_state=FLOATING): if new_float_state == MINIMIZED: self.state = IconicState self.hide() else: width = max(self.width, self.hints.get('min_width', 0)) height = max(self.height, self.hints.get('min_height', 0)) if self.hints['base_width'] and self.hints['width_inc']: width_adjustment = (width - self.hints['base_width']) % self.hints['width_inc'] width -= width_adjustment if new_float_state == FULLSCREEN: self.x += int(width_adjustment / 2) if self.hints['base_height'] and self.hints['height_inc']: height_adjustment = (height - self.hints['base_height']) % self.hints['height_inc'] height -= height_adjustment if new_float_state == FULLSCREEN: self.y += int(height_adjustment / 2) self.place( self.x, self.y, width, height, self.borderwidth, self.bordercolor, above=True, ) if self._float_state != new_float_state: self._float_state = new_float_state if self.group: # may be not, if it's called from hook self.group.mark_floating(self, True) hook.fire('float_change')
def update_hints(self): """Update the local copy of the window's WM_HINTS See http://tronche.com/gui/x/icccm/sec-4.html#WM_HINTS """ try: h = self.window.get_wm_hints() normh = self.window.get_wm_normal_hints() except (xcffib.xproto.WindowError, xcffib.xproto.AccessError): return if normh: self.hints.update(normh) if h and 'UrgencyHint' in h['flags']: if self.qtile.current_window != self: self.hints['urgent'] = True hook.fire('client_urgent_hint_changed', self) elif self.urgent: self.hints['urgent'] = False hook.fire('client_urgent_hint_changed', self) if h and 'InputHint' in h['flags']: self.hints['input'] = h['input'] if getattr(self, 'group', None): self.group.layout_all() return
def _on_set_title(self, _listener, _data): logger.debug("Signal: xwindow set_title") title = self.surface.title if title != self.name: self.name = title self.ftm_handle.set_title(title) hook.fire("client_name_updated", self)
def update_wm_net_icon(self): """Set a dict with the icons of the window""" icon = self.window.get_property('_NET_WM_ICON', 'CARDINAL') if not icon: return icon = list(map(ord, icon.value)) icons = {} while True: if not icon: break size = icon[:8] if len(size) != 8 or not size[0] or not size[4]: break icon = icon[8:] width = size[0] height = size[4] next_pix = width * height * 4 data = icon[:next_pix] arr = array.array("B", data) for i in range(0, len(arr), 4): mult = arr[i + 3] / 255. arr[i + 0] = int(arr[i + 0] * mult) arr[i + 1] = int(arr[i + 1] * mult) arr[i + 2] = int(arr[i + 2] * mult) icon = icon[next_pix:] icons["%sx%s" % (width, height)] = arr self.icons = icons hook.fire("net_wm_icon_change", self)
def update_hints(self): """Update the local copy of the window's WM_HINTS See http://tronche.com/gui/x/icccm/sec-4.html#WM_HINTS """ try: h = self.window.get_wm_hints() normh = self.window.get_wm_normal_hints() except (xcffib.xproto.WindowError, xcffib.xproto.AccessError): return # FIXME # h values # { # 'icon_pixmap': 4194337, # 'icon_window': 0, # 'icon_mask': 4194340, # 'icon_y': 0, # 'input': 1, # 'icon_x': 0, # 'window_group': 4194305 # 'initial_state': 1, # 'flags': set(['StateHint', # 'IconMaskHint', # 'WindowGroupHint', # 'InputHint', # 'UrgencyHint', # 'IconPixmapHint']), # } if normh: normh.pop('flags') normh['min_width'] = max(0, normh.get('min_width', 0)) normh['min_height'] = max(0, normh.get('min_height', 0)) if not normh['base_width'] and \ normh['min_width'] and \ normh['width_inc']: # seems xcffib does ignore base width :( normh['base_width'] = (normh['min_width'] % normh['width_inc']) if not normh['base_height'] and \ normh['min_height'] and \ normh['height_inc']: # seems xcffib does ignore base height :( normh['base_height'] = (normh['min_height'] % normh['height_inc']) self.hints.update(normh) if h and 'UrgencyHint' in h['flags']: if self.qtile.current_window != self: self.hints['urgent'] = True hook.fire('client_urgent_hint_changed', self) elif self.urgent: self.hints['urgent'] = False hook.fire('client_urgent_hint_changed', self) if getattr(self, 'group', None): self.group.layout_all() return
def restart(self): hook.fire("restart") lifecycle.behavior = lifecycle.behavior.RESTART state_file = os.path.join(tempfile.gettempdir(), 'qtile-state') with open(state_file, 'wb') as f: self.dump_state(f) lifecycle.state_file = state_file self._stop()
def handle_EnterNotify(self, e): # noqa: N802 hook.fire("client_mouse_enter", self) if self.qtile.config.follow_mouse_focus: if self.group.current_window != self: self.group.focus(self, False) if self.group.screen and self.qtile.current_screen != self.group.screen: self.qtile.focus_screen(self.group.screen.index, False) return True
def grab_chord(self, chord) -> None: self.chord_stack.append(chord) if self.chord_stack: hook.fire("enter_chord", self.chord_stack[-1].mode) self.ungrab_keys() for key in chord.submappings: self.grab_key(key)
def unmanage(self, win): c = self.windows_map.get(win) if c: hook.fire("client_killed", c) if isinstance(c, base.Static) and c.reserved_space: self.free_reserved_space(c.reserved_space, c.screen) if getattr(c, "group", None): c.group.remove(c) del self.windows_map[win] self.core.update_client_list(self.windows_map)
def test_can_unsubscribe_from_hook(): test = Call(0) hook.subscribe.group_window_add(test) hook.fire("group_window_add", 3) assert test.val == 3 hook.unsubscribe.group_window_add(test) hook.fire("group_window_add", 4) assert test.val == 3
async def t(): val = 0 async def co(new_val): nonlocal val val = new_val hook.subscribe.group_window_add(co(8)) hook.fire("group_window_add") await asyncio.sleep(0) assert val == 8
def test_hook_calls_subscriber_async_co(): val = 0 async def co(new_val): nonlocal val val = new_val hook.subscribe.group_window_add(co(8)) hook.fire("group_window_add") assert val == 8
def _reconfigure_floating(self, x, y, w, h, new_float_state=FloatStates.FLOATING): if new_float_state == FloatStates.MINIMIZED: self.hide() else: self.place( x, y, w, h, self.borderwidth, self.bordercolor, above=True, respect_hints=True ) if self._float_state != new_float_state: self._float_state = new_float_state if self.group: # may be not, if it's called from hook self.group.mark_floating(self, True) hook.fire("float_change")
def _reconfigure_floating(self, x, y, w, h, new_float_state=FloatStates.FLOATING): if new_float_state == FloatStates.MINIMIZED: self.hide() else: # TODO: Can we get min/max size, resizing increments etc and respect them? self.place( x, y, w, h, self.borderwidth, self.bordercolor, above=True, ) if self._float_state != new_float_state: self._float_state = new_float_state if self.group: # may be not, if it's called from hook self.group.mark_floating(self, True) hook.fire('float_change')
def focus(self, warp: bool) -> None: self.core.focus_window(self) if isinstance(self, base.Internal): # self.core.focus_window is enough for internal windows return if warp and self.qtile.config.cursor_warp: self.core.warp_pointer( self.x + self.width // 2, self.y + self.height // 2, ) hook.fire("client_focus", self)
def handle_PropertyNotify(self, event) -> None: # noqa: N802 name = self.conn.atoms.get_name(event.atom) # it's the selection property if name in ("PRIMARY", "CLIPBOARD"): assert event.window == self._selection_window.wid prop = self._selection_window.get_property(event.atom, "UTF8_STRING") # If the selection property is None, it is unset, which means the # clipboard is empty. value = prop and prop.value.to_utf8() or "" self._selection[name]["selection"] = value hook.fire("selection_change", name, self._selection[name])
def focus_screen(self, n, warp=True): """Have Qtile move to screen and put focus there""" if n >= len(self.screens): return old = self.current_screen self.current_screen = self.screens[n] if old != self.current_screen: hook.fire("current_screen_change") hook.fire("setgroup") old.group.layout_all() self.current_group.focus(self.current_window, warp) if self.current_window is None and warp: self.warp_to_screen()
def add_group(self, name, layout=None, layouts=None, label=None): if name not in self.groups_map.keys(): g = _Group(name, layout, label=label) self.groups.append(g) if not layouts: layouts = self.config.layouts g._configure(layouts, self.config.floating_layout, self) self.groups_map[name] = g hook.fire("addgroup", name) hook.fire("changegroup") self.update_desktops() return True return False
def add(self, win, focus=True, force=False): hook.fire("group_window_add", self, win) self.windows.add(win) win.group = self if self.qtile.config.auto_fullscreen and win.wants_to_fullscreen: win._float_state = FloatStates.FULLSCREEN elif self.floating_layout.match(win): win._float_state = FloatStates.FLOATING if win.floating: self.floating_layout.add(win) else: for i in self.layouts: i.add(win) if focus: self.focus(win, warp=True, force=force)
def setGroup(self, new_group): """ Put group on this screen """ if new_group.screen == self: return if not new_group.screen is None: return self.previous_group = self.group if new_group is None: return if new_group.screen: # g1 <-> s1 (self) # g2 (new_group) <-> s2 to # g1 <-> s2 # g2 <-> s1 g1 = self.group s1 = self g2 = new_group s2 = new_group.screen s2.group = g1 g1._setScreen(s2) s1.group = g2 g2._setScreen(s1) else: old_group = self.group self.group = new_group # display clients of the new group and then hide from old group # to remove the screen flickering new_group._setScreen(self) if old_group is not None: old_group._setScreen(None) hook.fire("setgroup") hook.fire("focus_change") hook.fire( "layout_change", self.group.layouts[self.group.currentLayout], self.group )