You are not logged in.

#1 2023-04-24 05:18:35

johnraff
nullglob
From: Nagoya, Japan
Registered: 2015-09-09
Posts: 12,558
Website

The keybinds pipemenu

There are a couple of issues with the "Display keybinds" menu item:

1) "Display keybinds in new window" puts the Openbox keybinds at the top,
while "Display keybinds in menu" has the xbindkeys items first.
One of them needs altering: maybe put xbindkeys at the top in both?

2) The Openbox window keybinds don't always give all the info you want. For example the top 4 items:

o     C-A-Left            GoToDesktop
o     C-A-Right           GoToDesktop
o     C-A-Up              GoToDesktop
o     C-A-Down            GoToDesktop

which isn't super-useful. The actual xml says:

    <keybind key="C-A-Left">
      <action name="GoToDesktop"><to>left</to><wrap>no</wrap></action>
    </keybind>
    <keybind key="C-A-Right">
      <action name="GoToDesktop"><to>right</to><wrap>no</wrap></action>
    </keybind>
    <keybind key="C-A-Up">
      <action name="GoToDesktop"><to>up</to><wrap>no</wrap></action>
    </keybind>
    <keybind key="C-A-Down">
      <action name="GoToDesktop"><to>down</to><wrap>no</wrap></action>
    </keybind>

I suppose you can sort of figure out what it does, but eg

o     C-A-Left            GoToDesktop        left

might make it clearer?

If anyone's Python is up to it (I'm still a beginner), might it be possible to add that last word to the output of bl-kb?

Also eight.bit.al had a suggestion of splitting the keybinds menu, or moving it off the top level.

I think if it was coming off the top level, maybe a more appropriate place than Utilities might be Preferences?
EDIT there is already a Keybinds submenu in preferences - the "display keybinds" pipemenu could go in there perhaps?

An instant check of what your current keybinds actually are remains useful IMO - people don't necessarily update the Conky keybinds info every time they're changed.

Last edited by johnraff (2023-04-24 05:38:56)


...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), now on Bluesky, there's also some GitStuff )

Introduction to the Bunsenlabs Boron Desktop

Offline

#2 2023-10-01 07:51:22

johnraff
nullglob
From: Nagoya, Japan
Registered: 2015-09-09
Posts: 12,558
Website

Re: The keybinds pipemenu

johnraff wrote:

...a suggestion of splitting the keybinds menu, or moving it off the top level.

> there is already a Keybinds submenu in preferences User Settings - the "display keybinds" pipemenu could go in there perhaps?

How about moving "Display Keybinds" to menu > User Settings > Keybinds ?
It would simplify the top level menu a bit.

---
Now I'm having a look at fixing the other issues above...


...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), now on Bluesky, there's also some GitStuff )

Introduction to the Bunsenlabs Boron Desktop

Offline

#3 2023-10-01 08:09:36

hhh
Gaucho
From: High in the Custerdome
Registered: 2015-09-17
Posts: 16,039
Website

Re: The keybinds pipemenu

johnraff wrote:

How about moving "Display Keybinds" to menu > User Settings > Keybinds ?
It would simplify the top level menu a bit.

This makes sense to me. Ni!

Sorry, I just watched Magical Mystery Tour and the whole thing reeked of Monty Python, I've got Python on the brain as a result.


No, he can't sleep on the floor. What do you think I'm yelling for?!!!

Offline

#4 2023-10-03 01:25:30

johnraff
nullglob
From: Nagoya, Japan
Registered: 2015-09-09
Posts: 12,558
Website

Re: The keybinds pipemenu

I've hacked tweaked the bl-kb keybinds parser so a bit more info is displayed. GoToDesktop and SendToDesktop show the destination, and DirectionalCycleWindows shows the direction. There's still a lot of data left out, eg  the width, height, x and y that go with MoveResizeTo, or all the actions included in a <finalactions> tag but it would seriously bulk out the output, and require quite some hours of python study to figure out how to parse it all out. My current python knowledge could be written on the palm of my hand.

Anyway, if anyone would like to try the modified parser:

cp /usr/bin/bl-kb ~/bin

Make sure ~/bin is in your PATH, copy this code into ~/bin/bl-kb, delete ~/.cache/bunsen/keybinds_menu.xml and see if the "display keybinds" menu item is a little bit more helpful in the openbox section:

#!/usr/bin/env python3
#
#    bl-kb: a script to read openbox's rc.xml and write out the keybinds
#    Copyright (C) 2010 wlourf
#    Copyright (C) 2015 damo    <damo@bunsenlabs.org>
#    Copyright (C) 2018 Jens John <dev@2ion.de>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR 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 this program.  If not, see <http://www.gnu.org/licenses/>.
#
########################################################################
#
# This script reads the keybinds configuration file
# ("$HOME/.config/openbox/rc.xml") and writes them to a text file
# ("$HOME/.config/openbox/kbinds.txt").  The script is used by bl-kb-pipemenu to
# pipe the output to the Openbox menu, or to display keybinds in a separate
# window
#
# Based on a script by wlourf 07/03/2010
# <http://u-scripts.blogspot.com/2010/03/how-to-display-openboxs-shortcuts.html>
#
# The original script parsed the keyboard and mouse commands from rc.xml, and
# passed them to Conkys to display on screen
#
# April 2015
#   : This script outputs the keyboard keybinds to terminal or, with a "--gui"
#   argument will display the output in a text window as well
#
#   Written by damo <damo@bunsenlabs.org> for BunsenLabs Linux, April 2015
#
########################################################################
#
# When the Openbox config schema version changes, you need to adjust the XML
# namespace alias in read_keybindings().
#
########################################################################

from collections import OrderedDict
from lxml import etree
from shutil import which
from typing import List
import argparse
import os
import subprocess
import sys

def read_keybindings(openbox_rc: str) -> OrderedDict:
    """ Reads the Openbox XML keybind configuration from openbox_rc (which must
    be a valid Openbox XML configuration file) and returns a Python-native
    representation in the following structure:
        {
            key: [
                (effect_type, effect_param),
                (effect_type, effect_param),
                (...)
            ]
        }
    """

    ns = { "namespaces": { "A": "http://openbox.org/3.4/rc" } }
    data = OrderedDict()

    with open(openbox_rc, "r") as FILE:
        xml = etree.parse(FILE)

    for keybind in xml.xpath("/A:openbox_config/A:keyboard/A:keybind", **ns):
        key = keybind.xpath("@key")[0]
        effects = []
        for action in keybind.xpath("A:action", **ns):
            name = action.xpath("@name")[0]
            if name == "Execute":
                cmd = action.xpath("A:command", **ns)[0].text
                effects.append(("x", cmd,))
            elif name == "ShowMenu":
                menu = action.xpath("A:menu", **ns)[0].text
                effects.append(("o", menu,))
            elif name == "GoToDesktop" or name == "SendToDesktop":
                target = action.xpath("A:to", **ns)[0].text
                effects.append(("o", name + " " + target,))
            elif name == "DirectionalCycleWindows":
                direction = action.xpath("A:direction", **ns)[0].text
                effects.append(("o", name + " " + direction,))
            else:
                effects.append(("o", name,))
        data[key] = effects

    return data

def aggregate_effects(effects:list) -> list:
    _effects = []
    types = set(map(lambda e: e[0], effects))
    for t in types:
        agg_sep = "; " if t == "x" else ", "
        agg_effects = filter(lambda e: e[0] == t, effects)
        _effects.append(
            (t, agg_sep.join(map(lambda e: e[1], agg_effects)),))

    return _effects

def keybind2lines(key: str, effects: list) -> List[str]:
    for effect_type, effect_params in aggregate_effects(effects):
        yield "{} {:<16}\t{}".format(effect_type, key, effect_params)

def keybind2readabletext(key: str, effects: list) -> List[str]:
    def makeline(effect_type: str, key: str, effect_params: str):
        return "{:2}\t{:<16}\t{}".format(effect_type, key, effect_params)
    for (effect_type, effect_params) in aggregate_effects(effects):
        yield makeline(effect_type, key, effect_params)

def emit_textfile(path: str, bindings: dict) -> None:
    with open(path, "w") as FILE:
        for key, effects in bindings.items():
            for line in keybind2readabletext(key, effects):
                print(line, file=FILE)

def emit_stdout(bindings: dict) -> None:
    for key, effects in bindings.items():
        for line in keybind2lines(key, effects):
            print(line)

def emit_gui(kbinds_path: str, bindings: dict) -> None:
    yad_binary = which("yad")
    if yad_binary is None:
        raise Exception("Could not find 'yad' in $PATH.")
    subprocess.run([
        yad_binary,
        "--text-info",
        "--title='Openbox Keybinds'",
        "--window-icon=distributor-logo-bunsenlabs",
        "--filename={}".format(kbinds_path),
        "--width=700",
        "--height=700",
        "--fontname=Monospace",
        "--button=gtk-close"
    ], check=True)

def getopts() -> argparse.Namespace:
    ap = argparse.ArgumentParser(description="Display Openbox key bindings",
            formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    ap.add_argument("-g", "--gui", action="store_true", default=False,
            help="display keybinds in a text window")
    ap.add_argument("--openbox-rc",
            default="{}/.config/openbox/rc.xml".format(os.getenv("HOME", "/root")),
            help="Path to Openbox's rc.xml")
    ap.add_argument("--openbox-kbinds",
            default="{}/.config/openbox/kbinds.txt".format(os.getenv("HOME", "/root")),
            help="Output file to save the keybind list to")
    return ap.parse_args()

if __name__ == "__main__":
    opts = getopts()

    if os.access(opts.openbox_rc, os.F_OK | os.R_OK):
        bindings = read_keybindings(opts.openbox_rc)
    else:
        print("Openbox RC file not readable: {}".format(opts.openbox_rc),
                file=sys.stderr)
        sys.exit(1)

    try:
        emit_textfile(opts.openbox_kbinds, bindings)
    except Exception as err:
        print("Failed to write kbinds text file: {}".format(err), file=sys.stderr)
        sys.exit(1)

    try:
        if opts.gui:
            emit_gui(opts.openbox_kbinds, bindings)
        else:
            emit_stdout(bindings)
    except Exception as err:
        print("Failed to show bindings: {}".format(err),file=sys.stderr)
        sys.exit(1)

    sys.exit(0)

...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), now on Bluesky, there's also some GitStuff )

Introduction to the Bunsenlabs Boron Desktop

Offline

#5 2023-10-03 07:48:27

johnraff
nullglob
From: Nagoya, Japan
Registered: 2015-09-09
Posts: 12,558
Website

Re: The keybinds pipemenu

Tweaked the pipemenu a bit too: put the openbox keybinds at the top in both the "new window" and "menu" displays. It would have needed more hacking to put xbindkeys at the top in the menu. Commented out the two "backup and restore" items in the pipemenu because they really belong in the parent menu which is menu > User Settings > Keybinds. There's already an "edit xbindkeysrc" item there which might be enough? Keeps it all a bit cleaner.

#!/bin/bash
#    bl-kb-pipemenu - an Openbox pipemenu for displaying keybinds
#    Copyright (C) 2015 damo    <damo@bunsenlabs.org>
#                  2016-2023 John Crawley    <john@bunsenlabs.org>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR 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 this program.  If not, see <http://www.gnu.org/licenses/>.

# NB The keybinds in the menu are clickable, except for the Openbox
# commands, which just exit the menu

# bl-kb is used to parse [bl-]rc.xml and send the output to a textfile
# bl-xbk does the same for .xbindkeysrc

HELP='    bl-kb-pipemenu an Openbox Pipe Menu for displaying keybinds
    It should normally be called from an openbox or jgmenu menu.

Options (usually passed from the menu):
    -h --help   show this message
    --kbmenu  display keybindings in .xbindkeysrc and [bl-]rc.xml as a menu
    (The keybinds in the menu are clickable, except for the Openbox
     commands, which just exit the menu.)
    --xbkedit   backup and edit .xbindkeysrc
    --rcedit    backup and edit [bl-]rc.xml
    --gui       show keybindings in a popup window

If bl-keyboard-pipemenu is called with no options (the usual method),
it will output an xml openbox pipemenu for displaying keybindings.

By default, this pipemenu uses rc.xml, but if openbox has been called
with --config-file then it uses whatever xml file is referenced there.
In the case of BunsenLabs this will be bl-rc.xml.

NOTE: Most of the functionality of this pipemenu depends on the scripts
/usr/bin/bl-kb and /usr/bin/bl-xbk
which are provided by the same package "bunsen-pipemenus".
'
# look for a help option somewhere
for i in "$@"
do
    case "$i" in
    -h|--help)
        echo "$HELP"
        exit 0
        ;;
    esac
done

BL_COMMON_LIBDIR="/usr/lib/bunsen/common"
USR_OBCFG_DIR="$HOME/.config/openbox"
USR_CACHE_DIR="$HOME/.cache/bunsen"
KBTEXT="$USR_CACHE_DIR/kbinds.txt"
MENU_CACHE="$USR_CACHE_DIR/kbinds_menu.xml"
OBSCRIPT="bl-kb"
XBKSCRIPT="bl-xbk"
RCPATH="$USR_OBCFG_DIR/rc.xml"
XBKPATH="$HOME/.xbindkeysrc"

mkdir -p "$USR_CACHE_DIR"

if ! . "$BL_COMMON_LIBDIR/bl-includes" 2> /dev/null; then
    echo $"Error: Failed to locate bl-includes in $BL_COMMON_LIBDIR" >&2
    exit 1
fi

declareDependencies "$OBSCRIPT" "$XBKSCRIPT"

using_openbox=false
if ob_cmd=$( pgrep -u "$USER" -ax openbox )
then
    using_openbox=true
    rc_regex='--config-file (.*\.xml)($|[[:blank:]])'
    if [[ $ob_cmd =~ $rc_regex ]]
    then
        RCPATH=${BASH_REMATCH[1]} # otherwise stay with default
    fi
fi

using_xbindkeys=false
if xbk_cmd=$( pgrep -u "$USER" -ax xbindkeys )
then
    using_xbindkeys=true
    xbk_regex='(-f|--file) (.*)$'
    if [[ $xbk_cmd =~ $xbk_regex ]]
    then
        XBKPATH=${BASH_REMATCH[2]}
    fi
fi

OBSCRIPT_CMD=( "$OBSCRIPT" '--openbox-rc' "$RCPATH" '--openbox-kbinds' "$KBTEXT" )
XBKSCRIPT_CMD=( "$XBKSCRIPT" '--xbindkeys-rc' "$XBKPATH" '--kbinds-text' "$KBTEXT" )

case $1 in
'--xbkedit') # "Backup & Edit .xbindkeysrc" is chosen in menu
    # backup .xbindkeysrc first
    NOW=$(date +"%Y%m%d-%H%M")
    XBKBKP="$XBKPATH.$NOW"
    cp "$XBKPATH" "$XBKBKP"
    bl-text-editor "$XBKPATH"        # open .xbindkeysrc in default editor
    ;;

'--rcedit') # "Backup & Edit [bl-]rc.xml" is chosen in menu
    # backup [bl-]rc.xml first
    NOW=$(date +"%Y%m%d-%H%M")
    RCBKP="$RCPATH.$NOW"
    cp "$RCPATH" "$RCBKP"
    bl-text-editor "$RCPATH"        # open [bl-]rc.xml in default editor
    ;;

'--gui')
    # run script to write kbinds.txt, then open it in yad
    # do not use caching because GUI takes time anyway
    :> "$KBTEXT" # XBKSCRIPT does not blank file, it appends
    if [[ $using_openbox = false ]];then
        GUI_TITLE='Xbindkeys keybinds'
        "${XBKSCRIPT_CMD[@]}"
    elif [[ $using_xbindkeys = false ]];then
        GUI_TITLE='Openbox keybinds'
        "${OBSCRIPT_CMD[@]}"
    else
        GUI_TITLE='Openbox & Xbindkeys keybinds'
        "${OBSCRIPT_CMD[@]}"
        "${XBKSCRIPT_CMD[@]}"
    fi
    yad --text-info --title="$GUI_TITLE" --window-icon=distributor-logo-bunsenlabs\
    --filename="$KBTEXT" --width=700 --height=700\
    --fontname=Monospace --button=gtk-close
    ;;

'--kbmenu')
    # If MENU_CACHE needs updating, run scripts to write kbinds.txt
    # and generate MENU_CACHE.
    # If MENU_CACHE is new enough, just output it.
    if [[ $RCPATH -nt $MENU_CACHE || $XBKPATH -nt $MENU_CACHE ]]; then
        :> "$KBTEXT" # XBKSCRIPT does not blank file, it appends
        [[ $using_openbox = true ]] && "${OBSCRIPT_CMD[@]}"
        [[ $using_xbindkeys = true ]] && "${XBKSCRIPT_CMD[@]}"

        OB_Wmenu=''
        OB_Rmenu=''
        OB_XBKmenu=''
        parsing_xbk=false
        mapfile -t lines < "$KBTEXT"
        for i in "${!lines[@]}"; do
            if [[ ${lines[i]} = '#### xbindkeys keybinds ####' ]];then
                parsing_xbk=true
                continue
            fi
            line=( ${lines[i]} )
            key=${line[1]}
            cmd="${line[*]:2}"
            curItem=$(printf "%-20s %s" "$key" "$cmd")
            if [[ ${line[0]} = o ]];then
                OB_Wmenu+=$(menuItem "$curItem" "echo >/dev/null 2>&1")$'\n'
            elif  [[ ${line[0]} = x ]];then
                if [[ $parsing_xbk = true ]];then
                    [[ ${lines[i-1]} = \#* ]] && curItem=$(printf "%-20s %s" "$key" "${lines[i-1]#\# }")
                    OB_XBKmenu+=$(menuItem "$curItem" "$cmd")$'\n'
                else
                    OB_Rmenu+=$(menuItem "$curItem" "$cmd")$'\n'
                fi
            fi
        done

        menuStart > "$MENU_CACHE"

        [[ -n $OB_Wmenu ]] && {
            menuSeparator "Openbox window commands"
            echo "${OB_Wmenu}"
        } >> "$MENU_CACHE"
        [[ -n $OB_Rmenu ]] && {
            menuSeparator
            menuSeparator "Openbox 'Run' commands"
            echo "${OB_Rmenu}"
        } >> "$MENU_CACHE"
        [[ -n $OB_XBKmenu ]] && {
            menuSeparator
            menuSeparator "Xbindkeys 'Run' commands"
            echo "${OB_XBKmenu}"
        } >> "$MENU_CACHE"

        menuEnd >> "$MENU_CACHE"
    fi

    cat "$MENU_CACHE"

    ;;

'')
    # pipemenu
    menuStart
    menuItem "Display Keybinds in new window" "bl-kb-pipemenu --gui"
    echo '        <menu execute="bl-kb-pipemenu --kbmenu" id="Display keybinds" label="Display Keybinds in menu"/>'
#    menuSeparator
#    [[ $using_xbindkeys = true ]] && menuItem "Backup and Edit .xbindkeysrc" "bl-kb-pipemenu --xbkedit"
#    [[ $using_openbox = true ]] && menuItem "Backup and Edit ${RCPATH##*/}" "bl-kb-pipemenu --rcedit"
    menuEnd
    ;;
*)
    echo "$0: $1: this option not supported" >&2
    exit 1
    ;;
esac

exit 0

Also moved the two helper scripts which this pipemenu uses, bl-kb and bl-xbk, from bunsen-utilities to bunsen-pipemenus. I don't think all that many people will be using them separate from the pipemenu and it means edits/upgrades are kept inside one package.

Tomorrow I'll upload bunsen-pipemenus, bunsen-utilities and bunsen-configs, and see if there's anything else which needs doing before pushing out beta2 builds for amd64 and i386.


...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), now on Bluesky, there's also some GitStuff )

Introduction to the Bunsenlabs Boron Desktop

Offline

#6 2023-10-04 05:40:23

johnraff
nullglob
From: Nagoya, Japan
Registered: 2015-09-09
Posts: 12,558
Website

Re: The keybinds pipemenu

Found that in the case of bl-kb-pipemenu it's not necessary to run the whole pipemenu at once - you can pull out the various options as separate menu items in the parent folder. This means fewer subfolders (they annoy me) and the items become searchable if they're not in a pipemenu. Try putting this code in menu > User Settings > Keybinds (or anywhere):

Display Keybinds in menu, ^pipe(jgmenu_run ob --cmd="bl-kb-pipemenu --kbmenu" --tag="bl-kb-pipemenu-kbmenu")
Display Keybinds in new window,bl-kb-pipemenu --gui
^sep()
Backup and Edit openbox rc file,bl-kb-pipemenu --rcedit
Backup and Edit .xbindkeysrc,bl-kb-pipemenu --xbkedit
Reconfigure openbox,openbox --reconfigure
Restart xbindkeys,sh -c 'pkill -x xbindkeys; xbindkeys_autostart'
^sep(Help)
man xbindkeys,x-terminal-emulator -T 'man xbindkeys' -e man xbindkeys

...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), now on Bluesky, there's also some GitStuff )

Introduction to the Bunsenlabs Boron Desktop

Offline

Board footer

Powered by FluxBB