ui: add egl-headless

ui: some vnc cleanups
 ui: absolute events for input-linux
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJZFaFvAAoJEEy22O7T6HE4TqgP/1bwf3/W6Xmg51A93URWK4uq
 RWGc9nCtIPh+CieqUByHGwDdV88p/6UBuLsXosHG3DM4olRgynAUOjTMBCe9Mq8Q
 nF2LSPW273Ir/H5bzrDP+3m6roxoMQQbSeXRuA5vQ8u4HpD+Y17T7NOeiwCLFg49
 96zSKlucIWHFkGHO4n6fwWR+sIVhsPXwOykFO7EScGav5Ir9SuFJDUx+JeBQ7wot
 eOKiYEKaJKKnjFHFUYQnyUq+6cZBbuR9B3FRHaCNTxGuWjCXxPO8O1Pz4B2oIZH9
 +pcvg+2f12Zw+dm0pMZHO5sH5hkIY7xzRMkFH0ifpscmGNuJT6oJ9xA4x+sJkAiX
 FrcunzAH939LtcgNloZq07PXhA8O+LqHiTKIPr1ZXomnW3jSmfuNvW1TwADJsL2E
 1Pnateibdiz7Xyb4b7hcvgYvjXA3iKbMgfpvu4n7x2P5orHaylmadXFfBjiOJ+u+
 z5n1ip3v417javuIAKwYepvHYjvkbcMPtUrkKwja6F+C3jvTH6wc+wMRrKPQxnOJ
 ZTPrlcEJOqSCYdzxI/fqg8B/HSDr1bcMX+odrCeJWC2O8deAld1aVXaFXz+JW483
 zi2vIBh+kHh6VHDtJynv7Sv7mUCY7jWEiBY6AidnMLR6zomKtwrn7wWqxBbCvI5g
 IVWjoRqul6sTNl3ywMhc
 =FimK
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'kraxel/tags/pull-ui-20170512-1' into staging

ui: add egl-headless
ui: some vnc cleanups
ui: absolute events for input-linux

# gpg: Signature made Fri 12 May 2017 12:50:07 PM BST
# gpg:                using RSA key 0x4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>"
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>"
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>"
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* kraxel/tags/pull-ui-20170512-1:
  vnc: replace hweight_long() with ctpopl()
  vnc: simple clean up
  opengl: add egl-headless display
  egl: explicitly ask for core context
  egl-helpers: add missing error check
  egl-helpers: fix display init for x11
  egl-helpers: drop support for gles and debug logging
  virtio-gpu: move virtio_gpu_gl_block
  ui: input-linux: Add absolute event support
  ui: Support non-zero minimum values for absolute input axes

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2017-05-15 14:26:02 +01:00
commit 384d9d554a
23 changed files with 311 additions and 116 deletions

View File

@ -600,6 +600,22 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g)
}
}
void virtio_gpu_gl_block(void *opaque, bool block)
{
VirtIOGPU *g = opaque;
if (block) {
g->renderer_blocked++;
} else {
g->renderer_blocked--;
}
assert(g->renderer_blocked >= 0);
if (g->renderer_blocked == 0) {
virtio_gpu_process_cmdq(g);
}
}
int virtio_gpu_virgl_init(VirtIOGPU *g)
{
int ret;

View File

@ -929,28 +929,14 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
return 0;
}
static void virtio_gpu_gl_block(void *opaque, bool block)
{
VirtIOGPU *g = opaque;
if (block) {
g->renderer_blocked++;
} else {
g->renderer_blocked--;
}
assert(g->renderer_blocked >= 0);
if (g->renderer_blocked == 0) {
virtio_gpu_process_cmdq(g);
}
}
const GraphicHwOps virtio_gpu_ops = {
.invalidate = virtio_gpu_invalidate_display,
.gfx_update = virtio_gpu_update_display,
.text_update = virtio_gpu_text_update,
.ui_info = virtio_gpu_ui_info,
#ifdef CONFIG_VIRGL
.gl_block = virtio_gpu_gl_block,
#endif
};
static const VMStateDescription vmstate_virtio_gpu_scanout = {

View File

@ -484,12 +484,14 @@ static struct virtio_input_config virtio_tablet_config[] = {
.select = VIRTIO_INPUT_CFG_ABS_INFO,
.subsel = ABS_X,
.size = sizeof(virtio_input_absinfo),
.u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE - 1),
.u.abs.min = const_le32(INPUT_EVENT_ABS_MIN),
.u.abs.max = const_le32(INPUT_EVENT_ABS_MAX),
},{
.select = VIRTIO_INPUT_CFG_ABS_INFO,
.subsel = ABS_Y,
.size = sizeof(virtio_input_absinfo),
.u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE - 1),
.u.abs.min = const_le32(INPUT_EVENT_ABS_MIN),
.u.abs.max = const_le32(INPUT_EVENT_ABS_MAX),
},
{ /* end of list */ },
};

View File

@ -169,6 +169,7 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd);
void virtio_gpu_virgl_fence_poll(VirtIOGPU *g);
void virtio_gpu_virgl_reset(VirtIOGPU *g);
void virtio_gpu_gl_block(void *opaque, bool block);
int virtio_gpu_virgl_init(VirtIOGPU *g);
#endif

View File

@ -201,16 +201,6 @@ static inline unsigned long find_first_zero_bit(const unsigned long *addr,
return find_next_zero_bit(addr, size, 0);
}
static inline unsigned long hweight_long(unsigned long w)
{
unsigned long count;
for (count = 0; w; w >>= 1) {
count += w & 1;
}
return count;
}
/**
* rol8 - rotate an 8-bit value left
* @word: value to rotate

View File

@ -527,4 +527,7 @@ static inline void early_gtk_display_init(int opengl)
}
#endif
/* egl-headless.c */
void egl_headless_init(void);
#endif

View File

@ -21,7 +21,8 @@ int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc);
EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win);
int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug);
int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy);
int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy);
EGLContext qemu_egl_init_ctx(void);
#endif /* EGL_HELPERS_H */

View File

@ -8,7 +8,8 @@
#define INPUT_EVENT_MASK_REL (1<<INPUT_EVENT_KIND_REL)
#define INPUT_EVENT_MASK_ABS (1<<INPUT_EVENT_KIND_ABS)
#define INPUT_EVENT_ABS_SIZE 0x8000
#define INPUT_EVENT_ABS_MIN 0x0000
#define INPUT_EVENT_ABS_MAX 0x7FFF
typedef struct QemuInputHandler QemuInputHandler;
typedef struct QemuInputHandlerState QemuInputHandlerState;
@ -54,12 +55,14 @@ void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map,
uint32_t button_old, uint32_t button_new);
bool qemu_input_is_absolute(void);
int qemu_input_scale_axis(int value, int size_in, int size_out);
int qemu_input_scale_axis(int value,
int min_in, int max_in,
int min_out, int max_out);
InputEvent *qemu_input_event_new_move(InputEventKind kind,
InputAxis axis, int value);
void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value);
void qemu_input_queue_abs(QemuConsole *src, InputAxis axis,
int value, int size);
void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value,
int min_in, int max_in);
void qemu_input_check_mode_change(void);
void qemu_add_mouse_mode_change_notifier(Notifier *notify);

View File

@ -33,6 +33,7 @@ common-obj-y += shader.o
common-obj-y += console-gl.o
common-obj-y += egl-helpers.o
common-obj-y += egl-context.o
common-obj-y += egl-headless.o
ifeq ($(CONFIG_GTK_GL),y)
common-obj-$(CONFIG_GTK) += gtk-gl-area.o
else

View File

@ -749,8 +749,8 @@ QemuCocoaView *cocoaView;
* clicks in the titlebar.
*/
if ([self screenContainsPoint:p]) {
qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, screen.width);
qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, screen.height - p.y, screen.height);
qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, 0, screen.width);
qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, screen.height - p.y, 0, screen.height);
}
} else {
qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, (int)[event deltaX]);

View File

@ -7,6 +7,7 @@ QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl,
{
EGLContext ctx;
EGLint ctx_att[] = {
EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
EGL_CONTEXT_CLIENT_VERSION, params->major_ver,
EGL_CONTEXT_MINOR_VERSION_KHR, params->minor_ver,
EGL_NONE

158
ui/egl-headless.c Normal file
View File

@ -0,0 +1,158 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "sysemu/sysemu.h"
#include "ui/console.h"
#include "ui/egl-helpers.h"
#include "ui/egl-context.h"
typedef struct egl_dpy {
DisplayChangeListener dcl;
DisplaySurface *ds;
int width, height;
GLuint texture;
GLuint framebuffer;
GLuint blit_texture;
GLuint blit_framebuffer;
bool y_0_top;
} egl_dpy;
static void egl_refresh(DisplayChangeListener *dcl)
{
graphic_hw_update(dcl->con);
}
static void egl_gfx_update(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
}
static void egl_gfx_switch(DisplayChangeListener *dcl,
struct DisplaySurface *new_surface)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
edpy->ds = new_surface;
}
static void egl_scanout_disable(DisplayChangeListener *dcl)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
edpy->texture = 0;
/* XXX: delete framebuffers here ??? */
}
static void 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)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
edpy->texture = backing_id;
edpy->y_0_top = backing_y_0_top;
/* source framebuffer */
if (!edpy->framebuffer) {
glGenFramebuffers(1, &edpy->framebuffer);
}
glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->framebuffer);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, edpy->texture, 0);
/* dest framebuffer */
if (!edpy->blit_framebuffer) {
glGenFramebuffers(1, &edpy->blit_framebuffer);
glGenTextures(1, &edpy->blit_texture);
edpy->width = 0;
edpy->height = 0;
}
if (edpy->width != backing_width || edpy->height != backing_height) {
edpy->width = backing_width;
edpy->height = backing_height;
glBindTexture(GL_TEXTURE_2D, edpy->blit_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
edpy->width, edpy->height,
0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
glBindFramebuffer(GL_FRAMEBUFFER_EXT, edpy->blit_framebuffer);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, edpy->blit_texture, 0);
}
}
static void egl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y,
uint32_t w, uint32_t h)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
GLuint y1, y2;
if (!edpy->texture || !edpy->ds) {
return;
}
assert(surface_width(edpy->ds) == edpy->width);
assert(surface_height(edpy->ds) == edpy->height);
assert(surface_format(edpy->ds) == PIXMAN_x8r8g8b8);
/* blit framebuffer, flip if needed */
glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, edpy->blit_framebuffer);
glViewport(0, 0, edpy->width, edpy->height);
y1 = edpy->y_0_top ? edpy->height : 0;
y2 = edpy->y_0_top ? 0 : edpy->height;
glBlitFramebuffer(0, y1, edpy->width, y2,
0, 0, edpy->width, edpy->height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
/* read pixels to surface */
glBindFramebuffer(GL_READ_FRAMEBUFFER, edpy->blit_framebuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
glReadPixels(0, 0, edpy->width, edpy->height,
GL_BGRA, GL_UNSIGNED_BYTE, surface_data(edpy->ds));
/* notify about updates */
dpy_gfx_update(edpy->dcl.con, x, y, w, h);
}
static const DisplayChangeListenerOps egl_ops = {
.dpy_name = "egl-headless",
.dpy_refresh = egl_refresh,
.dpy_gfx_update = egl_gfx_update,
.dpy_gfx_switch = egl_gfx_switch,
.dpy_gl_ctx_create = qemu_egl_create_context,
.dpy_gl_ctx_destroy = qemu_egl_destroy_context,
.dpy_gl_ctx_make_current = qemu_egl_make_context_current,
.dpy_gl_ctx_get_current = qemu_egl_get_current_context,
.dpy_gl_scanout_disable = egl_scanout_disable,
.dpy_gl_scanout_texture = egl_scanout_texture,
.dpy_gl_update = egl_scanout_flush,
};
void egl_headless_init(void)
{
QemuConsole *con;
egl_dpy *edpy;
int idx;
if (egl_rendernode_init(NULL) < 0) {
error_report("egl: render node init failed");
exit(1);
}
for (idx = 0;; idx++) {
con = qemu_console_lookup_by_index(idx);
if (!con || !qemu_console_is_graphic(con)) {
break;
}
edpy = g_new0(egl_dpy, 1);
edpy->dcl.con = con;
edpy->dcl.ops = &egl_ops;
register_displaychangelistener(&edpy->dcl);
}
}

View File

@ -26,18 +26,6 @@ EGLConfig qemu_egl_config;
/* ---------------------------------------------------------------------- */
static bool egl_gles;
static int egl_debug;
#define egl_dbg(_x ...) \
do { \
if (egl_debug) { \
fprintf(stderr, "egl: " _x); \
} \
} while (0);
/* ---------------------------------------------------------------------- */
#ifdef CONFIG_OPENGL_DMABUF
int qemu_egl_rn_fd;
@ -92,6 +80,7 @@ static int qemu_egl_rendernode_open(const char *rendernode)
int egl_rendernode_init(const char *rendernode)
{
qemu_egl_rn_fd = -1;
int rc;
qemu_egl_rn_fd = qemu_egl_rendernode_open(rendernode);
if (qemu_egl_rn_fd == -1) {
@ -105,7 +94,11 @@ int egl_rendernode_init(const char *rendernode)
goto err;
}
qemu_egl_init_dpy((EGLNativeDisplayType)qemu_egl_rn_gbm_dev, false, false);
rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev);
if (rc != 0) {
/* qemu_egl_init_dpy_mesa reports error */
goto err;
}
if (!epoxy_has_egl_extension(qemu_egl_display,
"EGL_KHR_surfaceless_context")) {
@ -171,8 +164,6 @@ EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win)
EGLSurface esurface;
EGLBoolean b;
egl_dbg("eglCreateWindowSurface (x11 win id 0x%lx) ...\n",
(unsigned long) win);
esurface = eglCreateWindowSurface(qemu_egl_display,
qemu_egl_config,
(EGLNativeWindowType)win, NULL);
@ -220,20 +211,19 @@ EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win)
* platform extensions (EGL_KHR_platform_gbm and friends) yet it doesn't seem
* like mesa will be able to advertise these (even though it can do EGL 1.5).
*/
static EGLDisplay qemu_egl_get_display(void *native)
static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
EGLenum platform)
{
EGLDisplay dpy = EGL_NO_DISPLAY;
#ifdef EGL_MESA_platform_gbm
/* In practise any EGL 1.5 implementation would support the EXT extension */
if (epoxy_has_egl_extension(NULL, "EGL_EXT_platform_base")) {
PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplayEXT =
(void *) eglGetProcAddress("eglGetPlatformDisplayEXT");
if (getPlatformDisplayEXT) {
dpy = getPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, native, NULL);
if (getPlatformDisplayEXT && platform != 0) {
dpy = getPlatformDisplayEXT(platform, native, NULL);
}
}
#endif
if (dpy == EGL_NO_DISPLAY) {
/* fallback */
@ -242,7 +232,8 @@ static EGLDisplay qemu_egl_get_display(void *native)
return dpy;
}
int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug)
static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
EGLenum platform)
{
static const EGLint conf_att_gl[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
@ -253,75 +244,66 @@ int qemu_egl_init_dpy(EGLNativeDisplayType dpy, bool gles, bool debug)
EGL_ALPHA_SIZE, 0,
EGL_NONE,
};
static const EGLint conf_att_gles[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 5,
EGL_BLUE_SIZE, 5,
EGL_ALPHA_SIZE, 0,
EGL_NONE,
};
EGLint major, minor;
EGLBoolean b;
EGLint n;
if (debug) {
egl_debug = 1;
setenv("EGL_LOG_LEVEL", "debug", true);
setenv("LIBGL_DEBUG", "verbose", true);
}
egl_dbg("qemu_egl_get_display (dpy %p) ...\n", dpy);
qemu_egl_display = qemu_egl_get_display(dpy);
qemu_egl_display = qemu_egl_get_display(dpy, platform);
if (qemu_egl_display == EGL_NO_DISPLAY) {
error_report("egl: eglGetDisplay failed");
return -1;
}
egl_dbg("eglInitialize ...\n");
b = eglInitialize(qemu_egl_display, &major, &minor);
if (b == EGL_FALSE) {
error_report("egl: eglInitialize failed");
return -1;
}
egl_dbg("eglBindAPI ...\n");
b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
b = eglBindAPI(EGL_OPENGL_API);
if (b == EGL_FALSE) {
error_report("egl: eglBindAPI failed");
return -1;
}
egl_dbg("eglChooseConfig ...\n");
b = eglChooseConfig(qemu_egl_display,
gles ? conf_att_gles : conf_att_gl,
b = eglChooseConfig(qemu_egl_display, conf_att_gl,
&qemu_egl_config, 1, &n);
if (b == EGL_FALSE || n != 1) {
error_report("egl: eglChooseConfig failed");
return -1;
}
egl_gles = gles;
return 0;
}
int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy)
{
#ifdef EGL_KHR_platform_x11
return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR);
#else
return qemu_egl_init_dpy(dpy, 0);
#endif
}
int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy)
{
#ifdef EGL_MESA_platform_gbm
return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA);
#else
return qemu_egl_init_dpy(dpy, 0);
#endif
}
EGLContext qemu_egl_init_ctx(void)
{
static const EGLint ctx_att_gl[] = {
EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
EGL_NONE
};
static const EGLint ctx_att_gles[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLContext ectx;
EGLBoolean b;
egl_dbg("eglCreateContext ...\n");
ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT,
egl_gles ? ctx_att_gles : ctx_att_gl);
ctx_att_gl);
if (ectx == EGL_NO_CONTEXT) {
error_report("egl: eglCreateContext failed");
return NULL;

View File

@ -246,7 +246,7 @@ void gtk_egl_init(void)
GdkDisplay *gdk_display = gdk_display_get_default();
Display *x11_display = gdk_x11_display_get_xdisplay(gdk_display);
if (qemu_egl_init_dpy(x11_display, false, false) < 0) {
if (qemu_egl_init_dpy_x11(x11_display) < 0) {
return;
}

View File

@ -912,9 +912,9 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
return TRUE;
}
qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
surface_width(vc->gfx.ds));
0, surface_width(vc->gfx.ds));
qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
surface_height(vc->gfx.ds));
0, surface_height(vc->gfx.ds));
qemu_input_event_sync();
} else if (s->last_set && s->ptr_owner == vc) {
qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);

View File

@ -169,6 +169,10 @@ struct InputLinux {
bool has_abs_x;
int num_keys;
int num_btns;
int abs_x_min;
int abs_x_max;
int abs_y_min;
int abs_y_max;
struct input_event event;
int read_offset;
@ -314,6 +318,18 @@ static void input_linux_handle_mouse(InputLinux *il, struct input_event *event)
break;
}
break;
case EV_ABS:
switch (event->code) {
case ABS_X:
qemu_input_queue_abs(NULL, INPUT_AXIS_X, event->value,
il->abs_x_min, il->abs_x_max);
break;
case ABS_Y:
qemu_input_queue_abs(NULL, INPUT_AXIS_Y, event->value,
il->abs_y_min, il->abs_y_max);
break;
}
break;
case EV_SYN:
qemu_input_event_sync();
if (il->wheel != 0) {
@ -351,7 +367,7 @@ static void input_linux_event(void *opaque)
if (il->num_keys) {
input_linux_handle_keyboard(il, &il->event);
}
if (il->has_rel_x && il->num_btns) {
if ((il->has_rel_x || il->has_abs_x) && il->num_btns) {
input_linux_handle_mouse(il, &il->event);
}
}
@ -364,6 +380,7 @@ static void input_linux_complete(UserCreatable *uc, Error **errp)
uint8_t keymap[KEY_CNT / 8], keystate[KEY_CNT / 8];
unsigned int i;
int rc, ver;
struct input_absinfo absinfo;
if (!il->evdev) {
error_setg(errp, "no input device specified");
@ -402,6 +419,12 @@ static void input_linux_complete(UserCreatable *uc, Error **errp)
rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap);
if (absmap & (1 << ABS_X)) {
il->has_abs_x = true;
rc = ioctl(il->fd, EVIOCGABS(ABS_X), &absinfo);
il->abs_x_min = absinfo.minimum;
il->abs_x_max = absinfo.maximum;
rc = ioctl(il->fd, EVIOCGABS(ABS_Y), &absinfo);
il->abs_y_min = absinfo.minimum;
il->abs_y_max = absinfo.maximum;
}
}

View File

@ -166,6 +166,11 @@ void qmp_input_send_event(bool has_device, const char *device,
qemu_input_event_sync();
}
static int qemu_input_transform_invert_abs_value(int value)
{
return (int64_t)INPUT_EVENT_ABS_MAX - value + INPUT_EVENT_ABS_MIN;
}
static void qemu_input_transform_abs_rotate(InputEvent *evt)
{
InputMoveEvent *move = evt->u.abs.data;
@ -175,16 +180,16 @@ static void qemu_input_transform_abs_rotate(InputEvent *evt)
move->axis = INPUT_AXIS_Y;
} else if (move->axis == INPUT_AXIS_Y) {
move->axis = INPUT_AXIS_X;
move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
move->value = qemu_input_transform_invert_abs_value(move->value);
}
break;
case 180:
move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
move->value = qemu_input_transform_invert_abs_value(move->value);
break;
case 270:
if (move->axis == INPUT_AXIS_X) {
move->axis = INPUT_AXIS_Y;
move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
move->value = qemu_input_transform_invert_abs_value(move->value);
} else if (move->axis == INPUT_AXIS_Y) {
move->axis = INPUT_AXIS_X;
}
@ -467,12 +472,17 @@ bool qemu_input_is_absolute(void)
return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS);
}
int qemu_input_scale_axis(int value, int size_in, int size_out)
int qemu_input_scale_axis(int value,
int min_in, int max_in,
int min_out, int max_out)
{
if (size_in < 2) {
return size_out / 2;
int64_t range_in = (int64_t)max_in - min_in;
int64_t range_out = (int64_t)max_out - min_out;
if (range_in < 1) {
return min_out + range_out / 2;
}
return (int64_t)value * (size_out - 1) / (size_in - 1);
return ((int64_t)value - min_in) * range_out / range_in + min_out;
}
InputEvent *qemu_input_event_new_move(InputEventKind kind,
@ -496,10 +506,13 @@ void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value)
qapi_free_InputEvent(evt);
}
void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, int size)
void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value,
int min_in, int max_in)
{
InputEvent *evt;
int scaled = qemu_input_scale_axis(value, size, INPUT_EVENT_ABS_SIZE);
int scaled = qemu_input_scale_axis(value, min_in, max_in,
INPUT_EVENT_ABS_MIN,
INPUT_EVENT_ABS_MAX);
evt = qemu_input_event_new_move(INPUT_EVENT_KIND_ABS, axis, scaled);
qemu_input_event_send(src, evt);
qapi_free_InputEvent(evt);

View File

@ -490,9 +490,9 @@ static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state)
if (qemu_input_is_absolute()) {
qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, x,
real_screen->w);
0, real_screen->w);
qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, y,
real_screen->h);
0, real_screen->h);
} else {
if (guest_cursor) {
x -= guest_x;

View File

@ -298,8 +298,8 @@ static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy,
}
}
}
qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w);
qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h);
qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, 0, max_w);
qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, 0, max_h);
} else {
if (guest_cursor) {
x -= guest_x;

View File

@ -172,8 +172,8 @@ static void tablet_position(SpiceTabletInstance* sin, int x, int y,
QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
spice_update_buttons(pointer, 0, buttons_state);
qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, pointer->width);
qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, pointer->height);
qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, 0, pointer->width);
qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, 0, pointer->height);
qemu_input_event_sync();
}

View File

@ -163,7 +163,6 @@ static void zrle_choose_palette_rle(VncState *vs, int w, int h,
if (packed_bytes < estimated_bytes) {
*use_rle = false;
*use_palette = true;
estimated_bytes = packed_bytes;
}
}
}

View File

@ -1556,8 +1556,8 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y)
}
if (vs->absolute) {
qemu_input_queue_abs(con, INPUT_AXIS_X, x, width);
qemu_input_queue_abs(con, INPUT_AXIS_Y, y, height);
qemu_input_queue_abs(con, INPUT_AXIS_X, x, 0, width);
qemu_input_queue_abs(con, INPUT_AXIS_Y, y, 0, height);
} else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) {
qemu_input_queue_rel(con, INPUT_AXIS_X, x - 0x7FFF);
qemu_input_queue_rel(con, INPUT_AXIS_Y, y - 0x7FFF);
@ -2061,15 +2061,15 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel,
}
vs->client_pf.rmax = red_max ? red_max : 0xFF;
vs->client_pf.rbits = hweight_long(red_max);
vs->client_pf.rbits = ctpopl(red_max);
vs->client_pf.rshift = red_shift;
vs->client_pf.rmask = red_max << red_shift;
vs->client_pf.gmax = green_max ? green_max : 0xFF;
vs->client_pf.gbits = hweight_long(green_max);
vs->client_pf.gbits = ctpopl(green_max);
vs->client_pf.gshift = green_shift;
vs->client_pf.gmask = green_max << green_shift;
vs->client_pf.bmax = blue_max ? blue_max : 0xFF;
vs->client_pf.bbits = hweight_long(blue_max);
vs->client_pf.bbits = ctpopl(blue_max);
vs->client_pf.bshift = blue_shift;
vs->client_pf.bmask = blue_max << blue_shift;
vs->client_pf.bits_per_pixel = bits_per_pixel;

16
vl.c
View File

@ -2050,6 +2050,7 @@ typedef enum DisplayType {
DT_SDL,
DT_COCOA,
DT_GTK,
DT_EGL,
DT_NONE,
} DisplayType;
@ -2127,6 +2128,15 @@ static DisplayType select_display(const char *p)
error_report("VNC requires a display argument vnc=<display>");
exit(1);
}
} else if (strstart(p, "egl-headless", &opts)) {
#ifdef CONFIG_OPENGL
request_opengl = 1;
display_opengl = 1;
display = DT_EGL;
#else
fprintf(stderr, "egl support is disabled\n");
exit(1);
#endif
} else if (strstart(p, "curses", &opts)) {
#ifdef CONFIG_CURSES
display = DT_CURSES;
@ -4658,6 +4668,12 @@ int main(int argc, char **argv, char **envp)
qemu_spice_display_init();
}
#ifdef CONFIG_OPENGL
if (display_type == DT_EGL) {
egl_headless_init();
}
#endif
if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
exit(1);
}