Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce78dd2e7c | ||
|
|
2f455a6a5e | ||
|
|
57fe74e4d1 | ||
|
|
46b6c1bd56 | ||
|
|
230243cfec | ||
|
|
4381c24a82 | ||
|
|
57b3328ebe | ||
|
|
280436f050 | ||
|
|
7b7d371332 | ||
|
|
0a44792155 | ||
|
|
2659ec1d9e | ||
|
|
d6b8aa7dce | ||
|
|
4ae10eb8dd | ||
|
|
f4ec4385b2 | ||
|
|
184832d805 | ||
|
|
d6015c5576 | ||
|
|
4646f1c230 | ||
|
|
34bfc4dea3 | ||
|
|
71f066f775 | ||
|
|
a056463fec | ||
|
|
1bd2a3bb7b | ||
|
|
85ec3cd1d2 | ||
|
|
6dc86952db | ||
|
|
8715e681ab | ||
|
|
39f384e64b | ||
|
|
f848e2f746 |
31
.github/workflows/python-publish.yml
vendored
Normal file
31
.github/workflows/python-publish.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# This workflows will upload a Python Package using Twine when a release is created
|
||||||
|
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
|
||||||
|
|
||||||
|
name: Upload Python Package
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install setuptools wheel twine
|
||||||
|
- name: Build and publish
|
||||||
|
env:
|
||||||
|
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
|
||||||
|
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
python setup.py sdist bdist_wheel
|
||||||
|
twine upload --repository pypi dist/*
|
||||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
**__pycache__/
|
||||||
|
.idea
|
||||||
|
**.egg-info/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
.pypirc
|
||||||
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
1
.idea/.name
generated
1
.idea/.name
generated
@@ -1 +0,0 @@
|
|||||||
pi3-smart-workspace
|
|
||||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
@@ -1,5 +0,0 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<state>
|
|
||||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
|
||||||
</state>
|
|
||||||
</component>
|
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
6
.idea/inspectionProfiles/profiles_settings.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
4
.idea/misc.xml
generated
4
.idea/misc.xml
generated
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (PyOutputHandler)" project-jdk-type="Python SDK" />
|
|
||||||
</project>
|
|
||||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/../pi3-smart-workspace/.idea/pi3-smart-workspace.iml" filepath="$PROJECT_DIR$/../pi3-smart-workspace/.idea/pi3-smart-workspace.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
11
.idea/pi3-smart-workspace.iml
generated
11
.idea/pi3-smart-workspace.iml
generated
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="PYTHON_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$" />
|
|
||||||
<orderEntry type="jdk" jdkName="Python 3.8 (PyOutputHandler)" jdkType="Python SDK" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
<component name="TestRunnerService">
|
|
||||||
<option name="PROJECT_TEST_RUNNER" value="Twisted Trial" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
124
README.rst
124
README.rst
@@ -10,55 +10,119 @@ Usage
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
usage: pi3-smar-switch [-h] [-f] [-p | -m | -s] WORKSPACE_NAME
|
usage: pi3-smart-workspace [-h] [-d] -i INDEX [-s] [-k]
|
||||||
|
|
||||||
Moves selected i3 workspace to the current output (by default determined by
|
Changes the workspace, based on what output your cursor is on.
|
||||||
cursor location) and focuses it.
|
|
||||||
|
|
||||||
positional arguments:
|
optional arguments:
|
||||||
workspace name of the i3 workspace
|
-h, --help show this help message and exit
|
||||||
|
-d, --debug Turn on debug mode.
|
||||||
|
|
||||||
|
Required:
|
||||||
|
|
||||||
|
-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
|
||||||
|
|
||||||
optional arguments:
|
|
||||||
-h, --help show this help message and exit
|
|
||||||
-f, --focus use focused window instead of cursor position to determine the
|
|
||||||
current output
|
|
||||||
-p, --push moves replaced workspace to the second output (works only if
|
|
||||||
there are two outputs, ignored otherwise)
|
|
||||||
-m, --master same as 'push' but will only move from primary output to the
|
|
||||||
secondary
|
|
||||||
-s, --swap (NOT IMPLEMENTED YET) behaves like xmonad, swaps workspaces if
|
|
||||||
they are on a different output
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Install using `pipsi`_ (recommended) or pip:
|
Install using pip (recommended):
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pipsi install pi3-switch
|
pip install pi3-smart-workspace
|
||||||
|
|
||||||
Add keybindings to ~/.config/i3/config and reload i3 (remember to modify flags to your liking):
|
Example config to be inserted into your i3 config.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
bindsym $mod+1 exec pi3-switch -p 1
|
# Displays
|
||||||
bindsym $mod+2 exec pi3-switch -p 2
|
set $primary eDP
|
||||||
bindsym $mod+3 exec pi3-switch -p 3
|
set $top HDMI-A-0
|
||||||
bindsym $mod+4 exec pi3-switch -p 4
|
set $bottom HDMI2
|
||||||
bindsym $mod+5 exec pi3-switch -p 5
|
|
||||||
bindsym $mod+6 exec pi3-switch -p 6
|
# Workspaces
|
||||||
bindsym $mod+7 exec pi3-switch -p 7
|
set $ws1 1:1
|
||||||
bindsym $mod+8 exec pi3-switch -p 8
|
... # And so on
|
||||||
bindsym $mod+9 exec pi3-switch -p 9
|
set $ws{n} {n}:{n}
|
||||||
bindsym $mod+0 exec pi3-switch -p 10
|
|
||||||
|
set $TopWs1 {n+1}:1
|
||||||
|
... # and so on
|
||||||
|
set $TopWs{k} {n+1+k}:{k}
|
||||||
|
|
||||||
|
set $BottomWs1 {k+1}:1
|
||||||
|
... # and so on
|
||||||
|
set $BottomWs{q} {k+1+q}:{q}
|
||||||
|
|
||||||
|
workspace $ws1 output $primary
|
||||||
|
... # and so on
|
||||||
|
workspace $ws{n} output $primary
|
||||||
|
|
||||||
|
workspace $TopWs1 output $top
|
||||||
|
... # and so on
|
||||||
|
workspace $TopWs{k} output $top
|
||||||
|
|
||||||
|
workspace $BottomWs1 output $bottom
|
||||||
|
... # and so on
|
||||||
|
workspace $BottomWs{q} output $bottom
|
||||||
|
|
||||||
|
# Shift workspace
|
||||||
|
bindsym $mod+1 exec --no-startup-id pi3-smart-workspace -i 1
|
||||||
|
bindsym $mod+2 exec --no-startup-id pi3-smart-workspace -i 2
|
||||||
|
bindsym $mod+3 exec --no-startup-id pi3-smart-workspace -i 3
|
||||||
|
bindsym $mod+4 exec --no-startup-id pi3-smart-workspace -i 4
|
||||||
|
bindsym $mod+5 exec --no-startup-id pi3-smart-workspace -i 5
|
||||||
|
bindsym $mod+6 exec --no-startup-id pi3-smart-workspace -i 6
|
||||||
|
bindsym $mod+7 exec --no-startup-id pi3-smart-workspace -i 7
|
||||||
|
bindsym $mod+8 exec --no-startup-id pi3-smart-workspace -i 8
|
||||||
|
|
||||||
|
# Move focused container to workspace
|
||||||
|
bindsym $mod+Shift+1 exec --no-startup-id pi3-smart-workspace -i 1 -s
|
||||||
|
bindsym $mod+Shift+2 exec --no-startup-id pi3-smart-workspace -i 2 -s
|
||||||
|
bindsym $mod+Shift+3 exec --no-startup-id pi3-smart-workspace -i 3 -s
|
||||||
|
bindsym $mod+Shift+4 exec --no-startup-id pi3-smart-workspace -i 4 -s
|
||||||
|
bindsym $mod+Shift+5 exec --no-startup-id pi3-smart-workspace -i 5 -s
|
||||||
|
bindsym $mod+Shift+6 exec --no-startup-id pi3-smart-workspace -i 6 -s
|
||||||
|
bindsym $mod+Shift+7 exec --no-startup-id pi3-smart-workspace -i 7 -s
|
||||||
|
bindsym $mod+Shift+8 exec --no-startup-id pi3-smart-workspace -i 8 -s
|
||||||
|
|
||||||
|
# Move to workspace with focused container
|
||||||
|
bindsym $mod+Ctrl+1 exec --no-startup-id pi3-smart-workspace -i 1 -sk
|
||||||
|
bindsym $mod+Ctrl+2 exec --no-startup-id pi3-smart-workspace -i 2 -sk
|
||||||
|
bindsym $mod+Ctrl+3 exec --no-startup-id pi3-smart-workspace -i 3 -sk
|
||||||
|
bindsym $mod+Ctrl+4 exec --no-startup-id pi3-smart-workspace -i 4 -sk
|
||||||
|
bindsym $mod+Ctrl+5 exec --no-startup-id pi3-smart-workspace -i 5 -sk
|
||||||
|
bindsym $mod+Ctrl+6 exec --no-startup-id pi3-smart-workspace -i 6 -sk
|
||||||
|
bindsym $mod+Ctrl+7 exec --no-startup-id pi3-smart-workspace -i 7 -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
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Thanks to Travis Finkenauer for an inspiration (`i3-wk-switch`_) and
|
Thanks to Michał Wieluński for an inspiration (`pi3-switch`_) and
|
||||||
Tony Crisci for an easy-to-use i3 python library (`i3ipc-python`_).
|
Tony Crisci for an easy-to-use i3 python library (`i3ipc-python`_).
|
||||||
|
|
||||||
.. _pipsi: https://github.com/mitsuhiko/pipsi
|
.. _pipsi: https://github.com/mitsuhiko/pipsi
|
||||||
.. _i3-wk-switch: https://github.com/tmfink/i3-wk-switch
|
.. _pi3-switch: https://github.com/landmaj/pi3-switch
|
||||||
.. _i3ipc-python: https://github.com/acrisci/i3ipc-python
|
.. _i3ipc-python: https://github.com/acrisci/i3ipc-python
|
||||||
|
|||||||
32
build.py
Normal file
32
build.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
import pathlib
|
||||||
|
import os
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser("Build script for pypi and pypi test")
|
||||||
|
|
||||||
|
group = parser.add_mutually_exclusive_group(required=True)
|
||||||
|
|
||||||
|
group.add_argument('--test', action='store_true', help='Build to test.pypi.org')
|
||||||
|
group.add_argument('--pypi', action='store_true', help='Build to pypi.org')
|
||||||
|
group.add_argument('--check', action='store_true', help='Displays the twine check for dist')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
for path in pathlib.Path('dist').iterdir():
|
||||||
|
os.remove(path)
|
||||||
|
|
||||||
|
subprocess.call(['python3', 'setup.py', 'sdist', 'bdist_wheel'], stdout=subprocess.PIPE)
|
||||||
|
|
||||||
|
if args.test:
|
||||||
|
subprocess.call(['twine', 'upload', '--config-file', '.pypirc', '--repository', 'testpypi', 'dist/*'])
|
||||||
|
|
||||||
|
elif args.pypi:
|
||||||
|
subprocess.call(['twine', 'upload', '--config-file', '.pypirc', '--repository', 'pypi', 'dist/*'])
|
||||||
|
|
||||||
|
else:
|
||||||
|
subprocess.call(['twine', 'check', 'dist/*'])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
from i3ipc import Connection
|
|
||||||
import sys
|
|
||||||
from pprint import pprint
|
|
||||||
import pynput
|
|
||||||
import re
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
|
|
||||||
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):
|
|
||||||
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-Z]+) ((HDMI|DP|VGA)-\d)', self.config, re.MULTILINE), start=1
|
|
||||||
):
|
|
||||||
config_outputs[match.group(1)] = match.group(2)
|
|
||||||
pprint(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)
|
|
||||||
pprint(config_workspace_names)
|
|
||||||
for matchNum, match in enumerate(
|
|
||||||
re.finditer(r'workspace (\$.*) output (\$.*)', self.config, re.MULTILINE)
|
|
||||||
):
|
|
||||||
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)])
|
|
||||||
|
|
||||||
except Exception as 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()
|
|
||||||
self.i3.command(f'workspace {self.workspaces_on_outputs[self.current_output_name][self.args]}')
|
|
||||||
|
|
||||||
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 main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="Dynamic changes the workspace, based on what output your cursoer is on."
|
|
||||||
)
|
|
||||||
parser.add_argument("-i", "--index", type=int,
|
|
||||||
help="the number index of the workspace that should be openend. 1 = workspace 1 etc.")
|
|
||||||
ws = WorkSpacer(parser.parse_args())
|
|
||||||
ws.run()
|
|
||||||
|
|
||||||
|
|
||||||
# Press the green button in the gutter to run the script.
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
||||||
|
|
||||||
140
pi3/smart_workspace.py
Normal file
140
pi3/smart_workspace.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
from i3ipc import Connection
|
||||||
|
import sys
|
||||||
|
import pynput
|
||||||
|
import re
|
||||||
|
import argparse
|
||||||
|
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:
|
||||||
|
|
||||||
|
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-Z]+) (' +
|
||||||
|
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 main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Changes the workspace, based on what output your cursor is on."
|
||||||
|
)
|
||||||
|
parser.add_argument('-d', '--debug', action='store_true',
|
||||||
|
help='Turn on debug mode.')
|
||||||
|
|
||||||
|
required_group = parser.add_argument_group('Required', '')
|
||||||
|
required_group.add_argument("-i", "--index", type=int, required=True,
|
||||||
|
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.add_argument("-s", "--shift", action='store_true',
|
||||||
|
help="Moves the active window to the index workspace")
|
||||||
|
shift_group.add_argument('-k', '--keep-with-it', action='store_true',
|
||||||
|
help='Moves the active window to the index workspace, and moves with it')
|
||||||
|
# pprint.pprint(parser.parse_args().__dict__)
|
||||||
|
WorkSpacer(parser.parse_args()).run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
38
setup.py
38
setup.py
@@ -1,29 +1,29 @@
|
|||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
with open('README.rst', 'r') as fh:
|
with open('README.rst', 'r') as fh:
|
||||||
long_description = fh.read()
|
long_description = fh.read()
|
||||||
with open('requirements.txt', 'r') as file:
|
|
||||||
required = []
|
|
||||||
for x in file.readline():
|
|
||||||
required.append(x)
|
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="pi3-smart-workspace",
|
name='pi3-smart-workspace',
|
||||||
description="A smart switcher for multiple workspaces.",
|
version='0.1.19',
|
||||||
|
packages=['pi3'],
|
||||||
|
url='https://github.com/GeneralDenmark/PyOutputHandler',
|
||||||
|
license='Apache-2.0 License ',
|
||||||
|
install_requires=[
|
||||||
|
"evdev==1.3.0",
|
||||||
|
"i3ipc==2.2.1",
|
||||||
|
"pynput==1.7.1",
|
||||||
|
"python-xlib==0.27",
|
||||||
|
"six==1.15.0"
|
||||||
|
],
|
||||||
|
entry_points={"console_scripts": ["pi3-smart-workspace=pi3.smart_workspace:main"]},
|
||||||
|
scripts=["pi3/smart_workspace.py"],
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
version="0.0.1",
|
long_description_content_type='text/x-rst',
|
||||||
license="Apache License",
|
author='Asger Geel Weirsøe',
|
||||||
author="Asger Geel Weirsøe",
|
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.',
|
||||||
url="https://github.com/GeneralDenmark/PyOutputHandler",
|
|
||||||
install_requires=required,
|
|
||||||
packages=["pi3"],
|
|
||||||
zip_safe=True,
|
|
||||||
entry_points={"console_scripts": ["pi3-smart-workspace = pi3.smart-workspace:main"]},
|
|
||||||
scripts=["pi3/smart-workspace.py"],
|
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Development Status :: 2 - Pre-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",
|
||||||
|
|||||||
Reference in New Issue
Block a user