Z-Pocket Game Pro(ZPG Pro)
vncviewer(SDL2)
參考資訊:
1. vncviewer
main.c
#include <SDL.h>
#include <signal.h>
#include <rfb/rfbclient.h>
struct {
int sdl;
int rfb;
} buttonMapping[] = {
{1, rfbButton1Mask},
{2, rfbButton2Mask},
{3, rfbButton3Mask},
{4, rfbButton4Mask},
{5, rfbButton5Mask},
{0, 0}
};
struct {
char mask;
int bits_stored;
} utf8Mapping[] = {
{0b00111111, 6},
{0b01111111, 7},
{0b00011111, 5},
{0b00001111, 4},
{0b00000111, 3},
{0, 0}
};
int sdlFlags;
SDL_Window *sdlWindow;
SDL_Texture *sdlTexture;
SDL_Renderer *sdlRenderer;
static int enableResizable = 1, viewOnly, listenLoop, buttonMask;
int x, y;
static int rightAltKeyDown, leftAltKeyDown;
static rfbBool resize(rfbClient *client)
{
int width = client->width, height = client->height, depth = client->format.bitsPerPixel;
if(enableResizable) {
sdlFlags |= SDL_WINDOW_RESIZABLE;
}
client->updateRect.x = client->updateRect.y = 0;
client->updateRect.w = width;
client->updateRect.h = height;
SDL_FreeSurface(rfbClientGetClientData(client, SDL_Init));
SDL_Surface *sdl = SDL_CreateRGBSurface(0, width, height, depth, 0, 0, 0, 0);
if(!sdl) {
rfbClientErr("resize: error creating surface: %s\n", SDL_GetError());
}
rfbClientSetClientData(client, SDL_Init, sdl);
client->width = sdl->pitch / (depth / 8);
client->frameBuffer = sdl->pixels;
client->format.bitsPerPixel = depth;
client->format.redShift = sdl->format->Rshift;
client->format.greenShift = sdl->format->Gshift;
client->format.blueShift = sdl->format->Bshift;
client->format.redMax = sdl->format->Rmask >> client->format.redShift;
client->format.greenMax = sdl->format->Gmask >> client->format.greenShift;
client->format.blueMax = sdl->format->Bmask >> client->format.blueShift;
SetFormatAndEncodings(client);
if(!sdlWindow) {
sdlWindow = SDL_CreateWindow(client->desktopName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, sdlFlags);
if(!sdlWindow) {
rfbClientErr("resize: error creating window: %s\n", SDL_GetError());
}
}
else {
SDL_SetWindowSize(sdlWindow, width, height);
}
if(!sdlRenderer) {
sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, 0);
if(!sdlRenderer) {
rfbClientErr("resize: error creating renderer: %s\n", SDL_GetError());
}
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
}
SDL_RenderSetLogicalSize(sdlRenderer, width, height);
if(sdlTexture) {
SDL_DestroyTexture(sdlTexture);
}
sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
if(!sdlTexture) {
rfbClientErr("resize: error creating texture: %s\n", SDL_GetError());
}
return TRUE;
}
static rfbKeySym SDL_key2rfbKeySym(SDL_KeyboardEvent *e)
{
rfbKeySym k = 0;
SDL_Keycode sym = e->keysym.sym;
switch(sym) {
case SDLK_BACKSPACE:
k = XK_BackSpace;
break;
case SDLK_TAB:
k = XK_Tab;
break;
case SDLK_CLEAR:
k = XK_Clear;
break;
case SDLK_RETURN:
k = XK_Return;
break;
case SDLK_PAUSE:
k = XK_Pause;
break;
case SDLK_ESCAPE:
k = XK_Escape;
break;
case SDLK_DELETE:
k = XK_Delete;
break;
case SDLK_KP_0:
k = XK_KP_0;
break;
case SDLK_KP_1:
k = XK_KP_1;
break;
case SDLK_KP_2:
k = XK_KP_2;
break;
case SDLK_KP_3:
k = XK_KP_3;
break;
case SDLK_KP_4:
k = XK_KP_4;
break;
case SDLK_KP_5:
k = XK_KP_5;
break;
case SDLK_KP_6:
k = XK_KP_6;
break;
case SDLK_KP_7:
k = XK_KP_7;
break;
case SDLK_KP_8:
k = XK_KP_8;
break;
case SDLK_KP_9:
k = XK_KP_9;
break;
case SDLK_KP_PERIOD:
k = XK_KP_Decimal;
break;
case SDLK_KP_DIVIDE:
k = XK_KP_Divide;
break;
case SDLK_KP_MULTIPLY:
k = XK_KP_Multiply;
break;
case SDLK_KP_MINUS:
k = XK_KP_Subtract;
break;
case SDLK_KP_PLUS:
k = XK_KP_Add;
break;
case SDLK_KP_ENTER:
k = XK_KP_Enter;
break;
case SDLK_KP_EQUALS:
k = XK_KP_Equal;
break;
case SDLK_UP:
k = XK_Up;
break;
case SDLK_DOWN:
k = XK_Down;
break;
case SDLK_RIGHT:
k = XK_Right;
break;
case SDLK_LEFT:
k = XK_Left;
break;
case SDLK_INSERT:
k = XK_Insert;
break;
case SDLK_HOME:
k = XK_Home;
break;
case SDLK_END:
k = XK_End;
break;
case SDLK_PAGEUP:
k = XK_Page_Up;
break;
case SDLK_PAGEDOWN:
k = XK_Page_Down;
break;
case SDLK_F1:
k = XK_F1;
break;
case SDLK_F2:
k = XK_F2;
break;
case SDLK_F3:
k = XK_F3;
break;
case SDLK_F4:
k = XK_F4;
break;
case SDLK_F5:
k = XK_F5;
break;
case SDLK_F6:
k = XK_F6;
break;
case SDLK_F7:
k = XK_F7;
break;
case SDLK_F8:
k = XK_F8;
break;
case SDLK_F9:
k = XK_F9;
break;
case SDLK_F10:
k = XK_F10;
break;
case SDLK_F11:
k = XK_F11;
break;
case SDLK_F12:
k = XK_F12;
break;
case SDLK_F13:
k = XK_F13;
break;
case SDLK_F14:
k = XK_F14;
break;
case SDLK_F15:
k = XK_F15;
break;
case SDLK_NUMLOCKCLEAR:
k = XK_Num_Lock;
break;
case SDLK_CAPSLOCK:
k = XK_Caps_Lock;
break;
case SDLK_SCROLLLOCK:
k = XK_Scroll_Lock;
break;
case SDLK_RSHIFT:
k = XK_Shift_R;
break;
case SDLK_LSHIFT:
k = XK_Shift_L;
break;
case SDLK_RCTRL:
k = XK_Control_R;
break;
case SDLK_LCTRL:
k = XK_Control_L;
break;
case SDLK_RALT:
k = XK_Alt_R;
break;
case SDLK_LALT:
k = XK_Alt_L;
break;
case SDLK_LGUI:
k = XK_Super_L;
break;
case SDLK_RGUI:
k = XK_Super_R;
break;
#if 0
case SDLK_COMPOSE:
k = XK_Compose;
break;
#endif
case SDLK_MODE:
k = XK_Mode_switch;
break;
case SDLK_HELP:
k = XK_Help;
break;
case SDLK_PRINTSCREEN:
k = XK_Print;
break;
case SDLK_SYSREQ:
k = XK_Sys_Req;
break;
default:
break;
}
if(k == 0 && sym > 0x0 && sym < 0x100 && e->keysym.mod & KMOD_CTRL) {
k = sym;
}
return k;
}
static rfbKeySym utf8char2rfbKeySym(const char chr[4])
{
int i;
int bytes = strlen(chr);
int shift = utf8Mapping[0].bits_stored * (bytes - 1);
rfbKeySym codep = (*chr++ & utf8Mapping[bytes].mask) << shift;
for(i = 1; i < bytes; ++i, ++chr) {
shift -= utf8Mapping[0].bits_stored;
codep |= ((char) * chr & utf8Mapping[0].mask) << shift;
}
return codep;
}
static void update(rfbClient *cl, int x, int y, int w, int h)
{
SDL_Rect r = {x, y, w, h};
SDL_Surface *sdl = rfbClientGetClientData(cl, SDL_Init);
if(SDL_UpdateTexture(sdlTexture, &r, sdl->pixels + y * sdl->pitch + x * 4, sdl->pitch) < 0) {
rfbClientErr("update: failed to update texture: %s\n", SDL_GetError());
}
if(SDL_RenderClear(sdlRenderer) < 0) {
rfbClientErr("update: failed to clear renderer: %s\n", SDL_GetError());
}
if(SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL) < 0) {
rfbClientErr("update: failed to copy texture to renderer: %s\n", SDL_GetError());
}
SDL_RenderPresent(sdlRenderer);
}
static void kbd_leds(rfbClient *cl, int value, int pad)
{
fprintf(stderr, "Led State= 0x%02X\n", value);
fflush(stderr);
}
static void text_chat(rfbClient *cl, int value, char *text)
{
switch(value) {
case rfbTextChatOpen:
fprintf(stderr, "TextChat: We should open a textchat window!\n");
TextChatOpen(cl);
break;
case rfbTextChatClose:
fprintf(stderr, "TextChat: We should close our window!\n");
break;
case rfbTextChatFinished:
fprintf(stderr, "TextChat: We should close our window!\n");
break;
default:
fprintf(stderr, "TextChat: Received \"%s\"\n", text);
break;
}
fflush(stderr);
}
#ifdef __MINGW32__
#define LOG_TO_FILE
#endif
#ifdef LOG_TO_FILE
#include <stdarg.h>
static void log_to_file(const char *format, ...)
{
va_list args;
FILE *logfile;
char buf[256];
time_t log_clock;
static char *logfile_str = 0;
if(!rfbEnableClientLogging) {
return;
}
if(logfile_str == 0) {
logfile_str = getenv("VNCLOG");
if(logfile_str == 0) {
logfile_str = "vnc.log";
}
}
logfile = fopen(logfile_str, "a");
va_start(args, format);
time(&log_clock);
strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock));
fprintf(logfile, buf);
vfprintf(logfile, format, args);
fflush(logfile);
va_end(args);
fclose(logfile);
}
#endif
static void cleanup(rfbClient *cl)
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
SDL_InitSubSystem(SDL_INIT_VIDEO);
if(cl) {
rfbClientCleanup(cl);
}
}
static rfbBool handleSDLEvent(rfbClient *cl, SDL_Event *e)
{
switch(e->type) {
case SDL_WINDOWEVENT:
switch(e->window.event) {
case SDL_WINDOWEVENT_EXPOSED:
SendFramebufferUpdateRequest(cl, 0, 0, cl->width, cl->height, FALSE);
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
if(SDL_HasClipboardText()) {
char *text = SDL_GetClipboardText();
if(text) {
rfbClientLog("sending clipboard text '%s'\n", text);
SendClientCutText(cl, text, strlen(text));
}
}
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
if(rightAltKeyDown) {
SendKeyEvent(cl, XK_Alt_R, FALSE);
rightAltKeyDown = FALSE;
rfbClientLog("released right Alt key\n");
}
if(leftAltKeyDown) {
SendKeyEvent(cl, XK_Alt_L, FALSE);
leftAltKeyDown = FALSE;
rfbClientLog("released left Alt key\n");
}
break;
}
break;
case SDL_MOUSEWHEEL: {
int steps;
if(viewOnly) {
break;
}
if(e->wheel.y > 0)
for(steps = 0; steps < e->wheel.y; ++steps) {
SendPointerEvent(cl, x, y, rfbButton4Mask);
SendPointerEvent(cl, x, y, 0);
}
if(e->wheel.y < 0)
for(steps = 0; steps > e->wheel.y; --steps) {
SendPointerEvent(cl, x, y, rfbButton5Mask);
SendPointerEvent(cl, x, y, 0);
}
if(e->wheel.x > 0)
for(steps = 0; steps < e->wheel.x; ++steps) {
SendPointerEvent(cl, x, y, 0b01000000);
SendPointerEvent(cl, x, y, 0);
}
if(e->wheel.x < 0)
for(steps = 0; steps > e->wheel.x; --steps) {
SendPointerEvent(cl, x, y, 0b00100000);
SendPointerEvent(cl, x, y, 0);
}
break;
}
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEMOTION: {
int state, i;
if(viewOnly) {
break;
}
if(e->type == SDL_MOUSEMOTION) {
x = e->motion.x;
y = e->motion.y;
state = e->motion.state;
}
else {
x = e->button.x;
y = e->button.y;
state = e->button.button;
for(i = 0; buttonMapping[i].sdl; i++)
if(state == buttonMapping[i].sdl) {
state = buttonMapping[i].rfb;
if(e->type == SDL_MOUSEBUTTONDOWN) {
buttonMask |= state;
}
else {
buttonMask &= ~state;
}
break;
}
}
SendPointerEvent(cl, x, y, buttonMask);
buttonMask &= ~(rfbButton4Mask | rfbButton5Mask);
break;
}
case SDL_KEYUP:
case SDL_KEYDOWN:
if(viewOnly) {
break;
}
SendKeyEvent(cl, SDL_key2rfbKeySym(&e->key), e->type == SDL_KEYDOWN ? TRUE : FALSE);
if(e->key.keysym.sym == SDLK_RALT) {
rightAltKeyDown = e->type == SDL_KEYDOWN;
}
if(e->key.keysym.sym == SDLK_LALT) {
leftAltKeyDown = e->type == SDL_KEYDOWN;
}
break;
case SDL_TEXTINPUT:
if(viewOnly) {
break;
}
rfbKeySym sym = utf8char2rfbKeySym(e->text.text);
SendKeyEvent(cl, sym, TRUE);
SendKeyEvent(cl, sym, FALSE);
break;
case SDL_JOYBUTTONUP:
case SDL_JOYBUTTONDOWN:
if(viewOnly) {
break;
}
{
//zpg pro
uint32_t myremap[]={
XK_Alt_L, XK_Control_L, XK_space, XK_Shift_L, XK_Tab, XK_BackSpace, XK_Page_Up, XK_Page_Down,
XK_Up, XK_Down, XK_Left, XK_Right, XK_Home, XK_End, XK_Escape, XK_Return, XK_F1, XK_F2
};
SendKeyEvent(cl, myremap[e->jbutton.button], e->type == SDL_JOYBUTTONDOWN ? TRUE : FALSE);
}
break;
case SDL_QUIT:
if(listenLoop) {
cleanup(cl);
return FALSE;
}
else {
rfbClientCleanup(cl);
exit(0);
}
default:
//rfbClientLog("ignore SDL event: 0x%x\n", e->type);
break;
}
return TRUE;
}
static void got_selection(rfbClient *cl, const char *text, int len)
{
rfbClientLog("received clipboard text '%s'\n", text);
if(SDL_SetClipboardText(text) != 0) {
rfbClientErr("could not set received clipboard text: %s\n", SDL_GetError());
}
}
static rfbCredential *get_credential(rfbClient *cl, int credentialType)
{
rfbCredential *c = malloc(sizeof(rfbCredential));
c->userCredential.username = malloc(RFB_BUF_SIZE);
c->userCredential.password = malloc(RFB_BUF_SIZE);
if(credentialType != rfbCredentialTypeUser) {
rfbClientErr("something else than username and password required for authentication\n");
return NULL;
}
rfbClientLog("username and password required for authentication!\n");
printf("user: ");
fgets(c->userCredential.username, RFB_BUF_SIZE, stdin);
printf("pass: ");
fgets(c->userCredential.password, RFB_BUF_SIZE, stdin);
c->userCredential.username[strcspn(c->userCredential.username, "\n")] = 0;
c->userCredential.password[strcspn(c->userCredential.password, "\n")] = 0;
return c;
}
#ifdef mac
#define main SDLmain
#endif
int main(int argc, char **argv)
{
int i, j;
SDL_Event e;
rfbClient *cl;
#ifdef LOG_TO_FILE
rfbClientLog = rfbClientErr = log_to_file;
#endif
for(i = 1, j = 1; i < argc; i++)
if(!strcmp(argv[i], "-viewonly")) {
viewOnly = 1;
}
else if(!strcmp(argv[i], "-resizable")) {
enableResizable = 1;
}
else if(!strcmp(argv[i], "-no-resizable")) {
enableResizable = 0;
}
else if(!strcmp(argv[i], "-listen")) {
listenLoop = 1;
argv[i] = "-listennofork";
++j;
}
else {
if(i != j) {
argv[j] = argv[i];
}
j++;
}
argc = j;
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK);
SDL_JoystickEventState(SDL_ENABLE);
SDL_JoystickOpen(0);
atexit(SDL_Quit);
signal(SIGINT, exit);
do {
cl = rfbGetClient(8, 3, 4);
cl->MallocFrameBuffer = resize;
cl->canHandleNewFBSize = TRUE;
cl->GotFrameBufferUpdate = update;
cl->HandleKeyboardLedState = kbd_leds;
cl->HandleTextChat = text_chat;
cl->GotXCutText = got_selection;
cl->GetCredential = get_credential;
cl->listenPort = LISTEN_PORT_OFFSET;
cl->listen6Port = LISTEN_PORT_OFFSET;
if(!rfbInitClient(cl, &argc, argv)) {
cl = NULL;
cleanup(cl);
break;
}
while(1) {
if(SDL_PollEvent(&e)) {
if(!handleSDLEvent(cl, &e)) {
break;
}
}
else {
i = WaitForMessage(cl, 500);
if(i < 0) {
cleanup(cl);
break;
}
if(i){
if(!HandleRFBServerMessage(cl)) {
cleanup(cl);
break;
}
}
}
}
}
while(listenLoop);
return 0;
}
編譯
$ cd $ wget https://github.com/steward-fu/zpg-pro/releases/download/v1.0/toolchain-x64.tar.gz $ tar xvf toolchain-x64.tar.gz $ sudo mv gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu /opt/zpg-sdk $ export PATH=$PATH:/opt/zpg-sdk/bin $ aarch64-none-linux-gnu-gcc main.c -o vnc -lSDL2 -lvncclient -I/opt/zpg-sdk/aarch64-none-linux-gnu/include/SDL2