-
-
Notifications
You must be signed in to change notification settings - Fork 496
/
x11.c
180 lines (152 loc) · 6.51 KB
/
x11.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Compile with LIBS := -lvncserver -lxcb -lxcb-xtest -lxcb-keysyms
// Need CMake 3.24.0 to find these libraries. see https://cmake.org/cmake/help/v3.24/module/FindX11.html
// XWayland not support to read screen, because wayland not allow it.
// Read screen in wayland need use XDG desktop portals' interface `org.freedesktop.portal.Screenshot` and `org.freedesktop.portal.ScreenCast`
// Under some environment, this code not work well, see https://github.com/LibVNC/libvncserver/pull/503#issuecomment-1064472566
#include <rfb/rfb.h>
#include <xcb/xcb.h>
#include <xcb/xtest.h>
#include <xcb/xcb_keysyms.h>
void dirty_copy(rfbScreenInfoPtr rfbScreen, const uint8_t* data, int width, int height, int nbytes);
void convert_bgrx_to_rgb(const uint8_t* in, uint16_t width, uint16_t height, uint8_t* buff);
void get_window_size(xcb_connection_t* conn, xcb_window_t window, uint16_t* width, uint16_t* height);
void get_window_image(xcb_connection_t* conn, xcb_window_t window, uint8_t* buff);
void send_keycode(xcb_connection_t *conn, xcb_keycode_t keycode, int press);
void send_keysym(xcb_connection_t *conn, xcb_keysym_t keysym, int press);
void send_button(xcb_connection_t *conn, xcb_button_t button, int press);
void send_motion(xcb_connection_t *conn, int16_t x, int16_t y);
// global
xcb_connection_t* conn;
static void keyCallback(rfbBool down, rfbKeySym keySym, rfbClientPtr client)
{
(void)(client);
send_keysym(conn, keySym, (int)down);
}
#define VNC_BUTTON_MASK_LEFT rfbButton1Mask
#define VNC_BUTTON_MASK_MIDDLE rfbButton2Mask
#define VNC_BUTTON_MASK_RIGHT rfbButton3Mask
#define VNC_BUTTON_MASK_UP rfbWheelUpMask
#define VNC_BUTTON_MASK_DOWN rfbWheelDownMask
#define X11_BUTTON_LEFT XCB_BUTTON_INDEX_1
#define X11_BUTTON_MIDDLE XCB_BUTTON_INDEX_2
#define X11_BUTTON_RIGHT XCB_BUTTON_INDEX_3
#define X11_BUTTON_UP XCB_BUTTON_INDEX_4
#define X11_BUTTON_DOWN XCB_BUTTON_INDEX_5
static void mouseCallback(int buttonMask, int x, int y, rfbClientPtr client)
{
(void)(client);
send_button(conn, X11_BUTTON_LEFT, !!(buttonMask & VNC_BUTTON_MASK_LEFT));
send_button(conn, X11_BUTTON_MIDDLE, !!(buttonMask & VNC_BUTTON_MASK_MIDDLE));
send_button(conn, X11_BUTTON_RIGHT, !!(buttonMask & VNC_BUTTON_MASK_RIGHT));
send_button(conn, X11_BUTTON_UP, !!(buttonMask & VNC_BUTTON_MASK_UP));
send_button(conn, X11_BUTTON_DOWN, !!(buttonMask & VNC_BUTTON_MASK_DOWN));
send_motion(conn, (int16_t)x, (int16_t)y);
}
int main(int argc, char* argv[])
{
conn = xcb_connect(NULL, NULL);
const xcb_setup_t* setup = xcb_get_setup(conn);
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
xcb_screen_t* screen = iter.data;
xcb_window_t root = screen->root;
int16_t width;
int16_t height;
get_window_size(conn, root, &width, &height);
void* frameBuffer = malloc(4UL * width * height);
rfbScreenInfoPtr rfbScreen = rfbGetScreen(&argc, argv, (int)width, (int)height, 8, 3, 4);
rfbScreen->desktopName = "LibVNCServer X11 Example";
rfbScreen->frameBuffer = (char*)malloc(4UL * width * height);
rfbScreen->alwaysShared = TRUE;
rfbScreen->kbdAddEvent = keyCallback;
rfbScreen->ptrAddEvent = mouseCallback;
rfbInitServer(rfbScreen);
rfbRunEventLoop(rfbScreen, 10000, TRUE);
while (TRUE)
{
get_window_image(conn, root, (uint8_t*)frameBuffer);
dirty_copy(rfbScreen, (uint8_t*)frameBuffer, (int)width, (int)height, 4);
}
free(rfbScreen->frameBuffer);
free(frameBuffer);
xcb_disconnect(conn);
return EXIT_SUCCESS;
}
void dirty_copy(rfbScreenInfoPtr rfbScreen, const uint8_t* data, int width, int height, int nbytes)
{
// check dirty by line, because it is convenient to copy
for (int y = 0; y < height; y++)
{
rfbBool dirty = FALSE;
for (int x = 0; x < width; x++)
{
const void* s1 = &rfbScreen->frameBuffer[(y*width+x)*nbytes];
const void* s2 = &data[(y*width+x)*nbytes];
if (memcmp(s1, s2, nbytes) != 0)
{
dirty = TRUE;
break;
}
}
if (dirty)
{
memcpy(&rfbScreen->frameBuffer[y*width*nbytes], &data[y*width*nbytes], width*nbytes);
rfbMarkRectAsModified(rfbScreen, 0, y, width, y+1);
}
}
}
void convert_bgrx_to_rgb(const uint8_t* in, uint16_t width, uint16_t height, uint8_t* buff)
{
for (uint16_t y = 0; y < height; y++)
{
for(uint16_t x = 0; x < width; x++)
{
buff[(y*width+x)*4] = in[(y*width+x)*4 + 2];
buff[(y*width+x)*4 + 1] = in[(y*width+x)*4 + 1];
buff[(y*width+x)*4 + 2] = in[(y*width+x)*4];
}
}
}
void get_window_size(xcb_connection_t* conn, xcb_window_t window, uint16_t* width, uint16_t* height)
{
xcb_get_geometry_cookie_t cookie = xcb_get_geometry(conn, window);
xcb_get_geometry_reply_t* reply = xcb_get_geometry_reply(conn, cookie, NULL);
*width = reply->width;
*height = reply->height;
free(reply);
}
void get_window_image(xcb_connection_t* conn, xcb_window_t window, uint8_t* buff)
{
uint16_t width = 0;
uint16_t height = 0;
get_window_size(conn, window, &width, &height);
// will failed in wayland, xcb_get_image_data will return NULL, convert_bgrx_to_rgb will abort
xcb_get_image_cookie_t cookie = xcb_get_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, window, 0, 0, width, height, UINT32_MAX);
xcb_get_image_reply_t* reply = xcb_get_image_reply(conn, cookie, NULL);
convert_bgrx_to_rgb(xcb_get_image_data(reply), width, height, buff);
free(reply);
}
void send_keycode(xcb_connection_t *conn, xcb_keycode_t keycode, int press)
{
xcb_test_fake_input(conn, press ? XCB_KEY_PRESS : XCB_KEY_RELEASE, keycode, XCB_CURRENT_TIME, XCB_NONE, 0, 0, 0);
xcb_flush(conn);
}
void send_keysym(xcb_connection_t *conn, xcb_keysym_t keysym, int press)
{
xcb_key_symbols_t* symbols = xcb_key_symbols_alloc(conn);
xcb_keycode_t* code = xcb_key_symbols_get_keycode(symbols, keysym);
for (; code != NULL && *code != XCB_NO_SYMBOL; code++)
{
send_keycode(conn, *code, press);
}
xcb_key_symbols_free(symbols);
}
void send_button(xcb_connection_t *conn, xcb_button_t button, int press)
{
xcb_test_fake_input(conn, press ? XCB_BUTTON_PRESS : XCB_BUTTON_RELEASE, button, XCB_CURRENT_TIME, XCB_NONE, 0, 0, 0);
xcb_flush(conn);
}
void send_motion(xcb_connection_t *conn, int16_t x, int16_t y)
{
xcb_test_fake_input(conn, XCB_MOTION_NOTIFY, 0, XCB_CURRENT_TIME, XCB_NONE, x, y, 0);
xcb_flush(conn);
}