diff --git a/CHANGELOG.org b/CHANGELOG.org index 4be6f3f..478ea72 100644 --- a/CHANGELOG.org +++ b/CHANGELOG.org @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. This change [Unreleased Commits]: https://github.com/yqrashawn/GokuRakuJoudo/compare/v0.6.0..HEAD +** 0.7.0 - 2024-10-16 +*** Added +- BREAKING `software_function` support, [karabiner doc](https://karabiner-elements.pqrs.org/docs/json/complex-modifications-manipulator-definition/to/software_function/) + ** 0.6.0 - 2022-07-17 *** Fixed - BREAKING remove deps on ~watchexec~ (gokuw still use ~watchexec~) diff --git a/Makefile b/Makefile index 421e27c..04fbb7c 100644 --- a/Makefile +++ b/Makefile @@ -21,4 +21,4 @@ test-binary: local: make -f Makefile.local local-bin: - make -f Makefile.local bin \ No newline at end of file + make -f Makefile.local bin diff --git a/src/karabiner_configurator/data.clj b/src/karabiner_configurator/data.clj index 66b9665..6f09c70 100644 --- a/src/karabiner_configurator/data.clj +++ b/src/karabiner_configurator/data.clj @@ -61,6 +61,29 @@ (or (string? text) (keyword? text) (nil? text)))))) +(defn softf? [vec] + (and (vector? vec) + (let [[k m] vec] + (and (= k :softf) + (map? m) + (or (map? (:open_application m)) + (map? (:set_mouse_cursor_position m)) + (map? (:cg_event_double_click m)) + (map? (:iokit_power_management_sleep_system m)) + (map? (:open m)) + (map? (:setmpos m)) + (map? (:dbc m)) + (map? (:sleep m))))))) +(defn op? [vec] + (and (vector? vec) + (let [[k p] vec] + (and (= k :op) + (string? p))))) +(defn oi? [vec] + (and (vector? vec) + (let [[k i] vec] + (and (= k :oi) + (string? i))))) (defn raw-rule? [rule] (and (map? rule) (or (= :basic (:type rule)) diff --git a/src/karabiner_configurator/rules.clj b/src/karabiner_configurator/rules.clj index 0f645a5..8b20316 100644 --- a/src/karabiner_configurator/rules.clj +++ b/src/karabiner_configurator/rules.clj @@ -1,7 +1,7 @@ (ns karabiner-configurator.rules (:require [karabiner-configurator.conditions :as conditions] - [karabiner-configurator.data :as d :refer [pkey? k? special-modi-k? conf-data pointing-k? consumer-k? noti? templates? devices? assoc-conf-data update-conf-data profile? raw-rule?]] + [karabiner-configurator.data :as d :refer [pkey? k? special-modi-k? conf-data pointing-k? consumer-k? noti? softf? templates? devices? assoc-conf-data update-conf-data profile? raw-rule?]] [karabiner-configurator.froms :as froms] [karabiner-configurator.misc :refer [massert contains??]] [karabiner-configurator.tos :as tos])) @@ -57,6 +57,15 @@ (defn parse-noti-with-tos [des to] (let [[_ id text] to] (first (tos/parse-to des [{:noti {:id id :text text}}])))) +(defn parse-softf-with-tos [des to] + (let [[_ m] to] + (first (tos/parse-to des [{:softf m}])))) +(defn parse-op-with-tos [des to] + (let [[_ path] to] + (first (tos/parse-to des [{:softf {:open {:file_path path}}}])))) +(defn parse-oi-with-tos [des to] + (let [[_ id] to] + (first (tos/parse-to des [{:softf {:open {:bundle_identifier id}}}])))) (defn to-key-vector [des to _prevresult] @@ -64,6 +73,12 @@ [(parse-simple-set-variable des to)] (noti? to) [(parse-noti-with-tos des to)] + (d/op? to) + [(parse-op-with-tos des to)] + (d/oi? to) + [(parse-oi-with-tos des to)] + (d/softf? to) + [(parse-softf-with-tos des to)] :else (vec (flatten @@ -72,6 +87,7 @@ (massert (or (contains? (:tos @conf-data) v) (k? v) (noti? v) + (softf? v) (special-modi-k? v) (vector? v) (string? v) diff --git a/src/karabiner_configurator/tos.clj b/src/karabiner_configurator/tos.clj index d5162de..bd3d659 100644 --- a/src/karabiner_configurator/tos.clj +++ b/src/karabiner_configurator/tos.clj @@ -1,5 +1,6 @@ (ns karabiner-configurator.tos - (:require [karabiner-configurator.data :refer [conf-data update-conf-data]] + (:require [clojure.set :as cset] + [karabiner-configurator.data :refer [conf-data update-conf-data]] [karabiner-configurator.keys :refer [parse-key]] [karabiner-configurator.misc :refer [massert]])) @@ -58,13 +59,16 @@ ;; :noti "set_notification_message" : { ;; "id": "identifier of the message", ;; "text": "message text" -;; } +;; } +;; :softf "software_function" : { +;; "open_application": bundle_identifier -> id +;; } (defn parse-to [tname tinfos] (mapv - (fn [{:keys [set input shell lazy repeat halt hold_down_ms select_input_source noti] :as tinfo}] - (let [result (parse-key tname tinfo true true) + (fn [{:keys [set input shell lazy repeat halt hold_down_ms select_input_source noti softf] :as tinfo}] + (let [result (parse-key tname tinfo true true) _validate-shell (massert (or (and (vector? shell) (contains? (:templates @conf-data) (first shell))) (string? shell) (nil? shell)) (str "invalid `shell` in to definition " tname " " shell ", should be string or keyword")) _validate-input (massert (or (nil? input) (and (keyword? input) (contains? (:input-sources @conf-data) input))) @@ -78,43 +82,53 @@ (keyword? (get noti :text)) (string? (get noti :text))))) (str "invalid `noti`, must be a map with at least :id, :id must be string or keyword")) - result (if (keyword? input) - (assoc result :select_input_source (input (:input-sources @conf-data))) - result) - result (if (string? shell) - (assoc result :shell_command shell) - result) - result (if (vector? shell) - (assoc result - :shell_command (apply - format - (flatten - [((first shell) - (:templates @conf-data)) - (rest shell) - ;; optional arguments - "" "" "" "" "" ""]))) - result) - result (if (vector? set) - (assoc result :set_variable {:name (first set) :value (second set)}) - result) - result (if (false? repeat) - (assoc result :repeat false) - result) - result (if (true? halt) - (assoc result :halt true) - result) - result (if (and (number? hold_down_ms) (not (= 0 hold_down_ms))) - (assoc result :hold_down_milliseconds hold_down_ms) - result) - result (if (boolean? lazy) - (assoc result :lazy lazy) - result) - result (if noti - (let [{:keys [id text]} noti] - (assoc result :set_notification_message {:id id :text (or text "")})) - result) - result (if select_input_source tinfo result)] + _validate-softf (massert (or (nil? softf) (map? softf)) + (str "invalid `softf`, must be a map with valid keys")) + result (if (keyword? input) + (assoc result :select_input_source (input (:input-sources @conf-data))) + result) + result (if (string? shell) + (assoc result :shell_command shell) + result) + result (if (vector? shell) + (assoc result + :shell_command (apply + format + (flatten + [((first shell) + (:templates @conf-data)) + (rest shell) + ;; optional arguments + "" "" "" "" "" ""]))) + result) + result (if (vector? set) + (assoc result :set_variable {:name (first set) :value (second set)}) + result) + result (if (false? repeat) + (assoc result :repeat false) + result) + result (if (true? halt) + (assoc result :halt true) + result) + result (if (and (number? hold_down_ms) (not (= 0 hold_down_ms))) + (assoc result :hold_down_milliseconds hold_down_ms) + result) + result (if (boolean? lazy) + (assoc result :lazy lazy) + result) + result (if noti + (let [{:keys [id text]} noti] + (assoc result :set_notification_message {:id id :text (or text "")})) + result) + softf (when softf + (cset/rename-keys softf {:dbc :cg_event_double_click + :sleep :iokit_power_management_sleep_system + :open :open_application + :setmpos :set_mouse_cursor_position})) + result (cond-> result + (map? softf) + (assoc :software_function softf)) + result (if select_input_source tinfo result)] result)) tinfos)) diff --git a/test/karabiner_configurator/rules_test.clj b/test/karabiner_configurator/rules_test.clj index 1a7bb7c..a8fd22b 100644 --- a/test/karabiner_configurator/rules_test.clj +++ b/test/karabiner_configurator/rules_test.clj @@ -88,7 +88,13 @@ [{:sim [:i :o] :modi {:mandatory [:left_command] :optional [:left_shift]}} [:!Ci :o]] [{:sim [:i :o] :modi {:optional [:left_command]}} [:i :o]] - [{:sim [:i :o] :modi {:optional [:any]}} [:i :o]]]}]) + [{:sim [:i :o] :modi {:optional [:any]}} [:i :o]]]} + {:des "software_function" + :rules [[:1 [:op "/Applications/Emacs.app"]] + [:2 [:oi "org.gnu.Emacs"]] + [:3 [:softf {:dbc {:button 0}}]] + [:4 [:softf {:sleep {:delay_milliseconds 500}}]] + [:5 [:softf {:setmpos {:x 0 :y 0 :screen 0}}]]]}]) (def result @@ -656,7 +662,33 @@ :key_up_when "any"}} :to [{:key_code "i"} {:key_code "o"}] - :type "basic"}]}] + :type "basic"}]} + {:description "software_function" + :manipulators + [{:from {:key_code "1"} + :to + [{:software_function + {:open_application {:file_path "/Applications/Emacs.app"}}}] + :type "basic"} + {:from {:key_code "2"} + :to + [{:software_function + {:open_application {:bundle_identifier "org.gnu.Emacs"}}}] + :type "basic"} + {:from {:key_code "3"}, + :to [{:software_function {:cg_event_double_click {:button 0}}}] + :type "basic"} + {:from {:key_code "4"} + :to + [{:software_function + {:iokit_power_management_sleep_system + {:delay_milliseconds 500}}}] + :type "basic"} + {:from {:key_code "5"} + :to + [{:software_function + {:set_mouse_cursor_position {:x 0, :y 0, :screen 0}}}] + :type "basic"}]}] :test-profile [{:description "Auto generated layer conditions" :manipulators [{:type "basic" :to [{:set_variable {:name "chunkwm-move-mode"