All of lore.kernel.org
 help / color / mirror / Atom feed
From: Anthony Liguori <anthony@codemonkey.ws>
To: Gerd Hoffmann <kraxel@redhat.com>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH v3 09/10] spice: simple display
Date: Wed, 25 Aug 2010 14:57:00 -0500	[thread overview]
Message-ID: <4C75758C.80501@codemonkey.ws> (raw)
In-Reply-To: <1282745970-11506-10-git-send-email-kraxel@redhat.com>

On 08/25/2010 09:19 AM, Gerd Hoffmann wrote:
> With that patch applied you'll actually see the guests screen in the
> spice client.  This does *not* bring qxl and full spice support though.
> This is basically the qxl vga mode made more generic, so it plays
> together with any qemu-emulated gfx card.  You can display stdvga or
> cirrus via spice client.  You can have both vnc and spice enabled and
> clients connected at the same time.
>
> Signed-off-by: Gerd Hoffmann<kraxel@redhat.com>
> ---
>   Makefile.objs      |    2 +-
>   ui/qemu-spice.h    |    1 +
>   ui/spice-display.c |  399 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>   ui/spice-display.h |   69 +++++++++
>   vl.c               |    5 +
>   5 files changed, 475 insertions(+), 1 deletions(-)
>   create mode 100644 ui/spice-display.c
>   create mode 100644 ui/spice-display.h
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 20e6ad7..d09a1e4 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -88,7 +88,7 @@ common-obj-y += pflib.o
>   common-obj-$(CONFIG_BRLAPI) += baum.o
>   common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
>
> -common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o
> +common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o
>
>   audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
>   audio-obj-$(CONFIG_SDL) += sdlaudio.o
> diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h
> index 75e2502..5d6e2ea 100644
> --- a/ui/qemu-spice.h
> +++ b/ui/qemu-spice.h
> @@ -30,6 +30,7 @@ extern int using_spice;
>
>   void qemu_spice_init(void);
>   void qemu_spice_input_init(void);
> +void qemu_spice_display_init(DisplayState *ds);
>
>   #else  /* CONFIG_SPICE */
>
> diff --git a/ui/spice-display.c b/ui/spice-display.c
> new file mode 100644
> index 0000000..7163714
> --- /dev/null
> +++ b/ui/spice-display.c
> @@ -0,0 +1,399 @@
> +/*
> + * Copyright (C) 2010 Red Hat, Inc.
> + *
> + * This program 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 2 or
> + * (at your option) version 3 of the License.
> + *
> + * This program 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 this program; if not, see<http://www.gnu.org/licenses/>.
> + */
> +
> +#include<pthread.h>
> +
> +#include "qemu-common.h"
> +#include "qemu-spice.h"
> +#include "qemu-timer.h"
> +#include "qemu-queue.h"
> +#include "monitor.h"
> +#include "console.h"
> +#include "sysemu.h"
> +
> +#include "spice-display.h"
> +
> +static int debug = 0;
> +
> +static void __attribute__((format(printf,2,3)))
> +dprint(int level, const char *fmt, ...)
> +{
> +    va_list args;
> +
> +    if (level<= debug) {
> +        va_start(args, fmt);
> +        vfprintf(stderr, fmt, args);
> +        va_end(args);
> +    }
> +}
> +
> +int qemu_spice_rect_is_empty(const QXLRect* r)
> +{
> +    return r->top == r->bottom || r->left == r->right;
> +}
> +
> +void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r)
> +{
> +    if (qemu_spice_rect_is_empty(r)) {
> +        return;
> +    }
> +
> +    if (qemu_spice_rect_is_empty(dest)) {
> +        *dest = *r;
> +        return;
> +    }
> +
> +    dest->top = MIN(dest->top, r->top);
> +    dest->left = MIN(dest->left, r->left);
> +    dest->bottom = MAX(dest->bottom, r->bottom);
> +    dest->right = MAX(dest->right, r->right);
> +}
> +
> +/* called from spice server thread context (via interface_get_command) */
> +SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
> +{
> +    SimpleSpiceUpdate *update;
> +    QXLDrawable *drawable;
> +    QXLImage *image;
> +    QXLCommand *cmd;
> +    uint8_t *src, *dst;
> +    int by, bw, bh;
> +
> +    if (qemu_spice_rect_is_empty(&ssd->dirty)) {
> +        return NULL;
> +    };
> +
> +    pthread_mutex_lock(&ssd->lock);
> +    dprint(2, "%s: lr %d ->  %d,  tb ->  %d ->  %d\n", __FUNCTION__,
> +           ssd->dirty.left, ssd->dirty.right,
> +           ssd->dirty.top, ssd->dirty.bottom);
> +
> +    update   = qemu_mallocz(sizeof(*update));
> +    drawable =&update->drawable;
> +    image    =&update->image;
> +    cmd      =&update->ext.cmd;
> +
> +    bw       = ssd->dirty.right - ssd->dirty.left;
> +    bh       = ssd->dirty.bottom - ssd->dirty.top;
> +    update->bitmap = qemu_malloc(bw * bh * 4);
> +
> +    drawable->bbox            = ssd->dirty;
> +    drawable->clip.type       = SPICE_CLIP_TYPE_NONE;
> +    drawable->effect          = QXL_EFFECT_OPAQUE;
> +    drawable->release_info.id = (intptr_t)update;
> +    drawable->type            = QXL_DRAW_COPY;
> +
> +    drawable->u.copy.rop_descriptor  = SPICE_ROPD_OP_PUT;
> +    drawable->u.copy.src_bitmap      = (intptr_t)image;
> +    drawable->u.copy.src_area.right  = bw;
> +    drawable->u.copy.src_area.bottom = bh;
> +
> +    QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++);
> +    image->descriptor.type   = SPICE_IMAGE_TYPE_BITMAP;
> +    image->bitmap.flags      = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
> +    image->bitmap.stride     = bw * 4;
> +    image->descriptor.width  = image->bitmap.x = bw;
> +    image->descriptor.height = image->bitmap.y = bh;
> +    image->bitmap.data = (intptr_t)(update->bitmap);
> +    image->bitmap.palette = 0;
> +    image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
> +
> +    if (ssd->conv == NULL) {
> +        PixelFormat dst = qemu_default_pixelformat(32);
> +        ssd->conv = qemu_pf_conv_get(&dst,&ssd->ds->surface->pf);
> +        assert(ssd->conv);
> +    }
> +
> +    src = ds_get_data(ssd->ds) +
> +        ssd->dirty.top * ds_get_linesize(ssd->ds) +
> +        ssd->dirty.left * ds_get_bytes_per_pixel(ssd->ds);
> +    dst = update->bitmap;
> +    for (by = 0; by<  bh; by++) {
> +        qemu_pf_conv_run(ssd->conv, dst, src, bw);
> +        src += ds_get_linesize(ssd->ds);
> +        dst += image->bitmap.stride;
> +    }
> +
> +    cmd->type = QXL_CMD_DRAW;
> +    cmd->data = (intptr_t)drawable;
> +
> +    memset(&ssd->dirty, 0, sizeof(ssd->dirty));
> +    pthread_mutex_unlock(&ssd->lock);
> +    return update;
> +}
> +
> +/* called from spice server thread context (via interface_release_ressource) */
>    

There's still the implicit assumption that the reader understands that 
we're not carrying the global mutex here and that we're relying on the 
functions used to be re-entrant.  I think we need a big scary warning 
here otherwise it's just far too easy to break this code.

Regards,

Anthony Liguori

> +void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update)
> +{
> +    qemu_free(update->bitmap);
> +    qemu_free(update);
> +}
> +
> +void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
> +{
> +    QXLDevMemSlot memslot;
> +
> +    dprint(1, "%s:\n", __FUNCTION__);
> +
> +    memset(&memslot, 0, sizeof(memslot));
> +    memslot.slot_group_id = MEMSLOT_GROUP_HOST;
> +    memslot.virt_end = ~0;
> +    ssd->worker->add_memslot(ssd->worker,&memslot);
> +}
> +
> +void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
> +{
> +    QXLDevSurfaceCreate surface;
> +
> +    dprint(1, "%s: %dx%d\n", __FUNCTION__,
> +           ds_get_width(ssd->ds), ds_get_height(ssd->ds));
> +
> +    surface.format     = SPICE_SURFACE_FMT_32_xRGB;
> +    surface.width      = ds_get_width(ssd->ds);
> +    surface.height     = ds_get_height(ssd->ds);
> +    surface.stride     = -surface.width * 4;
> +    surface.mouse_mode = 0;
> +    surface.flags      = 0;
> +    surface.type       = 0;
> +    surface.mem        = (intptr_t)ssd->buf;
> +    surface.group_id   = MEMSLOT_GROUP_HOST;
> +    ssd->worker->create_primary_surface(ssd->worker, 0,&surface);
> +}
> +
> +void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
> +{
> +    dprint(1, "%s:\n", __FUNCTION__);
> +
> +    ssd->worker->destroy_primary_surface(ssd->worker, 0);
> +}
> +
> +void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason)
> +{
> +    SimpleSpiceDisplay *ssd = opaque;
> +
> +    if (running) {
> +        ssd->worker->start(ssd->worker);
> +    } else {
> +        ssd->worker->stop(ssd->worker);
> +    }
> +    ssd->running = running;
> +}
> +
> +/* display listener callbacks */
> +
> +void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
> +                               int x, int y, int w, int h)
> +{
> +    QXLRect update_area;
> +
> +    dprint(2, "%s: x %d y %d w %d h %d\n", __FUNCTION__, x, y, w, h);
> +    update_area.left = x,
> +    update_area.right = x + w;
> +    update_area.top = y;
> +    update_area.bottom = y + h;
> +
> +    pthread_mutex_lock(&ssd->lock);
> +    if (qemu_spice_rect_is_empty(&ssd->dirty)) {
> +        ssd->notify++;
> +    }
> +    qemu_spice_rect_union(&ssd->dirty,&update_area);
> +    pthread_mutex_unlock(&ssd->lock);
> +}
> +
> +void qemu_spice_display_resize(SimpleSpiceDisplay *ssd)
> +{
> +    dprint(1, "%s:\n", __FUNCTION__);
> +
> +    pthread_mutex_lock(&ssd->lock);
> +    memset(&ssd->dirty, 0, sizeof(ssd->dirty));
> +    qemu_pf_conv_put(ssd->conv);
> +    ssd->conv = NULL;
> +    pthread_mutex_unlock(&ssd->lock);
> +
> +    qemu_spice_destroy_host_primary(ssd);
> +    qemu_spice_create_host_primary(ssd);
> +
> +    pthread_mutex_lock(&ssd->lock);
> +    memset(&ssd->dirty, 0, sizeof(ssd->dirty));
> +    ssd->notify++;
> +    pthread_mutex_unlock(&ssd->lock);
> +}
> +
> +void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
> +{
> +    dprint(3, "%s:\n", __FUNCTION__);
> +    vga_hw_update();
> +    if (ssd->notify) {
> +        ssd->notify = 0;
> +        ssd->worker->wakeup(ssd->worker);
> +        if (debug>  1)
> +            fprintf(stderr, "%s: notify\n", __FUNCTION__);
> +    }
> +}
> +
> +/* spice display interface callbacks */
> +
> +static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
> +{
> +    SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
> +
> +    dprint(1, "%s:\n", __FUNCTION__);
> +    ssd->worker = qxl_worker;
> +}
> +
> +static void interface_set_compression_level(QXLInstance *sin, int level)
> +{
> +    dprint(1, "%s:\n", __FUNCTION__);
> +    /* nothing to do */
> +}
> +
> +static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
> +{
> +    dprint(3, "%s:\n", __FUNCTION__);
> +    /* nothing to do */
> +}
> +
> +static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
> +{
> +    SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
> +
> +    info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
> +    info->memslot_id_bits  = MEMSLOT_SLOT_BITS;
> +    info->num_memslots = NUM_MEMSLOTS;
> +    info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
> +    info->internal_groupslot_id = 0;
> +    info->qxl_ram_size = ssd->bufsize;
> +    info->n_surfaces = NUM_SURFACES;
> +}
> +
> +static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
> +{
> +    SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
> +    SimpleSpiceUpdate *update;
> +
> +    dprint(3, "%s:\n", __FUNCTION__);
> +    update = qemu_spice_create_update(ssd);
> +    if (update == NULL) {
> +        return false;
> +    }
> +    *ext = update->ext;
> +    return true;
> +}
> +
> +static int interface_req_cmd_notification(QXLInstance *sin)
> +{
> +    dprint(1, "%s:\n", __FUNCTION__);
> +    return 1;
> +}
> +
> +static void interface_release_resource(QXLInstance *sin,
> +                                       struct QXLReleaseInfoExt ext)
> +{
> +    SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
> +    uintptr_t id;
> +
> +    dprint(2, "%s:\n", __FUNCTION__);
> +    id = ext.info->id;
> +    qemu_spice_destroy_update(ssd, (void*)id);
> +}
> +
> +static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
> +{
> +    dprint(3, "%s:\n", __FUNCTION__);
> +    return false;
> +}
> +
> +static int interface_req_cursor_notification(QXLInstance *sin)
> +{
> +    dprint(1, "%s:\n", __FUNCTION__);
> +    return 1;
> +}
> +
> +static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
> +{
> +    fprintf(stderr, "%s: abort()\n", __FUNCTION__);
> +    abort();
> +}
> +
> +static int interface_flush_resources(QXLInstance *sin)
> +{
> +    fprintf(stderr, "%s: abort()\n", __FUNCTION__);
> +    abort();
> +    return 0;
> +}
> +
> +static const QXLInterface dpy_interface = {
> +    .base.type               = SPICE_INTERFACE_QXL,
> +    .base.description        = "qemu simple display",
> +    .base.major_version      = SPICE_INTERFACE_QXL_MAJOR,
> +    .base.minor_version      = SPICE_INTERFACE_QXL_MINOR,
> +
> +    .attache_worker          = interface_attach_worker,
> +    .set_compression_level   = interface_set_compression_level,
> +    .set_mm_time             = interface_set_mm_time,
> +    .get_init_info           = interface_get_init_info,
> +
> +    /* the callbacks below are called from spice server thread context */
> +    .get_command             = interface_get_command,
> +    .req_cmd_notification    = interface_req_cmd_notification,
> +    .release_resource        = interface_release_resource,
> +    .get_cursor_command      = interface_get_cursor_command,
> +    .req_cursor_notification = interface_req_cursor_notification,
> +    .notify_update           = interface_notify_update,
> +    .flush_resources         = interface_flush_resources,
> +};
> +
> +static SimpleSpiceDisplay sdpy;
> +
> +static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
> +{
> +    qemu_spice_display_update(&sdpy, x, y, w, h);
> +}
> +
> +static void display_resize(struct DisplayState *ds)
> +{
> +    qemu_spice_display_resize(&sdpy);
> +}
> +
> +static void display_refresh(struct DisplayState *ds)
> +{
> +    qemu_spice_display_refresh(&sdpy);
> +}
> +
> +static DisplayChangeListener display_listener = {
> +    .dpy_update  = display_update,
> +    .dpy_resize  = display_resize,
> +    .dpy_refresh = display_refresh,
> +};
> +
> +void qemu_spice_display_init(DisplayState *ds)
> +{
> +    assert(sdpy.ds == NULL);
> +    sdpy.ds = ds;
> +    sdpy.bufsize = (16 * 1024 * 1024);
> +    sdpy.buf = qemu_malloc(sdpy.bufsize);
> +    pthread_mutex_init(&sdpy.lock, NULL);
> +    register_displaychangelistener(ds,&display_listener);
> +
> +    sdpy.qxl.base.sif =&dpy_interface.base;
> +    spice_server_add_interface(spice_server,&sdpy.qxl.base);
> +    assert(sdpy.worker);
> +
> +    qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler,&sdpy);
> +    qemu_spice_create_host_memslot(&sdpy);
> +    qemu_spice_create_host_primary(&sdpy);
> +}
> diff --git a/ui/spice-display.h b/ui/spice-display.h
> new file mode 100644
> index 0000000..e17671c
> --- /dev/null
> +++ b/ui/spice-display.h
> @@ -0,0 +1,69 @@
> +/*
> + * Copyright (C) 2010 Red Hat, Inc.
> + *
> + * This program 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 2 or
> + * (at your option) version 3 of the License.
> + *
> + * This program 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 this program; if not, see<http://www.gnu.org/licenses/>.
> + */
> +
> +#include<spice/ipc_ring.h>
> +#include<spice/enums.h>
> +#include<spice/qxl_dev.h>
> +
> +#include "pflib.h"
> +
> +#define NUM_MEMSLOTS 8
> +#define MEMSLOT_GENERATION_BITS 8
> +#define MEMSLOT_SLOT_BITS 8
> +
> +#define MEMSLOT_GROUP_HOST  0
> +#define MEMSLOT_GROUP_GUEST 1
> +#define NUM_MEMSLOTS_GROUPS 2
> +
> +#define NUM_SURFACES 1024
> +
> +typedef struct SimpleSpiceDisplay {
> +    DisplayState *ds;
> +    void *buf;
> +    int bufsize;
> +    QXLWorker *worker;
> +    QXLInstance qxl;
> +    uint32_t unique;
> +    QemuPfConv *conv;
> +
> +    pthread_mutex_t lock;
> +    QXLRect dirty;
> +    int notify;
> +    int running;
> +} SimpleSpiceDisplay;
> +
> +typedef struct SimpleSpiceUpdate {
> +    QXLDrawable drawable;
> +    QXLImage image;
> +    QXLCommandExt ext;
> +    uint8_t *bitmap;
> +} SimpleSpiceUpdate;
> +
> +int qemu_spice_rect_is_empty(const QXLRect* r);
> +void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r);
> +
> +SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *sdpy);
> +void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update);
> +void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd);
> +void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd);
> +void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd);
> +void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason);
> +
> +void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
> +                               int x, int y, int w, int h);
> +void qemu_spice_display_resize(SimpleSpiceDisplay *ssd);
> +void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd);
> diff --git a/vl.c b/vl.c
> index a8c2a33..a5229ad 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -2951,6 +2951,11 @@ int main(int argc, char **argv, char **envp)
>               printf("VNC server running on `%s'\n", vnc_display_local_addr(ds));
>           }
>       }
> +#ifdef CONFIG_SPICE
> +    if (using_spice) {
> +        qemu_spice_display_init(ds);
> +    }
> +#endif
>
>       /* display setup */
>       dpy_resize(ds);
>    

  reply	other threads:[~2010-08-25 20:08 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-08-25 14:19 [Qemu-devel] [PATCH v3 00/10] initial spice support Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 01/10] Use display types for local display only Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 02/10] Use machine_init() to register virtfs config options Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 03/10] add pflib: PixelFormat conversion library Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 04/10] configure: add logging Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 05/10] add spice into the configure file Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 06/10] spice: core bits Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 07/10] spice: add keyboard Gerd Hoffmann
2010-08-25 19:53   ` Anthony Liguori
2010-08-26  6:55     ` Gerd Hoffmann
2010-08-26 12:55       ` Anthony Liguori
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 08/10] spice: add mouse Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 09/10] spice: simple display Gerd Hoffmann
2010-08-25 19:57   ` Anthony Liguori [this message]
2010-08-26  6:56     ` Gerd Hoffmann
2010-08-25 14:19 ` [Qemu-devel] [PATCH v3 10/10] spice: add tablet support Gerd Hoffmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4C75758C.80501@codemonkey.ws \
    --to=anthony@codemonkey.ws \
    --cc=kraxel@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.