Commit 0fd54f99 authored by Jeremy White's avatar Jeremy White

Add a new scan thread, shift the logic to being to use it.

Also take advantage of xcb threading abilities to use a thread
to process X events instead of relying on a watch.
parent d3e3ef21
......@@ -9,4 +9,5 @@ x11spice_SOURCES = \
options.c \
session.c \
spice.c \
scan.c \
main.c
......@@ -29,10 +29,14 @@
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include <pixman.h>
#include <errno.h>
#include "x11spice.h"
#include "options.h"
#include "display.h"
#include "session.h"
#include "scan.h"
static xcb_screen_t *screen_of_display (xcb_connection_t *c, int screen)
......@@ -60,6 +64,58 @@ static int bits_per_pixel(display_t *d)
return 0;
}
static void * handle_xevents(void *opaque)
{
display_t *display = (display_t *) opaque;
xcb_generic_event_t *ev;
int i, n;
pixman_box16_t *p;
pixman_region16_t damage_region;
pixman_region_init(&damage_region);
while ((ev = xcb_wait_for_event(display->c)))
{
xcb_damage_notify_event_t *dev;
if (ev->response_type != display->damage_ext->first_event + XCB_DAMAGE_NOTIFY)
{
g_debug("Unexpected X event %d", ev->response_type);
continue;;
}
dev = (xcb_damage_notify_event_t *) ev;
g_debug("Damage Notify [seq %d|level %d|more %d|area (%dx%d)@%dx%d|geo (%dx%d)@%dx%d",
dev->sequence, dev->level, dev->level & 0x80,
dev->area.width, dev->area.height, dev->area.x, dev->area.y,
dev->geometry.width, dev->geometry.height, dev->geometry.x, dev->geometry.y);
pixman_region_union_rect(&damage_region, &damage_region,
dev->area.x, dev->area.y, dev->area.width, dev->area.height);
/* The MORE flag is 0x80 on the level field; the proto documentation
is wrong on this point. Check the xorg server code to see */
if (dev->level & 0x80)
continue;
xcb_damage_subtract(display->c, display->damage,
XCB_XFIXES_REGION_NONE, XCB_XFIXES_REGION_NONE);
p = pixman_region_rectangles(&damage_region, &n);
for (i = 0; i < n; i++)
scanner_push(&display->session->scanner, DAMAGE_SCAN_REPORT,
p[i].x1, p[i].y1, p[i].x2 - p[i].x1, p[i].y2 - p[i].y1);
pixman_region_clear(&damage_region);
}
pixman_region_clear(&damage_region);
return NULL;
}
int display_open(display_t *d, options_t *options)
{
......@@ -143,10 +199,13 @@ shm_image_t * create_shm_image(display_t *d, int w, int h)
shmi->shmaddr = shmat(shmi->shmid, 0, 0);
if (shmi->shmid == -1 || shmi->shmaddr == (void *) -1)
{
g_error("Cannot get shared memory of size %d", imgsize);
g_error("Cannot get shared memory of size %d; errno %d", imgsize, errno);
free(shmi);
return NULL;
}
/* We tell shmctl to detach now; that prevents us from holding this
shared memory segment forever in case of abnormal process exit. */
shmctl(shmi->shmid, IPC_RMID, NULL);
shmi->shmseg = xcb_generate_id(d->c);
cookie = xcb_shm_attach_checked(d->c, shmi->shmseg, shmi->shmid, 0);
......@@ -200,14 +259,37 @@ void destroy_shm_image(display_t *d, shm_image_t *shmi)
free(shmi->drawable_ptr);
}
void display_close(display_t *d)
// FIXME - is this necessary? And/or can we modify our pushing
// to spice to use it?
int display_create_fullscreen(display_t *d)
{
d->fullscreen = create_shm_image(d, 0, 0);
if (!d->fullscreen)
return X11SPICE_ERR_NOSHM;
return 0;
}
void display_destroy_fullscreen(display_t *d)
{
xcb_damage_destroy(d->c, d->damage);
if (d->fullscreen)
{
destroy_shm_image(d, d->fullscreen);
d->fullscreen = NULL;
}
}
int display_start_event_thread(display_t *d)
{
// FIXME - gthread?
return pthread_create(&d->event_thread, NULL, handle_xevents, d);
}
void display_close(display_t *d)
{
xcb_damage_destroy(d->c, d->damage);
display_destroy_fullscreen(d);
xcb_disconnect(d->c);
}
......@@ -25,6 +25,9 @@
#include <xcb/damage.h>
#include <xcb/shm.h>
typedef struct session_struct session_t;
/*----------------------------------------------------------------------------
** Structure definitions
**--------------------------------------------------------------------------*/
......@@ -51,6 +54,9 @@ typedef struct
const xcb_query_extension_reply_t *shm_ext;
shm_image_t *fullscreen;
pthread_t event_thread;
session_t *session;
} display_t;
......@@ -59,6 +65,9 @@ typedef struct
**--------------------------------------------------------------------------*/
int display_open(display_t *display, options_t *options);
void display_close(display_t *display);
int display_create_fullscreen(display_t *d);
void display_destroy_fullscreen(display_t *d);
int display_start_event_thread(display_t *d);
shm_image_t * create_shm_image(display_t *d, int w, int h);
int read_shm_image(display_t *d, shm_image_t *shmi, int x, int y);
void destroy_shm_image(display_t *d, shm_image_t *shmi);
......
......@@ -25,6 +25,8 @@
#include "options.h"
typedef struct session_struct session_t;
/*----------------------------------------------------------------------------
** Structure definitions
**--------------------------------------------------------------------------*/
......@@ -43,7 +45,7 @@ typedef struct
QXLWorker *worker;
int compression_level;
void *session_ptr;
session_t *session;
} spice_t;
/*----------------------------------------------------------------------------
......@@ -51,5 +53,7 @@ typedef struct
**--------------------------------------------------------------------------*/
int spice_start(spice_t *s, options_t *options);
void spice_end(spice_t *s);
int spice_create_primary(spice_t *s, int w, int h, int bytes_per_line, void *shmaddr);
void spice_destroy_primary(spice_t *s);
#endif
......@@ -36,6 +36,7 @@ int main(int argc, char *argv[])
int display_opened = 0;
int spice_started = 0;
int session_created = 0;
/*------------------------------------------------------------------------
** Parse arguments
......@@ -46,12 +47,21 @@ int main(int argc, char *argv[])
goto exit;
options_from_config(&session.options);
/*------------------------------------------------------------------------
** Create the session
**----------------------------------------------------------------------*/
rc = session_create(&session);
if (rc)
goto exit;
session_created = 1;
/*------------------------------------------------------------------------
** Open the display
**----------------------------------------------------------------------*/
rc = display_open(&session.display, &session.options);
if (rc)
goto exit;
session.display.session = &session;
display_opened = 1;
/*------------------------------------------------------------------------
......@@ -70,7 +80,8 @@ int main(int argc, char *argv[])
spice_started = 1;
/*------------------------------------------------------------------------
** Leave the GUI running until we have a reason to quit
** Start our session and leave the GUI running until we have
** a reason to quit
**----------------------------------------------------------------------*/
rc = session_start(&session);
if (rc)
......@@ -91,5 +102,8 @@ exit:
options_free(&session.options);
if (session_created)
session_destroy(&session);
return rc;
}
/*
Copyright (C) 2016 Jeremy White <jwhite@codeweavers.com>
All rights reserved.
This file is part of x11spice
x11spice is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
x11spice is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with x11spice. If not, see <http://www.gnu.org/licenses/>.
*/
#include "x11spice.h"
#include "session.h"
#include "scan.h"
#include <stdlib.h>
#include <pthread.h>
#include <glib.h>
// FIXME - refactor and move this...
static QXLDrawable *shm_image_to_drawable(shm_image_t *shmi, int x, int y)
{
QXLDrawable *drawable;
QXLImage *qxl_image;
int i;
drawable = calloc(1, sizeof(*drawable) + sizeof(*qxl_image));
if (! drawable)
return NULL;
qxl_image = (QXLImage *) (drawable + 1);
drawable->release_info.id = (uint64_t) shmi;
shmi->drawable_ptr = drawable;
drawable->surface_id = 0;
drawable->type = QXL_DRAW_COPY;
drawable->effect = QXL_EFFECT_OPAQUE;
drawable->clip.type = SPICE_CLIP_TYPE_NONE;
drawable->bbox.left = x;
drawable->bbox.top = y;
drawable->bbox.right = x + shmi->w;
drawable->bbox.bottom = y + shmi->h;
/*
* surfaces_dest[i] should apparently be filled out with the
* surfaces that we depend on, and surface_rects should be
* filled with the rectangles of those surfaces that we
* are going to use.
* FIXME - explore this instead of blindly copying...
*/
for (i = 0; i < 3; ++i)
drawable->surfaces_dest[i] = -1;
drawable->u.copy.src_area.left = 0;
drawable->u.copy.src_area.top = 0;
drawable->u.copy.src_area.right = shmi->w;
drawable->u.copy.src_area.bottom = shmi->h;
drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT;
drawable->u.copy.src_bitmap = (QXLPHYSICAL) qxl_image;
qxl_image->descriptor.id = 0;
qxl_image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
qxl_image->descriptor.flags = 0;
qxl_image->descriptor.width = shmi->w;
qxl_image->descriptor.height = shmi->h;
// FIXME - be a bit more dynamic...
qxl_image->bitmap.format = SPICE_BITMAP_FMT_RGBA;
qxl_image->bitmap.flags = SPICE_BITMAP_FLAGS_TOP_DOWN | QXL_BITMAP_DIRECT;
qxl_image->bitmap.x = shmi->w;
qxl_image->bitmap.y = shmi->h;
qxl_image->bitmap.stride = shmi->bytes_per_line;
qxl_image->bitmap.palette = 0;
qxl_image->bitmap.data = (QXLPHYSICAL) shmi->shmaddr;
// FIXME - cache images at all?
return drawable;
}
static guint64 get_timeout(scanner_t *scanner)
{
// FIXME - make this a bit smarter...
return G_USEC_PER_SEC / 30;
}
static void save_ximage_pnm(shm_image_t *shmi)
{
int x,y;
guint32 *pixel;
static int count = 0;
char fname[200];
FILE *fp;
sprintf(fname, "ximage%04d.ppm", count++);
fp = fopen(fname, "w");
pixel = (guint32 *) shmi->shmaddr;
fprintf(fp,"P3\n%d %d\n255\n", shmi->w, shmi->h);
for (y=0; y<shmi->h; y++)
{
for (x=0; x<shmi->w; x++)
{
fprintf(fp,"%u %u %u\n",
((*pixel)&0x0000ff)>>0,
((*pixel)&0x00ff00)>>8,
((*pixel)&0xff0000)>>16
);
pixel++;
}
}
fclose(fp);
}
static void handle_damage_report(session_t *session, scan_report_t *r)
{
shm_image_t *shmi;
shmi = create_shm_image(&session->display, r->w, r->h);
if (!shmi)
{
g_debug("Unexpected failure to create_shm_image of area %dx%d", r->w, r->h);
return;
}
if (read_shm_image(&session->display, shmi, r->x, r->y) == 0)
{
//save_ximage_pnm(shmi);
QXLDrawable *drawable = shm_image_to_drawable(shmi, r->x, r->y);
if (drawable)
{
g_async_queue_push(session->draw_queue, drawable);
spice_qxl_wakeup(&session->spice.display_sin);
// FIXME - Note that shmi is not cleaned up at this point
return;
}
else
g_debug("Unexpected failure to create drawable");
}
else
g_debug("Unexpected failure to read shm of area %dx%d", r->w, r->h);
if (shmi)
destroy_shm_image(&session->display, shmi);
}
static void * scanner_run(void *opaque)
{
scanner_t *scanner = (scanner_t *) opaque;
while (1)
{
scan_report_t *r;
r = (scan_report_t *) g_async_queue_timeout_pop(scanner->queue, get_timeout(scanner));
if (! r)
continue;
switch(r->type)
{
case EXIT_SCAN_REPORT:
return 0;
case DAMAGE_SCAN_REPORT:
handle_damage_report(scanner->session, r);
break;
// FIXME - implement hint + scan
}
}
return 0;
}
static void free_queue_item(gpointer data)
{
free(data);
// FIXME - test this...
}
int scanner_create(scanner_t *scanner)
{
scanner->queue = g_async_queue_new_full(free_queue_item);
// FIXME - gthread?
return pthread_create(&scanner->thread, NULL, scanner_run, scanner);
}
int scanner_destroy(scanner_t *scanner)
{
void *err;
int rc;
scanner_push(scanner, EXIT_SCAN_REPORT, 0, 0, 0, 0);
rc = pthread_join(scanner->thread, &err);
if (rc == 0)
rc = (int) (long) err;
if (scanner->queue)
{
g_async_queue_unref(scanner->queue);
scanner->queue = NULL;
}
return rc;
}
int scanner_push(scanner_t *scanner, scan_type_t type, int x, int y, int w, int h)
{
scan_report_t *r = malloc(sizeof(*r));
if (r)
{
r->type = type;
r->x = x;
r->y = y;
r->w = w;
r->h = h;
g_async_queue_push(scanner->queue, r);
return 0;
}
return X11SPICE_ERR_MALLOC;
}
/*
Copyright (C) 2016 Jeremy White <jwhite@codeweavers.com>
All rights reserved.
This file is part of x11spice
x11spice is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
x11spice is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with x11spice. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCAN_H_
#define SCAN_H_
/*----------------------------------------------------------------------------
** Definitions and simple types
**--------------------------------------------------------------------------*/
typedef enum { DAMAGE_SCAN_REPORT, SCANLINE_SCAN_REPORT, HUNCH_SCAN_REPORT, EXIT_SCAN_REPORT } scan_type_t;
typedef struct session_struct session_t;
/*----------------------------------------------------------------------------
** Structure definitions
**--------------------------------------------------------------------------*/
typedef struct
{
scan_type_t type;
int x;
int y;
int w;
int h;
} scan_report_t;
typedef struct
{
pthread_t thread;
GAsyncQueue *queue;
session_t *session;
} scanner_t;
/*----------------------------------------------------------------------------
** Prototypes
**--------------------------------------------------------------------------*/
int scanner_create(scanner_t *scanner);
int scanner_destroy(scanner_t *scanner);
int scanner_push(scanner_t *scanner, scan_type_t type, int x, int y, int w, int h);
#endif
This diff is collapsed.
......@@ -25,20 +25,18 @@
#include "display.h"
#include "local_spice.h"
#include "gui.h"
#include <pixman.h>
#include "scan.h"
/*----------------------------------------------------------------------------
** Structure definitions
**--------------------------------------------------------------------------*/
typedef struct
typedef struct session_struct
{
options_t options;
display_t display;
spice_t spice;
gui_t gui;
SpiceWatch *xwatch;
pixman_region16_t damage_region;
scanner_t scanner;
GAsyncQueue *cursor_queue;
GAsyncQueue *draw_queue;
......@@ -47,13 +45,15 @@ typedef struct
/*----------------------------------------------------------------------------
** Prototypes
**--------------------------------------------------------------------------*/
int session_create(session_t *s);
void session_destroy(session_t *s);
int session_start(session_t *s);
void session_end(session_t *s);
void *session_pop_draw(void *session_ptr);
int session_draw_waiting(void *session_ptr);
void session_handle_key(void *session_ptr, uint8_t keycode, int is_press);
void session_handle_mouse_position(void *session_ptr, int x, int y, uint32_t buttons_state);
void session_handle_mouse_buttons(void *session_ptr, uint32_t buttons_state);
void session_handle_mouse_wheel(void *session_ptr, int wheel_motion, uint32_t buttons_state);
void *session_pop_draw(session_t *session);
int session_draw_waiting(session_t *session);
void session_handle_key(session_t *session, uint8_t keycode, int is_press);
void session_handle_mouse_position(session_t *session, int x, int y, uint32_t buttons_state);
void session_handle_mouse_buttons(session_t *session, uint32_t buttons_state);
void session_handle_mouse_wheel(session_t *session, int wheel_motion, uint32_t buttons_state);
#endif
......@@ -224,7 +224,7 @@ static int get_command(QXLInstance *qin, struct QXLCommandExt *cmd)
spice_t *s = SPICE_CONTAINEROF(qin, spice_t, display_sin);
QXLDrawable *drawable;
drawable = session_pop_draw(s->session_ptr);
drawable = session_pop_draw(s->session);
if (! drawable)
return 0;
......@@ -241,7 +241,7 @@ static int req_cmd_notification(QXLInstance *qin)
{
spice_t *s = SPICE_CONTAINEROF(qin, spice_t, display_sin);
if (session_draw_waiting(s->session_ptr) > 0)
if (session_draw_waiting(s->session) > 0)
return 0;
return 1;
......@@ -354,7 +354,7 @@ static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
frag += MIN_KEYCODE;
}
session_handle_key(s->session_ptr, frag, is_down);
session_handle_key(s->session, frag, is_down);
}
static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
......@@ -371,19 +371,50 @@ void tablet_set_logical_size(SpiceTabletInstance* tablet, int width, int height)
void tablet_position(SpiceTabletInstance* tablet, int x, int y, uint32_t buttons_state)
{
spice_t *s = SPICE_CONTAINEROF(tablet, spice_t, tablet_sin);
session_handle_mouse_position(s->session_ptr, x, y, buttons_state);
session_handle_mouse_position(s->session, x, y, buttons_state);
}
void tablet_wheel(SpiceTabletInstance* tablet, int wheel_motion, uint32_t buttons_state)
{
spice_t *s = SPICE_CONTAINEROF(tablet, spice_t, tablet_sin);
session_handle_mouse_wheel(s->session_ptr, wheel_motion, buttons_state);
session_handle_mouse_wheel(s->session, wheel_motion, buttons_state);
}
void tablet_buttons(SpiceTabletInstance* tablet, uint32_t buttons_state)
{
spice_t *s = SPICE_CONTAINEROF(tablet, spice_t, tablet_sin);
session_handle_mouse_buttons(s->session_ptr, buttons_state);
session_handle_mouse_buttons(s->session, buttons_state);
}
int spice_create_primary(spice_t *s, int w, int h, int bytes_per_line, void *shmaddr)
{
QXLDevSurfaceCreate surface;
memset(&surface, 0, sizeof(surface));
surface.height = h;
surface.width = w;
// FIXME - negative stride?
surface.stride = bytes_per_line;
surface.type = QXL_SURF_TYPE_PRIMARY;
surface.flags = 0;
surface.group_id = 0;
surface.mouse_mode = TRUE;
// Position appears to be completely unused
surface.position = 0;
// FIXME - compute this dynamically?
surface.format = SPICE_SURFACE_FMT_32_xRGB;
surface.mem = (QXLPHYSICAL) shmaddr;
spice_qxl_create_primary_surface(&s->display_sin, 0, &surface);
return 0;
}
void spice_destroy_primary(spice_t *s)
{
spice_qxl_destroy_primary_surface(&s->display_sin, 0);
}
void initialize_spice_instance(spice_t *s)
......