diff --git a/README.md b/README.md index 7493959..940616e 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/config.toml b/config.toml index 94332a5..ae4e7f3 100644 --- a/config.toml +++ b/config.toml @@ -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 diff --git a/examle_sway_config b/example_sway_config similarity index 100% rename from examle_sway_config rename to example_sway_config diff --git a/src/config.py b/src/config.py index 8002558..843a095 100644 --- a/src/config.py +++ b/src/config.py @@ -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 . +layman. If not, see . """ -import tomli from logging import exception +import tomli CONFIG_PATH = ".config/layman/config.toml" @@ -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: diff --git a/src/config.toml b/src/config.toml deleted file mode 100644 index e3c42d1..0000000 --- a/src/config.toml +++ /dev/null @@ -1,32 +0,0 @@ -# This is an example config file that only sets the default values for each value. -# Configure your own desired options before using - -# The `layman` section configures options that apply to the layman daemon, and any fallback -# 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 -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 -masterWidth = 50 # MasterStack: Default master width for all workspaces - - -# `output` sections configure options for workspaces created on this output. -[output.DP-1] -defaultLayout = "none" # The default WLM to assign to a workspace on this output -debug = false # Enable debug messages for WLMs on this output -depthLimit = 0 # Autotiling: Depth limit (disabled) for workspaces on this output -stackLayout = "splitv" # MasterStack: Default stack layout for workspaces on this output -masterWidth = 50 # MasterStack: Default master width for workspaces on this output - - -# `workspace` sections configure options for the specified workspace. -[workspace.1] -defaultLayout = "none" # The default WLM to assign to this workspace -debug = false # Enable debug messages for this workspace -depthLimit = 0 # Autotiling: Depth limit (disabled) for this workspace -stackLayout = "splitv" # MasterStack: Stack layout for this workspace -masterWidth = 50 # MasterStack: Master width for this workspaces on this output - diff --git a/src/layman.py b/src/layman.py index 0058e19..1c3b311 100755 --- a/src/layman.py +++ b/src/layman.py @@ -16,22 +16,21 @@ You should have received a copy of the GNU General Public License along with layman. If not, see . """ -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: @@ -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) @@ -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) @@ -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: @@ -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) @@ -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) diff --git a/src/managers/WorkspaceLayoutManager.py b/src/managers/WorkspaceLayoutManager.py index cede51f..13ef1ec 100644 --- a/src/managers/WorkspaceLayoutManager.py +++ b/src/managers/WorkspaceLayoutManager.py @@ -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 . +layman. If not, see . """ 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 @@ -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 @@ -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 @@ -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): diff --git a/src/server.py b/src/server.py index 877a9fe..ae487ed 100644 --- a/src/server.py +++ b/src/server.py @@ -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 . +layman. If not, see . """ -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) @@ -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())