class LayoutSwitcher: def __init__(self): config_dir = os.getenv("XDG_CONFIG_HOME") or os.getenv("HOME") self.LAYOUT_STATE_FILE = os.path.join(config_dir, "screenlayout.state") self.LAYOUTS_DIR = os.path.join(os.getenv("HOME"), ".screenlayout") self.loadAvailableLayouts(self.LAYOUTS_DIR) self.loadCurrentLayout(self.LAYOUT_STATE_FILE) self.i3control = Connection() def loadAvailableLayouts(self, layoutsDir): self.AVAILABLE_LAYOUTS = [ getLayoutName(l) for l in os.listdir(layoutsDir) ] self.AVAILABLE_LAYOUTS.sort() print(self.AVAILABLE_LAYOUTS) self.NUM_AVAILABLE_LAYOUTS = len(self.AVAILABLE_LAYOUTS) if self.NUM_AVAILABLE_LAYOUTS == 0: raise Exception("No layouts defined in {}".format(layoutsDir)) def loadCurrentLayout(self, stateFile): self.CURRENT_LAYOUT = self.__readCurrentLayoutOrNull(stateFile) def __readCurrentLayoutOrNull(self, stateFile): try: return open(stateFile, "r").readline() except: return self.AVAILABLE_LAYOUTS[0] def __loadLayout(self, layout): layoutFile = os.path.join(self.LAYOUTS_DIR, layout + ".sh") print("loading layout {}".format(layout)) open(self.LAYOUT_STATE_FILE, "w").writelines([layout]) subprocess.run(layoutFile) print( subprocess.run([ "notify-send", "Layout-Switcher", "selected layout: {}".format(layout) ], capture_output=True)) def __loadLayoutByIndex(self, index): self.__loadLayout(self.AVAILABLE_LAYOUTS[index]) def __getCurrentLayoutIndexOrNull(self): try: return self.AVAILABLE_LAYOUTS.index(self.CURRENT_LAYOUT) except: return 0 def cycleLayouts(self): currentLayoutIndex = self.__getCurrentLayoutIndexOrNull() or 0 print("current layout idx: {}".format(currentLayoutIndex)) nextLayoutIndex = (currentLayoutIndex + 1) % self.NUM_AVAILABLE_LAYOUTS print("next layout: {}".format(nextLayoutIndex)) self.__loadLayoutByIndex(nextLayoutIndex) self.i3control.command("restart")
class Checker(): def __init__(self): try: self.ctx = Connection().get_tree().find_focused() except Exception as e: print('Could not connect to i3 ipc') exit(e) def check(self): if args.connect: self.set_title(self.get_warn_title()) if args.disconnect: title = '{}@{}: {}'.format( getuser(), gethostname(), getcwd().replace(path.expanduser("~"), "~")) self.set_title(title) def set_title(self, title): self.ctx.command(f'title_format { title }') def get_warn_title(self): span_start = f'<span size="x-large" foreground="{ args.font_color }">' warn_text = ' PRODUCTION SERVER:' title_text = self.ctx.name.replace('ssh ', '') return f'{ span_start }{ warn_text } { title_text } </span>'
class I3Thread(threading.Thread): def __init__(self, queue): self.queue = queue self.i3 = Connection() super().__init__() def i3_update(self): self.queue.put(self.i3.get_tree().descendants()) def on_new_window(self, _, event): # when bar window becomes visible if event.container.name == TITLE: # make it floating and sticky self.i3.command( f"[title=\"{TITLE}\"] floating enable, sticky enable") # send the containers structure to it self.i3_update() # disable this event handler self.i3.off(self.on_new_window) def run(self): # this event is used to set up the bar window self.i3.on(Event.WINDOW_NEW, self.on_new_window) def update_on(event): self.i3.on(event, (lambda _, __: self.i3_update())) update_on(Event.WORKSPACE) update_on(Event.WINDOW_NEW) update_on(Event.WINDOW_CLOSE) self.i3.main() def switch_to_workspace(self, workspace_index): self.i3.command(f"workspace {workspace_index}")
def main(): name = "1:1:AT_Translated_Set_2_keyboard" sway = Connection() focused = sway.get_tree().find_focused() if (focused.window_class == 'Emacs'): system( 'emacsclient -e "(with-current-buffer (window-buffer) (toggle-input-method))"' ) else: sway.command(f"input {name} xkb_switch_layout next")
def undo_window_renaming(ipc: i3ipc.Connection) -> None: """reset workspace names to original name""" for workspace in ipc.get_tree().workspaces(): name_parts = parse_workspace_name(workspace.name) name_parts["icons"] = None new_name = construct_workspace_name(name_parts) ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name)) ipc.main_quit() sys.exit(0)
class i3focus: def __init__(self): self.i3 = Connection() # Create the Connection object that can be used to send commands and subscribe self.updateWinState() def updateWinState(self): self.winstate = self.i3.get_tree().find_focused().workspace().find_focused().workspace().name # Define a funtion to switch workspaces when eye position changes def on_eye_change(self, ws): if ws == 0: self.i3.command('focus output left') else: self.i3.command('focus output right') self.updateWinState()
def rename_workspaces(ipc: i3ipc.Connection, duplicates: bool) -> None: """scans for windows in all workspaces as renames the workspaces""" for workspace in ipc.get_tree().workspaces(): name_parts = parse_workspace_name(workspace.name) icon_tuple = () for wksp in workspace: if wksp.app_id is not None or wksp.window_class is not None: icon = icon_for_window(wksp) if not duplicates and icon in icon_tuple: continue icon_tuple += (icon, ) name_parts["icons"] = " ".join(icon_tuple) new_name = construct_workspace_name(name_parts) ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name))
def swap_workspaces(): """Swap two workspaces""" direction = sys.argv[1] if len(sys.argv) > 1 else False if not direction in ['left', 'right']: return False _i3 = Connection() # Get workspace objects focused = _i3.get_tree().find_focused().workspace() outputs = [output for output in _i3.get_outputs() if output.active] workspaces = [o.current_workspace for o in outputs][::-1] focused_idx = workspaces.index(focused.name) direction_num = {'left': -1, 'right': 1}[direction] direction_opp = {'left': 'right', 'right': 'left'}[direction] swap_idx = (focused_idx + direction_num) % len(workspaces) _i3.command('workspace ' + workspaces[focused_idx]) _i3.command('move ' + 'workspace to output ' + direction) _i3.command('workspace ' + workspaces[swap_idx]) _i3.command('move ' + 'workspace to output ' + direction_opp) return True
#!/usr/bin/env python # Script to choose between the current scratchpads using rofi ########### REQUIRED ############# # Rofi (obviously) # # sudo pip install i3ipc-python # # sudo pip install python-rofi # ################################## import sys from i3ipc import Connection, Event from rofi import Rofi i3 = Connection() r = Rofi() scratchpads = [] for node in i3.get_tree().scratchpad().floating_nodes: scratchpads.append((node.nodes[0].name, node.nodes[0].window)) selected_index, selected_key = r.select("Choose a scratchpad", [x[0] for x in scratchpads]) if selected_index == -1: sys.exit(1) i3.command('[id='"%s"'] scratchpad show' % scratchpads[selected_index][1])
def i3_setup(): i3 = Connection() i3.command("workspace bar") i3.command("split v") i3.command("exec google-chrome-stable") i3.command("focus name 'gear bar'") i3.command("border none") i3.command("resize shrink width 30 px or 30 ppt")
#!/usr/bin/env python3 from i3ipc import Connection i3 = Connection() scratch = i3.get_tree().scratchpad().leaves() # If there is something in the scratchpad # Lists are true if not empty if scratch: #switch to workspace 42 and making the windows stack i3.command("workspace 42") i3.command("layout stacking") #remove each window from scratchpad and defloat it for window in scratch: window.command("scratchpad show") window.command("floating toggle") else: #make list of used workspaces workspaces = i3.get_workspaces() wsinuse = [] for i in workspaces: wsinuse.append(i.num) #if workspace 42 is already in use, switch to it if 42 in wsinuse: i3.command("workspace 42")
# Print the name of the focused window focused = i3.get_tree().find_focused() print('Focused window %s is on workspace %s' % (focused.name, focused.workspace().name)) # Query the ipc for outputs. The result is a list that represents the parsed # reply of a command like `i3-msg -t get_outputs`. outputs = i3.get_outputs() print('Active outputs:') for output in filter(lambda o: o.active, outputs): print(output.name) # Send a command to be executed synchronously. i3.command('focus left') # Take all fullscreen windows out of fullscreen for container in i3.get_tree().find_fullscreen(): container.command('fullscreen') # Print the names of all the containers in the tree root = i3.get_tree() print(root.name) for con in root: print(con.name) # Define a callback to be called when you switch workspaces. def on_workspace_focus(self, e): # The first parameter is the connection to the ipc and the second is an object
from i3ipc import Connection i3 = Connection() workspace_no = i3.get_tree().find_focused().workspace().num new_workspace_no = 1 if (workspace_no + 1) > 10 else (workspace_no + 1) command = f'move container to workspace number {new_workspace_no}' print(command) i3.command(command) command = f'workspace number "{new_workspace_no}"' print(command) i3.command(command)
#!/usr/bin/env python from i3ipc import Connection conn = Connection() terms = conn.get_tree().find_instanced('gnome-terminal-server') for term in terms: if term.parent.scratchpad_state != 'none': conn.command('scratchpad show') exit(0) conn.command('exec --no-startup-id "gnome-terminal --window --title terminal-dropdown"')
from i3ipc import Connection, Con if __name__ == "__main__": i3 = Connection() def _is_scratchpad(con: Con) -> bool: return con.window and con.parent.type != 'dockarea' and con.workspace( ).name == '__i3_scratch' for con in i3.get_tree(): if _is_scratchpad(con): con.command("fullscreen") break i3.command("scratchpad show")
#!/usr/bin/env python from i3ipc import Connection import re conn = Connection() es = conn.get_tree().find_classed("Evolution") killed = False for e in es: if re.findall("on", e.floating) and re.findall("(K|C)alend(e|a)r – Evolution", e.window_title): e.command('kill') killed = True if not killed: conn.command('exec "evolution -c calendar"')
class WorkSpacer: def __init__(self, args): self.i3 = None self.args = args self.workspaces_on_outputs = {} self.workspaces = None self.outputs = None self.config = None self.mouse = pynput.mouse.Controller() self.mouse_position = None self.current_output_name = None def _connect(self): self.print_if_debug('All available outputs on device') self.print_if_debug(names_for_outputs) try: self.i3 = Connection() self.config = self.i3.get_config().__dict__['config'] config_outputs = {} for matchNo, match in enumerate( re.finditer(r'set (\$[a-zA-Z0-9]+) (' + names_for_outputs + ')', self.config, re.MULTILINE), start=1 ): config_outputs[match.group(1)] = match.group(2) self.print_if_debug('All outputs listed in the config, matched on available configs') self.print_if_debug(config_outputs) config_workspace_names = {} for matchNum, match in enumerate( re.finditer(r'set (\$.*) (\d.*)', self.config, re.MULTILINE) ): config_workspace_names[match.group(1)] = match.group(2) self.print_if_debug('All config_workspaces_names') self.print_if_debug(config_workspace_names) for matchNum, match in enumerate( re.finditer(r'workspace (\$.*) output (\$.*)', self.config, re.MULTILINE) ): if not config_outputs.keys().__contains__(match.group(2)): continue # Not an active display, skip it if not self.workspaces_on_outputs.keys().__contains__(config_outputs[match.group(2)]): self.workspaces_on_outputs[config_outputs[match.group(2)]] = [] self.workspaces_on_outputs[config_outputs[match.group(2)]].append( config_workspace_names[match.group(1)]) self.print_if_debug("All workspaces with outputs") self.print_if_debug(self.workspaces_on_outputs) except Exception as exc: if self.args.debug: raise exc sys.exit(1) self.workspaces = [workspaces for workspaces in self.i3.get_workspaces()] outputs = self.i3.get_outputs() self.outputs = [output for output in outputs if output.__dict__["active"] is True] def run(self): self._connect() self.mouse_position = self.mouse.position self.current_output_name = self._get_workspace_from_courser_position() if self.args.shift: self.i3.command( f'move container to workspace {self.workspaces_on_outputs[self.current_output_name][self.args.index - 1]}') if not self.args.keep_with_it: return self.i3.command(f'workspace {self.workspaces_on_outputs[self.current_output_name][self.args.index - 1]}') def _get_workspace_from_courser_position(self): for output in self.outputs: width = output.__dict__["rect"].__dict__["width"] height = output.__dict__["rect"].__dict__["height"] x_offset = output.__dict__["rect"].__dict__["x"] y_offset = output.__dict__["rect"].__dict__["y"] if x_offset == 0 and y_offset == 0: if x_offset <= self.mouse_position[0] <= x_offset + width and y_offset <= \ self.mouse_position[1] <= y_offset + height: return output.__dict__["name"] elif x_offset == 0: if x_offset <= self.mouse_position[0] <= x_offset + width and y_offset < \ self.mouse_position[1] <= y_offset + height: return output.__dict__["name"] elif y_offset == 0: if x_offset < self.mouse_position[0] <= x_offset + width and y_offset <= \ self.mouse_position[1] <= y_offset + height: return output.__dict__["name"] else: if x_offset < self.mouse_position[0] <= x_offset + width and y_offset < \ self.mouse_position[1] <= y_offset + height: return output.__dict__["name"] def _get_workspaces_for_output(self, output): return [workspace for workspace in self.workspaces if workspace.__dict__['output'] == output] def print_if_debug(self, to_print): if self.args.debug: pprint.pprint(to_print)
def current_focused_workspace(con): return con.get_tree().find_focused().workspace().name def current_workspace(con, outputName): output = first(lambda x: x.name == outputName, con.get_outputs()) if output is None: return None return output.current_workspace def swap_workspaces(con, aOutput, bOutput): a_ws = current_workspace(con, aOutput) b_ws = current_workspace(con, bOutput) print("A workspace: %s\nB workspace: %s" % (a_ws, b_ws)) if a_ws is None or b_ws is None: print("either workspace %s or %s was not found" % (a_ws, b_ws)) return i3.command("focus output %s" % aOutput) i3.command("move workspace to %s" % bOutput) i3.command("focus output %s" % bOutput) i3.command("workspace %s" % b_ws) i3.command("move workspace to %s" % aOutput) current_focused = current_focused_workspace(i3) swap_workspaces(i3, sys.argv[1], sys.argv[2]) i3.command("workspace %s" % current_focused)
print("usage: swap_ws.py [ window# ]") sys.exit(2) return args[1] def ws_exists(num): return i3.get_tree().find_named(num) is not None if __name__ == "__main__": swap_num = check_usage(sys.argv) current_ws = i3.get_tree().find_focused() # current_ws_num = str(get_current_ws()['num']) current_ws_num = str(current_ws.workspace().num) print('current_ws_num', current_ws_num) print('swap_num', swap_num) print('ws_exists(swap_num)', ws_exists(swap_num)) if (current_ws_num == swap_num): print("already on that number") sys.exit(2) if (ws_exists(swap_num)): i3.command(f"rename workspace {swap_num} to {TEMPNAME}") i3.command(f"rename workspace {current_ws_num} to {swap_num}") i3.command(f"rename workspace {TEMPNAME} to {current_ws_num}") else: i3.command(f"rename workspace {current_ws_num} to {swap_num}")
#!/usr/bin/env python3 # # Promotes the focused window by swapping it with the largest window. from i3ipc import Connection, Event def find_biggest_window(container): max_leaf = None max_area = 0 for leaf in container.leaves(): rect = leaf.rect area = rect.width * rect.height if not leaf.focused and area > max_area: max_area = area max_leaf = leaf return max_leaf i3 = Connection() for reply in i3.get_workspaces(): if reply.focused: workspace = i3.get_tree().find_by_id(reply.ipc_data["id"]) master = find_biggest_window(workspace) i3.command("swap container with con_id %s" % master.id)
#!/usr/bin/env python3 from i3ipc import Connection import stash if __name__ == "__main__": i3 = Connection() workspace = i3.get_tree().find_focused().workspace().name if stash.is_minor(workspace): i3.command("workspace %s" % stash.major(workspace)) else: minor = stash.minor(workspace) i3.command("workspace %s" % minor)
#!/usr/bin/env python from i3ipc import Connection import re conn = Connection() htops = conn.get_tree().find_titled("htop-floating") killed = False for e in htops: print(e.window_title, e.floating) if re.findall("on", e.floating) and re.findall("htop-floating", e.window_title): e.command('kill') killed = True if not killed: conn.command( 'exec "gnome-terminal --window --title htop-floating -- htop"')
#!/usr/bin/python3 from i3ipc import Connection, Event import sys i3 = Connection() workspaces = i3.get_workspaces() current_ws = [ws for ws in workspaces if ws.focused][0] ws_on_output = [ws for ws in workspaces if ws.output == current_ws.output] # print((ws_on_output,current_ws)) if sys.argv[1] == "forward": next_num = ws_on_output[(ws_on_output.index(current_ws) + 1) % len(ws_on_output)].num elif sys.argv[1] == "backward": next_num = ws_on_output[ws_on_output.index(current_ws) - 1].num else: print("Error: No valid direction supplied.") next_num = current_ws.num i3.command('workspace number %d' % next_num)
#!/usr/bin/python import sys from i3ipc import Connection, Event i3 = Connection() next_num = next(i for i in range(1, 100) if not [ws for ws in i3.get_workspaces() if int(ws.name) == i]) i3.command(f'workspace number {next_num};')
def _try_ipc(conn: i3ipc.Connection, cmd: str): try: conn.command(cmd) except BrokenPipeError: pass
#!/usr/bin/env python3 from sys import argv from i3ipc import Connection i3 = Connection() ws = i3.get_tree().find_focused().workspace().num ws = ws % 10 + 1 if argv[1] == 'right' else (ws - 2) % 10 + 1 i3.command("workspace {}".format(ws))
def reload_i3(): i3 = Connection() ret_val = i3.command("reload") logger.debug(f"i3ipc reload command output: {ret_val}.")
#!/usr/bin/env python3 from i3ipc import Connection from sys import argv, exit if len(argv) < 2: exit() i3 = Connection() focused = i3.get_tree().find_focused() if focused.type != "workspace": windows = focused.workspace().leaves() windows[-1].command("focus") i3.command("split " + ("horizontal" if len(windows) % 2 else "vertical")) i3.command("exec " + argv[1])
def wifi_rfkill(): """ get wifi state from rfkill """ output = subprocess.check_output(['rfkill', 'list', 'wifi']) r = re.findall("blocked: yes", output.decode('utf-8')) if len(r) == 0: # wifi not blocked return True else: return False conn = Connection() nets = conn.get_tree().find_instanced("gnome-control-center") killed = False for e in nets: if re.findall("on", e.floating): e.command('kill') killed = True if not killed: if wifi_nm(): # show wifi settings conn.command('exec "gnome-control-center wifi') else: # show network settings conn.command('exec "gnome-control-center network')
def load_i3_layout(json_url): from i3ipc import Connection, Event _download(json_url, "/tmp/layout.json") i3 = Connection() i3.command("workspace 1; append_layout /tmp/layout.json")
#!/usr/bin/env python2 from i3ipc import Connection from workspacer import get_current_workspace_num, get_workspace_names from sys import argv import subprocess if __name__ == "__main__": con = Connection() names = get_workspace_names(con) current_num = get_current_workspace_num(con) if current_num in names: current_name = names[current_num] else: current_name = current_num p = subprocess.Popen(["dmenu", "-p","{}:".format(current_name)], stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.stdin.close() newname = p.stdout.read().strip() if newname: con.command("rename workspace to {}:{}".format(current_num,newname)) else: con.command("rename workspace to {}".format(current_num))