/* 字符控制台状态 */ int width; int height; int total_height; int backscroll_height; int x, y; int x_saved, y_saved; int y_displayed; int y_base; TextAttributes t_attrib_default; /* 默认字符属性 */ TextAttributes t_attrib; /* 当前活动字符属性 */ TextCell *cells; int text_x[2], text_y[2], cursor_invalidate; int echo;
int update_x0; int update_y0; int update_x1; int update_y1;
enumTTYStatestate; int esc_params[MAX_ESC_PARAMS]; int nb_esc_params;
structVncDisplay { QTAILQ_HEAD(, VncState) clients; int num_connecting; int num_shared; int num_exclusive; int connections_limit; VncSharePolicy share_policy; QIONetListener *listener; QIONetListener *wslistener; DisplaySurface *ds; DisplayChangeListener dcl; kbd_layout_t *kbd_layout; int lock_key_sync; QEMUPutLEDEntry *led; int ledstate; QKbdState *kbd; QemuMutex mutex;
QEMUCursor *cursor; int cursor_msize; uint8_t *cursor_mask;
structVncSurfaceguest;/* guest visible surface (aka ds->surface) */ pixman_image_t *server; /* vnc server surface */ int true_width; /* server surface width before rounding up */
constchar *id; QTAILQ_ENTRY(VncDisplay) next; bool is_unix; char *password; time_t expires; int auth; int subauth; /* Used by VeNCrypt */ int ws_auth; /* Used by websockets */ int ws_subauth; /* Used by websockets */ bool lossy; bool non_adaptive; bool power_control; QCryptoTLSCreds *tlscreds; QAuthZ *tlsauthz; char *tlsauthzid; #ifdef CONFIG_VNC_SASL VncDisplaySASL sasl; #endif
structVncState { uint64_t magic; QIOChannelSocket *sioc; /* The underlying socket */ QIOChannel *ioc; /* The channel currently used for I/O */ guint ioc_tag; gboolean disconnecting;
DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_DIRTY_BITS); uint8_t **lossy_rect; /* Not an Array to avoid costly memcpy in * vnc-jobs-async.c */
VncDisplay *vd; VncStateUpdate update; /* Most recent pending request from client */ VncStateUpdate job_update; /* Currently processed by job thread */ int has_dirty; uint32_t features; int absolute; int last_x; int last_y; uint32_t last_bmask; size_t client_width; /* limited to u16 by RFB proto */ size_t client_height; /* limited to u16 by RFB proto */ VncShareMode share_mode;
uint32_t vnc_encoding;
int major; int minor;
int auth; int subauth; /* Used by VeNCrypt */ char challenge[VNC_AUTH_CHALLENGE_SIZE]; QCryptoTLSSession *tls; /* Borrowed pointer from channel, don't free */ #ifdef CONFIG_VNC_SASL VncStateSASL sasl; #endif bool encode_ws; bool websocket;
#ifdef CONFIG_VNC VncClientInfo *info; #endif
/* Job thread bottom half has put data for a forced update * into the output buffer. This offset points to the end of * the update data in the output buffer. This lets us determine * when a force update is fully sent to the client, allowing * us to process further forced updates. */ size_t force_update_offset; /* We allow multiple incremental updates or audio capture * samples to be queued in output buffer, provided the * buffer size doesn't exceed this threshold. The value * is calculating dynamically based on framebuffer size * and audio sample settings in vnc_update_throttle_offset() */ size_t throttle_output_offset; Buffer output; Buffer input; /* current output mode information */ VncWritePixels *write_pixels; PixelFormat client_pf; pixman_format_code_t client_format; bool client_be;
/* * We use a single global queue, but most of the functions are * already reentrant, so we can easily add more than one encoding thread */ static VncJobQueue *queue;
staticintvnc_worker_thread_loop(VncJobQueue *queue) { VncJob *job; VncRectEntry *entry, *tmp; VncState vs = {}; int n_rectangles; int saved_offset;
vnc_lock_queue(queue); // 等待全局 queue 不为空 while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) { qemu_cond_wait(&queue->cond, &queue->mutex); } /* Here job can only be NULL if queue->exit is true */ job = QTAILQ_FIRST(&queue->jobs); // 取下第一个 job vnc_unlock_queue(queue); assert(job->vs->magic == VNC_MAGIC);
if (queue->exit) { return-1; }
vnc_lock_output(job->vs); // 锁上 VncState if (job->vs->ioc == NULL || job->vs->abort == true) { // io channel 为空或 abort 为真,断开连接 vnc_unlock_output(job->vs); goto disconnected; } if (buffer_empty(&job->vs->output)) { // 这里的 output 为 Buffer 类型,类似于 iovec,包含着长度与一个指向 buffer 的指针 // buffer_move_empty(*to, *from) 的作用就是释放 to 的 buffer,将 from 的 buffer 给到 to 的 buffer /* * Looks like a NOP as it obviously moves no data. But it * moves the empty buffer, so we don't have to malloc a new * one for vs.output */ buffer_move_empty(&vs.output, &job->vs->output); } vnc_unlock_output(job->vs);
/* Make a local copy of vs and switch output buffers */ vnc_async_encoding_start(job->vs, &vs); vs.magic = VNC_MAGIC;
/* Put n_rectangles at the beginning of the message */ vs.output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
vnc_lock_output(job->vs); if (job->vs->ioc != NULL) { buffer_move(&job->vs->jobs_buffer, &vs.output); /* Copy persistent encoding data */ vnc_async_encoding_end(job->vs, &vs);
voiddpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) { DisplayState *s = con->ds; DisplayChangeListener *dcl; int width = qemu_console_get_width(con, x + w); int height = qemu_console_get_height(con, y + h);
x = MAX(x, 0); y = MAX(y, 0); x = MIN(x, width); y = MIN(y, height); w = MIN(w, width - x); h = MIN(h, height - y);
if (!qemu_console_is_visible(con)) { return; } dpy_gfx_update_texture(con, con->surface, x, y, w, h); QLIST_FOREACH(dcl, &s->listeners, next) { if (con != (dcl->con ? dcl->con : active_console)) { continue; } if (dcl->ops->dpy_gfx_update) { dcl->ops->dpy_gfx_update(dcl, x, y, w, h); } } }
staticvoidvnc_dpy_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { VncDisplay *vd = container_of(dcl, VncDisplay, dcl); structVncSurface *s = &vd->guest;
staticintvnc_update_client(VncState *vs, int has_dirty) { VncDisplay *vd = vs->vd; VncJob *job; int y; int height, width; int n = 0;
if (vs->disconnecting) { vnc_disconnect_finish(vs); return0; }
vs->has_dirty += has_dirty; if (!vnc_should_update(vs)) { return0; }
if (!vs->has_dirty && vs->update != VNC_STATE_UPDATE_FORCE) { return0; }
/* * Send screen updates to the vnc client using the server * surface and server dirty map. guest surface updates * happening in parallel don't disturb us, the next pass will * send them to the client. */ job = vnc_job_new(vs); // 创建新的 VncJob