bashxfcewmctrl

wmctrl output in json format?


what utility or command/s can be used to transform the following output:

wmctrl -l -i -p

0x01000003 -1 3208   user xfce4-panel
0x01400013 -1 3217   user Desktop
0x00e00615  0 3212   user shared - Thunar
0x0540011b  2 5263   user Firefox Developer Edition
0x00e8421b  2 3212   user scripts - Thunar

into:

[
    { "windowId": "0x01000003", "desktop": -1, "windowsPid": 3208, "label": "xfce4-panel" },
    { "windowId": "0x01400013", "desktop": -1, "windowsPid": 3217, "label": "Desktop" },
    { "windowId": "0x00e00615", "desktop":  0, "windowsPid": 3212, "label": "shared - Thunar" },
    { "windowId": "0x0540011b", "desktop":  2, "windowsPid": 5263, "label": "Firefox Developer Edition" },
    { "windowId": "0x00e8421b", "desktop":  2, "windowsPid": 3212, "label": "scripts - Thunar" }
]

?


Solution

  • Ages ago, I wrote this short script which tried to clone wmctrl but format the output for -m, -l, and -d as JSON. Try it out to see if it already works for you.

    #!/bin/sh
    
    # Fall back to default if neither `-m`, `-l`, or `-d` is the first argument
    { { { { wmctrl "$@" "${1+-pGx}"; echo $? >&3; } | case "$1" in
      
      -m*)
        jq -Rn '[inputs | capture("(?<key>.+?):\\s+(?<value>.*)")] | from_entries'
        ;;
    
      -l*)
        jq -Rn '[
          inputs | capture([
            "(?<window_ID>\\S+)",
            "(?<desktop_ID>\\S+)",
            "(?<PID>\\d+|N/A)",
            "(?<geometry>\\d+\\s+\\d+\\s+\\d+\\s+\\d+|N/A)",
            "(?<WM_CLASS>\\S+)",
            "(?<client_machine>\\w+)",
            "(?<window_title>.*)$"
          ] | join("\\s+"))
          | (.window_ID, .desktop_ID, .PID) |= (tonumber? // .)
          | (.geometry) |= [scan("\\d+") | tonumber]
          | (.WM_CLASS) /= "."
        ]'
        ;;
      
      -d*)
        jq -Rn '[
          inputs | capture([
            "(?<desktop_ID>\\S+)",
            "(?<current_desktop>-|\\*)",
            "DG:", "(?<geometry>\\d+x\\d+|N/A)",
            "VP:", "(?<viewport>\\d+,\\d+|N/A)",
            "WA:", "(?<workarea>\\d,\\d\\s+\\d+x\\d+|N/A)",
            "(?<title>.*)$"
          ] | join("\\s+"))
          | (.desktop_ID) |= (tonumber? // .)
          | (.current_desktop) |= . == "*"
          | (.geometry, .viewport, .workarea) |= [scan("\\d+") | tonumber]
        ]'
        ;;
      
      *) cat ;;
        
    esac >&4; } 3>&1; } | { read -r status; exit "$status"; } } 4>&1
    # pipefail for posix shells: https://unix.stackexchange.com/a/70675
    

    If not, here's how I would adopt it to just match your sample input while using your field names:

    wmctrl -lip | jq -Rn '[inputs | capture([
      "(?<windowId>\\S+)",
      "(?<desktop>\\S+)",
      "(?<windowsPid>\\S+)",
      "\\S+",
      "(?<label>.*)"
    ] | join("\\s+")) | (.desktop, .windowsPid) |= (tonumber? // .)]'
    
    [
      {
        "windowId": "0x01000003",
        "desktop": -1,
        "windowsPid": 3208,
        "label": "xfce4-panel"
      },
      {
        "windowId": "0x01400013",
        "desktop": -1,
        "windowsPid": 3217,
        "label": "Desktop"
      },
      {
        "windowId": "0x00e00615",
        "desktop": 0,
        "windowsPid": 3212,
        "label": "shared - Thunar"
      },
      {
        "windowId": "0x0540011b",
        "desktop": 2,
        "windowsPid": 5263,
        "label": "Firefox Developer Edition"
      },
      {
        "windowId": "0x00e8421b",
        "desktop": 2,
        "windowsPid": 3212,
        "label": "scripts - Thunar"
      }
    ]
    

    Demo