Skip to content
This repository was archived by the owner on Feb 14, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ bindsym $mod+Shift+Up nop layman move up
bindsym $mod+Shift+Right nop layman move right
```

The `src/mananagers/` directoy contains files that each hold an implementation of a WLM, with `WorkspaceLayoutManager.py`
The `src/managers/` directory contains files that each hold an implementation of a WLM, with `WorkspaceLayoutManager.py`
containing the parent class from which all WLMs are derived.

### none
Expand Down
4 changes: 2 additions & 2 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
# values for options not set in a [workspace] or [output] section.
[layman]
defaultLayout = "none" # The default WLM to assign to a workspace
excludedWorkspaces = [] # Numbers of workspaces to be excuded
excludedWorkspaces = [] # Numbers of workspaces to be excluded
excludedOutputs = [] # Names of outputs to be excuded
debug = false # Enable logging debug messages globaly
depthLimit = 0 # Autotiling: Default depth limit (disabled) for all workspaces
stackLayout = "splitv" # MasterStack: Default stack layout for all workspaces
stackSidet = "right" # MasterStack: Default stack position for all workspaces
stackSide = "right" # MasterStack: Default stack position for all workspaces
masterWidth = 50 # MasterStack: Default master width for all workspaces


Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
layman. If not, see <https://www.gnu.org/licenses/>.
layman. If not, see <https://www.gnu.org/licenses/>.
"""
import tomli
from logging import exception

import tomli

CONFIG_PATH = ".config/layman/config.toml"

Expand Down Expand Up @@ -73,7 +73,7 @@ def getForWorkspace(self, workspaceNum, key):
except KeyError:
pass

# If output config doesn't have the key, falback to default
# If output config doesn't have the key, fallback to default
try:
value = self.configDict[TABLE_LAYMAN][key]
except KeyError:
Expand Down
32 changes: 0 additions & 32 deletions src/config.toml

This file was deleted.

34 changes: 19 additions & 15 deletions src/layman.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,21 @@
You should have received a copy of the GNU General Public License along with
layman. If not, see <https://www.gnu.org/licenses/>.
"""
from i3ipc import Event, Connection
from importlib.machinery import SourceFileLoader
import inspect
import logging
import os
from setproctitle import setproctitle
import shutil
from importlib.machinery import SourceFileLoader
from typing import cast

from .server import MessageServer
from i3ipc import Connection, Event, WindowEvent
from i3ipc.events import IpcBaseEvent
from setproctitle import setproctitle

from . import utils
from . import config
from .managers import WorkspaceLayoutManager
from .managers import MasterStackLayoutManager
from .managers import AutotilingLayoutManager
from .managers import GridLayoutManager
from . import config, utils
from .managers import (AutotilingLayoutManager, GridLayoutManager,
MasterStackLayoutManager, WorkspaceLayoutManager)
from .server import MessageServer


class Layman:
Expand All @@ -49,7 +48,8 @@ def __init__(self):
window::new, window::focus, window::close, window::move, and window::floating.
"""

def windowCreated(self, _, event):
def windowCreated(self, _, event: IpcBaseEvent):
event = cast(WindowEvent, event)
window = utils.findFocusedWindow(self.cmdConn)
workspace = utils.findFocusedWorkspace(self.cmdConn)

Expand All @@ -62,7 +62,8 @@ def windowCreated(self, _, event):
self.dispatchToManager(event, window, workspace)


def windowFocused(self, _, event):
def windowFocused(self, _, event: IpcBaseEvent):
event = cast(WindowEvent, event)
window = utils.findFocusedWindow(self.cmdConn)
workspace = utils.findFocusedWorkspace(self.cmdConn)

Expand All @@ -74,7 +75,8 @@ def windowFocused(self, _, event):
# Pass command to the appropriate manager
self.dispatchToManager(event, window, workspace)

def windowClosed(self, _, event):
def windowClosed(self, _, event: IpcBaseEvent):
event = cast(WindowEvent, event)
# Try to find workspace by locating where the window is recorded
workspaces = []
for num in self.workspaceWindows:
Expand All @@ -97,7 +99,8 @@ def windowClosed(self, _, event):
self.dispatchToManager(event, window, workspace)


def windowMoved(self, _, event):
def windowMoved(self, _, event: IpcBaseEvent):
event = cast(WindowEvent, event)
window = utils.findFocusedWindow(self.cmdConn)
workspace = utils.findFocusedWorkspace(self.cmdConn)

Expand All @@ -120,7 +123,8 @@ def windowMoved(self, _, event):
self.dispatchToManager(event, window, workspace)


def windowFloating(self, _, event):
def windowFloating(self, _, event: IpcBaseEvent):
event = cast(WindowEvent, event)
window = self.cmdConn.get_tree().find_by_id(event.container.id)
workspace = utils.findFocusedWorkspace(self.cmdConn)

Expand Down
31 changes: 21 additions & 10 deletions src/managers/WorkspaceLayoutManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,40 @@
A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
layman. If not, see <https://www.gnu.org/licenses/>.
layman. If not, see <https://www.gnu.org/licenses/>.
"""
import inspect

from ..config import KEY_DEBUG
import i3ipc

from ..config import KEY_DEBUG, LaymanConfig


class WorkspaceLayoutManager:
# These properties should be overriden to configure your WLM as
# Needed
# needed.
shortName = "none"
overridesMoveBinds = False # Should window movement commands be sent as binds
supportsFloating = False # Should windowFloating be used, or treated as Added/Removed

# These are the functions you should override for to implement a
# WLM.
def __init__(self, con, workspace, options):
con: i3ipc.Connection
workspaceId: int
workspaceName: str

# These are the functions you should override to implement a WLM.
#
# Parameters:
# con (i3ipc.Connection): An i3ipc connection for executing commands.
# workspace (i3ipc.WorkspaceReply): The workspace the layout manager is associated with.
# options (LaymanConfig): The loaded config file used for option defaults.
def __init__(self, con: i3ipc.Connection, workspace: i3ipc.WorkspaceReply, options: LaymanConfig):
self.con = con
self.workspaceId = workspace.ipc_data["id"]
self.workspaceNum = workspace.num
self.debug = options.getForWorkspace(self.workspaceNum, KEY_DEBUG)


# windowAdded is called when a new window is added to the workpsace,
# windowAdded is called when a new window is added to the workspace,
# either by being created on the workspace or moved to it from another.
def windowAdded(self, event, window):
pass
Expand All @@ -47,7 +58,7 @@ def windowRemoved(self, event, window):
pass


# windowFocused is called when a window on the workpsace is focused.
# windowFocused is called when a window on the workspace is focused.
def windowFocused(self, event, window):
pass

Expand All @@ -57,7 +68,7 @@ def windowFocused(self, event, window):
def windowMoved(self, event, window):
pass

# windowFloating is called when a windows floating state is toggled.
# windowFloating is called when a window's floating state is toggled.
def windowFloating(self, event, window):
pass

Expand Down Expand Up @@ -92,7 +103,7 @@ def log(self, msg):
print(("%s %d: %s: %s" % (self.shortName, self.workspaceNum, inspect.stack()[1][3], msg)))


# This log function includes the class name, workspace number, and the
# This log function includes the class name, workspace number, and the
# name of the function 2 calls up. This makes it useful for helper
# functions that get called by event handlers
def logCaller(self, msg):
Expand Down
26 changes: 3 additions & 23 deletions src/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,17 @@
A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
layman. If not, see <https://www.gnu.org/licenses/>.
layman. If not, see <https://www.gnu.org/licenses/>.
"""
from os import unlink, mkfifo
from queue import Queue
from os import mkfifo, unlink
from threading import Thread

PIPE = "/tmp/layman.pipe"

class MessageQueue(Queue):
def __init__(self, maxsize=0):
super().__init__(maxsize=0)
self.on_change_listeners = []

def _put(self, msg):
# Add to queue
super()._put(msg)

# Run any listeners on a separate thread
for listener in self.on_change_listeners:
listener(msg)

def registerListener(self, listener):
self.on_change_listeners.append(listener)

class MessageServer():

def __init__(self, callback):
self.callback = callback
self.queue = MessageQueue()
self.queue.registerListener(callback)

try:
unlink(PIPE)
Expand All @@ -56,5 +37,4 @@ def __init__(self, callback):
def readPipe(self):
while True:
with open(PIPE) as fifo:
self.queue.put(fifo.read())

self.callback(fifo.read())