Repository: Hammerspoon/hammerspoon

Issue #3430: `hs.window.filter:subscribe()` causes unacceptable lag
Created: 2023-03-15T06:57:44Z
Loading hammerspoon scripts lag severly (for about 6~7 seconds) when I uncomment codes that called  hs.window.filter:subscribe() as below:
local function registerInWinHotKeys(appname, config)
	-- enable when targeted windows are focused
	windowFilter = hs.window.filter.new(false):setAppFilter(appname, { allowTitles = config.filter.allowTitles })
	-- windowFilter:subscribe(hs.window.filter.windowFocused,
	-- 	function() enableInWinHotKeys(appname, config) end)

	-- reject other windows of current application
	otherWindowFilter = hs.window.filter.new(false):setAppFilter(appname, { rejectTitles = config.filter.allowTitles })
	-- otherWindowFilter:subscribe(hs.window.filter.windowFocused, 
	-- 	function() disableInWinHotKeys(appname, config) end)

	-- disable when current application not focused
	appFilter = hs.window.filter.new(false):setAppFilter(appname)
	-- appFilter:subscribe(hs.window.filter.windowUnfocused,
	-- 	function() disableInWinHotKeys(appname, config) end)

	-- process title change of current window
	windowTitleFilter = hs.window.filter.new(false):setAppFilter(appname)
	-- windowTitleFilter:subscribe(hs.window.filter.windowTitleChanged,
	-- 	function(winObj)
	-- 		if string.match(winObj:title(), config.filter.allowTitles) then
	-- 			enableInWinHotKeys(appname, config)
	-- 		else
	-- 			disableInWinHotKeys(appname, config)
	-- 		end
	-- 	end
	--)
end
It happens on all the latest releases.

Duplicate issue #3431: `hs.axuielement:elementSearch` lag severly recently possibly out of OS version upgrading
Created: 2023-03-15T07:07:25Z
hs.axuielement:elementSearch lag severly recently, while before it worked well.
Below are different implementations of the same function by hs.applescript and hs.axuielement. The applescript implementation costs almost no time while the axuielement one costs about 10 seconds.
-- via hs.applescript
local function popupControlCenterSubPanel(panel)
  local winObj = hs.application.get("控制中心"):mainWindow()

  local enter = nil
  if isInTable(panel, {"Wi‑Fi", "专注模式", "蓝牙", "隔空投送", "屏幕镜像", "隔空播放音频"}) then
    enter = string.format([[perform action 2 of checkbox "%s" of pane]], panel)
  elseif isInTable(panel, {"显示器", "声音", "音乐.app"}) then
    enter = string.format([[perform action 1 of static text "%s" of pane]], panel)
  elseif isInTable(panel, {"键盘亮度"}) then
    enter = string.format([[perform action 1 of button "%s" of pane]], panel)
  end

  if winObj == nil then
    hs.applescript(string.format([[
      tell application "System Events" to tell process "SystemUIServer"
        set controlitems to menu bar 1 of application process "ControlCenter" of application "System Events"
        set numitems to the number of menu bar items of controlitems

        repeat with i from 1 to numitems
          set itemtitle to the value of attribute "AXTitle" of menu bar item i of controlitems
          if itemtitle begins with "控制中心" or itemtitle begins with "ControlCenter" then
            click menu bar item i of controlitems
            exit repeat
          end if
        end repeat
      end tell

      tell application "System Events"
        set pane to window 1 of application process "ControlCenter"
        %s
      end tell
    ]], enter))
  else
    local already = nil
    if isInTable(panel, {"Wi‑Fi", "专注模式", "蓝牙", "隔空投送"}) then
      already = string.format('(exists checkbox "%s" of pane) and (exists static text "%s" of pane)', panel, panel)
    elseif panel == "显示器" then
      already = string.format('exists static text "%s" of scroll area 1 of pane', panel)
    elseif isInTable(panel, {"屏幕镜像", "键盘亮度"}) then
      already = string.format('exists static text "%s" of pane', panel)
    end

    ok, ret = hs.applescript(string.format([[
      tell application "System Events"
        set pane to window 1 of application process "ControlCenter"
        if %s then
          return 0
        else if (exists checkbox "Wi‑Fi" of pane) and (exists checkbox "蓝牙" of pane) then
          %s
          return 1
        else
          set controlitems to menu bar 1 of application process "ControlCenter"
          set numitems to the number of menu bar items of controlitems

          repeat with i from 1 to numitems
            set itemtitle to the value of attribute "AXTitle" of menu bar item i of controlitems
            if itemtitle begins with "控制中心" or itemtitle begins with "ControlCenter" then
              click menu bar item i of controlitems
              exit repeat
            end if
          end repeat
        end if
      end tell

      delay 0.5
      tell application "System Events"
        set pane to window 1 of application process "ControlCenter"
        %s
      end tell
      return -1
    ]], already, enter,  enter))
    print(ret)
  end
end
-- via hs.axuielement
local function searchInControlCenterPanelAndPerform(itemTitle)
  local winUIObj = hs.axuielement.windowElement("控制中心")
  winUIObj:elementSearch(
    function (msg, result, count)
      if #result == 1 then
        -- 显示详细信息
        result[1]:performAction(result[1]:actionNames()[2])
      else
        hs.timer.doAfter(0.5, function()
          winUIObj:elementSearch(
            function (msg, result, count)
              assert(#result == 1)
              -- 显示详细信息
              result[1]:performAction(result[1]:actionNames()[2])
            end,
            function (element)
              return element.AXRole == "AXCheckBox" and element.AXTitle == itemTitle
            end
          )
        end)
      end
    end,
    function (element)
      return element.AXRole == "AXCheckBox" and element.AXTitle == itemTitle
    end
  )
end

-- assume different target item title appears simultaneously only on primary panel
local function switchPanelAndPerform(itemTitle)
  local winUIObj = hs.axuielement.windowElement("控制中心")

  winUIObj:elementSearch(
    function (msg, result, count)
      if #result == 1 then
        target = result[1]
        winUIObj:elementSearch(
          function (msg, result, count)
            if #result == 1 then
              -- both found, now in primary panel of control center
              target:performAction(target:actionNames()[2])
            else
              -- only target found, now in own subpanel of control center
              assert(#result == 0)
              hs.eventtap.keyStroke("", "Escape")
            end
          end,
          function (element)
            if itemTitle == "蓝牙" then
              return element.AXRole == "AXCheckBox" and element.AXTitle == "Wi‑Fi"
            end
            return element.AXRole == "AXCheckBox" and element.AXTitle == "蓝牙"
          end
        )
      else
        -- target not found, now in other subpanel of control center
        if itemTitle == "屏幕镜像" then
          winUIObj:elementSearch(
          function (msg, result, count)
            if #result == 1 then
              -- found, now in own subpanel of control center
              hs.eventtap.keyStroke("", "Escape")
            else
              -- none found, now in other subpanel of control center
              assert(#result == 0)
              local succ = clickRightMenuBarItem("控制中心")
              if not succ then
                return
              end
              searchInControlCenterPanelAndPerform(itemTitle)
            end
          end,
          function (element)
            return element.AXRole == "AXStaticText" and element.AXValue == "屏幕镜像"
          end
        )
          return
        end
        -- target not found, now in other subpanel of control center
        local succ = clickRightMenuBarItem("控制中心")
        if not succ then
          return
        end
        searchInControlCenterPanelAndPerform(itemTitle)
      end
    end,
    function (element)
      return element.AXRole == "AXCheckBox" and element.AXTitle == itemTitle
    end
  )
end

local function popupControlCenterSubPanel(panel)
  local winObj = hs.application.get("控制中心"):mainWindow()
  if winObj == nil then
    local succ = clickRightMenuBarItem("控制中心")
    if not succ then
      return
    end
    searchInControlCenterPanelAndPerform(panel)
  else
    switchPanelAndPerform(panel)
  end
end

Closing comment (2023-03-15T14:51:39Z): I suspect this is the same as #3430

exactly the same causes