50 Commits

Author SHA1 Message Date
b0fd6777fc namespace: change to wvkbd
Hyprland recently added a layerrule to allow certain layers to appear on the lockscreen,
but layers only identify themselves by the namespace.

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2025-04-26 23:43:55 +02:00
3142271882 Do not expose drwsurf_flip 2025-04-18 07:51:19 +02:00
ace77b2aaa Tie the damage tracking to the drawing methods
So that keyboard.c just need to draw things, and stop worring about
damaging.
2025-04-18 07:51:19 +02:00
47d27e48d5 gitignore man page 2025-04-18 07:41:27 +02:00
3826cd6050 version bump 2025-04-17 22:02:29 +02:00
73caeee513 Deduplicate some drwbuf fields
We have those fields on the drwbuf struct, but we always use the
same ones. Let's deduplicate those fields by moving them on the drwsurf.

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2025-04-17 19:48:53 +02:00
55b88bc14f Optimize the backporting condition
At the moment we flip the two buffers, and we backport the damaged
area on every frame callback. We completely ignore the compositor
sent event buffer->release. This event means that the compositor
finished reading the buffer pixels, and will never read them again. So
we can keep using the same buffer instead of fliping, if we receive the
event before asking for a new frame callback.

The change is not complicated, but we have to distinguish
drwsurf_attach, and drwsurf_flip. We try to flip before trying to
draw anything. And if the buffer has already been released, we don't
flip.

We also have to track the backport_damage separately than the buffer
current damage.

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2025-04-17 19:48:50 +02:00
686b27a72d Implement double buffering
Until this point we was using one single buffer. This cause tearing when
they are scanned while we are drawing.

This refactorize our buffer and damage tracking to use two buffers. One
buffer, the back_buffer, is our dirty area. The other one, the
display_buffer, is left untouched.

Now we only flip buffers on adequate moments, indicated by the
compositor sending the frame callback event. We can draw multiple
time between those events, and we store all damaged areas. We only
schedule frame callbacks when needed, after storing a new damaged area.

Once flipped, we backport the damaged area from the new display_buffer
to the new back_buffer. Which generally means, for wvkbd, one rectangle
for each one of the surf and popup_surf.

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2025-04-17 19:48:43 +02:00
646d47d072 fixup: spacing 2025-04-15 23:44:51 +02:00
e0ecf38853 add missing cyrillic letter from ukrainian alphabet
Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2025-04-15 23:44:28 +02:00
ca53099c9d version bump after release 2024-11-25 13:40:36 +01:00
29639c28d5 Makefile: minor update to make clean 2024-11-25 13:18:53 +01:00
745a0b6f53 documentation: added section on compose button to man page 2024-11-25 13:11:50 +01:00
c742b58f51 documentation: updated man page
Using scdoc to make editing a bit more comfortable.
2024-11-25 13:01:20 +01:00
97efb1de74 README: fixed image links 2024-11-25 12:35:59 +01:00
125664afd5 use OVERLAY so keyboard is visible over fullscreen windows as well
Ref: https://github.com/jjsullivan5196/wvkbd/issues/73
2024-11-25 12:31:19 +01:00
6041473421 Only draw caps-lock upper-case labels for alphabetical keys
Fixes an issue introduced in 9d130e7fd2
2024-11-25 12:19:11 +01:00
9cc8931b46 Fix the initial output geometry guessing
wvkbd adapt its height, and layout depending on if the screen
is in landscape mode or not.

The only realiable way to know which outputs our surface is
rendered on is the wl_surface->enter(wl_output) event. But we receive
this event very late in the flow. After the first frame rendering, and
commit to the surface.

There already is some code to guess the best initial tentative, on the
most simple situations, to try to avoid an immediate re-creation of the
Wayland objects, buffer, and re-rendering of the layout.

But two issues was present:

First, we need a second roundtrip of the events, after the global
handle. This make the compositor to send the wl_outputs events earlier,
giving their geometry before our first show().

Then, the code that loop on them, if we don't already know the
current_output, was wrong. Because we changed the default state to
landscaped a while ago.

We also change how this code behave, we use the very first wl_output we
know about. Every behaviors are somehow wrong at this point, this one is
the simplest.

Now when starting wvkbd with only one screen, we don't need a second
loop anymore.

On situation where multiple screens are present, it will eventually need
a second one.

Signed-off-by: Willow Barraco <contact@willowbarraco.fr>
Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2024-11-05 10:14:12 +01:00
7d195c8217 Skip the first resize when landscaped while starting
Signed-off-by: Willow Barraco <contact@willowbarraco.fr>
Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2024-11-02 21:27:31 +01:00
d5db545dcc Restore cairo also when using rounding
Cairo is properly restored when not rounding, but in the codepath for
rounding it is not. Call cairo_restore() in both cases, otherwise
artefacts appear when using transparent fonts.

Signed-off-by: Frank Oltmanns <frank@oltmanns.dev>
Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2024-10-26 20:49:11 +02:00
7d677d23b7 Makefile: accomodate cross-build pkg-config 2024-10-02 23:26:24 +02:00
fe3257450a version bump 2024-09-01 18:23:07 +02:00
1852b3ab06 Don't forget the obscure AltGr modifier key 2024-09-01 18:21:53 +02:00
9d130e7fd2 Make all modifiers except capslock one shot and redraw keyboard when capslock pressed 2024-09-01 18:21:53 +02:00
2f72b176cb version bump 2024-05-04 19:42:12 +02:00
ba778478e6 added -R parameter to configure rounding 2024-04-15 20:38:02 +02:00
d423720553 Add basic rounding
There's probably a better way of doing this, But this will do for now.

Added basic rounding to buttons.

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2024-04-14 18:18:34 +02:00
e3081fb6e9 fixed malfunctioning theme at random 2024-04-12 22:07:35 +02:00
8106d7606d Check if popup surf configured on callbacks
In some situations, wl_touch_* events come between layer_surface_configure and xdg_popup_surface_configure.
It causes the keyboard to be drawn before the popup surf is configured, leading to "error 3: xdg_surface has never been configured".
This commit fixes this.
2024-03-29 23:16:53 +01:00
bb237f5afa Re-open the keyboard on the same output it was
Even if the user is focusing another output while the geometry change.

Signed-off-by: Willow Barraco <contact@willowbarraco.fr>
Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2024-03-03 11:33:58 +01:00
b083169ee4 Minimise visual glitches when starting
Most of the time, the first frame have to be rendered without knowing
which output is the current one. It means the first window could have
incorrect dimensions. This cause other program surface to shrink,
then grow back.

Let's try a smoother approach:

If we don't know the current output, we could check if one of them is
landscaped. If this is the case, we start landscaped to minimise the
visual glitch.

Also, the compositor might choose to not send any output geometry
information before the first surface role is assigned (sway master
829c75b9). Meaning after our initial set_size request. So we have to start
landscaped, and eventually flip to horizontal.

Thanks to the patch "Skip the first resize when landscaped while
starting", we do not draw multiple time on the buffer when we switch to vertical
mode.

This should cover most of the cases, and produce a more discrete
start.

Signed-off-by: Willow Barraco <contact@willowbarraco.fr>
Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2024-03-01 22:54:34 +01:00
538b48d08d fix fractional scalled buffer missing one pixel
Before 1920*1080 scaled 1.40 was giving a buffer width of 1919 pixels.

The buffer dimensions have to be ceiled here, instead of rounded.

The rest of the dimensions have to stay the same, here 1371x120.

Signed-off-by: Willow Barraco <contact@willowbarraco.fr>
Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2024-01-30 18:44:05 +01:00
de3b9a77e4 event loop: exit if the wayland socket disappears
(prevents infinite loop when your compositor crashes)

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2024-01-30 18:42:17 +01:00
77c6cf4fe6 implemented a stub wl_surface_leave
This fixes "listener function for opcode 1 of wl_surface is NULL" error in wayfire 0.8.0

Ref: https://github.com/jjsullivan5196/wvkbd/issues/52
2023-11-10 20:27:40 +01:00
927918ccc5 version bump 2023-11-04 14:33:44 +01:00
9f8e73b315 fixup: re-add flip_landscape() to wl_surface_enter (needed for landscape detection) 2023-11-04 14:33:30 +01:00
0e17680041 do not refresh on wl_surface_enter
there is no need to do so, because this event is followed by layer_surface_configure.
sway-HEAD(020a572) complains "A configure is scheduled for an uninitialized xdg_surface",
but it works anyway.

fix #50
2023-11-04 14:32:04 +01:00
3cccdab2e8 version bump 2023-11-03 20:53:14 +01:00
2d01b8963b fix hyprland crashes when creating a surface multiple times 2023-11-03 20:47:29 +01:00
c457d697aa optimize output iteration 2023-11-03 20:47:29 +01:00
5471e2ea0e resize keyboard only when entered to different output
the keyboard layout is saved even when hide/show (if output is same).
this is old behavior.
2023-11-03 20:47:29 +01:00
a126945401 handle screen resize and redraw in layer_surface_configure 2023-11-03 20:47:29 +01:00
aa9e8ab7ab avoid using "wl_output" literal 2023-10-29 16:42:19 +01:00
d6439afcb9 Make font selection scheme specific
Add the members font and font_descriptor to struct clr_scheme, so that
it is possible to specify a font for each scheme.

During initialization create the font descriptors for each scheme.

Instead of initially setting the font descriptor when setting up the
buffer, set the font descriptor when drawing the text.

Signed-off-by: Frank Oltmanns <frank@oltmanns.dev>
Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2023-10-29 13:07:57 +01:00
6e52be343d Add support for multiple schemes
Allow to use more than two color schemes.

Signed-off-by: Maarten van Gompel <proycon@anaproy.nl>
2023-10-13 21:20:18 +02:00
aaff22a105 set preferred scale default to 1
Ref: https://github.com/jjsullivan5196/wvkbd/issues/43
2023-09-22 18:42:27 +02:00
ab56a2748b Add missing include 2023-09-18 17:14:04 +02:00
7ff5605303 version bump 2023-09-18 17:07:38 +02:00
02027621af Cleanups
This cleanup a bit how we store and use the preferred_scale and
preferred_fractional_scale.

This rename some methods to make their behavior more explicit.
2023-09-18 12:13:43 +02:00
63c209ec7f Regression fix: keyboard didn't re-show on SIGUSR2 anymore
When triggering show() on kill USR2, everything works as expected, but
the keyboard attributes are the same as before. So in
layer_surface_configure, we don't match the condition.

Checking hidden, and resetting it from inside this scope is enough to
solve the problem.
2023-09-18 12:13:34 +02:00
13 changed files with 606 additions and 267 deletions

1
.gitignore vendored
View File

@ -6,5 +6,6 @@ include/config.h
.gdb_history .gdb_history
*.log *.log
wvkbd wvkbd
*.1
config.h config.h
wvkbd-mobintl wvkbd-mobintl

View File

@ -10,9 +10,10 @@ PKGS = wayland-client xkbcommon pangocairo
WVKBD_SOURCES += $(wildcard $(SRC)/*.c) WVKBD_SOURCES += $(wildcard $(SRC)/*.c)
WVKBD_HEADERS += $(wildcard $(SRC)/*.h) WVKBD_HEADERS += $(wildcard $(SRC)/*.h)
PKG_CONFIG ?= pkg-config
CFLAGS += -std=gnu99 -Wall -g -DWITH_WAYLAND_SHM -DLAYOUT=\"layout.${LAYOUT}.h\" -DKEYMAP=\"keymap.${LAYOUT}.h\" CFLAGS += -std=gnu99 -Wall -g -DWITH_WAYLAND_SHM -DLAYOUT=\"layout.${LAYOUT}.h\" -DKEYMAP=\"keymap.${LAYOUT}.h\"
CFLAGS += $(shell pkg-config --cflags $(PKGS)) CFLAGS += $(shell $(PKG_CONFIG) --cflags $(PKGS))
LDFLAGS += $(shell pkg-config --libs $(PKGS)) -lm -lutil -lrt LDFLAGS += $(shell $(PKG_CONFIG) --libs $(PKGS)) -lm -lutil -lrt
WAYLAND_HEADERS = $(wildcard proto/*.xml) WAYLAND_HEADERS = $(wildcard proto/*.xml)
@ -20,9 +21,12 @@ HDRS = $(WAYLAND_HEADERS:.xml=-client-protocol.h)
WAYLAND_SRC = $(HDRS:.h=.c) WAYLAND_SRC = $(HDRS:.h=.c)
SOURCES = $(WVKBD_SOURCES) $(WAYLAND_SRC) SOURCES = $(WVKBD_SOURCES) $(WAYLAND_SRC)
SCDOC=scdoc
DOCS = wvkbd.1
OBJECTS = $(SOURCES:.c=.o) OBJECTS = $(SOURCES:.c=.o)
all: ${BIN} all: ${BIN} ${DOCS}
config.h: config.h:
cp config.def.h config.h cp config.def.h config.h
@ -39,11 +43,14 @@ wvkbd-${LAYOUT}: config.h $(OBJECTS) layout.${LAYOUT}.h
$(CC) -o wvkbd-${LAYOUT} $(OBJECTS) $(LDFLAGS) $(CC) -o wvkbd-${LAYOUT} $(OBJECTS) $(LDFLAGS)
clean: clean:
rm -f $(OBJECTS) $(HDRS) $(WAYLAND_SRC) ${BIN} rm -f $(OBJECTS) $(HDRS) $(WAYLAND_SRC) ${BIN} ${DOCS}
format: format:
clang-format -i $(WVKBD_SOURCES) $(WVKBD_HEADERS) clang-format -i $(WVKBD_SOURCES) $(WVKBD_HEADERS)
%: %.scd
$(SCDOC) < $< > $@
install: all install: all
mkdir -p ${DESTDIR}${PREFIX}/bin mkdir -p ${DESTDIR}${PREFIX}/bin
cp -f ${NAME}-${LAYOUT} ${DESTDIR}${PREFIX}/bin cp -f ${NAME}-${LAYOUT} ${DESTDIR}${PREFIX}/bin

View File

@ -1,6 +1,6 @@
# wvkbd - On-screen keyboard for wlroots that sucks less # wvkbd - On-screen keyboard for wlroots that sucks less
<img src="https://raw.githubusercontent.com/proycon/wvkbd/master/contrib/wvkbd-mobintl.jpg" width=300 /> <img src="https://raw.githubusercontent.com/proycon/wvkbd/master/contrib/wvkbd-mobintl-cyrillic.jpg" width=300 /> <img src="https://raw.githubusercontent.com/jjsullivan5196/wvkbd/master/contrib/wvkbd-mobintl.jpg" width=300 /> <img src="https://raw.githubusercontent.com/jjsullivan5196/wvkbd/master/contrib/wvkbd-mobintl-cyrillic.jpg" width=300 />
This project aims to deliver a minimal but practically usable implementation of a wlroots on-screen This project aims to deliver a minimal but practically usable implementation of a wlroots on-screen
keyboard in legible C. This will **only** be a keyboard, not a feedback buzzer, keyboard in legible C. This will **only** be a keyboard, not a feedback buzzer,
@ -25,7 +25,7 @@ new features.
- Automatic portrait/landscape detection and subsequent layout switching - Automatic portrait/landscape detection and subsequent layout switching
<img src="https://raw.githubusercontent.com/proycon/wvkbd/master/contrib/wvkbd-mobintl-landscape.jpg" width=640 /> <img src="https://raw.githubusercontent.com/jjsullivan5196/wvkbd/master/contrib/wvkbd-mobintl-landscape.jpg" width=640 />
There are some areas that still need work: There are some areas that still need work:
@ -42,6 +42,8 @@ You'll need the following developer packages
- wayland-client - wayland-client
- xkbcommon - xkbcommon
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` 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 The default set of layouts is called `mobintl` *(mobile international)*, which groups various layouts aimed at mobile devices

View File

@ -1,24 +1,31 @@
#ifndef config_def_h_INCLUDED #ifndef config_def_h_INCLUDED
#define config_def_h_INCLUDED #define config_def_h_INCLUDED
static const char *default_font = "Sans 14"; #define DEFAULT_FONT "Sans 14"
#define DEFAULT_ROUNDING 5
static const int transparency = 255; static const int transparency = 255;
struct clr_scheme scheme = { struct clr_scheme schemes[] = {
{
/* colors */ /* colors */
.bg = {.bgra = {15, 15, 15, transparency}}, .bg = {.bgra = {15, 15, 15, transparency}},
.fg = {.bgra = {45, 45, 45, transparency}}, .fg = {.bgra = {45, 45, 45, transparency}},
.high = {.bgra = {100, 100, 100, transparency}}, .high = {.bgra = {100, 100, 100, transparency}},
.swipe = {.bgra = {100, 255, 100, 64}}, .swipe = {.bgra = {100, 255, 100, 64}},
.text = {.color = UINT32_MAX}, .text = {.color = UINT32_MAX},
}; .font = DEFAULT_FONT,
struct clr_scheme scheme1 = { .rounding = DEFAULT_ROUNDING,
},
{
/* colors */ /* colors */
.bg = {.bgra = {15, 15, 15, transparency}}, .bg = {.bgra = {15, 15, 15, transparency}},
.fg = {.bgra = {32, 32, 32, transparency}}, .fg = {.bgra = {32, 32, 32, transparency}},
.high = {.bgra = {100, 100, 100, transparency}}, .high = {.bgra = {100, 100, 100, transparency}},
.swipe = {.bgra = {100, 255, 100, 64}}, .swipe = {.bgra = {100, 255, 100, 64}},
.text = {.color = UINT32_MAX}, .text = {.color = UINT32_MAX},
.font = DEFAULT_FONT,
.rounding = DEFAULT_ROUNDING,
}
}; };
/* layers is an ordered list of layouts, used to cycle through */ /* layers is an ordered list of layouts, used to cycle through */

View File

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

218
drw.c
View File

@ -4,37 +4,139 @@
#include "drw.h" #include "drw.h"
#include "shm_open.h" #include "shm_open.h"
#include "math.h"
void drwbuf_handle_release(void *data, struct wl_buffer *wl_buffer) {
struct drwsurf *ds = data;
ds->released = true;
};
const struct wl_buffer_listener buffer_listener = {
.release = drwbuf_handle_release
};
void drwsurf_handle_frame_cb(void* data, struct wl_callback* callback,
uint32_t time)
{
struct drwsurf *ds = data;
wl_callback_destroy(ds->frame_cb);
ds->frame_cb = NULL;
cairo_rectangle_int_t r = {0};
for (int i = 0; i < cairo_region_num_rectangles(ds->damage); i++) {
cairo_region_get_rectangle(ds->damage, i, &r);
wl_surface_damage(ds->surf, r.x, r.y, r.width, r.height);
};
cairo_region_subtract(ds->damage, ds->damage);
drwsurf_attach(ds);
}
const struct wl_callback_listener frame_listener = {
.done = drwsurf_handle_frame_cb
};
void drwsurf_register_frame_cb(struct drwsurf *ds)
{
if (ds->frame_cb)
return;
if (!ds->attached)
return;
ds->frame_cb = wl_surface_frame(ds->surf);
wl_callback_add_listener(ds->frame_cb, &frame_listener, ds);
wl_surface_commit(ds->surf);
}
void drwsurf_damage(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
cairo_rectangle_int_t rect = { x, y, w, h };
cairo_region_union_rectangle(ds->damage, &rect);
cairo_region_union_rectangle(ds->backport_damage, &rect);
drwsurf_register_frame_cb(ds);
}
void void
drwsurf_resize(struct drwsurf *ds, uint32_t w, uint32_t h, double s) drwsurf_resize(struct drwsurf *ds, uint32_t w, uint32_t h, double s)
{ {
if (ds->buf) {
munmap(ds->pool_data, ds->size);
wl_buffer_destroy(ds->buf);
ds->buf = NULL;
}
ds->scale = s; ds->scale = s;
ds->width = w * s; ds->width = ceil(w * s);
ds->height = h * s; ds->height = ceil(h * s);
setup_buffer(ds); if (ds->damage)
cairo_region_destroy(ds->damage);
ds->damage = cairo_region_create();
if (ds->backport_damage)
cairo_region_destroy(ds->backport_damage);
ds->backport_damage = cairo_region_create();
ds->released = true;
setup_buffer(ds, ds->back_buffer);
setup_buffer(ds, ds->display_buffer);
}
void
drwsurf_backport(struct drwsurf *ds)
{
cairo_save(ds->back_buffer->cairo);
cairo_scale(ds->back_buffer->cairo, 1/ds->scale, 1/ds->scale);
cairo_set_operator(ds->back_buffer->cairo, CAIRO_OPERATOR_SOURCE);
cairo_rectangle_int_t r = {0};
for (int i = 0; i < cairo_region_num_rectangles(ds->backport_damage); i++) {
cairo_region_get_rectangle(ds->backport_damage, i, &r);
cairo_set_source_surface(ds->back_buffer->cairo, ds->display_buffer->cairo_surf, 0, 0);
cairo_rectangle(
ds->back_buffer->cairo,
r.x * ds->scale,
r.y * ds->scale,
r.width * ds->scale,
r.height * ds->scale
);
cairo_fill(ds->back_buffer->cairo);
};
cairo_restore(ds->back_buffer->cairo);
cairo_region_subtract(ds->backport_damage, ds->backport_damage);
}
void
drwsurf_attach(struct drwsurf *ds)
{
wl_surface_attach(ds->surf, ds->back_buffer->buf, 0, 0);
wl_surface_commit(ds->surf);
ds->released = false;
ds->attached = true;
} }
void void
drwsurf_flip(struct drwsurf *ds) drwsurf_flip(struct drwsurf *ds)
{ {
wl_surface_attach(ds->surf, ds->buf, 0, 0); if (ds->released)
wl_surface_commit(ds->surf); return;
ds->released = true;
struct drwbuf *tmp = ds->back_buffer;
ds->back_buffer = ds->display_buffer;
ds->display_buffer = tmp;
drwsurf_backport(ds);
} }
void void
drw_draw_text(struct drwsurf *d, Color color, uint32_t x, uint32_t y, drw_draw_text(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, uint32_t b, const char *label) uint32_t w, uint32_t h, uint32_t b, const char *label,
PangoFontDescription *font_description)
{ {
drwsurf_flip(ds);
struct drwbuf *d = ds->back_buffer;
drwsurf_damage(ds, x, y, w, h);
cairo_save(d->cairo); cairo_save(d->cairo);
pango_layout_set_font_description(d->layout, font_description);
cairo_set_source_rgba( cairo_set_source_rgba(
d->cairo, color.bgra[2] / (double)255, color.bgra[1] / (double)255, d->cairo, color.bgra[2] / (double)255, color.bgra[1] / (double)255,
color.bgra[0] / (double)255, color.bgra[3] / (double)255); color.bgra[0] / (double)255, color.bgra[3] / (double)255);
@ -54,8 +156,12 @@ drw_draw_text(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
} }
void void
drw_do_clear(struct drwsurf *d, uint32_t x, uint32_t y, uint32_t w, uint32_t h) drw_do_clear(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{ {
drwsurf_flip(ds);
struct drwbuf *d = ds->back_buffer;
drwsurf_damage(ds, x, y, w, h);
cairo_save(d->cairo); cairo_save(d->cairo);
cairo_set_operator(d->cairo, CAIRO_OPERATOR_CLEAR); cairo_set_operator(d->cairo, CAIRO_OPERATOR_CLEAR);
@ -66,9 +172,13 @@ drw_do_clear(struct drwsurf *d, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
} }
void void
drw_do_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y, drw_do_rectangle(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, bool over) uint32_t w, uint32_t h, bool over, int rounding)
{ {
drwsurf_flip(ds);
struct drwbuf *d = ds->back_buffer;
drwsurf_damage(ds, x, y, w, h);
cairo_save(d->cairo); cairo_save(d->cairo);
if (over) { if (over) {
@ -77,6 +187,28 @@ drw_do_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
cairo_set_operator(d->cairo, CAIRO_OPERATOR_SOURCE); cairo_set_operator(d->cairo, CAIRO_OPERATOR_SOURCE);
} }
if (rounding > 0) {
double radius = rounding / 1.0;
double degrees = M_PI / 180.0;
cairo_new_sub_path (d->cairo);
cairo_arc (d->cairo, x + w - radius, y + radius, radius, -90 * degrees, 0 * degrees);
cairo_arc (d->cairo, x + w - radius, y + h - radius, radius, 0 * degrees, 90 * degrees);
cairo_arc (d->cairo, x + radius, y + h - radius, radius, 90 * degrees, 180 * degrees);
cairo_arc (d->cairo, x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
cairo_close_path (d->cairo);
cairo_set_source_rgba(
d->cairo, color.bgra[2] / (double)255, color.bgra[1] / (double)255,
color.bgra[0] / (double)255, color.bgra[3] / (double)255);
cairo_fill (d->cairo);
cairo_set_source_rgba(d->cairo, 0, 0, 0, 0.9);
cairo_set_line_width(d->cairo, 1.0);
cairo_stroke(d->cairo);
cairo_restore(d->cairo);
}
else {
cairo_rectangle(d->cairo, x, y, w, h); cairo_rectangle(d->cairo, x, y, w, h);
cairo_set_source_rgba( cairo_set_source_rgba(
d->cairo, color.bgra[2] / (double)255, color.bgra[1] / (double)255, d->cairo, color.bgra[2] / (double)255, color.bgra[1] / (double)255,
@ -84,60 +216,70 @@ drw_do_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
cairo_fill(d->cairo); cairo_fill(d->cairo);
cairo_restore(d->cairo); cairo_restore(d->cairo);
}
} }
void void
drw_fill_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y, drw_fill_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h) uint32_t w, uint32_t h, int rounding)
{ {
drw_do_rectangle(d, color, x, y, w, h, false); drw_do_rectangle(d, color, x, y, w, h, false, rounding);
} }
void void
drw_over_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y, drw_over_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h) uint32_t w, uint32_t h, int rounding)
{ {
drw_do_rectangle(d, color, x, y, w, h, true); drw_do_rectangle(d, color, x, y, w, h, true, rounding);
} }
uint32_t uint32_t
setup_buffer(struct drwsurf *drwsurf) setup_buffer(struct drwsurf *drwsurf, struct drwbuf *drwbuf)
{ {
int prev_size = drwbuf->size;
int stride = drwsurf->width * 4; int stride = drwsurf->width * 4;
drwsurf->size = stride * drwsurf->height; drwbuf->size = stride * drwsurf->height;
int fd = allocate_shm_file(drwsurf->size); int fd = allocate_shm_file(drwbuf->size);
if (fd == -1) { if (fd == -1) {
return 1; return 1;
} }
drwsurf->pool_data = if (drwbuf->pool_data)
mmap(NULL, drwsurf->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); munmap(drwbuf->pool_data, prev_size);
if (drwsurf->pool_data == MAP_FAILED) { drwbuf->pool_data =
mmap(NULL, drwbuf->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (drwbuf->pool_data == MAP_FAILED) {
close(fd); close(fd);
return 1; return 1;
} }
if (drwbuf->buf)
wl_buffer_destroy(drwbuf->buf);
struct wl_shm_pool *pool = struct wl_shm_pool *pool =
wl_shm_create_pool(drwsurf->ctx->shm, fd, drwsurf->size); wl_shm_create_pool(drwsurf->ctx->shm, fd, drwbuf->size);
drwsurf->buf = drwbuf->buf =
wl_shm_pool_create_buffer(pool, 0, drwsurf->width, drwsurf->height, wl_shm_pool_create_buffer(pool, 0, drwsurf->width, drwsurf->height,
stride, WL_SHM_FORMAT_ARGB8888); stride, WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool); wl_shm_pool_destroy(pool);
close(fd); close(fd);
wl_buffer_add_listener(drwbuf->buf, &buffer_listener, drwsurf);
cairo_surface_t *s = cairo_image_surface_create_for_data(
drwsurf->pool_data, CAIRO_FORMAT_ARGB32, drwsurf->width, if (drwbuf->cairo_surf)
cairo_surface_destroy(drwbuf->cairo_surf);
drwbuf->cairo_surf = cairo_image_surface_create_for_data(
drwbuf->pool_data, CAIRO_FORMAT_ARGB32, drwsurf->width,
drwsurf->height, stride); drwsurf->height, stride);
drwsurf->cairo = cairo_create(s); if (drwbuf->cairo)
cairo_scale(drwsurf->cairo, drwsurf->scale, drwsurf->scale); cairo_destroy(drwbuf->cairo);
cairo_set_antialias(drwsurf->cairo, CAIRO_ANTIALIAS_NONE); drwbuf->cairo = cairo_create(drwbuf->cairo_surf);
drwsurf->layout = pango_cairo_create_layout(drwsurf->cairo); cairo_scale(drwbuf->cairo, drwsurf->scale, drwsurf->scale);
pango_layout_set_font_description(drwsurf->layout, cairo_set_antialias(drwbuf->cairo, CAIRO_ANTIALIAS_NONE);
drwsurf->ctx->font_description); drwbuf->layout = pango_cairo_create_layout(drwbuf->cairo);
pango_layout_set_auto_dir(drwsurf->layout, false); pango_layout_set_auto_dir(drwbuf->layout, false);
cairo_save(drwsurf->cairo); cairo_save(drwbuf->cairo);
return 0; return 0;
} }

45
drw.h
View File

@ -6,43 +6,54 @@
struct drw { struct drw {
struct wl_shm *shm; struct wl_shm *shm;
PangoFontDescription *font_description; };
struct drwbuf {
uint32_t size;
struct wl_buffer *buf;
cairo_surface_t *cairo_surf;
cairo_t *cairo;
PangoLayout *layout;
unsigned char *pool_data;
}; };
struct drwsurf { struct drwsurf {
uint32_t width, height, size; uint32_t width, height;
double scale; double scale;
struct drw *ctx; struct drw *ctx;
struct wl_surface *surf; struct wl_surface *surf;
struct wl_buffer *buf;
struct wl_shm *shm; struct wl_shm *shm;
unsigned char *pool_data; struct wl_callback *frame_cb;
cairo_t *cairo; cairo_region_t *damage, *backport_damage;
PangoLayout *layout; bool attached;
bool released;
struct drwbuf *back_buffer;
struct drwbuf *display_buffer;
}; };
struct kbd; struct kbd;
void drwsurf_resize(struct drwsurf *ds, uint32_t w, uint32_t h, double s); void drwsurf_resize(struct drwsurf *ds, uint32_t w, uint32_t h, double s);
void drwsurf_flip(struct drwsurf *ds); void drwsurf_attach(struct drwsurf *ds);
typedef union { typedef union {
uint8_t bgra[4]; uint8_t bgra[4];
uint32_t color; uint32_t color;
} Color; } Color;
void drw_do_clear(struct drwsurf *d, uint32_t x, uint32_t y, void drw_do_clear(struct drwsurf *ds, uint32_t x, uint32_t y,
uint32_t w, uint32_t h);
void drw_do_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, bool fill);
void drw_fill_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h);
void drw_over_rectangle(struct drwsurf *d, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h); uint32_t w, uint32_t h);
void drw_do_rectangle(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, bool fill, int rounding);
void drw_fill_rectangle(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, int rounding);
void drw_over_rectangle(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, int rounding);
void drw_draw_text(struct drwsurf *d, Color color, uint32_t x, uint32_t y, void drw_draw_text(struct drwsurf *ds, Color color, uint32_t x, uint32_t y,
uint32_t w, uint32_t h, uint32_t b, const char *label); uint32_t w, uint32_t h, uint32_t b, const char *label,
PangoFontDescription *font_description);
uint32_t setup_buffer(struct drwsurf *drwsurf); uint32_t setup_buffer(struct drwsurf *ds, struct drwbuf *db);
#endif #endif

View File

@ -3,6 +3,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <ctype.h>
#include "keyboard.h" #include "keyboard.h"
#include "drw.h" #include "drw.h"
#include "os-compatibility.h" #include "os-compatibility.h"
@ -276,13 +277,23 @@ kbd_get_layer_index(struct kbd *kb, struct layout *l)
void void
kbd_unpress_key(struct kbd *kb, uint32_t time) kbd_unpress_key(struct kbd *kb, uint32_t time)
{ {
bool unlatch_shift = false; bool unlatch_shift, unlatch_ctrl, unlatch_alt, unlatch_super, unlatch_altgr;
unlatch_shift = unlatch_ctrl = unlatch_alt = unlatch_super = unlatch_altgr = false;
if (kb->last_press) { if (kb->last_press) {
unlatch_shift = (kb->mods & Shift) == Shift; unlatch_shift = (kb->mods & Shift) == Shift;
unlatch_ctrl = (kb->mods & Ctrl) == Ctrl;
unlatch_alt = (kb->mods & Alt) == Alt;
unlatch_super = (kb->mods & Super) == Super;
unlatch_altgr = (kb->mods & AltGr) == AltGr;
if (unlatch_shift) { if (unlatch_shift) kb->mods ^= 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); zwp_virtual_keyboard_v1_modifiers(kb->vkbd, kb->mods, 0, 0, 0);
} }
@ -304,7 +315,7 @@ kbd_unpress_key(struct kbd *kb, uint32_t time)
if (kb->compose >= 2) { if (kb->compose >= 2) {
kb->compose = 0; kb->compose = 0;
kbd_switch_layout(kb, kb->last_abc_layout, kb->last_abc_index); kbd_switch_layout(kb, kb->last_abc_layout, kb->last_abc_index);
} else if (unlatch_shift) { } else if (unlatch_shift||unlatch_ctrl||unlatch_alt||unlatch_super||unlatch_altgr) {
kbd_draw_layout(kb); kbd_draw_layout(kb);
} else { } else {
kbd_draw_key(kb, kb->last_press, Unpress); kbd_draw_key(kb, kb->last_press, Unpress);
@ -326,10 +337,7 @@ kbd_release_key(struct kbd *kb, uint32_t time)
kb->last_swipe = NULL; kb->last_swipe = NULL;
} }
drwsurf_flip(kb->surf);
kbd_clear_last_popup(kb); kbd_clear_last_popup(kb);
drwsurf_flip(kb->popup_surf);
} }
void void
@ -355,10 +363,7 @@ kbd_motion_key(struct kbd *kb, uint32_t time, uint32_t x, uint32_t y)
kbd_unpress_key(kb, time); kbd_unpress_key(kb, time);
} }
drwsurf_flip(kb->surf);
kbd_clear_last_popup(kb); kbd_clear_last_popup(kb);
drwsurf_flip(kb->popup_surf);
} }
void void
@ -418,7 +423,7 @@ kbd_press_key(struct kbd *kb, struct key *k, uint32_t time)
break; break;
case Mod: case Mod:
kb->mods ^= k->code; kb->mods ^= k->code;
if (k->code == Shift) { if ((k->code == Shift) || (k->code == CapsLock)) {
kbd_draw_layout(kb); kbd_draw_layout(kb);
} else { } else {
if (kb->mods & k->code) { if (kb->mods & k->code) {
@ -490,9 +495,6 @@ kbd_press_key(struct kbd *kb, struct key *k, uint32_t time)
default: default:
break; break;
} }
drwsurf_flip(kb->surf);
drwsurf_flip(kb->popup_surf);
} }
void void
@ -527,7 +529,8 @@ kbd_print_key_stdout(struct kbd *kb, struct key *k)
} }
if (!handled) { if (!handled) {
if ((kb->mods & Shift) || (kb->mods & CapsLock)) if ((kb->mods & Shift) ||
((kb->mods & CapsLock) & (strlen(k->label) == 1 && isalpha(k->label[0]))))
printf("%s", k->shift_label); printf("%s", k->shift_label);
else if (!(kb->mods & Ctrl) && !(kb->mods & Alt) && !(kb->mods & Super)) else if (!(kb->mods & Ctrl) && !(kb->mods & Alt) && !(kb->mods & Super))
printf("%s", k->label); printf("%s", k->label);
@ -541,9 +544,6 @@ kbd_clear_last_popup(struct kbd *kb)
if (kb->last_popup_w && kb->last_popup_h) { if (kb->last_popup_w && kb->last_popup_h) {
drw_do_clear(kb->popup_surf, kb->last_popup_x, kb->last_popup_y, drw_do_clear(kb->popup_surf, kb->last_popup_x, kb->last_popup_y,
kb->last_popup_w, kb->last_popup_h); kb->last_popup_w, kb->last_popup_h);
wl_surface_damage(kb->popup_surf->surf, kb->last_popup_x,
kb->last_popup_y, kb->last_popup_w, kb->last_popup_h);
kb->last_popup_w = kb->last_popup_h = 0; kb->last_popup_w = kb->last_popup_h = 0;
} }
} }
@ -551,32 +551,31 @@ kbd_clear_last_popup(struct kbd *kb)
void void
kbd_draw_key(struct kbd *kb, struct key *k, enum key_draw_type type) kbd_draw_key(struct kbd *kb, struct key *k, enum key_draw_type type)
{ {
const char *label = (kb->mods & Shift) ? k->shift_label : k->label; const char *label = ((kb->mods & Shift)||((kb->mods & CapsLock) &&
strlen(k->label) == 1 && isalpha(k->label[0]))) ? k->shift_label : k->label;
if (kb->debug) if (kb->debug)
fprintf(stderr, "Draw key +%d+%d %dx%d -> %s\n", k->x, k->y, k->w, k->h, fprintf(stderr, "Draw key +%d+%d %dx%d -> %s\n", k->x, k->y, k->w, k->h,
label); label);
struct clr_scheme *scheme = struct clr_scheme *scheme = &kb->schemes[k->scheme];
(k->scheme == 0) ? &(kb->scheme) : &(kb->scheme1);
switch (type) { switch (type) {
case None: case None:
case Unpress: case Unpress:
draw_inset(kb->surf, k->x, k->y, k->w, k->h, KBD_KEY_BORDER, draw_inset(kb->surf, k->x, k->y, k->w, k->h, KBD_KEY_BORDER,
scheme->fg); scheme->fg, scheme->rounding);
break; break;
case Press: case Press:
draw_inset(kb->surf, k->x, k->y, k->w, k->h, KBD_KEY_BORDER, draw_inset(kb->surf, k->x, k->y, k->w, k->h, KBD_KEY_BORDER,
scheme->high); scheme->high, scheme->rounding);
break; break;
case Swipe: case Swipe:
draw_over_inset(kb->surf, k->x, k->y, k->w, k->h, KBD_KEY_BORDER, draw_over_inset(kb->surf, k->x, k->y, k->w, k->h, KBD_KEY_BORDER,
scheme->swipe); scheme->swipe, scheme->rounding);
break; break;
} }
drw_draw_text(kb->surf, scheme->text, k->x, k->y, k->w, k->h, drw_draw_text(kb->surf, scheme->text, k->x, k->y, k->w, k->h,
KBD_KEY_BORDER, label); KBD_KEY_BORDER, label, scheme->font_description);
wl_surface_damage(kb->surf->surf, k->x, k->y, k->w, k->h);
if (type == Press || type == Unpress) { if (type == Press || type == Unpress) {
kbd_clear_last_popup(kb); kbd_clear_last_popup(kb);
@ -586,14 +585,13 @@ kbd_draw_key(struct kbd *kb, struct key *k, enum key_draw_type type)
kb->last_popup_w = k->w; kb->last_popup_w = k->w;
kb->last_popup_h = k->h; kb->last_popup_h = k->h;
drw_fill_rectangle(kb->popup_surf, kb->scheme.bg, k->x, drw_fill_rectangle(kb->popup_surf, scheme->bg, k->x,
kb->last_popup_y, k->w, k->h); kb->last_popup_y, k->w, k->h, scheme->rounding);
draw_inset(kb->popup_surf, k->x, kb->last_popup_y, k->w, k->h, draw_inset(kb->popup_surf, k->x, kb->last_popup_y, k->w, k->h,
KBD_KEY_BORDER, scheme->high); KBD_KEY_BORDER, scheme->high, scheme->rounding);
drw_draw_text(kb->popup_surf, scheme->text, k->x, kb->last_popup_y, drw_draw_text(kb->popup_surf, scheme->text, k->x, kb->last_popup_y,
k->w, k->h, KBD_KEY_BORDER, label); k->w, k->h, KBD_KEY_BORDER, label,
wl_surface_damage(kb->popup_surf->surf, k->x, kb->last_popup_y, k->w, scheme->font_description);
k->h);
} }
} }
@ -605,7 +603,7 @@ kbd_draw_layout(struct kbd *kb)
if (kb->debug) if (kb->debug)
fprintf(stderr, "Draw layout\n"); fprintf(stderr, "Draw layout\n");
drw_fill_rectangle(d, kb->scheme.bg, 0, 0, kb->w, kb->h); drw_fill_rectangle(d, kb->schemes[0].bg, 0, 0, kb->w, kb->h, 0);
while (next_key->type != Last) { while (next_key->type != Last) {
if ((next_key->type == Pad) || (next_key->type == EndRow)) { if ((next_key->type == Pad) || (next_key->type == EndRow)) {
@ -620,7 +618,6 @@ kbd_draw_layout(struct kbd *kb)
} }
next_key++; next_key++;
} }
wl_surface_damage(d->surf, 0, 0, kb->w, kb->h);
} }
void void
@ -647,17 +644,17 @@ kbd_resize(struct kbd *kb, struct layout *layouts, uint8_t layoutcount)
void void
draw_inset(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t width, draw_inset(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, uint32_t border, Color color) uint32_t height, uint32_t border, Color color, int rounding)
{ {
drw_fill_rectangle(ds, color, x + border, y + border, width - (border * 2), drw_fill_rectangle(ds, color, x + border, y + border, width - (border * 2),
height - (border * 2)); height - (border * 2), rounding);
} }
void void
draw_over_inset(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t width, draw_over_inset(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, uint32_t border, Color color) uint32_t height, uint32_t border, Color color, int rounding)
{ {
drw_over_rectangle(ds, color, x + border, y + border, width - (border * 2), drw_over_rectangle(ds, color, x + border, y + border, width - (border * 2),
height - (border * 2)); height - (border * 2), rounding);
} }
void void

View File

@ -54,6 +54,9 @@ struct clr_scheme {
Color high; Color high;
Color swipe; Color swipe;
Color text; Color text;
char *font;
int rounding;
PangoFontDescription *font_description;
}; };
struct key { struct key {
@ -90,13 +93,13 @@ struct kbd {
bool debug; bool debug;
struct layout *layout; struct layout *layout;
struct clr_scheme scheme; struct clr_scheme *schemes;
struct clr_scheme scheme1;
bool print; bool print;
bool print_intersect; bool print_intersect;
uint32_t w, h; uint32_t w, h;
double scale, pending_scale; double scale;
double preferred_scale, preferred_fractional_scale;
bool landscape; bool landscape;
uint8_t mods; uint8_t mods;
uint8_t compose; uint8_t compose;
@ -119,9 +122,9 @@ struct kbd {
}; };
void draw_inset(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t width, void draw_inset(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, uint32_t border, Color color); uint32_t height, uint32_t border, Color color, int rounding);
void draw_over_inset(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t width, void draw_over_inset(struct drwsurf *ds, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, uint32_t border, Color color); uint32_t height, uint32_t border, Color color, int rounding);
void kbd_init(struct kbd *kb, struct layout *layouts, void kbd_init(struct kbd *kb, struct layout *layouts,
char *layer_names_list, char *landscape_layer_names_list); char *layer_names_list, char *landscape_layer_names_list);

View File

@ -1626,7 +1626,8 @@ static struct key keys_compose_cyr_tse[] = {
static struct key keys_compose_cyr_g[] = { static struct key keys_compose_cyr_g[] = {
{"ѓ", "Ѓ", 1.0, Copy, 0x0453, 0, 0x0403}, {"ѓ", "Ѓ", 1.0, Copy, 0x0453, 0, 0x0403},
{"", "", 9.0, Pad}, {"ґ", "Ґ", 1.0, Copy, 0x0491, 0, 0x0490},
{"", "", 8.0, Pad},
{"", "", 0.0, EndRow}, {"", "", 0.0, EndRow},
{"", "", 0.0, EndRow}, {"", "", 0.0, EndRow},
{"", "", 1.5, Mod, Shift, .scheme = 1}, {"", "", 1.5, Mod, Shift, .scheme = 1},

259
main.c
View File

@ -3,6 +3,7 @@
#include "proto/xdg-shell-client-protocol.h" #include "proto/xdg-shell-client-protocol.h"
#include "proto/fractional-scale-v1-client-protocol.h" #include "proto/fractional-scale-v1-client-protocol.h"
#include "proto/viewporter-client-protocol.h" #include "proto/viewporter-client-protocol.h"
#include <errno.h>
#include <linux/input-event-codes.h> #include <linux/input-event-codes.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -23,8 +24,11 @@
fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, __VA_ARGS__); \
exit(1) exit(1)
/* array size */
#define countof(x) (sizeof(x) / sizeof(*x))
/* client state */ /* client state */
static const char *namespace = "wlroots"; static const char *namespace = "wvkbd";
static struct wl_display *display; static struct wl_display *display;
static struct wl_compositor *compositor; static struct wl_compositor *compositor;
static struct wl_seat *seat; static struct wl_seat *seat;
@ -42,6 +46,7 @@ static struct wp_fractional_scale_v1 *wfs_draw_surf;
static struct wp_fractional_scale_manager_v1 *wfs_mgr; static struct wp_fractional_scale_manager_v1 *wfs_mgr;
static struct wp_viewport *draw_surf_viewport, *popup_draw_surf_viewport; static struct wp_viewport *draw_surf_viewport, *popup_draw_surf_viewport;
static struct wp_viewporter *viewporter; static struct wp_viewporter *viewporter;
static bool popup_xdg_surface_configured;
struct Output { struct Output {
uint32_t name; uint32_t name;
@ -57,10 +62,11 @@ static int wl_outputs_size;
/* drawing */ /* drawing */
static struct drw draw_ctx; static struct drw draw_ctx;
static struct drwbuf draw_surf_back_buffer, draw_surf_display_buffer, popup_draw_surf_back_buffer, popup_draw_surf_display_buffer;
static struct drwsurf draw_surf, popup_draw_surf; static struct drwsurf draw_surf, popup_draw_surf;
/* layer surface parameters */ /* layer surface parameters */
static uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; static uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
static uint32_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | static uint32_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
@ -71,6 +77,7 @@ static int cur_x = -1, cur_y = -1;
static bool cur_press = false; static bool cur_press = false;
static struct kbd keyboard; static struct kbd keyboard;
static uint32_t height, normal_height, landscape_height; static uint32_t height, normal_height, landscape_height;
static int rounding = DEFAULT_ROUNDING;
static bool hidden = false; static bool hidden = false;
/* event handler prototypes */ /* event handler prototypes */
@ -111,6 +118,8 @@ static void seat_handle_name(void *data, struct wl_seat *wl_seat,
static void wl_surface_enter(void *data, struct wl_surface *wl_surface, static void wl_surface_enter(void *data, struct wl_surface *wl_surface,
struct wl_output *wl_output); 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, static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t name, const char *interface,
@ -123,7 +132,8 @@ static void layer_surface_configure(void *data,
uint32_t serial, uint32_t w, uint32_t h); uint32_t serial, uint32_t w, uint32_t h);
static void layer_surface_closed(void *data, static void layer_surface_closed(void *data,
struct zwlr_layer_surface_v1 *surface); struct zwlr_layer_surface_v1 *surface);
static void resize(); static void flip_landscape();
static void show();
/* event handlers */ /* event handlers */
static const struct wl_pointer_listener pointer_listener = { static const struct wl_pointer_listener pointer_listener = {
@ -151,6 +161,7 @@ static const struct wl_seat_listener seat_listener = {
static const struct wl_surface_listener surface_listener = { static const struct wl_surface_listener surface_listener = {
.enter = wl_surface_enter, .enter = wl_surface_enter,
.leave = wl_surface_leave,
}; };
static const struct wl_registry_listener registry_listener = { static const struct wl_registry_listener registry_listener = {
@ -183,6 +194,10 @@ wl_touch_down(void *data, struct wl_touch *wl_touch, uint32_t serial,
uint32_t time, struct wl_surface *surface, int32_t id, uint32_t time, struct wl_surface *surface, int32_t id,
wl_fixed_t x, wl_fixed_t y) wl_fixed_t x, wl_fixed_t y)
{ {
if(!popup_xdg_surface_configured) {
return;
}
struct key *next_key; struct key *next_key;
uint32_t touch_x, touch_y; uint32_t touch_x, touch_y;
@ -205,6 +220,10 @@ void
wl_touch_up(void *data, struct wl_touch *wl_touch, uint32_t serial, wl_touch_up(void *data, struct wl_touch *wl_touch, uint32_t serial,
uint32_t time, int32_t id) uint32_t time, int32_t id)
{ {
if(!popup_xdg_surface_configured) {
return;
}
kbd_release_key(&keyboard, time); kbd_release_key(&keyboard, time);
} }
@ -212,6 +231,10 @@ void
wl_touch_motion(void *data, struct wl_touch *wl_touch, uint32_t time, wl_touch_motion(void *data, struct wl_touch *wl_touch, uint32_t time,
int32_t id, wl_fixed_t x, wl_fixed_t y) int32_t id, wl_fixed_t x, wl_fixed_t y)
{ {
if(!popup_xdg_surface_configured) {
return;
}
uint32_t touch_x, touch_y; uint32_t touch_x, touch_y;
touch_x = wl_fixed_to_int(x); touch_x = wl_fixed_to_int(x);
@ -260,6 +283,10 @@ void
wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time,
wl_fixed_t surface_x, wl_fixed_t surface_y) wl_fixed_t surface_x, wl_fixed_t surface_y)
{ {
if(!popup_xdg_surface_configured) {
return;
}
cur_x = wl_fixed_to_int(surface_x); cur_x = wl_fixed_to_int(surface_x);
cur_y = wl_fixed_to_int(surface_y); cur_y = wl_fixed_to_int(surface_y);
@ -272,6 +299,10 @@ void
wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
uint32_t time, uint32_t button, uint32_t state) uint32_t time, uint32_t button, uint32_t state)
{ {
if(!popup_xdg_surface_configured) {
return;
}
struct key *next_key; struct key *next_key;
cur_press = state == WL_POINTER_BUTTON_STATE_PRESSED; cur_press = state == WL_POINTER_BUTTON_STATE_PRESSED;
@ -297,8 +328,11 @@ void
wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time,
uint32_t axis, wl_fixed_t value) uint32_t axis, wl_fixed_t value)
{ {
if(!popup_xdg_surface_configured) {
return;
}
kbd_next_layer(&keyboard, NULL, (value >= 0)); kbd_next_layer(&keyboard, NULL, (value >= 0));
drwsurf_flip(keyboard.surf);
} }
void void
@ -338,16 +372,27 @@ void
wl_surface_enter(void *data, struct wl_surface *wl_surface, wl_surface_enter(void *data, struct wl_surface *wl_surface,
struct wl_output *wl_output) struct wl_output *wl_output)
{ {
for (int i = 0; i < WL_OUTPUTS_LIMIT; i += 1) { struct Output *old_output = current_output;
for (int i = 0; i < wl_outputs_size; i += 1) {
if (wl_outputs[i].data == wl_output) { if (wl_outputs[i].data == wl_output) {
current_output = &wl_outputs[i]; current_output = &wl_outputs[i];
break; break;
} }
} }
if (current_output == old_output) {
return;
}
resize(); 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 static void
display_handle_geometry(void *data, struct wl_output *wl_output, int x, int y, display_handle_geometry(void *data, struct wl_output *wl_output, int x, int y,
int physical_width, int physical_height, int subpixel, int physical_width, int physical_height, int subpixel,
@ -366,7 +411,7 @@ display_handle_geometry(void *data, struct wl_output *wl_output, int x, int y,
output->h = physical_height; output->h = physical_height;
if (current_output == output) { if (current_output == output) {
resize(); flip_landscape();
}; };
} }
@ -382,7 +427,8 @@ display_handle_scale(void *data, struct wl_output *wl_output, int32_t scale)
output->scale = scale; output->scale = scale;
if (current_output == output) { if (current_output == output) {
resize(); keyboard.preferred_scale = scale;
flip_landscape();
}; };
} }
@ -417,7 +463,7 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name,
wl_registry_bind(registry, name, &wl_compositor_interface, 3); wl_registry_bind(registry, name, &wl_compositor_interface, 3);
} else if (strcmp(interface, wl_shm_interface.name) == 0) { } else if (strcmp(interface, wl_shm_interface.name) == 0) {
draw_ctx.shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); draw_ctx.shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, "wl_output") == 0) { } else if (strcmp(interface, wl_output_interface.name) == 0) {
if (wl_outputs_size < WL_OUTPUTS_LIMIT) { if (wl_outputs_size < WL_OUTPUTS_LIMIT) {
struct Output *output = &wl_outputs[wl_outputs_size]; struct Output *output = &wl_outputs[wl_outputs_size];
output->data = output->data =
@ -453,10 +499,10 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name,
void void
handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
{ {
for (int i = 0; i < WL_OUTPUTS_LIMIT; i += 1) { for (int i = 0; i < wl_outputs_size; i += 1) {
if (wl_outputs[i].name == name) { if (wl_outputs[i].name == name) {
wl_output_destroy(wl_outputs[i].data); wl_output_destroy(wl_outputs[i].data);
for (; i < WL_OUTPUTS_LIMIT - 1; i += 1) { for (; i < wl_outputs_size - 1; i += 1) {
wl_outputs[i] = wl_outputs[i + 1]; wl_outputs[i] = wl_outputs[i + 1];
} }
wl_outputs_size -= 1; wl_outputs_size -= 1;
@ -470,7 +516,8 @@ xdg_popup_surface_configure(void *data, struct xdg_surface *xdg_surface,
uint32_t serial) uint32_t serial)
{ {
xdg_surface_ack_configure(xdg_surface, serial); xdg_surface_ack_configure(xdg_surface, serial);
drwsurf_flip(&popup_draw_surf); popup_xdg_surface_configured = true;
drwsurf_attach(&popup_draw_surf);
} }
static const struct xdg_surface_listener xdg_popup_surface_listener = { static const struct xdg_surface_listener xdg_popup_surface_listener = {
@ -481,9 +528,6 @@ static void
xdg_popup_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, xdg_popup_configure(void *data, struct xdg_popup *xdg_popup, int32_t x,
int32_t y, int32_t width, int32_t height) int32_t y, int32_t width, int32_t height)
{ {
kbd_resize(&keyboard, layouts, NumLayouts);
drwsurf_flip(&draw_surf);
} }
static void static void
@ -497,22 +541,28 @@ static const struct xdg_popup_listener xdg_popup_listener = {
}; };
static void static void
wp_fractional_scale_prefered_scale( wp_fractional_scale_preferred_scale(
void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1, void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1,
uint32_t scale) uint32_t scale)
{ {
keyboard.pending_scale = (double)scale / 120; keyboard.preferred_fractional_scale = (double)scale / 120;
} }
static const struct wp_fractional_scale_v1_listener static const struct wp_fractional_scale_v1_listener
wp_fractional_scale_listener = { wp_fractional_scale_listener = {
.preferred_scale = wp_fractional_scale_prefered_scale, .preferred_scale = wp_fractional_scale_preferred_scale,
}; };
void void
resize() flip_landscape()
{ {
bool previous_landscape = keyboard.landscape;
if (current_output) {
keyboard.landscape = current_output->w > current_output->h; keyboard.landscape = current_output->w > current_output->h;
} else if (wl_outputs_size) {
keyboard.landscape = wl_outputs[0].w > wl_outputs[0].h;
}
enum layout_id layer; enum layout_id layer;
if (keyboard.landscape) { if (keyboard.landscape) {
@ -529,14 +579,25 @@ resize()
keyboard.last_abc_layout = keyboard.layout; keyboard.last_abc_layout = keyboard.layout;
keyboard.last_abc_index = 0; keyboard.last_abc_index = 0;
if (!wfs_mgr || !viewporter) { if (layer_surface && previous_landscape != keyboard.landscape) {
keyboard.pending_scale = current_output->scale; 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;
} }
if (layer_surface) { zwlr_layer_surface_v1_destroy(layer_surface);
zwlr_layer_surface_v1_set_size(layer_surface, 0, height); layer_surface = NULL;
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, height); wl_surface_destroy(draw_surf.surf);
wl_surface_commit(draw_surf.surf);
show();
} }
} }
@ -544,24 +605,20 @@ void
layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
uint32_t serial, uint32_t w, uint32_t h) uint32_t serial, uint32_t w, uint32_t h)
{ {
if (keyboard.w != w || keyboard.h != h || double scale = keyboard.preferred_scale;
keyboard.scale != keyboard.pending_scale) { if (keyboard.preferred_fractional_scale) {
scale = keyboard.preferred_fractional_scale;
}
if (keyboard.w != w || keyboard.h != h || keyboard.scale != scale ||
hidden) {
keyboard.w = w; keyboard.w = w;
keyboard.h = h; keyboard.h = h;
keyboard.scale = keyboard.pending_scale; keyboard.scale = scale;
hidden = false;
if (wfs_mgr && viewporter) { if (wfs_mgr && viewporter) {
if (!wfs_draw_surf) {
wfs_draw_surf =
wp_fractional_scale_manager_v1_get_fractional_scale(
wfs_mgr, draw_surf.surf);
wp_fractional_scale_v1_add_listener(
wfs_draw_surf, &wp_fractional_scale_listener, NULL);
}
if (!draw_surf_viewport) {
draw_surf_viewport =
wp_viewporter_get_viewport(viewporter, draw_surf.surf);
}
wp_viewport_set_destination(draw_surf_viewport, keyboard.w, wp_viewport_set_destination(draw_surf_viewport, keyboard.w,
keyboard.h); keyboard.h);
} else { } else {
@ -586,6 +643,7 @@ layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
wl_surface_set_input_region(popup_draw_surf.surf, empty_region); wl_surface_set_input_region(popup_draw_surf.surf, empty_region);
popup_xdg_surface = popup_xdg_surface =
xdg_wm_base_get_xdg_surface(wm_base, popup_draw_surf.surf); 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, xdg_surface_add_listener(popup_xdg_surface, &xdg_popup_surface_listener,
NULL); NULL);
popup_xdg_popup = xdg_surface_get_popup(popup_xdg_surface, NULL, popup_xdg_popup = xdg_surface_get_popup(popup_xdg_surface, NULL,
@ -603,9 +661,13 @@ layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
} }
wl_surface_commit(popup_draw_surf.surf); wl_surface_commit(popup_draw_surf.surf);
}
zwlr_layer_surface_v1_ack_configure(surface, serial); zwlr_layer_surface_v1_ack_configure(surface, serial);
kbd_resize(&keyboard, layouts, NumLayouts);
drwsurf_attach(&draw_surf);
} else {
zwlr_layer_surface_v1_ack_configure(surface, serial);
}
} }
void void
@ -630,6 +692,7 @@ usage(char *argv0)
" -O - Print intersected keys to standard output\n"); " -O - Print intersected keys to standard output\n");
fprintf(stderr, " -H [int] - Height in pixels\n"); fprintf(stderr, " -H [int] - Height in pixels\n");
fprintf(stderr, " -L [int] - Landscape height in pixels\n"); fprintf(stderr, " -L [int] - Landscape height in pixels\n");
fprintf(stderr, " -R [int] - Rounding radius in pixels\n");
fprintf(stderr, " --fn [font] - Set font (e.g: DejaVu Sans 20)\n"); fprintf(stderr, " --fn [font] - Set font (e.g: DejaVu Sans 20)\n");
fprintf(stderr, " --hidden - Start hidden (send SIGUSR2 to show)\n"); fprintf(stderr, " --hidden - Start hidden (send SIGUSR2 to show)\n");
fprintf( fprintf(
@ -673,6 +736,15 @@ hide()
return; return;
} }
if(wfs_draw_surf) {
wp_fractional_scale_v1_destroy(wfs_draw_surf);
wfs_draw_surf = NULL;
}
if(draw_surf_viewport) {
wp_viewport_destroy(draw_surf_viewport);
draw_surf_viewport = NULL;
}
zwlr_layer_surface_v1_destroy(layer_surface); zwlr_layer_surface_v1_destroy(layer_surface);
wl_surface_destroy(draw_surf.surf); wl_surface_destroy(draw_surf.surf);
layer_surface = NULL; layer_surface = NULL;
@ -688,10 +760,25 @@ show()
wl_display_sync(display); wl_display_sync(display);
flip_landscape();
draw_surf.surf = wl_compositor_create_surface(compositor); 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);
wp_fractional_scale_v1_add_listener(
wfs_draw_surf, &wp_fractional_scale_listener, NULL);
draw_surf_viewport =
wp_viewporter_get_viewport(viewporter, draw_surf.surf);
}
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_surface = zwlr_layer_shell_v1_get_layer_surface(
layer_shell, draw_surf.surf, NULL, layer, namespace); 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_size(layer_surface, 0, height);
zwlr_layer_surface_v1_set_anchor(layer_surface, anchor); zwlr_layer_surface_v1_set_anchor(layer_surface, anchor);
@ -700,8 +787,6 @@ show()
zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener,
NULL); NULL);
wl_surface_commit(draw_surf.surf); wl_surface_commit(draw_surf.surf);
hidden = false;
} }
void void
@ -725,7 +810,7 @@ set_kbd_colors(uint8_t *bgra, char *hex)
// bg, fg, text, high, swipe // bg, fg, text, high, swipe
int length = strlen(hex); int length = strlen(hex);
if (length == 6 || length == 8) { if (length == 6 || length == 8) {
char subhex[2]; char subhex[3] = { 0 };
memcpy(subhex, hex, 2); memcpy(subhex, hex, 2);
bgra[2] = (int)strtol(subhex, NULL, 16); bgra[2] = (int)strtol(subhex, NULL, 16);
memcpy(subhex, hex + 2, 2); memcpy(subhex, hex + 2, 2);
@ -744,9 +829,9 @@ main(int argc, char **argv)
{ {
/* parse command line arguments */ /* parse command line arguments */
char *layer_names_list = NULL, *landscape_layer_names_list = NULL; char *layer_names_list = NULL, *landscape_layer_names_list = NULL;
const char *fc_font_pattern = NULL; char *fc_font_pattern = NULL;
height = normal_height = KBD_PIXEL_HEIGHT; height = landscape_height = KBD_PIXEL_LANDSCAPE_HEIGHT;
landscape_height = KBD_PIXEL_LANDSCAPE_HEIGHT; normal_height = KBD_PIXEL_HEIGHT;
char *tmp; char *tmp;
if ((tmp = getenv("WVKBD_LAYERS"))) if ((tmp = getenv("WVKBD_LAYERS")))
@ -761,10 +846,11 @@ main(int argc, char **argv)
/* keyboard settings */ /* keyboard settings */
keyboard.layers = (enum layout_id *)&layers; keyboard.layers = (enum layout_id *)&layers;
keyboard.landscape_layers = (enum layout_id *)&landscape_layers; keyboard.landscape_layers = (enum layout_id *)&landscape_layers;
keyboard.scheme = scheme; keyboard.schemes = schemes;
keyboard.landscape = true;
keyboard.layer_index = 0; keyboard.layer_index = 0;
keyboard.scheme1 = scheme1; keyboard.preferred_scale = 1;
keyboard.pending_scale = 1; keyboard.preferred_fractional_scale = 0;
uint8_t alpha = 0; uint8_t alpha = 0;
bool alpha_defined = false; bool alpha_defined = false;
@ -799,7 +885,7 @@ main(int argc, char **argv)
usage(argv[0]); usage(argv[0]);
exit(1); exit(1);
} }
set_kbd_colors(keyboard.scheme.bg.bgra, argv[++i]); set_kbd_colors(keyboard.schemes[0].bg.bgra, argv[++i]);
} else if ((!strcmp(argv[i], "-alpha")) || } else if ((!strcmp(argv[i], "-alpha")) ||
(!strcmp(argv[i], "--alpha"))) { (!strcmp(argv[i], "--alpha"))) {
if (i >= argc - 1) { if (i >= argc - 1) {
@ -813,68 +899,74 @@ main(int argc, char **argv)
usage(argv[0]); usage(argv[0]);
exit(1); exit(1);
} }
set_kbd_colors(keyboard.scheme.fg.bgra, argv[++i]); set_kbd_colors(keyboard.schemes[0].fg.bgra, argv[++i]);
} else if ((!strcmp(argv[i], "-fg-sp")) || } else if ((!strcmp(argv[i], "-fg-sp")) ||
(!strcmp(argv[i], "--fg-sp"))) { (!strcmp(argv[i], "--fg-sp"))) {
if (i >= argc - 1) { if (i >= argc - 1) {
usage(argv[0]); usage(argv[0]);
exit(1); exit(1);
} }
set_kbd_colors(keyboard.scheme1.fg.bgra, argv[++i]); set_kbd_colors(keyboard.schemes[1].fg.bgra, argv[++i]);
} else if ((!strcmp(argv[i], "-press")) || } else if ((!strcmp(argv[i], "-press")) ||
(!strcmp(argv[i], "--press"))) { (!strcmp(argv[i], "--press"))) {
if (i >= argc - 1) { if (i >= argc - 1) {
usage(argv[0]); usage(argv[0]);
exit(1); exit(1);
} }
set_kbd_colors(keyboard.scheme.high.bgra, argv[++i]); set_kbd_colors(keyboard.schemes[0].high.bgra, argv[++i]);
} else if ((!strcmp(argv[i], "-press-sp")) || } else if ((!strcmp(argv[i], "-press-sp")) ||
(!strcmp(argv[i], "--press-sp"))) { (!strcmp(argv[i], "--press-sp"))) {
if (i >= argc - 1) { if (i >= argc - 1) {
usage(argv[0]); usage(argv[0]);
exit(1); exit(1);
} }
set_kbd_colors(keyboard.scheme1.high.bgra, argv[++i]); set_kbd_colors(keyboard.schemes[1].high.bgra, argv[++i]);
} else if ((!strcmp(argv[i], "-swipe")) || } else if ((!strcmp(argv[i], "-swipe")) ||
(!strcmp(argv[i], "--swipe"))) { (!strcmp(argv[i], "--swipe"))) {
if (i >= argc - 1) { if (i >= argc - 1) {
usage(argv[0]); usage(argv[0]);
exit(1); exit(1);
} }
set_kbd_colors(keyboard.scheme.swipe.bgra, argv[++i]); set_kbd_colors(keyboard.schemes[0].swipe.bgra, argv[++i]);
} else if ((!strcmp(argv[i], "-swipe-sp")) || } else if ((!strcmp(argv[i], "-swipe-sp")) ||
(!strcmp(argv[i], "--swipe-sp"))) { (!strcmp(argv[i], "--swipe-sp"))) {
if (i >= argc - 1) { if (i >= argc - 1) {
usage(argv[0]); usage(argv[0]);
exit(1); exit(1);
} }
set_kbd_colors(keyboard.scheme1.swipe.bgra, argv[++i]); set_kbd_colors(keyboard.schemes[1].swipe.bgra, argv[++i]);
} else if ((!strcmp(argv[i], "-text")) || } else if ((!strcmp(argv[i], "-text")) ||
(!strcmp(argv[i], "--text"))) { (!strcmp(argv[i], "--text"))) {
if (i >= argc - 1) { if (i >= argc - 1) {
usage(argv[0]); usage(argv[0]);
exit(1); exit(1);
} }
set_kbd_colors(keyboard.scheme.text.bgra, argv[++i]); set_kbd_colors(keyboard.schemes[0].text.bgra, argv[++i]);
} else if ((!strcmp(argv[i], "-text-sp")) || } else if ((!strcmp(argv[i], "-text-sp")) ||
(!strcmp(argv[i], "--text-sp"))) { (!strcmp(argv[i], "--text-sp"))) {
if (i >= argc - 1) { if (i >= argc - 1) {
usage(argv[0]); usage(argv[0]);
exit(1); exit(1);
} }
set_kbd_colors(keyboard.scheme1.text.bgra, argv[++i]); set_kbd_colors(keyboard.schemes[1].text.bgra, argv[++i]);
} else if (!strcmp(argv[i], "-H")) { } else if (!strcmp(argv[i], "-H")) {
if (i >= argc - 1) { if (i >= argc - 1) {
usage(argv[0]); usage(argv[0]);
exit(1); exit(1);
} }
height = normal_height = atoi(argv[++i]); normal_height = atoi(argv[++i]);
} else if (!strcmp(argv[i], "-L")) { } else if (!strcmp(argv[i], "-L")) {
if (i >= argc - 1) { if (i >= argc - 1) {
usage(argv[0]); usage(argv[0]);
exit(1); exit(1);
} }
landscape_height = atoi(argv[++i]); height = landscape_height = atoi(argv[++i]);
} else if (!strcmp(argv[i], "-R")) {
if (i >= argc - 1) {
usage(argv[0]);
exit(1);
}
rounding = atoi(argv[++i]);
} else if (!strcmp(argv[i], "-D")) { } else if (!strcmp(argv[i], "-D")) {
keyboard.debug = true; keyboard.debug = true;
} else if ((!strcmp(argv[i], "-fn")) || (!strcmp(argv[i], "--fn"))) { } else if ((!strcmp(argv[i], "-fn")) || (!strcmp(argv[i], "--fn"))) {
@ -902,16 +994,22 @@ main(int argc, char **argv)
} }
if (alpha_defined) { if (alpha_defined) {
keyboard.scheme.bg.bgra[3] = alpha; keyboard.schemes[0].bg.bgra[3] = alpha;
keyboard.scheme.fg.bgra[3] = alpha; keyboard.schemes[0].fg.bgra[3] = alpha;
keyboard.scheme.high.bgra[3] = alpha; keyboard.schemes[0].high.bgra[3] = alpha;
keyboard.scheme1.bg.bgra[3] = alpha; keyboard.schemes[1].bg.bgra[3] = alpha;
keyboard.scheme1.fg.bgra[3] = alpha; keyboard.schemes[1].fg.bgra[3] = alpha;
keyboard.scheme1.high.bgra[3] = alpha; keyboard.schemes[1].high.bgra[3] = alpha;
} }
if (!fc_font_pattern) { if (fc_font_pattern) {
fc_font_pattern = default_font; for (i = 0; i < countof(schemes); i++)
schemes[i].font = fc_font_pattern;
}
if (rounding != DEFAULT_ROUNDING) {
for (i = 0; i < countof(schemes); i++)
schemes[i].rounding = rounding;
} }
display = wl_display_connect(NULL); display = wl_display_connect(NULL);
@ -920,7 +1018,11 @@ main(int argc, char **argv)
} }
draw_surf.ctx = &draw_ctx; draw_surf.ctx = &draw_ctx;
draw_surf.back_buffer = &draw_surf_back_buffer;
draw_surf.display_buffer = &draw_surf_display_buffer;
popup_draw_surf.ctx = &draw_ctx; popup_draw_surf.ctx = &draw_ctx;
popup_draw_surf.back_buffer = &popup_draw_surf_back_buffer;
popup_draw_surf.display_buffer = &popup_draw_surf_display_buffer;
keyboard.surf = &draw_surf; keyboard.surf = &draw_surf;
keyboard.popup_surf = &popup_draw_surf; keyboard.popup_surf = &popup_draw_surf;
@ -944,6 +1046,9 @@ main(int argc, char **argv)
die("virtual_keyboard_manager not available\n"); die("virtual_keyboard_manager not available\n");
} }
// A second round-trip to receive wl_outputs events
wl_display_roundtrip(display);
empty_region = wl_compositor_create_region(compositor); empty_region = wl_compositor_create_region(compositor);
popup_xdg_positioner = xdg_wm_base_create_positioner(wm_base); popup_xdg_positioner = xdg_wm_base_create_positioner(wm_base);
@ -956,8 +1061,10 @@ main(int argc, char **argv)
kbd_init(&keyboard, (struct layout *)&layouts, layer_names_list, kbd_init(&keyboard, (struct layout *)&layouts, layer_names_list,
landscape_layer_names_list); landscape_layer_names_list);
draw_ctx.font_description = for (i = 0; i < countof(schemes); i++) {
pango_font_description_from_string(fc_font_pattern); schemes[i].font_description =
pango_font_description_from_string(schemes[i].font);
}
if (!hidden) if (!hidden)
show(); show();
@ -994,6 +1101,12 @@ main(int argc, char **argv)
if (fds[WAYLAND_FD].revents & POLLIN) if (fds[WAYLAND_FD].revents & POLLIN)
wl_display_dispatch(display); wl_display_dispatch(display);
if (fds[WAYLAND_FD].revents & POLLERR) {
die("Exceptional condition on wayland socket.\n");
}
if (fds[WAYLAND_FD].revents & POLLHUP) {
die("Wayland socket has been disconnected.\n");
}
if (fds[SIGNAL_FD].revents & POLLIN) { if (fds[SIGNAL_FD].revents & POLLIN) {
struct signalfd_siginfo si; struct signalfd_siginfo si;
@ -1011,8 +1124,10 @@ main(int argc, char **argv)
} }
} }
if (fc_font_pattern != default_font) { if (fc_font_pattern) {
free((void *)fc_font_pattern); free((void *)fc_font_pattern);
for (i = 0; i < countof(schemes); i++)
schemes[i].font = NULL;
} }
return 0; return 0;

76
wvkbd.1
View File

@ -1,76 +0,0 @@
.TH "wvkbd" "1" "2022-03-12"
.P
.SH NAME
.P
wvkbd - on-screen keyboard for wlroots
.P
.SH SYNOPSIS
.P
wvkbd-mobintl [OPTION]...
.P
.SH DESCRIPTION
.P
wvkbd is an on-screen keyboard for wlroots that sucks less.
.P
.SH OPTIONS
.P
\fB-D\fR
.RS 4
enable debug
.P
.RE
\fB-o\fR
.RS 4
print pressed keys to standard output
.P
.RE
\fB-O\fR
.RS 4
print intersected keys to standard output
.P
.RE
\fB-l\fR
.RS 4
comma separated list of layers
.P
.RE
\fB--landscape-layers\fR
.RS 4
comma separated list of layers used in landscape mode
.P
.RE
\fB-H\fR \fIPX\fR
.RS 4
height in pixels
.P
.RE
\fB-L\fR \fIPX\fR
.RS 4
landscape height in pixels
.P
.RE
\fB--fn\fR \fIFONT\fR
.RS 4
set font (e.g: DejaVu Sans 20)
.P
.RE
\fB--hidden\fR
.RS 4
start hidden (send SIGUSR2 to show, SIGUSR1 to hide, SIGRTMIN to toggle)
.P
.RE
\fB-v\fR, \fB--version\fR
.RS 4
print version
.P
.RE
\fB-h\fR, \fB--help\fR
.RS 4
show help
.P
.RE
.SH AUTHORS
.P
Created by John Sullivan <jsullivan@csumb.edu>, maintained by the Sxmo project in
collaboration with other open source contributors. For more information about wvkbd development, see
<https://git.sr.ht/~proycon/wvkbd> or <https://github.com/jjsullivan5196/wvkbd>.

129
wvkbd.1.scd Normal file
View File

@ -0,0 +1,129 @@
wvkbd(1)
# NAME
wvkbd - on-screen virtual keyboard for wayland compositors using wlroots
# SYNOPSIS
wvkbd-mobintl [OPTIONS]...
*NOTE*: Your binary may have a different suffix depending on which layout you compiled.
# DESCRIPTION
This project aims to deliver a minimal but practically usable implementation of
a wlroots on-screen keyboard in legible C. This will _only_ be a keyboard, not
a feedback buzzer, led blinker, or anything that requires more than what's
needed to input text quickly. The end product should be a static codebase that
can be patched to add new features.
## OPTIONS
*-D*
enable debug mode.
*-o*
print pressed keys to standard output.
*-O*
print intersected keys to standard output.
*-l* _layers_
comma separated list of layers in vertical/portrait mode.
*--landscape-layers* _layers_
comma separated list of layers used in horizontal/landscape mode.
*--list-layers*
prints a list of all available layers.
*-H* _pixels_
Height of the keyboard in pixels, for vertical/portrait mode.
*-L* _pixels_
Height of the keyboard in pixels, for horizontal/landscape mode
*--fn* _font_
set font and size (e.g. DejaVu Sans 20)
*--hidden*
Start hidden (send SIGUSR2 to show).
*--alpha* _int_
Set alpha value (i.e. transparency) for all colors [0-255]
*--bg* _rrggbb|aa_
Set color of background
*--fg* _rrggbb|aa_
Set color of keys
*--fg-sp* _rrggbb|aa_
Set color of special keys
*--press* _rrggbb|aa_
Set color of pressed keys
*--press-sp* _rrggbb|aa_
Set color of pressed special keys
*--swipe* _rrggbb|aa_
Set color of swiped keys
*--swipe-sp* _rrggbb|aa_
Set color of swiped special keys
*--text* _rrggbb|aa_
Set color text on keys
*--text-sp* _rrggbb|aa_
Set color text on special keys
*--version*
Print version information
*-h*, *--help*
Print usage help
# SIGNALS
You can send signals to wvkbd to hide/show it (e.g. using _kill_(1) with _-s_):
*SIGUSR1*
Hide the keyboard.
*SIGUSR2*
Show the keyboard
*SIGRTMIN*
Toggle visibility
# COMPOSE BUTTON
The default mobile international layout features a Compose button (*Cmp*)
which, when combined with another key, opens up a layout that offers variants
for that key. This is similar to functionality that other keyboards implemented
using a *long press* (wvkbd has no such notion, holding a key will repeat
it like on a physical keyboard).
For example, press Cmp + a to access variants with diacritics like á,à,â,ä, etc..
Most layouts also feature the following that are less obvious:
- Press Cmp and . to access more punctuation
- Press Cmp and - or , to access 'mathematical' symbols (+,-,=,etc)
- Press Cmp and ' or 0 or 9 to access more brackets and quotes
- Press Cmp and q to access emojis
Last, but not least, pressing Cmp + space or Cmp + ⌨ or Cmp + Abc opens up an
index that allows you to immediately jump to any layout by name, even layouts
not explicitly added to your layers on startup.
# AUTHORS
Created by John Sullivan <jsullivan@csumb.edu>, maintained by the Sxmo project
<https://sxmo.org> in collaboration with other open source contributors. For
more information about wvkbd development, see <https://git.sr.ht/~proycon/wvkbd>
or <https://github.com/jjsullivan5196/wvkbd>.