⌨️

Keyballの操作性をmacOSとWindowsで揃える

2024/10/13に公開

要約

Keyball[1]をmacOSとWindowsで使っていて、

  • トラックボールやマウスキーによるスクロールの向きをmacOSとWindowsで揃えたい
  • アプリケーション切り替えがmacOSでCmd+Tab、WindowsでAlt+Tabなのを同じ操作に揃えたい

という希望を叶えるためにやってることをまとめます。

スクロールの向きを反転させられるように

Keyballのファームを改変してスクロール方向を反転できるようにしています。

制御用の変数定義など

keyball.hの変更

設定保持するためのメンバーをkeyball_tのメンバー変数に追加し、

typedef struct {
    /* 略 */
    uint8_t  scroll_reverse_mode;
    /* 略 */
} keyball_t;

この値には同じくkeyball.hで定義したenum

enum {
    KEYBALL_SCROLL_REVERSE_VERTICAL   = 1,
    KEYBALL_SCROLL_REVERSE_HORIZONTAL = 2,
};

の論理和で縦横の反転を表現することにします。

のちほどkeymap.cから制御するので、アクセッサの宣言もしておきます。

/// keyball_get_scroll_reverse_mode gets current scroll directions.
/// See also keyball_set_scroll_reverse_mode for the detail.
uint8_t keyball_get_scroll_reverse_mode(void);

/// keyball_set_scroll_reverse_mode changes scroll directions.
///
/// The directions are described by the mode which is composition of the flags
/// KEYBALL_SCROLL_REVERSE_VERTICAL and KEYBALL_SCROLL_REVERSE_HORIZONTAL.
void keyball_set_scroll_reverse_mode(uint8_t mode);

keyball.cの変更

アクセッサの定義

uint8_t keyball_get_scroll_reverse_mode(void) {
    return keyball.scroll_reverse_mode;
}

void keyball_set_scroll_reverse_mode(uint8_t mode) {
    if (mode <= (KEYBALL_SCROLL_REVERSE_VERTICAL | KEYBALL_SCROLL_REVERSE_HORIZONTAL)) {
        keyball.scroll_reverse_mode = mode;
    }
}

トラックボール操作のスクロールの向きを反転

Keyballではレイヤー3が有効になっているとトラックボール操作でスクロールができます[2]。それを制御している keyball.c 内の関数 keyball_on_apply_motion_to_mouse_scroll() の末尾でスクロール方向を反転させるようにします。

__attribute__((weak)) void keyball_on_apply_motion_to_mouse_scroll(keyball_motion_t *m, report_mouse_t *r, bool is_left) {

    /* 略 */

    if (keyball_get_scroll_reverse_mode() & KEYBALL_SCROLL_REVERSE_VERTICAL) {
        r->v = -r->v;
    }
    if (keyball_get_scroll_reverse_mode() & KEYBALL_SCROLL_REVERSE_HORIZONTAL) {
        r->h = -r->h;
    }
}

マウスキーによるスクロールの向きを反転

Keyballのデフォルトファームではマウスキーは無効で、横スクロールしたい場合はスクロールスナップモードを切り替えて操作するようになっています[3]が、私は切り替えが面倒なので普段はトラックボールは縦スクロールのみで使い、マウスキーを有効(rules.mkで MOUSEKEY_ENABLE = yes)にしてKC_MS_WH_[LEFT,RIGHT]で横スクロールしています。

こちらも反転させておきます。 keyball.c 内の関数 process_record_kb() で MOUSEKEY_ENABLE が定義されていた場合の処理を追加して、キーコードの読み替えでKC_MS_WH_*の動作を反転させます。 register_mouse() で処理した後 false を返却することで処理済みの扱いにしてデフォルトの処理が走らないようにしています:

bool process_record_kb(uint16_t keycode, keyrecord_t *record) {

    /* 略 */

    switch (keycode) {
#ifndef MOUSEKEY_ENABLE
        /* 略 */
#else
        case KC_MS_WH_UP:
        case KC_MS_WH_DOWN:
            if (keyball_get_scroll_reverse_mode() & KEYBALL_SCROLL_REVERSE_VERTICAL) {
                extern void register_mouse(uint8_t mouse_keycode, bool pressed);
                register_mouse(keycode == KC_MS_WH_UP ? KC_MS_WH_DOWN : KC_MS_WH_UP, record->event.pressed);
                return false;
            }
            break;
        case KC_MS_WH_LEFT:
        case KC_MS_WH_RIGHT:
            if (keyball_get_scroll_reverse_mode() & KEYBALL_SCROLL_REVERSE_HORIZONTAL) {
                extern void register_mouse(uint8_t mouse_keycode, bool pressed);
                register_mouse(keycode == KC_MS_WH_LEFT ? KC_MS_WH_RIGHT : KC_MS_WH_LEFT, record->event.pressed);
                return false;
            }
            break;
#endif
    /* 以下略 */

Cmd と Alt の入れ替えができるように

Cmd と Alt の入れ替えなど、ありがちなキーの入れ替えは QMK の標準機能で簡単にできる仕組みがあります。

Keyballのデフォルトのファームウェアでは無効になっていますが、rules.mkで MAGIC_ENABLE = yes とすることで Magic Keycodes が有効になり、それらをキーに割り当てておくことで、キー操作で入れ替えを制御できます。

今回は起動時のOS検出で制御するので Magic Keycodesを有効にする必要はないのですが、 Keyball のファームウェアはサイズ削減の為、rules.mkで MAGIC_ENABLE = yes としない場合には keycode_config() と mod_config() を空の定義で上書きしてしまいます。そのため、config.h に

#define KEYBALL_KEEP_MAGIC_FUNCTIONS

と書いておく必要があることに注意してください(手元ではMagic Keycodesを有効にするよりもファームウェアは484バイト小さくなりました)。

OS検出と切り替え

QMKはOS検出の機能を備えているため、それを用いて接続先に応じて自動的に動作を切り替えることにします。

私が使っているデフォルトレイヤーのキーマップは

で、赤枠の部分に関して

  • macOSならRALT()とRGUIを入れ替え
  • WindowsならLALTとLGUIを入れ替え

とすればアプリケーション切り替えの操作を揃えることができます。

ここでLとRの入れ替えをそれぞれに分散させている(どちらかのOSでは入れ替えなし、もう一方で両方を入れ替え、とはしていない)のはキーマップの設定の中でmacOS用のIMEトグル LGUI(KC_SPC)[4] とWindows用のIMEトグル RALT(KC_GRV) をわかりやすく記述できるようにするためです[5]

OS検出を仕込むため、rules.mkにて

OS_DETECTION_ENABLE = yes
DEFERRED_EXEC_ENABLE = yes

とし、keymap.cに以下のように記述することで、OS検出の上でGUI/ALT切り替えとスクロールの向きの切り替えをさせます。

#if defined(OS_DETECTION_ENABLE) && defined(DEFERRED_EXEC_ENABLE)
uint32_t os_detect_callback(uint32_t trigger_time, void *cb_arg) {
#if defined(MAGIC_KEYCODE_ENABLE) || defined(KEYBALL_KEEP_MAGIC_FUNCTIONS)
    keymap_config.raw = eeconfig_read_keymap();
#endif
    switch (detected_host_os()) {
    case OS_WINDOWS: {
        uint8_t mode = 0;
        keyball_set_scroll_reverse_mode(mode);
#if defined(MAGIC_KEYCODE_ENABLE) || defined(KEYBALL_KEEP_MAGIC_FUNCTIONS)
        keymap_config.swap_lalt_lgui = true;
        keymap_config.swap_ralt_rgui = false;
#endif
        break;
    }
    case OS_MACOS: {
        uint8_t mode = KEYBALL_SCROLL_REVERSE_VERTICAL | KEYBALL_SCROLL_REVERSE_HORIZONTAL;
        keyball_set_scroll_reverse_mode(mode);
#if defined(MAGIC_KEYCODE_ENABLE) || defined(KEYBALL_KEEP_MAGIC_FUNCTIONS)
        keymap_config.swap_lalt_lgui = false;
        keymap_config.swap_ralt_rgui = true;
#endif
        break;
    }
    default:
        break;
    }
#if defined(MAGIC_KEYCODE_ENABLE) || defined(KEYBALL_KEEP_MAGIC_FUNCTIONS)
    eeconfig_update_keymap(keymap_config.raw);
#endif
    return 0;
}
#endif

void keyboard_post_init_user(void) {
#if defined(OS_DETECTION_ENABLE) && defined(DEFERRED_EXEC_ENABLE)
    defer_exec(100, os_detect_callback, NULL);
#endif
}

defer_execで100ms待たせているのは、QMKのドキュメント(OS Detection)で "Note that it takes some time after firmware is booted to detect the OS." と書かれていることへの対応です。自分の環境だと、待ちを入れなくてもmacOSの検出はできる一方、Windowsの検出のためには待ちが必要でした。

まとめ

Keyball を macOS と Windows で似た操作性で使うためにしていることを紹介しました。キーボードを挿せばいい感じになるので環境ごとの設定を頑張らなくていいのが便利です。

脚注
  1. Keyball61Keyball44を使っています ↩︎

  2. keymap.cのkeyboard_post_init_user()でスクロールモードのオンオフが制御されています ↩︎

  3. Scroll snap modeの説明キー Kb 13~15 で切り替えられます ↩︎

  4. 正確にはIMEトグルではなくて「前の入力ソースを選択」で、デフォルトはCtrl+Spaceが割り当てられていました。Emacsのset-mark-commandと被るためCmd+Spaceに割り当て変更していました ↩︎

  5. IMEトグルの操作も揃えることは可能ですが、キーマップを見た時にわかりにくいので保留してます ↩︎

Discussion