8 Commits

3 changed files with 123 additions and 109 deletions

View File

@@ -10,18 +10,24 @@ Usage
:: ::
usage: pi3-smart-switch [-h] -i num usage: pi3-smart-workspace [-h] [-d] -i INDEX [-s] [-k]
Openens the [i] number of workspace assigned in the config, on the output the cursor is currently on.
Changes the workspace, based on what output your cursor is on.
requred arguments: optional arguments:
-i, --index the number index of the workspace that should be openend. 1 = first workspace in config etc. -h, --help show this help message and exit
-d, --debug Turn on debug mode.
Current limitations Required:
--------------------
The way this script is set up, it is sending commands in strings. and thus we cannot keep track of each workspace other than by its name. This is a limmitiaion as there is no way for us to know if the workspace "1" is reffering to the workspace 1 assigned to output DS-1 or output HDMI-2..
So in order to differentiate between these, you need to name your workspaces new names for each output. See example configuration under #Installation. -i INDEX, --index INDEX
The indexed workspace for the output where the cursor is currently located
Shift:
manipulate the active window
-s, --shift Moves the active window to the index workspace
-k, --keep-with-it Moves the active window to the index workspace, and moves with it
Installation Installation
@@ -37,98 +43,80 @@ Example config to be inserted into your i3 config.
:: ::
# Displays # Displays
set $primary DP-2 set $primary eDP
set $left HDMI-0 set $top HDMI-A-0
set $right HDMI-1 set $bottom HDMI2
# Workspaces # Workspaces
set $ws1 1:1:Code set $ws1 1:1
set $ws2 2:2:Code ... # And so on
set $ws3 3:3:Code set $ws{n} {n}:{n}
set $ws4 4:4:Code
set $ws5 5:5:Code
set $ws6 6:6:Code
set $ws7 7:7:Code
set $ws8 8:8:Code
set $LeftWs1 1:1:Browser set $TopWs1 {n+1}:1
set $LeftWs2 2:2:Left ... # and so on
set $LeftWs3 3:3:Left set $TopWs{k} {n+1+k}:{k}
set $LeftWs4 4:4:Left
set $LeftWs5 5:5:Left
set $LeftWs6 6:6:Left
set $LeftWs7 7:7:Left
set $LeftWs8 8:8:Left
set $RightWs1 1:1:Right set $BottomWs1 {k+1}:1
set $RightWs2 2:2:Right ... # and so on
set $RightWs3 3:3:Right set $BottomWs{q} {k+1+q}:{q}
set $RightWs4 4:4:Right
set $RightWs5 5:5:Right
set $RightWs6 6:6:Right
set $RightWs7 7:7:Right
set $RightWs8 8:8:Right
# Workspace assignments workspace $ws1 output $primary
workspace $ws1 output $primary ... # and so on
workspace $ws2 output $primary workspace $ws{n} output $primary
workspace $ws3 output $primary
workspace $ws4 output $primary
workspace $ws5 output $primary
workspace $ws6 output $primary
workspace $ws7 output $primary
workspace $ws8 output $primary
workspace $LeftWs1 output $left workspace $TopWs1 output $top
workspace $LeftWs2 output $left ... # and so on
workspace $LeftWs3 output $left workspace $TopWs{k} output $top
workspace $LeftWs4 output $left
workspace $LeftWs5 output $left
workspace $LeftWs6 output $left
workspace $LeftWs7 output $left
workspace $LeftWs8 output $left
workspace $RightWs1 output $right workspace $BottomWs1 output $bottom
workspace $RightWs2 output $right ... # and so on
workspace $RightWs3 output $right workspace $BottomWs{q} output $bottom
workspace $RightWs4 output $right
workspace $RightWs5 output $right
workspace $RightWs6 output $right
workspace $RightWs7 output $right
workspace $RightWs8 output $right
# Shift workspace # Shift workspace
bindsym $mod+1 exec pi3-smart-workspace -i 1 bindsym $mod+1 exec --no-startup-id pi3-smart-workspace -i 1
bindsym $mod+2 exec pi3-smart-workspace -i 2 bindsym $mod+2 exec --no-startup-id pi3-smart-workspace -i 2
bindsym $mod+3 exec pi3-smart-workspace -i 3 bindsym $mod+3 exec --no-startup-id pi3-smart-workspace -i 3
bindsym $mod+4 exec pi3-smart-workspace -i 4 bindsym $mod+4 exec --no-startup-id pi3-smart-workspace -i 4
bindsym $mod+5 exec pi3-smart-workspace -i 5 bindsym $mod+5 exec --no-startup-id pi3-smart-workspace -i 5
bindsym $mod+6 exec pi3-smart-workspace -i 6 bindsym $mod+6 exec --no-startup-id pi3-smart-workspace -i 6
bindsym $mod+7 exec pi3-smart-workspace -i 7 bindsym $mod+7 exec --no-startup-id pi3-smart-workspace -i 7
bindsym $mod+8 exec pi3-smart-workspace -i 8 bindsym $mod+8 exec --no-startup-id pi3-smart-workspace -i 8
# Move focused container to workspace # Move focused container to workspace
bindsym $mod+Ctrl+1 exec pi3-smart-workspace -i 1 -s bindsym $mod+Shift+1 exec --no-startup-id pi3-smart-workspace -i 1 -s
bindsym $mod+Ctrl+2 exec pi3-smart-workspace -i 2 -s bindsym $mod+Shift+2 exec --no-startup-id pi3-smart-workspace -i 2 -s
bindsym $mod+Ctrl+3 exec pi3-smart-workspace -i 3 -s bindsym $mod+Shift+3 exec --no-startup-id pi3-smart-workspace -i 3 -s
bindsym $mod+Ctrl+4 exec pi3-smart-workspace -i 4 -s bindsym $mod+Shift+4 exec --no-startup-id pi3-smart-workspace -i 4 -s
bindsym $mod+Ctrl+5 exec pi3-smart-workspace -i 5 -s bindsym $mod+Shift+5 exec --no-startup-id pi3-smart-workspace -i 5 -s
bindsym $mod+Ctrl+6 exec pi3-smart-workspace -i 6 -s bindsym $mod+Shift+6 exec --no-startup-id pi3-smart-workspace -i 6 -s
bindsym $mod+Ctrl+7 exec pi3-smart-workspace -i 7 -s bindsym $mod+Shift+7 exec --no-startup-id pi3-smart-workspace -i 7 -s
bindsym $mod+Ctrl+8 exec pi3-smart-workspace -i 8 -s bindsym $mod+Shift+8 exec --no-startup-id pi3-smart-workspace -i 8 -s
# Move to workspace with focused container # Move to workspace with focused container
bindsym $mod+Shift+1 exec pi3-smart-workspace -i 1 -sk bindsym $mod+Ctrl+1 exec --no-startup-id pi3-smart-workspace -i 1 -sk
bindsym $mod+Shift+2 exec pi3-smart-workspace -i 2 -sk bindsym $mod+Ctrl+2 exec --no-startup-id pi3-smart-workspace -i 2 -sk
bindsym $mod+Shift+3 exec pi3-smart-workspace -i 3 -sk bindsym $mod+Ctrl+3 exec --no-startup-id pi3-smart-workspace -i 3 -sk
bindsym $mod+Shift+4 exec pi3-smart-workspace -i 4 -sk bindsym $mod+Ctrl+4 exec --no-startup-id pi3-smart-workspace -i 4 -sk
bindsym $mod+Shift+5 exec pi3-smart-workspace -i 5 -sk bindsym $mod+Ctrl+5 exec --no-startup-id pi3-smart-workspace -i 5 -sk
bindsym $mod+Shift+6 exec pi3-smart-workspace -i 6 -sk bindsym $mod+Ctrl+6 exec --no-startup-id pi3-smart-workspace -i 6 -sk
bindsym $mod+Shift+7 exec pi3-smart-workspace -i 7 -sk bindsym $mod+Ctrl+7 exec --no-startup-id pi3-smart-workspace -i 7 -sk
bindsym $mod+Shift+8 exec pi3-smart-workspace -i 8 -sk bindsym $mod+Ctrl+8 exec --no-startup-id pi3-smart-workspace -i 8 -sk
Future work
-----------
Here a few ideas on how to improve pi3-smart-workspace could be improved in the future.
If anyone wants to submit a pr that solves one of the problems stated below feel free to do so :)
- Save the outputs and the mapped outputs in a json file, instead of looking through the config every time a button is pressed.
This would greatly reduce the cost of running this program, if we could just look up the required value in the json instead of the whole i3 config.
In order for this to be a thing, we need to transition away from looking at active display, have the user set a exec_always and out indexer in their i3 config.
-
Credits Credits
------- -------

View File

@@ -4,6 +4,17 @@ import pynput
import re import re
import argparse import argparse
import pprint import pprint
import subprocess
def get_active_outputs():
ps = subprocess.Popen(('xrandr', '--listmonitors'), stdout=subprocess.PIPE)
output = subprocess.check_output(('awk', "{print $4}"), stdin=ps.stdout)
ps.wait()
return [x for x in output.decode('UTF-8').split('\n') if x is not None and x != '']
names_for_outputs = r'(' + '|'.join(get_active_outputs()) + ')'
class WorkSpacer: class WorkSpacer:
@@ -20,32 +31,44 @@ class WorkSpacer:
self.current_output_name = None self.current_output_name = None
def _connect(self): def _connect(self):
self.print_if_debug('All available outputs on device')
self.print_if_debug(names_for_outputs)
try: try:
self.i3 = Connection() self.i3 = Connection()
self.config = self.i3.get_config().__dict__['config'] self.config = self.i3.get_config().__dict__['config']
config_outputs = {} config_outputs = {}
for matchNo, match in enumerate( for matchNo, match in enumerate(
re.finditer(r'set (\$[a-zA-Z]+) ((HDMI|DP|VGA|eDP)(-|)\d)', self.config, re.MULTILINE), start=1 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) 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 = {} config_workspace_names = {}
for matchNum, match in enumerate( for matchNum, match in enumerate(
re.finditer(r'set (\$.*) (\d.*)', self.config, re.MULTILINE) re.finditer(r'set (\$.*) (\d.*)', self.config, re.MULTILINE)
): ):
config_workspace_names[match.group(1)] = match.group(2) 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( for matchNum, match in enumerate(
re.finditer(r'workspace (\$.*) output (\$.*)', self.config, re.MULTILINE) 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)]): 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)]] = []
self.workspaces_on_outputs[config_outputs[match.group(2)]].append(config_workspace_names[match.group(1)]) 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("All workspaces with outputs")
self.print_if_debug(self.workspaces_on_outputs) self.print_if_debug(self.workspaces_on_outputs)
except Exception as exc: except Exception as exc:
self.print_if_debug(exc) if self.args.debug:
raise exc
sys.exit(1) sys.exit(1)
self.workspaces = [workspaces for workspaces in self.i3.get_workspaces()] self.workspaces = [workspaces for workspaces in self.i3.get_workspaces()]
outputs = self.i3.get_outputs() outputs = self.i3.get_outputs()
self.outputs = [output for output in outputs if output.__dict__["active"] is True] self.outputs = [output for output in outputs if output.__dict__["active"] is True]
@@ -55,7 +78,8 @@ class WorkSpacer:
self.current_output_name = self._get_workspace_from_courser_position() self.current_output_name = self._get_workspace_from_courser_position()
if self.args.shift: if self.args.shift:
self.i3.command(f'move container to workspace {self.workspaces_on_outputs[self.current_output_name][self.args.index - 1]}') 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: if not self.args.keep_with_it:
return return
self.i3.command(f'workspace {self.workspaces_on_outputs[self.current_output_name][self.args.index - 1]}') self.i3.command(f'workspace {self.workspaces_on_outputs[self.current_output_name][self.args.index - 1]}')
@@ -68,16 +92,20 @@ class WorkSpacer:
y_offset = output.__dict__["rect"].__dict__["y"] y_offset = output.__dict__["rect"].__dict__["y"]
if x_offset == 0 and y_offset == 0: 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: if x_offset <= self.mouse_position[0] <= x_offset + width and y_offset <= \
self.mouse_position[1] <= y_offset + height:
return output.__dict__["name"] return output.__dict__["name"]
elif x_offset == 0: elif x_offset == 0:
if x_offset <= self.mouse_position[0] <= x_offset + width and y_offset < self.mouse_position[1] <= y_offset + height: if x_offset <= self.mouse_position[0] <= x_offset + width and y_offset < \
self.mouse_position[1] <= y_offset + height:
return output.__dict__["name"] return output.__dict__["name"]
elif y_offset == 0: elif y_offset == 0:
if x_offset < self.mouse_position[0] <= x_offset + width and y_offset <= self.mouse_position[1] <= y_offset + height: if x_offset < self.mouse_position[0] <= x_offset + width and y_offset <= \
self.mouse_position[1] <= y_offset + height:
return output.__dict__["name"] return output.__dict__["name"]
else: else:
if x_offset < self.mouse_position[0] <= x_offset + width and y_offset < self.mouse_position[1] <= y_offset + height: if x_offset < self.mouse_position[0] <= x_offset + width and y_offset < \
self.mouse_position[1] <= y_offset + height:
return output.__dict__["name"] return output.__dict__["name"]
def _get_workspaces_for_output(self, output): def _get_workspaces_for_output(self, output):
@@ -90,25 +118,23 @@ class WorkSpacer:
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Dynamic changes the workspace, based on what output your cursoer is on." description="Changes the workspace, based on what output your cursor is on."
) )
parser.add_argument('-d', '--debug', action='store_true', parser.add_argument('-d', '--debug', action='store_true',
help='Turn on debug mode.') help='Turn on debug mode.')
required_group = parser.add_argument_group('Required', '') required_group = parser.add_argument_group('Required', '')
required_group.add_argument("-i", "--index", type=int, required=True, required_group.add_argument("-i", "--index", type=int, required=True,
help="the number index of the workspace that should be openend. 1 = first workspace in config etc.") help="The indexed workspace for the output where the cursor is currently located")
shift_group = parser.add_argument_group('Shift', 'manipulate the active window') shift_group = parser.add_argument_group('Shift', 'manipulate the active window')
shift_group.add_argument("-s", "--shift", action='store_true', shift_group.add_argument("-s", "--shift", action='store_true',
help="if present, moves the current active window to target workspace") help="Moves the active window to the index workspace")
shift_group.add_argument('-k', '--keep-with-it', action='store_true', shift_group.add_argument('-k', '--keep-with-it', action='store_true',
help='if present, moves with the ') help='Moves the active window to the index workspace, and moves with it')
pprint.pprint(parser.parse_args().__dict__) # pprint.pprint(parser.parse_args().__dict__)
WorkSpacer(parser.parse_args()).run() WorkSpacer(parser.parse_args()).run()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@@ -4,7 +4,7 @@ with open('README.rst', 'r') as fh:
setup( setup(
name='pi3-smart-workspace', name='pi3-smart-workspace',
version='0.1.4', version='0.1.20',
packages=['pi3'], packages=['pi3'],
url='https://github.com/GeneralDenmark/PyOutputHandler', url='https://github.com/GeneralDenmark/PyOutputHandler',
license='Apache-2.0 License ', license='Apache-2.0 License ',
@@ -23,7 +23,7 @@ setup(
author_email='asger@weirsoe.dk', author_email='asger@weirsoe.dk',
description='Simple program that looks through the i3 config and finds the bound workspaces for each output, and then opening that workspace on the output, that the mouse is currently on.', description='Simple program that looks through the i3 config and finds the bound workspaces for each output, and then opening that workspace on the output, that the mouse is currently on.',
classifiers=[ classifiers=[
"Development Status :: 4 - Alpha", "Development Status :: 3 - Alpha",
"License :: OSI Approved :: Apache Software License", "License :: OSI Approved :: Apache Software License",
"Operating System :: POSIX :: Linux", "Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",