def update_key(self): key = self.key() if key != self.current_key and key in self.manager.ghosts: InfiniteGlass.DEBUG("ghost", "DUPLICATE SHADOW %s\n" % (self, )) self.destroy() if not self.manager.restoring_ghosts and not self.from_config: cur = self.manager.dbconn.cursor() for name, value in self.properties.items(): if self._properties.get(name, NoValue) != value: cur.execute( """ insert or replace into ghosts (key, name, value) VALUES (?, ?, ?) """, (key, name, json.dumps(value, default=InfiniteGlass.tojson( self.manager.display)))) self.manager.changes = True for name, value in self._properties.items(): if name not in self.properties: cur.execute( """ delete from ghosts where key=? and name=? """, (key, name)) self.manager.changes = True if key != self.current_key and self.current_key is not None: try: cur.execute( """ update ghosts set key=? where key=? """, (key, self.current_key)) except Exception as e: InfiniteGlass.DEBUG( "ghost.database", "Error updating key in db: %s\nkey=%s => key=%s\n" % (e, self.current_key, key)) self.destroy() self.manager.changes = True if key == self.current_key: return client = self.manager.clients.get(self.properties.get("SM_CLIENT_ID")) if self.current_key is not None: del self.manager.ghosts[self.current_key] if client: del client.ghosts[self.current_key] InfiniteGlass.DEBUG( "ghost", "UPDATE KEY from %s to %s\n" % (self.current_key, key)) sys.stderr.flush() self.current_key = key self.manager.ghosts[self.current_key] = self if client: client.ghosts[self.current_key] = self
def animationfn(): while True: tick[0] += 1 current = time.time() progress = (current - start) / timeframe if progress > 1.: window[atom] = dst InfiniteGlass.DEBUG( "final", "SET FINAL %s.%s=%s\n" % (window.__window__(), atom, dst)) else: pos = easing(progress) res = [ pos * dstval + (1. - pos) * srcval for srcval, dstval in values ] if isint: res = [int(item) for item in res] window[atom] = res InfiniteGlass.DEBUG( "transition", "SET %s.%s=%s\n" % (window.__window__(), atom, [ progress * dstval + (1. - progress) * srcval for srcval, dstval in values ])) display.flush() display.sync() if progress > 1.: return yield
def zoom_1_1_1(self, event): "Zoom to make the current window full-screen, and change its resolution to the same as the screen" win = self.get_event_window(event) if not win or win == self.display.root: return InfiniteGlass.DEBUG("zoom", "zoom_screen_to_window_and_window_to_screen\n") size = self.display.root["IG_VIEW_DESKTOP_SIZE"] coords = list(win["IG_COORDS"]) screen = list(self.display.root["IG_VIEW_DESKTOP_VIEW"]) coords[3] = (size[1] * coords[2]) / size[0] screen[2] = coords[2] screen[3] = coords[3] screen[0] = coords[0] screen[1] = coords[1] - screen[3] InfiniteGlass.DEBUG("zoom", " screen=%s geom=%s\n" % (screen, size)) win["IG_COORDS_ANIMATE"] = coords win["IG_SIZE_ANIMATE"] = size self.display.root["IG_VIEW_DESKTOP_VIEW_ANIMATE"] = screen self.display.animate_window.send(self.display.animate_window, "IG_ANIMATE", win, "IG_COORDS", .5) self.display.animate_window.send(self.display.animate_window, "IG_ANIMATE", win, "IG_SIZE", .5) self.display.animate_window.send(self.display.animate_window, "IG_ANIMATE", self.display.root, "IG_VIEW_DESKTOP_VIEW", .5) self.display.flush() self.display.sync()
def destroy(self): InfiniteGlass.DEBUG("destroy", "ISLAND DESTROY %s\n" % (self, )) sys.stderr.flush() try: if self.window is not None: self.window.destroy() self.manager.display.eventhandlers.remove(self.RestartMessage) self.manager.display.eventhandlers.remove(self.DestroyNotify) self.manager.display.eventhandlers.remove(self.PropertyNotify) self.manager.display.eventhandlers.remove(self.WMDelete) self.manager.display.eventhandlers.remove(self.CloseMessage) self.manager.display.eventhandlers.remove(self.Expose) self.window = None except Exception as e: InfiniteGlass.DEBUG("destroy", "Error closing window: %s" % e) self.manager.islands.pop(self.key, None) cur = self.manager.dbconn.cursor() cur.execute( """ delete from islands where key = ? """, (self.key, )) self.manager.dbconn.commit()
def animationfn(): while True: tick[0] += 1 current = time.time() progress = (current - start) / timeframe if progress > 1.: current_values = dst InfiniteGlass.DEBUG( "final", "SET FINAL [%s]=%s\n" % (window.__window__(), dst)) else: pos = easing(progress) current_values = [ int(pos * dstval + (1. - pos) * srcval) for srcval, dstval in values ] if tick[0] % 100 == 0: InfiniteGlass.DEBUG( "transition", "SET [%s]=%s\n" % (window.__window__(), [ progress * dstval + (1. - progress) * srcval for srcval, dstval in values ])) window.configure(x=current_values[0], y=current_values[1], width=current_values[2], height=current_values[3]) display.flush() display.sync() if progress > 1.: return yield
def destroy(self): InfiniteGlass.DEBUG( "destroy", "Window destroyed %s" % self.window.get("WM_NAME", self.window)) InfiniteGlass.DEBUG("destroy", " key=%s" % self.key()) InfiniteGlass.DEBUG( "destroy", " %s" % ("IGNORED" if self.is_ignored() else "NOT IGNORED")) InfiniteGlass.DEBUG( "destroy", " %s" % ("has ghost" if self.ghost else "no ghost")) if not self.is_ignored(): if not self.ghost: self.ghost = glass_ghosts.ghost.Shadow(self.manager, self.properties) else: self.ghost.properties.update(self.properties) self.ghost.update_key() self.ghost.activate() else: if self.ghost: self.ghost.destroy() self.manager.windows.pop(self.id, None) self.manager.display.eventhandlers.remove(self.PropertyNotify) self.manager.display.eventhandlers.remove(self.DeleteMessage) self.manager.display.eventhandlers.remove(self.CloseMessage) self.manager.display.eventhandlers.remove(self.SleepMessage) self.manager.display.eventhandlers.remove(self.UnmapNotify) self.manager.display.eventhandlers.remove(self.ConfigureNotify) if self.client: self.client.remove_window(self)
def zoom_to_window_to_the(self, event, direction): print("ZOOM OUT TO WINDOW TO %s" % (direction)) view = list(self.display.root["IG_VIEW_DESKTOP_VIEW"]) vx = view[0] + view[2] / 2. vy = view[1] + view[3] / 2. def angular_diff(a, b): diff = (a - b) % (2 * numpy.pi) if diff <= numpy.pi: return diff return 2 * numpy.pi - diff windows = [] visible, overlap, invisible = InfiniteGlass.windows.get_windows( self.display, view) for child, coords in invisible + overlap: if child.get("IG_LAYER", "IG_LAYER_DESKTOP") != "IG_LAYER_DESKTOP": continue x = coords[0] + coords[2] / 2. y = coords[1] - coords[3] / 2. wdist = numpy.sqrt((x - vx)**2 + (y - vy)**2) wdir = angular_diff(numpy.arctan2(y - vy, x - vx), direction) / (numpy.pi / 4) if wdir < 1.0: windows.append((wdist * (1 + wdir), (x, y), coords, child)) if not windows: screeny = numpy.sin(direction) screenx = numpy.cos(direction) view = [view[0] + screenx * view[2], view[1] + screeny * view[3] ] + view[2:] else: windows.sort(key=lambda a: a[0]) dist, center, coords, next_window = windows[0] InfiniteGlass.DEBUG( "visible", "Visible: %s\n" % (",".join("%s/%s[%s] @ %s" % (v.get("WM_NAME", None), v.get("WM_CLASS", None), v.__window__(), c) for v, c in visible), )) InfiniteGlass.DEBUG( "next", "Next window %s/%s[%s] @ %s\n" % (next_window.get("WM_NAME", None), next_window.get( "WM_CLASS", None), next_window.__window__(), coords)) view = zoom_to_window_calc(self, view, next_window, view_center=(vx, vy), coords=coords, center=center) InfiniteGlass.DEBUG("view", "View %s\n" % (view, )) self.display.root["IG_VIEW_DESKTOP_VIEW_ANIMATE"] = view self.display.animate_window.send(self.display.animate_window, "IG_ANIMATE", self.display.root, "IG_VIEW_DESKTOP_VIEW", .5)
def adjust_view(self, view, win=None): visible, overlap, invisible = InfiniteGlass.windows.get_windows( self.display, view) visible_pixelcontent = [(w, c) for w, c in visible if "IG_CONTENT" not in w] if win is None: for win in visible: if "IG_CONTENT" not in win: break else: InfiniteGlass.DEBUG( "zoom", "No focus window, and no visible windows without IG_CONTENT to focus instead\n" ) return view if "IG_CONTENT" in win: return view zoomed_view = item_zoom_1_1_to_window_calc(self, win=win, screen=view) if len(visible_pixelcontent) > 1: bbox = utils.bbox([c for w, c in visible]) InfiniteGlass.DEBUG("zoom", "Visible windows bbox: %s\n" % (bbox, )) if zoomed_view[2] >= bbox[2] and zoomed_view[3] >= bbox[3]: InfiniteGlass.DEBUG( "zoom", "Zoom 1-to-1 to %s @ %s (View still contains all visible windows)\n" % (win, win.get("IG_COORDS"))) view = zoomed_view view[0] = bbox[0] - ((view[2] - bbox[2]) / 2) view[1] = bbox[1] - bbox[3] - ((view[3] - bbox[3]) / 2) else: InfiniteGlass.DEBUG( "zoom", "Zoom 1-to-1 to %s @ %s (Less than 2 visible windows)\n" % (win, win.get("IG_COORDS"))) return zoomed_view InfiniteGlass.DEBUG("zoom", "Zoom-adjusted view: %s\n" % (view, )) # Move the view as long as that makes more windows visible without removing any windows while True: view[0] = bbox[0] - ((view[2] - bbox[2]) / 2) view[1] = bbox[1] - bbox[3] - ((view[3] - bbox[3]) / 2) new_visible, new_overlap, new_invisible = InfiniteGlass.windows.get_windows( self.display, view) visible_set = set(w.__window__() for w, c in visible) new_visible_set = set(w.__window__() for w, c in new_visible) if len(visible_set - new_visible_set) != 0 or len(new_visible_set - visible_set) == 0: return view visible = new_visible bbox = utils.bbox([c for w, c in visible])
def handle_event(display, event): InfiniteGlass.DEBUG("event", "HANDLE %s\n" % event) mode = display.input_stack[-1] if mode.handle(event): InfiniteGlass.DEBUG("event", " BY %s\n" % (mode, )) return True InfiniteGlass.DEBUG("event", " UNHANDLED\n") return False
def send_sleep(self, event): "Make the active application store its state and exit" win = self.get_event_window(event) InfiniteGlass.DEBUG("sleep", "Sleep %s %s\n" % (win, win.get("WM_NAME", None))) if win and win != self.display.root: InfiniteGlass.DEBUG("sleep", "SENDING SLEEP %s\n" % win) win.send(win, "IG_SLEEP", event_mask=Xlib.X.StructureNotifyMask) self.display.flush()
def send_close(self, event): "Close the active window" win = self.get_event_window(event) InfiniteGlass.DEBUG("close", "Close %s %s\n" % (win, win.get("WM_NAME", None))) if win and win != self.display.root: InfiniteGlass.DEBUG("close", "SENDING CLOSE %s\n" % win) win.send(win, "IG_CLOSE", event_mask=Xlib.X.StructureNotifyMask) self.display.flush()
def ClientMessage(win, event): InfiniteGlass.DEBUG("ghost", "GHOST WM_DELETE_WINDOW %s\n" % (self, )) sys.stderr.flush() if event.parse("ATOM")[0] == "WM_DELETE_WINDOW": win.destroy() else: InfiniteGlass.DEBUG( "ghost", "%s: Unknown WM_PROTOCOLS message: %s\n" % (self, event)) sys.stderr.flush()
def toggle_sleep(self, event): win = self.get_event_window(event) if win and win != self.display.root: if win.get("IG_GHOST", None): InfiniteGlass.DEBUG("restart", "SENDING RESTART %s\n" % win) win.send(win, "IG_RESTART", event_mask=Xlib.X.StructureNotifyMask) self.display.flush() else: InfiniteGlass.DEBUG("sleep", "SENDING SLEEP %s\n" % win) win.send(win, "IG_SLEEP", event_mask=Xlib.X.StructureNotifyMask) self.display.flush()
def PropertyNotify(win, event): name = self.manager.display.get_atom_name(event.atom) try: self.properties[name] = win[name] InfiniteGlass.DEBUG("island.property", "%s=%s\n" % (name, self.properties[name])) sys.stderr.flush() except: pass else: self.save_changes() InfiniteGlass.DEBUG("setprop", "%s=%s" % (name, self.properties.get(name)))
def PropertyNotify(win, event): name = self.manager.display.get_atom_name(event.atom) if name not in self.manager.config["ghost_update"]: return try: self.properties.update( glass_ghosts.helpers.expand_property(win, name)) InfiniteGlass.DEBUG("ghost.property", "%s=%s\n" % (name, self.properties[name])) sys.stderr.flush() except: pass else: self.update_key() InfiniteGlass.DEBUG("setprop", "%s=%s" % (name, self.properties.get(name)))
def apply(self, window, type="set"): InfiniteGlass.DEBUG( "ghost", "SHADOW APPLY window_id=%s %s\n" % (window.__window__(), self)) sys.stderr.flush() if self.properties.get("IG_GHOSTS_DISABLED", 0): window["IG_GHOSTS_DISABLED"] = 1 else: for key in self.manager.config[type]: if key in self.properties: InfiniteGlass.DEBUG( "ghost.properties", "%s=%s\n" % (key, str(self.properties[key])[:100])) sys.stderr.flush() window[key] = self.properties[key]
def zoom_to_fewer_windows(self, event, margin=0.01): "Zoom in the screen so that one fewer window is visible" print("ZOOM IN TO FEWER WINDOWS") view = list(self.display.root["IG_VIEW_DESKTOP_VIEW"]) vx = view[0] + view[2] / 2. vy = view[1] + view[3] / 2. windows = [] visible, overlap, invisible = InfiniteGlass.windows.get_windows(self.display, view) for child, coords in visible: d = max( math.sqrt((coords[0] - vx)**2 + (coords[1] - vy)**2), math.sqrt((coords[0] + coords[2] - vx)**2 + (coords[1] - vy)**2), math.sqrt((coords[0] - vx)**2 + (coords[1] - coords[3] - vy)**2), math.sqrt((coords[0] + coords[2] - vx)**2 + (coords[1] - coords[3] - vy)**2),) windows.append((d, coords, child)) if len(windows) <= 1: return zoom.zoom_in(self, event) windows.sort(key=lambda a: a[0]) ratio = view[2] / view[3] def get_view(removed=1): xs = [x for d, window, w in windows[:-removed] for x in (window[0], window[0] + window[2])] ys = [y for d, window, w in windows[:-removed] for y in (window[1], window[1] - window[3])] view = [min(xs), min(ys), max(xs) - min(xs), max(ys) - min(ys)] if view[2] / ratio > view[3]: view[3] = view[2] / ratio else: view[2] = ratio * view[3] return view for i in range(1, len(windows)): new_view = get_view(i) if (new_view[2] * (1 + margin) < view[2]) or (new_view[3] * (1 + margin) < view[3]): adjusted_view = item_zoom_to.adjust_view(self, new_view, windows[-i-1][2]) if adjusted_view[2] < new_view[2] and adjusted_view[3] < new_view[3]: new_view = adjusted_view print("Removed %s windows to reduce width by %s and height by %s" % (i, view[2] - new_view[2], view[3] - new_view[3])) InfiniteGlass.DEBUG("view", "View %s\n" % (new_view,)) # self.display.root["IG_VIEW_DESKTOP_VIEW"] = new_view self.display.root["IG_VIEW_DESKTOP_VIEW_ANIMATE"] = new_view self.display.animate_window.send(self.display.animate_window, "IG_ANIMATE", self.display.root, "IG_VIEW_DESKTOP_VIEW", .5) return InfiniteGlass.DEBUG("view", "Windows are all overlapping... Not sure what to do...\n")
def island_toggle_sleep(self, event): island = self.get_event_window(event) if island and island != self.display.root: old = island.get("IG_ISLAND_PAUSED", 0) if old == 1: is_paused = 0 else: is_paused = 1 InfiniteGlass.DEBUG("island_toggle_sleep", "%s.IG_ISLAND_PAUSED=%s\n" % (island, is_paused)) island["IG_ISLAND_PAUSED"] = is_paused windows = [win for win, coords in island_windows(self.display, island)] clients_done = set() if is_paused: for win in windows: if "IG_GHOST" not in win: if "SM_CLIENT_ID" not in win or win["SM_CLIENT_ID"] not in clients_done: win.send(win, "IG_SLEEP", event_mask=Xlib.X.StructureNotifyMask) if "SM_CLIENT_ID" in win: clients_done.add(win["SM_CLIENT_ID"]) else: for win in windows: if "IG_GHOST" in win and "SM_CLIENT_ID" in win and win["SM_CLIENT_ID"] not in clients_done: win.send(win, "IG_RESTART", event_mask=Xlib.X.StructureNotifyMask) clients_done.add(win["SM_CLIENT_ID"]) self.display.flush()
def main(): manager = None try: with InfiniteGlass.Display() as display: overlay = display.root.composite_get_overlay_window().overlay_window overlay_geom = overlay.get_geometry() gc = overlay.create_gc( foreground = display.screen().black_pixel, background = display.screen().white_pixel) overlay.rectangle(gc, 0, 0, overlay_geom.width, overlay_geom.height, onerror = None) manager = glass_ghosts.manager.GhostManager(display) sys.stdout.write("%s\n" % manager.session.listen_address()) sys.stdout.flush() InfiniteGlass.DEBUG("init", "Session manager listening to %s\n" % manager.session.listen_address()) manager.components.shutdown() except Exception as e: print("Ghost manager systemic failure, restarting: %s" % (e,)) traceback.print_exc() try: if manager is not None and hasattr(manager, "components") and hasattr(manager.components, "components_by_pid"): for pid in manager.components.components_by_pid.keys(): os.kill(pid, signal.SIGINT) except Exception as e: print(e) traceback.print_exc() os.execlp(sys.argv[0], *sys.argv) print("END")
def ClientMessage(win, event): InfiniteGlass.DEBUG( "message", "RECEIVED DELETE %s %s %s" % (win, event, self.client)) sys.stderr.flush() self.under_deletion = True self.close()
def process(fd): InfiniteGlass.DEBUG("session", "PROCESS %s %s\n" % (fd, conn)) try: conn.IceProcessMessages() except Exception as e: print(e) self.display.mainloop.remove(conn.IceConnectionNumber())
def close_connection(self, *arg, **kw): InfiniteGlass.DEBUG("session", "close_connection %s %s\n" % (arg, kw)) sys.stderr.flush() self.manager.display.mainloop.remove(self.fd) if self.client: self.client.remove_connection(self)
def error_handler(self, swap, offendingMinorOpcode, offendingSequence, errorClass, severity): InfiniteGlass.DEBUG( "error", "Error: %s: swap=%s, offendingMinorOpcode=%s, offendingSequence=%s, errorClass=%s, severity=%s)\n" % (self, swap, offendingMinorOpcode, offendingSequence, errorClass, severity)) self.close_connection()
def accepter(listener): InfiniteGlass.DEBUG( "session", "LISTENING TO %s @ %s\n" % (listener, listener.IceGetListenConnectionNumber())) fd = listener.IceGetListenConnectionNumber() os.set_blocking(fd, False) self.display.mainloop.add( fd, lambda fd: self.accept_connection(listener))
def save_yourself_done(self, *arg, **kw): InfiniteGlass.DEBUG("session", "save_yourself_done %s %s\n" % (arg, kw)) if self.do_sleep: self.SmsDie() sys.stderr.flush()
def animationfn(): while True: current = time.time() progress = (current - start) / timeframe if progress > 1.: InfiniteGlass.DEBUG("final", "ANIMATION FINAL\n") return yield
def send_exit(self, event): "Ends your InfiniteGlass session" InfiniteGlass.DEBUG("debug", "SENDING EXIT\n") self.display.root.send(self.display.root, "IG_GHOSTS_EXIT", event_mask=Xlib.X.StructureNotifyMask | Xlib.X.SubstructureRedirectMask) self.display.flush()
def send_debug(self, event): "Make glass-renderer print its state to stdout" InfiniteGlass.DEBUG("debug", "SENDING DEBUG\n") self.display.root.send(self.display.root, "IG_DEBUG", event_mask=Xlib.X.StructureNotifyMask | Xlib.X.SubstructureRedirectMask) self.display.flush()
def focus_to_window_to_the(self, event, direction): view = list(self.display.root["IG_VIEW_DESKTOP_VIEW"]) vx = view[0] + view[2] / 2. vy = view[1] + view[3] / 2. current = self.display.root.get("_NET_ACTIVE_WINDOW", None) visible, overlap, invisible = InfiniteGlass.windows.get_windows( self.display, view) if current is None: if visible: current = visible[0][0] if current: coords = current["IG_COORDS"] cx = coords[0] + coords[2] / 2. cy = coords[1] - coords[3] / 2. else: cx = view[0] + view[2] / 2. cy = view[1] + view[3] / 2. windows = [] for child, coords in visible + invisible + overlap: if child.get("IG_LAYER", "IG_LAYER_DESKTOP") != "IG_LAYER_DESKTOP": continue if current and child.__window__() == current.__window__(): continue x = coords[0] + coords[2] / 2. y = coords[1] - coords[3] / 2. wdist = numpy.sqrt((x - cx)**2 + (y - cy)**2) wdir = angular_diff(numpy.arctan2(y - cy, x - cx), direction) / (numpy.pi / 4) if wdir < 1.0: windows.append((wdist * (1 + wdir), (x, y), coords, child)) if not windows: return windows.sort(key=lambda a: a[0]) dist, center, coords, next_window = windows[0] next_is_visible = next_window.__window__() in [ w.__window__() for w, c in visible ] if not next_is_visible: view = pan.zoom_to_window_calc(self, view, next_window, view_center=(vx, vy), coords=coords, center=center) self.display.root["IG_VIEW_DESKTOP_VIEW_ANIMATE"] = view self.display.animate_window.send(self.display.animate_window, "IG_ANIMATE", self.display.root, "IG_VIEW_DESKTOP_VIEW", .5) set_focus(self, next_window) InfiniteGlass.DEBUG("window", "Window %s\n" % (next_window, ))
def apply(self, window, action_type="set"): InfiniteGlass.DEBUG( "ghost.apply", "SHADOW APPLY %s window_id=%s %s\n" % (action_type, window, self)) sys.stderr.flush() if self.properties.get("IG_GHOSTS_DISABLED", 0): window["IG_GHOSTS_DISABLED"] = 1 else: for key in self.manager.config[action_type]: if key in self.properties: if InfiniteGlass.DEBUG_ENABLED("ghost.apply.properties"): itemtype, items, fmt = InfiniteGlass.parse_value( self.manager.display, self.properties[key]) itemtype = self.manager.display.get_atom_name(itemtype) InfiniteGlass.DEBUG( "ghost.properties", "%s=<%s/%s>%s\n" % (key, itemtype, fmt, str(items)[:100])) sys.stderr.flush() #if key == "__attributes__": # window.change_attributes(**self.properties[key]) if key == "__config__": try: window.configure(**self.properties[key]) except Exception as e: InfiniteGlass.ERROR( "ghost.properties", "Unable to configure %s[%s]\n" % (window, self.properties[key])) else: InfiniteGlass.DEBUG( "ghost.properties", " => %s[%s]\n" % (window.id, self.properties[key])) else: try: window[key] = self.properties[key] except Exception as e: InfiniteGlass.ERROR( "ghost.properties", "Unable to set property %s.%s=%s\n" % (window, key, self.properties[key])) else: InfiniteGlass.DEBUG( "ghost.properties", " => %s=%s\n" % (key, window[key])) self.manager.display.flush()