aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/display/virtio-gpu-3d.c13
-rw-r--r--include/ui/console.h21
-rw-r--r--include/ui/gtk.h25
-rw-r--r--include/ui/sdl2.h13
-rw-r--r--ui/console.c30
-rw-r--r--ui/gtk-egl.c25
-rw-r--r--ui/gtk-gl-area.c12
-rw-r--r--ui/gtk.c12
-rw-r--r--ui/sdl.c10
-rw-r--r--ui/sdl2-gl.c28
-rw-r--r--ui/sdl2.c3
-rw-r--r--ui/spice-core.c6
-rw-r--r--ui/spice-display.c74
-rw-r--r--ui/vnc.c3
14 files changed, 178 insertions, 97 deletions
diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c
index ecb09d17a1..f49b7fe8cd 100644
--- a/hw/display/virtio-gpu-3d.c
+++ b/hw/display/virtio-gpu-3d.c
@@ -177,16 +177,15 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
qemu_console_resize(g->scanout[ss.scanout_id].con,
ss.r.width, ss.r.height);
virgl_renderer_force_ctx_0();
- dpy_gl_scanout(g->scanout[ss.scanout_id].con, info.tex_id,
- info.flags & 1 /* FIXME: Y_0_TOP */,
- info.width, info.height,
- ss.r.x, ss.r.y, ss.r.width, ss.r.height);
+ dpy_gl_scanout_texture(g->scanout[ss.scanout_id].con, info.tex_id,
+ info.flags & 1 /* FIXME: Y_0_TOP */,
+ info.width, info.height,
+ ss.r.x, ss.r.y, ss.r.width, ss.r.height);
} else {
if (ss.scanout_id != 0) {
dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL);
}
- dpy_gl_scanout(g->scanout[ss.scanout_id].con, 0, false,
- 0, 0, 0, 0, 0, 0);
+ dpy_gl_scanout_disable(g->scanout[ss.scanout_id].con);
}
g->scanout[ss.scanout_id].resource_id = ss.resource_id;
}
@@ -597,7 +596,7 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g)
if (i != 0) {
dpy_gfx_replace_surface(g->scanout[i].con, NULL);
}
- dpy_gl_scanout(g->scanout[i].con, 0, false, 0, 0, 0, 0, 0, 0);
+ dpy_gl_scanout_disable(g->scanout[i].con);
}
}
diff --git a/include/ui/console.h b/include/ui/console.h
index af6350e96f..ac2895ca93 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -215,10 +215,14 @@ typedef struct DisplayChangeListenerOps {
QEMUGLContext ctx);
QEMUGLContext (*dpy_gl_ctx_get_current)(DisplayChangeListener *dcl);
- void (*dpy_gl_scanout)(DisplayChangeListener *dcl,
- uint32_t backing_id, bool backing_y_0_top,
- uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y, uint32_t w, uint32_t h);
+ void (*dpy_gl_scanout_disable)(DisplayChangeListener *dcl);
+ void (*dpy_gl_scanout_texture)(DisplayChangeListener *dcl,
+ uint32_t backing_id,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h);
void (*dpy_gl_update)(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
@@ -284,10 +288,11 @@ bool dpy_cursor_define_supported(QemuConsole *con);
bool dpy_gfx_check_format(QemuConsole *con,
pixman_format_code_t format);
-void dpy_gl_scanout(QemuConsole *con,
- uint32_t backing_id, bool backing_y_0_top,
- uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y, uint32_t w, uint32_t h);
+void dpy_gl_scanout_disable(QemuConsole *con);
+void dpy_gl_scanout_texture(QemuConsole *con,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t backing_width, uint32_t backing_height,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void dpy_gl_update(QemuConsole *con,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 47ffddb5b4..ca9a2268de 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -103,11 +103,14 @@ void gd_egl_switch(DisplayChangeListener *dcl,
DisplaySurface *surface);
QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl,
QEMUGLParams *params);
-void gd_egl_scanout(DisplayChangeListener *dcl,
- uint32_t backing_id, bool backing_y_0_top,
- uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y,
- uint32_t w, uint32_t h);
+void gd_egl_scanout_disable(DisplayChangeListener *dcl);
+void gd_egl_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t backing_id,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h);
void gd_egl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void gtk_egl_init(void);
@@ -126,11 +129,13 @@ QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl,
QEMUGLParams *params);
void gd_gl_area_destroy_context(DisplayChangeListener *dcl,
QEMUGLContext ctx);
-void gd_gl_area_scanout(DisplayChangeListener *dcl,
- uint32_t backing_id, bool backing_y_0_top,
- uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y,
- uint32_t w, uint32_t h);
+void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t backing_id,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h);
void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void gtk_gl_area_init(void);
diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h
index 683bb6af2e..aaf226c2c0 100644
--- a/include/ui/sdl2.h
+++ b/include/ui/sdl2.h
@@ -62,11 +62,14 @@ int sdl2_gl_make_context_current(DisplayChangeListener *dcl,
QEMUGLContext ctx);
QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl);
-void sdl2_gl_scanout(DisplayChangeListener *dcl,
- uint32_t backing_id, bool backing_y_0_top,
- uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y,
- uint32_t w, uint32_t h);
+void sdl2_gl_scanout_disable(DisplayChangeListener *dcl);
+void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t backing_id,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h);
void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
diff --git a/ui/console.c b/ui/console.c
index 49d0740b40..d1ff7504ec 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1735,16 +1735,30 @@ QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con)
return con->gl->ops->dpy_gl_ctx_get_current(con->gl);
}
-void dpy_gl_scanout(QemuConsole *con,
- uint32_t backing_id, bool backing_y_0_top,
- uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y, uint32_t width, uint32_t height)
+void dpy_gl_scanout_disable(QemuConsole *con)
{
assert(con->gl);
- con->gl->ops->dpy_gl_scanout(con->gl, backing_id,
- backing_y_0_top,
- backing_width, backing_height,
- x, y, width, height);
+ if (con->gl->ops->dpy_gl_scanout_disable) {
+ con->gl->ops->dpy_gl_scanout_disable(con->gl);
+ } else {
+ con->gl->ops->dpy_gl_scanout_texture(con->gl, 0, false, 0, 0,
+ 0, 0, 0, 0);
+ }
+}
+
+void dpy_gl_scanout_texture(QemuConsole *con,
+ uint32_t backing_id,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height)
+{
+ assert(con->gl);
+ con->gl->ops->dpy_gl_scanout_texture(con->gl, backing_id,
+ backing_y_0_top,
+ backing_width, backing_height,
+ x, y, width, height);
}
void dpy_gl_update(QemuConsole *con,
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 3f5d328c7b..d53288f027 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -170,11 +170,21 @@ QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl,
return qemu_egl_create_context(dcl, params);
}
-void gd_egl_scanout(DisplayChangeListener *dcl,
- uint32_t backing_id, bool backing_y_0_top,
- uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+void gd_egl_scanout_disable(DisplayChangeListener *dcl)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ vc->gfx.w = 0;
+ vc->gfx.h = 0;
+ vc->gfx.tex_id = 0;
+ gtk_egl_set_scanout_mode(vc, false);
+}
+
+void gd_egl_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t backing_width, uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
@@ -188,11 +198,6 @@ void gd_egl_scanout(DisplayChangeListener *dcl,
eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
vc->gfx.esurface, vc->gfx.ectx);
- if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) {
- gtk_egl_set_scanout_mode(vc, false);
- return;
- }
-
gtk_egl_set_scanout_mode(vc, true);
if (!vc->gfx.fbo_id) {
glGenFramebuffers(1, &vc->gfx.fbo_id);
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index 0df5a36a9f..b05c665cbb 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -167,11 +167,13 @@ void gd_gl_area_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
/* FIXME */
}
-void gd_gl_area_scanout(DisplayChangeListener *dcl,
- uint32_t backing_id, bool backing_y_0_top,
- uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t backing_id,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
diff --git a/ui/gtk.c b/ui/gtk.c
index f21e9e7f7b..a86848f3b0 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -669,7 +669,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = {
.dpy_gl_ctx_destroy = gd_gl_area_destroy_context,
.dpy_gl_ctx_make_current = gd_gl_area_make_current,
.dpy_gl_ctx_get_current = gd_gl_area_get_current_context,
- .dpy_gl_scanout = gd_gl_area_scanout,
+ .dpy_gl_scanout_texture = gd_gl_area_scanout_texture,
.dpy_gl_update = gd_gl_area_scanout_flush,
};
@@ -688,7 +688,8 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_gl_ctx_destroy = qemu_egl_destroy_context,
.dpy_gl_ctx_make_current = gd_egl_make_current,
.dpy_gl_ctx_get_current = qemu_egl_get_current_context,
- .dpy_gl_scanout = gd_egl_scanout,
+ .dpy_gl_scanout_disable = gd_egl_scanout_disable,
+ .dpy_gl_scanout_texture = gd_egl_scanout_texture,
.dpy_gl_update = gd_egl_scanout_flush,
};
@@ -2200,11 +2201,12 @@ static void gd_set_keycode_type(GtkDisplayState *s)
GdkDisplay *display = gtk_widget_get_display(s->window);
if (GDK_IS_X11_DISPLAY(display)) {
Display *x11_display = gdk_x11_display_get_xdisplay(display);
- XkbDescPtr desc = XkbGetKeyboard(x11_display, XkbGBN_AllComponentsMask,
- XkbUseCoreKbd);
+ XkbDescPtr desc = XkbGetMap(x11_display, XkbGBN_AllComponentsMask,
+ XkbUseCoreKbd);
char *keycodes = NULL;
- if (desc && desc->names) {
+ if (desc &&
+ (XkbGetNames(x11_display, XkbKeycodesNameMask, desc) == Success)) {
keycodes = XGetAtomName(x11_display, desc->names->keycodes);
}
if (keycodes == NULL) {
diff --git a/ui/sdl.c b/ui/sdl.c
index 19e8a848a7..37c21a00fb 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -233,10 +233,12 @@ static int check_for_evdev(void)
if (!SDL_GetWMInfo(&info)) {
return 0;
}
- desc = XkbGetKeyboard(info.info.x11.display,
- XkbGBN_AllComponentsMask,
- XkbUseCoreKbd);
- if (desc && desc->names) {
+ desc = XkbGetMap(info.info.x11.display,
+ XkbGBN_AllComponentsMask,
+ XkbUseCoreKbd);
+ if (desc &&
+ (XkbGetNames(info.info.x11.display,
+ XkbKeycodesNameMask, desc) == Success)) {
keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes);
if (keycodes == NULL) {
fprintf(stderr, "could not lookup keycode name\n");
diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c
index 039645df3e..1cd77e2c16 100644
--- a/ui/sdl2-gl.c
+++ b/ui/sdl2-gl.c
@@ -184,11 +184,24 @@ QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl)
return (QEMUGLContext)sdlctx;
}
-void sdl2_gl_scanout(DisplayChangeListener *dcl,
- uint32_t backing_id, bool backing_y_0_top,
- uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+void sdl2_gl_scanout_disable(DisplayChangeListener *dcl)
+{
+ struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+
+ assert(scon->opengl);
+ scon->w = 0;
+ scon->h = 0;
+ scon->tex_id = 0;
+ sdl2_set_scanout_mode(scon, false);
+}
+
+void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t backing_id,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
{
struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
@@ -202,11 +215,6 @@ void sdl2_gl_scanout(DisplayChangeListener *dcl,
SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
- if (scon->tex_id == 0 || scon->w == 0 || scon->h == 0) {
- sdl2_set_scanout_mode(scon, false);
- return;
- }
-
sdl2_set_scanout_mode(scon, true);
if (!scon->fbo_id) {
glGenFramebuffers(1, &scon->fbo_id);
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 91fb111aa5..faf9bdff5c 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -733,7 +733,8 @@ static const DisplayChangeListenerOps dcl_gl_ops = {
.dpy_gl_ctx_destroy = sdl2_gl_destroy_context,
.dpy_gl_ctx_make_current = sdl2_gl_make_context_current,
.dpy_gl_ctx_get_current = sdl2_gl_get_current_context,
- .dpy_gl_scanout = sdl2_gl_scanout,
+ .dpy_gl_scanout_disable = sdl2_gl_scanout_disable,
+ .dpy_gl_scanout_texture = sdl2_gl_scanout_texture,
.dpy_gl_update = sdl2_gl_scanout_flush,
};
#endif
diff --git a/ui/spice-core.c b/ui/spice-core.c
index 39ccab7561..804abc5c0f 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -497,6 +497,12 @@ static QemuOptsList qemu_spice_opts = {
},{
.name = "seamless-migration",
.type = QEMU_OPT_BOOL,
+ },{
+ .name = "display",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "head",
+ .type = QEMU_OPT_NUMBER,
#ifdef HAVE_SPICE_GL
},{
.name = "gl",
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 64e472eeb0..b353445f58 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -928,39 +928,44 @@ static QEMUGLContext qemu_spice_gl_create_context(DisplayChangeListener *dcl,
return qemu_egl_create_context(dcl, params);
}
-static void qemu_spice_gl_scanout(DisplayChangeListener *dcl,
- uint32_t tex_id,
- bool y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
- uint32_t x, uint32_t y,
- uint32_t w, uint32_t h)
+static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl)
+{
+ SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+
+ dprint(1, "%s: no framebuffer\n", __func__);
+ spice_qxl_gl_scanout(&ssd->qxl, -1, 0, 0, 0, 0, false);
+ qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0);
+ ssd->have_surface = false;
+ ssd->have_scanout = false;
+}
+
+static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t tex_id,
+ bool y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
{
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
EGLint stride = 0, fourcc = 0;
int fd = -1;
- if (tex_id) {
- fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc);
- if (fd < 0) {
- fprintf(stderr, "%s: failed to get fd for texture\n", __func__);
- return;
- }
- dprint(1, "%s: %dx%d (stride %d, fourcc 0x%x)\n", __func__,
- w, h, stride, fourcc);
- } else {
- dprint(1, "%s: no texture (no framebuffer)\n", __func__);
+ assert(tex_id);
+ fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc);
+ if (fd < 0) {
+ fprintf(stderr, "%s: failed to get fd for texture\n", __func__);
+ return;
}
-
- assert(!tex_id || fd >= 0);
+ dprint(1, "%s: %dx%d (stride %d, fourcc 0x%x)\n", __func__,
+ w, h, stride, fourcc);
/* note: spice server will close the fd */
spice_qxl_gl_scanout(&ssd->qxl, fd, backing_width, backing_height,
stride, fourcc, y_0_top);
- ssd->have_surface = false;
- ssd->have_scanout = (tex_id != 0);
-
qemu_spice_gl_monitor_config(ssd, x, y, w, h);
+ ssd->have_surface = false;
+ ssd->have_scanout = true;
}
static void qemu_spice_gl_update(DisplayChangeListener *dcl,
@@ -993,7 +998,8 @@ static const DisplayChangeListenerOps display_listener_gl_ops = {
.dpy_gl_ctx_make_current = qemu_egl_make_context_current,
.dpy_gl_ctx_get_current = qemu_egl_get_current_context,
- .dpy_gl_scanout = qemu_spice_gl_scanout,
+ .dpy_gl_scanout_disable = qemu_spice_gl_scanout_disable,
+ .dpy_gl_scanout_texture = qemu_spice_gl_scanout_texture,
.dpy_gl_update = qemu_spice_gl_update,
};
@@ -1029,9 +1035,26 @@ static void qemu_spice_display_init_one(QemuConsole *con)
void qemu_spice_display_init(void)
{
- QemuConsole *con;
+ QemuOptsList *olist = qemu_find_opts("spice");
+ QemuOpts *opts = QTAILQ_FIRST(&olist->head);
+ QemuConsole *spice_con, *con;
+ const char *str;
int i;
+ str = qemu_opt_get(opts, "display");
+ if (str) {
+ int head = qemu_opt_get_number(opts, "head", 0);
+ Error *err = NULL;
+
+ spice_con = qemu_console_lookup_by_device_name(str, head, &err);
+ if (err) {
+ error_report("Failed to lookup display/head");
+ exit(1);
+ }
+ } else {
+ spice_con = NULL;
+ }
+
for (i = 0;; i++) {
con = qemu_console_lookup_by_index(i);
if (!con || !qemu_console_is_graphic(con)) {
@@ -1040,6 +1063,9 @@ void qemu_spice_display_init(void)
if (qemu_spice_have_display_interface(con)) {
continue;
}
+ if (spice_con != NULL && spice_con != con) {
+ continue;
+ }
qemu_spice_display_init_one(con);
}
}
diff --git a/ui/vnc.c b/ui/vnc.c
index 62e85edf5d..51f4b30959 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3181,6 +3181,7 @@ static void vnc_display_close(VncDisplay *vd)
g_free(vd->lsock);
g_free(vd->lsock_tag);
vd->lsock = NULL;
+ vd->lsock_tag = NULL;
vd->nlsock = 0;
for (i = 0; i < vd->nlwebsock; i++) {
@@ -3192,6 +3193,7 @@ static void vnc_display_close(VncDisplay *vd)
g_free(vd->lwebsock);
g_free(vd->lwebsock_tag);
vd->lwebsock = NULL;
+ vd->lwebsock_tag = NULL;
vd->nlwebsock = 0;
vd->auth = VNC_AUTH_INVALID;
@@ -3204,6 +3206,7 @@ static void vnc_display_close(VncDisplay *vd)
vd->tlsaclname = NULL;
if (vd->lock_key_sync) {
qemu_remove_led_event_handler(vd->led);
+ vd->led = NULL;
}
}