{"id":18787199,"url":"https://github.com/michaelforney/wl9","last_synced_at":"2025-09-14T23:58:07.830Z","repository":{"id":65855393,"uuid":"557549776","full_name":"michaelforney/wl9","owner":"michaelforney","description":"rio-wayland shim server","archived":false,"fork":false,"pushed_at":"2023-09-10T20:10:40.000Z","size":136,"stargazers_count":17,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-13T13:12:10.732Z","etag":null,"topics":["plan9","wayland"],"latest_commit_sha":null,"homepage":"https://git.sr.ht/~mcf/wl9","language":"C","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/michaelforney.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-25T22:14:31.000Z","updated_at":"2025-04-03T01:22:12.000Z","dependencies_parsed_at":"2024-11-07T20:53:56.866Z","dependency_job_id":"899e74bf-0fc4-4038-b8a5-86e3bbc55772","html_url":"https://github.com/michaelforney/wl9","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/michaelforney/wl9","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelforney%2Fwl9","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelforney%2Fwl9/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelforney%2Fwl9/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelforney%2Fwl9/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michaelforney","download_url":"https://codeload.github.com/michaelforney/wl9/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelforney%2Fwl9/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275185376,"owners_count":25419919,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-14T02:00:10.474Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["plan9","wayland"],"created_at":"2024-11-07T20:53:50.808Z","updated_at":"2025-09-14T23:58:07.790Z","avatar_url":"https://github.com/michaelforney.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wl9\n\nwl9 is a POSIX wayland server that presents its windows to Plan 9's\nrio.\n\nIt communicates over 9p to an `exportfs` instance running host Plan\n9 system through a pair of file descriptors (one for reading, and\none for writing).\n\nA lightly patched version of sigrid's [c9] library is used as a 9p\nclient.\n\n[c9]: http://shithub.us/sigrid/c9/HEAD/info.html\n\n## Usage\n\n```\nwl9 [-t rfd[,wfd]] [cmd [args...]]\n```\n\nThe `-t` option specifies the file descriptors for the 9p connection.\nThey should be set up in advance before running wl9, or if not\nspecified, `/dev/virtio-ports/term` is opened for reading and\nwriting.\n\nIf `cmd [args...]` is given, it is launched as a child process after\nwl9 sets up its sockets. The first window created by the child\nwill run in the existing `/mnt/wsys` instead of mounting `$wsys`.\nThis has the effect of replacing the window running `exportfs`.\nAdditionally, wl9 will stop automatically when it has no clients.\n\n## Examples\n\n### vmx\n\nYou can connect through a virtio-serial pipe from a unix guest to\na host 9front system.\n\n```\nvmx -c virtio:name:term!^\u003c\u003e{exportfs -r / -m 32768 \u003e[1=0]}\n```\n\nIf the guest system has hotplug rules to create named symlinks for\nvirtio pipes, you can just run `wl9`. Otherwise, you will need to\nsetup the appropriate redirections.\n\n```\nwl9 -t 0 \u003c\u003e/dev/vportNpM\n```\n\n### ssh\n\nYou can use a pipeline with `exportfs` and `ssh` to remotely run a\nwayland application on a unix server that has wl9 installed.\n\n```\nexportf -r / \u003c[0=1] | ssh host wl9 -t 0,1 cmd \u003e[1=0]\n```\n\n## Wsys\n\nrio windows can be created in two ways: by writing a `new` message\nto `/dev/wctl`, or by attaching to `$wsys` with a `new` message as\nthe `aname`. Unfortunately, with the first method we can't find the\n`winid` of the created window, so wl9 uses the second method.\n\nHowever, attaching to `$wsys` poses an additional problem: we either\nneed a separate 9p channel for `exportfs -S $wsys`, or we need to\nspeak 9p over 9p to a separate `exportfs` process spawned by the\noriginal.\n\nAt least for now, wl9 uses a third approach. Using a small [exportfs\npatch], we can attach to services by walking to their fid, and\nattaching with a 12 character aname prefix consisting of an 11\ncharacter decimal fid number, and a blank. The prefix is stripped\nfor the mount of the service.\n\n[exportfs patch]: https://git.sr.ht/~mcf/wl9/blob/main/exportfs.patch\n\nOnce we mount `$wsys`, wl9 opens several files:\n\n- `winname`: used to locate the draw(3) image for the window\n- `label`: `xdg_toplevel.set_title` requests are translated to\n  writes to `label`\n- `wctl`: used to monitor window coordinates and status\n- `mouse`: used to read mouse events\n- `kbd`: used to read keyboard events\n\n## Draw\n\n## Snarf\n\nStill kind of buggy with some applications.\n\n## Mouse\n\n### Cursor\n\nNot implemented yet.\n\n## Keyboard\n\n### Keyboard map\n\nFor key codes, we use the 'k' and 'K' events from `/dev/kbd`. These\ncodes are the unshifted unicode characters corresponding to the\nkeys pressed and released. If there is no unshifted character\ncorresponding to that scancode, or the scancode is escaped, the\nshifted character is used instead.\n\nIn typical keymaps, some keys are mapped to same unshifted character,\ni.e. the mapping from scancode to character is not 1-1. Unfortunately,\nThis means that we can't distinguish which of the keys were pressed.\nIf there is a conflict, we choose the first one we encounter.\n\nIn particular, for the default kbmap, we have\n\n| rune | key (scan) ... |\n| --- | --- |\n| \\n | ENTER (`1c`) KPENTER (`e0 1c`) |\n| - | MINUS (`0c`) KPMINUS (`4a`) |\n| . | DOT (`34`) KPDOT (`53`) |\n| / | SLASH (`35`) KPSLASH (`e0 35`) |\n| 0 | 0 (`0b`) KP0 (`52`) |\n| 1 | 1 (`02`) KP1 (`4f`) |\n| 2 | 2 (`03`) KP2 (`50`) |\n| 3 | 3 (`04`) KP3 (`51`) |\n| 4 | 4 (`05`) KP4 (`4b`) |\n| 5 | 5 (`06`) KP5 (`4c`) |\n| 6 | 6 (`07`) KP6 (`4d`) |\n| 7 | 7 (`08`) KP7 (`47`) |\n| 8 | 8 (`09`) KP8 (`48`) |\n| 9 | 9 (`0a`) KP9 (`49`) |\n| Kup | UP (`e0 48`) ? (`7b`) ? (`e0 79`) |\n| Kshift | LEFTSHIFT (`2a`) RIGHTSHIFT (`36`) |\n| Kctl | LEFTCTRL (`1d`) RIGHTCTRL (`e0 1d`) CAPSLOCK (`3a`) |\n| Kdown | DOWN (`e0 50`) ? (`79`) |\n\nAt startup, /dev/kbmap is translated to an XKB keymap as follows:\n\nFor every keymap entry with table 0 (unshifted) 2 (escaped unshifted),\n5 (escaped with control), or 6 (escaped with shift), we add a keycode\n`\u003cU+XXXX\u003e` to the `xkb_keycodes` section with value `0xXXXX + 8`,\nwhere `U+XXXX` is the value of mapped unicode character. The 8\noffset is due to historical reasons to translate an kernel event\ncode to an XKB keycode.\n\nWe use the standard 2-level `xkb_types` (excluding `KEYPAD` since\nwe can't uniquely identify those keys). Currently, tables 3-9 in\n`/dev/kbmap` are ignored, which means that shift only affects\nnon-escaped keycodes, and control, alt, and mod4.\n\n```\nxkb_types \"plan9\" {\n\ttype \"ONE_LEVEL\" {\n\t\tmodifiers = none;\n\t\tlevel_name[1] = \"Any\";\n\t};\n\ttype \"TWO_LEVEL\" {\n\t\tmodifiers = Shift;\n\t\tmap[Shift] = 2;\n\t\tlevel_name[1] = \"Base\";\n\t\tlevel_name[2] = \"Shift\";\n\t};\n\ttype \"ALPHABETIC\" {\n\t\tmodifiers = Shift+Lock;\n\t\tmap[Shift] = 2;\n\t\tmap[Lock] = 2;\n\t\tlevel_name[1] = \"Base\";\n\t\tlevel_name[2] = \"Caps\";\n\t};\n};\n```\n\nWe use the following `xkb_compat` rules for standard shift/caps\nlock handling.\n\n```\nxkb_compat \"plan9\" {\n\tinterpret Shift_L {\n\t\taction = SetMods(modifiers=Shift,clearLocks);\n\t};\n\tinterpret Caps_Lock {\n\t\taction = LockMods(modifiers=Lock);\n\t};\n};\n```\n\nThe `xkb_symbols` section is constructed with a key for each keycode.\nSince XKB keysyms are more granular than Plan 9 key characters, for\nthe ambiguous cases, we choose keysyms corresponding to first key\nin the table above. Additionally, though Plan 9 doesn't have a\nkeymap entry the right meta key (bug?), `Kmod4` is mapped to `Super_L`\nrather than `Super_R`.\n\nIf that keycode corresponds to an non-escaped scancode and that\nscancode has an entry in table 1 (shift) as well, we add a two-level\nkey, `key \u003cU+XXXX\u003e {[KEY0, KEY1]}`, where `KEY0` and `KEY1` are the\nnames of the XKB symbols we mapped the table 0 and table 1 characters\nto. Otherwise, we add a one-level key f`key \u003cU+XXXX\u003e {[KEY02]}`.\nIf there is no XKB name for the character, we use an XKB unicode\nkeysym.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelforney%2Fwl9","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichaelforney%2Fwl9","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelforney%2Fwl9/lists"}