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
parent
da1ad20dcd
commit
0a68dbb720
|
@ -9,6 +9,7 @@ Expect bugs and missing features.
|
|||
* libvncclient
|
||||
* libwayland
|
||||
* libxkbcommon
|
||||
* pixman
|
||||
|
||||
## Build Dependencies
|
||||
* GCC/clang
|
||||
|
|
|
@ -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);
|
|
@ -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,
|
||||
|
|
165
src/main.c
165
src/main.c
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue