I've been able to port over my workspace management from Awesome to i3 using IPC, the config is attached.
The concept was discussed frequently on IRC. Imagine instead of using workspaces as tags, they are just elements in an array. If you represent a workspace as a cell on a piece of graph paper, and then use another piece of paper with one or more cells cut out as a mask to slide around and view the underlying layer it would be a fair analogy. Down to the issue I've found. I'm not sure if I'm outracing the IPC interface, but frequently IPC commands to make the workspaces change are failing while returning success. So I'll have a monitor where the workspace is still a scratch one (ie: by output name) instead of it changing to the workspace intended. No crashing, and often repeating the operation will make it work. Second I've run into an issue where Gkrellm cannot dock on the left or right side of the screen because left/right docking WM hints haven't been implemented yet. Is this a pending feature? Thanks. ------------------------------------------------------------------ Russell Adams rlad...@adamsinfoserv.com PGP Key ID: 0x1160DCB3 http://www.adamsinfoserv.com/ Fingerprint: 1723 D8CA 4280 1EC9 557F 66E8 1154 E018 1160 DCB3
set $mod Mod4 font pango:DejaVu Sans Mono 8 mouse_warping none # Use Mouse+$mod to drag floating windows to their wanted position floating_modifier $mod # start a terminal, I use xbindkeys #bindsym $mod+Shift+Return exec urxvt # class border backgr. text indicator client.focused #888888 #888888 #000000 #2e9ef4 client.focused_inactive #333333 #222222 #888888 #292d2e client.unfocused #333333 #222222 #888888 #292d2e client.urgent #2f343a #900000 #ffffff #900000 # reload the configuration file bindsym $mod+Shift+c reload # restart i3 inplace (preserves your layout/session, can be used to upgrade i3) bindsym $mod+Shift+r restart bindsym $mod+Shift+q exit # Inter-workspace navigation, panning around the 2d plane bindsym Control+Left exec --no-startup-id ~/.i3/workspaces.py -1 0 0 bindsym Control+Right exec --no-startup-id ~/.i3/workspaces.py 1 0 0 bindsym Control+Up exec --no-startup-id ~/.i3/workspaces.py 0 -1 0 bindsym Control+Down exec --no-startup-id ~/.i3/workspaces.py 0 1 0 # Inter-workspace navigation, swap planes on third dimension bindsym Control+Shift+Left exec --no-startup-id ~/.i3/workspaces.py 0 0 -1 bindsym Control+Shift+Right exec --no-startup-id ~/.i3/workspaces.py 0 0 1 # Intra-workspace navigation # change focus bindsym Shift+Left focus left bindsym Shift+Down focus down bindsym Shift+Up focus up bindsym Shift+Right focus right # Move the window column bindsym $mod+Shift+Left move left bindsym $mod+Shift+Down move down bindsym $mod+Shift+Up move up bindsym $mod+Shift+Right move right # split in horizontal orientation bindsym $mod+Shift+h split h # split in vertical orientation bindsym $mod+Shift+v split v # enter fullscreen mode for the focused container bindsym $mod+space fullscreen # change container layout (stacked, tabbed, toggle split) bindsym $mod+Shift+s layout stacking bindsym $mod+Shift+w layout tabbed bindsym $mod+Shift+e layout toggle split # toggle tiling / floating bindsym $mod+Shift+space floating toggle # resize window (you can also use the mouse for that) mode "resize" { # These bindings trigger as soon as you enter the resize mode # Pressing left will shrink the window’s width. # Pressing right will grow the window’s width. # Pressing up will shrink the window’s height. # Pressing down will grow the window’s height. bindsym j resize shrink width 10 px or 10 ppt bindsym k resize grow height 10 px or 10 ppt bindsym l resize shrink height 10 px or 10 ppt bindsym semicolon resize grow width 10 px or 10 ppt # same bindings, but for the arrow keys bindsym Left resize shrink width 10 px or 10 ppt bindsym Down resize grow height 10 px or 10 ppt bindsym Up resize shrink height 10 px or 10 ppt bindsym Right resize grow width 10 px or 10 ppt # back to normal: Enter or Escape bindsym Return mode "default" bindsym Escape mode "default" } bindsym $mod+r mode "resize" # Start i3bar to display a workspace bar (plus the system information i3status # finds out, if available) bar { status_command i3status } # Set defaults workspace 0_0_0 output LVDS1 workspace -1_0_0 output DVI-D-2 workspace 0_0_0 output DVI-I-1 workspace 1_0_0 output DVI-D-1
#!/usr/bin/python3 import struct import json import subprocess import socket import argparse import time # Workspaces in the format of 1:2:3, with negative integers ok. # List of monitors, with x/y offset from center centerScreen = "DVI-I-1" monitors = { "DVI-D-2": (-1, 0), # Left at dock "DVI-I-1": ( 0, 0), # Center for docking station "DVI-D-1": ( 1, 0), # Right at dock } ipcheader = b'i3-ipc' ipccmds = { 'COMMAND': 0, 'GET_WORKSPACES': 1, 'GET_OUTPUTS': 3, } # Obtain socket socketfile = subprocess.Popen(['/usr/bin/i3','--get-socket'], stdout=subprocess.PIPE).communicate()[0].rstrip() def socketCommand(mode, command=''): i3socket = socket.socket(socket.AF_UNIX) i3socket.connect(socketfile) i3socket.send( struct.pack("=6sLL" + str(len(command)) + "s", b'i3-ipc', len(command), ipccmds[mode], command.encode()) ) reply = i3socket.recv(8192) data = struct.unpack('=ccccccLL',reply[:14]) assert b''.join(data[0:6]) == b'i3-ipc' size = data[6] payload = struct.unpack("=ccccccLL" + str(size) + "s", reply)[8] i3socket.close() print(command) print(json.loads(payload.decode())) return json.loads(payload.decode()) def getOutputs(): """ [{'active': True, 'current_workspace': '0:0:0', 'name': 'DVI-I-1', 'primary': False, 'rect': {'height': 1080, 'width': 1920, 'x': 1920, 'y': 0}}, {'active': True, 'current_workspace': '-1:-1:0', 'name': 'DVI-D-1', 'primary': False, 'rect': {'height': 1080, 'width': 1920, 'x': 3840, 'y': 0}}, {'active': True, 'current_workspace': '-1:0:0', 'name': 'DVI-D-2', 'primary': False, 'rect': {'height': 1080, 'width': 1920, 'x': 0, 'y': 0}}, {'active': False, 'current_workspace': None, 'name': 'HDMI-1', 'primary': False, 'rect': {'height': 0, 'width': 0, 'x': 0, 'y': 0}}] """ return socketCommand('GET_OUTPUTS') def getWorkspaces(): """ [{'focused': True, 'name': '0:0:0', 'num': 0, 'output': 'DVI-I-1', 'rect': {'height': 1061, 'width': 1920, 'x': 1920, 'y': 0}, 'urgent': False, 'visible': True}, {'focused': False, 'name': '-1:0:0', 'num': None, 'output': 'DVI-D-2', 'rect': {'height': 1061, 'width': 1920, 'x': 0, 'y': 0}, 'urgent': False, 'visible': True}, {'focused': False, 'name': '1:0:0', 'num': 1, 'output': 'DVI-D-1', 'rect': {'height': 1061, 'width': 1920, 'x': 3840, 'y': 0}, 'urgent': False, 'visible': False}, {'focused': False, 'name': '-1:-1:0', 'num': None, 'output': 'DVI-D-1', 'rect': {'height': 1061, 'width': 1920, 'x': 3840, 'y': 0}, 'urgent': False, 'visible': True}] """ return socketCommand('GET_WORKSPACES') def changeWorkspaces( dx, dy, dz): outputs = getOutputs() workspaces = getWorkspaces() currentOutput = [i for i in workspaces if i['focused'] == True ][0]['output'] # Don't adjust based on data from every screen. Find center position and move it, then set the rest using offsets currentCenterWS = [i for i in workspaces if i['output'] == centerScreen and i['visible'] == True][0]['name'] # Enforce tag format or reset position try: coords = [int(i) for i in currentCenterWS.strip("'").split('_')] except ValueError: coords = [0,0,0] # Change all outputs to a workspace uniquely named by output temporarily for output in monitors: socketCommand('COMMAND', "focus output " + output) socketCommand('COMMAND', "workspace " + output) # Set each monitor by offset from center and the new delta for output in monitors: newWS = "{:d}_{:d}_{:d}".format(coords[0] + monitors[output][0] + dx, coords[1] + monitors[output][1] + dy, coords[2] + dz) socketCommand('COMMAND', "focus output " + output) socketCommand('COMMAND', "workspace " + newWS) socketCommand('COMMAND', "move workspace to output " + output) # Restore focus socketCommand('COMMAND', "focus output " + currentOutput) # MAIN parser = argparse.ArgumentParser(description='Change i3 workspaces in a 3d space') parser.add_argument('x', help='Delta X', nargs=1, type=int, default=0) parser.add_argument('y', help='Delta Y', nargs=1, type=int, default=0) parser.add_argument('z', help='Delta Z', nargs=1, type=int, default=0) options = parser.parse_args() changeWorkspaces(options.x[0], options.y[0], options.z[0])