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