def reapply_rule(win_class, geometry): 'reapply the rule to make dropdown wins floating and sticky' flags = ('state=floating layer=above ' f'private=on sticky=on rectangle={geometry}') new_rule = f'bspc rule -a {win_class} {flags}' fmt_rule = f'{win_class}:*:* => {flags}' if fmt_rule not in cmd_output('bspc rule -l').splitlines(): cmd_run(new_rule)
def advance_is_child(pid1, pid2): ps_out = [ line.split(maxsplit=3) for line in cmd_output(f'ps --ppid {pid1}').split('\n')[1:] ] print(ps_out) if any(line[-1] == 'tmux: client' for line in ps_out): tmux_shell_pid = cmd_output("tmux list-panes -F '#{pane_pid}'") print('tmux shell') print(tmux_shell_pid, pid2) return is_child(tmux_shell_pid, pid2) elif any(line[-1] == 'ranger' for line in ps_out): print(cmd_output(f'pstree -ls {pid2}').startswith('systemd---sh')) if cmd_output(f'pstree -ls {pid2}').startswith('systemd---sh'): return True return False else: return is_child(pid1, pid2)
def tab_all(): all_wids = cmd_output( "bspc query -N -d -n '.window.!floating.!hidden'").split('\n') all_wids = [wid for wid in all_wids if 'tabbed' not in get_class(wid)] if all_wids: tabbed_wid = tab_current_win(all_wids[0]) if len(all_wids) > 1: for wid in all_wids[1:]: add_win_to_tabbed(wid, tabbed_wid)
def create_geometry_str(x, y, w, h): screen_dims = screen_dim(cmd_output('bspc query -M -m --names')) if x <= 1: x *= screen_dims['width'] if y <= 1: y *= screen_dims['height'] if w <= 1: w *= screen_dims['width'] if h <= 1: h *= screen_dims['height'] return f'{w:0.0f}x{h:0.0f}{x:+0.0f}{y:+0.0f}'
def node_crawler(prefix, node, path): if node is None: return if node['client'] is None: node_crawler(prefix, node['firstChild'], path + ['1']) node_crawler(prefix, node['secondChild'], path + ['2']) else: client = node['client'] temp_paths[cmd_output(f'bspc query -N -n {node["id"]}')] = { 'className': client['className'], 'instanceName': client['instanceName'], 'path': prefix + '/'.join(path) }
def swallow(): ''' monitore node_add and node_remove events and store swallowed windows in a dictionary ''' swallowed = {} for event in execute('bspc subscribe node_add node_remove'): try: logging.debug(f'Processing Event: {event}') event = event.split() if not event: continue if event[0] == 'node_add': rotate_flag = False new_wid = event[-1] last_wid = cmd_output( "bspc query -N -d -n 'last.window.!floating.!fullscreen'") if not swallow_cond(new_wid, last_wid): continue new_pid = get_pid(new_wid) last_pid = get_pid(last_wid) if not all([new_pid, last_pid]): continue if advance_is_child(last_pid, new_pid): capture_layout() last_path = temp_paths[last_wid]['path'] print(last_path) new_path = last_path + '/1' print(new_path) cmd_run(f'bspc node {last_wid} --flag hidden=on') cmd_run(f"bspc node {new_wid} --to-node {new_path}") swallowed[new_wid] = last_wid if event[0] == 'node_remove': removed_wid = event[-1] if removed_wid in swallowed.keys(): swallowed_id = swallowed[removed_wid] del swallowed[removed_wid] cmd_run(f'bspc node {swallowed_id} --flag hidden=off') cmd_run(f"bspc node {last_wid} --to-node {last_path}") print(last_path.split(':')[-1]) if not len(last_path.split(':')[-1]) <= 5: if last_path[-3] == '2': cmd_run(f'bspc node {last_path[:-4]} --rotate 270') else: cmd_run(f'bspc node {last_path[:-4]} --rotate 90') cmd_run(f'bspc node --focus {swallowed_id}') except Exception as e: print(e) print(traceback.format_exc()) logging.debug('Error occured in mainloop:' f'\n{e}\n{traceback.format_exc()}')
def advance_is_child_depr(pid1, pid2): # TODO finish this # is this a terminal? ps_out = [ line.split(maxsplit=3) for line in cmd_output(f'ps --ppid {pid1}').split('\n')[1:] ] print(ps_out) ttys = {line[1]: (line[0], line[-1]) for line in ps_out if line[1] != '?'} print(ttys) if ttys: if any('tmux: client' in x[1] for x in ttys.values()): last_tmux_pid = cmd_output("tmux ") tmux_server_pid = cmd_output("pgrep 'tmux: server'") print(tmux_server_pid) ps_out = [ line.split(maxsplit=3) for line in cmd_output( f'ps --ppid {tmux_server_pid}').split('\n')[1:] ] tmux_ttys = { line[1]: (line[0], line[-1]) for line in ps_out if line[1] != '?' } print(ps_out) print(tmux_ttys) for tty in ttys: tty_pid = tmux_ttys.get(tty, '') print(tty_pid) if tty_pid: if is_child(tty_pid[0], pid2): return True else: for tty_pid, _ in ttys.values(): if is_child(tty_pid, pid2): return True return False
def repair_geometry(wid, gmt): current = win_geometry(wid) screen_dims = screen_dim(cmd_output('bspc query -M -m --names')) re_obj = re.match(r'(\d+)x(\d+)([+-]\d+)([+-]\d+)', gmt) w, h, x, y = (float(re_obj.group(i)) for i in range(1, 5)) x = x + screen_dims['width'] if x < 0 else x y = y + screen_dims['height'] if y < 0 else y if w != current['width']: w_chg = w - current['width'] cmd_run(f'bspc node {wid} --resize right {w_chg} 0') if h != current['height']: h_chg = current['height'] - h cmd_run(f'bspc node {wid} --resize top 0 {h_chg}') if x != current['x_pos']: x_chg = x - current['x_pos'] cmd_run(f'bspc node {wid} --move {x_chg} 0') if y != current['y_pos']: y_chg = y - current['y_pos'] cmd_run(f'bspc node {wid} --move 0 {y_chg}')
def swallow(): ''' monitore node_add and node_remove events and store swallowed windows in a dictionary ''' swallowed = {} for event in execute('bspc subscribe node_add node_remove'): try: logging.debug(f'Processing Event: {event}') event = event.split() if not event: continue if event[0] == 'node_add': new_wid = event[-1] last_wid = cmd_output( "bspc query -N -d -n 'last.window.!floating.!fullscreen'") print(new_wid, last_wid) print(swallow_cond(new_wid, last_wid)) if not swallow_cond(new_wid, last_wid): continue new_pid = get_pid(new_wid) last_pid = get_pid(last_wid) print(new_pid, last_pid) print(advance_is_child(last_pid, new_pid)) if not all([new_pid, last_pid]): continue if advance_is_child(last_pid, new_pid): cmd_run(f'bspc node {last_wid} --flag private=on') cmd_run(f'bspc node --swap {last_wid} --follow') cmd_run(f'bspc node {last_wid} --flag hidden=on') cmd_run(f'bspc node {new_wid} --flag private=on') swallowed[new_wid] = last_wid if event[0] == 'node_remove': removed_wid = event[-1] if removed_wid in swallowed.keys(): swallowed_id = swallowed[removed_wid] cmd_run(f'bspc node {swallowed_id} --flag hidden=off') cmd_run(f'bspc node --focus {swallowed_id}') except Exception as e: print(e) print(traceback.format_exc()) logging.debug('Error occured in mainloop:' f'\n{e}\n{traceback.format_exc()}')
#!/usr/bin/env python import time from wmutils.processes import cmd_run, cmd_output from wmutils.utils import is_desk_empty, bspwm_events events = bspwm_events('node_remove') for event in events: if is_desk_empty(cmd_output("bspc query -D -d --names")): try: time.sleep(4) if is_desk_empty(cmd_output("bspc query -D -d --names")): cmd_run('bspc desktop --focus last.occupied') except Exception: pass
#! /usr/bin/env python3 import os import sys import subprocess from wmutils.processes import cmd_run, cmd_output swap_flag = False if len(sys.argv) == 2: swap_flag = sys.argv[1] == 'swap' rofi_theme = os.getenv('ROFI_THEME') hidden_windows = cmd_output( "bspc query -N -d -n '.hidden.local.window.!sticky'") if len(hidden_windows.split('\n')) == 0: sys.exit() elif len(hidden_windows.split('\n')) == 1: selected_window = hidden_windows else: try: wins_titles = '\n'.join([ f'{i}|' + cmd_output('xtitle ' + win_id) for i, win_id in enumerate(hidden_windows.split('\n')) ]) selected_window = cmd_output( f'echo "{wins_titles}" | rofi -dmenu -dpi 0 -theme {rofi_theme}') selected_window = hidden_windows.split('\n')[int( selected_window.split('|')[0])] except subprocess.CalledProcessError: sys.exit()
return False if last_wid == '': return False win_class = get_class(last_wid) curwin_class = get_class(event[-1]) if 'tabbed' in win_class and 'tabbed' not in curwin_class: # weird but children windows ids in a `tabbed` # skip a `0` in window ids so it is 0x... instead of 0x0... # therefore the replace sentence was_tabbed_child = any(wid.lower() in children_ids for wid in (event[-1], event[-1].replace('0x0', '0x'))) if not was_tabbed_child: return True else: return False if __name__ == '__main__': children_ids = [] for event in execute('bspc subscribe node_add node_focus'): event = event.split() if event[0] == 'node_add': last_wid = cmd_output('bspc query -N -d -n last.local.window') if auto_tab_cond(event, last_wid): add_win_to_tabbed(event[-1], last_wid) if event[0] == 'node_focus': node_id = event[-1] if 'tabbed' in get_class(node_id): children_ids = get_tabbed_children(node_id)
#! /usr/bin/env python3 import os import sys import inspect cmd_folder = os.path.realpath( os.path.abspath(os.path.split(inspect.getfile(inspect.currentframe()))[0])) sys.path.append(os.path.join(cmd_folder, 'wmutils')) from wmutils.utils import win_geometry, screen_dim from wmutils.processes import cmd_run, cmd_output if __name__ == '__main__': wid = cmd_output("bspc query -N -d -n 'any.floating.window.!hidden'").strip() if wid == '': sys.exit() win_gt = win_geometry(wid) current_monitor = cmd_output('bspc query -M --names -m focused').strip() scr_dim = screen_dim(current_monitor) at_rightedge = win_gt['x_pos'] + win_gt['width'] == scr_dim['width'] at_leftedge = win_gt['x_pos'] == 0 at_bottom = win_gt['y_pos'] + win_gt['height'] == scr_dim['height'] if (at_bottom and at_rightedge) or (at_bottom and at_leftedge): if at_rightedge: cmd_run(
def rm_win_from_tabbed(wid): root_wid = cmd_output('xwininfo -root').split('\n')[0].split()[3] cmd_run(f'xdotool windowreparent {wid} {root_wid}')
def get_tabbed_children(tabbed_id): out = cmd_output(f'xwininfo -id {tabbed_id} -children') split_wrd = 'child:' if 'child:' in out else 'children:' children = [win.strip().split()[0].lower() for win in out.split(split_wrd)[1].split('\n') if win] return children
def tab_all(): all_wids = cmd_output( "bspc query -N -d -n '.window.!floating.!hidden'").split('\n') all_wids = [wid for wid in all_wids if 'tabbed' not in get_class(wid)] if all_wids: tabbed_wid = tab_current_win(all_wids[0]) if len(all_wids) > 1: for wid in all_wids[1:]: add_win_to_tabbed(wid, tabbed_wid) if __name__ == '__main__': cur_wid = cmd_output( "bspc query -N -d -n 'focused.window.!hidden.!floating'") if cur_wid: if sys.argv[1] == 'create': if 'tabbed' not in get_class(cur_wid): tab_current_win(cur_wid) elif sys.argv[1] == 'join': if 'tabbed' in get_class(sys.argv[2]): add_win_to_tabbed(cur_wid, sys.argv[2]) elif sys.argv[1] == 'remove': rm_win_from_tabbed(get_tabbed_children(cur_wid)[0]) elif sys.argv[1] == 'children': print(get_tabbed_children(cur_wid)) elif sys.argv[1] == 'all': tab_all()
def hideall(): for win in cmd_output( "bspc query -N -d -n '.!hidden.window.!focused'").split('\n'): cmd_run(f'bspc node {win} -g hidden=on')
#!/usr/bin/env python # Python version of the following code # https://github.com/phenax/dotfiles/blob/master/.config/bspwm/scripts/resize.sh import sys from wmutils.utils import is_floating from wmutils.processes import cmd_run, cmd_output dir_ = sys.argv[1] delta = 40 if len(sys.argv) < 3 else sys.argv[2] x = f'+{delta}' if dir_ == 'right' else f'-{delta}' if dir_ == 'left' else '0' y = f'+{delta}' if dir_ == 'down' else f'-{delta}' if dir_ == 'up' else '0' pair_dict = { 'right': 'left', 'left': 'right', 'top': 'bottom', 'bottom': 'top' } DIR_ = 'right' if dir_ in ('left', 'right') else 'top' FALLDIR = pair_dict[DIR_] cmd_run(f'bspc node --resize {DIR_} {x} {y}') if not is_floating(cmd_output('bspc query -N -n')): cmd_run(f'bspc node --resize {FALLDIR} {x} {y}')
def win_exists(win_class): 'Return True (and window id) if window with a matching class is open' for wid in cmd_output('bspc query -N -n .window').split('\n'): if win_class in get_class(wid): return wid return False
cmd_folder = os.path.realpath( os.path.abspath(os.path.split(inspect.getfile(inspect.currentframe()))[0])) sys.path.append(os.path.join(cmd_folder, 'wmutils')) from wmutils.utils import get_class from wmutils.processes import cmd_output, cmd_run def confirm_close_tabbed(cur_wid): rofi_cmd = ('echo -e "yes\nno" | rofi -selected-row 1 -width 20% -dmenu' f' -dpi 0 -theme {os.getenv("ROFI_THEME")} -lines 2 -p "Do you' ' want to close all tabbed windows?"') if 'tabbed' in get_class(cur_wid): if len(get_tabbed_children(cur_wid)) == 1: cmd_run(f'bspc node --{sys.argv[1]}') else: P = cmd_run(rofi_cmd, stdout=subprocess.PIPE) P.wait() answr = P.stdout.read().strip() if answr == 'yes': cmd_run(f'bspc node --{sys.argv[1]}') else: cmd_run(f'bspc node --{sys.argv[1]}') if __name__ == '__main__': cur_wid = cmd_output('bspc query -N -n') if cur_wid and sys.argv[1] in ['kill', 'close']: confirm_close_tabbed(cur_wid)