{"id":123,"date":"2021-10-19T10:17:50","date_gmt":"2021-10-19T02:17:50","guid":{"rendered":"https:\/\/www.osvlabs.com\/blog\/?p=123"},"modified":"2021-11-01T16:42:56","modified_gmt":"2021-11-01T08:42:56","slug":"%e6%8e%a8%e8%8d%90%e4%b8%80%e6%ac%be-mac-%e4%b8%8a%e7%9a%84%e8%bd%af%e4%bb%b6%ef%bc%9ahammerspoon","status":"publish","type":"post","link":"https:\/\/blog.osvlabs.com\/?p=123","title":{"rendered":"\u63a8\u8350\u4e00\u6b3e Mac \u5e73\u53f0\u7684\u81ea\u52a8\u5316\u8f6f\u4ef6\uff1aHammerspoon"},"content":{"rendered":"<p>Hammerspoon \u662f\u4e00\u6b3e\u4ee5\u81ea\u52a8\u5316\u95fb\u540d\u7684\u5f00\u6e90\u3001\u514d\u8d39\u7684\u8f6f\u4ef6\uff0c\u5c06\u7cfb\u7edf\u5e95\u5c42\u7684\u4e00\u4e9b\u63a5\u53e3\u5c01\u88c5\u6210\u7b80\u5355\u6613\u7528\u7684 <a href=\"https:\/\/www.hammerspoon.org\/docs\/index.html\">Lua Api<\/a>\uff0c\u53ef\u4ee5\u81ea\u5df1\u5b9e\u73b0\u4e00\u4e9b\u4e0d\u592a\u79bb\u8c31\u7684\u529f\u80fd\uff0cgithub \u4e0a\u4e5f\u6709\u8bb8\u591a\u811a\u672c\u3002<\/p>\n<p>\u6bd4\u8f83\u5e38\u7528\u7684\u529f\u80fd\u6709\u8fd9\u4e9b:<\/p>\n<ul>\n<li>\u663e\u793a\u7c98\u8d34\u677f\u8bb0\u5f55<\/li>\n<li>\u7a97\u53e3\u5927\u5c0f\u7ba1\u7406\uff08\u5feb\u6377\u952e\uff09<\/li>\n<li>\u97f3\u91cf\u8c03\u8282\uff08\u5feb\u6377\u952e\uff09<\/li>\n<li>\u81ea\u52a8\u5207\u6362\u8f93\u5165\u6cd5<\/li>\n<li>\u9f20\u6807\u8fde\u70b9<\/li>\n<li>\u5feb\u6377\u952e\u5524\u9192app<\/li>\n<li>\u7b49\u7b49<\/li>\n<\/ul>\n<hr \/>\n<p>\u4e0b\u9762\u5177\u4f53\u4ecb\u7ecd\u4e00\u4e2a\u6bd4\u8f83\u5e38\u7528\u7684\u529f\u80fd\uff1a<strong>\u81ea\u52a8\u5207\u6362\u8f93\u5165\u6cd5<\/strong><br \/>\n\u81ea\u52a8\u5207\u6362\u8f93\u5165\u6cd5\uff0c\u987e\u540d\u601d\u4e49\uff0c\u5728\u5207\u6362\u5230\u4e0d\u540c\u7684 app \u65f6\u81ea\u52a8\u5207\u6362\u4e0d\u540c\u7684\u8f93\u5165\u6cd5\uff0c\u7701\u53bb\u6309\u4e0b\u5207\u6362\u8f93\u5165\u6cd5\u6216\u8005\u5927\u5c0f\u5199\u3002<\/p>\n<p>\u4f7f\u7528\u573a\u666f\uff1a<\/p>\n<ul>\n<li>\u5728 QQ\uff0c\u5fae\u4fe1 \u804a\u5929\u65f6\u9700\u8981\u4f7f\u7528\u4e2d\u6587\u8f93\u5165\u6cd5<\/li>\n<li>\u5728 IDE\uff0cSlack\uff0cTerminal \u7b49\u9700\u8981\u4f7f\u7528\u82f1\u6587\u8f93\u5165<\/li>\n<\/ul>\n<p>\u5b89\u88c5\u6559\u7a0b\uff1a<\/p>\n<ol>\n<li>\u5b98\u7f51\u4e0b\u8f7d<\/li>\n<li>\u6253\u5f00<\/li>\n<li>\u6254\u5230 Application \u91cc<\/li>\n<li>\u5728 <code>~\/.hammerspoon\/init.lua<\/code> \u91cc\u5199\u4ee3\u7801\u6216\u8005\u70b9\u51fb\u83dc\u5355\u680f\u91cc\u7684\u9524\u5b50\uff0c\u70b9 <code>open config<\/code><\/li>\n<\/ol>\n<p>Demo \u5982\u4e0b\uff1a<\/p>\n<blockquote>\n<p>\u53ef\u4ee5\u5355\u72ec\u521b\u5efa\u4e00\u4e2alua \u6587\u4ef6\u6bd4\u5982\u521b\u5efa\u4e2a\u6587\u4ef6\u5939\u53eb <code>ime<\/code>, \u5728<code>ime<\/code>\u6587\u4ef6\u5939\u4e0b\u521b\u5efa\u4e00\u4e2a <code>ime.lua<\/code>\u6587\u4ef6\uff0c\u5728<code>init.lua<\/code>\u91cc\u5f15\u5165: <code>require &quot;ime.ime&quot;<\/code><\/p>\n<\/blockquote>\n<pre><code>-- \u4e2d\u6587\u8f93\u5165\u6cd5\nlocal function Chinese()\n    -- rime\u8f93\u5165\u6cd5\n    hs.keycodes.currentSourceID(&quot;im.rime.inputmethod.Squirrel.Rime&quot;)\n    -- \u7cfb\u7edf\u9ed8\u8ba4\u4e2d\u6587\u53cc\u62fc\u8f93\u5165\u6cd5\n    -- hs.keycodes.currentSourceID(&quot;com.apple.inputmethod.SCIM.Shuangpin&quot;)\nend\n\n-- \u82f1\u6587\u8f93\u5165\u6cd5\nlocal function English()\n    -- \u7cfb\u7edf\u9ed8\u8ba4\u82f1\u6587\u8f93\u5165\u6cd5\n    hs.keycodes.currentSourceID(&quot;com.apple.keylayout.ABC&quot;)\nend\n\n-- \u54c8\u5229\u8def\u4e9a\u82f1\u6587\u8f93\u5165\u6cd5\nlocal function Hlly()\n    hs.keycodes.currentSourceID(&quot;github.dongyuwei.inputmethod.hallelujahInputMethod&quot;)\nend\n\n-- app to expected ime config\nlocal app2Ime = {\n    {&#039;\/Applications\/Xcode.app&#039;, &#039;English&#039;},\n    -- {&#039;\/Applications\/Google Chrome.app&#039;, &#039;Chinese&#039;},\n    -- {&#039;\/System\/Library\/CoreServices\/Finder.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/DingTalk.app&#039;, &#039;Chinese&#039;},\n    -- {&#039;\/Applications\/Kindle.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/NeteaseMusic.app&#039;, &#039;Chinese&#039;},\n    {&#039;\/Applications\/WeChat.app&#039;, &#039;Chinese&#039;},\n    {&#039;\/Applications\/System Preferences.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/Dash.app&#039;, &#039;English&#039;},\n    -- {&#039;\/Applications\/MindNode.app&#039;, &#039;Chinese&#039;},\n    -- {&#039;\/Applications\/Preview.app&#039;, &#039;Chinese&#039;},\n    {&#039;\/Applications\/Fork.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/wechatwebdevtools.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/Sketch.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/Visual Studio Code.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/TablePlus.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/GitUp.app&#039;, &#039;English&#039;},\n    -- IDE \u662f\u4f7f\u7528 JetBrains Toolbox \u5b89\u88c5\u7684\uff0capp \u8def\u5f84\u4e0d\u5728 Application\/ \u4e0b\uff0c\u6709\u4e9b\u81ea\u52a8\u5207\u6362\u8f93\u5165\u6cd5\u7684\u5de5\u5177\u4f1a\u5931\u6548\uff0c\u6240\u4ee5\u4f7f\u7528\u5168\u8def\u5f84\n    {&#039;\/Users\/wanglikun\/Library\/Application Support\/JetBrains\/Toolbox\/apps\/AppCode\/ch-0\/212.5284.45\/AppCode.app&#039;, &#039;English&#039;},\n    {&#039;\/Users\/wanglikun\/Library\/Application Support\/JetBrains\/Toolbox\/apps\/PhpStorm\/ch-0\/212.5457.49\/PhpStorm.app&#039;, &#039;English&#039;},\n    {&#039;\/Users\/wanglikun\/Library\/Application Support\/JetBrains\/Toolbox\/apps\/WebStorm\/ch-0\/211.6693.108\/WebStorm.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/Xcode12.app&#039;, &#039;English&#039;},\n    {&#039;\/Users\/wanglikun\/Library\/Application Support\/JetBrains\/Toolbox\/apps\/AndroidStudio\/ch-0\/203.7678000\/Android Studio.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/eDEX-UI.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/QQ.app&#039;, &#039;Chinese&#039;},\n    {&#039;\/Applications\/Fork.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/iTerm.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/Unity\/Hub\/Editor\/2019.4.14f1c1\/Unity.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/Visual Studio.app&#039;, &#039;English&#039;},\n    {&#039;\/Applications\/Sublime Text.app&#039;, &#039;English&#039;},\n}\n\n-- \u5207\u6362\u8f93\u5165\u6cd5\nfunction updateFocusAppInputMethod()\n    local focusAppPath = hs.window.frontmostWindow():application():path()\n    for index, app in pairs(app2Ime) do\n        local appPath = app[1]\n        local expectedIme = app[2]\n\n        if focusAppPath == appPath then\n            hs.alert.closeAll()\n            if expectedIme == &#039;English&#039; then\n                English()\n                -- hs.alert.show(&#039;English&#039;, {}, 0.5)\n            else\n                Chinese()\n                -- hs.alert.show(&#039;\u4e2d\u6587&#039;, {}, 0.5)\n            end\n            break\n        end\n    end\nend\n\n-- \u6740\u6389\u54c8\u5229\u8def\u4e9a\u8f93\u5165\u6cd5\u8fdb\u7a0b\uff0c\u56e0\u4e3a\u4f7f\u7528\u54c8\u5229\u8def\u4e9a\u8f93\u5165\u6cd5\u65f6\u5207\u6362\u684c\u9762\u7684\u65f6\u5019\u4f1a\u6709\u4e00\u4e2a\u9ed1\u8272\u5f39\u7a97\u663e\u793a1\u79d2\u624d\u6d88\u5931\uff0c\u5b98\u65b9\u56de\u590d\u662f\u7cfb\u7edfbug\uff0c\u6740\u6389\u8fdb\u7a0b\u5c31\u4e0d\u4f1a\u663e\u793a\u4e86\n-- \u6b64\u5904\u6709bug\uff0c\u6740\u6389\u8fdb\u7a0b\u6709\u5ef6\u65f6\uff0c\u5076\u5c14\u6740\u6389\u8fd8\u663e\u793a\u9ed1\u8272\u5f39\u7a97\nfunction killHLLL() \n    local hlll = hs.application.get(&#039;hallelujah&#039;)\n    if hlll ~= nil then\n        hs.application.get(&#039;hallelujah&#039;):kill9()\n    end\nend\n\n-- helper hotkey to figure out the app path and name of current focused window\n-- \u7ed1\u5b9a\u5feb\u6377\u952e\uff0c\u83b7\u53d6\u5f53\u524dapp\u7684\u8def\u5f84\u548c\u8f93\u5165Source ID\uff0c\u5e76\u81ea\u52a8\u6dfb\u52a0\u5230\u7c98\u8d34\u677f\nhs.hotkey.bind({&#039;ctrl&#039;, &#039;option&#039;}, &quot;.&quot;, function()\n    hs.alert.show(&quot;App path:        &quot;\n    ..hs.window.focusedWindow():application():path()\n    ..&quot;\\n&quot;\n    ..&quot;App name:      &quot;\n    ..hs.window.focusedWindow():application():name()\n    ..&quot;\\n&quot;\n    ..&quot;IM source id:  &quot;\n    ..hs.keycodes.currentSourceID())\n    hs.pasteboard.setContents(&#039;{\\&#039;&#039; .. hs.window.focusedWindow():application():path() .. &#039;\\&#039;, \\&#039;\\&#039;},&#039;)\n    -- hs.pasteboard.setContents(hs.keycodes.currentSourceID())\nend)\n\n-- \u7ed1\u5b9a\u5feb\u6377\u952e\uff0c\u5207\u6362\u5f53\u524d\u8f93\u5165\u6cd5\u4e3a\u54c8\u5229\u8def\u4e9a\u8f93\u5165\u6cd5(\u8fd9\u91cc\u662f\u60f3\u8981\u624b\u52a8\u5207\u6362\u8f93\u5165\u6cd5\u7684\u65f6\u5019\u7528\u7684)\nhs.hotkey.bind({&#039;cmd&#039;, &#039;shift&#039;}, &quot;0&quot;, function()\n    Hlly()\nend)\n\n-- \u7ed1\u5b9a\u5feb\u6377\u952e\uff0c\u5207\u6362\u5f53\u524d\u8f93\u5165\u6cd5\u4e3a\u82f1\u6587\u8f93\u5165\u6cd5(\u8fd9\u91cc\u662f\u60f3\u8981\u624b\u52a8\u5207\u6362\u8f93\u5165\u6cd5\u7684\u65f6\u5019\u7528\u7684)\nhs.hotkey.bind({&#039;cmd&#039;, &#039;shift&#039;}, &quot;9&quot;, function()\n    English()\nend)\n\n-- \u7ed1\u5b9a\u5feb\u6377\u952e\uff0c\u5207\u6362\u5f53\u524d\u8f93\u5165\u6cd5\u4e3a\u4e2d\u6587\u8f93\u5165\u6cd5(\u8fd9\u91cc\u662f\u60f3\u8981\u624b\u52a8\u5207\u6362\u8f93\u5165\u6cd5\u7684\u65f6\u5019\u7528\u7684)\nhs.hotkey.bind({&#039;cmd&#039;, &#039;shift&#039;}, &quot;8&quot;, function()\n    Chinese()\nend)\n\n-- \u7ed1\u5b9a\u5feb\u6377\u952e\uff0c\u6740\u6389\u54c8\u5229\u8def\u4e9a\u8f93\u5165\u6cd5\u8fdb\u7a0b\nhs.hotkey.bind({&#039;cmd&#039;, &#039;shift&#039;}, &quot;7&quot;, function()\n    killHLLL()\nend)\n\n-- Handle cursor focus and application&#039;s screen manage.\nfunction applicationWatcher(appName, eventType, appObject)\n    if (eventType == hs.application.watcher.activated) then\n        updateFocusAppInputMethod()\n    end\nend\n\nappWatcher = hs.application.watcher.new(applicationWatcher)\nappWatcher:start()\n<\/code><\/pre>\n<p>\u5199\u5b8c\u4ee3\u7801\u4e4b\u540e\u70b9\u51fb\u83dc\u5355\u680f\u91cc\u7684\u9524\u5b50\uff0c\u70b9\u51fb <code>reload config<\/code> \u5982\u679c\u4ee3\u7801\u4e0d\u62a5\u9519\u5c31\u53ef\u4ee5\u751f\u6548\u3002<\/p>\n<p>\u53ef\u4ee5\u5728\u9524\u5b50\u91cc\u70b9\u51fb <code>console<\/code>, \u5728\u91cc\u9762\u6d4b\u8bd5\u4ee3\u7801<\/p>\n<p>Hammerspoon \u63d0\u4f9b\u4e86\u5f88\u591a\u7684 api\uff0c\u53ef\u4ee5\u81ea\u5df1\u5b9e\u73b0\u7279\u522b\u591a\u7684\u529f\u80fd\uff0c\u5e0c\u671b\u5927\u5bb6\u591a\u591a\u5206\u4eab.<\/p>\n<blockquote>\n<p>\u5f69\u86cb\uff1a\u4e00\u4e2a\u82f1\u6587\u8f93\u5165\u6cd5<a href=\"https:\/\/github.com\/dongyuwei\/hallelujahIM\">\u54c8\u5229\u8def\u4e9a\u8f93\u5165\u6cd5<\/a> <\/p>\n<\/blockquote>\n<p><img src=\"https:\/\/raw.githubusercontent.com\/dongyuwei\/hallelujahIM\/master\/snapshots\/suggestions2.png\" alt=\"\" \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hammerspoon \u662f\u4e00\u6b3e\u4ee5\u81ea\u52a8\u5316\u95fb\u540d\u7684\u5f00\u6e90\u3001\u514d\u8d39\u7684\u8f6f\u4ef6\uff0c\u5c06\u7cfb\u7edf\u5e95\u5c42\u7684\u4e00\u4e9b\u63a5\u53e3\u5c01\u88c5\u6210\u7b80\u5355\u6613\u7528\u7684 Lua Api\uff0c\u53ef\u4ee5\u81ea\u5df1\u5b9e\u73b0\u4e00\u4e9b\u4e0d\u592a\u79bb\u8c31\u7684\u529f\u80fd\uff0cgithub \u4e0a\u4e5f\u6709\u8bb8\u591a\u811a\u672c\u3002 \u6bd4\u8f83\u5e38\u7528\u7684\u529f\u80fd\u6709\u8fd9\u4e9b\u2026<\/p>\n","protected":false},"author":4,"featured_media":131,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":5}},"_links":{"self":[{"href":"https:\/\/blog.osvlabs.com\/index.php?rest_route=\/wp\/v2\/posts\/123"}],"collection":[{"href":"https:\/\/blog.osvlabs.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.osvlabs.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.osvlabs.com\/index.php?rest_route=\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.osvlabs.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=123"}],"version-history":[{"count":2,"href":"https:\/\/blog.osvlabs.com\/index.php?rest_route=\/wp\/v2\/posts\/123\/revisions"}],"predecessor-version":[{"id":140,"href":"https:\/\/blog.osvlabs.com\/index.php?rest_route=\/wp\/v2\/posts\/123\/revisions\/140"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.osvlabs.com\/index.php?rest_route=\/wp\/v2\/media\/131"}],"wp:attachment":[{"href":"https:\/\/blog.osvlabs.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=123"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.osvlabs.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=123"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.osvlabs.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}