summaryrefslogtreecommitdiffstats
path: root/powerline/segments/i3wm.py
diff options
context:
space:
mode:
Diffstat (limited to 'powerline/segments/i3wm.py')
-rw-r--r--powerline/segments/i3wm.py309
1 files changed, 309 insertions, 0 deletions
diff --git a/powerline/segments/i3wm.py b/powerline/segments/i3wm.py
new file mode 100644
index 0000000..57ab377
--- /dev/null
+++ b/powerline/segments/i3wm.py
@@ -0,0 +1,309 @@
+# vim:fileencoding=utf-8:noet
+from __future__ import (unicode_literals, division, absolute_import, print_function)
+
+import re
+
+from powerline.theme import requires_segment_info
+from powerline.bindings.wm import get_i3_connection
+
+WORKSPACE_REGEX = re.compile(r'^[0-9]+: ?')
+
+def workspace_groups(w):
+ group = []
+ if w.focused:
+ group.append('workspace:focused')
+ group.append('w_focused')
+ if w.urgent:
+ group.append('workspace:urgent')
+ group.append('w_urgent')
+ if w.visible:
+ group.append('workspace:visible')
+ group.append('w_visible')
+ group.append('workspace')
+ return group
+
+
+def format_name(name, strip=False):
+ if strip:
+ return WORKSPACE_REGEX.sub('', name, count=1)
+ return name
+
+
+def is_empty_workspace(workspace, containers):
+ if workspace.focused or workspace.visible:
+ return False
+ wins = [win for win in containers[workspace.name].leaves()]
+ return False if len(wins) > 0 else True
+
+WS_ICONS = {"multiple": "M"}
+
+def get_icon(workspace, separator, icons, show_multiple_icons, ws_containers):
+ icons_tmp = WS_ICONS
+ icons_tmp.update(icons)
+ icons = icons_tmp
+
+ wins = [win for win in ws_containers[workspace.name].leaves() \
+ if win.parent.scratchpad_state == 'none']
+ if len(wins) == 0:
+ return ''
+
+ result = ''
+ cnt = 0
+ for key in icons:
+ if not icons[key] or len(icons[key]) < 1:
+ continue
+ if any(key in win.window_class for win in wins if win.window_class):
+ result += (separator if cnt > 0 else '') + icons[key]
+ cnt += 1
+ if not show_multiple_icons and cnt > 1:
+ if 'multiple' in icons:
+ return icons['multiple']
+ else:
+ return ''
+ return result
+
+@requires_segment_info
+def workspaces(pl, segment_info, only_show=None, output=None, strip=0, format='{name}',
+ icons=WS_ICONS, sort_workspaces=False, show_output=False, priority_workspaces=[],
+ hide_empty_workspaces=False):
+ '''Return list of used workspaces
+
+ :param list only_show:
+ Specifies which workspaces to show. Valid entries are ``"visible"``,
+ ``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces
+ are shown.
+ :param str output:
+ May be set to the name of an X output. If specified, only workspaces
+ on that output are shown. Overrides automatic output detection by
+ the lemonbar renderer and bindings.
+ Use "__all__" to show workspaces on all outputs.
+ :param int strip:
+ Specifies how many characters from the front of each workspace name
+ should be stripped (e.g. to remove workspace numbers). Defaults to zero.
+ :param str format:
+ Specifies the format used to display workspaces; defaults to ``{name}``.
+ Valid fields are: ``name`` (workspace name), ``number`` (workspace number
+ if present), `stipped_name`` (workspace name stripped of leading number),
+ ``icon`` (if available, icon for application running in the workspace,
+ uses the ``multiple`` icon instead of multiple different icons), ``multi_icon``
+ (similar to ``icon``, but does not use ``multiple``, instead joins all icons
+ with a single space)
+ :param dict icons:
+ A dictionary mapping a substring of window classes to strings to be used as an
+ icon for that window class.
+ Further, there is a ``multiple`` icon for workspaces containing more than one
+ window.
+ :param bool sort_workspaces:
+ Sort the workspaces displayed by their name according to the natural ordering.
+ :param bool show_output:
+ Shows the name of the output if more than one output is connected.
+ :param list priority_workspaces:
+ A list of workspace names to be sorted before any other workspaces in the given
+ order.
+ :param bool hide_empty_workspaces:
+ Hides all workspaces without any open window.
+ Also hides non-focussed workspaces containing only an open scratchpad.
+
+
+ Highlight groups used: ``workspace`` or ``w_visible``, ``workspace:visible``, ``workspace`` or ``w_focused``, ``workspace:focused``, ``workspace`` or ``w_urgent``, ``workspace:urgent``, ``workspace`` or ``output``.
+ '''
+ conn = get_i3_connection()
+
+ if not output == "__all__":
+ output = output or segment_info.get('output')
+ else:
+ output = None
+
+ if output:
+ output = [output]
+ else:
+ output = [o.name for o in conn.get_outputs() if o.active]
+
+
+ def sort_ws(ws):
+ if sort_workspaces:
+ def natural_key(ws):
+ str = ws.name
+ return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', str)]
+ ws = sorted(ws, key=natural_key)
+ result = []
+ for n in priority_workspaces:
+ result += [w for w in ws if w.name == n]
+ return result + [w for w in ws if not w.name in priority_workspaces]
+
+ ws_containers = {w_con.name : w_con for w_con in conn.get_tree().workspaces()}
+
+ if len(output) <= 1:
+ res = []
+ if show_output:
+ res += [{
+ 'contents': output[0],
+ 'highlight_groups': ['output']
+ }]
+ res += [{
+ 'contents': format.format(name = w.name[min(len(w.name), strip):],
+ stripped_name = format_name(w.name, strip=True),
+ number = w.num,
+ icon = get_icon(w, '', icons, False, ws_containers),
+ multi_icon = get_icon(w, ' ', icons, True, ws_containers)),
+ 'highlight_groups': workspace_groups(w)
+ } for w in sort_ws(conn.get_workspaces()) \
+ if (not only_show or any(getattr(w, tp) for tp in only_show)) \
+ if w.output == output[0] \
+ if not (hide_empty_workspaces and is_empty_workspace(w, ws_containers))]
+ return res
+ else:
+ res = []
+ for n in output:
+ if show_output:
+ res += [{
+ 'contents': n,
+ 'highlight_groups': ['output']
+ }]
+ res += [{
+ 'contents': format.format(name = w.name[min(len(w.name), strip):],
+ stripped_name = format_name(w.name, strip=True),
+ number = w.num,
+ icon = get_icon(w, '', icons, False, ws_containers),
+ multi_icon = get_icon(w, ' ', icons, True, ws_containers)),
+ 'highlight_groups': workspace_groups(w)
+ } for w in sort_ws(conn.get_workspaces()) \
+ if (not only_show or any(getattr(w, tp) for tp in only_show)) \
+ if w.output == n \
+ if not (hide_empty_workspaces and is_empty_workspace(w, ws_containers))]
+ return res
+
+@requires_segment_info
+def workspace(pl, segment_info, workspace=None, strip=False, format=None, icons=WS_ICONS):
+ '''Return the specified workspace name
+
+ :param str workspace:
+ Specifies which workspace to show. If unspecified, may be set by the
+ ``list_workspaces`` lister if used, otherwise falls back to
+ currently focused workspace.
+
+ :param bool strip:
+ Specifies whether workspace numbers (in the ``1: name`` format) should
+ be stripped from workspace names before being displayed. Defaults to false.
+ Deprecated: Use {name} or {stripped_name} of format instead.
+
+ :param str format:
+ Specifies the format used to display workspaces; defaults to ``{name}``.
+ Valid fields are: ``name`` (workspace name), ``number`` (workspace number
+ if present), `stipped_name`` (workspace name stripped of leading number),
+ ``icon`` (if available, icon for application running in the workspace,
+ uses the ``multiple`` icon instead of multiple different icons), ``multi_icon``
+ (similar to ``icon``, but does not use ``multiple``, instead joins all icons
+ with a single space)
+
+ :param dict icons:
+ A dictionary mapping a substring of window classes to strings to be used as an
+ icon for that window class.
+ Further, there is a ``multiple`` icon for workspaces containing more than one
+ window.
+
+ Highlight groups used: ``workspace`` or ``w_visible``, ``workspace:visible``, ``workspace`` or ``w_focused``, ``workspace:focused``, ``workspace`` or ``w_urgent``, ``workspace:urgent``, ``workspace``.
+ '''
+ if format == None:
+ format = '{stripped_name}' if strip else '{name}'
+
+ conn = get_i3_connection()
+ ws_containers = {w_con.name : w_con for w_con in conn.get_tree().workspaces()}
+
+ if workspace:
+ try:
+ w = next((
+ w for w in conn.get_workspaces()
+ if w.name == workspace
+ ))
+ except StopIteration:
+ return None
+ elif segment_info.get('workspace'):
+ w = segment_info['workspace']
+ else:
+ try:
+ w = next((
+ w for w in conn.get_workspaces()
+ if w.focused
+ ))
+ except StopIteration:
+ return None
+
+ return [{
+ 'contents': format.format(name = w.name,
+ stripped_name = format_name(w.name, strip=True),
+ number = w.num,
+ icon = get_icon(w, '', icons, False, ws_containers),
+ multi_icon = get_icon(w, ' ', icons, True, ws_containers)),
+ 'highlight_groups': workspace_groups(w)
+ }]
+
+
+@requires_segment_info
+def mode(pl, segment_info, names={'default': None}):
+ '''Returns current i3 mode
+
+ :param dict names:
+ Specifies the string to show for various modes.
+ Use ``null`` to hide a mode (``default`` is hidden by default).
+
+ Highligh groups used: ``mode``
+ '''
+ mode = segment_info['mode']
+ if mode in names:
+ return names[mode]
+ return mode
+
+
+def scratchpad_groups(w):
+ group = []
+ if w.urgent:
+ group.append('scratchpad:urgent')
+ if w.nodes[0].focused:
+ group.append('scratchpad:focused')
+ if w.workspace().name != '__i3_scratch':
+ group.append('scratchpad:visible')
+ group.append('scratchpad')
+ return group
+
+
+SCRATCHPAD_ICONS = {
+ 'fresh': 'O',
+ 'changed': 'X',
+}
+
+
+def scratchpad(pl, icons=SCRATCHPAD_ICONS):
+ '''Returns the windows currently on the scratchpad
+
+ :param dict icons:
+ Specifies the strings to show for the different scratchpad window states. Must
+ contain the keys ``fresh`` and ``changed``.
+
+ Highlight groups used: ``scratchpad`` or ``scratchpad:visible``, ``scratchpad`` or ``scratchpad:focused``, ``scratchpad`` or ``scratchpad:urgent``.
+ '''
+
+ return [
+ {
+ 'contents': icons.get(w.scratchpad_state, icons['changed']),
+ 'highlight_groups': scratchpad_groups(w)
+ }
+ for w in get_i3_connection().get_tree().descendants()
+ if w.scratchpad_state != 'none'
+ ]
+
+def active_window(pl, cutoff=100):
+ '''Returns the title of the currently active window.
+
+ :param int cutoff:
+ Maximum title length. If the title is longer, the window_class is used instead.
+
+ Highlight groups used: ``active_window_title``.
+ '''
+
+ focused = get_i3_connection().get_tree().find_focused()
+ cont = focused.name
+ if len(cont) > cutoff:
+ cont = focused.window_class
+
+ return [{'contents': cont, 'highlight_groups': ['active_window_title']}] if focused.name != focused.workspace().name else []