console: simplify screendump
Screendumps are alot simpler as we can update non-active QemuConsoles now. So we only need to update the QemuConsole we want write out, then dump the DisplaySurface content into a ppm file. Done. No console switching needed. No special support code in the gfx card emulation needed. Zap it all. Also move ppm_save out of the vga code and next to the qmp_screendump function. For now screen dumping is limited to console #0 (like it used to be), even though it is dead simple to extend it to other consoles. I wanna finish the console cleanup before setting new qapi interfaces into stone. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Tested-by: Igor Mitsyanko <i.mitsyanko@gmail.com>
This commit is contained in:
parent
321f048d24
commit
2c62f08ddb
@ -612,7 +612,7 @@ static int musicpal_lcd_init(SysBusDevice *dev)
|
|||||||
sysbus_init_mmio(dev, &s->iomem);
|
sysbus_init_mmio(dev, &s->iomem);
|
||||||
|
|
||||||
s->con = graphic_console_init(lcd_refresh, lcd_invalidate,
|
s->con = graphic_console_init(lcd_refresh, lcd_invalidate,
|
||||||
NULL, NULL, s);
|
NULL, s);
|
||||||
qemu_console_resize(s->con, 128*3, 64*3);
|
qemu_console_resize(s->con, 128*3, 64*3);
|
||||||
|
|
||||||
qdev_init_gpio_in(&dev->qdev, musicpal_lcd_gpio_brigthness_in, 3);
|
qdev_init_gpio_in(&dev->qdev, musicpal_lcd_gpio_brigthness_in, 3);
|
||||||
|
@ -933,18 +933,6 @@ static void blizzard_update_display(void *opaque)
|
|||||||
s->my[1] = 0;
|
s->my[1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blizzard_screen_dump(void *opaque, const char *filename,
|
|
||||||
bool cswitch, Error **errp)
|
|
||||||
{
|
|
||||||
BlizzardState *s = (BlizzardState *) opaque;
|
|
||||||
DisplaySurface *surface = qemu_console_surface(s->con);
|
|
||||||
|
|
||||||
blizzard_update_display(opaque);
|
|
||||||
if (s && surface_data(surface)) {
|
|
||||||
ppm_save(filename, surface, errp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DEPTH 8
|
#define DEPTH 8
|
||||||
#include "blizzard_template.h"
|
#include "blizzard_template.h"
|
||||||
#define DEPTH 15
|
#define DEPTH 15
|
||||||
@ -965,7 +953,7 @@ void *s1d13745_init(qemu_irq gpio_int)
|
|||||||
|
|
||||||
s->con = graphic_console_init(blizzard_update_display,
|
s->con = graphic_console_init(blizzard_update_display,
|
||||||
blizzard_invalidate_display,
|
blizzard_invalidate_display,
|
||||||
blizzard_screen_dump, NULL, s);
|
NULL, s);
|
||||||
surface = qemu_console_surface(s->con);
|
surface = qemu_console_surface(s->con);
|
||||||
|
|
||||||
switch (surface_bits_per_pixel(surface)) {
|
switch (surface_bits_per_pixel(surface)) {
|
||||||
|
@ -2911,7 +2911,7 @@ static int vga_initfn(ISADevice *dev)
|
|||||||
cirrus_init_common(&d->cirrus_vga, CIRRUS_ID_CLGD5430, 0,
|
cirrus_init_common(&d->cirrus_vga, CIRRUS_ID_CLGD5430, 0,
|
||||||
isa_address_space(dev), isa_address_space_io(dev));
|
isa_address_space(dev), isa_address_space_io(dev));
|
||||||
s->con = graphic_console_init(s->update, s->invalidate,
|
s->con = graphic_console_init(s->update, s->invalidate,
|
||||||
s->screen_dump, s->text_update,
|
s->text_update,
|
||||||
s);
|
s);
|
||||||
rom_add_vga(VGABIOS_CIRRUS_FILENAME);
|
rom_add_vga(VGABIOS_CIRRUS_FILENAME);
|
||||||
/* XXX ISA-LFB support */
|
/* XXX ISA-LFB support */
|
||||||
@ -2960,7 +2960,7 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev)
|
|||||||
cirrus_init_common(s, device_id, 1, pci_address_space(dev),
|
cirrus_init_common(s, device_id, 1, pci_address_space(dev),
|
||||||
pci_address_space_io(dev));
|
pci_address_space_io(dev));
|
||||||
s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
|
s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
|
||||||
s->vga.screen_dump, s->vga.text_update,
|
s->vga.text_update,
|
||||||
&s->vga);
|
&s->vga);
|
||||||
|
|
||||||
/* setup PCI */
|
/* setup PCI */
|
||||||
|
@ -1901,7 +1901,7 @@ static int exynos4210_fimd_init(SysBusDevice *dev)
|
|||||||
"exynos4210.fimd", FIMD_REGS_SIZE);
|
"exynos4210.fimd", FIMD_REGS_SIZE);
|
||||||
sysbus_init_mmio(dev, &s->iomem);
|
sysbus_init_mmio(dev, &s->iomem);
|
||||||
s->console = graphic_console_init(exynos4210_fimd_update,
|
s->console = graphic_console_init(exynos4210_fimd_update,
|
||||||
exynos4210_fimd_invalidate, NULL, NULL, s);
|
exynos4210_fimd_invalidate, NULL, s);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -294,77 +294,6 @@ static void g364fb_reset(G364State *s)
|
|||||||
g364fb_invalidate_display(s);
|
g364fb_invalidate_display(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void g364fb_screen_dump(void *opaque, const char *filename, bool cswitch,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
G364State *s = opaque;
|
|
||||||
int ret, y, x;
|
|
||||||
uint8_t index;
|
|
||||||
uint8_t *data_buffer;
|
|
||||||
FILE *f;
|
|
||||||
|
|
||||||
qemu_flush_coalesced_mmio_buffer();
|
|
||||||
|
|
||||||
if (s->depth != 8) {
|
|
||||||
error_setg(errp, "g364: unknown guest depth %d", s->depth);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
f = fopen(filename, "wb");
|
|
||||||
if (!f) {
|
|
||||||
error_setg(errp, "failed to open file '%s': %s", filename,
|
|
||||||
strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s->ctla & CTLA_FORCE_BLANK) {
|
|
||||||
/* blank screen */
|
|
||||||
ret = fprintf(f, "P4\n%d %d\n", s->width, s->height);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
for (y = 0; y < s->height; y++)
|
|
||||||
for (x = 0; x < s->width; x++) {
|
|
||||||
ret = fputc(0, f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data_buffer = s->vram + s->top_of_screen;
|
|
||||||
ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
for (y = 0; y < s->height; y++)
|
|
||||||
for (x = 0; x < s->width; x++, data_buffer++) {
|
|
||||||
index = *data_buffer;
|
|
||||||
ret = fputc(s->color_palette[index][0], f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc(s->color_palette[index][1], f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc(s->color_palette[index][2], f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
fclose(f);
|
|
||||||
return;
|
|
||||||
|
|
||||||
write_err:
|
|
||||||
error_setg(errp, "failed to write to file '%s': %s", filename,
|
|
||||||
strerror(errno));
|
|
||||||
unlink(filename);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* called for accesses to io ports */
|
/* called for accesses to io ports */
|
||||||
static uint64_t g364fb_ctrl_read(void *opaque,
|
static uint64_t g364fb_ctrl_read(void *opaque,
|
||||||
hwaddr addr,
|
hwaddr addr,
|
||||||
@ -552,7 +481,7 @@ static void g364fb_init(DeviceState *dev, G364State *s)
|
|||||||
|
|
||||||
s->con = graphic_console_init(g364fb_update_display,
|
s->con = graphic_console_init(g364fb_update_display,
|
||||||
g364fb_invalidate_display,
|
g364fb_invalidate_display,
|
||||||
g364fb_screen_dump, NULL, s);
|
NULL, s);
|
||||||
|
|
||||||
memory_region_init_io(&s->mem_ctrl, &g364fb_ctrl_ops, s, "ctrl", 0x180000);
|
memory_region_init_io(&s->mem_ctrl, &g364fb_ctrl_ops, s, "ctrl", 0x180000);
|
||||||
memory_region_init_ram_ptr(&s->mem_vram, "vram",
|
memory_region_init_ram_ptr(&s->mem_vram, "vram",
|
||||||
|
@ -263,7 +263,6 @@ static int jazz_led_init(SysBusDevice *dev)
|
|||||||
|
|
||||||
s->con = graphic_console_init(jazz_led_update_display,
|
s->con = graphic_console_init(jazz_led_update_display,
|
||||||
jazz_led_invalidate_display,
|
jazz_led_invalidate_display,
|
||||||
NULL,
|
|
||||||
jazz_led_text_update, s);
|
jazz_led_text_update, s);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -280,7 +280,7 @@ static int milkymist_vgafb_init(SysBusDevice *dev)
|
|||||||
|
|
||||||
s->con = graphic_console_init(vgafb_update_display,
|
s->con = graphic_console_init(vgafb_update_display,
|
||||||
vgafb_invalidate_display,
|
vgafb_invalidate_display,
|
||||||
NULL, NULL, s);
|
NULL, s);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -227,90 +227,6 @@ static void omap_update_display(void *opaque)
|
|||||||
omap_lcd->invalidate = 0;
|
omap_lcd->invalidate = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void omap_ppm_save(const char *filename, uint8_t *data,
|
|
||||||
int w, int h, int linesize, Error **errp)
|
|
||||||
{
|
|
||||||
FILE *f;
|
|
||||||
uint8_t *d, *d1;
|
|
||||||
unsigned int v;
|
|
||||||
int ret, y, x, bpp;
|
|
||||||
|
|
||||||
f = fopen(filename, "wb");
|
|
||||||
if (!f) {
|
|
||||||
error_setg(errp, "failed to open file '%s': %s", filename,
|
|
||||||
strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ret = fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
d1 = data;
|
|
||||||
bpp = linesize / w;
|
|
||||||
for (y = 0; y < h; y ++) {
|
|
||||||
d = d1;
|
|
||||||
for (x = 0; x < w; x ++) {
|
|
||||||
v = *(uint32_t *) d;
|
|
||||||
switch (bpp) {
|
|
||||||
case 2:
|
|
||||||
ret = fputc((v >> 8) & 0xf8, f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc((v >> 3) & 0xfc, f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc((v << 3) & 0xf8, f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
default:
|
|
||||||
ret = fputc((v >> 16) & 0xff, f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc((v >> 8) & 0xff, f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc((v) & 0xff, f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
d += bpp;
|
|
||||||
}
|
|
||||||
d1 += linesize;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
fclose(f);
|
|
||||||
return;
|
|
||||||
|
|
||||||
write_err:
|
|
||||||
error_setg(errp, "failed to write to file '%s': %s", filename,
|
|
||||||
strerror(errno));
|
|
||||||
unlink(filename);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void omap_screen_dump(void *opaque, const char *filename, bool cswitch,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
struct omap_lcd_panel_s *omap_lcd = opaque;
|
|
||||||
DisplaySurface *surface = qemu_console_surface(omap_lcd->con);
|
|
||||||
|
|
||||||
omap_update_display(opaque);
|
|
||||||
if (omap_lcd && surface_data(surface))
|
|
||||||
omap_ppm_save(filename, surface_data(surface),
|
|
||||||
omap_lcd->width, omap_lcd->height,
|
|
||||||
surface_stride(surface), errp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void omap_invalidate_display(void *opaque) {
|
static void omap_invalidate_display(void *opaque) {
|
||||||
struct omap_lcd_panel_s *omap_lcd = opaque;
|
struct omap_lcd_panel_s *omap_lcd = opaque;
|
||||||
omap_lcd->invalidate = 1;
|
omap_lcd->invalidate = 1;
|
||||||
@ -487,7 +403,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
|
|||||||
|
|
||||||
s->con = graphic_console_init(omap_update_display,
|
s->con = graphic_console_init(omap_update_display,
|
||||||
omap_invalidate_display,
|
omap_invalidate_display,
|
||||||
omap_screen_dump, NULL, s);
|
NULL, s);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -454,7 +454,7 @@ static int pl110_init(SysBusDevice *dev)
|
|||||||
qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1);
|
qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1);
|
||||||
s->con = graphic_console_init(pl110_update_display,
|
s->con = graphic_console_init(pl110_update_display,
|
||||||
pl110_invalidate_display,
|
pl110_invalidate_display,
|
||||||
NULL, NULL, s);
|
NULL, s);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1010,7 +1010,7 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem,
|
|||||||
|
|
||||||
s->con = graphic_console_init(pxa2xx_update_display,
|
s->con = graphic_console_init(pxa2xx_update_display,
|
||||||
pxa2xx_invalidate_display,
|
pxa2xx_invalidate_display,
|
||||||
NULL, NULL, s);
|
NULL, s);
|
||||||
surface = qemu_console_surface(s->con);
|
surface = qemu_console_surface(s->con);
|
||||||
|
|
||||||
switch (surface_bits_per_pixel(surface)) {
|
switch (surface_bits_per_pixel(surface)) {
|
||||||
|
@ -1772,26 +1772,6 @@ static void qxl_hw_invalidate(void *opaque)
|
|||||||
vga->invalidate(vga);
|
vga->invalidate(vga);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
PCIQXLDevice *qxl = opaque;
|
|
||||||
VGACommonState *vga = &qxl->vga;
|
|
||||||
|
|
||||||
switch (qxl->mode) {
|
|
||||||
case QXL_MODE_COMPAT:
|
|
||||||
case QXL_MODE_NATIVE:
|
|
||||||
qxl_render_update(qxl);
|
|
||||||
ppm_save(filename, qxl->ssd.ds, errp);
|
|
||||||
break;
|
|
||||||
case QXL_MODE_VGA:
|
|
||||||
vga->screen_dump(vga, filename, cswitch, errp);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
|
static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
|
||||||
{
|
{
|
||||||
PCIQXLDevice *qxl = opaque;
|
PCIQXLDevice *qxl = opaque;
|
||||||
@ -2075,7 +2055,7 @@ static int qxl_init_primary(PCIDevice *dev)
|
|||||||
portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0);
|
portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0);
|
||||||
|
|
||||||
vga->con = graphic_console_init(qxl_hw_update, qxl_hw_invalidate,
|
vga->con = graphic_console_init(qxl_hw_update, qxl_hw_invalidate,
|
||||||
qxl_hw_screen_dump, qxl_hw_text_update,
|
qxl_hw_text_update,
|
||||||
qxl);
|
qxl);
|
||||||
qxl->ssd.con = vga->con,
|
qxl->ssd.con = vga->con,
|
||||||
qemu_spice_display_init_common(&qxl->ssd);
|
qemu_spice_display_init_common(&qxl->ssd);
|
||||||
|
@ -1446,5 +1446,5 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base,
|
|||||||
|
|
||||||
/* create qemu graphic console */
|
/* create qemu graphic console */
|
||||||
s->con = graphic_console_init(sm501_update_display, NULL,
|
s->con = graphic_console_init(sm501_update_display, NULL,
|
||||||
NULL, NULL, s);
|
NULL, s);
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ static int ssd0303_init(I2CSlave *i2c)
|
|||||||
|
|
||||||
s->con = graphic_console_init(ssd0303_update_display,
|
s->con = graphic_console_init(ssd0303_update_display,
|
||||||
ssd0303_invalidate_display,
|
ssd0303_invalidate_display,
|
||||||
NULL, NULL, s);
|
NULL, s);
|
||||||
qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY);
|
qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -339,7 +339,7 @@ static int ssd0323_init(SSISlave *dev)
|
|||||||
s->row_end = 79;
|
s->row_end = 79;
|
||||||
s->con = graphic_console_init(ssd0323_update_display,
|
s->con = graphic_console_init(ssd0323_update_display,
|
||||||
ssd0323_invalidate_display,
|
ssd0323_invalidate_display,
|
||||||
NULL, NULL, s);
|
NULL, s);
|
||||||
qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY);
|
qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY);
|
||||||
|
|
||||||
qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1);
|
qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1);
|
||||||
|
@ -585,7 +585,6 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq)
|
|||||||
s->scr_height = 640;
|
s->scr_height = 640;
|
||||||
s->con = graphic_console_init(tc6393xb_update_display,
|
s->con = graphic_console_init(tc6393xb_update_display,
|
||||||
NULL, /* invalidate */
|
NULL, /* invalidate */
|
||||||
NULL, /* screen_dump */
|
|
||||||
NULL, /* text_update */
|
NULL, /* text_update */
|
||||||
s);
|
s);
|
||||||
|
|
||||||
|
129
hw/display/tcx.c
129
hw/display/tcx.c
@ -56,11 +56,6 @@ typedef struct TCXState {
|
|||||||
uint8_t dac_index, dac_state;
|
uint8_t dac_index, dac_state;
|
||||||
} TCXState;
|
} TCXState;
|
||||||
|
|
||||||
static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch,
|
|
||||||
Error **errp);
|
|
||||||
static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch,
|
|
||||||
Error **errp);
|
|
||||||
|
|
||||||
static void tcx_set_dirty(TCXState *s)
|
static void tcx_set_dirty(TCXState *s)
|
||||||
{
|
{
|
||||||
memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY);
|
memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY);
|
||||||
@ -569,7 +564,7 @@ static int tcx_init1(SysBusDevice *dev)
|
|||||||
|
|
||||||
s->con = graphic_console_init(tcx24_update_display,
|
s->con = graphic_console_init(tcx24_update_display,
|
||||||
tcx24_invalidate_display,
|
tcx24_invalidate_display,
|
||||||
tcx24_screen_dump, NULL, s);
|
NULL, s);
|
||||||
} else {
|
} else {
|
||||||
/* THC 8 bit (dummy) */
|
/* THC 8 bit (dummy) */
|
||||||
memory_region_init_io(&s->thc8, &dummy_ops, s, "tcx.thc8",
|
memory_region_init_io(&s->thc8, &dummy_ops, s, "tcx.thc8",
|
||||||
@ -578,133 +573,13 @@ static int tcx_init1(SysBusDevice *dev)
|
|||||||
|
|
||||||
s->con = graphic_console_init(tcx_update_display,
|
s->con = graphic_console_init(tcx_update_display,
|
||||||
tcx_invalidate_display,
|
tcx_invalidate_display,
|
||||||
tcx_screen_dump, NULL, s);
|
NULL, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_console_resize(s->con, s->width, s->height);
|
qemu_console_resize(s->con, s->width, s->height);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
TCXState *s = opaque;
|
|
||||||
FILE *f;
|
|
||||||
uint8_t *d, *d1, v;
|
|
||||||
int ret, y, x;
|
|
||||||
|
|
||||||
f = fopen(filename, "wb");
|
|
||||||
if (!f) {
|
|
||||||
error_setg(errp, "failed to open file '%s': %s", filename,
|
|
||||||
strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
d1 = s->vram;
|
|
||||||
for(y = 0; y < s->height; y++) {
|
|
||||||
d = d1;
|
|
||||||
for(x = 0; x < s->width; x++) {
|
|
||||||
v = *d;
|
|
||||||
ret = fputc(s->r[v], f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc(s->g[v], f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc(s->b[v], f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
d++;
|
|
||||||
}
|
|
||||||
d1 += MAXX;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
fclose(f);
|
|
||||||
return;
|
|
||||||
|
|
||||||
write_err:
|
|
||||||
error_setg(errp, "failed to write to file '%s': %s", filename,
|
|
||||||
strerror(errno));
|
|
||||||
unlink(filename);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
TCXState *s = opaque;
|
|
||||||
FILE *f;
|
|
||||||
uint8_t *d, *d1, v;
|
|
||||||
uint32_t *s24, *cptr, dval;
|
|
||||||
int ret, y, x;
|
|
||||||
|
|
||||||
f = fopen(filename, "wb");
|
|
||||||
if (!f) {
|
|
||||||
error_setg(errp, "failed to open file '%s': %s", filename,
|
|
||||||
strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
d1 = s->vram;
|
|
||||||
s24 = s->vram24;
|
|
||||||
cptr = s->cplane;
|
|
||||||
for(y = 0; y < s->height; y++) {
|
|
||||||
d = d1;
|
|
||||||
for(x = 0; x < s->width; x++, d++, s24++) {
|
|
||||||
if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
|
|
||||||
dval = *s24 & 0x00ffffff;
|
|
||||||
ret = fputc((dval >> 16) & 0xff, f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc((dval >> 8) & 0xff, f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc(dval & 0xff, f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
v = *d;
|
|
||||||
ret = fputc(s->r[v], f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc(s->g[v], f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
ret = fputc(s->b[v], f);
|
|
||||||
if (ret == EOF) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d1 += MAXX;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
fclose(f);
|
|
||||||
return;
|
|
||||||
|
|
||||||
write_err:
|
|
||||||
error_setg(errp, "failed to write to file '%s': %s", filename,
|
|
||||||
strerror(errno));
|
|
||||||
unlink(filename);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Property tcx_properties[] = {
|
static Property tcx_properties[] = {
|
||||||
DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
|
DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
|
||||||
DEFINE_PROP_UINT16("width", TCXState, width, -1),
|
DEFINE_PROP_UINT16("width", TCXState, width, -1),
|
||||||
|
@ -136,7 +136,7 @@ int isa_vga_mm_init(hwaddr vram_base,
|
|||||||
vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space);
|
vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space);
|
||||||
|
|
||||||
s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
|
s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
|
||||||
s->vga.screen_dump, s->vga.text_update,
|
s->vga.text_update,
|
||||||
s);
|
s);
|
||||||
|
|
||||||
vga_init_vbe(&s->vga, address_space);
|
vga_init_vbe(&s->vga, address_space);
|
||||||
|
@ -63,7 +63,7 @@ static int vga_initfn(ISADevice *dev)
|
|||||||
vga_io_memory, 1);
|
vga_io_memory, 1);
|
||||||
memory_region_set_coalescing(vga_io_memory);
|
memory_region_set_coalescing(vga_io_memory);
|
||||||
s->con = graphic_console_init(s->update, s->invalidate,
|
s->con = graphic_console_init(s->update, s->invalidate,
|
||||||
s->screen_dump, s->text_update, s);
|
s->text_update, s);
|
||||||
|
|
||||||
vga_init_vbe(s, isa_address_space(dev));
|
vga_init_vbe(s, isa_address_space(dev));
|
||||||
/* ROM BIOS */
|
/* ROM BIOS */
|
||||||
|
@ -151,7 +151,7 @@ static int pci_std_vga_initfn(PCIDevice *dev)
|
|||||||
vga_init(s, pci_address_space(dev), pci_address_space_io(dev), true);
|
vga_init(s, pci_address_space(dev), pci_address_space_io(dev), true);
|
||||||
|
|
||||||
s->con = graphic_console_init(s->update, s->invalidate,
|
s->con = graphic_console_init(s->update, s->invalidate,
|
||||||
s->screen_dump, s->text_update, s);
|
s->text_update, s);
|
||||||
|
|
||||||
/* XXX: VGA_RAM_SIZE must be a power of two */
|
/* XXX: VGA_RAM_SIZE must be a power of two */
|
||||||
pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
|
pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
|
||||||
|
@ -166,9 +166,6 @@ static uint32_t expand4[256];
|
|||||||
static uint16_t expand2[256];
|
static uint16_t expand2[256];
|
||||||
static uint8_t expand4to8[16];
|
static uint8_t expand4to8[16];
|
||||||
|
|
||||||
static void vga_screen_dump(void *opaque, const char *filename, bool cswitch,
|
|
||||||
Error **errp);
|
|
||||||
|
|
||||||
static void vga_update_memory_access(VGACommonState *s)
|
static void vga_update_memory_access(VGACommonState *s)
|
||||||
{
|
{
|
||||||
MemoryRegion *region, *old_region = s->chain4_alias;
|
MemoryRegion *region, *old_region = s->chain4_alias;
|
||||||
@ -2298,7 +2295,6 @@ void vga_common_init(VGACommonState *s)
|
|||||||
s->get_resolution = vga_get_resolution;
|
s->get_resolution = vga_get_resolution;
|
||||||
s->update = vga_update_display;
|
s->update = vga_update_display;
|
||||||
s->invalidate = vga_invalidate_display;
|
s->invalidate = vga_invalidate_display;
|
||||||
s->screen_dump = vga_screen_dump;
|
|
||||||
s->text_update = vga_update_text;
|
s->text_update = vga_update_text;
|
||||||
switch (vga_retrace_method) {
|
switch (vga_retrace_method) {
|
||||||
case VGA_RETRACE_DUMB:
|
case VGA_RETRACE_DUMB:
|
||||||
@ -2393,65 +2389,3 @@ void vga_init_vbe(VGACommonState *s, MemoryRegion *system_memory)
|
|||||||
&s->vram_vbe);
|
&s->vram_vbe);
|
||||||
s->vbe_mapped = 1;
|
s->vbe_mapped = 1;
|
||||||
}
|
}
|
||||||
/********************************************************/
|
|
||||||
/* vga screen dump */
|
|
||||||
|
|
||||||
void ppm_save(const char *filename, struct DisplaySurface *ds, Error **errp)
|
|
||||||
{
|
|
||||||
int width = pixman_image_get_width(ds->image);
|
|
||||||
int height = pixman_image_get_height(ds->image);
|
|
||||||
FILE *f;
|
|
||||||
int y;
|
|
||||||
int ret;
|
|
||||||
pixman_image_t *linebuf;
|
|
||||||
|
|
||||||
trace_ppm_save(filename, ds);
|
|
||||||
f = fopen(filename, "wb");
|
|
||||||
if (!f) {
|
|
||||||
error_setg(errp, "failed to open file '%s': %s", filename,
|
|
||||||
strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255);
|
|
||||||
if (ret < 0) {
|
|
||||||
linebuf = NULL;
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
|
|
||||||
for (y = 0; y < height; y++) {
|
|
||||||
qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y);
|
|
||||||
clearerr(f);
|
|
||||||
ret = fwrite(pixman_image_get_data(linebuf), 1,
|
|
||||||
pixman_image_get_stride(linebuf), f);
|
|
||||||
(void)ret;
|
|
||||||
if (ferror(f)) {
|
|
||||||
goto write_err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
qemu_pixman_image_unref(linebuf);
|
|
||||||
fclose(f);
|
|
||||||
return;
|
|
||||||
|
|
||||||
write_err:
|
|
||||||
error_setg(errp, "failed to write to file '%s': %s", filename,
|
|
||||||
strerror(errno));
|
|
||||||
unlink(filename);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* save the vga display in a PPM image even if no display is
|
|
||||||
available */
|
|
||||||
static void vga_screen_dump(void *opaque, const char *filename, bool cswitch,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
VGACommonState *s = opaque;
|
|
||||||
DisplaySurface *surface = qemu_console_surface(s->con);
|
|
||||||
|
|
||||||
if (cswitch) {
|
|
||||||
vga_invalidate_display(s);
|
|
||||||
}
|
|
||||||
graphic_hw_update(s->con);
|
|
||||||
ppm_save(filename, surface, errp);
|
|
||||||
}
|
|
||||||
|
@ -154,7 +154,6 @@ typedef struct VGACommonState {
|
|||||||
unsigned int g, unsigned b);
|
unsigned int g, unsigned b);
|
||||||
graphic_hw_update_ptr update;
|
graphic_hw_update_ptr update;
|
||||||
graphic_hw_invalidate_ptr invalidate;
|
graphic_hw_invalidate_ptr invalidate;
|
||||||
graphic_hw_screen_dump_ptr screen_dump;
|
|
||||||
graphic_hw_text_update_ptr text_update;
|
graphic_hw_text_update_ptr text_update;
|
||||||
bool full_update_text;
|
bool full_update_text;
|
||||||
bool full_update_gfx;
|
bool full_update_gfx;
|
||||||
@ -198,7 +197,6 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val);
|
|||||||
uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr);
|
uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr);
|
||||||
void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val);
|
void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val);
|
||||||
void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2);
|
void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2);
|
||||||
void ppm_save(const char *filename, struct DisplaySurface *ds, Error **errp);
|
|
||||||
|
|
||||||
int vga_ioport_invalid(VGACommonState *s, uint32_t addr);
|
int vga_ioport_invalid(VGACommonState *s, uint32_t addr);
|
||||||
|
|
||||||
|
@ -1119,31 +1119,6 @@ static void vmsvga_invalidate_display(void *opaque)
|
|||||||
s->invalidated = 1;
|
s->invalidated = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save the vga display in a PPM image even if no display is
|
|
||||||
available */
|
|
||||||
static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
struct vmsvga_state_s *s = opaque;
|
|
||||||
DisplaySurface *surface = qemu_console_surface(s->vga.con);
|
|
||||||
|
|
||||||
if (!s->enable) {
|
|
||||||
s->vga.screen_dump(&s->vga, filename, cswitch, errp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (surface_bits_per_pixel(surface) == 32) {
|
|
||||||
DisplaySurface *ds = qemu_create_displaysurface_from(
|
|
||||||
surface_width(surface),
|
|
||||||
surface_height(surface),
|
|
||||||
32,
|
|
||||||
surface_stride(surface),
|
|
||||||
s->vga.vram_ptr, false);
|
|
||||||
ppm_save(filename, ds, errp);
|
|
||||||
g_free(ds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vmsvga_text_update(void *opaque, console_ch_t *chardata)
|
static void vmsvga_text_update(void *opaque, console_ch_t *chardata)
|
||||||
{
|
{
|
||||||
struct vmsvga_state_s *s = opaque;
|
struct vmsvga_state_s *s = opaque;
|
||||||
@ -1212,7 +1187,6 @@ static void vmsvga_init(struct vmsvga_state_s *s,
|
|||||||
|
|
||||||
s->vga.con = graphic_console_init(vmsvga_update_display,
|
s->vga.con = graphic_console_init(vmsvga_update_display,
|
||||||
vmsvga_invalidate_display,
|
vmsvga_invalidate_display,
|
||||||
vmsvga_screen_dump,
|
|
||||||
vmsvga_text_update, s);
|
vmsvga_text_update, s);
|
||||||
|
|
||||||
s->fifo_size = SVGA_FIFO_SIZE;
|
s->fifo_size = SVGA_FIFO_SIZE;
|
||||||
|
@ -1007,7 +1007,6 @@ wait_more:
|
|||||||
fb->c.con = graphic_console_init(xenfb_update,
|
fb->c.con = graphic_console_init(xenfb_update,
|
||||||
xenfb_invalidate,
|
xenfb_invalidate,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
|
||||||
fb);
|
fb);
|
||||||
fb->have_console = 1;
|
fb->have_console = 1;
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ static void puv3_load_kernel(const char *kernel_filename)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* cheat curses that we have a graphic console, only under ocd console */
|
/* cheat curses that we have a graphic console, only under ocd console */
|
||||||
graphic_console_init(NULL, NULL, NULL, NULL, NULL);
|
graphic_console_init(NULL, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void puv3_init(QEMUMachineInitArgs *args)
|
static void puv3_init(QEMUMachineInitArgs *args)
|
||||||
|
@ -279,13 +279,10 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch)
|
|||||||
|
|
||||||
typedef void (*graphic_hw_update_ptr)(void *);
|
typedef void (*graphic_hw_update_ptr)(void *);
|
||||||
typedef void (*graphic_hw_invalidate_ptr)(void *);
|
typedef void (*graphic_hw_invalidate_ptr)(void *);
|
||||||
typedef void (*graphic_hw_screen_dump_ptr)(void *, const char *, bool cswitch,
|
|
||||||
Error **errp);
|
|
||||||
typedef void (*graphic_hw_text_update_ptr)(void *, console_ch_t *);
|
typedef void (*graphic_hw_text_update_ptr)(void *, console_ch_t *);
|
||||||
|
|
||||||
QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
|
QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
|
||||||
graphic_hw_invalidate_ptr invalidate,
|
graphic_hw_invalidate_ptr invalidate,
|
||||||
graphic_hw_screen_dump_ptr screen_dump,
|
|
||||||
graphic_hw_text_update_ptr text_update,
|
graphic_hw_text_update_ptr text_update,
|
||||||
void *opaque);
|
void *opaque);
|
||||||
|
|
||||||
|
74
ui/console.c
74
ui/console.c
@ -121,7 +121,6 @@ struct QemuConsole {
|
|||||||
/* Graphic console state. */
|
/* Graphic console state. */
|
||||||
graphic_hw_update_ptr hw_update;
|
graphic_hw_update_ptr hw_update;
|
||||||
graphic_hw_invalidate_ptr hw_invalidate;
|
graphic_hw_invalidate_ptr hw_invalidate;
|
||||||
graphic_hw_screen_dump_ptr hw_screen_dump;
|
|
||||||
graphic_hw_text_update_ptr hw_text_update;
|
graphic_hw_text_update_ptr hw_text_update;
|
||||||
void *hw;
|
void *hw;
|
||||||
int g_width, g_height;
|
int g_width, g_height;
|
||||||
@ -188,28 +187,65 @@ void graphic_hw_invalidate(QemuConsole *con)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ppm_save(const char *filename, struct DisplaySurface *ds,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int width = pixman_image_get_width(ds->image);
|
||||||
|
int height = pixman_image_get_height(ds->image);
|
||||||
|
FILE *f;
|
||||||
|
int y;
|
||||||
|
int ret;
|
||||||
|
pixman_image_t *linebuf;
|
||||||
|
|
||||||
|
trace_ppm_save(filename, ds);
|
||||||
|
f = fopen(filename, "wb");
|
||||||
|
if (!f) {
|
||||||
|
error_setg(errp, "failed to open file '%s': %s", filename,
|
||||||
|
strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255);
|
||||||
|
if (ret < 0) {
|
||||||
|
linebuf = NULL;
|
||||||
|
goto write_err;
|
||||||
|
}
|
||||||
|
linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y);
|
||||||
|
clearerr(f);
|
||||||
|
ret = fwrite(pixman_image_get_data(linebuf), 1,
|
||||||
|
pixman_image_get_stride(linebuf), f);
|
||||||
|
(void)ret;
|
||||||
|
if (ferror(f)) {
|
||||||
|
goto write_err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
qemu_pixman_image_unref(linebuf);
|
||||||
|
fclose(f);
|
||||||
|
return;
|
||||||
|
|
||||||
|
write_err:
|
||||||
|
error_setg(errp, "failed to write to file '%s': %s", filename,
|
||||||
|
strerror(errno));
|
||||||
|
unlink(filename);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
void qmp_screendump(const char *filename, Error **errp)
|
void qmp_screendump(const char *filename, Error **errp)
|
||||||
{
|
{
|
||||||
QemuConsole *previous_active_console;
|
QemuConsole *con = consoles[0];
|
||||||
bool cswitch;
|
DisplaySurface *surface;
|
||||||
|
|
||||||
previous_active_console = active_console;
|
if (con == NULL) {
|
||||||
cswitch = previous_active_console && previous_active_console->index != 0;
|
error_setg(errp, "There is no QemuConsole I can screendump from.");
|
||||||
|
return;
|
||||||
/* There is currently no way of specifying which screen we want to dump,
|
|
||||||
so always dump the first one. */
|
|
||||||
if (cswitch) {
|
|
||||||
console_select(0);
|
|
||||||
}
|
|
||||||
if (consoles[0] && consoles[0]->hw_screen_dump) {
|
|
||||||
consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch, errp);
|
|
||||||
} else {
|
|
||||||
error_setg(errp, "device doesn't support screendump");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cswitch) {
|
graphic_hw_update(con);
|
||||||
console_select(previous_active_console->index);
|
surface = qemu_console_surface(con);
|
||||||
}
|
ppm_save(filename, surface, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
|
void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
|
||||||
@ -1411,7 +1447,6 @@ DisplayState *init_displaystate(void)
|
|||||||
|
|
||||||
QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
|
QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
|
||||||
graphic_hw_invalidate_ptr invalidate,
|
graphic_hw_invalidate_ptr invalidate,
|
||||||
graphic_hw_screen_dump_ptr screen_dump,
|
|
||||||
graphic_hw_text_update_ptr text_update,
|
graphic_hw_text_update_ptr text_update,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
@ -1425,7 +1460,6 @@ QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
|
|||||||
s = new_console(ds, GRAPHIC_CONSOLE);
|
s = new_console(ds, GRAPHIC_CONSOLE);
|
||||||
s->hw_update = update;
|
s->hw_update = update;
|
||||||
s->hw_invalidate = invalidate;
|
s->hw_invalidate = invalidate;
|
||||||
s->hw_screen_dump = screen_dump;
|
|
||||||
s->hw_text_update = text_update;
|
s->hw_text_update = text_update;
|
||||||
s->hw = opaque;
|
s->hw = opaque;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user