Aylur / ags

A customizable and extensible shell
GNU General Public License v3.0
1.74k stars 95 forks source link

Hyprland's service "keyboard-layout" signal doesn't capture params #414

Open Seme4eg opened 1 month ago

Seme4eg commented 1 month ago
Utils.watch(
    "EN",
    hyprland,
    "keyboard-layout",
    (kbdName, layoutName) => {
      print(kbdName, layoutName) // prints 'undefined, undefined'
      return "something"
    })
faileon commented 3 weeks ago

Hmm I am not sure if this is intentional or not. The callback in Utils.watch is defined without params, that's why its undefined. Perhaps @Aylur could clarify if this is intentional or not.

In the meantime this is my current solution:

const hyprland = await Service.import("hyprland");

interface HKeyboard {
  address: string;
  name: string;
  rules: string;
  model: string;
  layout: string;
  variant: string;
  options: string;
  active_keymap: string;
  main: boolean;
}

const getKeyboardLayout = (kbName = "monsgeek-monsgeek-keyboard-1") => {
  const devices = Utils.exec("hyprctl devices -j");
  const { keyboards } = JSON.parse(devices) as { keyboards: HKeyboard[] };
  const mainKb = keyboards.find(({ name }) => name === kbName);
  return mainKb?.active_keymap ?? "??";
};

const layout = Utils.watch(
  getKeyboardLayout(),
  hyprland,
  "keyboard-layout",
  () => getKeyboardLayout(),
);

const Language = () =>
  Widget.Label({
    label: layout.as(
      (layout) =>
        ({
          "Czech (QWERTY)": "cs",
          "English (US)": "en",
        })[layout] ?? layout,
    ),
  });

export default () =>
  Widget.Box({
    children: [Language()],
  });

EDIT: I did some digging in the source code. https://github.com/Aylur/ags/blob/main/src/utils/binding.ts#L46 the setter doesnt accept any params

I believe this should fix the issue:

export function watch<T>(
  init: T,
  objs: Connectable | Array<Connectable | [obj: Connectable, signal?: string]>,
  sigOrFn: string | ((...args: any[]) => T),
  callback?: (...args: any[]) => T,
) {
  const v = new Variable(init);
  const f =
    typeof sigOrFn === "function" ? sigOrFn : callback ?? (() => v.value);
  const set = (...args: any[]) => (v.value = f(...args));

  if (Array.isArray(objs)) {
    // multiple objects
    for (const obj of objs) {
      if (Array.isArray(obj)) {
        // obj signal pair
        const [o, s = "changed"] = obj;
        o.connect(s, set);
      } else {
        // obj on changed
        obj.connect("changed", set);
      }
    }
  } else {
    // watch single object
    const signal = typeof sigOrFn === "string" ? sigOrFn : "changed";
    objs.connect(signal, set);
  }

  return v.bind();
}

Should I send a PR?