commit 87769bb02d663f0b45c473a007bd63e124a2e845 Author: Ryan Thomas Date: Fri Jan 7 16:18:49 2022 -0500 Uploaded scripts. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a52cbb0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +old/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..de8e440 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +~rts/oarion7 + +Shell script to control the awesome wm systray from the keyboard using vimium-like number hints. Written for awesome v4.3. + +When "exec.sh" is executed, the included files are referenced to display an interactive number hint widget next to each icon in the system tray. The number entered on the keyboard sends a right click to the corresponding icon. (To left-click instead, press Ctrl+L before selecting a number. Ctrl+O will do the same for a "hovering" functionality, but it may be less reliable.) The mouse pointer is then restored to its original position. + +The script uses "awesome-client" to load and call functions from the included lua file. A consequent limitation is the inability to grab data in the shell from lua return statements directly. My approach therefore requires some use of global variables and functions. + +We also make use of rofi to function as a keygrabber to receive number inputs with the "-auto-select" flag so that hitting "return" is never necessary except when there are more than 9 icons to choose from. Finally, mouse.coords, iocane (https://github.com/TrilbyWhite/Iocane), and xdotool are used to simulate mouse actions. + +Because the number hints are displayed in the form of a popup widget, visibility of rofi is redundant. An "invisible" theme file has been provided, and assumes a compositor is running. This proved more reliable for us in our tests than enabling rofi's -normal-window mode and attempting to hide the window in rules, and the temporary reassignment of key commands in awesome was one of the relevant matters beyond our immediate grasp. It may also be possible in place of widgets to configure rofi to display options with fixed widths that could be tied to the geometry of the systray icons, but this would become unintuitive in any use case where multi-digit numbers are necessary. + +We have been using the following function in global keys to make this as seamless as possible in our environment, where incidentally the systray is hidden by default until needed. + +-- Paste New Code Here. Include also the invisible by default line in widgets module. + +Here one key combination is used to hide the systray if it is visible, or if not, to display the systray along with the number hints. Sometimes one may wish only to see what is running in the systray rather than actually click on anything in it, immediately pressing the same key combination to hide it. However, "Super+S" will never be received by the window manager when rofi (in its normal, non-windowed state) is in use. The "exit_key" variable, which should be assigned a single letter as its value, is added by the script to the list of valid options, which are themselves all numbers. The default value is "s" to pair with "Super+S". If we hit Super+S while rofi is active, rofi will read it as simply "s", which later triggers the script to clear the value. This allows us to hit the same combination key to cancel/hide the systray and hints as we do to display them. To disable this, exit_key can be set to null or simply commented out. Rofi can always be escaped via the "Escape" key, which should yield exactly the same result. + +Additionally, note that obtaining the geometry of the system tray is not directly references in the awesome API for a reason; sometimes it is incorrect and requires a second execution to display correctly. If the same shortcut key is elected to display systray hints as well as to hide them, and one encounters a situation where several icons appear but only one or two hints, a series of two additional taps of the key shortcut is usually sufficient to resolve it right away. + + + + #### #### #### #### ## ### ### ## # # ### #### + # # # # # # # # # # # ## # # # + ### #### ### ### #### ### ### #### # # # # ## ### + # # # # # # # # # # # # ## # # # + # # ## #### #### # # #### #### # # # # ### #### + + diff --git a/core.lua b/core.lua new file mode 100644 index 0000000..66cbd70 --- /dev/null +++ b/core.lua @@ -0,0 +1,144 @@ +found_widget_geometry = nil +local awful = require("awful") +local gears = require("gears") +local b = require("beautiful") +local s + +function hide_systray_hints() + if systray_hints_widget then + systray_hints_widget.visible = false + end +end + +function sys_hints_hide_systray() + local s + awful.screen.connect_for_each_screen(function(screen) + if screen.systray then s = screen end + end) + s.systray.visible = false +end + +function systray_show_hints(x, y, w, count) + + local sys_hints_geo_x = x or 1390 + local sys_hints_geo_y = y or 0 + local sys_hints_icon_width = w or 28 + local sys_hints_icon_count = count or 8 + + local systray_hints_font = b.systray_hints_font or b.font + local systray_hints_bg = b.systray_hints_bg or "#ff00ff" + local systray_hints_fg = b.systray_hints_fg or "#000000" + + sys_hints_icon_width = sys_hints_icon_width - 2 + --Subtract 2px from icon width for left/right margins + + if sys_hints_geo_y >= 100 then + sys_hints_geo_y = sys_hints_geo_y - sys_hints_icon_width + else sys_hints_geo_y = sys_hints_geo_y + sys_hints_icon_width + --Decide if hints should display above or below systray icons. + end + + awful.screen.connect_for_each_screen(function(screen) + if screen.systray.visible == true then s = screen end + end) + + if s == nil then return nil end + + hide_systray_hints() --hide if already displayed + + local num_rows = sys_hints_icon_count + local sys_hints_list = {} + local systray_widget_list = {} + + local widget_shape = function(cr, width, height) + gears.shape.rounded_rect(cr, width, height, 5) + end + + local var = {} + for i = 1, num_rows do table.insert(sys_hints_list, tostring(i)) end + for k, v in pairs(sys_hints_list) do + + var["text_" .. v] = wibox.widget.textbox(tostring(v)) + local text_alias = var["text_" .. v] + text_alias.font = systray_hints_font -- "Sans 14" + text_alias.markup = '' .. v .. '' + + local item_container_place = {} + table.insert(item_container_place, text_alias) + item_container_place.widget = wibox.container.place + + local item_container_background = {} + table.insert(item_container_background, item_container_place) + item_container_background.widget = wibox.container.background + item_container_background.bg = systray_hints_bg -- "#ff00ff" + item_container_background.forced_width = sys_hints_icon_width + item_container_background.shape = widget_shape + item_container_background.shape_border_width = 2 + item_container_background.shape_border_color = systray_hints_fg .. 33 + item_container_background.id = "systray_hint_background_item_" .. + tostring(v) + + local item_container_margin = {} + table.insert(item_container_margin, item_container_background) + item_container_margin.widget = wibox.container.margin + item_container_margin.right = 1 + item_container_margin.left = 1 + + local complete_widget = item_container_margin + table.insert(systray_widget_list, complete_widget) + + end + + systray_widget_list.layout = wibox.layout.fixed.horizontal + systray_hints_widget = awful.popup { + widget = { + screen = s, + systray_widget_list, + + layout = wibox.layout.fixed.horizontal, + }, + x = sys_hints_geo_x, + y = sys_hints_geo_y, + visible = true, + ontop = true, + bg = "#00000000", + --hide_on_right_click = true + } + +end + +local function find_widget_in_wibox(wb, wdg) + + local function get_geometry(hi) + local g = gears.matrix.transform_rectangle + local x, y, w, h = g(hi:get_matrix_to_device(), 0, 0, hi:get_size()) + local x = math.floor(x) ; local y = math.floor(y) + local w = math.floor(w) ; local h = math.floor(h) + found_widget_geometry = x .. "," .. y .. "," .. w .. "," .. h + end + + local function traverse(hi) + if hi:get_widget() == wdg then + get_geometry(hi) + return + end + for _, child in ipairs(hi:get_children()) do + -- return traverse(child) + traverse(child) + -- Others have the above line instead, but for us it returned + -- only container widgets. Removing "return" allows for an + -- additional round of recursion. + end + end + return traverse(wb._drawable._widget_hierarchy) +end + +awful.screen.connect_for_each_screen(function(screen) + if screen.systray.visible == true then s = screen end +end) + +if s == nil then return nil end +find_widget_in_wibox(s.mywibox, s.systray) +return found_widget_geometry + diff --git a/exec.sh b/exec.sh new file mode 100755 index 0000000..ef15d46 --- /dev/null +++ b/exec.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +#rts/oarion7 + +# See "README" ! Tested with awesome v4.3 + +script_path="$(dirname $(realpath "$BASH_SOURCE"))" +rofi_theme="${script_path}/invisible.rasi" #key grabber only +exit_key='s' # See "README" ! +hover_time='3' #time to hover in seconds if Ctrl+O + +deps=( awesome-client iocane rofi xdotool ) +for i in "${deps[@]}"; do command -v "$i" >/dev/null || +errors=$(printf '%s %s\n' "$errors" "$i" | sed -e 's/^ //'); done +[[ "$errors" ]] && { notify-send -u critical \ +"Veuillez corriger ces dépendances non satisfaites:" "$errors"; exit 1; } + +parse_string() { printf '%s\n' "$1" | awk '{$1=""}1' | sed 's|[ "]||g' ; } +# 'string "X"' --> 'X' + +awesome-exec() { + local file + [[ "$(realpath "$1")" == "$1" ]] && file="$1" || file="${script_path}/${1}" + [ -f "$file" ] && awesome-client 'dofile("'"$file"'")' || + { echo "Could not find ${1}."; exit 1; } +} + +mouse_location() { + [[ "$1" == "save" ]] && + { awesome-client 'sys_hints_saved_coords = mouse.coords({x = x, y = y})'; return; } + [[ "$1" == "restore" ]] && + awesome-client 'mouse.coords({x = sys_hints_saved_coords.x, y = sys_hints_saved_coords.y}, true)' +} + +hide_systray_hints() { awesome-client 'hide_systray_hints()' >/dev/null ; } + +hide_systray() { awesome-client 'sys_hints_hide_systray()' >/dev/null ; } + +seq_with_exit_key() { (printf '%s\n%s\n' "$exit_key" "$(seq $1)") ; } + +mouse_to_icon() { + [[ "$1" == "1" ]] && factor='0' || factor=$(("$1" - 1)) + target_position_x=$(( $first_icon_x + ( "$icon_width" * "$factor" ) )) + iocane -c "${target_position_x},${icons_y}" +} + +get_choice() { + choice=$(seq_with_exit_key "$icon_count" | rofi \ + -kb-custom-1 "Ctrl+l" -kb-custom-2 "Ctrl+o" -dmenu -theme "$rofi_theme" \ + -no-custom -auto-select 2>/dev/null ) + code="$?" +} + +awesome-exec "core.lua" +data=$(awesome-client "return found_widget_geometry") + +[[ -z "$data" ]] && { echo "Geometery data not found."; exit 1; } + +IFS=',' read -r -a array <<< "$(parse_string "$data")" +[[ -n "${array[4]}" ]] || [[ -z "${array[3]}" ]] && { echo "Invalid data: $data"; exit 1; } + +x="${array[0]}" ; y="${array[1]}" ; w="${array[2]}" ; h="${array[3]}" +icon_count=$((( $w - ( $w % $h )) / $h + 1 )) +icon_width=$(( $w / $icon_count )) +half_icon=$(( $icon_width / 2 )) +first_icon_x=$(( $x + $half_icon )) +icons_y=$(( $y + $half_icon )) + +awesome-client 'systray_show_hints('"${x}, ${y}, ${icon_width}, ${icon_count}"')' + +mouse_location save + +mouse_button='3' #default to right click +get_choice; [[ "$code" == "10" ]] && { mouse_button='1'; get_choice; } + [[ "$code" == "11" ]] && { unset mouse_button; get_choice; } + +hide_systray_hints + +[[ "$choice" == "$exit_key" ]] && unset choice +[[ -z "$choice" ]] && { hide_systray; exit 0; } + +mouse_to_icon "$choice" && + +[[ -n "$mouse_button" ]] && xdotool click "$mouse_button" +[[ -z "$mouse_button" ]] && sleep "$hover_time" #hover only (not so reliable) + +mouse_location restore diff --git a/invisible.rasi b/invisible.rasi new file mode 100644 index 0000000..8ff16a6 --- /dev/null +++ b/invisible.rasi @@ -0,0 +1,42 @@ +/** + * ROFI Color theme + * User: Qball + * Copyright: Dave Davenport + */ +* { + background-color: rgba ( 255, 255, 255, 0 % ); + border-color: rgba ( 255, 255, 255, 0 % ); + text-color: rgba ( 255, 255, 255, 0 % ); + font: "Times New Roman 12"; +} + +#window { + anchor: north; + location: north; + width: 100%; + padding: 4px; + children: [ horibox ]; +} + +#horibox { + orientation: horizontal; + children: [ prompt, entry, listview ]; +} + +#listview { + layout: horizontal; + spacing: 5px; + lines: 100; +} + +#entry { + expand: false; + width: 10em; +} + +#element { + padding: 0px 2px; +} +#element selected { + background-color: rgba ( 255, 255, 255, 0 % ); +} diff --git a/old/README b/old/README new file mode 100644 index 0000000..da64a98 --- /dev/null +++ b/old/README @@ -0,0 +1,44 @@ +systraykeys +~rts/oarion7 + + #### #### #### #### ## ### ### ## # # ### #### + # # # # # # # # # # # ## # # # + ### #### ### ### #### ### ### #### # # # # ## ### + # # # # # # # # # # # # ## # # # + # # ## #### #### # # #### #### # # # # ### #### + +# Displays a numbered hint widget next to each systray icon to allow use of the systray +# via the keyboard. Uses a hidden rofi instance as the keygrabber to simulate clicking. +# Uses mouse.coords, iocane, and xdotool to simulate mouse actions because the dude who +# wrote this is crazy, and his other personalities don't know anything about computers. + +# https://github.com/TrilbyWhite/Iocane + +# Defaults to right-click mode. Left-click & hover via Ctrl+L & Ctrl-O, respectively. + +# Tested with awesome v4.3 + +Displays a number hint widget next to each systray icon. The number entered on the keyboard sends a right click to the corresponding icon unless Ctrl+L (i.e. Left) is pressed first. Ctrl+O will do the same for a "hovering" functionality, but it is less reliable. Two lines calling the mouse_location function serve to save and then restore the original pointer coordinates + +Uses "awesome-client" to interact with the included lua files which are actually sourceable declarations of bash functions. Some global variables and functions were therefore necessary. Note that awesome-client does not print the results of a return statement to the terminal if that return statement is written inside a function that is called. Also, when editing those files, one must be careful to avoid single quotes when possible, or appropriately "escape" them like '"'"'this'"'"'. + +Uses rofi as temporary key grabber by calling it with the -auto-select flag so that hitting "return" is never necessary unless there are more than 9 systray icons. Because the menu options are all displayed in the widget, visibility of rofi is redundant. An "invisible" theme has been provided and enabled in-line, and assumes a compositor is running. This proved more reliable for us in our tests than enabling rofi's -normal-window mode and attempting to hide the window in rules. + +Alternative routes for future re-writes: not using 3 separate programs for mouse actions (mouse.coords, iocane, xdotool); just doing the whole damn thing in lua without bash; figure out how to temporarily grab keys in awesome natively instead of using rofi at all (seems perfectly doable, but maybe convoluted), or finally, instead of using widgets for hints and obscuring rofi, one might elect simply to configure rofi to display options with fixed widths, preferably without moving them around while multi-digit numbers are typed. + +We have been using the following function in global keys to make this as seamless as possible in our environment, where incidentally the systray is hidden by default until needed. + + awful.key({ modkey }, "s", function () + local s + awful.screen.connect_for_each_screen( + function(screen) if screen.systray then s = screen end + end) + if not s.systray.visible then + s.systray.visible = true + gears.timer.delayed_call(function() awful.util.spawn("/path/to/script/systraykeys/systraykeys.sh") end) + else + s.systray.visible = false + end + end, {description="toggle system tray", group="awesome"}), + +Here one key combination is used to hide the systray if it is visible, or if not, to display the systray along with the number hints. Sometimes one may wish only to see what is running in the systray rather than actually click on anything in it, immediately pressing the same key combination to hide it. However, "Super+S" will never be received by the window manager when rofi (in its normal, non-windowed state) is in use. The "exit_key" variable, which should be assigned a single letter as its value, is added by the script to the list of valid options, which are themselves all numbers. The default value is "s" to pair with "Super+S". If we hit Super+S while rofi is active, rofi will read it as simply "s", which later triggers the script to clear the value. This allows us to hit the same combination key to cancel/hide the systray and hints as we do to display them. To disable this, exit_key can be set to null or simply commented out. diff --git a/old/find_systray_lua b/old/find_systray_lua new file mode 100644 index 0000000..3ba9b25 --- /dev/null +++ b/old/find_systray_lua @@ -0,0 +1,38 @@ +get_systray_geometry() { + +awesome-client ' +local awful = require("awful") +local s +local found_widget_geometry + +awful.screen.connect_for_each_screen(function(screen) + if screen.systray.visible == true then s = screen end +end) + +local function find_widget_in_wibox(wb, wdg) + + local function get_geometry(hi) + local x, y, w, h = gears.matrix.transform_rectangle(hi:get_matrix_to_device(), 0, 0, hi:get_size()) + local x = math.floor(x) ; local y = math.floor(y) + local w = math.floor(w) ; local h = math.floor(h) + found_widget_geometry = x .. "," .. y .. "," .. w .. "," .. h + end + + local function traverse(hi) + if hi:get_widget() == wdg then + get_geometry(hi) + return + end + for _, child in ipairs(hi:get_children()) do + -- return traverse(child) -- Others have this line, but for us it returned only container widgets. + traverse(child) -- Removing "return" allows for an additional round of recursion, tested on v4.3 + end + end + return traverse(wb._drawable._widget_hierarchy) +end + +if s == nil then return nil end +find_widget_in_wibox(s.mywibox, s.systray) +return found_widget_geometry +' +} diff --git a/old/invisible.rasi b/old/invisible.rasi new file mode 100644 index 0000000..8ff16a6 --- /dev/null +++ b/old/invisible.rasi @@ -0,0 +1,42 @@ +/** + * ROFI Color theme + * User: Qball + * Copyright: Dave Davenport + */ +* { + background-color: rgba ( 255, 255, 255, 0 % ); + border-color: rgba ( 255, 255, 255, 0 % ); + text-color: rgba ( 255, 255, 255, 0 % ); + font: "Times New Roman 12"; +} + +#window { + anchor: north; + location: north; + width: 100%; + padding: 4px; + children: [ horibox ]; +} + +#horibox { + orientation: horizontal; + children: [ prompt, entry, listview ]; +} + +#listview { + layout: horizontal; + spacing: 5px; + lines: 100; +} + +#entry { + expand: false; + width: 10em; +} + +#element { + padding: 0px 2px; +} +#element selected { + background-color: rgba ( 255, 255, 255, 0 % ); +} diff --git a/old/old-window-mode-and-rules/find_systray_lua b/old/old-window-mode-and-rules/find_systray_lua new file mode 100644 index 0000000..23581e3 --- /dev/null +++ b/old/old-window-mode-and-rules/find_systray_lua @@ -0,0 +1,37 @@ +get_systray_geometry() { + +awesome-client ' +local awful = require("awful") +local s +local found_widget_geometry + +awful.screen.connect_for_each_screen(function(screen) + if screen.systray.visible == true then s = screen end +end) + +local function find_widget_in_wibox(wb, wdg) + + local function get_geometry(hi) + local x, y, w, h = gears.matrix.transform_rectangle(hi:get_matrix_to_device(), 0, 0, hi:get_size()) + local x = math.floor(x) ; local y = math.floor(y) + local w = math.floor(w) ; local h = math.floor(h) + found_widget_geometry = x .. "," .. y .. "," .. w .. "," .. h + end + + local function traverse(hi) + if hi:get_widget() == wdg then + get_geometry(hi) + return + end + for _, child in ipairs(hi:get_children()) do + traverse(child) + end + end + return traverse(wb._drawable._widget_hierarchy) +end + +if s == nil then return nil end +find_widget_in_wibox(s.mywibox, s.systray) +return found_widget_geometry +' +} diff --git a/old/old-window-mode-and-rules/invisible.rasi b/old/old-window-mode-and-rules/invisible.rasi new file mode 100644 index 0000000..8ff16a6 --- /dev/null +++ b/old/old-window-mode-and-rules/invisible.rasi @@ -0,0 +1,42 @@ +/** + * ROFI Color theme + * User: Qball + * Copyright: Dave Davenport + */ +* { + background-color: rgba ( 255, 255, 255, 0 % ); + border-color: rgba ( 255, 255, 255, 0 % ); + text-color: rgba ( 255, 255, 255, 0 % ); + font: "Times New Roman 12"; +} + +#window { + anchor: north; + location: north; + width: 100%; + padding: 4px; + children: [ horibox ]; +} + +#horibox { + orientation: horizontal; + children: [ prompt, entry, listview ]; +} + +#listview { + layout: horizontal; + spacing: 5px; + lines: 100; +} + +#entry { + expand: false; + width: 10em; +} + +#element { + padding: 0px 2px; +} +#element selected { + background-color: rgba ( 255, 255, 255, 0 % ); +} diff --git a/old/old-window-mode-and-rules/show_hints_lua b/old/old-window-mode-and-rules/show_hints_lua new file mode 100644 index 0000000..04fcd96 --- /dev/null +++ b/old/old-window-mode-and-rules/show_hints_lua @@ -0,0 +1,115 @@ +show_hints() { +#--called as show_hints "$x" "$y" "$icon_width" "$icon_count" + +[[ "$1" ]] && arg1="$1" || arg1='1390' +[[ "$2" ]] && arg2="$2" || arg2='0' +[[ "$3" ]] && arg3="$3" || arg3='28' +[[ "$4" ]] && arg4="$4" || arg4='8' + +#--For now, allow failure to declare parameters for testing purposes + +awesome-client ' +local awful = require("awful") +local beautiful = require("beautiful") +local gears = require("gears") +local s + +local sys_hints_geo_x = '"$arg1"' +local sys_hints_geo_y = '"$arg2"' +local sys_hints_icon_width = '"$arg3"' +local sys_hints_icon_count = '"$arg4"' + +local systray_hints_font = beautiful.systray_hints_font or beautiful.font +local systray_hints_bg = beautiful.systray_hints_bg or "#ff00ff" +local systray_hints_fg = beautiful.systray_hints_fg or "#000000" + +sys_hints_icon_width = sys_hints_icon_width - 2 --Subtract 2px from icon width for left/right margins + +if sys_hints_geo_y >= 100 then sys_hints_geo_y = sys_hints_geo_y - sys_hints_icon_width + else sys_hints_geo_y = sys_hints_geo_y + sys_hints_icon_width --Put hints above/below systray +end + +awful.screen.connect_for_each_screen(function(screen) + if screen.systray.visible == true then s = screen end +end) +if s == nil then return nil end + +function hide_systray_hints() + if systray_hints_widget then systray_hints_widget.visible = false end +end + +function sys_hints_kill_rofi(c) + if c.instance == "rofi" and systray_hints_widget and systray_hints_widget.visible then + c:kill() + client.disconnect_signal("unfocus", sys_hints_kill_rofi) + end +end + +function sys_hints_hide_systray() + local s + awful.screen.connect_for_each_screen( + function(screen) if screen.systray then s = screen end + end) + s.systray.visible = false +end + +hide_systray_hints() +client.connect_signal("unfocus", sys_hints_kill_rofi) + +local num_rows = sys_hints_icon_count +local sys_hints_list = {} +local systray_widget_list = {} + +local widget_shape = function(cr, width, height) + gears.shape.rounded_rect(cr, width, height, 5) +end + +local var = {} +for i = 1, num_rows do table.insert(sys_hints_list, tostring(i)) end +for k, v in pairs(sys_hints_list) do + var["ryan_text_" .. v] = wibox.widget.textbox(tostring(v)) + local text_alias = var["ryan_text_" .. v] + text_alias.font = systray_hints_font -- "Sans 14" + text_alias.markup = "" .. v .. "" + + local item_container_place = {} + table.insert(item_container_place, text_alias) + item_container_place.widget = wibox.container.place + + local item_container_background = {} + table.insert(item_container_background, item_container_place) + item_container_background.widget = wibox.container.background + item_container_background.bg = systray_hints_bg -- "#ff00ff" + item_container_background.forced_width = sys_hints_icon_width + item_container_background.shape = widget_shape + --item_container_background.shape_border_width = 1 + --item_container_background.shape_border_color = systray_hints_fg + item_container_background.id = "systray_hint_background_item_" .. tostring(v) + + local item_container_margin = {} + table.insert(item_container_margin, item_container_background) + item_container_margin.widget = wibox.container.margin + item_container_margin.right = 1 + item_container_margin.left = 1 + + local complete_widget = item_container_margin + table.insert(systray_widget_list, complete_widget) +end +systray_widget_list.layout = wibox.layout.fixed.horizontal + +systray_hints_widget = awful.popup { + widget = { + screen = s, + systray_widget_list, + + layout = wibox.layout.fixed.horizontal, + }, + x = sys_hints_geo_x, + y = sys_hints_geo_y, + visible = true, + ontop = true, + bg = "#00000000", + --hide_on_right_click = true +} +' +} diff --git a/old/old-window-mode-and-rules/systraykeys.sh b/old/old-window-mode-and-rules/systraykeys.sh new file mode 100755 index 0000000..f0d5149 --- /dev/null +++ b/old/old-window-mode-and-rules/systraykeys.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +#rts/oarion7 + +# * Displays number hints next to systray icons. The number entered on the keyboard sends +# right click to the corresponding icon unless Ctrl+L (i.e. Left) is pressed first. +# +# * Uses rofi as temporary key grabber by invoking it with auto-select. +# +# * Because the menu options are displays in the widget, visibility of rofi is +# unnecessary. Running rofi in normal window mode, we can put the following in +# our rules to keep it hidden: +# +# * * * Currently, trying an "invisible" theme instead of -normal-window, so these rules are *NOT* read or needed! +# +# { rule = { instance = "rofi" }, callback = function(c) +# if systray_hints_widget and systray_hints_widget.visible then +# c.name = "Select item by number key" ; c.border_width = 0; c.floating = true ; +# c.x = 1 ; c.y = 1; c.width = 1; c.height = 1 +# end end }, --c.opacity = 0 doesn't work here, so resize window & hide borders for now +# +# * I am using the following function in global keys to make this as seamless as possible in my environment +# +# awful.key({ modkey }, "s", function () +# local s +# awful.screen.connect_for_each_screen( +# function(screen) if screen.systray then s = screen end +# end) +# if not s.systray.visible then +# s.systray.visible = true +# gears.timer.delayed_call(function() awful.util.spawn("/home/ryan/scripts/systraykeys/systraykeys.sh") end) +# --awful.util.spawn_with_shell("/home/ryan/scripts/systraykeys/systraykeys.sh") +# else +# s.systray.visible = false +# if client.focus and client.focus.instance == "rofi" then +# client.focus:kill() +# end +# end +# end, {description="toggle system tray", group="awesome"}), +# +# * The if statement relating to rofi (contained in the last else section of the above) is only relevant if rofi is launched in normal window mode, as the window could be accidentally unfocused and thus needed to be killed. Super+S will never be received by the window manager if using the invisible normal (non-window state) rofi. Because of that, I have modifed the line include the seq command below to include a single letter string (the value of exit key), "s", which will be selected and sent if I hit Super+S (which rofi will read as "S", allow me to hit the same combination key to cancel as I do to start). To disable this, set exit_key value to null or comment it out + +exit_key='s' +rofi_theme='/home/ryan/scripts/systraykeys/invisible.rasi' +lua1='/home/ryan/scripts/systraykeys/find_systray_lua' # function: get_systray_geometry() +lua2='/home/ryan/scripts/systraykeys/show_hints_lua' # functions: show_hints() and hide_systray_hints() + +[ -f "$lua1" ] && source "$lua1" || { echo "Could not read $lua1"; exit 1; } +[ -f "$lua2" ] && source "$lua2" || { echo "Could not read $lua2"; exit 1; } + +deps=( awesome-client iocane rofi xdotool ) #https://github.com/TrilbyWhite/Iocane +for i in "${deps[@]}"; do command -v "$i" >/dev/null || errors=$(printf '%s %s\n' "$errors" "$i" | sed -e 's/^ //'); done +[[ "$errors" ]] && { notify-send -u critical "Veuillez corriger ces dépendances non satisfaites:" "$errors"; exit 1; } + +data=$(get_systray_geometry) ; [[ -z "$data" ]] && { echo "Geometery data not found."; exit 0; } + +parse_string() { printf '%s\n' "$1" | awk '{$1=""}1' | sed 's|[ "]||g' ; } # 'string "X"' --> 'X' + +mouse_location() { + [[ "$1" == "save" ]] && + { awesome-client 'sys_hints_saved_coords = mouse.coords({x = x, y = y})'; return; } + [[ "$1" == "restore" ]] && + awesome-client 'mouse.coords({x = sys_hints_saved_coords.x, y = sys_hints_saved_coords.y}, true)' +} + +#hide_systray() { awesome-client 'require("awful.screen").focused().systray.visible = false' >/dev/null ; } +hide_systray() { awesome-client 'sys_hints_hide_systray()' >/dev/null ; } + +seq_with_exit_key() { (printf '%s\n%s\n' "$exit_key" "$(seq $1)") ; } + +mouse_to_icon() { + [[ "$1" == "1" ]] && factor='0' || factor=$(("$1" - 1)) + target_position_x=$(( $first_icon_x + ( "$icon_width" * "$factor" ) )) + iocane -c "${target_position_x},${icons_y}" +} + +get_choice() { + choice=$(seq_with_exit_key "$icon_count" | rofi \ + -kb-custom-1 "Ctrl+l" -kb-custom-2 "Ctrl+o" -dmenu -theme "$rofi_theme" -no-custom -auto-select 2>/dev/null ) + # Change -theme "$rofi_theme" and add -normal-window if desired. + code="$?" +} + +IFS=',' read -r -a array <<< "$(parse_string "$data")" +[[ -n "${array[4]}" ]] || [[ -z "${array[3]}" ]] && { echo "Invalid data: $data"; exit 1; } +x="${array[0]}" ; y="${array[1]}" ; w="${array[2]}" ; h="${array[3]}" +#echo "${x}x$y ${w}x$h" +icon_count=$((( $w - ( $w % $h )) / $h + 1 )) +icon_width=$(( $w / $icon_count )) +half_icon=$(( $icon_width / 2 )) +first_icon_x=$(( $x + $half_icon )) +icons_y=$(( $y + $half_icon )) +#echo "Counted $icon_count icons." + +show_hints "$x" "$y" "$icon_width" "$icon_count" + +mouse_location save + +mouse_button='3' #default to right click +get_choice; [[ "$code" == "10" ]] && { mouse_button='1'; get_choice; } + [[ "$code" == "11" ]] && { unset mouse_button; get_choice; } + +awesome-client 'hide_systray_hints()' >/dev/null + +[[ "$choice" == "$exit_key" ]] && unset choice +[[ -z "$choice" ]] && { hide_systray; exit 0; } + +awesome-client 'client.disconnect_signal("unfocus", sys_hints_kill_rofi)' +#needed only for "-normal-window" mode, if you comment out, then also remove the connect command from $lua2 file + +mouse_to_icon "$choice" && + +[[ -n "$mouse_button" ]] && xdotool click "$mouse_button" + +[[ -z "$mouse_button" ]] && sleep 3 #hover only (this is not that reliable) + +mouse_location restore diff --git a/old/show_hints_lua b/old/show_hints_lua new file mode 100644 index 0000000..e424d5e --- /dev/null +++ b/old/show_hints_lua @@ -0,0 +1,108 @@ +show_hints() { +#--called as show_hints "$x" "$y" "$icon_width" "$icon_count" + +[[ "$1" ]] && arg1="$1" || arg1='1390' +[[ "$2" ]] && arg2="$2" || arg2='0' +[[ "$3" ]] && arg3="$3" || arg3='28' +[[ "$4" ]] && arg4="$4" || arg4='8' + +#--For now, allow failure to declare parameters for testing purposes + +awesome-client ' +local awful = require("awful") +local beautiful = require("beautiful") +local gears = require("gears") +local s + +local sys_hints_geo_x = '"$arg1"' +local sys_hints_geo_y = '"$arg2"' +local sys_hints_icon_width = '"$arg3"' +local sys_hints_icon_count = '"$arg4"' + +local systray_hints_font = beautiful.systray_hints_font or beautiful.font +local systray_hints_bg = beautiful.systray_hints_bg or "#ff00ff" +local systray_hints_fg = beautiful.systray_hints_fg or "#000000" + +sys_hints_icon_width = sys_hints_icon_width - 2 --Subtract 2px from icon width for left/right margins + +if sys_hints_geo_y >= 100 then sys_hints_geo_y = sys_hints_geo_y - sys_hints_icon_width + else sys_hints_geo_y = sys_hints_geo_y + sys_hints_icon_width --Put hints above/below systray +end + +local s +awful.screen.connect_for_each_screen(function(screen) + if screen.systray.visible == true then s = screen end +end) +if s == nil then return nil end + +function hide_systray_hints() + if systray_hints_widget then systray_hints_widget.visible = false end +end + +function sys_hints_hide_systray() + local s + awful.screen.connect_for_each_screen( + function(screen) if screen.systray then s = screen end + end) + s.systray.visible = false +end + +hide_systray_hints() --hide if already displayed + +local num_rows = sys_hints_icon_count +local sys_hints_list = {} +local systray_widget_list = {} + +local widget_shape = function(cr, width, height) + gears.shape.rounded_rect(cr, width, height, 5) +end + +local var = {} +for i = 1, num_rows do table.insert(sys_hints_list, tostring(i)) end +for k, v in pairs(sys_hints_list) do + var["text_" .. v] = wibox.widget.textbox(tostring(v)) + local text_alias = var["text_" .. v] + text_alias.font = systray_hints_font -- "Sans 14" + text_alias.markup = "" .. v .. "" + + local item_container_place = {} + table.insert(item_container_place, text_alias) + item_container_place.widget = wibox.container.place + + local item_container_background = {} + table.insert(item_container_background, item_container_place) + item_container_background.widget = wibox.container.background + item_container_background.bg = systray_hints_bg -- "#ff00ff" + item_container_background.forced_width = sys_hints_icon_width + item_container_background.shape = widget_shape + --item_container_background.shape_border_width = 1 + --item_container_background.shape_border_color = systray_hints_fg + item_container_background.id = "systray_hint_background_item_" .. tostring(v) + + local item_container_margin = {} + table.insert(item_container_margin, item_container_background) + item_container_margin.widget = wibox.container.margin + item_container_margin.right = 1 + item_container_margin.left = 1 + + local complete_widget = item_container_margin + table.insert(systray_widget_list, complete_widget) +end +systray_widget_list.layout = wibox.layout.fixed.horizontal + +systray_hints_widget = awful.popup { + widget = { + screen = s, + systray_widget_list, + + layout = wibox.layout.fixed.horizontal, + }, + x = sys_hints_geo_x, + y = sys_hints_geo_y, + visible = true, + ontop = true, + bg = "#00000000", + --hide_on_right_click = true +} +' +} diff --git a/old/systraykeys.sh b/old/systraykeys.sh new file mode 100755 index 0000000..1d084b4 --- /dev/null +++ b/old/systraykeys.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +#rts/oarion7 + +# Displays a numbered hint widget next to each systray icon to allow use of the systray +# via the keyboard. Uses a hidden rofi instance as keygrabber to receive number inputs. +# Uses mouse.coords, iocane, and xdotool to simulate mouse actions because the dude who +# wrote this is crazy, and his other personalities don't know anything about computers. + +# https://github.com/TrilbyWhite/Iocane + +# Defaults to right-click mode. Left-click & hover via Ctrl+L & Ctrl-O, respectively. + +# Tested with awesome v4.3 + +# See "README" ! + +script_path="$HOME/scripts/systraykeys/old" +rofi_theme="${script_path}/invisible.rasi" +lua1="${script_path}/find_systray_lua" #bash function get_systray_geometry +lua2="${script_path}/show_hints_lua" #bash function show_hints() + global lua functions +exit_key='s' #you can always hit Esc to exit rofi, but this can be useful! See README. +hover_time='3' #time to hover in seconds if Ctrl+O + +[ -f "$lua1" ] && source "$lua1" || { echo "Could not read $lua1"; exit 1; } +[ -f "$lua2" ] && source "$lua2" || { echo "Could not read $lua2"; exit 1; } + +deps=( awesome-client iocane rofi xdotool ) +for i in "${deps[@]}"; do command -v "$i" >/dev/null || +errors=$(printf '%s %s\n' "$errors" "$i" | sed -e 's/^ //'); done +[[ "$errors" ]] && { notify-send -u critical \ +"Veuillez corriger ces dépendances non satisfaites:" "$errors"; exit 1; } + +data=$(get_systray_geometry) +[[ -z "$data" ]] && { echo "Geometery data not found."; exit 0; } + +parse_string() { printf '%s\n' "$1" | awk '{$1=""}1' | sed 's|[ "]||g' ; } +# 'string "X"' --> 'X' + +mouse_location() { + [[ "$1" == "save" ]] && + { awesome-client 'sys_hints_saved_coords = mouse.coords({x = x, y = y})'; return; } + [[ "$1" == "restore" ]] && + awesome-client 'mouse.coords({x = sys_hints_saved_coords.x, y = sys_hints_saved_coords.y}, true)' +} + +hide_systray_hints() { awesome-client 'hide_systray_hints()' >/dev/null ; } + +hide_systray() { awesome-client 'sys_hints_hide_systray()' >/dev/null ; } + +seq_with_exit_key() { (printf '%s\n%s\n' "$exit_key" "$(seq $1)") ; } + +mouse_to_icon() { + [[ "$1" == "1" ]] && factor='0' || factor=$(("$1" - 1)) + target_position_x=$(( $first_icon_x + ( "$icon_width" * "$factor" ) )) + iocane -c "${target_position_x},${icons_y}" +} + +get_choice() { + choice=$(seq_with_exit_key "$icon_count" | rofi \ + -kb-custom-1 "Ctrl+l" -kb-custom-2 "Ctrl+o" -dmenu -theme "$rofi_theme" \ + -no-custom -auto-select 2>/dev/null ) + code="$?" +} + +IFS=',' read -r -a array <<< "$(parse_string "$data")" +[[ -n "${array[4]}" ]] || [[ -z "${array[3]}" ]] && { echo "Invalid data: $data"; exit 1; } +x="${array[0]}" ; y="${array[1]}" ; w="${array[2]}" ; h="${array[3]}" +#echo "${x}x$y ${w}x$h" +icon_count=$((( $w - ( $w % $h )) / $h + 1 )) +icon_width=$(( $w / $icon_count )) +half_icon=$(( $icon_width / 2 )) +first_icon_x=$(( $x + $half_icon )) +icons_y=$(( $y + $half_icon )) +#echo "Counted $icon_count icons." + +show_hints "$x" "$y" "$icon_width" "$icon_count" + +mouse_location save + +mouse_button='3' #default to right click +get_choice; [[ "$code" == "10" ]] && { mouse_button='1'; get_choice; } + [[ "$code" == "11" ]] && { unset mouse_button; get_choice; } + +hide_systray_hints + +[[ "$choice" == "$exit_key" ]] && unset choice +[[ -z "$choice" ]] && { hide_systray; exit 0; } + +mouse_to_icon "$choice" && + +[[ -n "$mouse_button" ]] && xdotool click "$mouse_button" +[[ -z "$mouse_button" ]] && sleep "$hover_time" #hover only (this is not that reliable) + +mouse_location restore