qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PULL 00/19] Ui patches
@ 2023-05-28 13:19 marcandre.lureau
  2023-05-28 17:43 ` Michael Tokarev
  2023-05-29 21:29 ` Richard Henderson
  0 siblings, 2 replies; 27+ messages in thread
From: marcandre.lureau @ 2023-05-28 13:19 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, berrange, Eric Blake, Stefan Weil,
	Gerd Hoffmann, Paolo Bonzini, Pavel Dovgalyuk,
	Marc-André Lureau, Michael S. Tsirkin

From: Marc-André Lureau <marcandre.lureau@redhat.com>

The following changes since commit ac84b57b4d74606f7f83667a0606deef32b2049d:

  Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging (2023-05-26 14:40:55 -0700)

are available in the Git repository at:

  https://gitlab.com/marcandre.lureau/qemu.git tags/ui-pull-request

for you to fetch changes up to 5a4cb61ae1ab0068ab53535ed0ccaf41a5e97d2f:

  ui/gtk: enable backend to send multi-touch events (2023-05-28 16:25:38 +0400)

----------------------------------------------------------------
UI queue

- virtio: add virtio-multitouch device
- sdl: various keyboard grab fixes
- gtk: enable multi-touch events
- misc fixes

----------------------------------------------------------------

Bernhard Beschow (2):
  ui/sdl2: Grab Alt+Tab also in fullscreen mode
  ui/sdl2: Grab Alt+F4 also under Windows

Erico Nunes (3):
  ui/gtk: fix passing y0_top parameter to scanout
  ui/gtk: use widget size for cursor motion event
  ui/gtk-egl: fix scaling for cursor position in scanout mode

Marc-André Lureau (6):
  ui/sdl2: fix surface_gl_update_texture: Assertion 'gls' failed
  ui/dbus: fix compilation when GBM && !OPENGL
  win32: wrap socket close() with an exception handler
  virtio-gpu: add a FIXME for virtio_gpu_load()
  gtk: add gl-area support on win32
  ui/dbus: add a FIXME about texture/dmabuf scanout handling

Mauro Matteo Cascella (1):
  ui/cursor: make width/height unsigned 16-bit integer

Sergio Lopez (6):
  virtio-input: generalize virtio_input_key_config()
  ui: add the infrastructure to support MT events
  virtio-input: add a virtio-mulitouch device
  virtio-input-pci: add virtio-multitouch-pci
  ui: add helpers for virtio-multitouch events
  ui/gtk: enable backend to send multi-touch events

Volker Rümelin (1):
  ui/sdl2: disable SDL_HINT_GRAB_KEYBOARD on Windows

 qapi/ui.json                     |  68 ++++++++++++--
 include/hw/virtio/virtio-input.h |   9 +-
 include/sysemu/os-win32.h        |   4 +
 include/ui/console.h             |   4 +-
 include/ui/input.h               |   8 ++
 hw/display/virtio-gpu.c          |   1 +
 hw/input/virtio-input-hid.c      | 156 +++++++++++++++++++++++++++----
 hw/virtio/virtio-input-pci.c     |  25 ++++-
 replay/replay-input.c            |  18 ++++
 ui/cursor.c                      |   3 +-
 ui/dbus-listener.c               |  15 ++-
 ui/gtk-egl.c                     |   6 +-
 ui/gtk-gl-area.c                 |   2 +-
 ui/gtk.c                         | 106 ++++++++++++++++++++-
 ui/input.c                       |  42 +++++++++
 ui/sdl2-gl.c                     |   4 +
 ui/sdl2.c                        |   7 ++
 util/oslib-win32.c               |  23 +++--
 ui/trace-events                  |   1 +
 19 files changed, 442 insertions(+), 60 deletions(-)

-- 
2.40.1



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PULL 00/19] Ui patches
  2023-05-28 13:19 marcandre.lureau
@ 2023-05-28 17:43 ` Michael Tokarev
  2023-05-29 21:29 ` Richard Henderson
  1 sibling, 0 replies; 27+ messages in thread
From: Michael Tokarev @ 2023-05-28 17:43 UTC (permalink / raw)
  To: marcandre.lureau, qemu-devel
  Cc: Markus Armbruster, berrange, Eric Blake, Stefan Weil,
	Gerd Hoffmann, Paolo Bonzini, Pavel Dovgalyuk, Michael S. Tsirkin,
	Erico Nunes, Marc-André Lureau, Bernhard Beschow,
	Volker Rümelin

28.05.2023 16:19, marcandre.lureau@redhat.com wrote:
> 
>    https://gitlab.com/marcandre.lureau/qemu.git tags/ui-pull-request
> 
> ----------------------------------------------------------------
> UI queue
> 
> - virtio: add virtio-multitouch device
> - sdl: various keyboard grab fixes
> - gtk: enable multi-touch events
> - misc fixes

 From the patch description it looks like quite a few patches should be
applied to -stable:

01/19 ui/gtk: fix passing y0_top parameter to scanout
02/19 ui/gtk: use widget size for cursor motion event
03/19 ui/gtk-egl: fix scaling for cursor position in scanout mode
04/19 ui/sdl2: fix surface_gl_update_texture: Assertion 'gls' failed
05/19 ui/dbus: fix compilation when GBM && !OPENGL
10/19 ui/sdl2: Grab Alt+Tab also in fullscreen mode
11/19 ui/sdl2: Grab Alt+F4 also under Windows
12/19 ui/sdl2: disable SDL_HINT_GRAB_KEYBOARD on Windows

This one is questionable because while it (apparently) fixes an issue,
it uses somewhat undocumented way to do that:

06/19 win32: wrap socket close() with an exception handler

Am I right about the -stable thing?

Please add "Cc: qemu-stable@nongnu.org" tag for subsequent patches
which you think are worth to backport to previous/stable qemu release(s).

Thanks!

/mjt

> ----------------------------------------------------------------
> 
> Bernhard Beschow (2):
>    ui/sdl2: Grab Alt+Tab also in fullscreen mode
>    ui/sdl2: Grab Alt+F4 also under Windows
> 
> Erico Nunes (3):
>    ui/gtk: fix passing y0_top parameter to scanout
>    ui/gtk: use widget size for cursor motion event
>    ui/gtk-egl: fix scaling for cursor position in scanout mode
> 
> Marc-André Lureau (6):
>    ui/sdl2: fix surface_gl_update_texture: Assertion 'gls' failed
>    ui/dbus: fix compilation when GBM && !OPENGL
>    win32: wrap socket close() with an exception handler
>    virtio-gpu: add a FIXME for virtio_gpu_load()
>    gtk: add gl-area support on win32
>    ui/dbus: add a FIXME about texture/dmabuf scanout handling
> 
> Mauro Matteo Cascella (1):
>    ui/cursor: make width/height unsigned 16-bit integer
> 
> Sergio Lopez (6):
>    virtio-input: generalize virtio_input_key_config()
>    ui: add the infrastructure to support MT events
>    virtio-input: add a virtio-mulitouch device
>    virtio-input-pci: add virtio-multitouch-pci
>    ui: add helpers for virtio-multitouch events
>    ui/gtk: enable backend to send multi-touch events
> 
> Volker Rümelin (1):
>    ui/sdl2: disable SDL_HINT_GRAB_KEYBOARD on Windows
> 




^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PULL 00/19] Ui patches
  2023-05-28 13:19 marcandre.lureau
  2023-05-28 17:43 ` Michael Tokarev
@ 2023-05-29 21:29 ` Richard Henderson
  1 sibling, 0 replies; 27+ messages in thread
From: Richard Henderson @ 2023-05-29 21:29 UTC (permalink / raw)
  To: marcandre.lureau, qemu-devel
  Cc: Markus Armbruster, berrange, Eric Blake, Stefan Weil,
	Gerd Hoffmann, Paolo Bonzini, Pavel Dovgalyuk, Michael S. Tsirkin

On 5/28/23 06:19, marcandre.lureau@redhat.com wrote:
> From: Marc-André Lureau<marcandre.lureau@redhat.com>
> 
> The following changes since commit ac84b57b4d74606f7f83667a0606deef32b2049d:
> 
>    Merge tag 'for-upstream' ofhttps://gitlab.com/bonzini/qemu  into staging (2023-05-26 14:40:55 -0700)
> 
> are available in the Git repository at:
> 
>    https://gitlab.com/marcandre.lureau/qemu.git  tags/ui-pull-request
> 
> for you to fetch changes up to 5a4cb61ae1ab0068ab53535ed0ccaf41a5e97d2f:
> 
>    ui/gtk: enable backend to send multi-touch events (2023-05-28 16:25:38 +0400)
> 
> ----------------------------------------------------------------
> UI queue
> 
> - virtio: add virtio-multitouch device
> - sdl: various keyboard grab fixes
> - gtk: enable multi-touch events
> - misc fixes

Applied, thanks.  Please update https://wiki.qemu.org/ChangeLog/8.1 as appropriate.


r~



^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PULL 00/19] Ui patches
@ 2025-05-24 17:34 marcandre.lureau
  2025-05-24 17:34 ` [PULL 01/19] ui/gtk: warn if setting the clipboard failed marcandre.lureau
                   ` (19 more replies)
  0 siblings, 20 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha

From: Marc-André Lureau <marcandre.lureau@redhat.com>

The following changes since commit 668df86ee8076152320345d8e36be7c95ec0a09a:

  Merge tag 'for-upstream' of https://repo.or.cz/qemu/kevin into staging (2025-05-23 09:26:29 -0400)

are available in the Git repository at:

  https://gitlab.com/marcandre.lureau/qemu.git tags/ui-pull-request

for you to fetch changes up to f05e1a93f48729b568fdf86a12d56ee142cee5e1:

  ui/gtk-egl: Render guest content with padding in fixed-scale mode (2025-05-24 17:04:09 +0200)

----------------------------------------------------------------
UI-related

- vdagent migration support
- gtk: improve scale handling

----------------------------------------------------------------

Marc-André Lureau (10):
  ui/gtk: warn if setting the clipboard failed
  ui/clipboard: use int for selection field
  ui/clipboard: split out QemuClipboardContent
  ui/clipboard: add vmstate_cbinfo
  ui/clipboard: delay clipboard update when not running
  ui/vdagent: replace Buffer with GByteArray
  ui/vdagent: keep "connected" state
  ui/vdagent: factor out clipboard peer registration
  ui/vdagent: add migration support
  ui/vdagent: remove migration blocker

Weifeng Liu (9):
  ui/gtk: Document scale and coordinate handling
  ui/gtk: Use consistent naming for variables in different coordinates
  gtk/ui: Introduce helper gd_update_scale
  ui/gtk: Update scales in fixed-scale mode when rendering GL area
  ui/sdl: Consider scaling in mouse event handling
  ui/gtk: Don't update scale in fixed scale mode in gtk-egl.c
  ui/gtk: Consider scaling when propagating ui info
  ui/gtk-gl-area: Render guest content with padding in fixed-scale mode
  ui/gtk-egl: Render guest content with padding in fixed-scale mode

 include/ui/clipboard.h   |  31 +++--
 include/ui/egl-helpers.h |   4 +-
 include/ui/gtk.h         |   2 +
 ui/clipboard.c           |  66 ++++++++++-
 ui/egl-helpers.c         |  10 +-
 ui/gtk-clipboard.c       |  13 ++-
 ui/gtk-egl.c             |  58 ++++++---
 ui/gtk-gl-area.c         |  53 +++++++--
 ui/gtk.c                 | 245 +++++++++++++++++++++++++++------------
 ui/sdl2-gl.c             |   2 +-
 ui/sdl2.c                |  20 +++-
 ui/vdagent.c             | 205 +++++++++++++++++++++++++++-----
 12 files changed, 561 insertions(+), 148 deletions(-)

-- 
2.49.0



^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PULL 01/19] ui/gtk: warn if setting the clipboard failed
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
@ 2025-05-24 17:34 ` marcandre.lureau
  2025-05-24 17:34 ` [PULL 02/19] ui/clipboard: use int for selection field marcandre.lureau
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Just in case.
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/gtk-clipboard.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/ui/gtk-clipboard.c b/ui/gtk-clipboard.c
index 8d8a636fd1..65d89ec601 100644
--- a/ui/gtk-clipboard.c
+++ b/ui/gtk-clipboard.c
@@ -19,6 +19,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/error-report.h"
 #include "qemu/main-loop.h"
 
 #include "ui/gtk.h"
@@ -95,11 +96,13 @@ static void gd_clipboard_update_info(GtkDisplayState *gd,
             gtk_clipboard_clear(gd->gtkcb[s]);
             if (targets) {
                 gd->cbowner[s] = true;
-                gtk_clipboard_set_with_data(gd->gtkcb[s],
-                                            targets, n_targets,
-                                            gd_clipboard_get_data,
-                                            gd_clipboard_clear,
-                                            gd);
+                if (!gtk_clipboard_set_with_data(gd->gtkcb[s],
+                                                 targets, n_targets,
+                                                 gd_clipboard_get_data,
+                                                 gd_clipboard_clear,
+                                                 gd)) {
+                    warn_report("Failed to set GTK clipboard");
+                }
 
                 gtk_target_table_free(targets, n_targets);
             }
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 02/19] ui/clipboard: use int for selection field
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
  2025-05-24 17:34 ` [PULL 01/19] ui/gtk: warn if setting the clipboard failed marcandre.lureau
@ 2025-05-24 17:34 ` marcandre.lureau
  2025-05-24 17:34 ` [PULL 03/19] ui/clipboard: split out QemuClipboardContent marcandre.lureau
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha

From: Marc-André Lureau <marcandre.lureau@redhat.com>

This allows to use a VMSTATE_INT32 field for migration purposes.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 include/ui/clipboard.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h
index ab6acdbd8a..14b6099e73 100644
--- a/include/ui/clipboard.h
+++ b/include/ui/clipboard.h
@@ -112,7 +112,7 @@ struct QemuClipboardNotify {
 struct QemuClipboardInfo {
     uint32_t refcount;
     QemuClipboardPeer *owner;
-    QemuClipboardSelection selection;
+    int selection; /* QemuClipboardSelection */
     bool has_serial;
     uint32_t serial;
     struct {
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 03/19] ui/clipboard: split out QemuClipboardContent
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
  2025-05-24 17:34 ` [PULL 01/19] ui/gtk: warn if setting the clipboard failed marcandre.lureau
  2025-05-24 17:34 ` [PULL 02/19] ui/clipboard: use int for selection field marcandre.lureau
@ 2025-05-24 17:34 ` marcandre.lureau
  2025-05-24 17:34 ` [PULL 04/19] ui/clipboard: add vmstate_cbinfo marcandre.lureau
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Allows to use VMSTATE STRUCT in following migration support patch.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 include/ui/clipboard.h | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h
index 14b6099e73..88cfff91ef 100644
--- a/include/ui/clipboard.h
+++ b/include/ui/clipboard.h
@@ -25,6 +25,7 @@ typedef enum QemuClipboardSelection QemuClipboardSelection;
 typedef struct QemuClipboardPeer QemuClipboardPeer;
 typedef struct QemuClipboardNotify QemuClipboardNotify;
 typedef struct QemuClipboardInfo QemuClipboardInfo;
+typedef struct QemuClipboardContent QemuClipboardContent;
 
 /**
  * enum QemuClipboardType
@@ -97,6 +98,24 @@ struct QemuClipboardNotify {
     };
 };
 
+
+/**
+ * struct QemuClipboardContent
+ *
+ * @available: whether the data is available
+ * @requested: whether the data was requested
+ * @size: the size of the @data
+ * @data: the clipboard data
+ *
+ * Clipboard content.
+ */
+struct QemuClipboardContent {
+    bool available;
+    bool requested;
+    uint32_t size;
+    void *data;
+};
+
 /**
  * struct QemuClipboardInfo
  *
@@ -115,12 +134,7 @@ struct QemuClipboardInfo {
     int selection; /* QemuClipboardSelection */
     bool has_serial;
     uint32_t serial;
-    struct {
-        bool available;
-        bool requested;
-        size_t size;
-        void *data;
-    } types[QEMU_CLIPBOARD_TYPE__COUNT];
+    QemuClipboardContent types[QEMU_CLIPBOARD_TYPE__COUNT];
 };
 
 /**
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 04/19] ui/clipboard: add vmstate_cbinfo
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (2 preceding siblings ...)
  2025-05-24 17:34 ` [PULL 03/19] ui/clipboard: split out QemuClipboardContent marcandre.lureau
@ 2025-05-24 17:34 ` marcandre.lureau
  2025-05-24 17:34 ` [PULL 05/19] ui/clipboard: delay clipboard update when not running marcandre.lureau
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Add a VMStateDescriptor for QemuClipboardInfo.

Each clipboard owner will have to save its QemuClipboardInfo and
reregister its owned clipboard after loading. (the global cbinfo has
only pointers to owners, so it can't restore the relation with its owner
if it was to handle migration)

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 include/ui/clipboard.h |  3 +++
 ui/clipboard.c         | 26 ++++++++++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h
index 88cfff91ef..62a96ce9ff 100644
--- a/include/ui/clipboard.h
+++ b/include/ui/clipboard.h
@@ -2,6 +2,7 @@
 #define QEMU_CLIPBOARD_H
 
 #include "qemu/notify.h"
+#include "migration/vmstate.h"
 
 /**
  * DOC: Introduction
@@ -27,6 +28,8 @@ typedef struct QemuClipboardNotify QemuClipboardNotify;
 typedef struct QemuClipboardInfo QemuClipboardInfo;
 typedef struct QemuClipboardContent QemuClipboardContent;
 
+extern const VMStateDescription vmstate_cbinfo;
+
 /**
  * enum QemuClipboardType
  *
diff --git a/ui/clipboard.c b/ui/clipboard.c
index 132086eb13..f5db60c63d 100644
--- a/ui/clipboard.c
+++ b/ui/clipboard.c
@@ -7,6 +7,32 @@ static NotifierList clipboard_notifiers =
 
 static QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT];
 
+static const VMStateDescription vmstate_cbcontent = {
+    .name = "clipboard/content",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (const VMStateField[]) {
+        VMSTATE_BOOL(available, QemuClipboardContent),
+        VMSTATE_BOOL(requested, QemuClipboardContent),
+        VMSTATE_UINT32(size, QemuClipboardContent),
+        VMSTATE_VBUFFER_ALLOC_UINT32(data, QemuClipboardContent, 0, 0, size),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+const VMStateDescription vmstate_cbinfo = {
+    .name = "clipboard",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (const VMStateField[]) {
+        VMSTATE_INT32(selection, QemuClipboardInfo),
+        VMSTATE_BOOL(has_serial, QemuClipboardInfo),
+        VMSTATE_UINT32(serial, QemuClipboardInfo),
+        VMSTATE_STRUCT_ARRAY(types, QemuClipboardInfo, QEMU_CLIPBOARD_TYPE__COUNT, 0, vmstate_cbcontent, QemuClipboardContent),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 void qemu_clipboard_peer_register(QemuClipboardPeer *peer)
 {
     notifier_list_add(&clipboard_notifiers, &peer->notifier);
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 05/19] ui/clipboard: delay clipboard update when not running
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (3 preceding siblings ...)
  2025-05-24 17:34 ` [PULL 04/19] ui/clipboard: add vmstate_cbinfo marcandre.lureau
@ 2025-05-24 17:34 ` marcandre.lureau
  2025-05-24 17:34 ` [PULL 06/19] ui/vdagent: replace Buffer with GByteArray marcandre.lureau
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha

From: Marc-André Lureau <marcandre.lureau@redhat.com>

When VM is paused, we shouldn't notify of clipboard changes, similar to
how input are being treated.

On unsuspend, notify of the current state.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 ui/clipboard.c | 40 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/ui/clipboard.c b/ui/clipboard.c
index f5db60c63d..ec00a0b8ec 100644
--- a/ui/clipboard.c
+++ b/ui/clipboard.c
@@ -1,4 +1,5 @@
 #include "qemu/osdep.h"
+#include "system/runstate.h"
 #include "ui/clipboard.h"
 #include "trace.h"
 
@@ -7,6 +8,10 @@ static NotifierList clipboard_notifiers =
 
 static QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT];
 
+static VMChangeStateEntry *cb_change_state_entry = NULL;
+
+static bool cb_reset_serial_on_resume = false;
+
 static const VMStateDescription vmstate_cbcontent = {
     .name = "clipboard/content",
     .version_id = 0,
@@ -33,8 +38,32 @@ const VMStateDescription vmstate_cbinfo = {
     }
 };
 
+static void qemu_clipboard_change_state(void *opaque, bool running, RunState state)
+{
+    int i;
+
+    if (!running) {
+        return;
+    }
+
+    if (cb_reset_serial_on_resume) {
+        qemu_clipboard_reset_serial();
+    }
+
+    for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) {
+        if (cbinfo[i]) {
+            qemu_clipboard_update(cbinfo[i]);
+        }
+    }
+
+}
+
 void qemu_clipboard_peer_register(QemuClipboardPeer *peer)
 {
+    if (cb_change_state_entry == NULL) {
+        cb_change_state_entry = qemu_add_vm_change_state_handler(qemu_clipboard_change_state, NULL);
+    }
+
     notifier_list_add(&clipboard_notifiers, &peer->notifier);
 }
 
@@ -109,7 +138,9 @@ void qemu_clipboard_update(QemuClipboardInfo *info)
         }
     }
 
-    notifier_list_notify(&clipboard_notifiers, &notify);
+    if (runstate_is_running() || runstate_check(RUN_STATE_SUSPENDED)) {
+        notifier_list_notify(&clipboard_notifiers, &notify);
+    }
 
     if (cbinfo[info->selection] != info) {
         qemu_clipboard_info_unref(cbinfo[info->selection]);
@@ -189,7 +220,12 @@ void qemu_clipboard_reset_serial(void)
             info->serial = 0;
         }
     }
-    notifier_list_notify(&clipboard_notifiers, &notify);
+
+    if (runstate_is_running() || runstate_check(RUN_STATE_SUSPENDED)) {
+        notifier_list_notify(&clipboard_notifiers, &notify);
+    } else {
+        cb_reset_serial_on_resume = true;
+    }
 }
 
 void qemu_clipboard_set_data(QemuClipboardPeer *peer,
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 06/19] ui/vdagent: replace Buffer with GByteArray
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (4 preceding siblings ...)
  2025-05-24 17:34 ` [PULL 05/19] ui/clipboard: delay clipboard update when not running marcandre.lureau
@ 2025-05-24 17:34 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 07/19] ui/vdagent: keep "connected" state marcandre.lureau
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Buffer is slightly more advanced than GByteArray, since it has a
cursor/position. But vdagent code doesn't need it. This simplify a bit
the code, and migration state.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 ui/vdagent.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/ui/vdagent.c b/ui/vdagent.c
index 04513ded29..4027126b7d 100644
--- a/ui/vdagent.c
+++ b/ui/vdagent.c
@@ -47,7 +47,7 @@ struct VDAgentChardev {
     uint32_t msgsize;
     uint8_t *xbuf;
     uint32_t xoff, xsize;
-    Buffer outbuf;
+    GByteArray *outbuf;
 
     /* mouse */
     DeviceState mouse_dev;
@@ -142,16 +142,16 @@ static void vdagent_send_buf(VDAgentChardev *vd)
 {
     uint32_t len;
 
-    while (!buffer_empty(&vd->outbuf)) {
+    while (vd->outbuf->len) {
         len = qemu_chr_be_can_write(CHARDEV(vd));
         if (len == 0) {
             return;
         }
-        if (len > vd->outbuf.offset) {
-            len = vd->outbuf.offset;
+        if (len > vd->outbuf->len) {
+            len = vd->outbuf->len;
         }
-        qemu_chr_be_write(CHARDEV(vd), vd->outbuf.buffer, len);
-        buffer_advance(&vd->outbuf, len);
+        qemu_chr_be_write(CHARDEV(vd), vd->outbuf->data, len);
+        g_byte_array_remove_range(vd->outbuf, 0, len);
     }
 }
 
@@ -166,7 +166,7 @@ static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg)
 
     msg->protocol = VD_AGENT_PROTOCOL;
 
-    if (vd->outbuf.offset + msgsize > VDAGENT_BUFFER_LIMIT) {
+    if (vd->outbuf->len + msgsize > VDAGENT_BUFFER_LIMIT) {
         error_report("buffer full, dropping message");
         return;
     }
@@ -177,9 +177,8 @@ static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg)
         if (chunk.size > 1024) {
             chunk.size = 1024;
         }
-        buffer_reserve(&vd->outbuf, sizeof(chunk) + chunk.size);
-        buffer_append(&vd->outbuf, &chunk, sizeof(chunk));
-        buffer_append(&vd->outbuf, msgbuf + msgoff, chunk.size);
+        g_byte_array_append(vd->outbuf, (void *)&chunk, sizeof(chunk));
+        g_byte_array_append(vd->outbuf, msgbuf + msgoff, chunk.size);
         msgoff += chunk.size;
     }
     vdagent_send_buf(vd);
@@ -859,7 +858,7 @@ static void vdagent_disconnect(VDAgentChardev *vd)
 {
     trace_vdagent_disconnect();
 
-    buffer_reset(&vd->outbuf);
+    g_byte_array_set_size(vd->outbuf, 0);
     vdagent_reset_bufs(vd);
     vd->caps = 0;
     if (vd->mouse_hs) {
@@ -920,7 +919,7 @@ static void vdagent_chr_init(Object *obj)
 {
     VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj);
 
-    buffer_init(&vd->outbuf, "vdagent-outbuf");
+    vd->outbuf = g_byte_array_new();
     error_setg(&vd->migration_blocker,
                "The vdagent chardev doesn't yet support migration");
 }
@@ -934,7 +933,7 @@ static void vdagent_chr_fini(Object *obj)
     if (vd->mouse_hs) {
         qemu_input_handler_unregister(vd->mouse_hs);
     }
-    buffer_free(&vd->outbuf);
+    g_clear_pointer(&vd->outbuf, g_byte_array_unref);
 }
 
 static const TypeInfo vdagent_chr_type_info = {
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 07/19] ui/vdagent: keep "connected" state
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (5 preceding siblings ...)
  2025-05-24 17:34 ` [PULL 06/19] ui/vdagent: replace Buffer with GByteArray marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 08/19] ui/vdagent: factor out clipboard peer registration marcandre.lureau
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha

From: Marc-André Lureau <marcandre.lureau@redhat.com>

During post-load of migration, virtio will notify of fe_open state.
However vdagent code will handle this as a reconnection. This will
trigger a connection reset/caps with the agent.

Check if the state actually changed before resetting the connection.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 ui/vdagent.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/ui/vdagent.c b/ui/vdagent.c
index 4027126b7d..210b8c14ca 100644
--- a/ui/vdagent.c
+++ b/ui/vdagent.c
@@ -40,6 +40,7 @@ struct VDAgentChardev {
     bool clipboard;
 
     /* guest vdagent */
+    bool connected;
     uint32_t caps;
     VDIChunkHeader chunk;
     uint32_t chunksize;
@@ -858,6 +859,7 @@ static void vdagent_disconnect(VDAgentChardev *vd)
 {
     trace_vdagent_disconnect();
 
+    vd->connected = false;
     g_byte_array_set_size(vd->outbuf, 0);
     vdagent_reset_bufs(vd);
     vd->caps = 0;
@@ -876,6 +878,10 @@ static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open)
 
     trace_vdagent_fe_open(fe_open);
 
+    if (vd->connected == fe_open) {
+        return;
+    }
+
     if (!fe_open) {
         trace_vdagent_close();
         vdagent_disconnect(vd);
@@ -885,6 +891,7 @@ static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open)
         return;
     }
 
+    vd->connected = true;
     vdagent_send_caps(vd, true);
 }
 
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 08/19] ui/vdagent: factor out clipboard peer registration
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (6 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 07/19] ui/vdagent: keep "connected" state marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 09/19] ui/vdagent: add migration support marcandre.lureau
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha

From: Marc-André Lureau <marcandre.lureau@redhat.com>

This allows common code reuse during migration.

Note that resetting the serial is now done regardless if the clipboard
peer was registered or not. This should still be correct.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 ui/vdagent.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/ui/vdagent.c b/ui/vdagent.c
index 210b8c14ca..fcbd7b167b 100644
--- a/ui/vdagent.c
+++ b/ui/vdagent.c
@@ -694,6 +694,18 @@ static void vdagent_chr_open(Chardev *chr,
     *be_opened = true;
 }
 
+static void vdagent_clipboard_peer_register(VDAgentChardev *vd)
+{
+    if (vd->cbpeer.notifier.notify != NULL) {
+        return;
+    }
+
+    vd->cbpeer.name = "vdagent";
+    vd->cbpeer.notifier.notify = vdagent_clipboard_notify;
+    vd->cbpeer.request = vdagent_clipboard_request;
+    qemu_clipboard_peer_register(&vd->cbpeer);
+}
+
 static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg)
 {
     VDAgentAnnounceCapabilities *caps = (void *)msg->data;
@@ -720,13 +732,9 @@ static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg)
 
     memset(vd->last_serial, 0, sizeof(vd->last_serial));
 
-    if (have_clipboard(vd) && vd->cbpeer.notifier.notify == NULL) {
+    if (have_clipboard(vd)) {
         qemu_clipboard_reset_serial();
-
-        vd->cbpeer.name = "vdagent";
-        vd->cbpeer.notifier.notify = vdagent_clipboard_notify;
-        vd->cbpeer.request = vdagent_clipboard_request;
-        qemu_clipboard_peer_register(&vd->cbpeer);
+        vdagent_clipboard_peer_register(vd);
     }
 }
 
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 09/19] ui/vdagent: add migration support
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (7 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 08/19] ui/vdagent: factor out clipboard peer registration marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 10/19] ui/vdagent: remove migration blocker marcandre.lureau
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 ui/vdagent.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 142 insertions(+)

diff --git a/ui/vdagent.c b/ui/vdagent.c
index fcbd7b167b..adc8755bd9 100644
--- a/ui/vdagent.c
+++ b/ui/vdagent.c
@@ -10,6 +10,7 @@
 #include "ui/clipboard.h"
 #include "ui/console.h"
 #include "ui/input.h"
+#include "migration/vmstate.h"
 #include "trace.h"
 
 #include "qapi/qapi-types-char.h"
@@ -930,6 +931,146 @@ static void vdagent_chr_class_init(ObjectClass *oc, const void *data)
     cc->chr_accept_input = vdagent_chr_accept_input;
 }
 
+static int post_load(void *opaque, int version_id)
+{
+    VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(opaque);
+
+    if (have_mouse(vd) && vd->mouse_hs) {
+        qemu_input_handler_activate(vd->mouse_hs);
+    }
+
+    if (have_clipboard(vd)) {
+        vdagent_clipboard_peer_register(vd);
+    }
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_chunk = {
+    .name = "vdagent/chunk",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32(port, VDIChunkHeader),
+        VMSTATE_UINT32(size, VDIChunkHeader),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_vdba = {
+    .name = "vdagent/bytearray",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32(len, GByteArray),
+        VMSTATE_VBUFFER_ALLOC_UINT32(data, GByteArray, 0, 0, len),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+struct CBInfoArray {
+    uint32_t n;
+    QemuClipboardInfo cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT];
+};
+
+static const VMStateDescription vmstate_cbinfo_array = {
+    .name = "cbinfoarray",
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32(n, struct CBInfoArray),
+        VMSTATE_STRUCT_VARRAY_UINT32(cbinfo, struct CBInfoArray, n,
+                                     0, vmstate_cbinfo, QemuClipboardInfo),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int put_cbinfo(QEMUFile *f, void *pv, size_t size,
+                      const VMStateField *field, JSONWriter *vmdesc)
+{
+    VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(pv);
+    struct CBInfoArray cbinfo = {};
+    int i;
+
+    if (!have_clipboard(vd)) {
+        return 0;
+    }
+
+    for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) {
+        if (qemu_clipboard_peer_owns(&vd->cbpeer, i)) {
+            cbinfo.cbinfo[cbinfo.n++] = *qemu_clipboard_info(i);
+        }
+    }
+
+    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc);
+}
+
+static int get_cbinfo(QEMUFile *f, void *pv, size_t size,
+                      const VMStateField *field)
+{
+    VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(pv);
+    struct CBInfoArray cbinfo = {};
+    int i, ret;
+
+    if (!have_clipboard(vd)) {
+        return 0;
+    }
+
+    vdagent_clipboard_peer_register(vd);
+
+    ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0);
+    if (ret) {
+        return ret;
+    }
+
+    for (i = 0; i < cbinfo.n; i++) {
+        g_autoptr(QemuClipboardInfo) info =
+            qemu_clipboard_info_new(&vd->cbpeer, cbinfo.cbinfo[i].selection);
+        /* this will steal clipboard data pointer from cbinfo.types */
+        memcpy(info->types, cbinfo.cbinfo[i].types, sizeof(cbinfo.cbinfo[i].types));
+        qemu_clipboard_update(info);
+    }
+
+    return 0;
+}
+
+static const VMStateInfo vmstate_cbinfos = {
+    .name = "vdagent/cbinfos",
+    .get  = get_cbinfo,
+    .put  = put_cbinfo,
+};
+
+static const VMStateDescription vmstate_vdagent = {
+    .name = "vdagent",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .post_load = post_load,
+    .fields = (const VMStateField[]) {
+        VMSTATE_BOOL(connected, VDAgentChardev),
+        VMSTATE_UINT32(caps, VDAgentChardev),
+        VMSTATE_STRUCT(chunk, VDAgentChardev, 0, vmstate_chunk, VDIChunkHeader),
+        VMSTATE_UINT32(chunksize, VDAgentChardev),
+        VMSTATE_UINT32(msgsize, VDAgentChardev),
+        VMSTATE_VBUFFER_ALLOC_UINT32(msgbuf, VDAgentChardev, 0, 0, msgsize),
+        VMSTATE_UINT32(xsize, VDAgentChardev),
+        VMSTATE_UINT32(xoff, VDAgentChardev),
+        VMSTATE_VBUFFER_ALLOC_UINT32(xbuf, VDAgentChardev, 0, 0, xsize),
+        VMSTATE_STRUCT_POINTER(outbuf, VDAgentChardev, vmstate_vdba, GByteArray),
+        VMSTATE_UINT32(mouse_x, VDAgentChardev),
+        VMSTATE_UINT32(mouse_y, VDAgentChardev),
+        VMSTATE_UINT32(mouse_btn, VDAgentChardev),
+        VMSTATE_UINT32(mouse_display, VDAgentChardev),
+        VMSTATE_UINT32_ARRAY(last_serial, VDAgentChardev,
+                             QEMU_CLIPBOARD_SELECTION__COUNT),
+        VMSTATE_UINT32_ARRAY(cbpending, VDAgentChardev,
+                             QEMU_CLIPBOARD_SELECTION__COUNT),
+        {
+            .name         = "cbinfos",
+            .info         = &vmstate_cbinfos,
+            .flags        = VMS_SINGLE,
+        },
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static void vdagent_chr_init(Object *obj)
 {
     VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj);
@@ -937,6 +1078,7 @@ static void vdagent_chr_init(Object *obj)
     vd->outbuf = g_byte_array_new();
     error_setg(&vd->migration_blocker,
                "The vdagent chardev doesn't yet support migration");
+    vmstate_register_any(NULL, &vmstate_vdagent, vd);
 }
 
 static void vdagent_chr_fini(Object *obj)
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 10/19] ui/vdagent: remove migration blocker
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (8 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 09/19] ui/vdagent: add migration support marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 11/19] ui/gtk: Document scale and coordinate handling marcandre.lureau
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha

From: Marc-André Lureau <marcandre.lureau@redhat.com>

Fixes: https://issues.redhat.com/browse/RHEL-81894
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 ui/vdagent.c | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/ui/vdagent.c b/ui/vdagent.c
index adc8755bd9..c0746fe5b1 100644
--- a/ui/vdagent.c
+++ b/ui/vdagent.c
@@ -6,7 +6,6 @@
 #include "qemu/option.h"
 #include "qemu/units.h"
 #include "hw/qdev-core.h"
-#include "migration/blocker.h"
 #include "ui/clipboard.h"
 #include "ui/console.h"
 #include "ui/input.h"
@@ -33,9 +32,6 @@
 struct VDAgentChardev {
     Chardev parent;
 
-    /* TODO: migration isn't yet supported */
-    Error *migration_blocker;
-
     /* config */
     bool mouse;
     bool clipboard;
@@ -673,10 +669,6 @@ static void vdagent_chr_open(Chardev *chr,
     return;
 #endif
 
-    if (migrate_add_blocker(&vd->migration_blocker, errp) != 0) {
-        return;
-    }
-
     vd->mouse = VDAGENT_MOUSE_DEFAULT;
     if (cfg->has_mouse) {
         vd->mouse = cfg->mouse;
@@ -1076,8 +1068,6 @@ static void vdagent_chr_init(Object *obj)
     VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj);
 
     vd->outbuf = g_byte_array_new();
-    error_setg(&vd->migration_blocker,
-               "The vdagent chardev doesn't yet support migration");
     vmstate_register_any(NULL, &vmstate_vdagent, vd);
 }
 
@@ -1085,7 +1075,6 @@ static void vdagent_chr_fini(Object *obj)
 {
     VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj);
 
-    migrate_del_blocker(&vd->migration_blocker);
     vdagent_disconnect(vd);
     if (vd->mouse_hs) {
         qemu_input_handler_unregister(vd->mouse_hs);
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 11/19] ui/gtk: Document scale and coordinate handling
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (9 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 10/19] ui/vdagent: remove migration blocker marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 12/19] ui/gtk: Use consistent naming for variables in different coordinates marcandre.lureau
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha, Weifeng Liu

From: Weifeng Liu <weifeng.liu.z@gmail.com>

The existence of multiple scaling factors forces us to deal with various
coordinate systems and this would be confusing. It would be beneficial
to define the concepts clearly and use consistent representation for
variables in different coordinates.

Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
Message-ID: <20250511073337.876650-2-weifeng.liu.z@gmail.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/gtk.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/ui/gtk.c b/ui/gtk.c
index 982037b2c0..9f3171abc5 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -800,6 +800,71 @@ void gd_update_monitor_refresh_rate(VirtualConsole *vc, GtkWidget *widget)
 #endif
 }
 
+/**
+ * DOC: Coordinate handling.
+ *
+ * We are coping with sizes and positions in various coordinates and the
+ * handling of these coordinates is somewhat confusing. It would benefit us
+ * all if we define these coordinates explicitly and clearly. Besides, it's
+ * also helpful to follow the same naming convention for variables
+ * representing values in different coordinates.
+ *
+ * I. Definitions
+ *
+ * - (guest) buffer coordinate: this is the coordinates that the guest will
+ *   see. The x/y offsets and width/height specified in commands sent by
+ *   guest is basically in buffer coordinate.
+ *
+ * - (host) pixel coordinate: this is the coordinate in pixel level on the
+ *   host destop. A window/widget of width 300 in pixel coordinate means it
+ *   occupies 300 pixels horizontally.
+ *
+ * - (host) logical window coordinate: the existence of global scaling
+ *   factor in desktop level makes this kind of coordinate play a role. It
+ *   always holds that (logical window size) * (global scale factor) =
+ *   (pixel size).
+ *
+ * - global scale factor: this is specified in desktop level and is
+ *   typically invariant during the life cycle of the process. Users with
+ *   high-DPI monitors might set this scale, for example, to 2, in order to
+ *   make the UI look larger.
+ *
+ * - zooming scale: this can be freely controlled by the QEMU user to zoom
+ *   in/out the guest content.
+ *
+ * II. Representation
+ *
+ * We'd like to use consistent representation for variables in different
+ * coordinates:
+ * - buffer coordinate: prefix fb
+ * - pixel coordinate: prefix p
+ * - logical window coordinate: prefix w
+ *
+ * For scales:
+ * - global scale factor: prefix gs
+ * - zooming scale: prefix scale/s
+ *
+ * Example: fbw, pw, ww for width in different coordinates
+ *
+ * III. Equation
+ *
+ * - fbw * gs * scale_x = pw
+ * - pw = gs * ww
+ *
+ * Consequently we have
+ *
+ * - fbw * scale_x = ww
+ *
+ * Example: assuming we are running QEMU on a 3840x2160 screen and have set
+ * global scaling factor to 2, if the guest buffer size is 1920x1080 and the
+ * zooming scale is 0.5, then we have:
+ * - fbw = 1920, fbh = 1080
+ * - pw  = 1920, ph  = 1080
+ * - ww  = 960,  wh  = 540
+ * A bonus of this configuration is that we can achieve pixel to pixel
+ * presentation of the guest content.
+ */
+
 static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
 {
     VirtualConsole *vc = opaque;
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 12/19] ui/gtk: Use consistent naming for variables in different coordinates
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (10 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 11/19] ui/gtk: Document scale and coordinate handling marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 13/19] gtk/ui: Introduce helper gd_update_scale marcandre.lureau
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha, Weifeng Liu

From: Weifeng Liu <weifeng.liu.z@gmail.com>

Now that we've documented definitions and presentation of various
coordinates, let's enforce the rules.

Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
Message-ID: <20250511073337.876650-3-weifeng.liu.z@gmail.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/gtk-egl.c     |  12 +++--
 ui/gtk-gl-area.c |  14 ++---
 ui/gtk.c         | 133 ++++++++++++++++++++++++-----------------------
 3 files changed, 82 insertions(+), 77 deletions(-)

diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index f7a428c86a..947c99334b 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -70,16 +70,18 @@ void gd_egl_draw(VirtualConsole *vc)
     QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
     int fence_fd;
 #endif
-    int ww, wh, ws;
+    int ww, wh, pw, ph, gs;
 
     if (!vc->gfx.gls) {
         return;
     }
 
     window = gtk_widget_get_window(vc->gfx.drawing_area);
-    ws = gdk_window_get_scale_factor(window);
-    ww = gdk_window_get_width(window) * ws;
-    wh = gdk_window_get_height(window) * ws;
+    gs = gdk_window_get_scale_factor(window);
+    ww = gdk_window_get_width(window);
+    wh = gdk_window_get_height(window);
+    pw = ww * gs;
+    ph = wh * gs;
 
     if (vc->gfx.scanout_mode) {
 #ifdef CONFIG_GBM
@@ -115,7 +117,7 @@ void gd_egl_draw(VirtualConsole *vc)
         eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
                        vc->gfx.esurface, vc->gfx.ectx);
 
-        surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
+        surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, pw, ph);
         surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
 
         eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index 2c9a0db425..ba9fbec432 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -42,16 +42,16 @@ void gd_gl_area_draw(VirtualConsole *vc)
 #ifdef CONFIG_GBM
     QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
 #endif
-    int ww, wh, ws, y1, y2;
+    int pw, ph, gs, y1, y2;
 
     if (!vc->gfx.gls) {
         return;
     }
 
     gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
-    ws = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area));
-    ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area) * ws;
-    wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area) * ws;
+    gs = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area));
+    pw = gtk_widget_get_allocated_width(vc->gfx.drawing_area) * gs;
+    ph = gtk_widget_get_allocated_height(vc->gfx.drawing_area) * gs;
 
     if (vc->gfx.scanout_mode) {
         if (!vc->gfx.guest_fb.framebuffer) {
@@ -71,11 +71,11 @@ void gd_gl_area_draw(VirtualConsole *vc)
         glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.guest_fb.framebuffer);
         /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */
 
-        glViewport(0, 0, ww, wh);
+        glViewport(0, 0, pw, ph);
         y1 = vc->gfx.y0_top ? 0 : vc->gfx.h;
         y2 = vc->gfx.y0_top ? vc->gfx.h : 0;
         glBlitFramebuffer(0, y1, vc->gfx.w, y2,
-                          0, 0, ww, wh,
+                          0, 0, pw, ph,
                           GL_COLOR_BUFFER_BIT, GL_NEAREST);
 #ifdef CONFIG_GBM
         if (dmabuf) {
@@ -101,7 +101,7 @@ void gd_gl_area_draw(VirtualConsole *vc)
         }
         gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
 
-        surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
+        surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, pw, ph);
         surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
     }
 }
diff --git a/ui/gtk.c b/ui/gtk.c
index 9f3171abc5..8f5bb4b62e 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -387,16 +387,16 @@ static void *gd_win32_get_hwnd(VirtualConsole *vc)
 /** DisplayState Callbacks **/
 
 static void gd_update(DisplayChangeListener *dcl,
-                      int x, int y, int w, int h)
+                      int fbx, int fby, int fbw, int fbh)
 {
     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
     GdkWindow *win;
-    int x1, x2, y1, y2;
-    int mx, my;
-    int fbw, fbh;
-    int ww, wh;
+    int wx1, wx2, wy1, wy2;
+    int wx_offset, wy_offset;
+    int ww_surface, wh_surface;
+    int ww_widget, wh_widget;
 
-    trace_gd_update(vc->label, x, y, w, h);
+    trace_gd_update(vc->label, fbx, fby, fbw, fbh);
 
     if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
         return;
@@ -405,35 +405,36 @@ static void gd_update(DisplayChangeListener *dcl,
     if (vc->gfx.convert) {
         pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
                                NULL, vc->gfx.convert,
-                               x, y, 0, 0, x, y, w, h);
+                               fbx, fby, 0, 0, fbx, fby, fbw, fbh);
     }
 
-    x1 = floor(x * vc->gfx.scale_x);
-    y1 = floor(y * vc->gfx.scale_y);
+    wx1 = floor(fbx * vc->gfx.scale_x);
+    wy1 = floor(fby * vc->gfx.scale_y);
 
-    x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
-    y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
+    wx2 = ceil(fbx * vc->gfx.scale_x + fbw * vc->gfx.scale_x);
+    wy2 = ceil(fby * vc->gfx.scale_y + fbh * vc->gfx.scale_y);
 
-    fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
-    fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
+    ww_surface = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
+    wh_surface = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
 
     win = gtk_widget_get_window(vc->gfx.drawing_area);
     if (!win) {
         return;
     }
-    ww = gdk_window_get_width(win);
-    wh = gdk_window_get_height(win);
+    ww_widget = gdk_window_get_width(win);
+    wh_widget = gdk_window_get_height(win);
 
-    mx = my = 0;
-    if (ww > fbw) {
-        mx = (ww - fbw) / 2;
+    wx_offset = wy_offset = 0;
+    if (ww_widget > ww_surface) {
+        wx_offset = (ww_widget - ww_surface) / 2;
     }
-    if (wh > fbh) {
-        my = (wh - fbh) / 2;
+    if (wh_widget > wh_surface) {
+        wy_offset = (wh_widget - wh_surface) / 2;
     }
 
     gtk_widget_queue_draw_area(vc->gfx.drawing_area,
-                               mx + x1, my + y1, (x2 - x1), (y2 - y1));
+                               wx_offset + wx1, wy_offset + wy1,
+                               (wx2 - wx1), (wy2 - wy1));
 }
 
 static void gd_refresh(DisplayChangeListener *dcl)
@@ -869,8 +870,8 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
 {
     VirtualConsole *vc = opaque;
     GtkDisplayState *s = vc->s;
-    int mx, my;
-    int ww, wh;
+    int wx_offset, wy_offset;
+    int ww_widget, wh_widget, ww_surface, wh_surface;
     int fbw, fbh;
 
 #if defined(CONFIG_OPENGL)
@@ -904,46 +905,47 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
     fbw = surface_width(vc->gfx.ds);
     fbh = surface_height(vc->gfx.ds);
 
-    ww = gdk_window_get_width(gtk_widget_get_window(widget));
-    wh = gdk_window_get_height(gtk_widget_get_window(widget));
+    ww_widget = gdk_window_get_width(gtk_widget_get_window(widget));
+    wh_widget = gdk_window_get_height(gtk_widget_get_window(widget));
 
     if (s->full_screen) {
-        vc->gfx.scale_x = (double)ww / fbw;
-        vc->gfx.scale_y = (double)wh / fbh;
+        vc->gfx.scale_x = (double)ww_widget / fbw;
+        vc->gfx.scale_y = (double)wh_widget / fbh;
     } else if (s->free_scale) {
         double sx, sy;
 
-        sx = (double)ww / fbw;
-        sy = (double)wh / fbh;
+        sx = (double)ww_widget / fbw;
+        sy = (double)wh_widget / fbh;
 
         vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
     }
 
-    fbw *= vc->gfx.scale_x;
-    fbh *= vc->gfx.scale_y;
+    ww_surface = fbw * vc->gfx.scale_x;
+    wh_surface = fbh * vc->gfx.scale_y;
 
-    mx = my = 0;
-    if (ww > fbw) {
-        mx = (ww - fbw) / 2;
+    wx_offset = wy_offset = 0;
+    if (ww_widget > ww_surface) {
+        wx_offset = (ww_widget - ww_surface) / 2;
     }
-    if (wh > fbh) {
-        my = (wh - fbh) / 2;
+    if (wh_widget > wh_surface) {
+        wy_offset = (wh_widget - wh_surface) / 2;
     }
 
-    cairo_rectangle(cr, 0, 0, ww, wh);
+    cairo_rectangle(cr, 0, 0, ww_widget, wh_widget);
 
     /* Optionally cut out the inner area where the pixmap
        will be drawn. This avoids 'flashing' since we're
        not double-buffering. Note we're using the undocumented
        behaviour of drawing the rectangle from right to left
        to cut out the whole */
-    cairo_rectangle(cr, mx + fbw, my,
-                    -1 * fbw, fbh);
+    cairo_rectangle(cr, wx_offset + ww_surface, wy_offset,
+                    -1 * ww_surface, wh_surface);
     cairo_fill(cr);
 
     cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
     cairo_set_source_surface(cr, vc->gfx.surface,
-                             mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
+                             wx_offset / vc->gfx.scale_x,
+                             wy_offset / vc->gfx.scale_y);
     cairo_paint(cr);
 
     return TRUE;
@@ -954,19 +956,19 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
 {
     VirtualConsole *vc = opaque;
     GtkDisplayState *s = vc->s;
-    int x, y;
-    int mx, my;
-    int fbh, fbw;
-    int ww, wh;
+    int fbx, fby;
+    int wx_offset, wy_offset;
+    int wh_surface, ww_surface;
+    int ww_widget, wh_widget;
 
     if (!vc->gfx.ds) {
         return TRUE;
     }
 
-    fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
-    fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
-    ww = gtk_widget_get_allocated_width(widget);
-    wh = gtk_widget_get_allocated_height(widget);
+    ww_surface = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
+    wh_surface = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
+    ww_widget = gtk_widget_get_allocated_width(widget);
+    wh_widget = gtk_widget_get_allocated_height(widget);
 
     /*
      * `widget` may not have the same size with the frame buffer.
@@ -974,41 +976,42 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
      * To achieve that, `vc` will be displayed at (mx, my)
      * so that it is displayed at the center of the widget.
      */
-    mx = my = 0;
-    if (ww > fbw) {
-        mx = (ww - fbw) / 2;
+    wx_offset = wy_offset = 0;
+    if (ww_widget > ww_surface) {
+        wx_offset = (ww_widget - ww_surface) / 2;
     }
-    if (wh > fbh) {
-        my = (wh - fbh) / 2;
+    if (wh_widget > wh_surface) {
+        wy_offset = (wh_widget - wh_surface) / 2;
     }
 
     /*
      * `motion` is reported in `widget` coordinates
      * so translating it to the coordinates in `vc`.
      */
-    x = (motion->x - mx) / vc->gfx.scale_x;
-    y = (motion->y - my) / vc->gfx.scale_y;
+    fbx = (motion->x - wx_offset) / vc->gfx.scale_x;
+    fby = (motion->y - wy_offset) / vc->gfx.scale_y;
 
-    trace_gd_motion_event(ww, wh, gtk_widget_get_scale_factor(widget), x, y);
+    trace_gd_motion_event(ww_widget, wh_widget,
+                          gtk_widget_get_scale_factor(widget), fbx, fby);
 
     if (qemu_input_is_absolute(vc->gfx.dcl.con)) {
-        if (x < 0 || y < 0 ||
-            x >= surface_width(vc->gfx.ds) ||
-            y >= surface_height(vc->gfx.ds)) {
+        if (fbx < 0 || fby < 0 ||
+            fbx >= surface_width(vc->gfx.ds) ||
+            fby >= surface_height(vc->gfx.ds)) {
             return TRUE;
         }
-        qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
+        qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, fbx,
                              0, surface_width(vc->gfx.ds));
-        qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
+        qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, fby,
                              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);
-        qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
+        qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, fbx - s->last_x);
+        qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, fby - s->last_y);
         qemu_input_event_sync();
     }
-    s->last_x = x;
-    s->last_y = y;
+    s->last_x = fbx;
+    s->last_y = fby;
     s->last_set = TRUE;
 
     if (!qemu_input_is_absolute(vc->gfx.dcl.con) && s->ptr_owner == vc) {
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 13/19] gtk/ui: Introduce helper gd_update_scale
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (11 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 12/19] ui/gtk: Use consistent naming for variables in different coordinates marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 14/19] ui/gtk: Update scales in fixed-scale mode when rendering GL area marcandre.lureau
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha, Weifeng Liu

From: Weifeng Liu <weifeng.liu.z@gmail.com>

The code snippet updating scale_x/scale_y is general and will be used in
next patch. Make it a function.

Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
Message-ID: <20250511073337.876650-4-weifeng.liu.z@gmail.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/gtk.h |  2 ++
 ui/gtk.c         | 30 +++++++++++++++++++-----------
 2 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index aa3d637029..d3944046db 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -224,4 +224,6 @@ int gd_gl_area_make_current(DisplayGLCtx *dgc,
 /* gtk-clipboard.c */
 void gd_clipboard_init(GtkDisplayState *gd);
 
+void gd_update_scale(VirtualConsole *vc, int ww, int wh, int fbw, int fbh);
+
 #endif /* UI_GTK_H */
diff --git a/ui/gtk.c b/ui/gtk.c
index 8f5bb4b62e..47af49e387 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -801,6 +801,24 @@ void gd_update_monitor_refresh_rate(VirtualConsole *vc, GtkWidget *widget)
 #endif
 }
 
+void gd_update_scale(VirtualConsole *vc, int ww, int wh, int fbw, int fbh)
+{
+    if (!vc) {
+        return;
+    }
+
+    if (vc->s->full_screen) {
+        vc->gfx.scale_x = (double)ww / fbw;
+        vc->gfx.scale_y = (double)wh / fbh;
+    } else if (vc->s->free_scale) {
+        double sx, sy;
+
+        sx = (double)ww / fbw;
+        sy = (double)wh / fbh;
+
+        vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
+    }
+}
 /**
  * DOC: Coordinate handling.
  *
@@ -908,17 +926,7 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
     ww_widget = gdk_window_get_width(gtk_widget_get_window(widget));
     wh_widget = gdk_window_get_height(gtk_widget_get_window(widget));
 
-    if (s->full_screen) {
-        vc->gfx.scale_x = (double)ww_widget / fbw;
-        vc->gfx.scale_y = (double)wh_widget / fbh;
-    } else if (s->free_scale) {
-        double sx, sy;
-
-        sx = (double)ww_widget / fbw;
-        sy = (double)wh_widget / fbh;
-
-        vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
-    }
+    gd_update_scale(vc, ww_widget, wh_widget, fbw, fbh);
 
     ww_surface = fbw * vc->gfx.scale_x;
     wh_surface = fbh * vc->gfx.scale_y;
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 14/19] ui/gtk: Update scales in fixed-scale mode when rendering GL area
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (12 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 13/19] gtk/ui: Introduce helper gd_update_scale marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-07-10 12:22   ` Peter Maydell
  2025-05-24 17:35 ` [PULL 15/19] ui/sdl: Consider scaling in mouse event handling marcandre.lureau
                   ` (5 subsequent siblings)
  19 siblings, 1 reply; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha, Weifeng Liu

From: Weifeng Liu <weifeng.liu.z@gmail.com>

When gl=on, scale_x and scale_y were set to 1 on startup that didn't
reflect the real situation of the scan-out in free scale mode, resulting
in incorrect cursor coordinates to be sent when moving the mouse
pointer. Simply updating the scales before rendering the image fixes
this issue.

Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
Message-ID: <20250511073337.876650-5-weifeng.liu.z@gmail.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/gtk-gl-area.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index ba9fbec432..db93cd6204 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -43,6 +43,8 @@ void gd_gl_area_draw(VirtualConsole *vc)
     QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
 #endif
     int pw, ph, gs, y1, y2;
+    int ww, wh;
+    int fbw, fbh;
 
     if (!vc->gfx.gls) {
         return;
@@ -50,8 +52,14 @@ void gd_gl_area_draw(VirtualConsole *vc)
 
     gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
     gs = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area));
-    pw = gtk_widget_get_allocated_width(vc->gfx.drawing_area) * gs;
-    ph = gtk_widget_get_allocated_height(vc->gfx.drawing_area) * gs;
+    fbw = surface_width(vc->gfx.ds);
+    fbh = surface_height(vc->gfx.ds);
+    ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area);
+    wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area);
+    pw = ww * gs;
+    ph = wh * gs;
+
+    gd_update_scale(vc, ww, wh, fbw, fbh);
 
     if (vc->gfx.scanout_mode) {
         if (!vc->gfx.guest_fb.framebuffer) {
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 15/19] ui/sdl: Consider scaling in mouse event handling
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (13 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 14/19] ui/gtk: Update scales in fixed-scale mode when rendering GL area marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 16/19] ui/gtk: Don't update scale in fixed scale mode in gtk-egl.c marcandre.lureau
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha, Weifeng Liu

From: Weifeng Liu <weifeng.liu@intel.com>

When using sdl display backend, if the window is scaled, incorrect mouse
positions will be reported since scaling is not properly handled. Fix it
by transforming the positions from window coordinate to guest buffer
coordinate.

Signed-off-by: Weifeng Liu <weifeng.liu@intel.com>
Message-ID: <20250511073337.876650-6-weifeng.liu.z@gmail.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/sdl2.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/ui/sdl2.c b/ui/sdl2.c
index cda4293a53..b00e421f7f 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -488,14 +488,14 @@ static void handle_mousemotion(SDL_Event *ev)
 {
     int max_x, max_y;
     struct sdl2_console *scon = get_scon_from_window(ev->motion.windowID);
+    int scr_w, scr_h, surf_w, surf_h, x, y, dx, dy;
 
     if (!scon || !qemu_console_is_graphic(scon->dcl.con)) {
         return;
     }
 
+    SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
     if (qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) {
-        int scr_w, scr_h;
-        SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
         max_x = scr_w - 1;
         max_y = scr_h - 1;
         if (gui_grab && !gui_fullscreen
@@ -509,9 +509,14 @@ static void handle_mousemotion(SDL_Event *ev)
             sdl_grab_start(scon);
         }
     }
+    surf_w = surface_width(scon->surface);
+    surf_h = surface_height(scon->surface);
+    x = (int64_t)ev->motion.x * surf_w / scr_w;
+    y = (int64_t)ev->motion.y * surf_h / scr_h;
+    dx = (int64_t)ev->motion.xrel * surf_w / scr_w;
+    dy = (int64_t)ev->motion.yrel * surf_h / scr_h;
     if (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) {
-        sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel,
-                             ev->motion.x, ev->motion.y, ev->motion.state);
+        sdl_send_mouse_event(scon, dx, dy, x, y, ev->motion.state);
     }
 }
 
@@ -520,12 +525,17 @@ static void handle_mousebutton(SDL_Event *ev)
     int buttonstate = SDL_GetMouseState(NULL, NULL);
     SDL_MouseButtonEvent *bev;
     struct sdl2_console *scon = get_scon_from_window(ev->button.windowID);
+    int scr_w, scr_h, x, y;
 
     if (!scon || !qemu_console_is_graphic(scon->dcl.con)) {
         return;
     }
 
     bev = &ev->button;
+    SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
+    x = (int64_t)bev->x * surface_width(scon->surface) / scr_w;
+    y = (int64_t)bev->y * surface_height(scon->surface) / scr_h;
+
     if (!gui_grab && !qemu_input_is_absolute(scon->dcl.con)) {
         if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
             /* start grabbing all events */
@@ -537,7 +547,7 @@ static void handle_mousebutton(SDL_Event *ev)
         } else {
             buttonstate &= ~SDL_BUTTON(bev->button);
         }
-        sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate);
+        sdl_send_mouse_event(scon, 0, 0, x, y, buttonstate);
     }
 }
 
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 16/19] ui/gtk: Don't update scale in fixed scale mode in gtk-egl.c
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (14 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 15/19] ui/sdl: Consider scaling in mouse event handling marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 17/19] ui/gtk: Consider scaling when propagating ui info marcandre.lureau
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha, Weifeng Liu

From: Weifeng Liu <weifeng.liu.z@gmail.com>

Scale shouldn't be changed until user explicitly requests it in fixed
scale mode (full-screen=false and free-scale=false). Use function
gd_update_scale to complete scale updating instead.

Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
Message-ID: <20250511073337.876650-7-weifeng.liu.z@gmail.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/gtk-egl.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 947c99334b..f8e4f4bc70 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -95,8 +95,9 @@ void gd_egl_draw(VirtualConsole *vc)
 #endif
         gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h);
 
-        vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds);
-        vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds);
+        gd_update_scale(vc, ww, wh,
+                        surface_width(vc->gfx.ds),
+                        surface_height(vc->gfx.ds));
 
         glFlush();
 #ifdef CONFIG_GBM
@@ -122,8 +123,9 @@ void gd_egl_draw(VirtualConsole *vc)
 
         eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
 
-        vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds);
-        vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds);
+        gd_update_scale(vc, ww, wh,
+                        surface_width(vc->gfx.ds),
+                        surface_height(vc->gfx.ds));
 
         glFlush();
     }
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 17/19] ui/gtk: Consider scaling when propagating ui info
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (15 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 16/19] ui/gtk: Don't update scale in fixed scale mode in gtk-egl.c marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 18/19] ui/gtk-gl-area: Render guest content with padding in fixed-scale mode marcandre.lureau
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha, Weifeng Liu

From: Weifeng Liu <weifeng.liu.z@gmail.com>

The ui width and height sent to guest is supposed to be in buffer
coordinate. Hence conversion is required.

If scaling (global window scale and zooming scale) is not respected in
non-free-scale mode, window size could keep changing because of the
existence of the iteration of the following steps:

1. In resize event or configure event, a size larger (or smaller) than
   the currently used one might be calculated due to not considering
   scaling.
2. On reception of the display size change event in guest, the guest
   might decide to do a mode setting and use the larger (or smaller)
   mode.
3. When the new guest scan-out command arrives, QEMU would request the
   window size to change to fit the new buffer size. This will trigger a
   resize event or a configure event, making us go back to step 1.

Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
Message-ID: <20250511073337.876650-8-weifeng.liu.z@gmail.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/gtk.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/ui/gtk.c b/ui/gtk.c
index 47af49e387..8c4a94c8f6 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -772,8 +772,21 @@ static void gd_resize_event(GtkGLArea *area,
                             gint width, gint height, gpointer *opaque)
 {
     VirtualConsole *vc = (void *)opaque;
+    double pw = width, ph = height;
+    double sx = vc->gfx.scale_x, sy = vc->gfx.scale_y;
+    GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(area));
+    const int gs = gdk_window_get_scale_factor(window);
 
-    gd_set_ui_size(vc, width, height);
+    if (!vc->s->free_scale && !vc->s->full_screen) {
+        pw /= sx;
+        ph /= sy;
+    }
+
+    /**
+     * width and height here are in pixel coordinate, so we must divide it
+     * by global window scale (gs)
+     */
+    gd_set_ui_size(vc, pw / gs, ph / gs);
 }
 
 #endif
@@ -1836,8 +1849,16 @@ static gboolean gd_configure(GtkWidget *widget,
                              GdkEventConfigure *cfg, gpointer opaque)
 {
     VirtualConsole *vc = opaque;
+    const double sx = vc->gfx.scale_x, sy = vc->gfx.scale_y;
+    double width = cfg->width, height = cfg->height;
+
+    if (!vc->s->free_scale && !vc->s->full_screen) {
+        width /= sx;
+        height /= sy;
+    }
+
+    gd_set_ui_size(vc, width, height);
 
-    gd_set_ui_size(vc, cfg->width, cfg->height);
     return FALSE;
 }
 
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 18/19] ui/gtk-gl-area: Render guest content with padding in fixed-scale mode
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (16 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 17/19] ui/gtk: Consider scaling when propagating ui info marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-24 17:35 ` [PULL 19/19] ui/gtk-egl: " marcandre.lureau
  2025-05-25 15:20 ` [PULL 00/19] Ui patches Stefan Hajnoczi
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha, Weifeng Liu

From: Weifeng Liu <weifeng.liu.z@gmail.com>

In fixed-scale mode (zoom-to-fit=false), we expect that scale should not
change, meaning that if window size is larger than guest surface,
padding is supposed to be added to preserve the scale. However, in
OpenGL mode (gl=on), guest surface is always painted to the whole canvas
without any padding. This change tries to fix this bug by adding
appropriate padding when drawing surfaces.

Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
Message-ID: <20250511073337.876650-9-weifeng.liu.z@gmail.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 ui/gtk-gl-area.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index db93cd6204..8151cc413c 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -44,7 +44,9 @@ void gd_gl_area_draw(VirtualConsole *vc)
 #endif
     int pw, ph, gs, y1, y2;
     int ww, wh;
+    int ww_surface, wh_surface;
     int fbw, fbh;
+    int wx_offset, wy_offset;
 
     if (!vc->gfx.gls) {
         return;
@@ -61,6 +63,17 @@ void gd_gl_area_draw(VirtualConsole *vc)
 
     gd_update_scale(vc, ww, wh, fbw, fbh);
 
+    ww_surface = fbw * vc->gfx.scale_x;
+    wh_surface = fbh * vc->gfx.scale_y;
+
+    wx_offset = wy_offset = 0;
+    if (ww > ww_surface) {
+        wx_offset = (ww - ww_surface) / 2;
+    }
+    if (wh > wh_surface) {
+        wy_offset = (wh - wh_surface) / 2;
+    }
+
     if (vc->gfx.scanout_mode) {
         if (!vc->gfx.guest_fb.framebuffer) {
             return;
@@ -79,11 +92,29 @@ void gd_gl_area_draw(VirtualConsole *vc)
         glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.guest_fb.framebuffer);
         /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */
 
+        if (wx_offset > 0) {
+            glEnable(GL_SCISSOR_TEST);
+            glScissor(0, 0, wx_offset * gs, wh * gs);
+            glClear(GL_COLOR_BUFFER_BIT);
+            glScissor((ww - wx_offset) * gs, 0, wx_offset * gs, wh * gs);
+            glClear(GL_COLOR_BUFFER_BIT);
+            glDisable(GL_SCISSOR_TEST);
+        }
+        if (wy_offset > 0) {
+            glEnable(GL_SCISSOR_TEST);
+            glScissor(0, 0, ww * gs, wy_offset * gs);
+            glClear(GL_COLOR_BUFFER_BIT);
+            glScissor(0, (wh - wy_offset) * gs, ww * gs, wy_offset * gs);
+            glClear(GL_COLOR_BUFFER_BIT);
+            glDisable(GL_SCISSOR_TEST);
+        }
+
         glViewport(0, 0, pw, ph);
         y1 = vc->gfx.y0_top ? 0 : vc->gfx.h;
         y2 = vc->gfx.y0_top ? vc->gfx.h : 0;
         glBlitFramebuffer(0, y1, vc->gfx.w, y2,
-                          0, 0, pw, ph,
+                          wx_offset * gs, wy_offset * gs,
+                          (ww - wx_offset) * gs, (wh - wy_offset) * gs,
                           GL_COLOR_BUFFER_BIT, GL_NEAREST);
 #ifdef CONFIG_GBM
         if (dmabuf) {
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PULL 19/19] ui/gtk-egl: Render guest content with padding in fixed-scale mode
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (17 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 18/19] ui/gtk-gl-area: Render guest content with padding in fixed-scale mode marcandre.lureau
@ 2025-05-24 17:35 ` marcandre.lureau
  2025-05-25 15:20 ` [PULL 00/19] Ui patches Stefan Hajnoczi
  19 siblings, 0 replies; 27+ messages in thread
From: marcandre.lureau @ 2025-05-24 17:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Marc-André Lureau, stefanha, Weifeng Liu

From: Weifeng Liu <weifeng.liu.z@gmail.com>

Scaling was not respected when rendering frames in gtk-egl.c (used if
gl=on and X11 mode). To fix this, add fields x and y to struct egl_fb
for x offset and y offset so we can add padding to window.

Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
Message-ID: <20250511073337.876650-10-weifeng.liu.z@gmail.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/ui/egl-helpers.h |  4 +++-
 ui/egl-helpers.c         | 10 ++++++++--
 ui/gtk-egl.c             | 36 +++++++++++++++++++++++++++++++-----
 ui/sdl2-gl.c             |  2 +-
 4 files changed, 43 insertions(+), 9 deletions(-)

diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index fb80e15142..acf993fcf5 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -17,6 +17,8 @@ extern bool qemu_egl_angle_d3d;
 typedef struct egl_fb {
     int width;
     int height;
+    int x;
+    int y;
     GLuint texture;
     GLuint framebuffer;
     bool delete_texture;
@@ -26,7 +28,7 @@ typedef struct egl_fb {
 #define EGL_FB_INIT { 0, }
 
 void egl_fb_destroy(egl_fb *fb);
-void egl_fb_setup_default(egl_fb *fb, int width, int height);
+void egl_fb_setup_default(egl_fb *fb, int width, int height, int x, int y);
 void egl_fb_setup_for_tex(egl_fb *fb, int width, int height,
                           GLuint texture, bool delete);
 void egl_fb_setup_new_tex(egl_fb *fb, int width, int height);
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 9cda2bbbee..5503a795e4 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -93,14 +93,18 @@ void egl_fb_destroy(egl_fb *fb)
 
     fb->width = 0;
     fb->height = 0;
+    fb->x = 0;
+    fb->y = 0;
     fb->texture = 0;
     fb->framebuffer = 0;
 }
 
-void egl_fb_setup_default(egl_fb *fb, int width, int height)
+void egl_fb_setup_default(egl_fb *fb, int width, int height, int x, int y)
 {
     fb->width = width;
     fb->height = height;
+    fb->x = x;
+    fb->y = y;
     fb->framebuffer = 0; /* default framebuffer */
 }
 
@@ -145,6 +149,7 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip)
     glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer);
     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->framebuffer);
     glViewport(0, 0, dst->width, dst->height);
+    glClear(GL_COLOR_BUFFER_BIT);
 
     if (src->dmabuf) {
         x1 = qemu_dmabuf_get_x(src->dmabuf);
@@ -161,7 +166,8 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip)
     x2 = x1 + w;
 
     glBlitFramebuffer(x1, y1, x2, y2,
-                      0, 0, dst->width, dst->height,
+                      dst->x, dst->y,
+                      dst->x + dst->width, dst->y + dst->height,
                       GL_COLOR_BUFFER_BIT, GL_LINEAR);
 }
 
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index f8e4f4bc70..0b787bea25 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -340,7 +340,11 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
 {
     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
     GdkWindow *window;
-    int ww, wh, ws;
+    int px_offset, py_offset;
+    int gs;
+    int pw_widget, ph_widget, pw_surface, ph_surface;
+    int ww_widget, wh_widget, ww_surface, wh_surface;
+    int fbw, fbh;
 
     if (!vc->gfx.scanout_mode) {
         return;
@@ -353,10 +357,32 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
                    vc->gfx.esurface, vc->gfx.ectx);
 
     window = gtk_widget_get_window(vc->gfx.drawing_area);
-    ws = gdk_window_get_scale_factor(window);
-    ww = gdk_window_get_width(window) * ws;
-    wh = gdk_window_get_height(window) * ws;
-    egl_fb_setup_default(&vc->gfx.win_fb, ww, wh);
+    gs = gdk_window_get_scale_factor(window);
+    ww_widget = gdk_window_get_width(window);
+    wh_widget = gdk_window_get_height(window);
+    fbw = surface_width(vc->gfx.ds);
+    fbh = surface_height(vc->gfx.ds);
+
+    gd_update_scale(vc, ww_widget, wh_widget, fbw, fbh);
+
+    ww_surface = fbw * vc->gfx.scale_x;
+    wh_surface = fbh * vc->gfx.scale_y;
+    pw_widget = ww_widget * gs;
+    ph_widget = wh_widget * gs;
+    pw_surface = ww_surface * gs;
+    ph_surface = wh_surface * gs;
+
+    px_offset = 0;
+    py_offset = 0;
+    if (pw_widget > pw_surface) {
+        px_offset = (pw_widget - pw_surface) / 2;
+    }
+    if (ph_widget > ph_surface) {
+        py_offset = (ph_widget - ph_surface) / 2;
+    }
+
+    egl_fb_setup_default(&vc->gfx.win_fb, pw_surface, ph_surface,
+                         px_offset, py_offset);
     if (vc->gfx.cursor_fb.texture) {
         egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb,
                          vc->gfx.y0_top);
diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c
index e01d9ab0c7..3be17d1079 100644
--- a/ui/sdl2-gl.c
+++ b/ui/sdl2-gl.c
@@ -241,7 +241,7 @@ void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
     SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
 
     SDL_GetWindowSize(scon->real_window, &ww, &wh);
-    egl_fb_setup_default(&scon->win_fb, ww, wh);
+    egl_fb_setup_default(&scon->win_fb, ww, wh, 0, 0);
     egl_fb_blit(&scon->win_fb, &scon->guest_fb, !scon->y0_top);
 
     SDL_GL_SwapWindow(scon->real_window);
-- 
2.49.0



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PULL 00/19] Ui patches
  2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
                   ` (18 preceding siblings ...)
  2025-05-24 17:35 ` [PULL 19/19] ui/gtk-egl: " marcandre.lureau
@ 2025-05-25 15:20 ` Stefan Hajnoczi
  19 siblings, 0 replies; 27+ messages in thread
From: Stefan Hajnoczi @ 2025-05-25 15:20 UTC (permalink / raw)
  To: marcandre.lureau; +Cc: qemu-devel, Marc-André Lureau, stefanha

[-- Attachment #1: Type: text/plain, Size: 116 bytes --]

Applied, thanks.

Please update the changelog at https://wiki.qemu.org/ChangeLog/10.1 for any user-visible changes.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PULL 14/19] ui/gtk: Update scales in fixed-scale mode when rendering GL area
  2025-05-24 17:35 ` [PULL 14/19] ui/gtk: Update scales in fixed-scale mode when rendering GL area marcandre.lureau
@ 2025-07-10 12:22   ` Peter Maydell
  2025-07-11  7:01     ` Marc-André Lureau
  0 siblings, 1 reply; 27+ messages in thread
From: Peter Maydell @ 2025-07-10 12:22 UTC (permalink / raw)
  To: marcandre.lureau; +Cc: qemu-devel, stefanha, Weifeng Liu

On Sat, 24 May 2025 at 18:37, <marcandre.lureau@redhat.com> wrote:
>
> From: Weifeng Liu <weifeng.liu.z@gmail.com>
>
> When gl=on, scale_x and scale_y were set to 1 on startup that didn't
> reflect the real situation of the scan-out in free scale mode, resulting
> in incorrect cursor coordinates to be sent when moving the mouse
> pointer. Simply updating the scales before rendering the image fixes
> this issue.
>
> Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
> Message-ID: <20250511073337.876650-5-weifeng.liu.z@gmail.com>
> Acked-by: Gerd Hoffmann <kraxel@redhat.com>
> Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Hi; Coverity complains about this change CID 1610328):

> @@ -50,8 +52,14 @@ void gd_gl_area_draw(VirtualConsole *vc)
>
>      gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
>      gs = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area));
> -    pw = gtk_widget_get_allocated_width(vc->gfx.drawing_area) * gs;
> -    ph = gtk_widget_get_allocated_height(vc->gfx.drawing_area) * gs;
> +    fbw = surface_width(vc->gfx.ds);
> +    fbh = surface_height(vc->gfx.ds);

Here we now unconditionally dereference vc->gfx.ds at the start of
gd_gl_area_draw().

But towards the end of this function we have a NULL check:

        if (!vc->gfx.ds) {
            return;
        }

Either vc->gfx.ds can be NULL, in which case we need some
kind of guard on these surface_width() and surface_height()
calls; or else it can't, and the NULL check later is dead code.
Which is correct ?

thanks
-- PMM


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PULL 14/19] ui/gtk: Update scales in fixed-scale mode when rendering GL area
  2025-07-10 12:22   ` Peter Maydell
@ 2025-07-11  7:01     ` Marc-André Lureau
  2025-07-11  8:38       ` Weifeng Liu
  0 siblings, 1 reply; 27+ messages in thread
From: Marc-André Lureau @ 2025-07-11  7:01 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel, stefanha, Weifeng Liu

Hi

On Thu, Jul 10, 2025 at 4:24 PM Peter Maydell <peter.maydell@linaro.org> wrote:
>
> On Sat, 24 May 2025 at 18:37, <marcandre.lureau@redhat.com> wrote:
> >
> > From: Weifeng Liu <weifeng.liu.z@gmail.com>
> >
> > When gl=on, scale_x and scale_y were set to 1 on startup that didn't
> > reflect the real situation of the scan-out in free scale mode, resulting
> > in incorrect cursor coordinates to be sent when moving the mouse
> > pointer. Simply updating the scales before rendering the image fixes
> > this issue.
> >
> > Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
> > Message-ID: <20250511073337.876650-5-weifeng.liu.z@gmail.com>
> > Acked-by: Gerd Hoffmann <kraxel@redhat.com>
> > Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> Hi; Coverity complains about this change CID 1610328):
>
> > @@ -50,8 +52,14 @@ void gd_gl_area_draw(VirtualConsole *vc)
> >
> >      gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> >      gs = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area));
> > -    pw = gtk_widget_get_allocated_width(vc->gfx.drawing_area) * gs;
> > -    ph = gtk_widget_get_allocated_height(vc->gfx.drawing_area) * gs;
> > +    fbw = surface_width(vc->gfx.ds);
> > +    fbh = surface_height(vc->gfx.ds);
>
> Here we now unconditionally dereference vc->gfx.ds at the start of
> gd_gl_area_draw().
>
> But towards the end of this function we have a NULL check:
>
>         if (!vc->gfx.ds) {
>             return;
>         }
>
> Either vc->gfx.ds can be NULL, in which case we need some
> kind of guard on these surface_width() and surface_height()
> calls; or else it can't, and the NULL check later is dead code.
> Which is correct ?

Given that it's simply called from a GTK callback, it can be NULL. I
think we should simply return in this case, or perhaps use the older
code path as a fallback. Weifeng, wdyt?

-- 
Marc-André Lureau


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PULL 14/19] ui/gtk: Update scales in fixed-scale mode when rendering GL area
  2025-07-11  7:01     ` Marc-André Lureau
@ 2025-07-11  8:38       ` Weifeng Liu
  0 siblings, 0 replies; 27+ messages in thread
From: Weifeng Liu @ 2025-07-11  8:38 UTC (permalink / raw)
  To: Marc-André Lureau, Peter Maydell; +Cc: qemu-devel, stefanha

Hi,

On Fri, 2025-07-11 at 11:01 +0400, Marc-André Lureau wrote:
> Hi
> 
> On Thu, Jul 10, 2025 at 4:24 PM Peter Maydell
> <peter.maydell@linaro.org> wrote:
> > 
> > On Sat, 24 May 2025 at 18:37, <marcandre.lureau@redhat.com> wrote:
> > > 
> > > From: Weifeng Liu <weifeng.liu.z@gmail.com>
> > > 
> > > When gl=on, scale_x and scale_y were set to 1 on startup that
> > > didn't
> > > reflect the real situation of the scan-out in free scale mode,
> > > resulting
> > > in incorrect cursor coordinates to be sent when moving the mouse
> > > pointer. Simply updating the scales before rendering the image
> > > fixes
> > > this issue.
> > > 
> > > Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
> > > Message-ID: <20250511073337.876650-5-weifeng.liu.z@gmail.com>
> > > Acked-by: Gerd Hoffmann <kraxel@redhat.com>
> > > Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > 
> > Hi; Coverity complains about this change CID 1610328):
> > 
> > > @@ -50,8 +52,14 @@ void gd_gl_area_draw(VirtualConsole *vc)
> > > 
> > >      gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
> > >      gs = gdk_window_get_scale_factor(gtk_widget_get_window(vc-
> > > >gfx.drawing_area));
> > > -    pw = gtk_widget_get_allocated_width(vc->gfx.drawing_area) *
> > > gs;
> > > -    ph = gtk_widget_get_allocated_height(vc->gfx.drawing_area) *
> > > gs;
> > > +    fbw = surface_width(vc->gfx.ds);
> > > +    fbh = surface_height(vc->gfx.ds);
> > 
> > Here we now unconditionally dereference vc->gfx.ds at the start of
> > gd_gl_area_draw().
> > 
> > But towards the end of this function we have a NULL check:
> > 
> >         if (!vc->gfx.ds) {
> >             return;
> >         }
> > 
> > Either vc->gfx.ds can be NULL, in which case we need some
> > kind of guard on these surface_width() and surface_height()
> > calls; or else it can't, and the NULL check later is dead code.
> > Which is correct ?
> 
> Given that it's simply called from a GTK callback, it can be NULL. I
> think we should simply return in this case, or perhaps use the older
> code path as a fallback. Weifeng, wdyt?

It makes sense to skip drawing when ds is NULL, although that shouldn’t
happen since dpy_gfx_replace_surface installs a dummy DisplaySurface
for any NULL input. I’ll draft a patch shortly to add this check (and
possibly other refinements).

Best regards,
Weifeng


^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2025-07-11  8:43 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-24 17:34 [PULL 00/19] Ui patches marcandre.lureau
2025-05-24 17:34 ` [PULL 01/19] ui/gtk: warn if setting the clipboard failed marcandre.lureau
2025-05-24 17:34 ` [PULL 02/19] ui/clipboard: use int for selection field marcandre.lureau
2025-05-24 17:34 ` [PULL 03/19] ui/clipboard: split out QemuClipboardContent marcandre.lureau
2025-05-24 17:34 ` [PULL 04/19] ui/clipboard: add vmstate_cbinfo marcandre.lureau
2025-05-24 17:34 ` [PULL 05/19] ui/clipboard: delay clipboard update when not running marcandre.lureau
2025-05-24 17:34 ` [PULL 06/19] ui/vdagent: replace Buffer with GByteArray marcandre.lureau
2025-05-24 17:35 ` [PULL 07/19] ui/vdagent: keep "connected" state marcandre.lureau
2025-05-24 17:35 ` [PULL 08/19] ui/vdagent: factor out clipboard peer registration marcandre.lureau
2025-05-24 17:35 ` [PULL 09/19] ui/vdagent: add migration support marcandre.lureau
2025-05-24 17:35 ` [PULL 10/19] ui/vdagent: remove migration blocker marcandre.lureau
2025-05-24 17:35 ` [PULL 11/19] ui/gtk: Document scale and coordinate handling marcandre.lureau
2025-05-24 17:35 ` [PULL 12/19] ui/gtk: Use consistent naming for variables in different coordinates marcandre.lureau
2025-05-24 17:35 ` [PULL 13/19] gtk/ui: Introduce helper gd_update_scale marcandre.lureau
2025-05-24 17:35 ` [PULL 14/19] ui/gtk: Update scales in fixed-scale mode when rendering GL area marcandre.lureau
2025-07-10 12:22   ` Peter Maydell
2025-07-11  7:01     ` Marc-André Lureau
2025-07-11  8:38       ` Weifeng Liu
2025-05-24 17:35 ` [PULL 15/19] ui/sdl: Consider scaling in mouse event handling marcandre.lureau
2025-05-24 17:35 ` [PULL 16/19] ui/gtk: Don't update scale in fixed scale mode in gtk-egl.c marcandre.lureau
2025-05-24 17:35 ` [PULL 17/19] ui/gtk: Consider scaling when propagating ui info marcandre.lureau
2025-05-24 17:35 ` [PULL 18/19] ui/gtk-gl-area: Render guest content with padding in fixed-scale mode marcandre.lureau
2025-05-24 17:35 ` [PULL 19/19] ui/gtk-egl: " marcandre.lureau
2025-05-25 15:20 ` [PULL 00/19] Ui patches Stefan Hajnoczi
  -- strict thread matches above, loose matches on Subject: below --
2023-05-28 13:19 marcandre.lureau
2023-05-28 17:43 ` Michael Tokarev
2023-05-29 21:29 ` Richard Henderson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).