Create a nicer API around libvncclient

This hides some implementation details of libvncclient from the rest of
the system and reduces coupling.
qemu-extended-key-event
Andri Yngvason 2020-07-11 17:54:35 +00:00
parent da1ad20dcd
commit 0a68dbb720
5 changed files with 315 additions and 91 deletions

View File

@ -9,6 +9,7 @@ Expect bugs and missing features.
* libvncclient
* libwayland
* libxkbcommon
* pixman
## Build Dependencies
* GCC/clang

37
include/vnc.h 100644
View File

@ -0,0 +1,37 @@
#pragma once
#include <stdbool.h>
#include <pixman.h>
#include <wayland-client.h>
#include <rfb/rfbclient.h>
struct vnc_client {
rfbClient* client;
int (*alloc_fb)(struct vnc_client*);
void (*update_fb)(struct vnc_client*);
void* userdata;
struct pixman_region16 damage;
};
struct vnc_client* vnc_client_create(void);
void vnc_client_destroy(struct vnc_client* self);
int vnc_client_connect(struct vnc_client* self, const char* address, int port);
int vnc_client_set_pixel_format(struct vnc_client* self,
enum wl_shm_format format);
int vnc_client_get_fd(const struct vnc_client* self);
int vnc_client_get_width(const struct vnc_client* self);
int vnc_client_get_height(const struct vnc_client* self);
int vnc_client_get_stride(const struct vnc_client* self);
void* vnc_client_get_fb(const struct vnc_client* self);
void vnc_client_set_fb(struct vnc_client* self, void* fb);
const char* vnc_client_get_desktop_name(const struct vnc_client* self);
int vnc_client_process(struct vnc_client* self);
void vnc_client_send_pointer_event(struct vnc_client* self, int x, int y,
uint32_t button_mask);
void vnc_client_send_keyboard_event(struct vnc_client* self, uint32_t symbol,
bool is_pressed);

View File

@ -28,6 +28,7 @@ libm = cc.find_library('m', required: false)
librt = cc.find_library('rt', required: false)
xkbcommon = dependency('xkbcommon')
pixman = dependency('pixman-1')
wayland_client = dependency('wayland-client')
libvncclient = dependency('libvncclient')
@ -48,6 +49,7 @@ sources = [
'src/seat.c',
'src/pointer.c',
'src/keyboard.c',
'src/vnc.c',
'src/strlcpy.c',
]
@ -55,6 +57,7 @@ dependencies = [
libm,
librt,
xkbcommon,
pixman,
aml,
wayland_client,
libvncclient,

View File

@ -26,14 +26,15 @@
#include <sys/mman.h>
#include <aml.h>
#include <wayland-client.h>
#include <rfb/rfbclient.h>
#include <xkbcommon/xkbcommon.h>
#include "pixman.h"
#include "xdg-shell.h"
#include "shm.h"
#include "seat.h"
#include "pointer.h"
#include "keyboard.h"
#include "vnc.h"
struct buffer {
int width, height, stride;
@ -49,7 +50,6 @@ struct window {
struct xdg_toplevel* xdg_toplevel;
struct buffer* buffer;
bool is_attached;
};
static struct wl_display* wl_display;
@ -357,18 +357,18 @@ static void window_destroy(struct window* w)
void on_pointer_event(struct pointer_collection* collection,
struct pointer* pointer)
{
rfbClient* client = collection->userdata;
struct vnc_client* client = collection->userdata;
int x = wl_fixed_to_int(pointer->x);
int y = wl_fixed_to_int(pointer->y);
SendPointerEvent(client, x, y, pointer->pressed);
vnc_client_send_pointer_event(client, x, y, pointer->pressed);
}
void on_keyboard_event(struct keyboard_collection* collection,
struct keyboard* keyboard, uint32_t key, bool is_pressed)
{
rfbClient* client = collection->userdata;
struct vnc_client* client = collection->userdata;
// TODO handle multiple symbols
xkb_keysym_t symbol = xkb_state_key_get_one_sym(keyboard->state, key);
@ -376,109 +376,81 @@ void on_keyboard_event(struct keyboard_collection* collection,
char name[256];
xkb_keysym_get_name(symbol, name, sizeof(name));
SendKeyEvent(client, symbol, is_pressed);
vnc_client_send_keyboard_event(client, symbol, is_pressed);
}
rfbBool rfb_client_alloc_fb(rfbClient* cl)
int on_vnc_client_alloc_fb(struct vnc_client* client)
{
int stride = cl->width * 4; // TODO?
assert(!window); // TODO: Support resizing
window = window_create(cl->desktopName);
int width = vnc_client_get_width(client);
int height = vnc_client_get_height(client);
int stride = vnc_client_get_stride(client);
window = window_create(vnc_client_get_desktop_name(client));
if (!window)
return FALSE;
window->buffer = buffer_create(cl->width, cl->height, stride,
wl_shm_format);
cl->frameBuffer = window->buffer->pixels;
return TRUE;
}
void rfb_client_update_box(rfbClient* cl, int x, int y, int width, int height)
{
// TODO: Make sure that the buffer is released at this point, or make
// this a side-buffer and copy damaged regions into double buffers.
if (!window->is_attached)
window_attach(window, 0, 0);
window_damage(window, x, y, width, height);
}
void rfb_client_finish_update(rfbClient* cl)
{
window_commit(window);
}
void on_rfb_client_server_event(void* obj)
{
rfbClient* cl = aml_get_userdata(obj);
if (!HandleRFBServerMessage(cl))
do_run = false;
}
static int rfb_format_from_wl_shm_format(rfbPixelFormat* dst,
enum wl_shm_format src)
{
int bpp = -1;
switch (src) {
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_XRGB8888:
dst->redShift = 16;
dst->greenShift = 8;
dst->blueShift = 0;
bpp = 32;
break;
default:
return -1;
}
switch (bpp) {
case 32:
dst->bitsPerPixel = 32;
dst->depth = 24;
dst->redMax = 0xff;
dst->greenMax = 0xff;
dst->blueMax = 0xff;
break;
default:
abort();
}
window->buffer = buffer_create(width, height, stride, wl_shm_format);
vnc_client_set_fb(client, window->buffer->pixels);
return 0;
}
static rfbClient* rfb_client_create(int* argc, char* argv[])
void on_vnc_client_update_fb(struct vnc_client* client)
{
int bits_per_sample = 8;
int samples_per_pixel = 3;
int bytes_per_pixel = 4;
if (!pixman_region_not_empty(&client->damage))
return;
rfbClient* cl = rfbGetClient(bits_per_sample, samples_per_pixel,
bytes_per_pixel);
if (!cl)
// TODO: Make sure that the buffer is released at this point, or make
// this a side-buffer and copy damaged regions into double buffers.
window_attach(window, 0, 0);
int n_rects = 0;
struct pixman_box16* box = pixman_region_rectangles(&client->damage,
&n_rects);
for (int i = 0; i < n_rects; ++i) {
int x = box[i].x1;
int y = box[i].y1;
int width = box[i].x2 - x;
int height = box[i].y2 - y;
window_damage(window, x, y, width, height);
}
window_commit(window);
}
void on_vnc_client_event(void* obj)
{
struct vnc_client* client = aml_get_userdata(obj);
if (vnc_client_process(client) < 0)
do_run = false;
}
static struct vnc_client* connect_to_server(const char* address, int port)
{
struct vnc_client* client = vnc_client_create();
if (!client)
return NULL;
cl->MallocFrameBuffer = rfb_client_alloc_fb;
cl->GotFrameBufferUpdate = rfb_client_update_box;
cl->FinishedFrameBufferUpdate = rfb_client_finish_update;
client->alloc_fb = on_vnc_client_alloc_fb;
client->update_fb = on_vnc_client_update_fb;
if (rfb_format_from_wl_shm_format(&cl->format, wl_shm_format) < 0) {
if (vnc_client_set_pixel_format(client, wl_shm_format) < 0) {
fprintf(stderr, "Unsupported pixel format\n");
return NULL;
}
if (!rfbInitClient(cl, argc, argv))
return NULL;
if (vnc_client_connect(client, address, port) < 0) {
fprintf(stderr, "Failed to connect to server\n");
goto failure;
}
int fd = cl->sock;
int fd = vnc_client_get_fd(client);
struct aml_handler* handler;
handler = aml_handler_new(fd, on_rfb_client_server_event, cl, NULL);
handler = aml_handler_new(fd, on_vnc_client_event, client, NULL);
if (!handler)
goto failure;
@ -488,22 +460,33 @@ static rfbClient* rfb_client_create(int* argc, char* argv[])
if (rc < 0)
goto failure;
return cl;
return client;
failure:
rfbClientCleanup(cl);
vnc_client_destroy(client);
return NULL;
}
static void rfb_client_destroy(rfbClient* cl)
static int usage(int r)
{
rfbClientCleanup(cl);
fprintf(r ? stderr : stdout, "\
Usage: wlvncc <address> [port]\n\
");
return r;
}
int main(int argc, char* argv[])
{
int rc = -1;
if (argc < 2)
return usage(1);
const char* address = argv[1];
int port = 5900;
if (argc > 2)
port = atoi(argv[2]);
struct aml* aml = aml_new();
if (!aml)
return 1;
@ -550,7 +533,7 @@ int main(int argc, char* argv[])
wl_display_roundtrip(wl_display);
wl_display_roundtrip(wl_display);
rfbClient* vnc = rfb_client_create(&argc, argv);
struct vnc_client* vnc = connect_to_server(address, port);
if (!vnc)
goto vnc_failure;
@ -568,7 +551,7 @@ int main(int argc, char* argv[])
rc = 0;
if (window)
window_destroy(window);
rfb_client_destroy(vnc);
vnc_client_destroy(vnc);
vnc_failure:
seat_list_destroy(&seats);
wl_compositor_destroy(wl_compositor);

200
src/vnc.c 100644
View File

@ -0,0 +1,200 @@
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
#include <pixman.h>
#include <rfb/rfbclient.h>
#include <wayland-client.h>
#include "vnc.h"
static rfbBool vnc_client_alloc_fb(rfbClient* client)
{
struct vnc_client* self = rfbClientGetClientData(client, NULL);
assert(self);
return self->alloc_fb(self) < 0 ? FALSE : TRUE;
}
static void vnc_client_update_box(rfbClient* client, int x, int y, int width,
int height)
{
struct vnc_client* self = rfbClientGetClientData(client, NULL);
assert(self);
pixman_region_union_rect(&self->damage, &self->damage, x, y, width,
height);
}
static void vnc_client_finish_update(rfbClient* client)
{
struct vnc_client* self = rfbClientGetClientData(client, NULL);
assert(self);
self->update_fb(self);
pixman_region_clear(&self->damage);
}
struct vnc_client* vnc_client_create(void)
{
struct vnc_client* self = calloc(1, sizeof(*self));
if (!self)
return NULL;
/* These are defaults that can be changed with
* vnc_client_set_pixel_format().
*/
int bits_per_sample = 8;
int samples_per_pixel = 3;
int bytes_per_pixel = 4;
rfbClient* client = rfbGetClient(bits_per_sample, samples_per_pixel,
bytes_per_pixel);
if (!client)
goto failure;
self->client = client;
rfbClientSetClientData(client, NULL, self);
client->MallocFrameBuffer = vnc_client_alloc_fb;
client->GotFrameBufferUpdate = vnc_client_update_box;
client->FinishedFrameBufferUpdate = vnc_client_finish_update;
return self;
failure:
free(self);
return NULL;
}
void vnc_client_destroy(struct vnc_client* self)
{
rfbClientCleanup(self->client);
free(self);
}
int vnc_client_connect(struct vnc_client* self, const char* address, int port)
{
rfbClient* client = self->client;
if (!ConnectToRFBServer(client, address, port))
return -1;
if (!InitialiseRFBConnection(client))
return -1;
client->width = client->si.framebufferWidth;
client->height = client->si.framebufferHeight;
if (!client->MallocFrameBuffer(client))
return -1;
if (!SetFormatAndEncodings(client))
return -1;
if (client->updateRect.x < 0) {
client->updateRect.x = client->updateRect.y = 0;
client->updateRect.w = client->width;
client->updateRect.h = client->height;
}
if (!SendFramebufferUpdateRequest(client,
client->updateRect.x, client->updateRect.y,
client->updateRect.w, client->updateRect.h,
FALSE))
return -1;
return 0;
}
int vnc_client_set_pixel_format(struct vnc_client* self,
enum wl_shm_format format)
{
rfbPixelFormat* dst = &self->client->format;
int bpp = -1;
switch (format) {
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_XRGB8888:
dst->redShift = 16;
dst->greenShift = 8;
dst->blueShift = 0;
bpp = 32;
break;
default:
return -1;
}
switch (bpp) {
case 32:
dst->bitsPerPixel = 32;
dst->depth = 24;
dst->redMax = 0xff;
dst->greenMax = 0xff;
dst->blueMax = 0xff;
break;
default:
abort();
}
dst->trueColour = 1;
dst->bigEndian = FALSE;
self->client->appData.requestedDepth = dst->depth;
return 0;
}
int vnc_client_get_width(const struct vnc_client* self)
{
return self->client->width;
}
int vnc_client_get_height(const struct vnc_client* self)
{
return self->client->height;
}
int vnc_client_get_stride(const struct vnc_client* self)
{
// TODO: What happens if bitsPerPixel == 24?
return self->client->width * self->client->format.bitsPerPixel / 8;
}
void* vnc_client_get_fb(const struct vnc_client* self)
{
return self->client->frameBuffer;
}
void vnc_client_set_fb(struct vnc_client* self, void* fb)
{
self->client->frameBuffer = fb;
}
int vnc_client_get_fd(const struct vnc_client* self)
{
return self->client->sock;
}
const char* vnc_client_get_desktop_name(const struct vnc_client* self)
{
return self->client->desktopName;
}
int vnc_client_process(struct vnc_client* self)
{
return HandleRFBServerMessage(self->client) ? 0 : -1;
}
void vnc_client_send_pointer_event(struct vnc_client* self, int x, int y,
uint32_t button_mask)
{
SendPointerEvent(self->client, x, y, button_mask);
}
void vnc_client_send_keyboard_event(struct vnc_client* self, uint32_t symbol,
bool is_pressed)
{
SendKeyEvent(self->client, symbol, is_pressed);
}