From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 6E21CC433EF for ; Tue, 21 Dec 2021 09:02:13 +0000 (UTC) Received: from localhost ([::1]:40044 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mzb2A-00046O-Bg for qemu-devel@archiver.kernel.org; Tue, 21 Dec 2021 04:02:11 -0500 Received: from eggs.gnu.org ([209.51.188.92]:50158) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mzazu-0002h2-R1 for qemu-devel@nongnu.org; Tue, 21 Dec 2021 03:59:50 -0500 Received: from [2a00:1450:4864:20::435] (port=44735 helo=mail-wr1-x435.google.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1mzazr-0006se-Tn for qemu-devel@nongnu.org; Tue, 21 Dec 2021 03:59:50 -0500 Received: by mail-wr1-x435.google.com with SMTP id t18so25368739wrg.11 for ; Tue, 21 Dec 2021 00:59:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=2a8xYyslGpBhqtgkmMw9AZg6mE5UWscxt9h84np07Xw=; b=JVyZCRu7agd00WjX7wycLLk76rkvK9q6z9m3uwswSq9fLUMVdSU4h6PG680l/VxRBz xfNES6caQfzySl/dS+UXn2W7PK2DQqvEYYoEIzbvM+JHStCgzg3hUj4zqUDu9aOaiI/s L+ZpgQ5BS6fp28JNMrC38y1Un5rV5FJqUZinbXVpIW7Djm8gPnDWBYncHnKvRdRkAkjX 2njrWg+berRqfPqS+FON5aC1UprUN7XPG7GgAJX/+jGdNr6xElw24tyWaQatZbrLKRYn gh+/azeGdP81lViOcQnEmvw6qu2TjrjEEV62lwOKaVGD92A8at8lG+NefutKwAQ28Zc+ hL1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=2a8xYyslGpBhqtgkmMw9AZg6mE5UWscxt9h84np07Xw=; b=iF0Y38+JOrzqQmOyBnJSXQdul5C1I00W9HEOKm0cnTdBkvSV1kmg8f859HEhORuLWg /qgfpH40OEe8jKK70bIQZ2zh/5iXpMIZQWeGGiHsFjBssNmHSzmJTpVNycSBB16xCVym zxiuqFHvd/OHPHaZJf87ZsiDxM8+05cLBdyw2gyBXCwsmJOw+gz30Q0vlahgKFiCKkBO SHdiZjUKfPiRd/bB0MgzVB89xuqZmf6HBk4ic0gaiyRgB7eOpRHLzNKZawt81y5TYatG 54k1vtG18Oqi4o9BYN1Grz5AkW0o3QS1K1RY8M1oRXPZY6y94Ceq0hEP5V8wtyB94oaK 396A== X-Gm-Message-State: AOAM530yPzdy7+AbQQq5mAg72Gx/Y4YG7MUboAwT7tc9jE/i5X6ux3w1 jaZ9rgQvDWq1Gb4UuaRBIpSYOgK2pRIP2ZkIlU8= X-Google-Smtp-Source: ABdhPJxnim7049Gpsq+MHItsd+qDNbLVzntIGX84BkS42RbU3PSQ8BZLvmxXc7rK4yltHLnExogKpn3Ddj4rnUSV3K0= X-Received: by 2002:a5d:47a1:: with SMTP id 1mr1776113wrb.436.1640077185432; Tue, 21 Dec 2021 00:59:45 -0800 (PST) MIME-Version: 1.0 References: <20211220233722.1262821-1-dpetroff@gmail.com> In-Reply-To: <20211220233722.1262821-1-dpetroff@gmail.com> From: =?UTF-8?B?TWFyYy1BbmRyw6kgTHVyZWF1?= Date: Tue, 21 Dec 2021 12:59:33 +0400 Message-ID: Subject: Re: [PATCH] ps2: Initial horizontal scroll support To: Dmitry Petrov Content-Type: multipart/alternative; boundary="0000000000000ac0aa05d3a43a32" X-Host-Lookup-Failed: Reverse DNS lookup failed for 2a00:1450:4864:20::435 (failed) Received-SPF: pass client-ip=2a00:1450:4864:20::435; envelope-from=marcandre.lureau@gmail.com; helo=mail-wr1-x435.google.com X-Spam_score_int: -12 X-Spam_score: -1.3 X-Spam_bar: - X-Spam_report: (-1.3 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: QEMU Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" --0000000000000ac0aa05d3a43a32 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi On Tue, Dec 21, 2021 at 4:10 AM Dmitry Petrov wrote: > This patch introduces horizontal scroll support for the ps/2 mouse. > It includes changes in the ps/2 device driver as well as support > for three display options - cocoa, gtk and sdl, tested and working > on all of them against guest ubuntu system. > > The patch is based on the previous work by Brad Jorsch done in 2010 > but never merge, see > https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=3D579968 You should split the patch for the different subsystems/ui etc Looks good to me, although I didn't test it yet. Some comments below > > Signed-off-by: Dmitry Petrov > --- > hw/input/ps2.c | 54 ++++++++++++++++++++++++++++++++++++++++------- > qapi/ui.json | 2 +- > ui/cocoa.m | 18 ++++++++++------ > ui/gtk.c | 54 ++++++++++++++++++++++++++++++++++++----------- > ui/input-legacy.c | 16 ++++++++++++++ > ui/sdl2.c | 5 +++++ > 6 files changed, 122 insertions(+), 27 deletions(-) > > diff --git a/hw/input/ps2.c b/hw/input/ps2.c > index 9376a8f4ce..9e42284cd9 100644 > --- a/hw/input/ps2.c > +++ b/hw/input/ps2.c > @@ -123,6 +123,7 @@ typedef struct { > int mouse_dx; /* current values, needed for 'poll' mode */ > int mouse_dy; > int mouse_dz; > + int mouse_dw; > uint8_t mouse_buttons; > } PS2MouseState; > > @@ -715,7 +716,7 @@ static int ps2_mouse_send_packet(PS2MouseState *s) > /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */ > const int needed =3D s->mouse_type ? 4 : 3; > unsigned int b; > - int dx1, dy1, dz1; > + int dx1, dy1, dz1, dw1; > > if (PS2_QUEUE_SIZE - s->common.queue.count < needed) { > return 0; > @@ -724,6 +725,7 @@ static int ps2_mouse_send_packet(PS2MouseState *s) > dx1 =3D s->mouse_dx; > dy1 =3D s->mouse_dy; > dz1 =3D s->mouse_dz; > + dw1 =3D s->mouse_dw; > /* XXX: increase range to 8 bits ? */ > if (dx1 > 127) > dx1 =3D 127; > @@ -740,6 +742,9 @@ static int ps2_mouse_send_packet(PS2MouseState *s) > /* extra byte for IMPS/2 or IMEX */ > switch(s->mouse_type) { > default: > + /* Just ignore the wheels if not supported */ > + s->mouse_dz =3D 0; > + s->mouse_dw =3D 0; > break; > case 3: > if (dz1 > 127) > @@ -747,13 +752,38 @@ static int ps2_mouse_send_packet(PS2MouseState *s) > else if (dz1 < -127) > dz1 =3D -127; > ps2_queue_noirq(&s->common, dz1 & 0xff); > + s->mouse_dz -=3D dz1; > + s->mouse_dw =3D 0; > break; > case 4: > - if (dz1 > 7) > - dz1 =3D 7; > - else if (dz1 < -7) > - dz1 =3D -7; > - b =3D (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); > + /* > + * This matches what the Linux kernel expects for exps/2 in > + * drivers/input/mouse/psmouse-base.c. Note, if you happen to > + * press/release the 4th or 5th buttons at the same moment as a > + * horizontal wheel scroll, those button presses will get lost. > I'm not > + * sure what to do about that, since by this point we don't know > + * whether those buttons actually changed state. > + */ > Reading the kernel code helped me guess what is going on, but it would be nice to have more doc or link to specifications instead. > + if (dw1 !=3D 0) { > + if (dw1 > 15) { > + dw1 =3D 15; > + } else if (dw1 < -15) { > + dw1 =3D -15; > + } > + > + /* 0x3f was found by trial and error vs ubuntu instance */ > + b =3D (dw1 & 0x3f) | 0x40; > Ok, clamp at 15 (I think you could go at 31 actually, since 5 bits seem to be used) and go to the kernel: case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */ This case doesn't handle buttons simultaneously indeed. I think 0x3f comes from 5 bits + 1 sign bit. + s->mouse_dw -=3D dw1; > + } else { > + if (dz1 > 7) { > + dz1 =3D 7; > + } else if (dz1 < -7) { > + dz1 =3D -7; > + } > + > + b =3D (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); > + s->mouse_dz -=3D dz1; > Here clamp at 7, since we should fall in the kernel case 0x00: and only 3 bits seem to be used (thus & 0x0f for 3+1 sign). This case handles buttons simultaneously, but only vertical scroll (unless a4tech_workaround is set and triggered) > + } > ps2_queue_noirq(&s->common, b); > break; > } > @@ -764,7 +794,6 @@ static int ps2_mouse_send_packet(PS2MouseState *s) > /* update deltas */ > s->mouse_dx -=3D dx1; > s->mouse_dy -=3D dy1; > - s->mouse_dz -=3D dz1; > > return 1; > } > @@ -806,6 +835,12 @@ static void ps2_mouse_event(DeviceState *dev, > QemuConsole *src, > } else if (btn->button =3D=3D INPUT_BUTTON_WHEEL_DOWN) { > s->mouse_dz++; > } > + > + if (btn->button =3D=3D INPUT_BUTTON_WHEEL_RIGHT) { > + s->mouse_dw--; > + } else if (btn->button =3D=3D INPUT_BUTTON_WHEEL_LEFT) { > + s->mouse_dw++; > + } > } else { > s->mouse_buttons &=3D ~bmap[btn->button]; > } > @@ -833,8 +868,10 @@ static void ps2_mouse_sync(DeviceState *dev) > /* if not remote, send event. Multiple events are sent if > too big deltas */ > while (ps2_mouse_send_packet(s)) { > - if (s->mouse_dx =3D=3D 0 && s->mouse_dy =3D=3D 0 && s->mouse= _dz =3D=3D 0) > + if (s->mouse_dx =3D=3D 0 && s->mouse_dy =3D=3D 0 > + && s->mouse_dz =3D=3D 0 && s->mouse_dw =3D=3D 0) { > break; > + } > } > } > } > @@ -1036,6 +1073,7 @@ static void ps2_mouse_reset(void *opaque) > s->mouse_dx =3D 0; > s->mouse_dy =3D 0; > s->mouse_dz =3D 0; > + s->mouse_dw =3D 0; > s->mouse_buttons =3D 0; > } > > diff --git a/qapi/ui.json b/qapi/ui.json > index d7567ac866..9dac1bf548 100644 > --- a/qapi/ui.json > +++ b/qapi/ui.json > @@ -905,7 +905,7 @@ > ## > { 'enum' : 'InputButton', > 'data' : [ 'left', 'middle', 'right', 'wheel-up', 'wheel-down', 'side= ', > - 'extra' ] } > + 'extra', 'wheel-left', 'wheel-right' ] } > > ## > # @InputAxis: > diff --git a/ui/cocoa.m b/ui/cocoa.m > index 68a6302184..c898a8aeaa 100644 > --- a/ui/cocoa.m > +++ b/ui/cocoa.m > @@ -970,21 +970,27 @@ QemuCocoaView *cocoaView; > */ > > /* > - * When deltaY is zero, it means that this scrolling event w= as > - * either horizontal, or so fine that it only appears in > - * scrollingDeltaY. So we drop the event. > + * We shouldn't have got a scroll event when deltaY and delt= a > Y > + * are zero, hence no harm in dropping the event > */ > - if ([event deltaY] !=3D 0) { > + if ([event deltaY] !=3D 0 || [event deltaX] !=3D 0) { > /* Determine if this is a scroll up or scroll down event */ > - buttons =3D ([event deltaY] > 0) ? > + if ([event deltaY] !=3D 0) { > + buttons =3D ([event deltaY] > 0) ? > INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; > + } else if ([event deltaX] !=3D 0) { > + buttons =3D ([event deltaX] > 0) ? > + INPUT_BUTTON_WHEEL_LEFT : INPUT_BUTTON_WHEEL_RIGHT; > + } > + > qemu_input_queue_btn(dcl.con, buttons, true); > qemu_input_event_sync(); > qemu_input_queue_btn(dcl.con, buttons, false); > qemu_input_event_sync(); > } > + > /* > - * Since deltaY also reports scroll wheel events we prevent > mouse > + * Since deltaY/deltaY also report scroll wheel events we > prevent mouse > * movement code from executing. > */ > mouse_event =3D false; > diff --git a/ui/gtk.c b/ui/gtk.c > index 428f02f2df..b52eec6fe9 100644 > --- a/ui/gtk.c > +++ b/ui/gtk.c > @@ -963,33 +963,63 @@ static gboolean gd_scroll_event(GtkWidget *widget, > GdkEventScroll *scroll, > void *opaque) > { > VirtualConsole *vc =3D opaque; > - InputButton btn; > + InputButton btn_vertical; > + InputButton btn_horizontal; > + bool has_vertical =3D false; > + bool has_horizontal =3D false; > > if (scroll->direction =3D=3D GDK_SCROLL_UP) { > - btn =3D INPUT_BUTTON_WHEEL_UP; > + btn_vertical =3D INPUT_BUTTON_WHEEL_UP; > + has_vertical =3D true; > } else if (scroll->direction =3D=3D GDK_SCROLL_DOWN) { > - btn =3D INPUT_BUTTON_WHEEL_DOWN; > + btn_vertical =3D INPUT_BUTTON_WHEEL_DOWN; > + has_vertical =3D true; > + } else if (scroll->direction =3D=3D GDK_SCROLL_LEFT) { > + btn_horizontal =3D INPUT_BUTTON_WHEEL_LEFT; > + has_horizontal =3D true; > + } else if (scroll->direction =3D=3D GDK_SCROLL_RIGHT) { > + btn_horizontal =3D INPUT_BUTTON_WHEEL_RIGHT; > + has_horizontal =3D true; > } else if (scroll->direction =3D=3D GDK_SCROLL_SMOOTH) { > gdouble delta_x, delta_y; > if (!gdk_event_get_scroll_deltas((GdkEvent *)scroll, > &delta_x, &delta_y)) { > return TRUE; > } > - if (delta_y =3D=3D 0) { > - return TRUE; > - } else if (delta_y > 0) { > - btn =3D INPUT_BUTTON_WHEEL_DOWN; > + > + if (delta_y > 0) { > + btn_vertical =3D INPUT_BUTTON_WHEEL_DOWN; > + has_vertical =3D true; > + } else if (delta_y < 0) { > + btn_vertical =3D INPUT_BUTTON_WHEEL_UP; > + has_vertical =3D true; > + } else if (delta_x > 0) { > + btn_horizontal =3D INPUT_BUTTON_WHEEL_RIGHT; > + has_horizontal =3D true; > + } else if (delta_x < 0) { > + btn_horizontal =3D INPUT_BUTTON_WHEEL_LEFT; > + has_horizontal =3D true; > } else { > - btn =3D INPUT_BUTTON_WHEEL_UP; > + return TRUE; > } > } else { > return TRUE; > } > > - qemu_input_queue_btn(vc->gfx.dcl.con, btn, true); > - qemu_input_event_sync(); > - qemu_input_queue_btn(vc->gfx.dcl.con, btn, false); > - qemu_input_event_sync(); > + if (has_vertical) { > + qemu_input_queue_btn(vc->gfx.dcl.con, btn_vertical, true); > + qemu_input_event_sync(); > + qemu_input_queue_btn(vc->gfx.dcl.con, btn_vertical, false); > + qemu_input_event_sync(); > + } > + > + if (has_horizontal) { > + qemu_input_queue_btn(vc->gfx.dcl.con, btn_horizontal, true); > + qemu_input_event_sync(); > + qemu_input_queue_btn(vc->gfx.dcl.con, btn_horizontal, false); > + qemu_input_event_sync(); > + } > + > return TRUE; > } > > diff --git a/ui/input-legacy.c b/ui/input-legacy.c > index 9fc78a639b..2c9a215d7f 100644 > --- a/ui/input-legacy.c > +++ b/ui/input-legacy.c > @@ -23,6 +23,7 @@ > */ > > #include "qemu/osdep.h" > +#include "qemu/log.h" > #include "qapi/qapi-commands-ui.h" > #include "ui/console.h" > #include "keymaps.h" > @@ -179,6 +180,20 @@ static void legacy_mouse_event(DeviceState *dev, > QemuConsole *src, > 1, > s->buttons); > } > + if (btn->down && btn->button =3D=3D INPUT_BUTTON_WHEEL_RIGHT) { > + s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, > + s->axis[INPUT_AXIS_X], > + s->axis[INPUT_AXIS_Y], > + -2, > + s->buttons); > + } > + if (btn->down && btn->button =3D=3D INPUT_BUTTON_WHEEL_LEFT) { > + s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque, > + s->axis[INPUT_AXIS_X], > + s->axis[INPUT_AXIS_Y], > + 2, > + s->buttons); > + } > break; > case INPUT_EVENT_KIND_ABS: > move =3D evt->u.abs.data; > @@ -216,6 +231,7 @@ QEMUPutMouseEntry > *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, > QEMUPutMouseEntry *s; > > s =3D g_new0(QEMUPutMouseEntry, 1); > + qemu_log("qemu_add_mouse_event_handler %s", name); > > s->qemu_put_mouse_event =3D func; > s->qemu_put_mouse_event_opaque =3D opaque; > diff --git a/ui/sdl2.c b/ui/sdl2.c > index 17c0ec30eb..19bbc1fdd4 100644 > --- a/ui/sdl2.c > +++ b/ui/sdl2.c > @@ -33,6 +33,7 @@ > #include "sysemu/runstate-action.h" > #include "sysemu/sysemu.h" > #include "ui/win32-kbd-hook.h" > +#include "qemu/log.h" > > static int sdl2_num_outputs; > static struct sdl2_console *sdl2_console; > @@ -535,6 +536,10 @@ static void handle_mousewheel(SDL_Event *ev) > btn =3D INPUT_BUTTON_WHEEL_UP; > } else if (wev->y < 0) { > btn =3D INPUT_BUTTON_WHEEL_DOWN; > + } else if (wev->x < 0) { > + btn =3D INPUT_BUTTON_WHEEL_RIGHT; > + } else if (wev->x > 0) { > + btn =3D INPUT_BUTTON_WHEEL_LEFT; > } else { > return; > } > -- > 2.32.0 > > > --=20 Marc-Andr=C3=A9 Lureau --0000000000000ac0aa05d3a43a32 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi

On Tue, Dec 21, 2021 at 4:10 AM Dmi= try Petrov <dpetroff@gmail.com= > wrote:
This= patch introduces horizontal scroll support for the ps/2 mouse.
It includes changes in the ps/2 device driver as well as support
for three display options - cocoa, gtk and sdl, tested and working
on all of them against guest ubuntu system.

The patch is based on the previous work by Brad Jorsch done in 2010
but never merge, see
https://bugs.debian.org/cgi-bin/bugreport= .cgi?bug=3D579968

You should split the = patch for the different subsystems/ui etc

Looks go= od to me, although I didn't test it yet. Some comments below
<= div>


Signed-off-by: Dmitry Petrov <dpetroff@gmail.com>
---
=C2=A0hw/input/ps2.c=C2=A0 =C2=A0 | 54 ++++++++++++++++++++++++++++++++++++= ++++-------
=C2=A0qapi/ui.json=C2=A0 =C2=A0 =C2=A0 |=C2=A0 2 +-
=C2=A0ui/cocoa.m=C2=A0 =C2=A0 =C2=A0 =C2=A0 | 18 ++++++++++------
=C2=A0ui/gtk.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | 54 +++++++++++++++++++++= +++++++++++++++-----------
=C2=A0ui/input-legacy.c | 16 ++++++++++++++
=C2=A0ui/sdl2.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 5 +++++
=C2=A06 files changed, 122 insertions(+), 27 deletions(-)

diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index 9376a8f4ce..9e42284cd9 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -123,6 +123,7 @@ typedef struct {
=C2=A0 =C2=A0 =C2=A0int mouse_dx; /* current values, needed for 'poll&#= 39; mode */
=C2=A0 =C2=A0 =C2=A0int mouse_dy;
=C2=A0 =C2=A0 =C2=A0int mouse_dz;
+=C2=A0 =C2=A0 int mouse_dw;
=C2=A0 =C2=A0 =C2=A0uint8_t mouse_buttons;
=C2=A0} PS2MouseState;

@@ -715,7 +716,7 @@ static int ps2_mouse_send_packet(PS2MouseState *s)
=C2=A0 =C2=A0 =C2=A0/* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */ =C2=A0 =C2=A0 =C2=A0const int needed =3D s->mouse_type ? 4 : 3;
=C2=A0 =C2=A0 =C2=A0unsigned int b;
-=C2=A0 =C2=A0 int dx1, dy1, dz1;
+=C2=A0 =C2=A0 int dx1, dy1, dz1, dw1;

=C2=A0 =C2=A0 =C2=A0if (PS2_QUEUE_SIZE - s->common.queue.count < need= ed) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return 0;
@@ -724,6 +725,7 @@ static int ps2_mouse_send_packet(PS2MouseState *s)
=C2=A0 =C2=A0 =C2=A0dx1 =3D s->mouse_dx;
=C2=A0 =C2=A0 =C2=A0dy1 =3D s->mouse_dy;
=C2=A0 =C2=A0 =C2=A0dz1 =3D s->mouse_dz;
+=C2=A0 =C2=A0 dw1 =3D s->mouse_dw;
=C2=A0 =C2=A0 =C2=A0/* XXX: increase range to 8 bits ? */
=C2=A0 =C2=A0 =C2=A0if (dx1 > 127)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dx1 =3D 127;
@@ -740,6 +742,9 @@ static int ps2_mouse_send_packet(PS2MouseState *s)
=C2=A0 =C2=A0 =C2=A0/* extra byte for IMPS/2 or IMEX */
=C2=A0 =C2=A0 =C2=A0switch(s->mouse_type) {
=C2=A0 =C2=A0 =C2=A0default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Just ignore the wheels if not supported */<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 s->mouse_dz =3D 0;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 s->mouse_dw =3D 0;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;
=C2=A0 =C2=A0 =C2=A0case 3:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (dz1 > 127)
@@ -747,13 +752,38 @@ static int ps2_mouse_send_packet(PS2MouseState *s) =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0else if (dz1 < -127)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dz1 =3D -127;=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ps2_queue_noirq(&s->common, dz1 &a= mp; 0xff);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 s->mouse_dz -=3D dz1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 s->mouse_dw =3D 0;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;
=C2=A0 =C2=A0 =C2=A0case 4:
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (dz1 > 7)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dz1 =3D 7;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 else if (dz1 < -7)
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dz1 =3D -7;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 b =3D (dz1 & 0x0f) | ((s->mouse_buttons= & 0x18) << 1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* This matches what the Linux kernel exp= ects for exps/2 in
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* drivers/input/mouse/psmouse-base.c. No= te, if you happen to
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* press/release the 4th or 5th buttons a= t the same moment as a
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* horizontal wheel scroll, those button = presses will get lost. I'm not
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* sure what to do about that, since by t= his point we don't know
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* whether those buttons actually changed= state.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/

Reading the kernel code helped me guess wh= at is going on, but it would be nice to have more doc or link to specificat= ions instead.
=C2=A0
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (dw1 !=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (dw1 > 15) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dw1 =3D 15;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } else if (dw1 < -15) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dw1 =3D -15;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* 0x3f was found by trial and e= rror vs ubuntu instance */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 b =3D (dw1 & 0x3f) | 0x40;

Ok, clamp at 15 (I think you could go at= 31 actually, since 5 bits seem to be used) and go to the kernel:
case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */
=
This case doesn't handle buttons simultaneously indeed.<= /div>

I think 0x3f comes from 5 bits + 1 sign bit.
<= /div>

+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->mouse_dw -=3D dw1;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (dz1 > 7) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dz1 =3D 7;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } else if (dz1 < -7) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dz1 =3D -7;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 b =3D (dz1 & 0x0f) | ((s->= ;mouse_buttons & 0x18) << 1);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->mouse_dz -=3D dz1;

Here clamp at 7, since we should fall in t= he kernel
case 0x00:

and only 3 = bits seem to be used (thus & 0x0f for 3+1 sign).

This case handles buttons simultaneously, but only vertical scroll (unle= ss a4tech_workaround is set and triggered)


=C2=A0
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ps2_queue_noirq(&s->common, b); =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;
=C2=A0 =C2=A0 =C2=A0}
@@ -764,7 +794,6 @@ static int ps2_mouse_send_packet(PS2MouseState *s)
=C2=A0 =C2=A0 =C2=A0/* update deltas */
=C2=A0 =C2=A0 =C2=A0s->mouse_dx -=3D dx1;
=C2=A0 =C2=A0 =C2=A0s->mouse_dy -=3D dy1;
-=C2=A0 =C2=A0 s->mouse_dz -=3D dz1;

=C2=A0 =C2=A0 =C2=A0return 1;
=C2=A0}
@@ -806,6 +835,12 @@ static void ps2_mouse_event(DeviceState *dev, QemuCons= ole *src,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} else if (btn->button = =3D=3D INPUT_BUTTON_WHEEL_DOWN) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->mouse_d= z++;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (btn->button =3D=3D INPUT_= BUTTON_WHEEL_RIGHT) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->mouse_dw--;<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } else if (btn->button =3D=3D= INPUT_BUTTON_WHEEL_LEFT) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->mouse_dw++;<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} else {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->mouse_buttons &= =3D ~bmap[btn->button];
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
@@ -833,8 +868,10 @@ static void ps2_mouse_sync(DeviceState *dev)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* if not remote, send event. Multiple ev= ents are sent if
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 too big deltas */
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0while (ps2_mouse_send_packet(s)) {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (s->mouse_dx =3D=3D 0 &= ;& s->mouse_dy =3D=3D 0 && s->mouse_dz =3D=3D 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (s->mouse_dx =3D=3D 0 &= ;& s->mouse_dy =3D=3D 0
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &= ;& s->mouse_dz =3D=3D 0 && s->mouse_dw =3D=3D 0) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
=C2=A0 =C2=A0 =C2=A0}
=C2=A0}
@@ -1036,6 +1073,7 @@ static void ps2_mouse_reset(void *opaque)
=C2=A0 =C2=A0 =C2=A0s->mouse_dx =3D 0;
=C2=A0 =C2=A0 =C2=A0s->mouse_dy =3D 0;
=C2=A0 =C2=A0 =C2=A0s->mouse_dz =3D 0;
+=C2=A0 =C2=A0 s->mouse_dw =3D 0;
=C2=A0 =C2=A0 =C2=A0s->mouse_buttons =3D 0;
=C2=A0}

diff --git a/qapi/ui.json b/qapi/ui.json
index d7567ac866..9dac1bf548 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -905,7 +905,7 @@
=C2=A0##
=C2=A0{ 'enum'=C2=A0 : 'InputButton',
=C2=A0 =C2=A0'data'=C2=A0 : [ 'left', 'middle', = 9;right', 'wheel-up', 'wheel-down', 'side',
-=C2=A0 'extra' ] }
+=C2=A0 'extra', 'wheel-left', 'wheel-right' ] }
=C2=A0##
=C2=A0# @InputAxis:
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 68a6302184..c898a8aeaa 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -970,21 +970,27 @@ QemuCocoaView *cocoaView;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 */

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* When deltaY is zero, it = means that this scrolling event was
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* either horizontal, or so= fine that it only appears in
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* scrollingDeltaY. So we d= rop the event.
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* We shouldn't have go= t a scroll event when deltaY and delta Y
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* are zero, hence no harm = in dropping the event
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 */
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if ([event deltaY] !=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if ([event deltaY] !=3D 0 || [ev= ent deltaX] !=3D 0) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Determine if this is a s= croll up or scroll down event */
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buttons =3D ([even= t deltaY] > 0) ?
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if ([event deltaY]= !=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buttons =3D= ([event deltaY] > 0) ?
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } else if ([event = deltaX] !=3D 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buttons =3D= ([event deltaX] > 0) ?
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 INPU= T_BUTTON_WHEEL_LEFT : INPUT_BUTTON_WHEEL_RIGHT;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_input_qu= eue_btn(dcl.con, buttons, true);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_input_ev= ent_sync();
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_input_qu= eue_btn(dcl.con, buttons, false);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0qemu_input_ev= ent_sync();
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
+
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Since deltaY also report= s scroll wheel events we prevent mouse
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* Since deltaY/deltaY also= report scroll wheel events we prevent mouse
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * movement code from execu= ting.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 */
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0mouse_event =3D false;
diff --git a/ui/gtk.c b/ui/gtk.c
index 428f02f2df..b52eec6fe9 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -963,33 +963,63 @@ static gboolean gd_scroll_event(GtkWidget *widget, Gd= kEventScroll *scroll,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0void *opaque)
=C2=A0{
=C2=A0 =C2=A0 =C2=A0VirtualConsole *vc =3D opaque;
-=C2=A0 =C2=A0 InputButton btn;
+=C2=A0 =C2=A0 InputButton btn_vertical;
+=C2=A0 =C2=A0 InputButton btn_horizontal;
+=C2=A0 =C2=A0 bool has_vertical =3D false;
+=C2=A0 =C2=A0 bool has_horizontal =3D false;

=C2=A0 =C2=A0 =C2=A0if (scroll->direction =3D=3D GDK_SCROLL_UP) {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 btn =3D INPUT_BUTTON_WHEEL_UP;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 btn_vertical =3D INPUT_BUTTON_WHEEL_UP;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 has_vertical =3D true;
=C2=A0 =C2=A0 =C2=A0} else if (scroll->direction =3D=3D GDK_SCROLL_DOWN)= {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 btn =3D INPUT_BUTTON_WHEEL_DOWN;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 btn_vertical =3D INPUT_BUTTON_WHEEL_DOWN;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 has_vertical =3D true;
+=C2=A0 =C2=A0 } else if (scroll->direction =3D=3D GDK_SCROLL_LEFT) { +=C2=A0 =C2=A0 =C2=A0 =C2=A0 btn_horizontal =3D INPUT_BUTTON_WHEEL_LEFT; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 has_horizontal =3D true;
+=C2=A0 =C2=A0 } else if (scroll->direction =3D=3D GDK_SCROLL_RIGHT) { +=C2=A0 =C2=A0 =C2=A0 =C2=A0 btn_horizontal =3D INPUT_BUTTON_WHEEL_RIGHT; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 has_horizontal =3D true;
=C2=A0 =C2=A0 =C2=A0} else if (scroll->direction =3D=3D GDK_SCROLL_SMOOT= H) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0gdouble delta_x, delta_y;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!gdk_event_get_scroll_deltas((GdkEven= t *)scroll,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &= amp;delta_x, &delta_y)) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return TRUE;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (delta_y =3D=3D 0) {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return TRUE;
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 } else if (delta_y > 0) {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 btn =3D INPUT_BUTTON_WHEEL_DOWN;=
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (delta_y > 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 btn_vertical =3D INPUT_BUTTON_WH= EEL_DOWN;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 has_vertical =3D true;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 } else if (delta_y < 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 btn_vertical =3D INPUT_BUTTON_WH= EEL_UP;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 has_vertical =3D true;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 } else if (delta_x > 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 btn_horizontal =3D INPUT_BUTTON_= WHEEL_RIGHT;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 has_horizontal =3D true;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 } else if (delta_x < 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 btn_horizontal =3D INPUT_BUTTON_= WHEEL_LEFT;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 has_horizontal =3D true;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} else {
-=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 btn =3D INPUT_BUTTON_WHEEL_UP; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return TRUE;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
=C2=A0 =C2=A0 =C2=A0} else {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return TRUE;
=C2=A0 =C2=A0 =C2=A0}

-=C2=A0 =C2=A0 qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
-=C2=A0 =C2=A0 qemu_input_event_sync();
-=C2=A0 =C2=A0 qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
-=C2=A0 =C2=A0 qemu_input_event_sync();
+=C2=A0 =C2=A0 if (has_vertical) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_input_queue_btn(vc->gfx.dcl.con, btn_v= ertical, true);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_input_event_sync();
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_input_queue_btn(vc->gfx.dcl.con, btn_v= ertical, false);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_input_event_sync();
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (has_horizontal) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_input_queue_btn(vc->gfx.dcl.con, btn_h= orizontal, true);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_input_event_sync();
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_input_queue_btn(vc->gfx.dcl.con, btn_h= orizontal, false);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_input_event_sync();
+=C2=A0 =C2=A0 }
+
=C2=A0 =C2=A0 =C2=A0return TRUE;
=C2=A0}

diff --git a/ui/input-legacy.c b/ui/input-legacy.c
index 9fc78a639b..2c9a215d7f 100644
--- a/ui/input-legacy.c
+++ b/ui/input-legacy.c
@@ -23,6 +23,7 @@
=C2=A0 */

=C2=A0#include "qemu/osdep.h"
+#include "qemu/log.h"
=C2=A0#include "qapi/qapi-commands-ui.h"
=C2=A0#include "ui/console.h"
=C2=A0#include "keymaps.h"
@@ -179,6 +180,20 @@ static void legacy_mouse_event(DeviceState *dev, QemuC= onsole *src,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A01,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s->buttons);<= br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (btn->down && btn->button =3D= =3D INPUT_BUTTON_WHEEL_RIGHT) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->qemu_put_mouse_event(s->= ;qemu_put_mouse_event_opaque,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->axis[INPUT_AXIS_= X],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->axis[INPUT_AXIS_= Y],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 -2,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->buttons);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (btn->down && btn->button =3D= =3D INPUT_BUTTON_WHEEL_LEFT) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->qemu_put_mouse_event(s->= ;qemu_put_mouse_event_opaque,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->axis[INPUT_AXIS_= X],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->axis[INPUT_AXIS_= Y],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 2,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s->buttons);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;
=C2=A0 =C2=A0 =C2=A0case INPUT_EVENT_KIND_ABS:
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0move =3D evt->u.abs.data;
@@ -216,6 +231,7 @@ QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPut= MouseEvent *func,
=C2=A0 =C2=A0 =C2=A0QEMUPutMouseEntry *s;

=C2=A0 =C2=A0 =C2=A0s =3D g_new0(QEMUPutMouseEntry, 1);
+=C2=A0 =C2=A0 qemu_log("qemu_add_mouse_event_handler %s", name);=

=C2=A0 =C2=A0 =C2=A0s->qemu_put_mouse_event =3D func;
=C2=A0 =C2=A0 =C2=A0s->qemu_put_mouse_event_opaque =3D opaque;
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 17c0ec30eb..19bbc1fdd4 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -33,6 +33,7 @@
=C2=A0#include "sysemu/runstate-action.h"
=C2=A0#include "sysemu/sysemu.h"
=C2=A0#include "ui/win32-kbd-hook.h"
+#include "qemu/log.h"

=C2=A0static int sdl2_num_outputs;
=C2=A0static struct sdl2_console *sdl2_console;
@@ -535,6 +536,10 @@ static void handle_mousewheel(SDL_Event *ev)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0btn =3D INPUT_BUTTON_WHEEL_UP;
=C2=A0 =C2=A0 =C2=A0} else if (wev->y < 0) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0btn =3D INPUT_BUTTON_WHEEL_DOWN;
+=C2=A0 =C2=A0 } else if (wev->x < 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 btn =3D INPUT_BUTTON_WHEEL_RIGHT;
+=C2=A0 =C2=A0 } else if (wev->x > 0) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 btn =3D INPUT_BUTTON_WHEEL_LEFT;
=C2=A0 =C2=A0 =C2=A0} else {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return;
=C2=A0 =C2=A0 =C2=A0}
--
2.32.0




--
Marc-Andr=C3=A9 Lureau
--0000000000000ac0aa05d3a43a32--