Compare commits
25 commits
Author | SHA1 | Date | |
---|---|---|---|
|
51eb844678 | ||
|
2e09a4c4a9 | ||
df47274af9 | |||
c7d9040ad9 | |||
c113b04eaf | |||
89ba780e4a | |||
|
f480b9bf88 | ||
f265731ce5 | |||
fcc799a1d4 | |||
aabaeea9ce | |||
6c80fd7836 | |||
257307fbc1 | |||
c44dea5228 | |||
1a50032339 | |||
2cc964aa07 | |||
|
4edc1a3103 | ||
|
ca14e335fc | ||
|
d82b3d254e | ||
a29a4b8290 | |||
817c016d17 | |||
19eae57536 | |||
a3bd8ad3d0 | |||
dc69905254 | |||
4e3007aed9 | |||
749ddf2eef |
8 changed files with 311 additions and 69 deletions
26
README.md
26
README.md
|
@ -1,10 +1,5 @@
|
||||||
# swayswitch
|
# swayswitch
|
||||||
A simple windowswitcher written in python using wxPython
|
A simple window switcher for the [Sway](https://swaywm.org/) Wayland compositor written in python using wxPython.
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
For Debian/Ubuntu: ```python3-wxgtk4.0```
|
|
||||||
|
|
||||||
For Fedora/RHEL: ```python3-wxpython4```
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -24,8 +19,23 @@ sudo dnf install swayswitch
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
Reload config and open up window switcher with $mod+tab. Move around the switcher using arrow-keys or Tab.
|
Reload config and open up window switcher with Mod4+tab. Move around the switcher using arrow-keys or Tab.
|
||||||
Esc aborts and enter switches window. It is also possible to select window with the mouse. Configuration is installed to /etc/sway/config.d/swayswitch.conf
|
Esc aborts and enter switches window. It is also possible to select window with the mouse.
|
||||||
|
|
||||||
|
Two keybindings work by default, Mod4+f to toggle fullscreen mode, that is if you manage to bring up the switcher while in fullscreen mode you can display the
|
||||||
|
switcher window by exiting fullscreen mode. You can also exit switcher mode by pressing Mod4+q, this is usefull if you manage to get another window on top of
|
||||||
|
the switcher window somehow.
|
||||||
|
|
||||||
|
### Config files
|
||||||
|
SwaySwitch uses two configuration files, one is supplied with the package and is installed to /etc/sway/config.d/swayswitch.conf. It contains default keybindings for Sway.
|
||||||
|
The other one is optionally supplied by the user as $HOME/.local/swayswitch/config in toml format.
|
||||||
|
|
||||||
|
Possible options are:
|
||||||
|
```
|
||||||
|
label_len = 20
|
||||||
|
icon_size = 128
|
||||||
|
```
|
||||||
|
The example above is the default options. ```label_len``` is the total length of the text label above buttons in number of characters and ```icon_size``` is the size of the icons in pixels.
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
Thanks to tobiaspc for the startingpoint for this code: <https://github.com/tobiaspc/wofi-scripts>
|
Thanks to tobiaspc for the startingpoint for this code: <https://github.com/tobiaspc/wofi-scripts>
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
0.0.2
|
0.1.0
|
||||||
|
|
49
debian/changelog
vendored
49
debian/changelog
vendored
|
@ -1,3 +1,52 @@
|
||||||
|
swayswitch (0.1.0) unstable; urgency=low
|
||||||
|
|
||||||
|
* Uses optional config file
|
||||||
|
|
||||||
|
-- Micke Nordin <hej@mic.ke> Tue, 05 Jan 2021 11:09:04 +0100
|
||||||
|
|
||||||
|
|
||||||
|
swayswitch (0.0.8) unstable; urgency=low
|
||||||
|
|
||||||
|
* Use svg icons if possible
|
||||||
|
|
||||||
|
-- Micke Nordin <hej@mic.ke> Mon, 04 Jan 2021 16:20:58 +0100
|
||||||
|
|
||||||
|
|
||||||
|
swayswitch (0.0.7) unstable; urgency=low
|
||||||
|
|
||||||
|
* Better error handling
|
||||||
|
|
||||||
|
-- Micke Nordin <hej@mic.ke> Mon, 04 Jan 2021 14:49:22 +0100
|
||||||
|
|
||||||
|
|
||||||
|
swayswitch (0.0.6) unstable; urgency=low
|
||||||
|
|
||||||
|
* Handle missing icons
|
||||||
|
|
||||||
|
-- Micke Nordin <hej@mic.ke> Mon, 04 Jan 2021 14:22:59 +0100
|
||||||
|
|
||||||
|
|
||||||
|
swayswitch (0.0.5) unstable; urgency=low
|
||||||
|
|
||||||
|
* Add support for icons
|
||||||
|
|
||||||
|
-- Micke Nordin <hej@mic.ke> Mon, 04 Jan 2021 14:00:09 +0100
|
||||||
|
|
||||||
|
|
||||||
|
swayswitch (0.0.4) unstable; urgency=low
|
||||||
|
|
||||||
|
* Add keybinding for exiting switcher mode
|
||||||
|
|
||||||
|
-- Micke Nordin <hej@mic.ke> Mon, 28 Dec 2020 11:49:12 +0100
|
||||||
|
|
||||||
|
|
||||||
|
swayswitch (0.0.3) unstable; urgency=low
|
||||||
|
|
||||||
|
* Don't overwrite conffiles
|
||||||
|
|
||||||
|
-- Micke Nordin <hej@mic.ke> Sun, 27 Dec 2020 16:21:14 +0100
|
||||||
|
|
||||||
|
|
||||||
swayswitch (0.0.2) unstable; urgency=low
|
swayswitch (0.0.2) unstable; urgency=low
|
||||||
|
|
||||||
* Add ability to toggle fullscreen mode when in switcher mode
|
* Add ability to toggle fullscreen mode when in switcher mode
|
||||||
|
|
2
debian/control
vendored
2
debian/control
vendored
|
@ -10,6 +10,6 @@ Package: swayswitch
|
||||||
Section: utils
|
Section: utils
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: python3-wxgtk4.0, sway
|
Depends: python3-cairosvg, python3-toml, python3-wxgtk4.0, sway
|
||||||
Essential: no
|
Essential: no
|
||||||
Description: SwaySwitch is a simple window switcher for Sway
|
Description: SwaySwitch is a simple window switcher for Sway
|
||||||
|
|
|
@ -2,7 +2,7 @@ Buildroot: /home/micke/sources/swayswitch-##VERSION##
|
||||||
Name: swayswitch
|
Name: swayswitch
|
||||||
Version: ##VERSION##
|
Version: ##VERSION##
|
||||||
Release: 1
|
Release: 1
|
||||||
Requires: python3-wxpython4
|
Requires: python3-cairosvg, python3-toml, python3-wxpython4, sway
|
||||||
Summary: SwaySwitch is a simple window switcher for sway
|
Summary: SwaySwitch is a simple window switcher for sway
|
||||||
License: GPLv3+
|
License: GPLv3+
|
||||||
Distribution: Fedora
|
Distribution: Fedora
|
||||||
|
@ -18,6 +18,8 @@ SwaySwitch is a simple window switcher for sway:
|
||||||
<https://swaywm.org>
|
<https://swaywm.org>
|
||||||
|
|
||||||
%files
|
%files
|
||||||
|
%dir "/etc/sway/config.d/"
|
||||||
|
"/etc/sway/config.d/swayswitch.conf"
|
||||||
"/usr/bin/swayswitch"
|
"/usr/bin/swayswitch"
|
||||||
%dir "/usr/share/doc/swayswitch/"
|
%dir "/usr/share/doc/swayswitch/"
|
||||||
"/usr/share/doc/swayswitch/changelog.gz"
|
"/usr/share/doc/swayswitch/changelog.gz"
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 844 KiB After Width: | Height: | Size: 1 MiB |
290
src/swayswitch
290
src/swayswitch
|
@ -8,8 +8,12 @@
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
import cairosvg
|
||||||
|
import toml
|
||||||
import wx
|
import wx
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,84 +25,214 @@ class SwaySwitch(wx.Frame): # pylint: disable=no-member
|
||||||
# create a panel in the frame
|
# create a panel in the frame
|
||||||
self.pnl = wx.Panel(self) # pylint: disable=no-member
|
self.pnl = wx.Panel(self) # pylint: disable=no-member
|
||||||
|
|
||||||
|
# Some xdg data
|
||||||
|
self.home = os.environ.get('HOME')
|
||||||
|
self.base_dirs = self.get_base_dirs()
|
||||||
# get windows from sway
|
# get windows from sway
|
||||||
windows = get_windows()
|
windows = get_windows()
|
||||||
label_len = 20
|
|
||||||
|
# Config defaults
|
||||||
|
self.label_len = 20
|
||||||
|
self.icon_size = 128
|
||||||
|
# Can be overwritten in config_file $HOME/.local/swayswitch/config
|
||||||
|
self.set_config_from_file()
|
||||||
|
|
||||||
|
self.icon_dirs = self.get_icon_dirs()
|
||||||
|
|
||||||
# and create a sizer to manage the layout of child widgets
|
# and create a sizer to manage the layout of child widgets
|
||||||
x_and_y = int(math.sqrt(len(windows)) + 0.5)
|
x_and_y = int(math.sqrt(len(windows)) + 0.5)
|
||||||
sizer = wx.GridSizer(x_and_y) # pylint: disable=no-member
|
self.sizer = wx.GridSizer(x_and_y) # pylint: disable=no-member
|
||||||
self.pnl.SetSizer(sizer)
|
self.pnl.SetSizer(self.sizer)
|
||||||
for window in windows:
|
self.set_buttons(windows)
|
||||||
|
self.sizer.Fit(self)
|
||||||
|
self.sizer.Layout()
|
||||||
|
|
||||||
|
def get_base_dirs(self):
|
||||||
|
"""We want to follow the XDG standard if possible"""
|
||||||
|
base_dirs = []
|
||||||
|
xdg_dirs = os.environ.get('XDG_DATA_DIRS')
|
||||||
|
pos_dirs = []
|
||||||
|
if xdg_dirs:
|
||||||
|
pos_dirs = xdg_dirs.split(":")
|
||||||
|
if not pos_dirs:
|
||||||
|
pos_dirs = [
|
||||||
|
"/usr/share", "/usr/local/share", self.home + "/.local/share",
|
||||||
|
self.home + "/.local/share/flatpak/exports/share",
|
||||||
|
"/var/lib/flatpak/exports/share"
|
||||||
|
]
|
||||||
|
for pos_dir in pos_dirs:
|
||||||
|
if os.path.exists(pos_dir):
|
||||||
|
base_dirs.append(pos_dir)
|
||||||
|
return base_dirs
|
||||||
|
|
||||||
|
def get_desktop_file(self, command):
|
||||||
|
"""From here we return first dektopfile"""
|
||||||
|
desktop_file = None
|
||||||
|
for base_dir in self.base_dirs:
|
||||||
|
directory = base_dir + "/applications"
|
||||||
|
if os.path.exists(directory):
|
||||||
|
command = "grep -s -l {} {}/*.desktop".format(
|
||||||
|
command, directory)
|
||||||
|
for data in [i for i in run_command(command) if i]:
|
||||||
|
for result in data.decode().split('\n'):
|
||||||
|
desktop_file = result
|
||||||
|
break
|
||||||
|
return desktop_file
|
||||||
|
|
||||||
|
def get_icon_bitmap(self, icon):
|
||||||
|
"""Create a bitmap with right size from svg or png"""
|
||||||
|
if icon:
|
||||||
|
if icon.endswith(".svg"):
|
||||||
|
svgpng = cairosvg.svg2png(
|
||||||
|
bytestring=open(icon).read().encode('utf-8'))
|
||||||
|
image = wx.Image(BytesIO(svgpng), wx.BITMAP_TYPE_PNG) # pylint: disable=no-member
|
||||||
|
else:
|
||||||
|
unscaled_bitmap = wx.Bitmap(icon) # pylint: disable=no-member
|
||||||
|
image = unscaled_bitmap.ConvertToImage()
|
||||||
|
image = image.Scale(self.icon_size, self.icon_size,
|
||||||
|
wx.IMAGE_QUALITY_HIGH) # pylint: disable=no-member
|
||||||
|
bitmap = wx.Bitmap(image) # pylint: disable=no-member
|
||||||
|
else:
|
||||||
|
bitmap = wx.Bitmap() # pylint: disable=no-member
|
||||||
|
return bitmap
|
||||||
|
|
||||||
|
def get_icon_dirs(self):
|
||||||
|
"""Find icon dirs"""
|
||||||
|
icon_dirs = []
|
||||||
|
for base_dir in [
|
||||||
|
directory for directory in self.base_dirs if directory
|
||||||
|
]:
|
||||||
|
directory = base_dir + "/icons"
|
||||||
|
icon_dirs.append(directory)
|
||||||
|
icon_dirs.append(self.home + "/.icons")
|
||||||
|
icon_dirs.append("/usr/share/pixmaps")
|
||||||
|
|
||||||
|
return icon_dirs
|
||||||
|
|
||||||
|
def get_icon_path(self, desktop_file):
|
||||||
|
"""Glue function to get icon path"""
|
||||||
|
icon_name = get_icon_name(desktop_file)
|
||||||
|
pos_icons = self.get_pos_icons(icon_name)
|
||||||
|
icon_path = self.get_right_size_icon(pos_icons)
|
||||||
|
return icon_path
|
||||||
|
|
||||||
|
def get_label(self, window):
|
||||||
|
"""Construct the label for the window"""
|
||||||
|
try:
|
||||||
|
label = window['window_properties']['class']
|
||||||
|
except KeyError:
|
||||||
try:
|
try:
|
||||||
label = window['window_properties']['class']
|
label = window['app_id']
|
||||||
|
if len(label) < self.label_len:
|
||||||
|
label = label + ':' + window['name'][:self.label_len -
|
||||||
|
len(label)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
try:
|
label = window['name']
|
||||||
label = window['app_id']
|
if len(label) > self.label_len:
|
||||||
if len(label) < label_len:
|
label = label[:self.label_len]
|
||||||
label = label + ':' + window['name'][:label_len -
|
|
||||||
len(label)]
|
label = "ws" + str(window['workspace']) + ": " + label
|
||||||
except KeyError:
|
return label
|
||||||
label = window['name']
|
|
||||||
if len(label) > label_len:
|
def get_pos_icons(self, icon_name):
|
||||||
label = label[:label_len]
|
"""Collect possible icons, prefer svg"""
|
||||||
label = "ws" + str(window['workspace']) + ":\n" + label
|
possible_icons = []
|
||||||
winid = window['id']
|
for icon_dir in [
|
||||||
size = wx.Window.GetFont(self).GetPointSize() * label_len # pylint: disable=no-member
|
directory for directory in self.icon_dirs if directory
|
||||||
btn = wx.Button( # pylint: disable=no-member
|
]:
|
||||||
parent=self.pnl,
|
if os.path.exists(icon_dir):
|
||||||
id=winid,
|
command = "find {} -name *{}.svg".format(icon_dir, icon_name)
|
||||||
label=label,
|
result = run_command(command)[0].split()
|
||||||
size=wx.Size(size, size)) # pylint: disable=no-member
|
if not result:
|
||||||
btn.Bind(
|
command = "find {} -name *{}.png".format(
|
||||||
wx.EVT_BUTTON,
|
icon_dir, icon_name)
|
||||||
lambda event, mwinid=winid: self.switch_window(event, mwinid))
|
result = run_command(command)[0].split()
|
||||||
sizer.Add(btn, 0, wx.ALIGN_CENTER) # pylint: disable=no-member
|
for data in result:
|
||||||
# Set up esc keybinding
|
icon_cand = data.decode()
|
||||||
self.Bind(wx.EVT_CHAR_HOOK, lambda event: self.on_key_press(event)) # pylint: disable=unnecessary-lambda
|
if icon_cand not in possible_icons:
|
||||||
sizer.Fit(self)
|
possible_icons.append(icon_cand)
|
||||||
sizer.Layout()
|
return possible_icons
|
||||||
|
|
||||||
|
def get_right_size_icon(self, possible_icons):
|
||||||
|
"""Try to find prefered size"""
|
||||||
|
icon = None
|
||||||
|
for pos_icon in possible_icons:
|
||||||
|
if pos_icon.endswith(".svg"):
|
||||||
|
icon = pos_icon
|
||||||
|
break
|
||||||
|
if not icon and (str(self.icon_size) + "x" + str(self.icon_size)
|
||||||
|
in pos_icon):
|
||||||
|
icon = pos_icon
|
||||||
|
if not icon:
|
||||||
|
try:
|
||||||
|
icon = sorted(possible_icons)[0]
|
||||||
|
except IndexError:
|
||||||
|
icon = ""
|
||||||
|
return icon
|
||||||
|
|
||||||
def on_key_press(self, event):
|
def on_key_press(self, event):
|
||||||
"""Intercept esc key press"""
|
"""Intercept esc key press"""
|
||||||
keycode = event.GetUnicodeKey()
|
keycode = event.GetUnicodeKey()
|
||||||
if keycode == wx.WXK_ESCAPE: # pylint: disable=no-member
|
if keycode == wx.WXK_ESCAPE: # pylint: disable=no-member
|
||||||
"""enter normal mode and exit"""
|
# enter normal mode and exit
|
||||||
command = 'swaymsg mode "default"'
|
command = 'swaymsg mode "default"'
|
||||||
subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
|
run_command(command)
|
||||||
self.Close(True)
|
self.Close(True)
|
||||||
else:
|
else:
|
||||||
event.Skip(True)
|
event.Skip(True)
|
||||||
|
|
||||||
|
def set_buttons(self, windows):
|
||||||
|
"""Loop over windows and create buttons for them"""
|
||||||
|
for window in windows:
|
||||||
|
# This is setting up an inner sizer with a static text label and an image icon
|
||||||
|
inner_sizer = wx.BoxSizer(orient=wx.VERTICAL) # pylint: disable=no-member
|
||||||
|
label = self.get_label(window)
|
||||||
|
winid = window['id']
|
||||||
|
size = wx.Window.GetFont(self).GetPointSize() * self.label_len # pylint: disable=no-member
|
||||||
|
command = get_command(window['pid'])
|
||||||
|
desktop_file = self.get_desktop_file(command)
|
||||||
|
icon_path = self.get_icon_path(desktop_file)
|
||||||
|
bitmap = self.get_icon_bitmap(icon_path)
|
||||||
|
btn = wx.BitmapButton( # pylint: disable=no-member
|
||||||
|
self.pnl,
|
||||||
|
id=winid,
|
||||||
|
bitmap=bitmap,
|
||||||
|
size=wx.Size(size, size)) # pylint: disable=no-member
|
||||||
|
btn.Bind(
|
||||||
|
wx.EVT_BUTTON,
|
||||||
|
lambda event, mwinid=winid: self.switch_window(event, mwinid))
|
||||||
|
# Set up esc keybinding
|
||||||
|
self.Bind(wx.EVT_CHAR_HOOK, lambda event: self.on_key_press(event)) # pylint: disable=unnecessary-lambda
|
||||||
|
statictext = wx.StaticText(self.pnl, -1, label) # pylint: disable=no-member
|
||||||
|
inner_sizer.Add(statictext, 0, wx.ALIGN_CENTER) # pylint: disable=no-member
|
||||||
|
inner_sizer.Add(btn, 0, wx.ALIGN_CENTER) # pylint: disable=no-member
|
||||||
|
inner_sizer.Fit(self)
|
||||||
|
inner_sizer.Layout()
|
||||||
|
self.sizer.Add(inner_sizer, 0, wx.ALIGN_CENTER) # pylint: disable=no-member
|
||||||
|
|
||||||
|
def set_config_from_file(self):
|
||||||
|
"""If user has created a config file we will use values from there"""
|
||||||
|
config_file = self.home + "/.local/swayswitch/config"
|
||||||
|
if os.path.exists(config_file):
|
||||||
|
try:
|
||||||
|
cfg = toml.load(config_file)
|
||||||
|
if cfg["label_len"]:
|
||||||
|
self.label_len = cfg["label_len"]
|
||||||
|
if cfg["icon_size"]:
|
||||||
|
self.icon_size = cfg["icon_size"]
|
||||||
|
except toml.TomlDecodeError:
|
||||||
|
#If use has formating errors we warn to stderr, but move on with defaults
|
||||||
|
os.write(
|
||||||
|
2, b"WARNING: formatting errors in " +
|
||||||
|
config_file.encode() + b"\n")
|
||||||
|
|
||||||
def switch_window(self, event, winid): # pylint: disable=unused-argument
|
def switch_window(self, event, winid): # pylint: disable=unused-argument
|
||||||
"""Switches the focus to the given id and enter normalmode"""
|
"""Switches the focus to the given id and enter normalmode"""
|
||||||
command = 'swaymsg [con_id={}] focus, mode "default"'.format(winid)
|
command = 'swaymsg [con_id={}] focus, mode "default"'.format(winid)
|
||||||
subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
|
run_command(command)
|
||||||
self.Close(True)
|
self.Close(True)
|
||||||
|
|
||||||
|
|
||||||
def get_windows():
|
|
||||||
"""Returns a list of all json window objects"""
|
|
||||||
command = "swaymsg -t get_tree"
|
|
||||||
process = subprocess.Popen(command,
|
|
||||||
shell=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE)
|
|
||||||
data = json.loads(process.communicate()[0])
|
|
||||||
|
|
||||||
# Select outputs that are active
|
|
||||||
windows = []
|
|
||||||
for output in data['nodes']:
|
|
||||||
|
|
||||||
# The scratchpad (under __i3) is not supported
|
|
||||||
if output.get('name') != '__i3' and output.get('type') == 'output':
|
|
||||||
workspaces = output.get('nodes')
|
|
||||||
for workspace in workspaces:
|
|
||||||
if workspace.get('type') == 'workspace':
|
|
||||||
windows += extract_nodes_iterative(workspace)
|
|
||||||
return windows
|
|
||||||
|
|
||||||
|
|
||||||
def extract_nodes_iterative(workspace):
|
def extract_nodes_iterative(workspace):
|
||||||
"""Extracts all windows from a sway workspace json object"""
|
"""Extracts all windows from a sway workspace json object"""
|
||||||
all_nodes = []
|
all_nodes = []
|
||||||
|
@ -126,6 +260,52 @@ def extract_nodes_iterative(workspace):
|
||||||
return all_nodes
|
return all_nodes
|
||||||
|
|
||||||
|
|
||||||
|
def get_command(pid):
|
||||||
|
"""Returns a list of all json window objects"""
|
||||||
|
command = "ps h c -p {} -o command".format(pid)
|
||||||
|
result = run_command(command)[0].rstrip().decode()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_icon_name(desktop_file):
|
||||||
|
""" Find icon from a desktop file"""
|
||||||
|
command = "grep Icon= {}".format(desktop_file)
|
||||||
|
for data in [i.rstrip() for i in run_command(command) if i]:
|
||||||
|
try:
|
||||||
|
icon_name = data.decode().split('=')[1]
|
||||||
|
return icon_name
|
||||||
|
except IndexError:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def get_windows():
|
||||||
|
"""Returns a list of all json window objects"""
|
||||||
|
command = "swaymsg -t get_tree"
|
||||||
|
data = json.loads(run_command(command)[0])
|
||||||
|
|
||||||
|
# Select outputs that are active
|
||||||
|
windows = []
|
||||||
|
for output in data['nodes']:
|
||||||
|
|
||||||
|
# The scratchpad (under __i3) is not supported
|
||||||
|
if output.get('name') != '__i3' and output.get('type') == 'output':
|
||||||
|
workspaces = output.get('nodes')
|
||||||
|
for workspace in workspaces:
|
||||||
|
if workspace.get('type') == 'workspace':
|
||||||
|
windows += extract_nodes_iterative(workspace)
|
||||||
|
return windows
|
||||||
|
|
||||||
|
|
||||||
|
def run_command(command):
|
||||||
|
"""Run a command on system and capture result"""
|
||||||
|
process = subprocess.Popen(command,
|
||||||
|
shell=True,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
result = process.communicate()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
# Entry point
|
# Entry point
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# When this module is run (not imported) then create the app, the
|
# When this module is run (not imported) then create the app, the
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mode "switcher" {
|
mode "switcher" {
|
||||||
bindsym $mod+f fullscreen
|
bindsym Mod4+f fullscreen
|
||||||
|
bindsym Mod4+q mode "default"
|
||||||
}
|
}
|
||||||
|
|
||||||
bindsym $mod+Tab exec /usr/bin/swayswitch, mode "switcher"
|
bindsym Mod4+Tab exec /usr/bin/swayswitch, mode "switcher"
|
||||||
|
|
Loading…
Add table
Reference in a new issue