19 Commits
v0.17 ... v0.19

Author SHA1 Message Date
Willow Barraco
b0eff37239 Send modifiers keyboard.key additionaly to keyboard.modifiers
At the moment we just use keyboard.modifiers to maintain our clients
modifiers states. But we should also send the associated key codes.
Clients like wlvncc need them to actually send the keys to the remote
compositor.

We got no clear way to map our modifier enums to key codes. So I added
this zwp_virtual_keyboard_v1_key_mods method.

I think I tried most of the use-cases. The hardest part was arround the
Shift+Space that send Tab. We have to depress the Shift key, reset the
modifiers, send the Tab key, and finally depress back Shift and its
modifier.

I've tested a bit the Code with .code_mod (and .reset_mod), but I'm not
that sure what their behavior should be.

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2026-01-21 22:00:51 +01:00
Maarten van Gompel
f0c99d276a Cancel pending frame callback before destroying surface and ignore old surfaces
This applies two of the improvements in the pull request
"Fix bug complying with wlr-layer-shell-unstable-v1 which causes crashes" to the overhauled codebase.

Reference: https://github.com/jjsullivan5196/wvkbd/pull/pr114
Co-authored-by: Armando DiCianno <armando@dicianno.org>
2026-01-20 08:24:07 +01:00
Maarten van Gompel
bd4a176eef update touch/pointer event checks in order to sequence drawing to a surface before configured.
This applies one of the improvements in the pull request
"Fix bug complying with wlr-layer-shell-unstable-v1 which causes crashes" to the overhauled codebase.

Reference: https://github.com/jjsullivan5196/wvkbd/pull/114
Co-authored-by: Armando DiCianno <armando@dicianno.org>
2026-01-20 08:19:37 +01:00
Anjandev Momi
a12c42cd18 build: fix clang-format build name 2026-01-18 14:12:08 -08:00
Anjandev Momi
66cd3bf49d build: add clang-format 2026-01-18 14:11:14 -08:00
Anjandev Momi
b215035efc add sourcehut build 2026-01-18 14:10:01 -08:00
Maarten van Gompel
f917d02591 version bump prior to release 2026-01-14 22:10:54 +01:00
Willow Barraco
34a33b41fa Overhaul the output available dimension detection (once again)
Recently I've opened a ticket on the Wayland protocol to discuss
on how to determine the very first dimension a client must use, before
receiving the very first actual wl_surface events:

https://gitlab.freedesktop.org/wayland/wayland/-/issues/576

This is a issue I had on wvkbd, but also on bemenu. Here we need the
dimension to determine which layout and height to use.

This implement another approach, recommended by the Wayland folks:

The wl_output object is currently being slowly deprecated, because it is
mostly unusable for the unprivileged clients. In our context, using this
value to determine the available geometry is actually wrong. Other
layer_shell surfaces might prevent us to use the full width or height.
This explain the most recent addition of the two preferred_buffer_scale
and preferred_scale events to the protocols.

The recommended way is to first ask for a layer_shell surface anchored
to the four anchors (top, right, bottom, left). The received configure
event would then give us the whole available geometry. We now can
prepare our future keyboard, and then we make sure we receive the
expected widht and height with its configure event.

This patch drop all of our wl_output management. To receive the surface
scale value, we upgrade the used Wayland protocol version to 6, to
receive the event wl_surface.preferred_buffer_scale. This drop lot of
code.

This repurpose the function show(), hide(), flip_landscape() (renamed
to redimension_keyboard()), because their behavior was too much
intricated.

Signed-off-by: Willow Barraco <contact@willowbarraco.fr>
Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2026-01-14 22:01:48 +01:00
Maarten van Gompel
ed702f9562 version bump prior to release 2025-08-31 17:40:05 +02:00
Maarten van Gompel
ecc3d5ae1a desktop layout: added cyrillic 2025-08-31 13:48:59 +02:00
Jun Aruga
74667dc2e8 Adjust the deskintl layout set's default font size.
Because the deskintl layout set is for desktop, laptop, and tablet devices with
a larger touchscreen. The default font size can be larger than the mobintl
layout set's default font size.

We created the config.deskintl.h based on config.mobintl.h, which is
for mobile devices. However, the current font "Sans 14" is a bit small in my
environment, Framework Laptop 12 (a 2-in-1 laptop with a 12.2-inch touchscreen).

Signed-off-by: Jun Aruga <jun.aruga@gmail.com>
Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2025-08-10 14:17:39 +02:00
Jun Aruga
cf60305a61 Update README for layout-specific config.h and deskintl layout set
This commit is to update the README.md for layout-specific config.h
at the commit <49975e78ee437c6fb4a5f6ea3ccf8bc90dc65536>, and
deskintl layout set the commit <90ac0454cda3e376147c49abbb6b2fa0022211f0>.

Signed-off-by: Jun Aruga <jun.aruga@gmail.com>
2025-08-10 00:31:03 +02:00
Maarten van Gompel
915a881111 README: fixed typo in git send-email instructions 2025-08-10 00:29:06 +02:00
Maarten van Gompel
5318ee9256 updated man page
mentioned desktop layout and documented --non-exclusive parameter
2025-08-09 21:56:48 +02:00
Maarten van Gompel
3a44beac1f Added --non-exclusive parameter to skip requesting exclusive zone from compositor
This allows wvkbd to show on top of existing windows, not pushing them
out of the way.

Ref: https://github.com/jjsullivan5196/wvkbd/issues/102
Ref: https://github.com/jjsullivan5196/wvkbd/issues/93
2025-08-09 21:44:01 +02:00
Maarten van Gompel
b3a7e95c69 added SHIFT_SPACE_IS_TAB as compile time parameter
for the mobile layout, we want shift+space to produce tab , which needed
to be implemented in the main code. For the desktop layout, we don't want
this though, a config parameter now handles this at compile time.

Ref: https://github.com/jjsullivan5196/wvkbd/pull/103
2025-08-09 21:19:08 +02:00
Maarten van Gompel
5689b6bd33 Fix landscape appearance bug in multi-monitor setup
In a multi-monitor setup, the keyboard didn't appear correctly since
commit 9cc8931b46 . This affected only
landscape mode and only on multi-monitor setups where wvkbd is launched
on anything but the first output (and perhaps even only in
multi-monitor setups where one is portrait mode and one landscape like
mine, I'm not sure).

In the mentioned commit Willow commented:

> On situation where multiple screens are present, it will eventually need
> a second loop (roundtrip)

It seems that second loop never really made it all the way to drawing.
Adding an explicit drwsurf_attach() unconditionally in
layer_surface_configure fixes this.

I'm not sure if this is the best way but this fixes this bug, and as far
as I know doesn't cause extra redraws/resizes in the normal
single-monitor scenario.

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2025-08-09 20:54:28 +02:00
Jun Aruga
90ac0454cd Add deskintl (desktop international) layout set.
Add a layout set aimed at desktop, laptop, and tablet devices with a larger
touchscreen. The layout is the US-International English, adding a compose key.
https://en.wikipedia.org/wiki/QWERTY#US-International

This layout set was created based on the layout set by the user nine7nine
(Jordan Johnston) <https://github.com/nine7nine>.

Note I copied the keymap.mobintl.h into the keymap.deskintl.h, and modified the
keymap.deskintl.h as follows to fix "<" (comma+shift), ">" (period+shift), "?"
(slash+shift) keys.

The following commit changed the key codes in the keymap.h for mobile. I
reverted the change for the keymap.deskintl.h.
9e4cf69137

Below is the difference between the keymap.mobintl.h and keymap.deskintl.h now.

```
$ diff -u keymap.mobintl.h keymap.deskintl.h
...
@@ -1226,9 +1225,9 @@
         key <AB05>               {	[               b,               B, equal ] };\
         key <AB06>               {	[               n,               N, question ] };\
         key <AB07>               {	[               m,               M, exclam ] };\
-        key <AB08>               {	[           comma,      apostrophe, less, U00AB] };\
-        key <AB09>               {	[          period,        question, greater, U00BB] };\
-        key <AB10>               {	[           slash,        greater ] };\
+        key <AB08>               {	[           comma,            less, backslash] };\
+        key <AB09>               {	[          period,         greater, bar ] };\
+        key <AB10>               {	[           slash,        question ] };\
         key <I147>               {  [      exclamdown,   questiondown, exclamdown ] };\
         key <RTSH>               {	[         Shift_R ] };\
         key <KPMU>               {\
...
```

Co-authored-by: Jordan Johnston <johnstonljordan@gmail.com>
Signed-off-by: Jun Aruga <jun.aruga@gmail.com>
2025-08-09 20:52:27 +02:00
Jun Aruga
49975e78ee Support a layout-specific config.h.
* Rename config.def.h to config.mobintl.h.
* config.mobintl.h:
  * Rename the macro config_def_h_INCLUDED to config_h_INCLUDED.
  * Remove a tailing space as a refactoring.
* Makefile: Add config.h to the clean task.

Signed-off-by: Jun Aruga <jun.aruga@gmail.com>
2025-08-09 20:52:27 +02:00
13 changed files with 4267 additions and 246 deletions

22
.build.yml Normal file
View File

@@ -0,0 +1,22 @@
arch: x86_64
image: alpine/edge
packages:
- fontconfig
- wayland-dev
- libxkbcommon-dev
- pango-dev
- scdoc
- clang21-extra-tools # needed for formatting
sources:
- https://git.sr.ht/~proycon/wvkbd
triggers:
- action: email
condition: failure
to: "~mil/sxmo-devel@lists.sr.ht"
tasks:
- format: |
cd wvkbd
make format
- build: |
cd wvkbd
make

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ wvkbd
*.1
config.h
wvkbd-mobintl
wvkbd-deskintl

View File

@@ -29,7 +29,7 @@ OBJECTS = $(SOURCES:.c=.o)
all: ${BIN} ${DOCS}
config.h:
cp config.def.h config.h
cp config.${LAYOUT}.h config.h
proto/%-client-protocol.c: proto/%.xml
wayland-scanner code < $? > $@
@@ -43,7 +43,7 @@ wvkbd-${LAYOUT}: config.h $(OBJECTS) layout.${LAYOUT}.h
$(CC) -o wvkbd-${LAYOUT} $(OBJECTS) $(LDFLAGS)
clean:
rm -f $(OBJECTS) $(HDRS) $(WAYLAND_SRC) ${BIN} ${DOCS}
rm -f $(OBJECTS) config.h $(HDRS) $(WAYLAND_SRC) ${BIN} ${DOCS}
format:
clang-format -i $(WVKBD_SOURCES) $(WVKBD_HEADERS)

View File

@@ -44,18 +44,20 @@ You'll need the following developer packages
You also need [scdoc](https://git.sr.ht/~sircmpwn/scdoc/) to generate the documentation.
Make any customizations you would like in `config.def.h` and run `make`
The default set of layouts is called `mobintl` *(mobile international)*, which groups various layouts aimed at mobile devices
and also attempts to accommodate various international users. The resulting binary is called `wvkbd-mobintl`.
and also attempts to accommodate various international users. Run `make`. The resulting binary is called `wvkbd-mobintl`.
You can, however, define your own layouts by copying and modifying `layout.mobintl.h` and `keymap.mobintl.h`
(replace `mobintl` for something like `yourlayout`). Then make your layout set using `make LAYOUT=yourlayout`, and
the resulting binary will be `wvkbd-yourlayout`
The other set of layouts is called `deskintl` *(desktop international)*, which groups layouts aimed at desktop, laptop, and
tablet devices with a larger touchscreen. The set is US-International English. Run `make LAYOUT=deskintl`. The resulting binary
is called `wvkbd-deskintl`.
You can, however, define your own layouts by copying and modifying `config.mobintl.h`, `layout.mobintl.h` and `keymap.mobintl.h`
(replace `mobintl` for something like `yourlayout`), or `config.deskintl.h`, `layout.deskintl.h` and `keymap.deskintl.h`. Then
make your layout set using `make LAYOUT=yourlayout`, and the resulting binary will be `wvkbd-yourlayout`.
## Usage
Run `wvkbd-mobintl` (or the binary for your custom layout set).
Run `wvkbd-mobintl`, `wvkbd-deskintl` or the binary for your custom layout set.
You can switch between the layouts/layers of the keyboard by pressing the
⌨ key (little keyboard) the bottom-left (press shift to iterate back instead of
@@ -101,7 +103,7 @@ layout by name, even layouts not explicitly added to your layers on startup.
Any contributions are welcome, there are two ways to contribute, the first one is **preferred**:
1. [Sourcehut](https://git.sr.ht/~proycon/wvkbd) - Submit your patches using `git mail` to [~mil/sxmo-devel@lists.sr.ht](mailto:~mil/sxmo-devel@lists.sr.ht), follow [these contribution guidelines](https://sxmo.org/contribute/). Questions can also be asked on Sxmo's [mailing lists](https://sxmo.org/support/).
1. [Sourcehut](https://git.sr.ht/~proycon/wvkbd) - Submit your patches using `git send-email` to [~mil/sxmo-devel@lists.sr.ht](mailto:~mil/sxmo-devel@lists.sr.ht), follow [these contribution guidelines](https://sxmo.org/contribute/). Questions can also be asked on Sxmo's [mailing lists](https://sxmo.org/support/).
2. [Github](https://github.com/jjsullivan5196/wvkbd/) - Submit a pull request or open an issue *(legacy method)*
This project was started by [John Sullivan](https://jsullivan.cc/) and is

45
config.deskintl.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef config_h_INCLUDED
#define config_h_INCLUDED
#define DEFAULT_FONT "Sans 18"
#define DEFAULT_ROUNDING 5
static const int transparency = 255;
struct clr_scheme schemes[] = {
{
/* colors */
.bg = {.bgra = {15, 15, 15, transparency}},
.fg = {.bgra = {45, 45, 45, transparency}},
.high = {.bgra = {100, 100, 100, transparency}},
.swipe = {.bgra = {100, 255, 100, 64}},
.text = {.color = UINT32_MAX},
.font = DEFAULT_FONT,
.rounding = DEFAULT_ROUNDING,
},
{
/* colors */
.bg = {.bgra = {15, 15, 15, transparency}},
.fg = {.bgra = {32, 32, 32, transparency}},
.high = {.bgra = {100, 100, 100, transparency}},
.swipe = {.bgra = {100, 255, 100, 64}},
.text = {.color = UINT32_MAX},
.font = DEFAULT_FONT,
.rounding = DEFAULT_ROUNDING,
}
};
/* layers is an ordered list of layouts, used to cycle through */
static enum layout_id layers[] = {
Full, // First layout is the default layout on startup
Special,
NumLayouts // signals the last item, may not be omitted
};
/* layers is an ordered list of layouts, used to cycle through */
static enum layout_id landscape_layers[] = {
Full, // First layout is the default layout on startup
Special,
NumLayouts // signals the last item, may not be omitted
};
#endif // config_h_INCLUDED

View File

@@ -1,4 +1,4 @@
VERSION = 0.17
VERSION = 0.19
CFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=700
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man

View File

@@ -1,8 +1,9 @@
#ifndef config_def_h_INCLUDED
#define config_def_h_INCLUDED
#ifndef config_h_INCLUDED
#define config_h_INCLUDED
#define DEFAULT_FONT "Sans 14"
#define DEFAULT_ROUNDING 5
#define SHIFT_SPACE_IS_TAB
static const int transparency = 255;
struct clr_scheme schemes[] = {
@@ -31,7 +32,7 @@ struct clr_scheme schemes[] = {
/* layers is an ordered list of layouts, used to cycle through */
static enum layout_id layers[] = {
Full, // First layout is the default layout on startup
Special,
Special,
NumLayouts // signals the last item, may not be omitted
};
@@ -42,4 +43,4 @@ static enum layout_id landscape_layers[] = {
NumLayouts // signals the last item, may not be omitted
};
#endif // config_def_h_INCLUDED
#endif // config_h_INCLUDED

View File

@@ -274,12 +274,28 @@ kbd_get_layer_index(struct kbd *kb, struct layout *l)
return 0;
}
void
zwp_virtual_keyboard_v1_key_mods(struct zwp_virtual_keyboard_v1 *vkbd, uint32_t time, uint32_t mods, enum wl_keyboard_key_state state)
{
if ((mods & Shift) == Shift)
zwp_virtual_keyboard_v1_key(vkbd, time, KEY_LEFTSHIFT, state);
if ((mods & CapsLock) == CapsLock)
zwp_virtual_keyboard_v1_key(vkbd, time, KEY_CAPSLOCK, state);
if ((mods & Ctrl) == Ctrl)
zwp_virtual_keyboard_v1_key(vkbd, time, KEY_LEFTCTRL, state);
if ((mods & Alt) == Alt)
zwp_virtual_keyboard_v1_key(vkbd, time, KEY_LEFTALT, state);
if ((mods & Super) == Super)
zwp_virtual_keyboard_v1_key(vkbd, time, KEY_LEFTMETA, state);
if ((mods & AltGr) == AltGr)
zwp_virtual_keyboard_v1_key(vkbd, time, KEY_RIGHTALT, state);
}
void
kbd_unpress_key(struct kbd *kb, uint32_t time)
{
bool unlatch_shift, unlatch_ctrl, unlatch_alt, unlatch_super, unlatch_altgr;
unlatch_shift = unlatch_ctrl = unlatch_alt = unlatch_super = unlatch_altgr = false;
bool unlatch_shift, unlatch_ctrl, unlatch_alt, unlatch_super, unlatch_altgr, unlatch_tab;
unlatch_shift = unlatch_ctrl = unlatch_alt = unlatch_super = unlatch_altgr = unlatch_tab = false;
if (kb->last_press) {
unlatch_shift = (kb->mods & Shift) == Shift;
unlatch_ctrl = (kb->mods & Ctrl) == Ctrl;
@@ -287,22 +303,15 @@ kbd_unpress_key(struct kbd *kb, uint32_t time)
unlatch_super = (kb->mods & Super) == Super;
unlatch_altgr = (kb->mods & AltGr) == AltGr;
if (unlatch_shift) kb->mods ^= Shift;
if (unlatch_ctrl) kb->mods ^= Ctrl;
if (unlatch_alt) kb->mods ^= Alt;
if (unlatch_super) kb->mods ^= Super;
if (unlatch_altgr) kb->mods ^= AltGr;
if (unlatch_shift||unlatch_ctrl||unlatch_alt||unlatch_super||unlatch_altgr) {
zwp_virtual_keyboard_v1_modifiers(kb->vkbd, kb->mods, 0, 0, 0);
}
if (kb->last_press->type == Copy) {
zwp_virtual_keyboard_v1_key(kb->vkbd, time, 127, // COMP key
WL_KEYBOARD_KEY_STATE_RELEASED);
} else {
if ((kb->last_press->code == KEY_SPACE) && (unlatch_shift)) {
if ((kb->shift_space_is_tab) && (kb->last_press->code == KEY_SPACE) && (unlatch_shift)) {
// shift + space is tab
unlatch_shift = false;
unlatch_tab = true;
kb->mods ^= Shift;
zwp_virtual_keyboard_v1_key(kb->vkbd, time, KEY_TAB,
WL_KEYBOARD_KEY_STATE_RELEASED);
} else {
@@ -312,10 +321,35 @@ kbd_unpress_key(struct kbd *kb, uint32_t time)
}
}
if (unlatch_shift) {
kb->mods ^= Shift;
zwp_virtual_keyboard_v1_key_mods(kb->vkbd, time, Shift, WL_KEYBOARD_KEY_STATE_RELEASED);
}
if (unlatch_ctrl) {
kb->mods ^= Ctrl;
zwp_virtual_keyboard_v1_key_mods(kb->vkbd, time, Ctrl, WL_KEYBOARD_KEY_STATE_RELEASED);
}
if (unlatch_alt) {
kb->mods ^= Alt;
zwp_virtual_keyboard_v1_key_mods(kb->vkbd, time, Alt, WL_KEYBOARD_KEY_STATE_RELEASED);
}
if (unlatch_super) {
kb->mods ^= Super;
zwp_virtual_keyboard_v1_key_mods(kb->vkbd, time, Super, WL_KEYBOARD_KEY_STATE_RELEASED);
}
if (unlatch_altgr) {
kb->mods ^= AltGr;
zwp_virtual_keyboard_v1_key_mods(kb->vkbd, time, AltGr, WL_KEYBOARD_KEY_STATE_RELEASED);
}
if (unlatch_shift||unlatch_ctrl||unlatch_alt||unlatch_super||unlatch_altgr||unlatch_tab) {
zwp_virtual_keyboard_v1_modifiers(kb->vkbd, kb->mods, 0, 0, 0);
}
if (kb->compose >= 2) {
kb->compose = 0;
kbd_switch_layout(kb, kb->last_abc_layout, kb->last_abc_index);
} else if (unlatch_shift||unlatch_ctrl||unlatch_alt||unlatch_super||unlatch_altgr) {
} else if (unlatch_shift||unlatch_ctrl||unlatch_alt||unlatch_super||unlatch_altgr||unlatch_tab) {
kbd_draw_layout(kb);
} else {
kbd_draw_key(kb, kb->last_press, Unpress);
@@ -392,6 +426,7 @@ kbd_press_key(struct kbd *kb, struct key *k, uint32_t time)
switch (k->type) {
case Code:
if (k->code_mod) {
zwp_virtual_keyboard_v1_key_mods(kb->vkbd, time, k->code_mod, WL_KEYBOARD_KEY_STATE_PRESSED);
if (k->reset_mod) {
zwp_virtual_keyboard_v1_modifiers(kb->vkbd, k->code_mod, 0, 0,
0);
@@ -404,9 +439,10 @@ kbd_press_key(struct kbd *kb, struct key *k, uint32_t time)
}
kb->last_swipe = kb->last_press = k;
kbd_draw_key(kb, k, Press);
if ((k->code == KEY_SPACE) && (kb->mods & Shift)) {
if ((kb->shift_space_is_tab) && (k->code == KEY_SPACE) && (kb->mods & Shift)) {
// shift space is tab
zwp_virtual_keyboard_v1_modifiers(kb->vkbd, 0, 0, 0, 0);
zwp_virtual_keyboard_v1_key_mods(kb->vkbd, time, Shift, WL_KEYBOARD_KEY_STATE_RELEASED);
zwp_virtual_keyboard_v1_modifiers(kb->vkbd, kb->mods ^ Shift, 0, 0, 0);
zwp_virtual_keyboard_v1_key(kb->vkbd, time, KEY_TAB,
WL_KEYBOARD_KEY_STATE_PRESSED);
} else {
@@ -423,6 +459,11 @@ kbd_press_key(struct kbd *kb, struct key *k, uint32_t time)
break;
case Mod:
kb->mods ^= k->code;
if (kb->mods & k->code) {
zwp_virtual_keyboard_v1_key_mods(kb->vkbd, time, k->code, WL_KEYBOARD_KEY_STATE_PRESSED);
} else {
zwp_virtual_keyboard_v1_key_mods(kb->vkbd, time, k->code, WL_KEYBOARD_KEY_STATE_RELEASED);
}
if ((k->code == Shift) || (k->code == CapsLock)) {
kbd_draw_layout(kb);
} else {

View File

@@ -101,6 +101,8 @@ struct kbd {
double scale;
double preferred_scale, preferred_fractional_scale;
bool landscape;
bool shift_space_is_tab;
bool exclusive;
uint8_t mods;
uint8_t compose;
struct key *last_press;
@@ -111,6 +113,7 @@ struct kbd {
size_t last_abc_index; //the layer index of the last alphabetical layout
struct layout *layouts;
struct Output *output; //only used to keep track of landscape flipping, never dereferenced
enum layout_id *layers;
enum layout_id *landscape_layers;

2934
keymap.deskintl.h Normal file

File diff suppressed because it is too large Load Diff

1007
layout.deskintl.h Normal file

File diff suppressed because it is too large Load Diff

388
main.c
View File

@@ -47,18 +47,10 @@ static struct wp_fractional_scale_manager_v1 *wfs_mgr;
static struct wp_viewport *draw_surf_viewport, *popup_draw_surf_viewport;
static struct wp_viewporter *viewporter;
static bool popup_xdg_surface_configured;
static bool layer_surface_configured;
struct Output {
uint32_t name;
uint32_t w, h;
double scale;
struct wl_output *data;
};
static struct Output *current_output;
#define WL_OUTPUTS_LIMIT 8
static struct Output wl_outputs[WL_OUTPUTS_LIMIT];
static int wl_outputs_size;
static uint32_t available_width, available_height = 0;
static void refresh_available_dimension();
/* drawing */
static struct drw draw_ctx;
@@ -116,11 +108,6 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
const char *name);
static void wl_surface_enter(void *data, struct wl_surface *wl_surface,
struct wl_output *wl_output);
static void wl_surface_leave(void *data, struct wl_surface *wl_surface,
struct wl_output *wl_output);
static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface,
uint32_t version);
@@ -132,8 +119,9 @@ static void layer_surface_configure(void *data,
uint32_t serial, uint32_t w, uint32_t h);
static void layer_surface_closed(void *data,
struct zwlr_layer_surface_v1 *surface);
static void flip_landscape();
static void redimension_keyboard();
static void show();
static void hide();
/* event handlers */
static const struct wl_pointer_listener pointer_listener = {
@@ -159,9 +147,33 @@ static const struct wl_seat_listener seat_listener = {
.name = seat_handle_name,
};
void
wl_surface_enter(void *data, struct wl_surface *wl_surface,
struct wl_output *wl_output)
{
}
void
wl_surface_leave(void *data, struct wl_surface *wl_surface,
struct wl_output *wl_output) {
}
void
wl_preferred_buffer_scale(void *data, struct wl_surface *wl_surface,
int scale) {
keyboard.preferred_scale = scale;
}
void
wl_preferred_buffer_transform(void *data, struct wl_surface *wl_surface,
uint32_t transform) {
}
static const struct wl_surface_listener surface_listener = {
.enter = wl_surface_enter,
.leave = wl_surface_leave,
.preferred_buffer_scale = wl_preferred_buffer_scale,
.preferred_buffer_transform = wl_preferred_buffer_transform,
};
static const struct wl_registry_listener registry_listener = {
@@ -169,6 +181,25 @@ static const struct wl_registry_listener registry_listener = {
.global_remove = handle_global_remove,
};
void
initiate_configure(void *data, struct zwlr_layer_surface_v1 *surface,
uint32_t serial, uint32_t w, uint32_t h)
{
available_width = w;
available_height = h;
zwlr_layer_surface_v1_ack_configure(surface, serial);
}
void
initiate_closed(void *data, struct zwlr_layer_surface_v1 *surface)
{
}
static const struct zwlr_layer_surface_v1_listener initiate_listener = {
.configure = initiate_configure,
.closed = initiate_closed,
};
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layer_surface_configure,
.closed = layer_surface_closed,
@@ -194,7 +225,7 @@ wl_touch_down(void *data, struct wl_touch *wl_touch, uint32_t serial,
uint32_t time, struct wl_surface *surface, int32_t id,
wl_fixed_t x, wl_fixed_t y)
{
if(!popup_xdg_surface_configured) {
if(!layer_surface_configured || !popup_xdg_surface_configured) {
return;
}
@@ -220,7 +251,7 @@ void
wl_touch_up(void *data, struct wl_touch *wl_touch, uint32_t serial,
uint32_t time, int32_t id)
{
if(!popup_xdg_surface_configured) {
if(!layer_surface_configured || !popup_xdg_surface_configured) {
return;
}
@@ -231,7 +262,7 @@ void
wl_touch_motion(void *data, struct wl_touch *wl_touch, uint32_t time,
int32_t id, wl_fixed_t x, wl_fixed_t y)
{
if(!popup_xdg_surface_configured) {
if(!layer_surface_configured || !popup_xdg_surface_configured) {
return;
}
@@ -283,7 +314,7 @@ void
wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time,
wl_fixed_t surface_x, wl_fixed_t surface_y)
{
if(!popup_xdg_surface_configured) {
if(!layer_surface_configured || !popup_xdg_surface_configured) {
return;
}
@@ -299,7 +330,7 @@ void
wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
uint32_t time, uint32_t button, uint32_t state)
{
if(!popup_xdg_surface_configured) {
if(!layer_surface_configured || !popup_xdg_surface_configured) {
return;
}
@@ -328,7 +359,7 @@ void
wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time,
uint32_t axis, wl_fixed_t value)
{
if(!popup_xdg_surface_configured) {
if(!layer_surface_configured || !popup_xdg_surface_configured) {
return;
}
@@ -368,82 +399,6 @@ seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name)
{
}
void
wl_surface_enter(void *data, struct wl_surface *wl_surface,
struct wl_output *wl_output)
{
struct Output *old_output = current_output;
for (int i = 0; i < wl_outputs_size; i += 1) {
if (wl_outputs[i].data == wl_output) {
current_output = &wl_outputs[i];
break;
}
}
if (current_output == old_output) {
return;
}
keyboard.preferred_scale = current_output->scale;
flip_landscape();
}
void
wl_surface_leave(void *data, struct wl_surface *wl_surface,
struct wl_output *wl_output) {
}
static void
display_handle_geometry(void *data, struct wl_output *wl_output, int x, int y,
int physical_width, int physical_height, int subpixel,
const char *make, const char *model, int transform)
{
struct Output *output = data;
// Swap width and height on rotated displays
if (transform % 2 != 0) {
int tmp = physical_width;
physical_width = physical_height;
physical_height = tmp;
}
output->w = physical_width;
output->h = physical_height;
if (current_output == output) {
flip_landscape();
};
}
static void
display_handle_done(void *data, struct wl_output *wl_output)
{
}
static void
display_handle_scale(void *data, struct wl_output *wl_output, int32_t scale)
{
struct Output *output = data;
output->scale = scale;
if (current_output == output) {
keyboard.preferred_scale = scale;
flip_landscape();
};
}
static void
display_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags,
int width, int height, int refresh)
{
}
static const struct wl_output_listener output_listener = {
.geometry = display_handle_geometry,
.mode = display_handle_mode,
.done = display_handle_done,
.scale = display_handle_scale};
static void
xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
{
@@ -460,19 +415,9 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name,
{
if (strcmp(interface, wl_compositor_interface.name) == 0) {
compositor =
wl_registry_bind(registry, name, &wl_compositor_interface, 3);
wl_registry_bind(registry, name, &wl_compositor_interface, 6);
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
draw_ctx.shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, wl_output_interface.name) == 0) {
if (wl_outputs_size < WL_OUTPUTS_LIMIT) {
struct Output *output = &wl_outputs[wl_outputs_size];
output->data =
wl_registry_bind(registry, name, &wl_output_interface, 2);
output->name = name;
output->scale = 1;
wl_output_add_listener(output->data, &output_listener, output);
wl_outputs_size += 1;
}
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
wl_seat_add_listener(seat, &seat_listener, NULL);
@@ -499,16 +444,6 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name,
void
handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
{
for (int i = 0; i < wl_outputs_size; i += 1) {
if (wl_outputs[i].name == name) {
wl_output_destroy(wl_outputs[i].data);
for (; i < wl_outputs_size - 1; i += 1) {
wl_outputs[i] = wl_outputs[i + 1];
}
wl_outputs_size -= 1;
break;
}
}
}
static void
@@ -554,15 +489,9 @@ static const struct wp_fractional_scale_v1_listener
};
void
flip_landscape()
redimension_keyboard()
{
bool previous_landscape = keyboard.landscape;
if (current_output) {
keyboard.landscape = current_output->w > current_output->h;
} else if (wl_outputs_size) {
keyboard.landscape = wl_outputs[0].w > wl_outputs[0].h;
}
keyboard.landscape = available_width > available_height;
enum layout_id layer;
if (keyboard.landscape) {
@@ -573,101 +502,80 @@ flip_landscape()
height = normal_height;
}
keyboard.w = available_width;
keyboard.h = height;
keyboard.layout = &keyboard.layouts[layer];
keyboard.layer_index = 0;
keyboard.prevlayout = keyboard.layout;
keyboard.last_abc_layout = keyboard.layout;
keyboard.last_abc_index = 0;
if (layer_surface && previous_landscape != keyboard.landscape) {
if (popup_xdg_popup) {
xdg_popup_destroy(popup_xdg_popup);
popup_xdg_popup = NULL;
}
if (popup_xdg_surface) {
xdg_surface_destroy(popup_xdg_surface);
popup_xdg_surface = NULL;
}
if (popup_draw_surf.surf) {
wl_surface_destroy(popup_draw_surf.surf);
popup_draw_surf.surf = NULL;
}
zwlr_layer_surface_v1_destroy(layer_surface);
layer_surface = NULL;
wl_surface_destroy(draw_surf.surf);
show();
}
}
void
layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
uint32_t serial, uint32_t w, uint32_t h)
{
// Swallow events for old/destroyed surface
if (surface != layer_surface) {
zwlr_layer_surface_v1_ack_configure(surface, serial);
return;
};
// Not what we expected, or redimension, refresh and restart
if (keyboard.w != w || keyboard.h != h) {
zwlr_layer_surface_v1_ack_configure(surface, serial);
hide();
show();
return;
};
// Swallow useless events
if (layer_surface_configured) {
return;
};
layer_surface_configured = true;
double scale = keyboard.preferred_scale;
if (keyboard.preferred_fractional_scale) {
scale = keyboard.preferred_fractional_scale;
}
if (keyboard.w != w || keyboard.h != h || keyboard.scale != scale ||
hidden) {
keyboard.scale = scale;
hidden = false;
keyboard.w = w;
keyboard.h = h;
keyboard.scale = scale;
hidden = false;
if (wfs_mgr && viewporter) {
wp_viewport_set_destination(draw_surf_viewport, keyboard.w,
keyboard.h);
} else {
wl_surface_set_buffer_scale(draw_surf.surf, keyboard.scale);
}
if (popup_xdg_popup) {
xdg_popup_destroy(popup_xdg_popup);
}
if (popup_xdg_surface) {
xdg_surface_destroy(popup_xdg_surface);
}
if (popup_draw_surf.surf) {
wl_surface_destroy(popup_draw_surf.surf);
}
popup_draw_surf.surf = wl_compositor_create_surface(compositor);
xdg_positioner_set_size(popup_xdg_positioner, w, h * 2);
xdg_positioner_set_anchor_rect(popup_xdg_positioner, 0, -h, w, h * 2);
wl_surface_set_input_region(popup_draw_surf.surf, empty_region);
popup_xdg_surface =
xdg_wm_base_get_xdg_surface(wm_base, popup_draw_surf.surf);
popup_xdg_surface_configured = false;
xdg_surface_add_listener(popup_xdg_surface, &xdg_popup_surface_listener,
NULL);
popup_xdg_popup = xdg_surface_get_popup(popup_xdg_surface, NULL,
popup_xdg_positioner);
xdg_popup_add_listener(popup_xdg_popup, &xdg_popup_listener, NULL);
zwlr_layer_surface_v1_get_popup(layer_surface, popup_xdg_popup);
if (wfs_mgr && viewporter) {
popup_draw_surf_viewport =
wp_viewporter_get_viewport(viewporter, popup_draw_surf.surf);
wp_viewport_set_destination(popup_draw_surf_viewport, keyboard.w,
keyboard.h * 2);
} else {
wl_surface_set_buffer_scale(popup_draw_surf.surf, keyboard.scale);
}
wl_surface_commit(popup_draw_surf.surf);
zwlr_layer_surface_v1_ack_configure(surface, serial);
kbd_resize(&keyboard, layouts, NumLayouts);
drwsurf_attach(&draw_surf);
if (wfs_mgr && viewporter) {
wp_viewport_set_destination(draw_surf_viewport, keyboard.w,
keyboard.h);
} else {
zwlr_layer_surface_v1_ack_configure(surface, serial);
wl_surface_set_buffer_scale(draw_surf.surf, keyboard.scale);
}
popup_draw_surf.surf = wl_compositor_create_surface(compositor);
xdg_positioner_set_size(popup_xdg_positioner, w, h * 2);
xdg_positioner_set_anchor_rect(popup_xdg_positioner, 0, -h, w, h * 2);
wl_surface_set_input_region(popup_draw_surf.surf, empty_region);
popup_xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, popup_draw_surf.surf);
popup_xdg_surface_configured = false;
xdg_surface_add_listener(popup_xdg_surface, &xdg_popup_surface_listener, NULL);
popup_xdg_popup = xdg_surface_get_popup(popup_xdg_surface, NULL, popup_xdg_positioner);
xdg_popup_add_listener(popup_xdg_popup, &xdg_popup_listener, NULL);
zwlr_layer_surface_v1_get_popup(layer_surface, popup_xdg_popup);
if (wfs_mgr && viewporter) {
popup_draw_surf_viewport = wp_viewporter_get_viewport(viewporter, popup_draw_surf.surf);
wp_viewport_set_destination(popup_draw_surf_viewport, keyboard.w, keyboard.h * 2);
} else {
wl_surface_set_buffer_scale(popup_draw_surf.surf, keyboard.scale);
}
wl_surface_commit(popup_draw_surf.surf);
zwlr_layer_surface_v1_ack_configure(surface, serial);
kbd_resize(&keyboard, layouts, NumLayouts);
drwsurf_attach(&draw_surf);
}
void
@@ -716,6 +624,9 @@ usage(char *argv0)
" -l - Comma separated list of layers\n");
fprintf(stderr, " --landscape-layers - Comma separated list of "
"landscape layers\n");
fprintf(stderr, " --non-exclusive - Allow the keyboard to overlap"
" windows. Do not request an exclusive zone from the"
"compositor\n");
}
void
@@ -736,18 +647,40 @@ hide()
return;
}
if(wfs_draw_surf) {
if (wfs_draw_surf) {
wp_fractional_scale_v1_destroy(wfs_draw_surf);
wfs_draw_surf = NULL;
}
if(draw_surf_viewport) {
if (draw_surf_viewport) {
wp_viewport_destroy(draw_surf_viewport);
draw_surf_viewport = NULL;
}
if (popup_xdg_popup) {
xdg_popup_destroy(popup_xdg_popup);
popup_xdg_popup = NULL;
}
if (popup_xdg_surface) {
xdg_surface_destroy(popup_xdg_surface);
popup_xdg_surface = NULL;
}
if (popup_draw_surf.surf) {
wl_surface_destroy(popup_draw_surf.surf);
popup_draw_surf.surf = NULL;
}
zwlr_layer_surface_v1_destroy(layer_surface);
wl_surface_destroy(draw_surf.surf);
layer_surface = NULL;
layer_surface_configured = false;
// Cancel pending frame callback before destroying surface
if (draw_surf.frame_cb) {
wl_callback_destroy(draw_surf.frame_cb);
draw_surf.frame_cb = NULL;
}
wl_surface_destroy(draw_surf.surf);
draw_surf.attached = false;
hidden = true;
}
@@ -758,12 +691,12 @@ show()
return;
}
wl_display_sync(display);
flip_landscape();
refresh_available_dimension();
redimension_keyboard();
draw_surf.surf = wl_compositor_create_surface(compositor);
wl_surface_add_listener(draw_surf.surf, &surface_listener, NULL);
wl_surface_add_listener(
draw_surf.surf, &surface_listener, NULL);
if (wfs_mgr && viewporter) {
wfs_draw_surf = wp_fractional_scale_manager_v1_get_fractional_scale(
wfs_mgr, draw_surf.surf);
@@ -774,15 +707,15 @@ show()
}
struct wl_output *current_output_data = NULL;
if (current_output)
current_output_data = current_output->data;
layer_surface = zwlr_layer_shell_v1_get_layer_surface(
layer_shell, draw_surf.surf, current_output_data, layer, namespace);
zwlr_layer_surface_v1_set_size(layer_surface, 0, height);
zwlr_layer_surface_v1_set_anchor(layer_surface, anchor);
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, height);
if (keyboard.exclusive) {
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, height);
}
zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, false);
zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener,
NULL);
@@ -824,6 +757,25 @@ set_kbd_colors(uint8_t *bgra, char *hex)
}
}
void
refresh_available_dimension()
{
struct wl_surface *surface = wl_compositor_create_surface(compositor);
struct zwlr_layer_surface_v1 *layer_surface = zwlr_layer_shell_v1_get_layer_surface(
layer_shell, surface, NULL, layer, namespace);
zwlr_layer_surface_v1_set_anchor(layer_surface,
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
zwlr_layer_surface_v1_add_listener(layer_surface, &initiate_listener, NULL);
wl_surface_commit(surface);
wl_display_roundtrip(display);
zwlr_layer_surface_v1_destroy(layer_surface);
wl_surface_destroy(surface);
wl_display_roundtrip(display);
}
int
main(int argc, char **argv)
{
@@ -851,6 +803,7 @@ main(int argc, char **argv)
keyboard.layer_index = 0;
keyboard.preferred_scale = 1;
keyboard.preferred_fractional_scale = 0;
keyboard.exclusive = true;
uint8_t alpha = 0;
bool alpha_defined = false;
@@ -986,6 +939,8 @@ main(int argc, char **argv)
(!strcmp(argv[i], "--list-layers"))) {
list_layers();
exit(0);
} else if ((!strcmp(argv[i], "-non-exclusive")) || (!strcmp(argv[i], "--non-exclusive"))) {
keyboard.exclusive = false;
} else {
fprintf(stderr, "Invalid argument: %s\n", argv[i]);
usage(argv[0]);
@@ -1057,6 +1012,11 @@ main(int argc, char **argv)
if (keyboard.vkbd == NULL) {
die("failed to init virtual keyboard_manager\n");
}
#ifdef SHIFT_SPACE_IS_TAB
keyboard.shift_space_is_tab = true;
#else
keyboard.shift_space_is_tab = false;
#endif
kbd_init(&keyboard, (struct layout *)&layouts, layer_names_list,
landscape_layer_names_list);

View File

@@ -7,6 +7,7 @@ wvkbd - on-screen virtual keyboard for wayland compositors using wlroots
# SYNOPSIS
wvkbd-mobintl [OPTIONS]...
wvkbd-deskintl [OPTIONS]...
*NOTE*: Your binary may have a different suffix depending on which layout you compiled.
@@ -50,6 +51,10 @@ can be patched to add new features.
*--hidden*
Start hidden (send SIGUSR2 to show).
*--non-exclusive*
Allow keyboard to overlap existing windows, do not request an
exclusive zone from the compositor.
*--alpha* _int_
Set alpha value (i.e. transparency) for all colors [0-255]