vnc: usb libfxlink to grab a calculator and stream it the frames
My use of libfxlink is terrible though, doesn't handle disconnects etc. Will improve soon.
This commit is contained in:
parent
1493645f98
commit
b39ccf9758
|
@ -1,10 +1,19 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(CGVirtualMonitorVNCClient VERSION 1.0 LANGUAGES C)
|
||||
|
||||
# Usual install spot
|
||||
list(APPEND CMAKE_MODULE_PATH "$ENV{HOME}/.local/lib/cmake")
|
||||
# Environment-provided hint
|
||||
if(DEFINED "$ENV{FXSDK_PATH}")
|
||||
list(APPEND CMAKE_MODULE_PATH "$ENV{FXSDK_PATH}/lib/cmake")
|
||||
endif()
|
||||
|
||||
find_package(LibVNCServer 0.9 REQUIRED)
|
||||
find_package(SDL2 2.0 REQUIRED)
|
||||
find_package(LibFxlink 2.9 REQUIRED)
|
||||
|
||||
add_executable(cgvm_vnc src/main.c)
|
||||
set_target_properties(cgvm_vnc PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
target_link_libraries(cgvm_vnc LibVNCServer::vncclient SDL2::SDL2)
|
||||
target_link_libraries(cgvm_vnc PRIVATE
|
||||
LibVNCServer::vncclient SDL2::SDL2 LibFxlink::LibFxlink)
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
#include <SDL2/SDL.h>
|
||||
#include <rfb/rfbclient.h>
|
||||
#include <fxlink/devices.h>
|
||||
#include <fxlink/logging.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Application globals */
|
||||
struct app {
|
||||
/* RFB/VNC client to get framebuffers from VNC server */
|
||||
rfbClient *client;
|
||||
/* SDL globals used to store the framebuffer and show it on-screen */
|
||||
SDL_Window *window;
|
||||
SDL_Surface *surface;
|
||||
/* CLI options */
|
||||
bool display_calc;
|
||||
bool display_sdl;
|
||||
/* Calculator tracking */
|
||||
libusb_context *libusb_ctx;
|
||||
struct fxlink_device *calc;
|
||||
};
|
||||
static struct app app = { 0 };
|
||||
|
||||
|
@ -19,35 +30,107 @@ static void cleanup(void)
|
|||
if(app.window)
|
||||
SDL_DestroyWindow(app.window);
|
||||
SDL_Quit();
|
||||
if(app.calc) {
|
||||
fxlink_device_cleanup(app.calc);
|
||||
free(app.calc);
|
||||
}
|
||||
if(app.libusb_ctx)
|
||||
libusb_exit(app.libusb_ctx);
|
||||
}
|
||||
|
||||
static void fb_update(rfbClient *client)
|
||||
{
|
||||
/* SDL_LockSurface(app.surface);
|
||||
assert(app.surface->format->BytesPerPixel == 4);
|
||||
uint8_t *buffer = (void *)app.surface->pixels;
|
||||
for(int y = 0; y < app.surface->h; y++) {
|
||||
if(app.display_sdl) {
|
||||
SDL_UpdateWindowSurface(app.window);
|
||||
}
|
||||
if(app.display_calc && app.calc) {
|
||||
uint16_t *fb16 = malloc(396 * 224 * 2);
|
||||
uint8_t *buffer = (void *)app.surface->pixels;
|
||||
|
||||
for(int y = 0; y < app.surface->h; y++)
|
||||
for(int x = 0; x < app.surface->w; x++) {
|
||||
int offset = y * app.surface->pitch + 4 * x;
|
||||
// R, G, B
|
||||
buffer[offset + 2] = 255;
|
||||
buffer[offset + 1] = 255;
|
||||
buffer[offset + 0] = 0;
|
||||
int R = buffer[offset + 2];
|
||||
int G = buffer[offset + 1];
|
||||
int B = buffer[offset + 0];
|
||||
|
||||
/* Conversion to RGB565 */
|
||||
int c = ((R & 0xf8) << 8) | ((G & 0xfc) << 3) | ((B & 0xf8) >> 3);
|
||||
fb16[396*y + x] = (c >> 8) | (c << 8);
|
||||
}
|
||||
|
||||
fxlink_device_start_bulk_OUT(app.calc,
|
||||
"cgvm", "fb", fb16, 396*224*2, true);
|
||||
}
|
||||
SDL_UnlockSurface(app.surface); */
|
||||
SDL_UpdateWindowSurface(app.window);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
static int get_calculator(void)
|
||||
{
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
int rc;
|
||||
if((rc = libusb_init(&app.libusb_ctx)))
|
||||
return elog_libusb(rc, "error initializing libusb");
|
||||
|
||||
libusb_set_option(app.libusb_ctx, LIBUSB_OPTION_LOG_LEVEL,
|
||||
LIBUSB_LOG_LEVEL_WARNING);
|
||||
fxlink_log_grab_libusb_logs();
|
||||
|
||||
delay_t d = delay_infinite();
|
||||
app.calc = fxlink_device_find_wait(app.libusb_ctx, NULL, &d);
|
||||
if(!app.calc)
|
||||
return elog("no calculator found!\n");
|
||||
|
||||
/* Claim the fxlink interface */
|
||||
if(!fxlink_device_claim_fxlink(app.calc)) {
|
||||
fxlink_device_cleanup(app.calc);
|
||||
free(app.calc);
|
||||
app.calc = NULL;
|
||||
return elog("could not claim fxlink interface on calculator!\n");
|
||||
}
|
||||
|
||||
hlog("cgvm");
|
||||
log_("connected to %s\n", fxlink_device_id(app.calc));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(int rc)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: cgvm_vnc [--calc] [--sdl]\n"
|
||||
"Connects to VNC server 127.0.0.1 and gets raw frames.\n"
|
||||
"--calc: Send frames to a calculator (once one is detected).\n"
|
||||
"--sdl: Show frames on an SDL window.\n");
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
for(int i = 1; i < argc; i++) {
|
||||
if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help"))
|
||||
usage(0);
|
||||
else if(!strcmp(argv[i], "--calc"))
|
||||
app.display_calc = true;
|
||||
else if(!strcmp(argv[i], "--sdl"))
|
||||
app.display_sdl = true;
|
||||
else {
|
||||
fprintf(stderr, "error: unrecognized option '%s'\n", argv[i]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if(argc == 1)
|
||||
usage(0);
|
||||
if(!app.display_calc && !app.display_sdl)
|
||||
usage(1);
|
||||
|
||||
atexit(cleanup);
|
||||
signal(SIGINT, exit);
|
||||
|
||||
/* Create the SDL window regarless of whether display_sdl is set, since we
|
||||
use its surface as RFB framebuffer */
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
app.window = SDL_CreateWindow("CG Virtual Monitor test VNC client",
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 396, 224, 0);
|
||||
app.surface = SDL_GetWindowSurface(app.window);
|
||||
assert(app.surface->format->BytesPerPixel == 4);
|
||||
|
||||
app.client = rfbGetClient(8, 3, 4);
|
||||
app.client->FinishedFrameBufferUpdate = fb_update;
|
||||
|
@ -64,14 +147,17 @@ int main(void)
|
|||
app.client->format.blueMax=app.surface->format->Bmask>>app.client->format.blueShift;
|
||||
SetFormatAndEncodings(app.client);
|
||||
|
||||
int argc = 4;
|
||||
char *argv[] = { "cgvm_vnc", "-encodings", "raw", "127.0.0.1", NULL };
|
||||
if(!rfbInitClient(app.client, &argc, argv)) {
|
||||
int _argc = 4;
|
||||
char *_argv[] = { "cgvm_vnc", "-encodings", "raw", "127.0.0.1", NULL };
|
||||
if(!rfbInitClient(app.client, &_argc, _argv)) {
|
||||
fprintf(stderr, "rfbInitClient failed\n");
|
||||
app.client = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(app.display_calc)
|
||||
get_calculator();
|
||||
|
||||
while(1) {
|
||||
SDL_Event e;
|
||||
while(SDL_PollEvent(&e)) {
|
||||
|
@ -90,6 +176,9 @@ int main(void)
|
|||
fprintf(stderr, "HandleRFBServerMessage() failed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
struct timeval zero_tv = { 0 };
|
||||
libusb_handle_events_timeout(app.libusb_ctx, &zero_tv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue