From 5d0cbf1991595a43ba185761517d256c6431d12c Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 16 May 2023 06:09:46 +0000 Subject: [PATCH] import mutter-3.32.2-68.el8 --- .gitignore | 1 + .mutter.metadata | 1 + ...1-Add-support-for-quad-buffer-stereo.patch | 907 +++ ...WacomDevices-for-tablet-touchpad-dev.patch | 55 + .../0001-EGL-Include-EGL-eglmesaext.h.patch | 68 + ...orManager-ignore-hotplug_mode_update.patch | 28 + ...acom-touchpads-when-updating-setting.patch | 94 + ...ackend-Add-getter-for-MetaScreenCast.patch | 49 + ...enable-tap-to-click-drag-on-opaque-W.patch | 80 + ...oth-input-settings-and-mapper-for-ta.patch | 205 + ...r-pen-eraser-devices-when-looking-fo.patch | 30 + ...erve-multiple-pad-mode-switch-button.patch | 118 + ...-x11-Support-synaptics-configuration.patch | 348 + ...eload-when-GPU-memory-is-invalidated.patch | 118 + .../0001-clutter-Backport-of-touch-mode.patch | 357 + ...ouchpad-device-property-check-for-Sy.patch | 61 + ...y-reset-scroll-axes-on-slave-devices.patch | 27 + ...dundant-_clutter_paint_node_init_typ.patch | 53 + ...ew-Hide-double-buffered-shadowfb-beh.patch | 46 + ...e-unused-OFFSCREEN_BLIT-feature-flag.patch | 37 + ...sure-_NET_WM_FRAME_DRAWN-timestamp-h.patch | 265 + ...e-switch-events-always-go-through-Me.patch | 64 + ...ndr-Respect-configured-RANDR-panning.patch | 73 + ...ke-check-alive-timeout-configureable.patch | 248 + ...types-Use-basename-in-header-comment.patch | 55 + ...e-sloppy-focus-while-buttons-are-pre.patch | 42 + ...entTime-to-XIAllowEvents-when-unfree.patch | 26 + ...ding-pointer-events-without-a-window.patch | 122 + ...xrender-picture-formats-when-creatin.patch | 136 + ...e-aggressive-in-assuming-X11-backend.patch | 49 + ...anager-Handle-multiple-builtin-panel.patch | 211 + ...Consider-external-layout-before-defa.patch | 152 + ...kms-Trigger-hotplug-processing-on-gp.patch | 41 + ...only-reuse-initial-config-if-monitor.patch | 144 + ...xrandr-Force-an-update-when-resuming.patch | 272 + ...xrandr-Move-dpms-state-and-screen-si.patch | 114 + ...xrandr-Work-around-spurious-hotplugs.patch | 62 + ...ore-heuristics-to-decide-when-to-off.patch | 81 + ...-to-check-whether-renderer-is-hardwa.patch | 108 + ...Destroy-hash-dmabuf-table-after-stre.patch | 40 + ...that-message-is-WM_PROTOCOLS-before-.patch | 101 + ...llow-Xwayland-grabs-on-selected-apps.patch | 35 + ...ylus-serials-on-meta_wayland_seat_ca.patch | 109 + ...ck-for-present-window-out-of-the-act.patch | 150 + ...-t-show-actor-until-meta_window_acto.patch | 35 + ...tor-Special-case-shaped-Java-windows.patch | 35 + ...ade-assert-to-warning-when-adding-wi.patch | 33 + ...us-only-ancestors-that-are-focusable.patch | 83 + ...ace-manager-Expose-layout-properties.patch | 80 + ...button-flags-to-determine-whether-bu.patch | 53 + ...pew-warnings-when-looking-for-X11-di.patch | 304 + ...signal_emit_by_name-from-ClutterActo.patch | 192 + ...l-Fix-doc-for-_cogl_blit_framebuffer.patch | 42 + ...-Fix-software-acceleration-detection.patch | 27 + ...TC-mode-and-output-list-if-no-resour.patch | 28 + ...xrandr-Create-dummy-screen-sized-mon.patch | 185 + ...er-native-Add-API-to-get-primary-GPU.patch | 46 + ...Don-t-leak-DMA-buffer-CoglFramebuffe.patch | 44 + ...ace-Propagate-commit-to-parent-class.patch | 38 + ...rror-and-return-when-trying-to-activ.patch | 42 + ...yland-Make-sure-tmp-.X11-unix-exists.patch | 90 + ...clutter-fix-hole-in-ClutterPaintNode.patch | 167 + ...LE-with-GLES3-and-NV-framebuffer_bli.patch | 77 + ...-DMA-buffer-allocation-to-MetaScreen.patch | 108 + ...l-Relax-formats-on-glBlitFramebuffer.patch | 100 + ...ble-DMA-buffer-based-screen-casting-.patch | 209 + ...tFramebuffer-between-onscreen-offscr.patch | 145 + ...ure-OFFSCREEN_BLIT-to-BLIT_FRAMEBUFF.patch | 115 + ...07-cogl-Expose-cogl_blit_framebuffer.patch | 261 + ...ew-Use-cogl_blit_framebuffer-for-sha.patch | 42 + ...ew-Ignore-clipping-rectangle-for-off.patch | 49 + ...0-cogl-Flush-journal-before-blitting.patch | 34 + ...view-Separate-offscreen-and-shadowfb.patch | 304 + ...tive-Separate-offscreen-and-shadowfb.patch | 98 + ...for-plain-old-x-device-configuration.patch | 375 ++ SOURCES/covscan-fixes.patch | 246 + .../cursor-move-only-screen-cast-fixes.patch | 1115 +++ ...re-gracefully-with-oversized-windows.patch | 85 + SOURCES/eglstream-overview-fixes.patch | 333 + SOURCES/fix-text-selection-drawing.patch | 138 + SOURCES/geometric-picking.patch | 2091 ++++++ SOURCES/handle-hotplug-better.patch | 614 ++ SOURCES/idle-monitor-reset-fix.patch | 128 + SOURCES/inherit-xrandr-metamodes.patch | 365 + SOURCES/input-after-long-idle-fix.patch | 374 ++ SOURCES/monitor-config-policy.patch | 2356 +++++++ .../mutter-bump-screencast-api-version.patch | 13 + ...een-cast-remote-desktop-improvements.patch | 5977 +++++++++++++++++ SOURCES/shadow-buffer-tile-damage.patch | 3248 +++++++++ SOURCES/wayland-frame-callback-rework.patch | 1045 +++ SOURCES/xwayland-xauth-xhost-user.patch | 479 ++ SPECS/mutter.spec | 1523 +++++ 92 files changed, 28954 insertions(+) create mode 100644 .gitignore create mode 100644 .mutter.metadata create mode 100644 SOURCES/0001-Add-support-for-quad-buffer-stereo.patch create mode 100644 SOURCES/0001-Create-explicit-WacomDevices-for-tablet-touchpad-dev.patch create mode 100644 SOURCES/0001-EGL-Include-EGL-eglmesaext.h.patch create mode 100644 SOURCES/0001-Revert-MetaMonitorManager-ignore-hotplug_mode_update.patch create mode 100644 SOURCES/0001-Skip-wacom-touchpads-when-updating-setting.patch create mode 100644 SOURCES/0001-backend-Add-getter-for-MetaScreenCast.patch create mode 100644 SOURCES/0001-backends-Always-enable-tap-to-click-drag-on-opaque-W.patch create mode 100644 SOURCES/0001-backends-Check-both-input-settings-and-mapper-for-ta.patch create mode 100644 SOURCES/0001-backends-Consider-pen-eraser-devices-when-looking-fo.patch create mode 100644 SOURCES/0001-backends-x11-Observe-multiple-pad-mode-switch-button.patch create mode 100644 SOURCES/0001-backends-x11-Support-synaptics-configuration.patch create mode 100644 SOURCES/0001-background-Reload-when-GPU-memory-is-invalidated.patch create mode 100644 SOURCES/0001-clutter-Backport-of-touch-mode.patch create mode 100644 SOURCES/0001-clutter-Extend-touchpad-device-property-check-for-Sy.patch create mode 100644 SOURCES/0001-clutter-Only-reset-scroll-axes-on-slave-devices.patch create mode 100644 SOURCES/0001-clutter-avoid-redundant-_clutter_paint_node_init_typ.patch create mode 100644 SOURCES/0001-clutter-stage-view-Hide-double-buffered-shadowfb-beh.patch create mode 100644 SOURCES/0001-cogl-Remove-unused-OFFSCREEN_BLIT-feature-flag.patch create mode 100644 SOURCES/0001-compositor-Make-sure-_NET_WM_FRAME_DRAWN-timestamp-h.patch create mode 100644 SOURCES/0001-core-Let-pad-mode-switch-events-always-go-through-Me.patch create mode 100644 SOURCES/0001-crtc-xrandr-Respect-configured-RANDR-panning.patch create mode 100644 SOURCES/0001-display-Make-check-alive-timeout-configureable.patch create mode 100644 SOURCES/0001-enum-types-Use-basename-in-header-comment.patch create mode 100644 SOURCES/0001-events-Don-t-move-sloppy-focus-while-buttons-are-pre.patch create mode 100644 SOURCES/0001-events-Pass-CurrentTime-to-XIAllowEvents-when-unfree.patch create mode 100644 SOURCES/0001-events-Sync-pending-pointer-events-without-a-window.patch create mode 100644 SOURCES/0001-iconcache-Avoid-xrender-picture-formats-when-creatin.patch create mode 100644 SOURCES/0001-main-be-more-aggressive-in-assuming-X11-backend.patch create mode 100644 SOURCES/0001-monitor-config-manager-Handle-multiple-builtin-panel.patch create mode 100644 SOURCES/0001-monitor-manager-Consider-external-layout-before-defa.patch create mode 100644 SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch create mode 100644 SOURCES/0001-monitor-manager-only-reuse-initial-config-if-monitor.patch create mode 100644 SOURCES/0001-monitor-manager-xrandr-Force-an-update-when-resuming.patch create mode 100644 SOURCES/0001-monitor-manager-xrandr-Move-dpms-state-and-screen-si.patch create mode 100644 SOURCES/0001-monitor-manager-xrandr-Work-around-spurious-hotplugs.patch create mode 100644 SOURCES/0001-output-kms-Add-more-heuristics-to-decide-when-to-off.patch create mode 100644 SOURCES/0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch create mode 100644 SOURCES/0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch create mode 100644 SOURCES/0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch create mode 100644 SOURCES/0001-wayland-Allow-Xwayland-grabs-on-selected-apps.patch create mode 100644 SOURCES/0001-wayland-Check-stylus-serials-on-meta_wayland_seat_ca.patch create mode 100644 SOURCES/0001-wayland-Move-check-for-present-window-out-of-the-act.patch create mode 100644 SOURCES/0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch create mode 100644 SOURCES/0001-window-actor-Special-case-shaped-Java-windows.patch create mode 100644 SOURCES/0001-workspace-Downgrade-assert-to-warning-when-adding-wi.patch create mode 100644 SOURCES/0001-workspace-Focus-only-ancestors-that-are-focusable.patch create mode 100644 SOURCES/0001-workspace-manager-Expose-layout-properties.patch create mode 100644 SOURCES/0001-x11-Check-wacom-button-flags-to-determine-whether-bu.patch create mode 100644 SOURCES/0001-xwayland-Don-t-spew-warnings-when-looking-for-X11-di.patch create mode 100644 SOURCES/0002-clutter-avoid-g_signal_emit_by_name-from-ClutterActo.patch create mode 100644 SOURCES/0002-cogl-Fix-doc-for-_cogl_blit_framebuffer.patch create mode 100644 SOURCES/0002-cogl-gpu-info-Fix-software-acceleration-detection.patch create mode 100644 SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch create mode 100644 SOURCES/0002-monitor-manager-xrandr-Create-dummy-screen-sized-mon.patch create mode 100644 SOURCES/0002-renderer-native-Add-API-to-get-primary-GPU.patch create mode 100644 SOURCES/0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch create mode 100644 SOURCES/0002-wayland-dnd-surface-Propagate-commit-to-parent-class.patch create mode 100644 SOURCES/0002-window-Emit-an-error-and-return-when-trying-to-activ.patch create mode 100644 SOURCES/0002-xwayland-Make-sure-tmp-.X11-unix-exists.patch create mode 100644 SOURCES/0003-clutter-fix-hole-in-ClutterPaintNode.patch create mode 100644 SOURCES/0003-cogl-Replace-ANGLE-with-GLES3-and-NV-framebuffer_bli.patch create mode 100644 SOURCES/0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch create mode 100644 SOURCES/0004-cogl-Relax-formats-on-glBlitFramebuffer.patch create mode 100644 SOURCES/0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch create mode 100644 SOURCES/0005-cogl-Allow-glBlitFramebuffer-between-onscreen-offscr.patch create mode 100644 SOURCES/0006-cogl-Rename-feature-OFFSCREEN_BLIT-to-BLIT_FRAMEBUFF.patch create mode 100644 SOURCES/0007-cogl-Expose-cogl_blit_framebuffer.patch create mode 100644 SOURCES/0008-clutter-stage-view-Use-cogl_blit_framebuffer-for-sha.patch create mode 100644 SOURCES/0009-clutter-stage-view-Ignore-clipping-rectangle-for-off.patch create mode 100644 SOURCES/0010-cogl-Flush-journal-before-blitting.patch create mode 100644 SOURCES/0011-clutter-stage-view-Separate-offscreen-and-shadowfb.patch create mode 100644 SOURCES/0012-renderer-native-Separate-offscreen-and-shadowfb.patch create mode 100644 SOURCES/add-support-for-plain-old-x-device-configuration.patch create mode 100644 SOURCES/covscan-fixes.patch create mode 100644 SOURCES/cursor-move-only-screen-cast-fixes.patch create mode 100644 SOURCES/deal-more-gracefully-with-oversized-windows.patch create mode 100644 SOURCES/eglstream-overview-fixes.patch create mode 100644 SOURCES/fix-text-selection-drawing.patch create mode 100644 SOURCES/geometric-picking.patch create mode 100644 SOURCES/handle-hotplug-better.patch create mode 100644 SOURCES/idle-monitor-reset-fix.patch create mode 100644 SOURCES/inherit-xrandr-metamodes.patch create mode 100644 SOURCES/input-after-long-idle-fix.patch create mode 100644 SOURCES/monitor-config-policy.patch create mode 100644 SOURCES/mutter-bump-screencast-api-version.patch create mode 100644 SOURCES/screen-cast-remote-desktop-improvements.patch create mode 100644 SOURCES/shadow-buffer-tile-damage.patch create mode 100644 SOURCES/wayland-frame-callback-rework.patch create mode 100644 SOURCES/xwayland-xauth-xhost-user.patch create mode 100644 SPECS/mutter.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d71c898 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/mutter-3.32.2.tar.xz diff --git a/.mutter.metadata b/.mutter.metadata new file mode 100644 index 0000000..9e3c1a4 --- /dev/null +++ b/.mutter.metadata @@ -0,0 +1 @@ +5068f43514a6212e4b5b5f7f856b7713cbc3d420 SOURCES/mutter-3.32.2.tar.xz diff --git a/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch b/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch new file mode 100644 index 0000000..1130e5d --- /dev/null +++ b/SOURCES/0001-Add-support-for-quad-buffer-stereo.patch @@ -0,0 +1,907 @@ +From fd67e75df470b50510b68ccf0f52b0b98d05c63f Mon Sep 17 00:00:00 2001 +From: "Owen W. Taylor" +Date: Thu, 8 May 2014 18:44:15 -0400 +Subject: [PATCH] Add support for quad-buffer stereo + +Track the stereo status of windows using the new EXT_stereo_tree +GLX extension. + +When stereo is enabled or disabled, a restart is triggered via +meta_restart() after a timeout, setting a _META_ENABLE_STEREO +property on the root window to indicate whether we should +turn on a stereo stage for clutter. The property avoids a loop, +since we need to enable stereo *before* initializing Clutter and GL, +but we need GL to figure out whether we have stereo windows. + +Stereo windows are drawn to the stage using new functionality +in Cogl to setup a stereo context, select which buffer to draw +to, and draw either the left or right buffer of a stereo +texture_from_pixmap. +--- + src/compositor/compositor-private.h | 9 ++ + src/compositor/compositor.c | 125 +++++++++++++++ + src/compositor/meta-shaped-texture-private.h | 5 +- + src/compositor/meta-shaped-texture.c | 85 +++++++++- + src/compositor/meta-surface-actor-wayland.c | 2 +- + src/compositor/meta-surface-actor-x11.c | 54 ++++++- + src/compositor/meta-surface-actor-x11.h | 5 + + src/compositor/meta-window-actor-private.h | 5 + + src/compositor/meta-window-actor.c | 22 +++ + src/core/main.c | 4 + + src/core/stereo.c | 154 +++++++++++++++++++ + src/core/stereo.h | 28 ++++ + src/meson.build | 2 + + src/wayland/meta-wayland-surface.c | 2 +- + 14 files changed, 482 insertions(+), 20 deletions(-) + create mode 100644 src/core/stereo.c + create mode 100644 src/core/stereo.h + +diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h +index 6ab33416c..f70087512 100644 +--- a/src/compositor/compositor-private.h ++++ b/src/compositor/compositor-private.h +@@ -24,6 +24,10 @@ struct _MetaCompositor + gint64 server_time_query_time; + gint64 server_time_offset; + ++ int glx_opcode; ++ guint stereo_tree_ext : 1; ++ guint have_stereo_windows : 1; ++ + guint server_time_is_monotonic_time : 1; + + ClutterActor *stage, *window_group, *top_window_group, *feedback_group; +@@ -63,6 +67,11 @@ void meta_end_modal_for_plugin (MetaCompositor *compositor, + gint64 meta_compositor_monotonic_time_to_server_time (MetaDisplay *display, + gint64 monotonic_time); + ++gboolean meta_compositor_window_is_stereo (MetaDisplay *display, ++ Window xwindow); ++void meta_compositor_select_stereo_notify (MetaDisplay *display, ++ Window xwindow); ++ + void meta_compositor_flash_window (MetaCompositor *compositor, + MetaWindow *window); + +diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c +index 2a2c8fb3b..6c08c8fe4 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -69,6 +69,8 @@ + #include "core/core.h" + #include "core/display-private.h" + #include "core/frame.h" ++#include "core/stack-tracker.h" ++#include "core/stereo.h" + #include "core/util-private.h" + #include "core/window-private.h" + #include "meta/compositor-mutter.h" +@@ -514,6 +516,94 @@ redirect_windows (MetaX11Display *x11_display) + } + } + ++#define GLX_STEREO_TREE_EXT 0x20F5 ++#define GLX_STEREO_NOTIFY_MASK_EXT 0x00000001 ++#define GLX_STEREO_NOTIFY_EXT 0x00000000 ++ ++typedef struct { ++ int type; ++ unsigned long serial; ++ Bool send_event; ++ Display *display; ++ int extension; ++ int evtype; ++ Drawable window; ++ Bool stereo_tree; ++} StereoNotifyEvent; ++ ++static gboolean ++display_has_stereo_tree_ext (MetaX11Display *x11_display) ++{ ++ Display *xdisplay = x11_display->xdisplay; ++ const char *extensions_string; ++ ++ static const char * (*query_extensions_string) (Display *display, ++ int screen); ++ ++ if (query_extensions_string == NULL) ++ query_extensions_string = ++ (const char * (*) (Display *, int)) ++ cogl_get_proc_address ("glXQueryExtensionsString"); ++ ++ extensions_string = query_extensions_string (xdisplay, ++ meta_x11_display_get_screen_number (x11_display)); ++ ++ return extensions_string && strstr (extensions_string, "EXT_stereo_tree") != 0; ++} ++ ++#include ++ ++gboolean ++meta_compositor_window_is_stereo (MetaDisplay *display, ++ Window xwindow) ++{ ++ MetaCompositor *compositor = get_compositor_for_display (display); ++ Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); ++ ++ static int (*query_drawable) (Display *dpy, ++ Drawable draw, ++ int attribute, ++ unsigned int *value); ++ ++ if (compositor->stereo_tree_ext) ++ { ++ unsigned int stereo_tree = 0; ++ ++ if (query_drawable == NULL) ++ query_drawable = ++ (int (*) (Display *, Drawable, int, unsigned int *)) ++ cogl_get_proc_address ("glXQueryDrawable"); ++ ++ query_drawable (xdisplay, xwindow, GLX_STEREO_TREE_EXT, &stereo_tree); ++ ++ return stereo_tree != 0; ++ } ++ else ++ return FALSE; ++} ++ ++void ++meta_compositor_select_stereo_notify (MetaDisplay *display, ++ Window xwindow) ++{ ++ MetaCompositor *compositor = get_compositor_for_display (display); ++ Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); ++ ++ static void (*select_event) (Display *dpy, ++ Drawable draw, ++ unsigned long event_mask); ++ ++ if (compositor->stereo_tree_ext) ++ { ++ if (select_event == NULL) ++ select_event = ++ (void (*) (Display *, Drawable, unsigned long)) ++ cogl_get_proc_address ("glXSelectEvent"); ++ ++ select_event (xdisplay, xwindow, GLX_STEREO_NOTIFY_MASK_EXT); ++ } ++} ++ + void + meta_compositor_manage (MetaCompositor *compositor) + { +@@ -525,6 +615,8 @@ meta_compositor_manage (MetaCompositor *compositor) + { + xdisplay = display->x11_display->xdisplay; + meta_x11_display_set_cm_selection (display->x11_display); ++ ++ compositor->stereo_tree_ext = display_has_stereo_tree_ext (display->x11_display); + } + + compositor->stage = meta_backend_get_stage (backend); +@@ -822,6 +914,23 @@ meta_compositor_process_event (MetaCompositor *compositor, + if (window) + process_damage (compositor, (XDamageNotifyEvent *) event, window); + } ++ else if (!meta_is_wayland_compositor () && ++ event->type == GenericEvent && ++ event->xcookie.extension == compositor->glx_opcode) ++ { ++ if (event->xcookie.evtype == GLX_STEREO_NOTIFY_EXT) ++ { ++ StereoNotifyEvent *stereo_event = (StereoNotifyEvent *)(event->xcookie.data); ++ window = meta_x11_display_lookup_x_window (x11_display, stereo_event->window); ++ ++ if (window != NULL) ++ { ++ MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); ++ meta_window_actor_stereo_notify (window_actor, stereo_event->stereo_tree); ++ meta_stack_tracker_queue_sync_stack (window->display->stack_tracker); ++ } ++ } ++ } + + if (compositor->have_x11_sync_object) + meta_sync_ring_handle_event (event); +@@ -1038,6 +1147,7 @@ meta_compositor_sync_stack (MetaCompositor *compositor, + GList *stack) + { + GList *old_stack; ++ int stereo_window_count = 0; + + /* This is painful because hidden windows that we are in the process + * of animating out of existence. They'll be at the bottom of the +@@ -1113,6 +1223,8 @@ meta_compositor_sync_stack (MetaCompositor *compositor, + * near the front of the other.) + */ + compositor->windows = g_list_prepend (compositor->windows, actor); ++ if (meta_window_actor_is_stereo (actor)) ++ stereo_window_count++; + + stack = g_list_remove (stack, window); + old_stack = g_list_remove (old_stack, actor); +@@ -1120,6 +1232,8 @@ meta_compositor_sync_stack (MetaCompositor *compositor, + + sync_actor_stacking (compositor); + ++ meta_stereo_set_have_stereo_windows (stereo_window_count > 0); ++ + if (compositor->top_window_actor) + g_signal_handlers_disconnect_by_func (compositor->top_window_actor, + on_top_window_actor_destroyed, +@@ -1325,6 +1439,17 @@ meta_compositor_new (MetaDisplay *display) + meta_post_paint_func, + compositor, + NULL); ++ if (!meta_is_wayland_compositor ()) ++ { ++ Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); ++ int glx_major_opcode, glx_first_event, glx_first_error; ++ ++ if (XQueryExtension (xdisplay, ++ "GLX", ++ &glx_major_opcode, &glx_first_event, &glx_first_error)) ++ compositor->glx_opcode = glx_major_opcode; ++ } ++ + return compositor; + } + +diff --git a/src/compositor/meta-shaped-texture-private.h b/src/compositor/meta-shaped-texture-private.h +index a86a2bff0..d0efdd4dc 100644 +--- a/src/compositor/meta-shaped-texture-private.h ++++ b/src/compositor/meta-shaped-texture-private.h +@@ -31,8 +31,9 @@ + #include "meta/meta-shaped-texture.h" + + ClutterActor *meta_shaped_texture_new (void); +-void meta_shaped_texture_set_texture (MetaShapedTexture *stex, +- CoglTexture *texture); ++void meta_shaped_texture_set_textures (MetaShapedTexture *stex, ++ CoglTexture *texture, ++ CoglTexture *texture_right); + void meta_shaped_texture_set_is_y_inverted (MetaShapedTexture *stex, + gboolean is_y_inverted); + void meta_shaped_texture_set_snippet (MetaShapedTexture *stex, +diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c +index d64e214e5..332b4c814 100644 +--- a/src/compositor/meta-shaped-texture.c ++++ b/src/compositor/meta-shaped-texture.c +@@ -88,8 +88,10 @@ struct _MetaShapedTexture + ClutterActor parent; + + MetaTextureTower *paint_tower; ++ MetaTextureTower *paint_tower_right; + + CoglTexture *texture; ++ CoglTexture *texture_right; + CoglTexture *mask_texture; + CoglSnippet *snippet; + +@@ -160,6 +162,7 @@ static void + meta_shaped_texture_init (MetaShapedTexture *stex) + { + stex->paint_tower = meta_texture_tower_new (); ++ stex->paint_tower_right = NULL; /* demand create */ + + stex->texture = NULL; + stex->mask_texture = NULL; +@@ -297,7 +300,11 @@ meta_shaped_texture_dispose (GObject *object) + meta_texture_tower_free (stex->paint_tower); + stex->paint_tower = NULL; + ++ g_clear_pointer (&stex->paint_tower, meta_texture_tower_free); ++ g_clear_pointer (&stex->paint_tower_right, meta_texture_tower_free); ++ + g_clear_pointer (&stex->texture, cogl_object_unref); ++ g_clear_pointer (&stex->texture_right, cogl_object_unref); + g_clear_pointer (&stex->opaque_region, cairo_region_destroy); + + meta_shaped_texture_set_mask_texture (stex, NULL); +@@ -507,8 +514,9 @@ paint_clipped_rectangle (MetaShapedTexture *stex, + } + + static void +-set_cogl_texture (MetaShapedTexture *stex, +- CoglTexture *cogl_tex) ++set_cogl_textures (MetaShapedTexture *stex, ++ CoglTexture *cogl_tex, ++ CoglTexture *cogl_tex_right) + { + int width, height; + +@@ -516,8 +524,11 @@ set_cogl_texture (MetaShapedTexture *stex, + + if (stex->texture) + cogl_object_unref (stex->texture); ++ if (stex->texture_right) ++ cogl_object_unref (stex->texture_right); + + stex->texture = cogl_tex; ++ stex->texture_right = cogl_tex_right; + + if (cogl_tex != NULL) + { +@@ -531,6 +542,9 @@ set_cogl_texture (MetaShapedTexture *stex, + height = 0; + } + ++ if (cogl_tex_right != NULL) ++ cogl_object_ref (cogl_tex_right); ++ + if (stex->tex_width != width || + stex->tex_height != height) + { +@@ -544,8 +558,23 @@ set_cogl_texture (MetaShapedTexture *stex, + * previous buffer. We only queue a redraw in response to surface + * damage. */ + ++ if (cogl_tex_right != NULL) ++ { ++ if (stex->paint_tower_right == NULL) ++ stex->paint_tower_right = meta_texture_tower_new (); ++ } ++ else ++ { ++ g_clear_pointer (&stex->paint_tower_right, meta_texture_tower_free); ++ } ++ + if (stex->create_mipmaps) +- meta_texture_tower_set_base_texture (stex->paint_tower, cogl_tex); ++ { ++ meta_texture_tower_set_base_texture (stex->paint_tower, cogl_tex); ++ ++ if (stex->paint_tower_right) ++ meta_texture_tower_set_base_texture (stex->paint_tower_right, cogl_tex_right); ++ } + } + + static gboolean +@@ -779,7 +808,9 @@ meta_shaped_texture_paint (ClutterActor *actor) + { + MetaShapedTexture *stex = META_SHAPED_TEXTURE (actor); + CoglTexture *paint_tex; ++ CoglTexture *paint_tex_right = NULL; + CoglFramebuffer *fb; ++ gboolean stereo; + + if (!stex->texture) + return; +@@ -841,7 +872,32 @@ meta_shaped_texture_paint (ClutterActor *actor) + return; + + fb = cogl_get_draw_framebuffer (); +- do_paint (META_SHAPED_TEXTURE (actor), fb, paint_tex, stex->clip_region); ++ ++ stereo = stex->texture_right && cogl_framebuffer_get_is_stereo (fb); ++ ++ if (stereo) ++ { ++ if (stex->create_mipmaps) ++ paint_tex_right = meta_texture_tower_get_paint_texture (stex->paint_tower_right); ++ ++ if (!paint_tex_right) ++ paint_tex_right = COGL_TEXTURE (stex->texture_right); ++ } ++ else ++ paint_tex_right = NULL; ++ ++ if (stereo) ++ cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_LEFT); ++ do_paint (stex, fb, paint_tex, stex->clip_region); ++ if (stereo) ++ cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH); ++ ++ if (paint_tex_right != NULL) ++ { ++ cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_RIGHT); ++ do_paint (stex, fb, paint_tex_right, stex->clip_region); ++ cogl_framebuffer_set_stereo_mode (fb, COGL_STEREO_BOTH); ++ } + } + + static void +@@ -915,6 +971,12 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, + stex->create_mipmaps = create_mipmaps; + base_texture = create_mipmaps ? stex->texture : NULL; + meta_texture_tower_set_base_texture (stex->paint_tower, base_texture); ++ ++ if (stex->paint_tower_right) ++ { ++ base_texture = create_mipmaps ? stex->texture_right : NULL; ++ meta_texture_tower_set_base_texture (stex->paint_tower_right, base_texture); ++ } + } + } + +@@ -1046,6 +1108,12 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, + clip.y, + clip.width, + clip.height); ++ if (stex->paint_tower_right) ++ meta_texture_tower_update_area (stex->paint_tower_right, ++ clip.x, ++ clip.y, ++ clip.width, ++ clip.height); + + stex->prev_invalidation = stex->last_invalidation; + stex->last_invalidation = g_get_monotonic_time (); +@@ -1092,17 +1160,18 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, + } + + /** +- * meta_shaped_texture_set_texture: ++ * meta_shaped_texture_set_textures: + * @stex: The #MetaShapedTexture + * @pixmap: The #CoglTexture to display + */ + void +-meta_shaped_texture_set_texture (MetaShapedTexture *stex, +- CoglTexture *texture) ++meta_shaped_texture_set_textures (MetaShapedTexture *stex, ++ CoglTexture *texture, ++ CoglTexture *texture_right) + { + g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); + +- set_cogl_texture (stex, texture); ++ set_cogl_textures (stex, texture, texture_right); + } + + /** +diff --git a/src/compositor/meta-surface-actor-wayland.c b/src/compositor/meta-surface-actor-wayland.c +index f8d6c32b7..a75c4dd09 100644 +--- a/src/compositor/meta-surface-actor-wayland.c ++++ b/src/compositor/meta-surface-actor-wayland.c +@@ -182,7 +182,7 @@ meta_surface_actor_wayland_dispose (GObject *object) + MetaShapedTexture *stex = + meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); + +- meta_shaped_texture_set_texture (stex, NULL); ++ meta_shaped_texture_set_textures (stex, NULL, NULL); + if (self->surface) + { + g_object_remove_weak_pointer (G_OBJECT (self->surface), +diff --git a/src/compositor/meta-surface-actor-x11.c b/src/compositor/meta-surface-actor-x11.c +index 244b1e885..3cd164d77 100644 +--- a/src/compositor/meta-surface-actor-x11.c ++++ b/src/compositor/meta-surface-actor-x11.c +@@ -32,6 +32,7 @@ + #include "cogl/winsys/cogl-texture-pixmap-x11.h" + #include "compositor/meta-cullable.h" + #include "compositor/meta-shaped-texture-private.h" ++#include "compositor-private.h" + #include "core/window-private.h" + #include "meta/meta-x11-errors.h" + #include "x11/meta-x11-display-private.h" +@@ -46,6 +47,7 @@ struct _MetaSurfaceActorX11 + MetaDisplay *display; + + CoglTexture *texture; ++ CoglTexture *texture_right; + Pixmap pixmap; + Damage damage; + +@@ -61,6 +63,8 @@ struct _MetaSurfaceActorX11 + guint size_changed : 1; + + guint unredirected : 1; ++ ++ guint stereo : 1; + }; + + G_DEFINE_TYPE (MetaSurfaceActorX11, +@@ -96,7 +100,7 @@ detach_pixmap (MetaSurfaceActorX11 *self) + * you are supposed to be able to free a GLXPixmap after freeing the underlying + * pixmap, but it certainly doesn't work with current DRI/Mesa + */ +- meta_shaped_texture_set_texture (stex, NULL); ++ meta_shaped_texture_set_textures (stex, NULL, NULL); + cogl_flush (); + + meta_x11_error_trap_push (display->x11_display); +@@ -105,6 +109,7 @@ detach_pixmap (MetaSurfaceActorX11 *self) + meta_x11_error_trap_pop (display->x11_display); + + g_clear_pointer (&self->texture, cogl_object_unref); ++ g_clear_pointer (&self->texture_right, cogl_object_unref); + } + + static void +@@ -114,23 +119,37 @@ set_pixmap (MetaSurfaceActorX11 *self, + CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); + MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); + CoglError *error = NULL; +- CoglTexture *texture; ++ CoglTexturePixmapX11 *texture; ++ CoglTexturePixmapX11 *texture_right; + + g_assert (self->pixmap == None); + self->pixmap = pixmap; + +- texture = COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, self->pixmap, FALSE, &error)); ++ if (self->stereo) ++ texture = cogl_texture_pixmap_x11_new_left (ctx, pixmap, FALSE, &error); ++ else ++ texture = cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, &error); ++ ++ if (self->stereo) ++ texture_right = cogl_texture_pixmap_x11_new_right (texture); ++ else ++ texture_right = NULL; + + if (error != NULL) + { + g_warning ("Failed to allocate stex texture: %s", error->message); + cogl_error_free (error); + } +- else if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (COGL_TEXTURE_PIXMAP_X11 (texture)))) ++ else if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (texture))) + g_warning ("NOTE: Not using GLX TFP!\n"); + +- self->texture = texture; +- meta_shaped_texture_set_texture (stex, texture); ++ self->texture = COGL_TEXTURE (texture); ++ if (self->stereo) ++ self->texture_right = COGL_TEXTURE (texture_right); ++ ++ meta_shaped_texture_set_textures (stex, ++ COGL_TEXTURE (texture), ++ COGL_TEXTURE (texture_right));; + } + + static void +@@ -419,8 +438,8 @@ reset_texture (MetaSurfaceActorX11 *self) + /* Setting the texture to NULL will cause all the FBO's cached by the + * shaped texture's MetaTextureTower to be discarded and recreated. + */ +- meta_shaped_texture_set_texture (stex, NULL); +- meta_shaped_texture_set_texture (stex, self->texture); ++ meta_shaped_texture_set_textures (stex, NULL, NULL); ++ meta_shaped_texture_set_textures (stex, self->texture, self->texture_right); + } + + MetaSurfaceActor * +@@ -428,12 +447,17 @@ meta_surface_actor_x11_new (MetaWindow *window) + { + MetaSurfaceActorX11 *self = g_object_new (META_TYPE_SURFACE_ACTOR_X11, NULL); + MetaDisplay *display = meta_window_get_display (window); ++ Window xwindow; + + g_assert (!meta_is_wayland_compositor ()); + + self->window = window; + self->display = display; + ++ xwindow = meta_window_x11_get_toplevel_xwindow (window); ++ self->stereo = meta_compositor_window_is_stereo (display, xwindow); ++ meta_compositor_select_stereo_notify (display, xwindow); ++ + g_signal_connect_object (self->display, "gl-video-memory-purged", + G_CALLBACK (reset_texture), self, G_CONNECT_SWAPPED); + +@@ -463,3 +487,17 @@ meta_surface_actor_x11_set_size (MetaSurfaceActorX11 *self, + self->last_height = height; + meta_shaped_texture_set_fallback_size (stex, width, height); + } ++ ++void ++meta_surface_actor_x11_stereo_notify (MetaSurfaceActorX11 *self, ++ gboolean stereo_tree) ++{ ++ self->stereo = stereo_tree != FALSE; ++ detach_pixmap (self); ++} ++ ++gboolean ++meta_surface_actor_x11_is_stereo (MetaSurfaceActorX11 *self) ++{ ++ return self->stereo; ++} +diff --git a/src/compositor/meta-surface-actor-x11.h b/src/compositor/meta-surface-actor-x11.h +index 2c4ed4dd6..3bdd5fdb0 100644 +--- a/src/compositor/meta-surface-actor-x11.h ++++ b/src/compositor/meta-surface-actor-x11.h +@@ -47,6 +47,11 @@ MetaSurfaceActor * meta_surface_actor_x11_new (MetaWindow *window); + void meta_surface_actor_x11_set_size (MetaSurfaceActorX11 *self, + int width, int height); + ++void meta_surface_actor_x11_stereo_notify (MetaSurfaceActorX11 *self, ++ gboolean stereo_tree); ++ ++gboolean meta_surface_actor_x11_is_stereo (MetaSurfaceActorX11 *self); ++ + G_END_DECLS + + #endif /* __META_SURFACE_ACTOR_X11_H__ */ +diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h +index 6333f43db..9c1c12d09 100644 +--- a/src/compositor/meta-window-actor-private.h ++++ b/src/compositor/meta-window-actor-private.h +@@ -76,4 +76,9 @@ MetaSurfaceActor *meta_window_actor_get_surface (MetaWindowActor *self); + void meta_window_actor_update_surface (MetaWindowActor *self); + MetaWindowActor *meta_window_actor_from_window (MetaWindow *window); + ++void meta_window_actor_stereo_notify (MetaWindowActor *actor, ++ gboolean stereo_tree); ++ ++gboolean meta_window_actor_is_stereo (MetaWindowActor *actor); ++ + #endif /* META_WINDOW_ACTOR_PRIVATE_H */ +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index 1c8dc8fe5..11686d00b 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -2031,3 +2031,25 @@ screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface) + iface->capture_into = meta_window_actor_capture_into; + iface->has_damage = meta_window_actor_has_damage; + } ++ ++void ++meta_window_actor_stereo_notify (MetaWindowActor *self, ++ gboolean stereo_tree) ++{ ++ MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); ++ ++ if (META_IS_SURFACE_ACTOR_X11 (priv->surface)) ++ meta_surface_actor_x11_stereo_notify (META_SURFACE_ACTOR_X11 (priv->surface), ++ stereo_tree); ++} ++ ++gboolean ++meta_window_actor_is_stereo (MetaWindowActor *self) ++{ ++ MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); ++ ++ if (META_IS_SURFACE_ACTOR_X11 (priv->surface)) ++ return meta_surface_actor_x11_is_stereo (META_SURFACE_ACTOR_X11 (priv->surface)); ++ else ++ return FALSE; ++} +diff --git a/src/core/main.c b/src/core/main.c +index e8464720f..629f8e94e 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -81,6 +81,7 @@ + #include "meta/meta-backend.h" + #include "meta/meta-x11-errors.h" + #include "meta/prefs.h" ++#include "stereo.h" + #include "ui/ui.h" + #include "x11/session.h" + +@@ -589,6 +590,9 @@ meta_init (void) + + meta_init_backend (backend_gtype); + ++ if (!meta_is_wayland_compositor ()) ++ meta_stereo_init (); ++ + meta_clutter_init (); + + #ifdef HAVE_WAYLAND +diff --git a/src/core/stereo.c b/src/core/stereo.c +new file mode 100644 +index 000000000..817056527 +--- /dev/null ++++ b/src/core/stereo.c +@@ -0,0 +1,154 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++ ++/* ++ * Copyright (C) 2014 Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see . ++ */ ++ ++/* ++ * SECTION:stereo ++ * @short_description: Keep track of whether we are a stereo compositor ++ * ++ * With GLX, we need to use a different GL context for stereo and ++ * non-stereo support. Support for multiple GL contexts is unfinished ++ * in Cogl and entirely lacking in Clutter, so it's by far easier ++ * to just restart Mutter when we detect a stereo window. ++ * ++ * A property _MUTTER_ENABLE_STEREO is maintained on the root window ++ * to know whether we should initialize clutter for stereo or not. ++ * When the presence or absence of stereo windows mismatches the ++ * stereo-enabled state for a sufficiently long period of time, ++ * we restart Mutter. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++ ++#include "display-private.h" ++#include ++#include ++#include ++#include "stereo.h" ++#include "ui/ui.h" ++#include "util-private.h" ++ ++static guint stereo_switch_id = 0; ++static gboolean stereo_enabled = FALSE; ++/* -1 so the first time meta_stereo_set_have_stereo_windows() is called ++ * we avoid the short-circuit and set up a timeout to restart ++ * if necessary */ ++static gboolean stereo_have_windows = (gboolean)-1; ++static gboolean stereo_restart = FALSE; ++ ++#define STEREO_ENABLE_WAIT 1000 ++#define STEREO_DISABLE_WAIT 5000 ++ ++void ++meta_stereo_init (void) ++{ ++ Display *xdisplay; ++ Window root; ++ Atom atom_enable_stereo; ++ Atom type; ++ int format; ++ unsigned long n_items, bytes_after; ++ guchar *data; ++ ++ xdisplay = XOpenDisplay (NULL); ++ if (xdisplay == NULL) ++ meta_fatal ("Unable to open X display %s\n", XDisplayName (NULL)); ++ ++ root = DefaultRootWindow (xdisplay); ++ atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False); ++ ++ XGetWindowProperty (xdisplay, root, atom_enable_stereo, ++ 0, 1, False, XA_INTEGER, ++ &type, &format, &n_items, &bytes_after, &data); ++ if (type == XA_INTEGER) ++ { ++ if (format == 32 && n_items == 1 && bytes_after == 0) ++ { ++ stereo_enabled = *(long *)data; ++ } ++ else ++ { ++ meta_warning ("Bad value for _MUTTER_ENABLE_STEREO property\n"); ++ } ++ ++ XFree (data); ++ } ++ else if (type != None) ++ { ++ meta_warning ("Bad type for _MUTTER_ENABLE_STEREO property\n"); ++ } ++ ++ meta_verbose ("On startup, _MUTTER_ENABLE_STEREO=%s", ++ stereo_enabled ? "yes" : "no"); ++ clutter_x11_set_use_stereo_stage (stereo_enabled); ++ XCloseDisplay (xdisplay); ++} ++ ++static gboolean ++meta_stereo_switch (gpointer data) ++{ ++ stereo_switch_id = 0; ++ stereo_restart = TRUE; ++ ++ meta_restart (stereo_have_windows ? ++ _("Enabling stereo...") : ++ _("Disabling stereo...")); ++ ++ return FALSE; ++} ++ ++void ++meta_stereo_set_have_stereo_windows (gboolean have_windows) ++{ ++ have_windows = have_windows != FALSE; ++ ++ if (!stereo_restart && have_windows != stereo_have_windows) ++ { ++ MetaDisplay *display = meta_get_display (); ++ Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); ++ Window root = DefaultRootWindow (xdisplay); ++ Atom atom_enable_stereo = XInternAtom (xdisplay, "_MUTTER_ENABLE_STEREO", False); ++ long value; ++ ++ stereo_have_windows = have_windows; ++ ++ if (stereo_have_windows) ++ meta_verbose ("Detected stereo windows\n"); ++ else ++ meta_verbose ("No stereo windows detected\n"); ++ ++ value = stereo_have_windows; ++ XChangeProperty (xdisplay, root, ++ atom_enable_stereo, XA_INTEGER, 32, ++ PropModeReplace, (guchar *)&value, 1); ++ ++ if (stereo_switch_id != 0) ++ { ++ g_source_remove (stereo_switch_id); ++ stereo_switch_id = 0; ++ } ++ ++ if (stereo_have_windows != stereo_enabled) ++ stereo_switch_id = g_timeout_add (stereo_have_windows ? STEREO_ENABLE_WAIT : STEREO_DISABLE_WAIT, ++ meta_stereo_switch, NULL); ++ } ++} +diff --git a/src/core/stereo.h b/src/core/stereo.h +new file mode 100644 +index 000000000..ccd1d702a +--- /dev/null ++++ b/src/core/stereo.h +@@ -0,0 +1,28 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++ ++/* ++ * Copyright (C) 2014 Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see . ++ */ ++ ++#ifndef META_STEREO_H ++#define META_STEREO_H ++ ++void meta_stereo_init (void); ++void meta_stereo_set_have_stereo_windows (gboolean have_windows); ++gboolean meta_stereo_is_restart (void); ++void meta_stereo_finish_restart (void); ++ ++#endif +diff --git a/src/meson.build b/src/meson.build +index 9919b5cfb..7cced8f53 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -353,6 +353,8 @@ mutter_sources = [ + 'core/stack.h', + 'core/stack-tracker.c', + 'core/stack-tracker.h', ++ 'core/stereo.c', ++ 'core/stereo.h', + 'core/startup-notification.c', + 'core/startup-notification-private.h', + 'core/util.c', +diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c +index da0acfcbb..ddad1a45c 100644 +--- a/src/wayland/meta-wayland-surface.c ++++ b/src/wayland/meta-wayland-surface.c +@@ -731,7 +731,7 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface *surface, + snippet = meta_wayland_buffer_create_snippet (pending->buffer); + is_y_inverted = meta_wayland_buffer_is_y_inverted (pending->buffer); + +- meta_shaped_texture_set_texture (stex, texture); ++ meta_shaped_texture_set_textures (stex, texture, NULL); + meta_shaped_texture_set_snippet (stex, snippet); + meta_shaped_texture_set_is_y_inverted (stex, is_y_inverted); + g_clear_pointer (&snippet, cogl_object_unref); +-- +2.28.0 + diff --git a/SOURCES/0001-Create-explicit-WacomDevices-for-tablet-touchpad-dev.patch b/SOURCES/0001-Create-explicit-WacomDevices-for-tablet-touchpad-dev.patch new file mode 100644 index 0000000..732b754 --- /dev/null +++ b/SOURCES/0001-Create-explicit-WacomDevices-for-tablet-touchpad-dev.patch @@ -0,0 +1,55 @@ +From 38d88d4e4286c3ada041561426873e44fdba3c40 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Fri, 17 Jan 2020 14:45:00 +0100 +Subject: [PATCH] Create explicit WacomDevices for tablet "touchpad" devices + +--- + src/backends/meta-input-settings.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/src/backends/meta-input-settings.c b/src/backends/meta-input-settings.c +index 28dc387ef9..820a3b201e 100644 +--- a/src/backends/meta-input-settings.c ++++ b/src/backends/meta-input-settings.c +@@ -521,27 +521,34 @@ static gboolean + device_is_tablet_touchpad (MetaInputSettings *input_settings, + ClutterInputDevice *device) + { ++ gboolean is_tablet = FALSE; + #ifdef HAVE_LIBWACOM ++ MetaInputSettingsPrivate *priv; + WacomIntegrationFlags flags = 0; + WacomDevice *wacom_device; + ++ priv = meta_input_settings_get_instance_private (input_settings); ++ + if (clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) + return FALSE; + + wacom_device = +- meta_input_settings_get_tablet_wacom_device (input_settings, +- device); ++ libwacom_new_from_path (priv->wacom_db, ++ clutter_input_device_get_device_node (device), ++ WFALLBACK_NONE, NULL); + if (wacom_device) + { + flags = libwacom_get_integration_flags (wacom_device); + + if ((flags & (WACOM_DEVICE_INTEGRATED_SYSTEM | + WACOM_DEVICE_INTEGRATED_DISPLAY)) == 0) +- return TRUE; ++ is_tablet = TRUE; ++ ++ libwacom_destroy (wacom_device); + } + #endif + +- return FALSE; ++ return is_tablet; + } + + static void +-- +2.25.0.rc2 + diff --git a/SOURCES/0001-EGL-Include-EGL-eglmesaext.h.patch b/SOURCES/0001-EGL-Include-EGL-eglmesaext.h.patch new file mode 100644 index 0000000..ae7ff46 --- /dev/null +++ b/SOURCES/0001-EGL-Include-EGL-eglmesaext.h.patch @@ -0,0 +1,68 @@ +From abfc64268d4135663fb46c5f3529cd5f082a5c20 Mon Sep 17 00:00:00 2001 +From: "Jan Alexander Steffens (heftig)" +Date: Sun, 20 Oct 2019 12:04:31 +0200 +Subject: [PATCH] EGL: Include EGL/eglmesaext.h + +The eglext.h shipped by libglvnd does not include the Mesa extensions, +unlike the header shipped in Mesa. + +Fixes https://gitlab.gnome.org/GNOME/mutter/issues/876 +--- + cogl/cogl/meson.build | 2 +- + src/backends/meta-egl-ext.h | 1 + + src/backends/meta-egl.c | 1 + + src/backends/meta-egl.h | 1 + + 4 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/cogl/cogl/meson.build b/cogl/cogl/meson.build +index cb940420a..8032669e4 100644 +--- a/cogl/cogl/meson.build ++++ b/cogl/cogl/meson.build +@@ -48,7 +48,7 @@ cogl_gl_header_h = configure_file( + built_headers += [cogl_gl_header_h] + + if have_egl +- cogl_egl_includes_string = '#include \n#include ' ++ cogl_egl_includes_string = '#include \n#include \n#include ' + else + cogl_egl_includes_string = '' + endif +diff --git a/src/backends/meta-egl-ext.h b/src/backends/meta-egl-ext.h +index 8705e7d5b..db0b74f76 100644 +--- a/src/backends/meta-egl-ext.h ++++ b/src/backends/meta-egl-ext.h +@@ -29,6 +29,7 @@ + + #include + #include ++#include + + /* + * This is a little different to the tests shipped with EGL implementations, +diff --git a/src/backends/meta-egl.c b/src/backends/meta-egl.c +index 8b953449a..a28eef4ca 100644 +--- a/src/backends/meta-egl.c ++++ b/src/backends/meta-egl.c +@@ -26,6 +26,7 @@ + + #include + #include ++#include + #include + #include + #include +diff --git a/src/backends/meta-egl.h b/src/backends/meta-egl.h +index ff37f124f..81b53b32d 100644 +--- a/src/backends/meta-egl.h ++++ b/src/backends/meta-egl.h +@@ -27,6 +27,7 @@ + + #include + #include ++#include + #include + + #define META_EGL_ERROR meta_egl_error_quark () +-- +2.23.0 + diff --git a/SOURCES/0001-Revert-MetaMonitorManager-ignore-hotplug_mode_update.patch b/SOURCES/0001-Revert-MetaMonitorManager-ignore-hotplug_mode_update.patch new file mode 100644 index 0000000..f61ecf6 --- /dev/null +++ b/SOURCES/0001-Revert-MetaMonitorManager-ignore-hotplug_mode_update.patch @@ -0,0 +1,28 @@ +From d9d355bfd8ecfb7dcf65a3810ec30e12f12673ab Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 24 Feb 2020 16:09:59 +0100 +Subject: [PATCH] Revert "MetaMonitorManager: ignore hotplug_mode_update at + startup" + +This reverts commit 183f4b0c13f3dc9565bf5f693f2e5d61ca0199c9. +--- + src/backends/meta-monitor-manager.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c +index 076dca8cb..0adf2100d 100644 +--- a/src/backends/meta-monitor-manager.c ++++ b/src/backends/meta-monitor-manager.c +@@ -527,8 +527,7 @@ meta_monitor_manager_has_hotplug_mode_update (MetaMonitorManager *manager) + static gboolean + should_use_stored_config (MetaMonitorManager *manager) + { +- return (manager->in_init || +- !meta_monitor_manager_has_hotplug_mode_update (manager)); ++ return !meta_monitor_manager_has_hotplug_mode_update (manager); + } + + static gboolean +-- +2.24.1 + diff --git a/SOURCES/0001-Skip-wacom-touchpads-when-updating-setting.patch b/SOURCES/0001-Skip-wacom-touchpads-when-updating-setting.patch new file mode 100644 index 0000000..ba10e5e --- /dev/null +++ b/SOURCES/0001-Skip-wacom-touchpads-when-updating-setting.patch @@ -0,0 +1,94 @@ +From dafc9cb414fd47112b972d34c205e73797a3c1c1 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Fri, 21 Feb 2020 16:45:35 +0100 +Subject: [PATCH] Skip wacom touchpads when updating setting + +--- + src/backends/meta-input-settings.c | 46 +++++++++++++++++++++++------- + 1 file changed, 36 insertions(+), 10 deletions(-) + +diff --git a/src/backends/meta-input-settings.c b/src/backends/meta-input-settings.c +index cdff7b346..7d866594a 100644 +--- a/src/backends/meta-input-settings.c ++++ b/src/backends/meta-input-settings.c +@@ -569,20 +569,33 @@ update_touchpad_tap_enabled (MetaInputSettings *input_settings, + + priv = meta_input_settings_get_instance_private (input_settings); + input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); +- enabled = device_is_tablet_touchpad (input_settings, device) || +- g_settings_get_boolean (priv->touchpad_settings, "tap-to-click"); + + if (device) + { ++ enabled = device_is_tablet_touchpad (input_settings, device) || ++ g_settings_get_boolean (priv->touchpad_settings, "tap-to-click"); + settings_device_set_bool_setting (input_settings, device, + input_settings_class->set_tap_enabled, + enabled); + } + else + { +- settings_set_bool_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, +- input_settings_class->set_tap_enabled, +- enabled); ++ const GSList *devices, *l; ++ ++ devices = clutter_device_manager_peek_devices (priv->device_manager); ++ for (l = devices; l; l = l->next) ++ { ++ device = l->data; ++ ++ if (clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) ++ continue; ++ ++ enabled = device_is_tablet_touchpad (input_settings, device) || ++ g_settings_get_boolean (priv->touchpad_settings, "tap-to-click"); ++ settings_device_set_bool_setting (input_settings, device, ++ input_settings_class->set_tap_enabled, ++ enabled); ++ } + } + } + +@@ -600,20 +613,33 @@ update_touchpad_tap_and_drag_enabled (MetaInputSettings *input_settings, + + priv = meta_input_settings_get_instance_private (input_settings); + input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); +- enabled = device_is_tablet_touchpad (input_settings, device) || +- g_settings_get_boolean (priv->touchpad_settings, "tap-and-drag"); + + if (device) + { ++ enabled = device_is_tablet_touchpad (input_settings, device) || ++ g_settings_get_boolean (priv->touchpad_settings, "tap-and-drag"); + settings_device_set_bool_setting (input_settings, device, + input_settings_class->set_tap_and_drag_enabled, + enabled); + } + else + { +- settings_set_bool_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, +- input_settings_class->set_tap_and_drag_enabled, +- enabled); ++ const GSList *devices, *l; ++ ++ devices = clutter_device_manager_peek_devices (priv->device_manager); ++ for (l = devices; l; l = l->next) ++ { ++ device = l->data; ++ ++ if (clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) ++ continue; ++ ++ enabled = device_is_tablet_touchpad (input_settings, device) || ++ g_settings_get_boolean (priv->touchpad_settings, "tap-and-drag"); ++ settings_device_set_bool_setting (input_settings, device, ++ input_settings_class->set_tap_and_drag_enabled, ++ enabled); ++ } + } + } + +-- +2.24.1 + diff --git a/SOURCES/0001-backend-Add-getter-for-MetaScreenCast.patch b/SOURCES/0001-backend-Add-getter-for-MetaScreenCast.patch new file mode 100644 index 0000000..d50b11b --- /dev/null +++ b/SOURCES/0001-backend-Add-getter-for-MetaScreenCast.patch @@ -0,0 +1,49 @@ +From 967d8236d81c8689f2fe60621ec7e66d88b43dea Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 17 Jun 2020 17:46:25 +0200 +Subject: [PATCH 1/4] backend: Add getter for MetaScreenCast + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1318 +--- + src/backends/meta-backend-private.h | 2 ++ + src/backends/meta-backend.c | 11 +++++++++++ + 2 files changed, 13 insertions(+) + +diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h +index 81ec81e5f1..77f4da77c4 100644 +--- a/src/backends/meta-backend-private.h ++++ b/src/backends/meta-backend-private.h +@@ -138,6 +138,8 @@ MetaEgl * meta_backend_get_egl (MetaBackend *backend); + + #ifdef HAVE_REMOTE_DESKTOP + MetaRemoteDesktop * meta_backend_get_remote_desktop (MetaBackend *backend); ++ ++MetaScreenCast * meta_backend_get_screen_cast (MetaBackend *backend); + #endif + + gboolean meta_backend_grab_device (MetaBackend *backend, +diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c +index 750a9248a8..b498b7aa44 100644 +--- a/src/backends/meta-backend.c ++++ b/src/backends/meta-backend.c +@@ -965,6 +965,17 @@ meta_backend_get_remote_desktop (MetaBackend *backend) + + return priv->remote_desktop; + } ++ ++/** ++ * meta_backend_get_screen_cast: (skip) ++ */ ++MetaScreenCast * ++meta_backend_get_screen_cast (MetaBackend *backend) ++{ ++ MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); ++ ++ return priv->screen_cast; ++} + #endif /* HAVE_REMOTE_DESKTOP */ + + /** +-- +2.26.2 + diff --git a/SOURCES/0001-backends-Always-enable-tap-to-click-drag-on-opaque-W.patch b/SOURCES/0001-backends-Always-enable-tap-to-click-drag-on-opaque-W.patch new file mode 100644 index 0000000..0420251 --- /dev/null +++ b/SOURCES/0001-backends-Always-enable-tap-to-click-drag-on-opaque-W.patch @@ -0,0 +1,80 @@ +From eeff82f534f81b086d10d53124362d9e316e2cf9 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Thu, 12 Dec 2019 18:05:08 +0100 +Subject: [PATCH] backends: Always enable tap-to-click/drag on opaque Wacom + tablets + +Touch-wise, those are essentially giant touchpads, but have no buttons +associated to the "touchpad" device (There may be pad buttons, but +those are not mouse buttons). + +Without tap-to-click/drag, touch in those devices is somewhat useless +out of the box. Have them always enable these features, despite the +setting. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/968 +--- + src/backends/meta-input-settings.c | 33 ++++++++++++++++++++++++++++-- + 1 file changed, 31 insertions(+), 2 deletions(-) + +diff --git a/src/backends/meta-input-settings.c b/src/backends/meta-input-settings.c +index 2e6672d9c..28dc387ef 100644 +--- a/src/backends/meta-input-settings.c ++++ b/src/backends/meta-input-settings.c +@@ -517,6 +517,33 @@ update_touchpad_disable_while_typing (MetaInputSettings *input_settings, + } + } + ++static gboolean ++device_is_tablet_touchpad (MetaInputSettings *input_settings, ++ ClutterInputDevice *device) ++{ ++#ifdef HAVE_LIBWACOM ++ WacomIntegrationFlags flags = 0; ++ WacomDevice *wacom_device; ++ ++ if (clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) ++ return FALSE; ++ ++ wacom_device = ++ meta_input_settings_get_tablet_wacom_device (input_settings, ++ device); ++ if (wacom_device) ++ { ++ flags = libwacom_get_integration_flags (wacom_device); ++ ++ if ((flags & (WACOM_DEVICE_INTEGRATED_SYSTEM | ++ WACOM_DEVICE_INTEGRATED_DISPLAY)) == 0) ++ return TRUE; ++ } ++#endif ++ ++ return FALSE; ++} ++ + static void + update_touchpad_tap_enabled (MetaInputSettings *input_settings, + ClutterInputDevice *device) +@@ -531,7 +558,8 @@ update_touchpad_tap_enabled (MetaInputSettings *input_settings, + + priv = meta_input_settings_get_instance_private (input_settings); + input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); +- enabled = g_settings_get_boolean (priv->touchpad_settings, "tap-to-click"); ++ enabled = device_is_tablet_touchpad (input_settings, device) || ++ g_settings_get_boolean (priv->touchpad_settings, "tap-to-click"); + + if (device) + { +@@ -561,7 +589,8 @@ update_touchpad_tap_and_drag_enabled (MetaInputSettings *input_settings, + + priv = meta_input_settings_get_instance_private (input_settings); + input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); +- enabled = g_settings_get_boolean (priv->touchpad_settings, "tap-and-drag"); ++ enabled = device_is_tablet_touchpad (input_settings, device) || ++ g_settings_get_boolean (priv->touchpad_settings, "tap-and-drag"); + + if (device) + { +-- +2.23.0 + diff --git a/SOURCES/0001-backends-Check-both-input-settings-and-mapper-for-ta.patch b/SOURCES/0001-backends-Check-both-input-settings-and-mapper-for-ta.patch new file mode 100644 index 0000000..9880a83 --- /dev/null +++ b/SOURCES/0001-backends-Check-both-input-settings-and-mapper-for-ta.patch @@ -0,0 +1,205 @@ +From 20fcc3e045287c1ca591f3e795b19e120479a89a Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Wed, 12 Feb 2020 20:26:56 +0100 +Subject: [PATCH 1/2] backends/x11: Implement is_grouped for X11 + +If the devices have a wacom description, compare those. Otherwise, +look up the devices' VID:PID, if they match they should also be +grouped. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/971 +--- + .../clutter/x11/clutter-input-device-xi2.c | 25 +++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/clutter/clutter/x11/clutter-input-device-xi2.c b/clutter/clutter/x11/clutter-input-device-xi2.c +index ae2fa27..9eca34d 100644 +--- a/clutter/clutter/x11/clutter-input-device-xi2.c ++++ b/clutter/clutter/x11/clutter-input-device-xi2.c +@@ -98,6 +98,31 @@ static gboolean + clutter_input_device_xi2_is_grouped (ClutterInputDevice *device, + ClutterInputDevice *other_device) + { ++#ifdef HAVE_LIBWACOM ++ ClutterInputDeviceXI2 *device_x11 = CLUTTER_INPUT_DEVICE_XI2 (device); ++ ClutterInputDeviceXI2 *other_device_x11 = CLUTTER_INPUT_DEVICE_XI2 (other_device); ++ ++ if (device_x11->wacom_device && ++ other_device_x11->wacom_device && ++ libwacom_compare (device_x11->wacom_device, ++ other_device_x11->wacom_device, ++ WCOMPARE_NORMAL) == 0) ++ return TRUE; ++#endif ++ ++ /* Devices with the same VID:PID get grouped together */ ++ if (clutter_input_device_get_vendor_id (device) && ++ clutter_input_device_get_product_id (device) && ++ clutter_input_device_get_vendor_id (other_device) && ++ clutter_input_device_get_product_id (other_device)) ++ { ++ if (strcmp (clutter_input_device_get_vendor_id (device), ++ clutter_input_device_get_vendor_id (other_device)) == 0 && ++ strcmp (clutter_input_device_get_product_id (device), ++ clutter_input_device_get_product_id (other_device)) == 0) ++ return TRUE; ++ } ++ + return FALSE; + } + +-- +2.24.1 + + +From 5914ab9ac79ce42da054036c4a8f118a3a868cc0 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Fri, 13 Dec 2019 15:26:05 +0100 +Subject: [PATCH 2/2] backends: Check both input settings and mapper for tablet + monitors + +The upper layers (OSDs basically) want to know the monitor that a +tablet is currently assigned to, not the monitor just as configured +through settings. + +This broke proper OSD positioning for display-attached tablets since +commit 87858a4e01d9, as the MetaInputMapper kicks in precisely when +there is no configured monitor for the given device. + +Consulting both about the assigned output will make OSDs pop up +again in the right place. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/971 +--- + src/backends/meta-input-mapper-private.h | 3 ++ + src/backends/meta-input-mapper.c | 26 ++++++++++++ + src/backends/meta-input-settings.c | 54 +++++++++++++++++++++++- + 3 files changed, 81 insertions(+), 2 deletions(-) + +diff --git a/src/backends/meta-input-mapper-private.h b/src/backends/meta-input-mapper-private.h +index 3431457..cdfdccd 100644 +--- a/src/backends/meta-input-mapper-private.h ++++ b/src/backends/meta-input-mapper-private.h +@@ -42,5 +42,8 @@ ClutterInputDevice * + meta_input_mapper_get_logical_monitor_device (MetaInputMapper *mapper, + MetaLogicalMonitor *logical_monitor, + ClutterInputDeviceType device_type); ++MetaLogicalMonitor * ++meta_input_mapper_get_device_logical_monitor (MetaInputMapper *mapper, ++ ClutterInputDevice *device); + + #endif /* META_INPUT_MAPPER_H */ +diff --git a/src/backends/meta-input-mapper.c b/src/backends/meta-input-mapper.c +index fc4f3bd..fe02ab8 100644 +--- a/src/backends/meta-input-mapper.c ++++ b/src/backends/meta-input-mapper.c +@@ -675,3 +675,29 @@ meta_input_mapper_get_logical_monitor_device (MetaInputMapper *mapper, + + return NULL; + } ++ ++MetaLogicalMonitor * ++meta_input_mapper_get_device_logical_monitor (MetaInputMapper *mapper, ++ ClutterInputDevice *device) ++{ ++ MetaMapperOutputInfo *output; ++ MetaLogicalMonitor *logical_monitor; ++ GHashTableIter iter; ++ GList *l; ++ ++ g_hash_table_iter_init (&iter, mapper->output_devices); ++ ++ while (g_hash_table_iter_next (&iter, (gpointer *) &logical_monitor, ++ (gpointer *) &output)) ++ { ++ for (l = output->input_devices; l; l = l->next) ++ { ++ MetaMapperInputInfo *input = l->data; ++ ++ if (input->device == device) ++ return logical_monitor; ++ } ++ } ++ ++ return NULL; ++} +diff --git a/src/backends/meta-input-settings.c b/src/backends/meta-input-settings.c +index b84595e..ab80bee 100644 +--- a/src/backends/meta-input-settings.c ++++ b/src/backends/meta-input-settings.c +@@ -1937,6 +1937,42 @@ meta_input_settings_get_tablet_settings (MetaInputSettings *settings, + return info ? g_object_ref (info->settings) : NULL; + } + ++static ClutterInputDevice * ++find_grouped_pen (MetaInputSettings *settings, ++ ClutterInputDevice *device) ++{ ++ MetaInputSettingsPrivate *priv; ++ GSList *l, *devices; ++ ClutterInputDeviceType device_type; ++ ClutterInputDevice *pen = NULL; ++ ++ device_type = clutter_input_device_get_device_type (device); ++ ++ if (device_type == CLUTTER_TABLET_DEVICE || ++ device_type == CLUTTER_PEN_DEVICE) ++ return device; ++ ++ priv = meta_input_settings_get_instance_private (settings); ++ devices = clutter_device_manager_peek_devices (priv->device_manager); ++ ++ for (l = devices; l; l = l->next) ++ { ++ ClutterInputDevice *device = l->data; ++ ++ device_type = clutter_input_device_get_device_type (l->data); ++ ++ if ((device_type == CLUTTER_TABLET_DEVICE || ++ device_type == CLUTTER_PEN_DEVICE) && ++ clutter_input_device_is_grouped (device, l->data)) ++ { ++ pen = l->data; ++ break; ++ } ++ } ++ ++ return pen; ++} ++ + MetaLogicalMonitor * + meta_input_settings_get_tablet_logical_monitor (MetaInputSettings *settings, + ClutterInputDevice *device) +@@ -1948,13 +1984,27 @@ meta_input_settings_get_tablet_logical_monitor (MetaInputSettings *settings, + g_return_val_if_fail (META_IS_INPUT_SETTINGS (settings), NULL); + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); + ++ if (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE) ++ { ++ device = find_grouped_pen (settings, device); ++ if (!device) ++ return NULL; ++ } ++ + priv = meta_input_settings_get_instance_private (settings); + info = g_hash_table_lookup (priv->mappable_devices, device); + if (!info) + return NULL; + +- meta_input_settings_find_monitor (settings, info->settings, device, +- NULL, &logical_monitor); ++ logical_monitor = ++ meta_input_mapper_get_device_logical_monitor (priv->input_mapper, device); ++ ++ if (!logical_monitor) ++ { ++ meta_input_settings_find_monitor (settings, info->settings, device, ++ NULL, &logical_monitor); ++ } ++ + return logical_monitor; + } + +-- +2.24.1 + diff --git a/SOURCES/0001-backends-Consider-pen-eraser-devices-when-looking-fo.patch b/SOURCES/0001-backends-Consider-pen-eraser-devices-when-looking-fo.patch new file mode 100644 index 0000000..f7fa3b2 --- /dev/null +++ b/SOURCES/0001-backends-Consider-pen-eraser-devices-when-looking-fo.patch @@ -0,0 +1,30 @@ +From e512c397a640994807f239c570333e9942717ef5 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Fri, 13 Dec 2019 17:01:44 +0100 +Subject: [PATCH] backends: Consider pen/eraser devices when looking for + matching WacomDevice + +Those device types are still in use through the X11 backend, breaking some +checks around on that backend... + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/972 +--- + src/backends/meta-input-settings.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/backends/meta-input-settings.c b/src/backends/meta-input-settings.c +index 2e6672d9c..18ae52dd7 100644 +--- a/src/backends/meta-input-settings.c ++++ b/src/backends/meta-input-settings.c +@@ -1589,6 +1589,8 @@ check_add_mappable_device (MetaInputSettings *input_settings, + + #ifdef HAVE_LIBWACOM + if (device_type == CLUTTER_TABLET_DEVICE || ++ device_type == CLUTTER_PEN_DEVICE || ++ device_type == CLUTTER_ERASER_DEVICE || + device_type == CLUTTER_PAD_DEVICE) + { + WacomError *error = libwacom_error_new (); +-- +2.23.0 + diff --git a/SOURCES/0001-backends-x11-Observe-multiple-pad-mode-switch-button.patch b/SOURCES/0001-backends-x11-Observe-multiple-pad-mode-switch-button.patch new file mode 100644 index 0000000..ac6986f --- /dev/null +++ b/SOURCES/0001-backends-x11-Observe-multiple-pad-mode-switch-button.patch @@ -0,0 +1,118 @@ +From a8f12e7afdb35ebda581cee6a32b295cb6e643ec Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Fri, 13 Dec 2019 14:22:12 +0100 +Subject: [PATCH] backends/x11: Observe multiple pad mode switch buttons in a + group + +Some tablets like the Cintiq 24HDT have several mode switch buttons +per group. Those are meant to jump straight to a given mode, however +we just handle cycling across modes (as most other tablets have a +single mode switch button per group). + +So spice up the mode switch handling so we handle multiple mode +switch buttons, assigning each of them a mode. If the device only +has one mode switch button, we do the old-fashioned cycling. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/970 +--- + .../clutter/x11/clutter-input-device-xi2.c | 71 ++++++++++++++++--- + 1 file changed, 60 insertions(+), 11 deletions(-) + +diff --git a/clutter/clutter/x11/clutter-input-device-xi2.c b/clutter/clutter/x11/clutter-input-device-xi2.c +index 1254aca3a..c33adffc2 100644 +--- a/clutter/clutter/x11/clutter-input-device-xi2.c ++++ b/clutter/clutter/x11/clutter-input-device-xi2.c +@@ -318,6 +318,57 @@ clutter_input_device_xi2_get_pad_group_mode (ClutterInputDevice *device, + return g_array_index (device_xi2->group_modes, guint, group); + } + ++static gboolean ++pad_switch_mode (ClutterInputDevice *device, ++ uint32_t button, ++ uint32_t group, ++ uint32_t *mode) ++{ ++ ClutterInputDeviceXI2 *device_x11 = CLUTTER_INPUT_DEVICE_XI2 (device); ++ uint32_t n_buttons, n_modes, button_group, next_mode, i; ++ GList *switch_buttons = NULL; ++ ++ n_buttons = libwacom_get_num_buttons (device_x11->wacom_device); ++ ++ for (i = 0; i < n_buttons; i++) ++ { ++ button_group = clutter_input_device_xi2_get_button_group (device, i); ++ if (button_group == group) ++ switch_buttons = g_list_prepend (switch_buttons, GINT_TO_POINTER (i)); ++ } ++ ++ switch_buttons = g_list_reverse (switch_buttons); ++ n_modes = clutter_input_device_get_group_n_modes (device, group); ++ ++ if (g_list_length (switch_buttons) > 1) ++ { ++ /* If there's multiple switch buttons, we don't toggle but assign a mode ++ * to each of those buttons. ++ */ ++ next_mode = g_list_index (switch_buttons, GINT_TO_POINTER (button)); ++ } ++ else if (switch_buttons) ++ { ++ uint32_t cur_mode; ++ ++ /* If there is a single button, have it toggle across modes */ ++ cur_mode = g_array_index (device_x11->group_modes, uint32_t, group); ++ next_mode = (cur_mode + 1) % n_modes; ++ } ++ else ++ { ++ return FALSE; ++ } ++ ++ g_list_free (switch_buttons); ++ ++ if (next_mode < 0 || next_mode > n_modes) ++ return FALSE; ++ ++ *mode = next_mode; ++ return TRUE; ++} ++ + void + clutter_input_device_xi2_update_pad_state (ClutterInputDevice *device, + guint button, +@@ -330,23 +381,21 @@ clutter_input_device_xi2_update_pad_state (ClutterInputDevice *device, + gboolean is_mode_switch = FALSE; + + button_group = clutter_input_device_xi2_get_button_group (device, button); +- is_mode_switch = button_group >= 0; + +- /* Assign all non-mode-switch buttons to group 0 so far */ +- button_group = MAX (0, button_group); +- +- if (button_group >= device_xi2->group_modes->len) +- return; ++ if (button_group < 0 || button_group >= device_xi2->group_modes->len) ++ { ++ *group = *mode = 0; ++ return; ++ } + + group_mode = &g_array_index (device_xi2->group_modes, guint, button_group); + +- if (is_mode_switch && state) ++ if (state) + { +- guint next, n_modes; ++ uint32_t next_mode; + +- n_modes = clutter_input_device_get_group_n_modes (device, button_group); +- next = (*group_mode + 1) % n_modes; +- *group_mode = next; ++ if (pad_switch_mode (device, button, button_group, &next_mode)) ++ *group_mode = next_mode; + } + + if (group) +-- +2.23.0 + diff --git a/SOURCES/0001-backends-x11-Support-synaptics-configuration.patch b/SOURCES/0001-backends-x11-Support-synaptics-configuration.patch new file mode 100644 index 0000000..643648a --- /dev/null +++ b/SOURCES/0001-backends-x11-Support-synaptics-configuration.patch @@ -0,0 +1,348 @@ +From 01803de944153694501cb64bb4250ba76ed945f5 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Thu, 19 Jan 2017 15:03:41 +0100 +Subject: [PATCH] backends/x11: Support synaptics configuration + +The code is taken mostly as-is from g-s-d, so we can drag the +dead horse a bit longer. +--- + src/backends/x11/meta-input-settings-x11.c | 267 +++++++++++++++++++++ + 1 file changed, 267 insertions(+) + +diff --git a/src/backends/x11/meta-input-settings-x11.c b/src/backends/x11/meta-input-settings-x11.c +index 89f07ee1f..b2f5ca060 100644 +--- a/src/backends/x11/meta-input-settings-x11.c ++++ b/src/backends/x11/meta-input-settings-x11.c +@@ -26,6 +26,7 @@ + #include "backends/x11/meta-input-settings-x11.h" + + #include ++#include + #include + #include + #include +@@ -162,6 +163,179 @@ change_property (ClutterInputDevice *device, + meta_XFree (data_ret); + } + ++static gboolean ++is_device_synaptics (ClutterInputDevice *device) ++{ ++ guchar *has_setting; ++ ++ /* We just need looking for a synaptics-specific property */ ++ has_setting = get_property (device, "Synaptics Off", XA_INTEGER, 8, 1); ++ if (!has_setting) ++ return FALSE; ++ ++ meta_XFree (has_setting); ++ return TRUE; ++} ++ ++static void ++change_synaptics_tap_left_handed (ClutterInputDevice *device, ++ gboolean tap_enabled, ++ gboolean left_handed) ++{ ++ MetaDisplay *display = meta_get_display (); ++ MetaBackend *backend = meta_get_backend (); ++ Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); ++ XDevice *xdevice; ++ guchar *tap_action, *buttons; ++ guint buttons_capacity = 16, n_buttons; ++ ++ xdevice = XOpenDevice(xdisplay, clutter_input_device_get_device_id (device)); ++ if (!xdevice) ++ return; ++ ++ tap_action = get_property (device, "Synaptics Tap Action", ++ XA_INTEGER, 8, 7); ++ if (!tap_action) ++ goto out; ++ ++ tap_action[4] = tap_enabled ? (left_handed ? 3 : 1) : 0; ++ tap_action[5] = tap_enabled ? (left_handed ? 1 : 3) : 0; ++ tap_action[6] = tap_enabled ? 2 : 0; ++ ++ change_property (device, "Synaptics Tap Action", ++ XA_INTEGER, 8, tap_action, 7); ++ meta_XFree (tap_action); ++ ++ clutter_x11_trap_x_errors(); ++ buttons = g_new (guchar, buttons_capacity); ++ n_buttons = XGetDeviceButtonMapping (xdisplay, xdevice, ++ buttons, buttons_capacity); ++ ++ while (n_buttons > buttons_capacity) ++ { ++ buttons_capacity = n_buttons; ++ buttons = (guchar *) g_realloc (buttons, ++ buttons_capacity * sizeof (guchar)); ++ ++ n_buttons = XGetDeviceButtonMapping (xdisplay, xdevice, ++ buttons, buttons_capacity); ++ } ++ ++ buttons[0] = left_handed ? 3 : 1; ++ buttons[2] = left_handed ? 1 : 3; ++ XSetDeviceButtonMapping (xdisplay, xdevice, buttons, n_buttons); ++ g_free (buttons); ++ ++ if (clutter_x11_untrap_x_errors()) ++ { ++ g_warning ("Could not set synaptics touchpad left-handed for %s", ++ clutter_input_device_get_device_name (device)); ++ } ++ ++ out: ++ XCloseDevice (xdisplay, xdevice); ++} ++ ++static void ++change_synaptics_speed (ClutterInputDevice *device, ++ gdouble speed) ++{ ++ MetaDisplay *display = meta_get_display (); ++ MetaBackend *backend = meta_get_backend (); ++ Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); ++ XDevice *xdevice; ++ XPtrFeedbackControl feedback; ++ XFeedbackState *states, *state; ++ int i, num_feedbacks, motion_threshold, numerator, denominator; ++ gfloat motion_acceleration; ++ ++ xdevice = XOpenDevice(xdisplay, clutter_input_device_get_device_id (device)); ++ if (!xdevice) ++ return; ++ /* Get the list of feedbacks for the device */ ++ clutter_x11_trap_x_errors(); ++ states = XGetFeedbackControl (xdisplay, xdevice, &num_feedbacks); ++ if (clutter_x11_untrap_x_errors()) ++ return; ++ if (!states) ++ return; ++ ++ /* Calculate acceleration and threshold */ ++ motion_acceleration = (speed + 1) * 5; /* speed is [-1..1], map to [0..10] */ ++ motion_threshold = CLAMP (10 - floor (motion_acceleration), 1, 10); ++ ++ if (motion_acceleration >= 1.0) ++ { ++ /* we want to get the acceleration, with a resolution of 0.5 ++ */ ++ if ((motion_acceleration - floor (motion_acceleration)) < 0.25) ++ { ++ numerator = floor (motion_acceleration); ++ denominator = 1; ++ } ++ else if ((motion_acceleration - floor (motion_acceleration)) < 0.5) ++ { ++ numerator = ceil (2.0 * motion_acceleration); ++ denominator = 2; ++ } ++ else if ((motion_acceleration - floor (motion_acceleration)) < 0.75) ++ { ++ numerator = floor (2.0 *motion_acceleration); ++ denominator = 2; ++ } ++ else ++ { ++ numerator = ceil (motion_acceleration); ++ denominator = 1; ++ } ++ } ++ else if (motion_acceleration < 1.0 && motion_acceleration > 0) ++ { ++ /* This we do to 1/10ths */ ++ numerator = floor (motion_acceleration * 10) + 1; ++ denominator= 10; ++ } ++ else ++ { ++ numerator = -1; ++ denominator = -1; ++ } ++ ++ clutter_x11_trap_x_errors(); ++ ++ state = (XFeedbackState *) states; ++ ++ for (i = 0; i < num_feedbacks; i++) ++ { ++ if (state->class == PtrFeedbackClass) ++ { ++ /* And tell the device */ ++ feedback.class = PtrFeedbackClass; ++ feedback.length = sizeof (XPtrFeedbackControl); ++ feedback.id = state->id; ++ feedback.threshold = motion_threshold; ++ feedback.accelNum = numerator; ++ feedback.accelDenom = denominator; ++ ++ XChangeFeedbackControl (xdisplay, xdevice, ++ DvAccelNum | DvAccelDenom | DvThreshold, ++ (XFeedbackControl *) &feedback); ++ break; ++ } ++ ++ state = (XFeedbackState *) ((char *) state + state->length); ++ } ++ ++ if (clutter_x11_untrap_x_errors()) ++ { ++ g_warning ("Could not set synaptics touchpad acceleration for %s", ++ clutter_input_device_get_device_name (device)); ++ } ++ ++ XFreeFeedbackList (states); ++ XCloseDevice (xdisplay, xdevice); ++} ++ + static void + meta_input_settings_x11_set_send_events (MetaInputSettings *settings, + ClutterInputDevice *device, +@@ -170,6 +344,13 @@ meta_input_settings_x11_set_send_events (MetaInputSettings *settings, + guchar values[2] = { 0 }; /* disabled, disabled-on-external-mouse */ + guchar *available; + ++ if (is_device_synaptics (device)) ++ { ++ values[0] = mode != G_DESKTOP_DEVICE_SEND_EVENTS_ENABLED; ++ change_property (device, "Synaptics Off", XA_INTEGER, 8, &values, 1); ++ return; ++ } ++ + available = get_property (device, "libinput Send Events Modes Available", + XA_INTEGER, 8, 2); + if (!available) +@@ -222,6 +403,12 @@ meta_input_settings_x11_set_speed (MetaInputSettings *settings, + Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); + gfloat value = speed; + ++ if (is_device_synaptics (device)) ++ { ++ change_synaptics_speed (device, speed); ++ return; ++ } ++ + change_property (device, "libinput Accel Speed", + XInternAtom (xdisplay, "FLOAT", False), + 32, &value, 1); +@@ -248,6 +435,19 @@ meta_input_settings_x11_set_left_handed (MetaInputSettings *settings, + else + { + value = enabled ? 1 : 0; ++ ++ if (is_device_synaptics (device)) ++ { ++ GSettings *settings; ++ ++ settings = g_settings_new ("org.gnome.desktop.peripherals.touchpad"); ++ change_synaptics_tap_left_handed (device, ++ g_settings_get_boolean (settings, "tap-to-click"), ++ enabled); ++ g_object_unref (settings); ++ return; ++ } ++ + change_property (device, "libinput Left Handed Enabled", + XA_INTEGER, 8, &value, 1); + } +@@ -271,6 +471,20 @@ meta_input_settings_x11_set_tap_enabled (MetaInputSettings *settings, + { + guchar value = (enabled) ? 1 : 0; + ++ if (is_device_synaptics (device)) ++ { ++ GDesktopTouchpadHandedness handedness; ++ GSettings *settings; ++ ++ settings = g_settings_new ("org.gnome.desktop.peripherals.touchpad"); ++ handedness = g_settings_get_enum (settings, "left-handed"); ++ g_object_unref (settings); ++ ++ change_synaptics_tap_left_handed (device, enabled, ++ handedness == G_DESKTOP_TOUCHPAD_HANDEDNESS_LEFT); ++ return; ++ } ++ + change_property (device, "libinput Tapping Enabled", + XA_INTEGER, 8, &value, 1); + } +@@ -293,6 +507,27 @@ meta_input_settings_x11_set_invert_scroll (MetaInputSettings *settings, + { + guchar value = (inverted) ? 1 : 0; + ++ if (is_device_synaptics (device)) ++ { ++ gint32 *scrolling_distance; ++ ++ scrolling_distance = get_property (device, "Synaptics Scrolling Distance", ++ XA_INTEGER, 32, 2); ++ if (scrolling_distance) ++ { ++ scrolling_distance[0] = inverted ? ++ -abs (scrolling_distance[0]) : abs (scrolling_distance[0]); ++ scrolling_distance[1] = inverted ? ++ -abs (scrolling_distance[1]) : abs (scrolling_distance[1]); ++ ++ change_property (device, "Synaptics Scrolling Distance", ++ XA_INTEGER, 32, scrolling_distance, 2); ++ meta_XFree (scrolling_distance); ++ } ++ ++ return; ++ } ++ + change_property (device, "libinput Natural Scrolling Enabled", + XA_INTEGER, 8, &value, 1); + } +@@ -306,6 +541,22 @@ meta_input_settings_x11_set_edge_scroll (MetaInputSettings *settings, + guchar *current = NULL; + guchar *available = NULL; + ++ if (is_device_synaptics (device)) ++ { ++ current = get_property (device, "Synaptics Edge Scrolling", ++ XA_INTEGER, 8, 3); ++ if (current) ++ { ++ current[0] = !!edge_scroll_enabled; ++ current[1] = !!edge_scroll_enabled; ++ change_property (device, "Synaptics Edge Scrolling", ++ XA_INTEGER, 8, current, 3); ++ meta_XFree (current); ++ } ++ ++ return; ++ } ++ + available = get_property (device, "libinput Scroll Methods Available", + XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS); + if (!available || !available[SCROLL_METHOD_FIELD_EDGE]) +@@ -335,6 +586,22 @@ meta_input_settings_x11_set_two_finger_scroll (MetaInputSettings *set + guchar *current = NULL; + guchar *available = NULL; + ++ if (is_device_synaptics (device)) ++ { ++ current = get_property (device, "Synaptics Two-Finger Scrolling", ++ XA_INTEGER, 8, 2); ++ if (current) ++ { ++ current[0] = !!two_finger_scroll_enabled; ++ current[1] = !!two_finger_scroll_enabled; ++ change_property (device, "Synaptics Two-Finger Scrolling", ++ XA_INTEGER, 8, current, 2); ++ meta_XFree (current); ++ } ++ ++ return; ++ } ++ + available = get_property (device, "libinput Scroll Methods Available", + XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS); + if (!available || !available[SCROLL_METHOD_FIELD_2FG]) +-- +2.36.1 + diff --git a/SOURCES/0001-background-Reload-when-GPU-memory-is-invalidated.patch b/SOURCES/0001-background-Reload-when-GPU-memory-is-invalidated.patch new file mode 100644 index 0000000..fae4a7e --- /dev/null +++ b/SOURCES/0001-background-Reload-when-GPU-memory-is-invalidated.patch @@ -0,0 +1,118 @@ +From 5a486f5b6bf5f838db5dc2bfc5819a0cba5d2d19 Mon Sep 17 00:00:00 2001 +From: Daniel van Vugt +Date: Thu, 23 May 2019 18:15:28 +0800 +Subject: [PATCH] background: Reload when GPU memory is invalidated + +Fixes corrupt background wallpaper when resuming from suspend on the +Nvidia driver. + +https://gitlab.gnome.org/GNOME/gnome-shell/issues/1084 + +(cherry picked from commit a5265365dd268e15a461a58000a10b122d0bccba) + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/777 +--- + src/compositor/meta-background.c | 46 +++++++++++++++++++++++++------- + 1 file changed, 36 insertions(+), 10 deletions(-) + +diff --git a/src/compositor/meta-background.c b/src/compositor/meta-background.c +index c033395fe..387ce5dd3 100644 +--- a/src/compositor/meta-background.c ++++ b/src/compositor/meta-background.c +@@ -252,12 +252,11 @@ static void + set_file (MetaBackground *self, + GFile **filep, + MetaBackgroundImage **imagep, +- GFile *file) ++ GFile *file, ++ gboolean force_reload) + { +- if (!file_equal0 (*filep, file)) ++ if (force_reload || !file_equal0 (*filep, file)) + { +- g_clear_object (filep); +- + if (*imagep) + { + g_signal_handlers_disconnect_by_func (*imagep, +@@ -267,11 +266,12 @@ set_file (MetaBackground *self, + *imagep = NULL; + } + ++ g_set_object (filep, file); ++ + if (file) + { + MetaBackgroundImageCache *cache = meta_background_image_cache_get_default (); + +- *filep = g_object_ref (file); + *imagep = meta_background_image_cache_load (cache, file); + g_signal_connect (*imagep, "loaded", + G_CALLBACK (on_background_loaded), self); +@@ -279,6 +279,32 @@ set_file (MetaBackground *self, + } + } + ++static void ++on_gl_video_memory_purged (MetaBackground *self) ++{ ++ MetaBackgroundImageCache *cache = meta_background_image_cache_get_default (); ++ ++ /* The GPU memory that just got invalidated is the texture inside ++ * self->background_image1,2 and/or its mipmaps. However, to save memory the ++ * original pixbuf isn't kept in RAM so we can't do a simple re-upload. The ++ * only copy of the image was the one in texture memory that got invalidated. ++ * So we need to do a full reload from disk. ++ */ ++ if (self->file1) ++ { ++ meta_background_image_cache_purge (cache, self->file1); ++ set_file (self, &self->file1, &self->background_image1, self->file1, TRUE); ++ } ++ ++ if (self->file2) ++ { ++ meta_background_image_cache_purge (cache, self->file2); ++ set_file (self, &self->file2, &self->background_image2, self->file2, TRUE); ++ } ++ ++ mark_changed (self); ++} ++ + static void + meta_background_dispose (GObject *object) + { +@@ -287,8 +313,8 @@ meta_background_dispose (GObject *object) + free_color_texture (self); + free_wallpaper_texture (self); + +- set_file (self, &self->file1, &self->background_image1, NULL); +- set_file (self, &self->file2, &self->background_image2, NULL); ++ set_file (self, &self->file1, &self->background_image1, NULL, FALSE); ++ set_file (self, &self->file2, &self->background_image2, NULL, FALSE); + + set_display (self, NULL); + +@@ -312,7 +338,7 @@ meta_background_constructed (GObject *object) + G_OBJECT_CLASS (meta_background_parent_class)->constructed (object); + + g_signal_connect_object (self->display, "gl-video-memory-purged", +- G_CALLBACK (mark_changed), object, G_CONNECT_SWAPPED); ++ G_CALLBACK (on_gl_video_memory_purged), object, G_CONNECT_SWAPPED); + + g_signal_connect_object (monitor_manager, "monitors-changed", + G_CALLBACK (on_monitors_changed), self, +@@ -937,8 +963,8 @@ meta_background_set_blend (MetaBackground *self, + g_return_if_fail (META_IS_BACKGROUND (self)); + g_return_if_fail (blend_factor >= 0.0 && blend_factor <= 1.0); + +- set_file (self, &self->file1, &self->background_image1, file1); +- set_file (self, &self->file2, &self->background_image2, file2); ++ set_file (self, &self->file1, &self->background_image1, file1, FALSE); ++ set_file (self, &self->file2, &self->background_image2, file2, FALSE); + + self->blend_factor = blend_factor; + self->style = style; +-- +2.26.2 + diff --git a/SOURCES/0001-clutter-Backport-of-touch-mode.patch b/SOURCES/0001-clutter-Backport-of-touch-mode.patch new file mode 100644 index 0000000..dff32fa --- /dev/null +++ b/SOURCES/0001-clutter-Backport-of-touch-mode.patch @@ -0,0 +1,357 @@ +From 2a2e870c139e2130b00d582546616269bca27458 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Fri, 4 Sep 2020 17:11:36 +0200 +Subject: [PATCH] clutter: Backport of ::touch-mode + +In upstream/master this is a ClutterSeat readonly property. Add it to +ClutterDeviceManager here, the mechanism and triggering is the same +though. +--- + clutter/clutter/clutter-device-manager.c | 24 +++ + clutter/clutter/clutter-device-manager.h | 2 + + .../evdev/clutter-device-manager-evdev.c | 179 ++++++++++++++++++ + 3 files changed, 205 insertions(+) + +diff --git a/clutter/clutter/clutter-device-manager.c b/clutter/clutter/clutter-device-manager.c +index c676384..e1cc455 100644 +--- a/clutter/clutter/clutter-device-manager.c ++++ b/clutter/clutter/clutter-device-manager.c +@@ -62,6 +62,7 @@ enum + PROP_0, + + PROP_BACKEND, ++ PROP_TOUCH_MODE, + + PROP_LAST + }; +@@ -108,6 +109,7 @@ clutter_device_manager_set_property (GObject *gobject, + priv->backend = g_value_get_object (value); + break; + ++ case PROP_TOUCH_MODE: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +@@ -127,6 +129,10 @@ clutter_device_manager_get_property (GObject *gobject, + g_value_set_object (value, priv->backend); + break; + ++ case PROP_TOUCH_MODE: ++ g_value_set_boolean (value, FALSE); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +@@ -143,6 +149,12 @@ clutter_device_manager_class_init (ClutterDeviceManagerClass *klass) + P_("The ClutterBackend of the device manager"), + CLUTTER_TYPE_BACKEND, + CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); ++ obj_props[PROP_TOUCH_MODE] = ++ g_param_spec_boolean ("touch-mode", ++ P_("Touch mode"), ++ P_("Touch mode"), ++ FALSE, ++ CLUTTER_PARAM_READABLE); + + gobject_class->set_property = clutter_device_manager_set_property; + gobject_class->get_property = clutter_device_manager_get_property; +@@ -579,3 +591,15 @@ clutter_device_manager_get_kbd_a11y_settings (ClutterDeviceManager *device_man + + *settings = device_manager->priv->kbd_a11y_settings; + } ++ ++gboolean ++clutter_device_manager_get_touch_mode (ClutterDeviceManager *device_manager) ++{ ++ gboolean touch_mode; ++ ++ g_return_val_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager), FALSE); ++ ++ g_object_get (G_OBJECT (device_manager), "touch-mode", &touch_mode, NULL); ++ ++ return touch_mode; ++} +diff --git a/clutter/clutter/clutter-device-manager.h b/clutter/clutter/clutter-device-manager.h +index 1cbf030..a4a6271 100644 +--- a/clutter/clutter/clutter-device-manager.h ++++ b/clutter/clutter/clutter-device-manager.h +@@ -155,6 +155,8 @@ void clutter_device_manager_set_kbd_a11y_settings (ClutterDeviceManager *devic + CLUTTER_EXPORT + void clutter_device_manager_get_kbd_a11y_settings (ClutterDeviceManager *device_manager, + ClutterKbdA11ySettings *settings); ++CLUTTER_EXPORT ++gboolean clutter_device_manager_get_touch_mode (ClutterDeviceManager *device_manager); + + G_END_DECLS + +diff --git a/clutter/clutter/evdev/clutter-device-manager-evdev.c b/clutter/clutter/evdev/clutter-device-manager-evdev.c +index 84b0aad..78b5b64 100644 +--- a/clutter/clutter/evdev/clutter-device-manager-evdev.c ++++ b/clutter/clutter/evdev/clutter-device-manager-evdev.c +@@ -108,6 +108,19 @@ struct _ClutterDeviceManagerEvdevPrivate + + gint device_id_next; + GList *free_device_ids; ++ ++ guint tablet_mode_switch_state : 1; ++ guint has_touchscreen : 1; ++ guint has_tablet_switch : 1; ++ guint has_pointer : 1; ++ guint touch_mode : 1; ++}; ++ ++enum ++{ ++ PROP_0, ++ PROP_TOUCH_MODE, ++ N_PROPS + }; + + static void clutter_device_manager_evdev_event_extender_init (ClutterEventExtenderInterface *iface); +@@ -765,6 +778,34 @@ clutter_event_source_free (ClutterEventSource *source) + g_source_unref (g_source); + } + ++static void ++update_touch_mode (ClutterDeviceManagerEvdev *manager_evdev) ++{ ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ gboolean touch_mode; ++ ++ /* No touch mode if we don't have a touchscreen, easy */ ++ if (!priv->has_touchscreen) ++ touch_mode = FALSE; ++ /* If we have a tablet mode switch, honor it being unset */ ++ else if (priv->has_tablet_switch && !priv->tablet_mode_switch_state) ++ touch_mode = FALSE; ++ /* If tablet mode is enabled, go for it */ ++ else if (priv->has_tablet_switch && priv->tablet_mode_switch_state) ++ touch_mode = TRUE; ++ /* If there is no tablet mode switch (eg. kiosk machines), ++ * assume touch-mode is mutually exclusive with pointers. ++ */ ++ else ++ touch_mode = !priv->has_pointer; ++ ++ if (priv->touch_mode != touch_mode) ++ { ++ priv->touch_mode = touch_mode; ++ g_object_notify (G_OBJECT (manager_evdev), "touch-mode"); ++ } ++} ++ + static void + evdev_add_device (ClutterDeviceManagerEvdev *manager_evdev, + struct libinput_device *libinput_device) +@@ -942,19 +983,81 @@ flush_event_queue (void) + } + } + ++static gboolean ++has_touchscreen (ClutterDeviceManagerEvdev *manager_evdev) ++{ ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ GSList *l; ++ ++ for (l = priv->devices; l; l = l->next) ++ { ++ ClutterInputDeviceType device_type; ++ ++ device_type = clutter_input_device_get_device_type (l->data); ++ ++ if (device_type == CLUTTER_TOUCHSCREEN_DEVICE) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static gboolean ++device_type_is_pointer (ClutterInputDeviceType device_type) ++{ ++ return (device_type == CLUTTER_POINTER_DEVICE || ++ device_type == CLUTTER_TOUCHPAD_DEVICE); ++} ++ ++static gboolean ++has_pointer (ClutterDeviceManagerEvdev *manager_evdev) ++{ ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ GSList *l; ++ ++ for (l = priv->devices; l; l = l->next) ++ { ++ ClutterInputDeviceType device_type; ++ ++ device_type = clutter_input_device_get_device_type (l->data); ++ ++ if (device_type_is_pointer (device_type)) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ + static gboolean + process_base_event (ClutterDeviceManagerEvdev *manager_evdev, + struct libinput_event *event) + { ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; + ClutterInputDevice *device; + struct libinput_device *libinput_device; + gboolean handled = TRUE; ++ gboolean check_touch_mode; + + switch (libinput_event_get_type (event)) + { + case LIBINPUT_EVENT_DEVICE_ADDED: + libinput_device = libinput_event_get_device (event); + ++ priv->has_touchscreen |= ++ libinput_device_has_capability (libinput_device, LIBINPUT_DEVICE_CAP_TOUCH); ++ priv->has_pointer |= ++ libinput_device_has_capability (libinput_device, LIBINPUT_DEVICE_CAP_POINTER); ++ check_touch_mode = priv->has_touchscreen | priv->has_pointer; ++ ++ if (libinput_device_has_capability (libinput_device, ++ LIBINPUT_DEVICE_CAP_SWITCH) && ++ libinput_device_switch_has_switch (libinput_device, ++ LIBINPUT_SWITCH_TABLET_MODE)) ++ { ++ priv->has_tablet_switch = TRUE; ++ check_touch_mode = TRUE; ++ } ++ + evdev_add_device (manager_evdev, libinput_device); + break; + +@@ -966,7 +1069,17 @@ process_base_event (ClutterDeviceManagerEvdev *manager_evdev, + + libinput_device = libinput_event_get_device (event); + ++ check_touch_mode = ++ libinput_device_has_capability (libinput_device, LIBINPUT_DEVICE_CAP_TOUCH); + device = libinput_device_get_user_data (libinput_device); ++ if (check_touch_mode) ++ priv->has_touchscreen = has_touchscreen (manager_evdev); ++ if (device_type_is_pointer (clutter_input_device_get_device_type (device))) ++ { ++ priv->has_pointer = has_pointer (manager_evdev); ++ check_touch_mode = TRUE; ++ } ++ + evdev_remove_device (manager_evdev, + CLUTTER_INPUT_DEVICE_EVDEV (device)); + break; +@@ -975,6 +1088,9 @@ process_base_event (ClutterDeviceManagerEvdev *manager_evdev, + handled = FALSE; + } + ++ if (check_touch_mode) ++ update_touch_mode (manager_evdev); ++ + return handled; + } + +@@ -1752,6 +1868,23 @@ process_device_event (ClutterDeviceManagerEvdev *manager_evdev, + notify_pad_ring (device, time, number, source, group, mode, angle); + break; + } ++ case LIBINPUT_EVENT_SWITCH_TOGGLE: ++ { ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ struct libinput_event_switch *switch_event = ++ libinput_event_get_switch_event (event); ++ enum libinput_switch sw = ++ libinput_event_switch_get_switch (switch_event); ++ enum libinput_switch_state state = ++ libinput_event_switch_get_switch_state (switch_event); ++ ++ if (sw == LIBINPUT_SWITCH_TABLET_MODE) ++ { ++ priv->tablet_mode_switch_state = (state == LIBINPUT_SWITCH_STATE_ON); ++ update_touch_mode (manager_evdev); ++ } ++ break; ++ } + default: + handled = FALSE; + } +@@ -1967,6 +2100,10 @@ clutter_device_manager_evdev_constructed (GObject *gobject) + + source = clutter_event_source_new (manager_evdev); + priv->event_source = source; ++ ++ priv->has_touchscreen = has_touchscreen (manager_evdev); ++ priv->has_pointer = has_pointer (manager_evdev); ++ update_touch_mode (manager_evdev); + } + + static void +@@ -2001,6 +2138,43 @@ clutter_device_manager_evdev_dispose (GObject *object) + G_OBJECT_CLASS (clutter_device_manager_evdev_parent_class)->dispose (object); + } + ++static void ++clutter_device_manager_evdev_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ switch (prop_id) ++ { ++ case PROP_TOUCH_MODE: ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static void ++clutter_device_manager_evdev_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ ClutterDeviceManagerEvdev *manager_evdev; ++ ClutterDeviceManagerEvdevPrivate *priv; ++ ++ manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (object); ++ priv = manager_evdev->priv; ++ ++ switch (prop_id) ++ { ++ case PROP_TOUCH_MODE: ++ g_value_set_boolean (value, priv->touch_mode); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++ + static void + clutter_device_manager_evdev_finalize (GObject *object) + { +@@ -2036,6 +2210,8 @@ clutter_device_manager_evdev_class_init (ClutterDeviceManagerEvdevClass *klass) + gobject_class->constructed = clutter_device_manager_evdev_constructed; + gobject_class->finalize = clutter_device_manager_evdev_finalize; + gobject_class->dispose = clutter_device_manager_evdev_dispose; ++ gobject_class->set_property = clutter_device_manager_evdev_set_property; ++ gobject_class->get_property = clutter_device_manager_evdev_get_property; + + manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass); + manager_class->add_device = clutter_device_manager_evdev_add_device; +@@ -2047,6 +2223,9 @@ clutter_device_manager_evdev_class_init (ClutterDeviceManagerEvdevClass *klass) + manager_class->get_supported_virtual_device_types = clutter_device_manager_evdev_get_supported_virtual_device_types; + manager_class->compress_motion = clutter_device_manager_evdev_compress_motion; + manager_class->apply_kbd_a11y_settings = clutter_device_manager_evdev_apply_kbd_a11y_settings; ++ ++ g_object_class_override_property (gobject_class, PROP_TOUCH_MODE, ++ "touch-mode"); + } + + static void +-- +2.29.2 + diff --git a/SOURCES/0001-clutter-Extend-touchpad-device-property-check-for-Sy.patch b/SOURCES/0001-clutter-Extend-touchpad-device-property-check-for-Sy.patch new file mode 100644 index 0000000..96fe26b --- /dev/null +++ b/SOURCES/0001-clutter-Extend-touchpad-device-property-check-for-Sy.patch @@ -0,0 +1,61 @@ +From 368fdebe8f4f4e0c0e41f5be9961a748f328cb57 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Tue, 13 Feb 2018 11:44:40 +0100 +Subject: [PATCH] clutter: Extend touchpad device property check for Synaptics + +So we reliably get CLUTTER_TOUCHPAD_DEVICE for those. The other heuristics +to get the device type may fall short. +--- + .../clutter/x11/clutter-device-manager-xi2.c | 22 ++++++++++++++++--- + 1 file changed, 19 insertions(+), 3 deletions(-) + +diff --git a/clutter/clutter/x11/clutter-device-manager-xi2.c b/clutter/clutter/x11/clutter-device-manager-xi2.c +index 87da4b050..297d3acfe 100644 +--- a/clutter/clutter/x11/clutter-device-manager-xi2.c ++++ b/clutter/clutter/x11/clutter-device-manager-xi2.c +@@ -282,8 +282,9 @@ is_touch_device (XIAnyClassInfo **classes, + } + + static gboolean +-is_touchpad_device (ClutterBackendX11 *backend_x11, +- XIDeviceInfo *info) ++query_exists_device_property (ClutterBackendX11 *backend_x11, ++ XIDeviceInfo *info, ++ const gchar *property) + { + gulong nitems, bytes_after; + guint32 *data = NULL; +@@ -291,7 +292,7 @@ is_touchpad_device (ClutterBackendX11 *backend_x11, + Atom type; + Atom prop; + +- prop = XInternAtom (backend_x11->xdpy, "libinput Tapping Enabled", True); ++ prop = XInternAtom (backend_x11->xdpy, property, True); + if (prop == None) + return FALSE; + +@@ -312,6 +313,21 @@ is_touchpad_device (ClutterBackendX11 *backend_x11, + return TRUE; + } + ++static gboolean ++is_touchpad_device (ClutterBackendX11 *backend_x11, ++ XIDeviceInfo *info) ++{ ++ if (query_exists_device_property (backend_x11, info, ++ "libinput Tapping Enabled")) ++ return TRUE; ++ ++ if (query_exists_device_property (backend_x11, info, ++ "Synaptics Off")) ++ return TRUE; ++ ++ return FALSE; ++} ++ + static gboolean + get_device_ids (ClutterBackendX11 *backend_x11, + XIDeviceInfo *info, +-- +2.21.0 + diff --git a/SOURCES/0001-clutter-Only-reset-scroll-axes-on-slave-devices.patch b/SOURCES/0001-clutter-Only-reset-scroll-axes-on-slave-devices.patch new file mode 100644 index 0000000..dd9eeb5 --- /dev/null +++ b/SOURCES/0001-clutter-Only-reset-scroll-axes-on-slave-devices.patch @@ -0,0 +1,27 @@ +From 2259241e4e6f03bea4e9d746582a9e6a82b3c755 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Wed, 13 Jun 2018 13:48:24 +0200 +Subject: [PATCH] clutter: Only reset scroll axes on slave devices + +As a plus, unknown source device IDs will just warn instead of crash. +--- + clutter/clutter/x11/clutter-device-manager-xi2.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/clutter/clutter/x11/clutter-device-manager-xi2.c b/clutter/clutter/x11/clutter-device-manager-xi2.c +index 297d3acfe..76ef420ed 100644 +--- a/clutter/clutter/x11/clutter-device-manager-xi2.c ++++ b/clutter/clutter/x11/clutter-device-manager-xi2.c +@@ -1899,7 +1899,8 @@ clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator, + _clutter_input_device_set_stage (device, NULL); + } + +- _clutter_input_device_reset_scroll_info (source_device); ++ if (clutter_input_device_get_device_mode (source_device) == CLUTTER_INPUT_MODE_SLAVE) ++ _clutter_input_device_reset_scroll_info (source_device); + + clutter_event_set_device (event, device); + clutter_event_set_source_device (event, source_device); +-- +2.21.0 + diff --git a/SOURCES/0001-clutter-avoid-redundant-_clutter_paint_node_init_typ.patch b/SOURCES/0001-clutter-avoid-redundant-_clutter_paint_node_init_typ.patch new file mode 100644 index 0000000..028c8bd --- /dev/null +++ b/SOURCES/0001-clutter-avoid-redundant-_clutter_paint_node_init_typ.patch @@ -0,0 +1,53 @@ +From 4c1c3541efa37acf3a03822289a8ab8705cbbc4e Mon Sep 17 00:00:00 2001 +From: Christian Hergert +Date: Sun, 23 Feb 2020 17:27:08 -0800 +Subject: [PATCH 1/3] clutter: avoid redundant _clutter_paint_node_init_types() + +This only needs to be initialized once but is in the hot path of creating +new paint nodes (for which we create many). Instead, do this as part of +the clutter_init() workflow to keep it out of the hot path. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1087 +--- + clutter/clutter/clutter-main.c | 4 ++++ + clutter/clutter/clutter-paint-node.c | 2 -- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c +index 71ec0d80c..645c8bceb 100644 +--- a/clutter/clutter/clutter-main.c ++++ b/clutter/clutter/clutter-main.c +@@ -61,6 +61,7 @@ + #include "clutter-main.h" + #include "clutter-master-clock.h" + #include "clutter-mutter.h" ++#include "clutter-paint-node-private.h" + #include "clutter-private.h" + #include "clutter-settings-private.h" + #include "clutter-stage-manager.h" +@@ -1366,6 +1367,9 @@ clutter_init_real (GError **error) + if (clutter_enable_accessibility) + cally_accessibility_init (); + ++ /* Initialize types required for paint nodes */ ++ _clutter_paint_node_init_types (); ++ + return CLUTTER_INIT_SUCCESS; + } + +diff --git a/clutter/clutter/clutter-paint-node.c b/clutter/clutter/clutter-paint-node.c +index e731ca60a..73765a4e9 100644 +--- a/clutter/clutter/clutter-paint-node.c ++++ b/clutter/clutter/clutter-paint-node.c +@@ -1177,8 +1177,6 @@ _clutter_paint_node_create (GType gtype) + { + g_return_val_if_fail (g_type_is_a (gtype, CLUTTER_TYPE_PAINT_NODE), NULL); + +- _clutter_paint_node_init_types (); +- + return (gpointer) g_type_create_instance (gtype); + } + +-- +2.26.0 + diff --git a/SOURCES/0001-clutter-stage-view-Hide-double-buffered-shadowfb-beh.patch b/SOURCES/0001-clutter-stage-view-Hide-double-buffered-shadowfb-beh.patch new file mode 100644 index 0000000..6d9cffd --- /dev/null +++ b/SOURCES/0001-clutter-stage-view-Hide-double-buffered-shadowfb-beh.patch @@ -0,0 +1,46 @@ +From 7bcc274dbc6cb75814cce3e5c2e7f45cf25b0538 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 9 Feb 2021 17:59:08 +0100 +Subject: [PATCH 1/2] clutter/stage-view: Hide double buffered shadowfb behind + envvar + +It still results in worse performance than a single FBO based shadowfb, +so don't use it. It will need a new EGL extension for zero copy CPU +memory based FBO to be feasable. +--- + clutter/clutter/clutter-stage-view.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 5e5966d06..ec18db7b8 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -282,6 +282,14 @@ init_dma_buf_shadowfbs (ClutterStageView *view, + CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context); + CoglFramebuffer *initial_shadowfb; + ++ if (g_strcmp0 (g_getenv ("MUTTER_DEBUG_ENABLE_DOUBLE_BUFFER_SHADOWFB"), ++ "1") != 0) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Double buffered shadowfb not enabled"); ++ return FALSE; ++ } ++ + if (!cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, +@@ -390,8 +398,8 @@ init_shadowfb (ClutterStageView *view) + return; + } + +- g_warning ("Failed to initialize double buffered shadow fb for %s: %s", +- priv->name, error->message); ++ g_debug ("Failed to initialize double buffered shadow fb for %s: %s", ++ priv->name, error->message); + g_clear_error (&error); + + if (!init_fallback_shadowfb (view, cogl_context, width, height, &error)) +-- +2.29.2 + diff --git a/SOURCES/0001-cogl-Remove-unused-OFFSCREEN_BLIT-feature-flag.patch b/SOURCES/0001-cogl-Remove-unused-OFFSCREEN_BLIT-feature-flag.patch new file mode 100644 index 0000000..b2965a3 --- /dev/null +++ b/SOURCES/0001-cogl-Remove-unused-OFFSCREEN_BLIT-feature-flag.patch @@ -0,0 +1,37 @@ +From 251ef4ff4bacefac211e21873e10da7fa067dd68 Mon Sep 17 00:00:00 2001 +From: Pekka Paalanen +Date: Fri, 26 Apr 2019 12:23:18 +0300 +Subject: [PATCH 01/12] cogl: Remove unused OFFSCREEN_BLIT feature flag + +This named constant is never used anywhere. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/615 + +(cherry picked from commit c08a24bb40ad7aa7746e86251c9dbe6c264b4d7c) +--- + cogl/cogl/cogl-types.h | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/cogl/cogl/cogl-types.h b/cogl/cogl/cogl-types.h +index 690daa16a..69d304cf0 100644 +--- a/cogl/cogl/cogl-types.h ++++ b/cogl/cogl/cogl-types.h +@@ -325,7 +325,6 @@ typedef enum /*< prefix=COGL_PIXEL_FORMAT >*/ + * @COGL_FEATURE_SHADERS_GLSL: GLSL support + * @COGL_FEATURE_OFFSCREEN: FBO support + * @COGL_FEATURE_OFFSCREEN_MULTISAMPLE: Multisample support on FBOs +- * @COGL_FEATURE_OFFSCREEN_BLIT: Blit support on FBOs + * @COGL_FEATURE_FOUR_CLIP_PLANES: At least 4 clip planes available + * @COGL_FEATURE_STENCIL_BUFFER: Stencil buffer support + * @COGL_FEATURE_VBOS: VBO support +@@ -368,7 +367,6 @@ typedef enum + COGL_FEATURE_SHADERS_GLSL = (1 << 5), + COGL_FEATURE_OFFSCREEN = (1 << 6), + COGL_FEATURE_OFFSCREEN_MULTISAMPLE = (1 << 7), +- COGL_FEATURE_OFFSCREEN_BLIT = (1 << 8), + COGL_FEATURE_FOUR_CLIP_PLANES = (1 << 9), + COGL_FEATURE_STENCIL_BUFFER = (1 << 10), + COGL_FEATURE_VBOS = (1 << 11), +-- +2.21.0 + diff --git a/SOURCES/0001-compositor-Make-sure-_NET_WM_FRAME_DRAWN-timestamp-h.patch b/SOURCES/0001-compositor-Make-sure-_NET_WM_FRAME_DRAWN-timestamp-h.patch new file mode 100644 index 0000000..2a2bb03 --- /dev/null +++ b/SOURCES/0001-compositor-Make-sure-_NET_WM_FRAME_DRAWN-timestamp-h.patch @@ -0,0 +1,265 @@ +From b0f3604cdb653ef133f9684adffeb6b93f6906f8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 26 Jan 2022 10:51:07 +0100 +Subject: [PATCH] compositor: Make sure _NET_WM_FRAME_DRAWN timestamp has the + right scope + +The timestamp sent with _NET_WM_FRAME_DRAWN should be in "high +resolution X server timestamps", meaning they should have the same scope +as the built in X11 32 bit unsigned integer timestamps, i.e. overflow at +the same time. + +This was not done correctly when mutter had determined the X server used +the monotonic clock, where it'd just forward the monotonic clock, +confusing any client using _NET_WM_FRAME_DRAWN and friends. + +Fix this by 1) splitting the timestamp conversiot into an X11 case and a +display server case, where the display server case simply clamps the +monotonic clock, as it is assumed Xwayland is always usign the monotonic +clock, and 2) if we're a X11 compositing manager, if the X server is +using the monotonic clock, apply the same semantics as the display +server case and always just clamp, or if not, calculate the offset every +10 seconds, and offset the monotonic clock timestamp with the calculated +X server timestamp offset. + +This fixes an issue that would occur if mutter (or rather GNOME Shell) +would have been started before a X11 timestamp overflow, after the +overflow happened. In this case, GTK3 clients would get unclamped +timestamps, and get very confused, resulting in frames queued several +weeks into the future. +--- + src/compositor/compositor-private.h | 9 +- + src/compositor/compositor.c | 117 +++++++++++++++++++------ + src/compositor/meta-window-actor-x11.c | 12 +-- + 3 files changed, 104 insertions(+), 34 deletions(-) + +diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h +index f7008751215d..4588a8af7f2f 100644 +--- a/src/compositor/compositor-private.h ++++ b/src/compositor/compositor-private.h +@@ -49,6 +49,10 @@ struct _MetaCompositor + + gboolean frame_has_updated_xsurfaces; + gboolean have_x11_sync_object; ++ ++ gboolean xserver_uses_monotonic_clock; ++ int64_t xserver_time_query_time_us; ++ int64_t xserver_time_offset_us; + }; + + /* Wait 2ms after vblank before starting to draw next frame */ +@@ -64,8 +68,9 @@ void meta_end_modal_for_plugin (MetaCompositor *compositor, + MetaPlugin *plugin, + guint32 timestamp); + +-gint64 meta_compositor_monotonic_time_to_server_time (MetaDisplay *display, +- gint64 monotonic_time); ++int64_t ++meta_compositor_monotonic_to_high_res_xserver_time (MetaDisplay *display, ++ int64_t monotonic_time_us); + + gboolean meta_compositor_window_is_stereo (MetaDisplay *display, + Window xwindow); +diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c +index ce2c1b8a3bc1..a3fbe5d888f9 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -88,6 +88,40 @@ + #include "wayland/meta-wayland-private.h" + #endif + ++static inline int64_t ++us (int64_t us) ++{ ++ return us; ++} ++ ++static inline int64_t ++ms2us (int64_t ms) ++{ ++ return us (ms * 1000); ++} ++ ++static inline int64_t ++s2us (int64_t s) ++{ ++ return ms2us(s * 1000); ++} ++ ++/* ++ * This function takes a 64 bit time stamp from the monotonic clock, and clamps ++ * it to the scope of the X server clock, without losing the granularity. ++ */ ++static inline int64_t ++meta_translate_to_high_res_xserver_time (int64_t time_us) ++{ ++ int64_t us; ++ int64_t ms; ++ ++ us = time_us % 1000; ++ ms = time_us / 1000; ++ ++ return ms2us (ms & 0xffffffff) + us; ++} ++ + static void + on_presented (ClutterStage *stage, + CoglFrameEvent event, +@@ -612,6 +646,37 @@ meta_compositor_select_stereo_notify (MetaDisplay *display, + } + } + ++static void ++determine_server_clock_source (MetaCompositor *compositor) ++{ ++ MetaDisplay *display = compositor->display; ++ MetaX11Display *x11_display = display->x11_display; ++ uint32_t server_time_ms; ++ int64_t server_time_us; ++ int64_t translated_monotonic_now_us; ++ ++ if (meta_is_wayland_compositor ()) ++ { ++ compositor->xserver_uses_monotonic_clock = TRUE; ++ return; ++ } ++ ++ server_time_ms = meta_x11_display_get_current_time_roundtrip (x11_display); ++ server_time_us = ms2us (server_time_ms); ++ translated_monotonic_now_us = ++ meta_translate_to_high_res_xserver_time (g_get_monotonic_time ()); ++ ++ /* If the server time offset is within a second of the monotonic time, we ++ * assume that they are identical. This seems like a big margin, but we want ++ * to be as robust as possible even if the system is under load and our ++ * processing of the server response is delayed. ++ */ ++ if (ABS (server_time_us - translated_monotonic_now_us) < s2us (1)) ++ compositor->xserver_uses_monotonic_clock = TRUE; ++ else ++ compositor->xserver_uses_monotonic_clock = FALSE; ++} ++ + void + meta_compositor_manage (MetaCompositor *compositor) + { +@@ -622,6 +687,9 @@ meta_compositor_manage (MetaCompositor *compositor) + if (display->x11_display) + { + xdisplay = display->x11_display->xdisplay; ++ ++ determine_server_clock_source (compositor); ++ + meta_x11_display_set_cm_selection (display->x11_display); + + compositor->stereo_tree_ext = display_has_stereo_tree_ext (display->x11_display); +@@ -1593,7 +1661,7 @@ meta_compositor_flash_window (MetaCompositor *compositor, + } + + /** +- * meta_compositor_monotonic_time_to_server_time: ++ * meta_compositor_monotonic_to_high_res_xserver_time: + * @display: a #MetaDisplay + * @monotonic_time: time in the units of g_get_monotonic_time() + * +@@ -1606,38 +1674,35 @@ meta_compositor_flash_window (MetaCompositor *compositor, + * a time representation with high accuracy. If there is not a common + * time source, then the time synchronization will be less accurate. + */ +-gint64 +-meta_compositor_monotonic_time_to_server_time (MetaDisplay *display, +- gint64 monotonic_time) ++int64_t ++meta_compositor_monotonic_to_high_res_xserver_time (MetaDisplay *display, ++ int64_t monotonic_time_us) + { + MetaCompositor *compositor = display->compositor; ++ int64_t now_us; ++ ++ if (compositor->xserver_uses_monotonic_clock) ++ return meta_translate_to_high_res_xserver_time (monotonic_time_us); + +- if (compositor->server_time_query_time == 0 || +- (!compositor->server_time_is_monotonic_time && +- monotonic_time > compositor->server_time_query_time + 10*1000*1000)) /* 10 seconds */ ++ now_us = g_get_monotonic_time (); ++ ++ if (compositor->xserver_time_query_time_us == 0 || ++ now_us > (compositor->xserver_time_query_time_us + s2us (10))) + { +- guint32 server_time = meta_display_get_current_time_roundtrip (display); +- gint64 server_time_usec = (gint64)server_time * 1000; +- gint64 current_monotonic_time = g_get_monotonic_time (); +- compositor->server_time_query_time = current_monotonic_time; +- +- /* If the server time is within a second of the monotonic time, +- * we assume that they are identical. This seems like a big margin, +- * but we want to be as robust as possible even if the system +- * is under load and our processing of the server response is +- * delayed. +- */ +- if (server_time_usec > current_monotonic_time - 1000*1000 && +- server_time_usec < current_monotonic_time + 1000*1000) +- compositor->server_time_is_monotonic_time = TRUE; ++ MetaDisplay *display = compositor->display; ++ MetaX11Display *x11_display = display->x11_display; ++ uint32_t xserver_time_ms; ++ int64_t xserver_time_us; + +- compositor->server_time_offset = server_time_usec - current_monotonic_time; ++ compositor->xserver_time_query_time_us = now_us; ++ ++ xserver_time_ms = ++ meta_x11_display_get_current_time_roundtrip (x11_display); ++ xserver_time_us = ms2us (xserver_time_ms); ++ compositor->xserver_time_offset_us = xserver_time_us - now_us; + } + +- if (compositor->server_time_is_monotonic_time) +- return monotonic_time; +- else +- return monotonic_time + compositor->server_time_offset; ++ return monotonic_time_us + compositor->xserver_time_offset_us; + } + + void +diff --git a/src/compositor/meta-window-actor-x11.c b/src/compositor/meta-window-actor-x11.c +index a364323fe057..2b9c25510dc9 100644 +--- a/src/compositor/meta-window-actor-x11.c ++++ b/src/compositor/meta-window-actor-x11.c +@@ -105,8 +105,8 @@ do_send_frame_drawn (MetaWindowActorX11 *actor_x11, + XClientMessageEvent ev = { 0, }; + + frame->frame_drawn_time = +- meta_compositor_monotonic_time_to_server_time (display, +- g_get_monotonic_time ()); ++ meta_compositor_monotonic_to_high_res_xserver_time (display, ++ g_get_monotonic_time ()); + actor_x11->frame_drawn_time = frame->frame_drawn_time; + + ev.type = ClientMessage; +@@ -147,8 +147,8 @@ do_send_frame_timings (MetaWindowActorX11 *actor_x11, + if (presentation_time != 0) + { + int64_t presentation_time_server = +- meta_compositor_monotonic_time_to_server_time (display, +- presentation_time); ++ meta_compositor_monotonic_to_high_res_xserver_time (display, ++ presentation_time); + int64_t presentation_time_offset = presentation_time_server - frame->frame_drawn_time; + if (presentation_time_offset == 0) + presentation_time_offset = 1; +@@ -246,8 +246,8 @@ queue_send_frame_messages_timeout (MetaWindowActorX11 *actor_x11) + } + + current_time = +- meta_compositor_monotonic_time_to_server_time (display, +- g_get_monotonic_time ()); ++ meta_compositor_monotonic_to_high_res_xserver_time (display, ++ g_get_monotonic_time ()); + interval = (int) (1000000 / refresh_rate) * 6; + offset = MAX (0, actor_x11->frame_drawn_time + interval - current_time) / 1000; + +-- +2.33.1 + diff --git a/SOURCES/0001-core-Let-pad-mode-switch-events-always-go-through-Me.patch b/SOURCES/0001-core-Let-pad-mode-switch-events-always-go-through-Me.patch new file mode 100644 index 0000000..5ee31d1 --- /dev/null +++ b/SOURCES/0001-core-Let-pad-mode-switch-events-always-go-through-Me.patch @@ -0,0 +1,64 @@ +From 5cab6bac4d4fb06e60d3198dc654a5d70fa6240e Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Mon, 16 Dec 2019 13:53:26 +0100 +Subject: [PATCH] core: Let pad mode switch events always go through + MetaInputSettings + +We used to inhibit all pad actions while the OSD is shown, but one we +would actually want to handle are mode switches while the OSD is open. +So it has an opportunity to catch up to the mode switch. + +This lets MetaInputSettings reflect the mode switch (eg. when querying +action labels), so the OSD has an opportunity to update the current +actions. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/975 +--- + src/core/events.c | 30 ++++++++++++++++++++++++------ + 1 file changed, 24 insertions(+), 6 deletions(-) + +diff --git a/src/core/events.c b/src/core/events.c +index d383778629..44f28d0b97 100644 +--- a/src/core/events.c ++++ b/src/core/events.c +@@ -256,13 +256,31 @@ meta_display_handle_event (MetaDisplay *display, + } + #endif + +- if (!display->current_pad_osd && +- (event->type == CLUTTER_PAD_BUTTON_PRESS || +- event->type == CLUTTER_PAD_BUTTON_RELEASE || +- event->type == CLUTTER_PAD_RING || +- event->type == CLUTTER_PAD_STRIP)) ++ if (event->type == CLUTTER_PAD_BUTTON_PRESS || ++ event->type == CLUTTER_PAD_BUTTON_RELEASE || ++ event->type == CLUTTER_PAD_RING || ++ event->type == CLUTTER_PAD_STRIP) + { +- if (meta_input_settings_handle_pad_event (meta_backend_get_input_settings (backend), ++ gboolean handle_pad_event = TRUE; ++ gboolean is_mode_switch = FALSE; ++ ++ if (event->type == CLUTTER_PAD_BUTTON_PRESS || ++ event->type == CLUTTER_PAD_BUTTON_RELEASE) ++ { ++ ClutterInputDevice *pad; ++ uint32_t button; ++ ++ pad = clutter_event_get_source_device (event); ++ button = clutter_event_get_button (event); ++ ++ is_mode_switch = ++ clutter_input_device_get_mode_switch_button_group (pad, button) >= 0; ++ } ++ ++ handle_pad_event = !display->current_pad_osd || is_mode_switch; ++ ++ if (handle_pad_event && ++ meta_input_settings_handle_pad_event (meta_backend_get_input_settings (backend), + event)) + { + bypass_wayland = bypass_clutter = TRUE; +-- +2.24.0 + diff --git a/SOURCES/0001-crtc-xrandr-Respect-configured-RANDR-panning.patch b/SOURCES/0001-crtc-xrandr-Respect-configured-RANDR-panning.patch new file mode 100644 index 0000000..29e6b88 --- /dev/null +++ b/SOURCES/0001-crtc-xrandr-Respect-configured-RANDR-panning.patch @@ -0,0 +1,73 @@ +From bac090f571e6f413ba2a362ed2d70146b7701d16 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 24 Feb 2020 17:37:34 +0100 +Subject: [PATCH] crtc-xrandr: Respect configured RANDR panning + +A user may have configured an output to be panning, e.g. using xrandr +--output --mode --panning . Respect this by making +the logical monitor use the panning size, instead of the mode. This +makes e.g. makes the background cover the whole panning size, and panels +etc will cover the whole top of the panned area, instead of just the top +left part covering the monitor if having panned to (0, 0). + +No support is added to configuring panning, i.e. a panned monitor +configuration cannot be stored in monitors.xml. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1085 +--- + src/backends/x11/meta-crtc-xrandr.c | 31 +++++++++++++++++++++++++---- + 1 file changed, 27 insertions(+), 4 deletions(-) + +diff --git a/src/backends/x11/meta-crtc-xrandr.c b/src/backends/x11/meta-crtc-xrandr.c +index d201b8581..dc3f931e3 100644 +--- a/src/backends/x11/meta-crtc-xrandr.c ++++ b/src/backends/x11/meta-crtc-xrandr.c +@@ -177,7 +177,14 @@ meta_create_xrandr_crtc (MetaGpuXrandr *gpu_xrandr, + RRCrtc crtc_id, + XRRScreenResources *resources) + { ++ MetaGpu *gpu = META_GPU (gpu_xrandr); ++ MetaMonitorManager *monitor_manager = meta_gpu_get_monitor_manager (gpu); ++ MetaMonitorManagerXrandr *monitor_manager_xrandr = ++ META_MONITOR_MANAGER_XRANDR (monitor_manager); ++ Display *xdisplay = ++ meta_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr); + MetaCrtc *crtc; ++ XRRPanning *panning; + unsigned int i; + GList *modes; + +@@ -185,10 +192,26 @@ meta_create_xrandr_crtc (MetaGpuXrandr *gpu_xrandr, + + crtc->gpu = META_GPU (gpu_xrandr); + crtc->crtc_id = crtc_id; +- crtc->rect.x = xrandr_crtc->x; +- crtc->rect.y = xrandr_crtc->y; +- crtc->rect.width = xrandr_crtc->width; +- crtc->rect.height = xrandr_crtc->height; ++ ++ panning = XRRGetPanning (xdisplay, resources, crtc_id); ++ if (panning && panning->width > 0 && panning->height > 0) ++ { ++ crtc->rect = (MetaRectangle) { ++ .x = panning->left, ++ .y = panning->top, ++ .width = panning->width, ++ .height = panning->height, ++ }; ++ } ++ else ++ { ++ crtc->rect = (MetaRectangle) { ++ .x = xrandr_crtc->x, ++ .y = xrandr_crtc->y, ++ .width = xrandr_crtc->width, ++ .height = xrandr_crtc->height, ++ }; ++ } + crtc->is_dirty = FALSE; + crtc->transform = + meta_monitor_transform_from_xrandr (xrandr_crtc->rotation); +-- +2.24.1 + diff --git a/SOURCES/0001-display-Make-check-alive-timeout-configureable.patch b/SOURCES/0001-display-Make-check-alive-timeout-configureable.patch new file mode 100644 index 0000000..e0dfabe --- /dev/null +++ b/SOURCES/0001-display-Make-check-alive-timeout-configureable.patch @@ -0,0 +1,248 @@ +From 7f6f326a1bb96aad0b7aea9c4d7e257bf53c026c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 21 Feb 2020 21:03:16 +0100 +Subject: [PATCH] display: Make check-alive timeout configureable + +The check-alive feature is there for the user to be able to terminate +frozen applications more easily. However, sometimes applications are +implemented in a way where they fail to be reply to ping requests in a +timely manner, resulting in that, to the compositor, they are +indistinguishable from clients that have frozen indefinitely. + +When using an application that has these issues, the GUI showed in +response to the failure to respond to ping requests can become annoying, +as it disrupts the visual presentation of the application. + +To allow users to work-around these issues, add a setting allowing them +to configure the timeout waited until an application is considered +frozen, or disabling the check completely. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1080 +--- + data/org.gnome.mutter.gschema.xml.in | 10 ++++ + src/core/display.c | 18 ++++---- + src/core/prefs.c | 68 ++++++++++++++++++++++++++++ + src/meta/prefs.h | 3 ++ + 4 files changed, 90 insertions(+), 9 deletions(-) + +diff --git a/data/org.gnome.mutter.gschema.xml.in b/data/org.gnome.mutter.gschema.xml.in +index 6cbd9c1b5..4d37b1488 100644 +--- a/data/org.gnome.mutter.gschema.xml.in ++++ b/data/org.gnome.mutter.gschema.xml.in +@@ -123,6 +123,16 @@ + + + ++ ++ 5000 ++ Timeout for check-alive ping ++ ++ Number of milliseconds a client has to respond to a ping request in ++ order to not be detected as frozen. Using 0 will disable the alive check ++ completely. ++ ++ ++ + + + +diff --git a/src/core/display.c b/src/core/display.c +index eb7dc43b6..c30a03385 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -1923,12 +1923,6 @@ meta_set_syncing (gboolean setting) + } + } + +-/* +- * How long, in milliseconds, we should wait after pinging a window +- * before deciding it's not going to get back to us. +- */ +-#define PING_TIMEOUT_DELAY 5000 +- + /** + * meta_display_ping_timeout: + * @data: All the information about this ping. It is a #MetaPingData +@@ -1986,6 +1980,11 @@ meta_display_ping_window (MetaWindow *window, + { + MetaDisplay *display = window->display; + MetaPingData *ping_data; ++ unsigned int check_alive_timeout; ++ ++ check_alive_timeout = meta_prefs_get_check_alive_timeout (); ++ if (check_alive_timeout == 0) ++ return; + + if (serial == 0) + { +@@ -1999,9 +1998,10 @@ meta_display_ping_window (MetaWindow *window, + ping_data = g_new (MetaPingData, 1); + ping_data->window = window; + ping_data->serial = serial; +- ping_data->ping_timeout_id = g_timeout_add (PING_TIMEOUT_DELAY, +- meta_display_ping_timeout, +- ping_data); ++ ping_data->ping_timeout_id = ++ g_timeout_add (check_alive_timeout, ++ meta_display_ping_timeout, ++ ping_data); + g_source_set_name_by_id (ping_data->ping_timeout_id, "[mutter] meta_display_ping_timeout"); + + display->pending_pings = g_slist_prepend (display->pending_pings, ping_data); +diff --git a/src/core/prefs.c b/src/core/prefs.c +index 3f0db8afc..4892406ce 100644 +--- a/src/core/prefs.c ++++ b/src/core/prefs.c +@@ -99,6 +99,7 @@ static gboolean bell_is_visible = FALSE; + static gboolean bell_is_audible = TRUE; + static gboolean gnome_accessibility = FALSE; + static gboolean gnome_animations = TRUE; ++static unsigned int check_alive_timeout = 5000; + static char *cursor_theme = NULL; + /* cursor_size will, when running as an X11 compositing window manager, be the + * actual cursor size, multiplied with the global window scaling factor. On +@@ -213,6 +214,12 @@ typedef struct + gint *target; + } MetaIntPreference; + ++typedef struct ++{ ++ MetaBasePreference base; ++ unsigned int *target; ++} MetaUintPreference; ++ + + /* All preferences that are not keybindings must be listed here, + * plus in the GSettings schemas and the MetaPreference enum. +@@ -491,6 +498,18 @@ static MetaIntPreference preferences_int[] = + { { NULL, 0, 0 }, NULL }, + }; + ++static MetaUintPreference preferences_uint[] = ++ { ++ { ++ { "check-alive-timeout", ++ SCHEMA_MUTTER, ++ META_PREF_CHECK_ALIVE_TIMEOUT, ++ }, ++ &check_alive_timeout, ++ }, ++ { { NULL, 0, 0 }, NULL }, ++ }; ++ + static void + handle_preference_init_enum (void) + { +@@ -613,6 +632,21 @@ handle_preference_init_int (void) + } + } + ++static void ++handle_preference_init_uint (void) ++{ ++ MetaUintPreference *cursor = preferences_uint; ++ ++ while (cursor->base.key != NULL) ++ { ++ if (cursor->target) ++ *cursor->target = g_settings_get_uint (SETTINGS (cursor->base.schema), ++ cursor->base.key); ++ ++ ++cursor; ++ } ++} ++ + static void + handle_preference_update_enum (GSettings *settings, + gchar *key) +@@ -788,6 +822,28 @@ handle_preference_update_int (GSettings *settings, + } + } + ++static void ++handle_preference_update_uint (GSettings *settings, ++ char *key) ++{ ++ MetaUintPreference *cursor = preferences_uint; ++ unsigned int new_value; ++ ++ while (cursor->base.key && strcmp (key, cursor->base.key) != 0) ++ ++cursor; ++ ++ if (!cursor->base.key || !cursor->target) ++ return; ++ ++ new_value = g_settings_get_uint (SETTINGS (cursor->base.schema), key); ++ ++ if (*cursor->target != new_value) ++ { ++ *cursor->target = new_value; ++ queue_changed (cursor->base.pref); ++ } ++} ++ + + /****************************************************************************/ + /* Listeners. */ +@@ -964,6 +1020,7 @@ meta_prefs_init (void) + handle_preference_init_string (); + handle_preference_init_string_array (); + handle_preference_init_int (); ++ handle_preference_init_uint (); + + init_bindings (); + } +@@ -1017,6 +1074,8 @@ settings_changed (GSettings *settings, + handle_preference_update_bool (settings, key); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) + handle_preference_update_int (settings, key); ++ else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32)) ++ handle_preference_update_uint (settings, key); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING_ARRAY)) + handle_preference_update_string_array (settings, key); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING)) +@@ -1640,6 +1699,9 @@ meta_preference_to_string (MetaPreference pref) + + case META_PREF_AUTO_MAXIMIZE: + return "AUTO_MAXIMIZE"; ++ ++ case META_PREF_CHECK_ALIVE_TIMEOUT: ++ return "CHECK_ALIVE_TIMEOUT"; + } + + return "(unknown)"; +@@ -1966,6 +2028,12 @@ meta_prefs_get_overlay_binding (MetaKeyCombo *combo) + *combo = overlay_key_combo; + } + ++unsigned int ++meta_prefs_get_check_alive_timeout (void) ++{ ++ return check_alive_timeout; ++} ++ + const char * + meta_prefs_get_iso_next_group_option (void) + { +diff --git a/src/meta/prefs.h b/src/meta/prefs.h +index 9664b5c07..f42d1c63c 100644 +--- a/src/meta/prefs.h ++++ b/src/meta/prefs.h +@@ -103,6 +103,7 @@ typedef enum + META_PREF_AUTO_MAXIMIZE, + META_PREF_CENTER_NEW_WINDOWS, + META_PREF_DRAG_THRESHOLD, ++ META_PREF_CHECK_ALIVE_TIMEOUT, + } MetaPreference; + + typedef void (* MetaPrefsChangedFunc) (MetaPreference pref, +@@ -475,4 +476,6 @@ gboolean meta_prefs_bell_is_audible (void); + META_EXPORT + GDesktopVisualBellType meta_prefs_get_visual_bell_type (void); + ++unsigned int meta_prefs_get_check_alive_timeout (void); ++ + #endif +-- +2.28.0 + diff --git a/SOURCES/0001-enum-types-Use-basename-in-header-comment.patch b/SOURCES/0001-enum-types-Use-basename-in-header-comment.patch new file mode 100644 index 0000000..88b3a4f --- /dev/null +++ b/SOURCES/0001-enum-types-Use-basename-in-header-comment.patch @@ -0,0 +1,55 @@ +From 62387eb649b7b33d923d5382f85c9210a3bedbe8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 30 May 2019 16:32:35 +0200 +Subject: [PATCH] enum-types: Use @basename@ in header comment + +@filename@ may contain arch-specific bits that introduce unnecessary +multi-lib issues. +--- + clutter/clutter/clutter-enum-types.h.in | 2 +- + cogl/cogl-path/cogl-path-enum-types.h.in | 2 +- + src/meta/meta-enum-types.h.in | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/clutter/clutter/clutter-enum-types.h.in b/clutter/clutter/clutter-enum-types.h.in +index 2e5b6707e..17f9ee644 100644 +--- a/clutter/clutter/clutter-enum-types.h.in ++++ b/clutter/clutter/clutter-enum-types.h.in +@@ -13,7 +13,7 @@ G_BEGIN_DECLS + /*** END file-header ***/ + + /*** BEGIN file-production ***/ +-/* enumerations from "@filename@" */ ++/* enumerations from "@basename@" */ + /*** END file-production ***/ + + /*** BEGIN value-header ***/ +diff --git a/cogl/cogl-path/cogl-path-enum-types.h.in b/cogl/cogl-path/cogl-path-enum-types.h.in +index 071686acd..2b377ed18 100644 +--- a/cogl/cogl-path/cogl-path-enum-types.h.in ++++ b/cogl/cogl-path/cogl-path-enum-types.h.in +@@ -9,7 +9,7 @@ G_BEGIN_DECLS + /*** END file-header ***/ + + /*** BEGIN file-production ***/ +-/* enumerations from "@filename@" */ ++/* enumerations from "@basename@" */ + /*** END file-production ***/ + + /*** BEGIN file-tail ***/ +diff --git a/src/meta/meta-enum-types.h.in b/src/meta/meta-enum-types.h.in +index 6e3b67b26..bee0196de 100644 +--- a/src/meta/meta-enum-types.h.in ++++ b/src/meta/meta-enum-types.h.in +@@ -10,7 +10,7 @@ G_BEGIN_DECLS + /*** END file-header ***/ + + /*** BEGIN file-production ***/ +-/* enumerations from "@filename@" */ ++/* enumerations from "@basename@" */ + /*** END file-production ***/ + + /*** BEGIN file-tail ***/ +-- +2.21.0 + diff --git a/SOURCES/0001-events-Don-t-move-sloppy-focus-while-buttons-are-pre.patch b/SOURCES/0001-events-Don-t-move-sloppy-focus-while-buttons-are-pre.patch new file mode 100644 index 0000000..69fc6ef --- /dev/null +++ b/SOURCES/0001-events-Don-t-move-sloppy-focus-while-buttons-are-pre.patch @@ -0,0 +1,42 @@ +From f735f345ad8390a7fb09ef54ca3e0e419d395d1b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 21 Jul 2016 15:43:12 +0200 +Subject: [PATCH] events: Don't move (sloppy) focus while buttons are pressed + +(https://bugzilla.redhat.com/show_bug.cgi?id=1358535) +--- + src/x11/events.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/src/x11/events.c b/src/x11/events.c +index e363fdbb6..905b5bf9d 100644 +--- a/src/x11/events.c ++++ b/src/x11/events.c +@@ -832,6 +832,16 @@ crossing_serial_is_ignored (MetaX11Display *x11_display, + return FALSE; + } + ++static gboolean ++event_has_button_mask (XIEnterEvent *enter_event) ++{ ++ int i; ++ for (i = 0; i < enter_event->buttons.mask_len; i++) ++ if (enter_event->buttons.mask[i] != '\0') ++ return TRUE; ++ return FALSE; ++} ++ + static gboolean + handle_input_xevent (MetaX11Display *x11_display, + XIEvent *input_event, +@@ -876,6 +886,7 @@ handle_input_xevent (MetaX11Display *x11_display, + * avoid races. + */ + if (window && !crossing_serial_is_ignored (x11_display, serial) && ++ !event_has_button_mask (enter_event) && + enter_event->mode != XINotifyGrab && + enter_event->mode != XINotifyUngrab && + enter_event->detail != XINotifyInferior && +-- +2.21.0 + diff --git a/SOURCES/0001-events-Pass-CurrentTime-to-XIAllowEvents-when-unfree.patch b/SOURCES/0001-events-Pass-CurrentTime-to-XIAllowEvents-when-unfree.patch new file mode 100644 index 0000000..24a5bcd --- /dev/null +++ b/SOURCES/0001-events-Pass-CurrentTime-to-XIAllowEvents-when-unfree.patch @@ -0,0 +1,26 @@ +From 65ffd7e4df42cd62633f93107644f87208881578 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 14 Apr 2022 18:07:41 +0200 +Subject: [PATCH] events: Pass CurrentTime to XIAllowEvents() when unfreezing + pointer + +--- + src/core/events.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/events.c b/src/core/events.c +index 0dc3a73222..dd9b4ec981 100644 +--- a/src/core/events.c ++++ b/src/core/events.c +@@ -205,7 +205,7 @@ maybe_unfreeze_pointer_events (MetaBackend *backend, + } + + xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); +- XIAllowEvents (xdisplay, device_id, event_mode, event->button.time); ++ XIAllowEvents (xdisplay, device_id, event_mode, CurrentTime); + } + + static gboolean +-- +2.34.1 + diff --git a/SOURCES/0001-events-Sync-pending-pointer-events-without-a-window.patch b/SOURCES/0001-events-Sync-pending-pointer-events-without-a-window.patch new file mode 100644 index 0000000..3ba3963 --- /dev/null +++ b/SOURCES/0001-events-Sync-pending-pointer-events-without-a-window.patch @@ -0,0 +1,122 @@ +From f108395c32351cda8722130e0e2970827b18e5a9 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Wed, 2 Oct 2019 16:49:28 +0200 +Subject: [PATCH] events: Sync pending pointer events without a window + +Mutter issues a synchronous grab on the pointer for unfocused client +windows to be able to catch the button events first and raise/focus +client windows accordingly. + +When there is a synchronous grab in effect, all events are queued until +the grabbing client releases the event queue as it processes the events. + +Mutter does release the events in its event handler function but does so +only if it is able to find the window matching the event. If the window +is a shell widget, that matching may fail and therefore Mutter will not +release the events, hence causing a freeze in pointer events delivery. + +To avoid the issue, make sure we sync the pointer events in case we +can't find a matching window. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/821 +--- + src/core/events.c | 62 ++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 51 insertions(+), 11 deletions(-) + +diff --git a/src/core/events.c b/src/core/events.c +index 5b8e49fc7..831cb007b 100644 +--- a/src/core/events.c ++++ b/src/core/events.c +@@ -50,6 +50,12 @@ + #define IS_KEY_EVENT(e) ((e)->type == CLUTTER_KEY_PRESS || \ + (e)->type == CLUTTER_KEY_RELEASE) + ++typedef enum ++{ ++ EVENTS_UNFREEZE_SYNC, ++ EVENTS_UNFREEZE_REPLAY, ++} EventsUnfreezeMethod; ++ + static gboolean + stage_has_key_focus (void) + { +@@ -167,6 +173,43 @@ sequence_is_pointer_emulated (MetaDisplay *display, + return FALSE; + } + ++static void ++maybe_unfreeze_pointer_events (MetaBackend *backend, ++ const ClutterEvent *event, ++ EventsUnfreezeMethod unfreeze_method) ++{ ++ Display *xdisplay; ++ int event_mode; ++ int device_id; ++ ++ if (event->type != CLUTTER_BUTTON_PRESS) ++ return; ++ ++ if (!META_IS_BACKEND_X11 (backend)) ++ return; ++ ++ device_id = clutter_event_get_device_id (event); ++ switch (unfreeze_method) ++ { ++ case EVENTS_UNFREEZE_SYNC: ++ event_mode = XISyncDevice; ++ meta_verbose ("Syncing events time %u device %i\n", ++ (unsigned int) event->button.time, device_id); ++ break; ++ case EVENTS_UNFREEZE_REPLAY: ++ event_mode = XIReplayDevice; ++ meta_verbose ("Replaying events time %u device %i\n", ++ (unsigned int) event->button.time, device_id); ++ break; ++ default: ++ g_assert_not_reached (); ++ return; ++ } ++ ++ xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); ++ XIAllowEvents (xdisplay, device_id, event_mode, event->button.time); ++} ++ + static gboolean + meta_display_handle_event (MetaDisplay *display, + const ClutterEvent *event) +@@ -366,17 +409,7 @@ meta_display_handle_event (MetaDisplay *display, + { + /* Only replay button press events, since that's where we + * have the synchronous grab. */ +- if (event->type == CLUTTER_BUTTON_PRESS) +- { +- if (META_IS_BACKEND_X11 (backend)) +- { +- Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); +- meta_verbose ("Allowing events time %u\n", +- (unsigned int)event->button.time); +- XIAllowEvents (xdisplay, clutter_event_get_device_id (event), +- XIReplayDevice, event->button.time); +- } +- } ++ maybe_unfreeze_pointer_events (backend, event, EVENTS_UNFREEZE_REPLAY); + + /* If the focus window has an active close dialog let clutter + * events go through, so fancy clutter dialogs can get to handle +@@ -392,6 +425,13 @@ meta_display_handle_event (MetaDisplay *display, + + goto out; + } ++ else ++ { ++ /* We could not match the event with a window, make sure we sync ++ * the pointer to discard the sequence and don't keep events frozen. ++ */ ++ maybe_unfreeze_pointer_events (backend, event, EVENTS_UNFREEZE_SYNC); ++ } + + out: + /* If the compositor has a grab, don't pass that through to Wayland */ +-- +2.23.0 + diff --git a/SOURCES/0001-iconcache-Avoid-xrender-picture-formats-when-creatin.patch b/SOURCES/0001-iconcache-Avoid-xrender-picture-formats-when-creatin.patch new file mode 100644 index 0000000..0ed0857 --- /dev/null +++ b/SOURCES/0001-iconcache-Avoid-xrender-picture-formats-when-creatin.patch @@ -0,0 +1,136 @@ +From 80f79e0cc7509b79b38193a006b0d98d03432044 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 5 Aug 2019 14:39:21 -0400 +Subject: [PATCH] iconcache: Avoid xrender picture formats when creating cairo + surface + +If an application provides its window icon via wmhints, then mutter +loads the pixmap specified by the application into a cairo xlib surface. When +creating the surface it specifies the visual, indirectly, via an XRender +picture format. + +This is suboptimal, since XRender picture formats don't have a way to specify +16bpp depth, which an application may be using. + +In particular, applications are likely to use 16bpp depth pixmaps for their +icons, if the video card offers a 16bpp framebuffer/root window. + +This commit drops the XRender middleman, and just tells cairo a visual to use +directly. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/715 +--- + src/x11/iconcache.c | 31 ++++++------------------------- + 1 file changed, 6 insertions(+), 25 deletions(-) + +diff --git a/src/x11/iconcache.c b/src/x11/iconcache.c +index 15d72da65..521c77b8d 100644 +--- a/src/x11/iconcache.c ++++ b/src/x11/iconcache.c +@@ -261,97 +261,78 @@ get_pixmap_geometry (MetaX11Display *x11_display, + Pixmap pixmap, + int *w, + int *h, + int *d) + { + Window root_ignored; + int x_ignored, y_ignored; + guint width, height; + guint border_width_ignored; + guint depth; + + if (w) + *w = 1; + if (h) + *h = 1; + if (d) + *d = 1; + + XGetGeometry (x11_display->xdisplay, + pixmap, &root_ignored, &x_ignored, &y_ignored, + &width, &height, &border_width_ignored, &depth); + + if (w) + *w = width; + if (h) + *h = height; + if (d) + *d = depth; + } + +-static int +-standard_pict_format_for_depth (int depth) +-{ +- switch (depth) +- { +- case 1: +- return PictStandardA1; +- case 24: +- return PictStandardRGB24; +- case 32: +- return PictStandardARGB32; +- default: +- g_assert_not_reached (); +- } +- return 0; +-} +- +-static XRenderPictFormat * +-pict_format_for_depth (Display *xdisplay, int depth) +-{ +- return XRenderFindStandardFormat (xdisplay, standard_pict_format_for_depth (depth)); +-} +- + static cairo_surface_t * + surface_from_pixmap (Display *xdisplay, Pixmap xpixmap, + int width, int height) + { + Window root_return; ++ XVisualInfo visual_info; + int x_ret, y_ret; + unsigned int w_ret, h_ret, bw_ret, depth_ret; + + if (!XGetGeometry (xdisplay, xpixmap, &root_return, + &x_ret, &y_ret, &w_ret, &h_ret, &bw_ret, &depth_ret)) + return NULL; + +- return cairo_xlib_surface_create_with_xrender_format (xdisplay, xpixmap, DefaultScreenOfDisplay (xdisplay), +- pict_format_for_depth (xdisplay, depth_ret), w_ret, h_ret); ++ if (!XMatchVisualInfo (xdisplay, DefaultScreen (xdisplay), ++ depth_ret, TrueColor, &visual_info)) ++ return NULL; ++ ++ return cairo_xlib_surface_create (xdisplay, xpixmap, visual_info.visual, w_ret, h_ret); + } + + static gboolean + try_pixmap_and_mask (MetaX11Display *x11_display, + Pixmap src_pixmap, + Pixmap src_mask, + cairo_surface_t **iconp) + { + Display *xdisplay = x11_display->xdisplay; + cairo_surface_t *icon, *mask = NULL; + int w, h, d; + + if (src_pixmap == None) + return FALSE; + + meta_x11_error_trap_push (x11_display); + + get_pixmap_geometry (x11_display, src_pixmap, &w, &h, &d); + icon = surface_from_pixmap (xdisplay, src_pixmap, w, h); + + if (icon && src_mask != None) + { + get_pixmap_geometry (x11_display, src_mask, &w, &h, &d); + + if (d == 1) + mask = surface_from_pixmap (xdisplay, src_mask, w, h); + } + + meta_x11_error_trap_pop (x11_display); + +-- +2.21.0 + diff --git a/SOURCES/0001-main-be-more-aggressive-in-assuming-X11-backend.patch b/SOURCES/0001-main-be-more-aggressive-in-assuming-X11-backend.patch new file mode 100644 index 0000000..495d859 --- /dev/null +++ b/SOURCES/0001-main-be-more-aggressive-in-assuming-X11-backend.patch @@ -0,0 +1,49 @@ +From 18d4fbb1fb641e2b507b3adcd13d231145a01cd6 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 13 Feb 2018 09:44:50 -0500 +Subject: [PATCH] main: be more aggressive in assuming X11 backend + +If the session is started by vncserver right now, the +XDG_SESSION_TYPE won't be X11. Ideally that would be +fixed, but for backward compatibility we should default +to X11 if the session type isn't set to wayland explicitly. +--- + src/core/main.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/src/core/main.c b/src/core/main.c +index 629f8e94e..1e1e13367 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -333,7 +333,6 @@ find_session_type (void) + char *session_id; + char *session_type; + const char *session_type_env; +- gboolean is_tty = FALSE; + int ret, i; + + ret = sd_pid_get_session (0, &session_id); +@@ -346,8 +345,7 @@ find_session_type (void) + { + if (session_type_is_supported (session_type)) + goto out; +- else +- is_tty = g_strcmp0 (session_type, "tty") == 0; ++ + free (session_type); + } + } +@@ -379,8 +377,8 @@ find_session_type (void) + goto out; + } + +- /* Legacy support for starting through xinit */ +- if (is_tty && (g_getenv ("MUTTER_DISPLAY") || g_getenv ("DISPLAY"))) ++ /* Legacy support for starting through xinit or vncserver */ ++ if (g_getenv ("MUTTER_DISPLAY") || g_getenv ("DISPLAY")) + { + session_type = strdup ("x11"); + goto out; +-- +2.21.0 + diff --git a/SOURCES/0001-monitor-config-manager-Handle-multiple-builtin-panel.patch b/SOURCES/0001-monitor-config-manager-Handle-multiple-builtin-panel.patch new file mode 100644 index 0000000..73fcb7b --- /dev/null +++ b/SOURCES/0001-monitor-config-manager-Handle-multiple-builtin-panel.patch @@ -0,0 +1,211 @@ +From 19024a5b2eff02b22cdb3fc90142f522dd361996 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 27 Nov 2020 09:03:38 +0100 +Subject: [PATCH] monitor-config-manager: Handle multiple builtin panels + gracefully + +While multiple built-in panels isn't actually supported in any +meaningful manner, if we would ever end up with such a situation, e.g. +due to kernel bugs[0], we shouldn't crash when trying to set an +'external only' without any external monitors. + +While we could handle this with more degraded functionality (e.g. don't +support the 'switch' method of monitor configuration at all), handle it +by simply not trying to switch to external-only when there are no, +according to the kernel, external monitors available. This would e.g. +still allow betwene 'mirror-all', and 'linear' switches. + +The crash itself was disguised as an arbitrary X11 BadValue error, due +to mutter trying to resize the root window to 0x0, as the monitor +configuration that was applied consisted of zero logical monitors, thus +was effectively empty. + +[0] https://bugzilla.redhat.com/show_bug.cgi?id=1896904 + +Related: https://bugzilla.redhat.com/show_bug.cgi?id=1899260 +Part-of: +--- + src/backends/meta-monitor-config-manager.c | 3 + + src/tests/monitor-unit-tests.c | 145 +++++++++++++++++++++ + 2 files changed, 148 insertions(+) + +diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c +index bc1a39db8..d62bad52d 100644 +--- a/src/backends/meta-monitor-config-manager.c ++++ b/src/backends/meta-monitor-config-manager.c +@@ -1157,6 +1157,9 @@ create_for_switch_config_external (MetaMonitorConfigManager *config_manager) + x += logical_monitor_config->layout.width; + } + ++ if (!logical_monitor_configs) ++ return NULL; ++ + return meta_monitors_config_new (monitor_manager, + logical_monitor_configs, + layout_mode, +diff --git a/src/tests/monitor-unit-tests.c b/src/tests/monitor-unit-tests.c +index f47544b03..725f84173 100644 +--- a/src/tests/monitor-unit-tests.c ++++ b/src/tests/monitor-unit-tests.c +@@ -3175,6 +3175,149 @@ meta_test_monitor_non_upright_panel (void) + check_monitor_configuration (&test_case); + } + ++static void ++meta_test_monitor_switch_external_without_external (void) ++{ ++ MonitorTestCase test_case = { ++ .setup = { ++ .modes = { ++ { ++ .width = 1024, ++ .height = 768, ++ .refresh_rate = 60.0 ++ } ++ }, ++ .n_modes = 1, ++ .outputs = { ++ { ++ .crtc = 0, ++ .modes = { 0 }, ++ .n_modes = 1, ++ .preferred_mode = 0, ++ .possible_crtcs = { 0 }, ++ .n_possible_crtcs = 1, ++ .width_mm = 222, ++ .height_mm = 125, ++ .is_laptop_panel = TRUE ++ }, ++ { ++ .crtc = 1, ++ .modes = { 0 }, ++ .n_modes = 1, ++ .preferred_mode = 0, ++ .possible_crtcs = { 1 }, ++ .n_possible_crtcs = 1, ++ .width_mm = 222, ++ .height_mm = 125, ++ .is_laptop_panel = TRUE ++ } ++ }, ++ .n_outputs = 2, ++ .crtcs = { ++ { ++ .current_mode = 0 ++ }, ++ { ++ .current_mode = 0 ++ } ++ }, ++ .n_crtcs = 2 ++ }, ++ ++ .expect = { ++ .monitors = { ++ { ++ .outputs = { 0 }, ++ .n_outputs = 1, ++ .modes = { ++ { ++ .width = 1024, ++ .height = 768, ++ .refresh_rate = 60.0, ++ .crtc_modes = { ++ { ++ .output = 0, ++ .crtc_mode = 0 ++ } ++ } ++ } ++ }, ++ .n_modes = 1, ++ .current_mode = 0, ++ .width_mm = 222, ++ .height_mm = 125 ++ }, ++ { ++ .outputs = { 1 }, ++ .n_outputs = 1, ++ .modes = { ++ { ++ .width = 1024, ++ .height = 768, ++ .refresh_rate = 60.0, ++ .crtc_modes = { ++ { ++ .output = 1, ++ .crtc_mode = 0 ++ } ++ } ++ } ++ }, ++ .n_modes = 1, ++ .current_mode = 0, ++ .width_mm = 222, ++ .height_mm = 125 ++ } ++ }, ++ .n_monitors = 2, ++ .logical_monitors = { ++ { ++ .monitors = { 0 }, ++ .n_monitors = 1, ++ .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, ++ .scale = 1 ++ }, ++ { ++ .monitors = { 1 }, ++ .n_monitors = 1, ++ .layout = { .x = 1024, .y = 0, .width = 1024, .height = 768 }, ++ .scale = 1 ++ } ++ }, ++ .n_logical_monitors = 2, ++ .primary_logical_monitor = 0, ++ .n_outputs = 2, ++ .crtcs = { ++ { ++ .current_mode = 0, ++ }, ++ { ++ .current_mode = 0, ++ }, ++ }, ++ .n_crtcs = 2, ++ .n_tiled_monitors = 0, ++ .screen_width = 2048, ++ .screen_height = 768 ++ } ++ }; ++ MetaMonitorTestSetup *test_setup; ++ MetaBackend *backend = meta_get_backend (); ++ MetaMonitorManager *monitor_manager = ++ meta_backend_get_monitor_manager (backend); ++ ++ test_setup = create_monitor_test_setup (&test_case.setup, ++ MONITOR_TEST_FLAG_NO_STORED); ++ emulate_hotplug (test_setup); ++ check_monitor_configuration (&test_case); ++ ++ meta_monitor_manager_switch_config (monitor_manager, ++ META_MONITOR_SWITCH_CONFIG_EXTERNAL); ++ check_monitor_configuration (&test_case); ++ ++ check_monitor_test_clients_state (); ++} ++ + static void + meta_test_monitor_custom_vertical_config (void) + { +@@ -5969,6 +6112,8 @@ init_monitor_tests (void) + meta_test_monitor_preferred_non_first_mode); + add_monitor_test ("/backends/monitor/non-upright-panel", + meta_test_monitor_non_upright_panel); ++ add_monitor_test ("/backends/monitor/switch-external-without-external", ++ meta_test_monitor_switch_external_without_external); + + add_monitor_test ("/backends/monitor/custom/vertical-config", + meta_test_monitor_custom_vertical_config); +-- +2.29.2 + diff --git a/SOURCES/0001-monitor-manager-Consider-external-layout-before-defa.patch b/SOURCES/0001-monitor-manager-Consider-external-layout-before-defa.patch new file mode 100644 index 0000000..d204242 --- /dev/null +++ b/SOURCES/0001-monitor-manager-Consider-external-layout-before-defa.patch @@ -0,0 +1,152 @@ +From 4904f1a1e5b881dfd5a051c15acecb3232dc8207 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 28 Jan 2016 15:26:33 +0100 +Subject: [PATCH] monitor-manager: Consider external layout before default + linear config + +In case of no existing configuration, we use a default layout of +aligning attached displays horizontally. This sidesteps any layout +configuration that is done externally, for instance via xorg.conf, +which is not desirable. Instead, base the initial configuration on +the existing layout if it passes some sanity checks before falling +back to the default linear config. +--- + src/backends/meta-monitor-config-manager.c | 77 ++++++++++++++++++++++ + src/backends/meta-monitor-config-manager.h | 2 + + src/backends/meta-monitor-manager.c | 19 ++++++ + 3 files changed, 98 insertions(+) + +diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c +index 9a54ce50f..d64ca1f79 100644 +--- a/src/backends/meta-monitor-config-manager.c ++++ b/src/backends/meta-monitor-config-manager.c +@@ -643,6 +643,83 @@ create_preferred_logical_monitor_config (MetaMonitorManager *monitor_ma + return logical_monitor_config; + } + ++static MetaLogicalMonitorConfig * ++create_logical_monitor_config_from_output (MetaMonitorManager *monitor_manager, ++ MetaMonitor *monitor, ++ MetaLogicalMonitorConfig *primary_logical_monitor_config, ++ MetaLogicalMonitorLayoutMode layout_mode) ++{ ++ MetaOutput *output; ++ MetaCrtc *crtc; ++ ++ output = meta_monitor_get_main_output (monitor); ++ crtc = meta_output_get_assigned_crtc (output); ++ return create_preferred_logical_monitor_config (monitor_manager, ++ monitor, ++ crtc->rect.x, ++ crtc->rect.y, ++ primary_logical_monitor_config, ++ layout_mode); ++} ++ ++MetaMonitorsConfig * ++meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_manager) ++{ ++ MetaMonitorManager *monitor_manager = config_manager->monitor_manager; ++ GList *logical_monitor_configs; ++ MetaMonitor *primary_monitor; ++ MetaLogicalMonitorLayoutMode layout_mode; ++ MetaLogicalMonitorConfig *primary_logical_monitor_config; ++ GList *monitors; ++ GList *l; ++ ++ if (meta_monitor_config_store_get_config_count (config_manager->config_store) > 0) ++ return NULL; ++ ++ primary_monitor = find_primary_monitor (monitor_manager); ++ if (!primary_monitor || !meta_monitor_is_active (primary_monitor)) ++ return NULL; ++ ++ layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); ++ ++ primary_logical_monitor_config = ++ create_logical_monitor_config_from_output (monitor_manager, ++ primary_monitor, ++ NULL, ++ layout_mode); ++ ++ primary_logical_monitor_config->is_primary = TRUE; ++ logical_monitor_configs = g_list_append (NULL, ++ primary_logical_monitor_config); ++ ++ monitors = meta_monitor_manager_get_monitors (monitor_manager); ++ for (l = monitors; l; l = l->next) ++ { ++ MetaMonitor *monitor = l->data; ++ MetaLogicalMonitorConfig *logical_monitor_config; ++ ++ if (monitor == primary_monitor) ++ continue; ++ ++ if (!meta_monitor_is_active (monitor)) ++ continue; ++ ++ logical_monitor_config = ++ create_logical_monitor_config_from_output (monitor_manager, ++ monitor, ++ primary_logical_monitor_config, ++ layout_mode); ++ ++ logical_monitor_configs = g_list_append (logical_monitor_configs, ++ logical_monitor_config); ++ } ++ ++ return meta_monitors_config_new (monitor_manager, ++ logical_monitor_configs, ++ layout_mode, ++ META_MONITORS_CONFIG_FLAG_NONE); ++} ++ + MetaMonitorsConfig * + meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager) + { +diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h +index 3875e04e9..364a2b36b 100644 +--- a/src/backends/meta-monitor-config-manager.h ++++ b/src/backends/meta-monitor-config-manager.h +@@ -94,6 +94,8 @@ gboolean meta_monitor_config_manager_assign (MetaMonitorManager *manager, + META_EXPORT_TEST + MetaMonitorsConfig * meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager); + ++META_EXPORT_TEST ++MetaMonitorsConfig * meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_manager); + META_EXPORT_TEST + MetaMonitorsConfig * meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager); + +diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c +index 2d898c757..05b27c6be 100644 +--- a/src/backends/meta-monitor-manager.c ++++ b/src/backends/meta-monitor-manager.c +@@ -614,6 +614,25 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) + g_clear_object (&config); + } + ++ config = meta_monitor_config_manager_create_current (manager->config_manager); ++ if (config) ++ { ++ if (!meta_monitor_manager_apply_monitors_config (manager, ++ config, ++ method, ++ &error)) ++ { ++ g_clear_object (&config); ++ g_warning ("Failed to use current monitor configuration: %s", ++ error->message); ++ g_clear_error (&error); ++ } ++ else ++ { ++ goto done; ++ } ++ } ++ + config = meta_monitor_config_manager_create_linear (manager->config_manager); + if (config) + { +-- +2.21.0 + diff --git a/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch b/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch new file mode 100644 index 0000000..427bb17 --- /dev/null +++ b/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch @@ -0,0 +1,41 @@ +From 9f8564ce066aeb704341d6f926daec0045243b70 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 25 Jun 2020 10:06:38 +0200 +Subject: [PATCH 1/2] monitor-manager-kms: Trigger hotplug processing on gpu + removal + +--- + src/backends/native/meta-monitor-manager-kms.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c +index 9a0364441a..2819881576 100644 +--- a/src/backends/native/meta-monitor-manager-kms.c ++++ b/src/backends/native/meta-monitor-manager-kms.c +@@ -470,12 +470,18 @@ on_uevent (GUdevClient *client, + + if (!g_strcmp0 (seat_id, device_seat)) + handle_gpu_hotplug (manager_kms, device); +- } +- +- if (!g_udev_device_get_property_as_boolean (device, "HOTPLUG")) +- return; + +- handle_hotplug_event (manager); ++ handle_hotplug_event (manager); ++ } ++ else if (g_str_equal (action, "remove") && ++ g_udev_device_get_device_file (device) != NULL) ++ { ++ handle_hotplug_event (manager); ++ } ++ else if (g_udev_device_get_property_as_boolean (device, "HOTPLUG")) ++ { ++ handle_hotplug_event (manager); ++ } + } + + static void +-- +2.26.2 + diff --git a/SOURCES/0001-monitor-manager-only-reuse-initial-config-if-monitor.patch b/SOURCES/0001-monitor-manager-only-reuse-initial-config-if-monitor.patch new file mode 100644 index 0000000..dc34cee --- /dev/null +++ b/SOURCES/0001-monitor-manager-only-reuse-initial-config-if-monitor.patch @@ -0,0 +1,144 @@ +From 4ad8fd80355189ecbde6c38961335ae4be4db8b3 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Tue, 11 Sep 2018 10:19:44 -0400 +Subject: [PATCH] monitor-manager: only reuse initial-config if monitor + topology matches startup + +Right now we try to apply the current monitor config when a new +monitor is attached. The current config obviously doesn't include the +new monitor, so the new monitor isn't lit up. + +The only reason we apply the current config at all is to handle the +startup case: We want to reuse the config set in Xorg when first +logging in. + +This commit changes the code to look at the *initial config* instead +of the current config, and only if the new monitor topology matches +the start up topology. +--- + src/backends/meta-monitor-config-manager.c | 20 +++++++++++++++----- + src/backends/meta-monitor-config-manager.h | 2 +- + src/backends/meta-monitor-manager.c | 16 +++++++++++++++- + 3 files changed, 31 insertions(+), 7 deletions(-) + +diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c +index d64ca1f79..c09edbe00 100644 +--- a/src/backends/meta-monitor-config-manager.c ++++ b/src/backends/meta-monitor-config-manager.c +@@ -42,6 +42,7 @@ struct _MetaMonitorConfigManager + MetaMonitorConfigStore *config_store; + + MetaMonitorsConfig *current_config; ++ MetaMonitorsConfig *initial_config; + GQueue config_history; + }; + +@@ -663,9 +664,10 @@ create_logical_monitor_config_from_output (MetaMonitorManager *monitor + } + + MetaMonitorsConfig * +-meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_manager) ++meta_monitor_config_manager_create_initial (MetaMonitorConfigManager *config_manager) + { + MetaMonitorManager *monitor_manager = config_manager->monitor_manager; ++ MetaMonitorsConfig *initial_config; + GList *logical_monitor_configs; + MetaMonitor *primary_monitor; + MetaLogicalMonitorLayoutMode layout_mode; +@@ -673,6 +675,9 @@ meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_man + GList *monitors; + GList *l; + ++ if (config_manager->initial_config != NULL) ++ return g_object_ref (config_manager->initial_config); ++ + if (meta_monitor_config_store_get_config_count (config_manager->config_store) > 0) + return NULL; + +@@ -714,10 +719,14 @@ meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_man + logical_monitor_config); + } + +- return meta_monitors_config_new (monitor_manager, +- logical_monitor_configs, +- layout_mode, +- META_MONITORS_CONFIG_FLAG_NONE); ++ initial_config = meta_monitors_config_new (monitor_manager, ++ logical_monitor_configs, ++ layout_mode, ++ META_MONITORS_CONFIG_FLAG_NONE); ++ ++ config_manager->initial_config = g_object_ref (initial_config); ++ ++ return initial_config; + } + + MetaMonitorsConfig * +@@ -1256,6 +1265,7 @@ meta_monitor_config_manager_dispose (GObject *object) + META_MONITOR_CONFIG_MANAGER (object); + + g_clear_object (&config_manager->current_config); ++ g_clear_object (&config_manager->initial_config); + meta_monitor_config_manager_clear_history (config_manager); + + G_OBJECT_CLASS (meta_monitor_config_manager_parent_class)->dispose (object); +diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h +index 364a2b36b..409611bb0 100644 +--- a/src/backends/meta-monitor-config-manager.h ++++ b/src/backends/meta-monitor-config-manager.h +@@ -95,7 +95,7 @@ META_EXPORT_TEST + MetaMonitorsConfig * meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager); + + META_EXPORT_TEST +-MetaMonitorsConfig * meta_monitor_config_manager_create_current (MetaMonitorConfigManager *config_manager); ++MetaMonitorsConfig * meta_monitor_config_manager_create_initial (MetaMonitorConfigManager *config_manager); + META_EXPORT_TEST + MetaMonitorsConfig * meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager); + +diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c +index 05b27c6be..bb4b44188 100644 +--- a/src/backends/meta-monitor-manager.c ++++ b/src/backends/meta-monitor-manager.c +@@ -534,9 +534,11 @@ should_use_stored_config (MetaMonitorManager *manager) + MetaMonitorsConfig * + meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) + { ++ g_autoptr (MetaMonitorsConfig) initial_config = NULL; + MetaMonitorsConfig *config = NULL; + GError *error = NULL; + gboolean use_stored_config; ++ MetaMonitorsConfigKey *current_state_key; + MetaMonitorsConfigMethod method; + MetaMonitorsConfigMethod fallback_method = + META_MONITORS_CONFIG_METHOD_TEMPORARY; +@@ -547,6 +549,18 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) + else + method = META_MONITORS_CONFIG_METHOD_TEMPORARY; + ++ initial_config = meta_monitor_config_manager_create_initial (manager->config_manager); ++ ++ if (initial_config) ++ { ++ current_state_key = meta_create_monitors_config_key_for_current_state (manager); ++ ++ /* don't ever reuse initial configuration, if the monitor topology changed ++ */ ++ if (current_state_key && !meta_monitors_config_key_equal (current_state_key, initial_config->key)) ++ g_clear_object (&initial_config); ++ } ++ + if (use_stored_config) + { + config = meta_monitor_config_manager_get_stored (manager->config_manager); +@@ -614,7 +628,7 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) + g_clear_object (&config); + } + +- config = meta_monitor_config_manager_create_current (manager->config_manager); ++ config = g_steal_pointer (&initial_config); + if (config) + { + if (!meta_monitor_manager_apply_monitors_config (manager, +-- +2.21.0 + diff --git a/SOURCES/0001-monitor-manager-xrandr-Force-an-update-when-resuming.patch b/SOURCES/0001-monitor-manager-xrandr-Force-an-update-when-resuming.patch new file mode 100644 index 0000000..2242c67 --- /dev/null +++ b/SOURCES/0001-monitor-manager-xrandr-Force-an-update-when-resuming.patch @@ -0,0 +1,272 @@ +From 849902beff553de41dd3940b17672ef98f687be5 Mon Sep 17 00:00:00 2001 +From: Rui Matos +Date: Mon, 4 Jun 2018 16:35:04 -0400 +Subject: [PATCH] monitor-manager-xrandr: Force an update when resuming from + suspend + +The stack below us isn't as reliable as we'd like and in some cases +doesn't generate RRScreenChangeNotify events when e.g. resuming a +laptop on a dock, meaning that we'd miss newly attached outputs. +--- + src/backends/meta-gpu.c | 7 ++ + src/backends/meta-gpu.h | 4 + + src/backends/x11/meta-gpu-xrandr.c | 26 ++++- + .../x11/meta-monitor-manager-xrandr.c | 96 +++++++++++++++++-- + 4 files changed, 123 insertions(+), 10 deletions(-) + +diff --git a/src/backends/meta-gpu.c b/src/backends/meta-gpu.c +index 3577391e5..946f72387 100644 +--- a/src/backends/meta-gpu.c ++++ b/src/backends/meta-gpu.c +@@ -64,6 +64,13 @@ meta_gpu_has_hotplug_mode_update (MetaGpu *gpu) + return FALSE; + } + ++void ++meta_gpu_poll_hardware (MetaGpu *gpu) ++{ ++ if (META_GPU_GET_CLASS (gpu)->poll_hardware) ++ META_GPU_GET_CLASS (gpu)->poll_hardware (gpu); ++} ++ + gboolean + meta_gpu_read_current (MetaGpu *gpu, + GError **error) +diff --git a/src/backends/meta-gpu.h b/src/backends/meta-gpu.h +index 701acdc97..a2fd061f7 100644 +--- a/src/backends/meta-gpu.h ++++ b/src/backends/meta-gpu.h +@@ -36,8 +36,12 @@ struct _MetaGpuClass + + gboolean (* read_current) (MetaGpu *gpu, + GError **error); ++ void (* poll_hardware) (MetaGpu *gpu); + }; + ++META_EXPORT_TEST ++void meta_gpu_poll_hardware (MetaGpu *gpu); ++ + META_EXPORT_TEST + gboolean meta_gpu_read_current (MetaGpu *gpu, + GError **error); +diff --git a/src/backends/x11/meta-gpu-xrandr.c b/src/backends/x11/meta-gpu-xrandr.c +index 3e8a7318d..90b33d486 100644 +--- a/src/backends/x11/meta-gpu-xrandr.c ++++ b/src/backends/x11/meta-gpu-xrandr.c +@@ -44,6 +44,8 @@ struct _MetaGpuXrandr + + int max_screen_width; + int max_screen_height; ++ ++ gboolean need_hardware_poll; + }; + + G_DEFINE_TYPE (MetaGpuXrandr, meta_gpu_xrandr, META_TYPE_GPU) +@@ -81,6 +83,14 @@ get_xmode_name (XRRModeInfo *xmode) + return g_strdup_printf ("%dx%d", width, height); + } + ++static void ++meta_gpu_xrandr_poll_hardware (MetaGpu *gpu) ++{ ++ MetaGpuXrandr *gpu_xrandr = META_GPU_XRANDR (gpu); ++ ++ gpu_xrandr->need_hardware_poll = TRUE; ++} ++ + static gboolean + meta_gpu_xrandr_read_current (MetaGpu *gpu, + GError **error) +@@ -116,8 +126,18 @@ meta_gpu_xrandr_read_current (MetaGpu *gpu, + monitor_manager->screen_width = WidthOfScreen (screen); + monitor_manager->screen_height = HeightOfScreen (screen); + +- resources = XRRGetScreenResourcesCurrent (xdisplay, +- DefaultRootWindow (xdisplay)); ++ if (gpu_xrandr->need_hardware_poll) ++ { ++ resources = XRRGetScreenResources (xdisplay, ++ DefaultRootWindow (xdisplay)); ++ gpu_xrandr->need_hardware_poll = FALSE; ++ } ++ else ++ { ++ resources = XRRGetScreenResourcesCurrent (xdisplay, ++ DefaultRootWindow (xdisplay)); ++ } ++ + if (!resources) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, +@@ -250,6 +270,7 @@ meta_gpu_xrandr_finalize (GObject *object) + static void + meta_gpu_xrandr_init (MetaGpuXrandr *gpu_xrandr) + { ++ gpu_xrandr->need_hardware_poll = TRUE; + } + + static void +@@ -261,4 +282,5 @@ meta_gpu_xrandr_class_init (MetaGpuXrandrClass *klass) + object_class->finalize = meta_gpu_xrandr_finalize; + + gpu_class->read_current = meta_gpu_xrandr_read_current; ++ gpu_class->poll_hardware = meta_gpu_xrandr_poll_hardware; + } +diff --git a/src/backends/x11/meta-monitor-manager-xrandr.c b/src/backends/x11/meta-monitor-manager-xrandr.c +index 448e51fae..d60f00325 100644 +--- a/src/backends/x11/meta-monitor-manager-xrandr.c ++++ b/src/backends/x11/meta-monitor-manager-xrandr.c +@@ -71,6 +71,10 @@ struct _MetaMonitorManagerXrandr + Display *xdisplay; + int rr_event_base; + int rr_error_base; ++ ++ guint logind_watch_id; ++ guint logind_signal_sub_id; ++ + gboolean has_randr15; + + /* +@@ -102,6 +106,8 @@ typedef struct _MetaMonitorXrandrData + + GQuark quark_meta_monitor_xrandr_data; + ++static void meta_monitor_manager_xrandr_update (MetaMonitorManagerXrandr *manager_xrandr); ++ + Display * + meta_monitor_manager_xrandr_get_xdisplay (MetaMonitorManagerXrandr *manager_xrandr) + { +@@ -1016,6 +1022,62 @@ meta_monitor_manager_xrandr_get_default_layout_mode (MetaMonitorManager *manager + return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; + } + ++static void ++logind_signal_handler (GDBusConnection *connection, ++ const gchar *sender_name, ++ const gchar *object_path, ++ const gchar *interface_name, ++ const gchar *signal_name, ++ GVariant *parameters, ++ gpointer user_data) ++{ ++ MetaMonitorManagerXrandr *manager_xrandr = user_data; ++ gboolean suspending; ++ ++ if (!g_str_equal (signal_name, "PrepareForSleep")) ++ return; ++ ++ g_variant_get (parameters, "(b)", &suspending); ++ if (!suspending) ++ { ++ meta_gpu_poll_hardware (manager_xrandr->gpu); ++ meta_monitor_manager_xrandr_update (manager_xrandr); ++ } ++} ++ ++static void ++logind_appeared (GDBusConnection *connection, ++ const gchar *name, ++ const gchar *name_owner, ++ gpointer user_data) ++{ ++ MetaMonitorManagerXrandr *manager_xrandr = user_data; ++ ++ manager_xrandr->logind_signal_sub_id = g_dbus_connection_signal_subscribe (connection, ++ "org.freedesktop.login1", ++ "org.freedesktop.login1.Manager", ++ "PrepareForSleep", ++ "/org/freedesktop/login1", ++ NULL, ++ G_DBUS_SIGNAL_FLAGS_NONE, ++ logind_signal_handler, ++ manager_xrandr, ++ NULL); ++} ++ ++static void ++logind_vanished (GDBusConnection *connection, ++ const gchar *name, ++ gpointer user_data) ++{ ++ MetaMonitorManagerXrandr *manager_xrandr = user_data; ++ ++ if (connection && manager_xrandr->logind_signal_sub_id > 0) ++ g_dbus_connection_signal_unsubscribe (connection, manager_xrandr->logind_signal_sub_id); ++ ++ manager_xrandr->logind_signal_sub_id = 0; ++} ++ + static void + meta_monitor_manager_xrandr_constructed (GObject *object) + { +@@ -1072,12 +1134,23 @@ meta_monitor_manager_xrandr_finalize (GObject *object) + g_hash_table_destroy (manager_xrandr->tiled_monitor_atoms); + g_free (manager_xrandr->supported_scales); + ++ if (manager_xrandr->logind_watch_id > 0) ++ g_bus_unwatch_name (manager_xrandr->logind_watch_id); ++ manager_xrandr->logind_watch_id = 0; ++ + G_OBJECT_CLASS (meta_monitor_manager_xrandr_parent_class)->finalize (object); + } + + static void + meta_monitor_manager_xrandr_init (MetaMonitorManagerXrandr *manager_xrandr) + { ++ manager_xrandr->logind_watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, ++ "org.freedesktop.login1", ++ G_BUS_NAME_WATCHER_FLAGS_NONE, ++ logind_appeared, ++ logind_vanished, ++ manager_xrandr, ++ NULL); + } + + static void +@@ -1123,9 +1196,8 @@ is_xvnc (MetaMonitorManager *manager) + return FALSE; + } + +-gboolean +-meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xrandr, +- XEvent *event) ++static void ++meta_monitor_manager_xrandr_update (MetaMonitorManagerXrandr *manager_xrandr) + { + MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr); + MetaGpuXrandr *gpu_xrandr; +@@ -1134,11 +1206,6 @@ meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xra + gboolean is_our_configuration; + unsigned int timestamp; + +- if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify) +- return FALSE; +- +- XRRUpdateConfiguration (event); +- + meta_monitor_manager_read_current_state (manager); + + gpu_xrandr = META_GPU_XRANDR (manager_xrandr->gpu); +@@ -1173,6 +1240,19 @@ meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xra + + meta_monitor_manager_xrandr_rebuild_derived (manager, config); + } ++} ++ ++gboolean ++meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xrandr, ++ XEvent *event) ++{ ++ ++ if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify) ++ return FALSE; ++ ++ XRRUpdateConfiguration (event); ++ ++ meta_monitor_manager_xrandr_update (manager_xrandr); + + return TRUE; + } +-- +2.21.0 + diff --git a/SOURCES/0001-monitor-manager-xrandr-Move-dpms-state-and-screen-si.patch b/SOURCES/0001-monitor-manager-xrandr-Move-dpms-state-and-screen-si.patch new file mode 100644 index 0000000..8a0242c --- /dev/null +++ b/SOURCES/0001-monitor-manager-xrandr-Move-dpms-state-and-screen-si.patch @@ -0,0 +1,114 @@ +From 078547521dd709d41ac3791322f711030ccc50e9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 27 Nov 2019 19:03:50 +0100 +Subject: [PATCH 1/2] monitor-manager-xrandr: Move dpms state and screen size + updating into helpers + +To be used by no-Xrandr fallback path. +--- + src/backends/x11/meta-gpu-xrandr.c | 37 +++++++++++++------ + .../x11/meta-monitor-manager-xrandr.c | 18 ++++++--- + 2 files changed, 38 insertions(+), 17 deletions(-) + +diff --git a/src/backends/x11/meta-gpu-xrandr.c b/src/backends/x11/meta-gpu-xrandr.c +index 90b33d486..1884278ca 100644 +--- a/src/backends/x11/meta-gpu-xrandr.c ++++ b/src/backends/x11/meta-gpu-xrandr.c +@@ -91,6 +91,30 @@ meta_gpu_xrandr_poll_hardware (MetaGpu *gpu) + gpu_xrandr->need_hardware_poll = TRUE; + } + ++static void ++update_screen_size (MetaGpuXrandr *gpu_xrandr) ++{ ++ MetaGpu *gpu = META_GPU (gpu_xrandr); ++ MetaMonitorManager *monitor_manager = meta_gpu_get_monitor_manager (gpu); ++ MetaMonitorManagerXrandr *monitor_manager_xrandr = ++ META_MONITOR_MANAGER_XRANDR (monitor_manager); ++ Display *xdisplay = ++ meta_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr); ++ int min_width, min_height; ++ Screen *screen; ++ ++ XRRGetScreenSizeRange (xdisplay, DefaultRootWindow (xdisplay), ++ &min_width, ++ &min_height, ++ &gpu_xrandr->max_screen_width, ++ &gpu_xrandr->max_screen_height); ++ ++ screen = ScreenOfDisplay (xdisplay, DefaultScreen (xdisplay)); ++ /* This is updated because we called XRRUpdateConfiguration. */ ++ monitor_manager->screen_width = WidthOfScreen (screen); ++ monitor_manager->screen_height = HeightOfScreen (screen); ++} ++ + static gboolean + meta_gpu_xrandr_read_current (MetaGpu *gpu, + GError **error) +@@ -105,8 +129,6 @@ meta_gpu_xrandr_read_current (MetaGpu *gpu, + RROutput primary_output; + unsigned int i, j; + GList *l; +- int min_width, min_height; +- Screen *screen; + GList *outputs = NULL; + GList *modes = NULL; + GList *crtcs = NULL; +@@ -115,16 +137,7 @@ meta_gpu_xrandr_read_current (MetaGpu *gpu, + XRRFreeScreenResources (gpu_xrandr->resources); + gpu_xrandr->resources = NULL; + +- XRRGetScreenSizeRange (xdisplay, DefaultRootWindow (xdisplay), +- &min_width, +- &min_height, +- &gpu_xrandr->max_screen_width, +- &gpu_xrandr->max_screen_height); +- +- screen = ScreenOfDisplay (xdisplay, DefaultScreen (xdisplay)); +- /* This is updated because we called XRRUpdateConfiguration. */ +- monitor_manager->screen_width = WidthOfScreen (screen); +- monitor_manager->screen_height = HeightOfScreen (screen); ++ update_screen_size (gpu_xrandr); + + if (gpu_xrandr->need_hardware_poll) + { +diff --git a/src/backends/x11/meta-monitor-manager-xrandr.c b/src/backends/x11/meta-monitor-manager-xrandr.c +index b8d6342b6..7a0b43ac4 100644 +--- a/src/backends/x11/meta-monitor-manager-xrandr.c ++++ b/src/backends/x11/meta-monitor-manager-xrandr.c +@@ -146,12 +146,9 @@ x11_dpms_state_to_power_save (CARD16 dpms_state) + } + + static void +-meta_monitor_manager_xrandr_read_current_state (MetaMonitorManager *manager) ++meta_monitor_manager_xrandr_update_dpms_state (MetaMonitorManagerXrandr *manager_xrandr) + { +- MetaMonitorManagerXrandr *manager_xrandr = +- META_MONITOR_MANAGER_XRANDR (manager); +- MetaMonitorManagerClass *parent_class = +- META_MONITOR_MANAGER_CLASS (meta_monitor_manager_xrandr_parent_class); ++ MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr); + Display *xdisplay = meta_monitor_manager_xrandr_get_xdisplay (manager_xrandr); + BOOL dpms_capable, dpms_enabled; + CARD16 dpms_state; +@@ -167,6 +164,17 @@ meta_monitor_manager_xrandr_read_current_state (MetaMonitorManager *manager) + power_save_mode = META_POWER_SAVE_UNSUPPORTED; + + meta_monitor_manager_power_save_mode_changed (manager, power_save_mode); ++} ++ ++static void ++meta_monitor_manager_xrandr_read_current_state (MetaMonitorManager *manager) ++{ ++ MetaMonitorManagerXrandr *manager_xrandr = ++ META_MONITOR_MANAGER_XRANDR (manager); ++ MetaMonitorManagerClass *parent_class = ++ META_MONITOR_MANAGER_CLASS (meta_monitor_manager_xrandr_parent_class); ++ ++ meta_monitor_manager_xrandr_update_dpms_state (manager_xrandr); + + parent_class->read_current_state (manager); + } +-- +2.23.0 + diff --git a/SOURCES/0001-monitor-manager-xrandr-Work-around-spurious-hotplugs.patch b/SOURCES/0001-monitor-manager-xrandr-Work-around-spurious-hotplugs.patch new file mode 100644 index 0000000..c487119 --- /dev/null +++ b/SOURCES/0001-monitor-manager-xrandr-Work-around-spurious-hotplugs.patch @@ -0,0 +1,62 @@ +From 7e21503dc7c3b8321475eb5ccfdb23e71f86c0a0 Mon Sep 17 00:00:00 2001 +From: Rui Matos +Date: Tue, 6 Oct 2015 21:16:18 +0200 +Subject: [PATCH] monitor-manager-xrandr: Work around spurious hotplugs on Xvnc + +Xvnc turns its outputs off/on on every mode set which makes us believe +there was an hotplug when there actually wasn't. Work around this by +requiring new randr configuration timestamps to be ahead of the last +set timestamp by at least 100 ms for us to consider them an actual +hotplug. +--- + .../x11/meta-monitor-manager-xrandr.c | 20 ++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/src/backends/x11/meta-monitor-manager-xrandr.c b/src/backends/x11/meta-monitor-manager-xrandr.c +index 45c81f4eb..448e51fae 100644 +--- a/src/backends/x11/meta-monitor-manager-xrandr.c ++++ b/src/backends/x11/meta-monitor-manager-xrandr.c +@@ -1110,6 +1110,19 @@ meta_monitor_manager_xrandr_class_init (MetaMonitorManagerXrandrClass *klass) + g_quark_from_static_string ("-meta-monitor-xrandr-data"); + } + ++static gboolean ++is_xvnc (MetaMonitorManager *manager) ++{ ++ MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); ++ GList *l; ++ ++ for (l = meta_gpu_get_outputs (manager_xrandr->gpu); l; l = l->next) ++ if (g_str_has_prefix (((MetaOutput *)l->data)->name, "VNC-")) ++ return TRUE; ++ ++ return FALSE; ++} ++ + gboolean + meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xrandr, + XEvent *event) +@@ -1119,6 +1132,7 @@ meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xra + XRRScreenResources *resources; + gboolean is_hotplug; + gboolean is_our_configuration; ++ unsigned int timestamp; + + if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify) + return FALSE; +@@ -1130,7 +1144,11 @@ meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xra + gpu_xrandr = META_GPU_XRANDR (manager_xrandr->gpu); + resources = meta_gpu_xrandr_get_resources (gpu_xrandr); + +- is_hotplug = resources->timestamp < resources->configTimestamp; ++ timestamp = resources->timestamp; ++ if (is_xvnc (manager)) ++ timestamp += 100; ++ ++ is_hotplug = (timestamp < resources->configTimestamp); + is_our_configuration = (resources->timestamp == + manager_xrandr->last_xrandr_set_timestamp); + if (is_hotplug) +-- +2.21.0 + diff --git a/SOURCES/0001-output-kms-Add-more-heuristics-to-decide-when-to-off.patch b/SOURCES/0001-output-kms-Add-more-heuristics-to-decide-when-to-off.patch new file mode 100644 index 0000000..9f26891 --- /dev/null +++ b/SOURCES/0001-output-kms-Add-more-heuristics-to-decide-when-to-off.patch @@ -0,0 +1,81 @@ +From cf16c0d16b42215f35dcede6163235ad5ec1734b Mon Sep 17 00:00:00 2001 +From: Mario Limonciello +Date: Thu, 18 Aug 2022 13:36:20 -0500 +Subject: [PATCH] output/kms: Add more heuristics to decide when to offer + fallback modes + +If the panel is connected via eDP and supports more than one mode +at different resolutions don't try to add more. + +Part-of: +(cherry picked from commit 96aa0fb8536eca579ceb1b17d83e19cf9e3e9e81) +--- + src/backends/native/meta-output-kms.c | 45 ++++++++++++++++++++++----- + 1 file changed, 38 insertions(+), 7 deletions(-) + +diff --git a/src/backends/native/meta-output-kms.c b/src/backends/native/meta-output-kms.c +index f3cc543c4d..44c25c5648 100644 +--- a/src/backends/native/meta-output-kms.c ++++ b/src/backends/native/meta-output-kms.c +@@ -505,6 +505,43 @@ compare_modes (const void *one, + return g_strcmp0 (b->name, a->name); + } + ++static gboolean ++are_all_modes_equally_sized (MetaOutput *output) ++{ ++ MetaCrtcMode *base = output->modes[0]; ++ int i; ++ ++ for (i = 1; i < output->n_modes; i++) ++ { ++ MetaCrtcMode *crtc_mode = output->modes[i]; ++ ++ if (base->width != crtc_mode->width || ++ base->height != crtc_mode->height) ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static void ++maybe_add_fallback_modes (MetaOutput *output, ++ MetaGpuKms *gpu_kms) ++{ ++ MetaOutputKms *output_kms = output->driver_private; ++ ++ if (!output_kms->has_scaling) ++ return; ++ ++ if (output->n_modes == 0) ++ return; ++ ++ if (output_kms->connector->connector_type == DRM_MODE_CONNECTOR_eDP && ++ !are_all_modes_equally_sized (output)) ++ return; ++ ++ add_common_modes (output, gpu_kms); ++} ++ + static gboolean + init_output_modes (MetaOutput *output, + MetaGpuKms *gpu_kms, +@@ -528,13 +565,7 @@ init_output_modes (MetaOutput *output, + output->preferred_mode = output->modes[i]; + } + +- /* FIXME: MSC feature bit? */ +- /* Presume that if the output supports scaling, then we have +- * a panel fitter capable of adjusting any mode to suit. +- */ +- if (output_kms->has_scaling) +- add_common_modes (output, gpu_kms); +- ++ maybe_add_fallback_modes (output, gpu_kms); + if (!output->modes) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, +-- +2.37.1 + diff --git a/SOURCES/0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch b/SOURCES/0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch new file mode 100644 index 0000000..2be8b13 --- /dev/null +++ b/SOURCES/0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch @@ -0,0 +1,108 @@ +From d107b52939ca0acb1f8dacf1275278edba64eebe Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 1 Oct 2019 11:53:57 +0200 +Subject: [PATCH] renderer: Add API to check whether renderer is hardware + accelerated + +Also expose an introspected variant via the MetaBackend. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/838 +--- + src/backends/meta-backend.c | 15 +++++++++++++++ + src/backends/meta-renderer.c | 27 +++++++++++++++++++++++++++ + src/backends/meta-renderer.h | 2 ++ + src/meta/meta-backend.h | 3 +++ + 4 files changed, 47 insertions(+) + +diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c +index 72cfbdaf3..e61181f9a 100644 +--- a/src/backends/meta-backend.c ++++ b/src/backends/meta-backend.c +@@ -985,6 +985,21 @@ meta_backend_get_remote_access_controller (MetaBackend *backend) + #endif + } + ++/** ++ * meta_backend_is_rendering_hardware_accelerated: ++ * @backend: A #MetaBackend ++ * ++ * Returns: %TRUE if the rendering is hardware accelerated, otherwise ++ * %FALSE. ++ */ ++gboolean ++meta_backend_is_rendering_hardware_accelerated (MetaBackend *backend) ++{ ++ MetaRenderer *renderer = meta_backend_get_renderer (backend); ++ ++ return meta_renderer_is_hardware_accelerated (renderer); ++} ++ + /** + * meta_backend_grab_device: (skip) + */ +diff --git a/src/backends/meta-renderer.c b/src/backends/meta-renderer.c +index 87ba9f9f0..470220fc8 100644 +--- a/src/backends/meta-renderer.c ++++ b/src/backends/meta-renderer.c +@@ -166,6 +166,33 @@ meta_renderer_get_view_from_logical_monitor (MetaRenderer *renderer, + return NULL; + } + ++gboolean ++meta_renderer_is_hardware_accelerated (MetaRenderer *renderer) ++{ ++ MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); ++ MetaBackend *backend = meta_get_backend (); ++ ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); ++ CoglContext *cogl_context = ++ clutter_backend_get_cogl_context (clutter_backend); ++ CoglGpuInfo *info = &cogl_context->gpu; ++ ++ switch (info->architecture) ++ { ++ case COGL_GPU_INFO_ARCHITECTURE_UNKNOWN: ++ case COGL_GPU_INFO_ARCHITECTURE_SANDYBRIDGE: ++ case COGL_GPU_INFO_ARCHITECTURE_SGX: ++ case COGL_GPU_INFO_ARCHITECTURE_MALI: ++ return TRUE; ++ case COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE: ++ case COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE: ++ case COGL_GPU_INFO_ARCHITECTURE_SWRAST: ++ return FALSE; ++ } ++ ++ g_assert_not_reached (); ++ return FALSE; ++} ++ + static void + meta_renderer_finalize (GObject *object) + { +diff --git a/src/backends/meta-renderer.h b/src/backends/meta-renderer.h +index 478baee91..97bf36860 100644 +--- a/src/backends/meta-renderer.h ++++ b/src/backends/meta-renderer.h +@@ -59,4 +59,6 @@ GList * meta_renderer_get_views (MetaRenderer *renderer); + MetaRendererView * meta_renderer_get_view_from_logical_monitor (MetaRenderer *renderer, + MetaLogicalMonitor *logical_monitor); + ++gboolean meta_renderer_is_hardware_accelerated (MetaRenderer *renderer); ++ + #endif /* META_RENDERER_H */ +diff --git a/src/meta/meta-backend.h b/src/meta/meta-backend.h +index aaa6aae97..8edc0bf2c 100644 +--- a/src/meta/meta-backend.h ++++ b/src/meta/meta-backend.h +@@ -64,6 +64,9 @@ MetaSettings *meta_backend_get_settings (MetaBackend *backend); + META_EXPORT + MetaRemoteAccessController * meta_backend_get_remote_access_controller (MetaBackend *backend); + ++META_EXPORT ++gboolean meta_backend_is_rendering_hardware_accelerated (MetaBackend *backend); ++ + META_EXPORT + void meta_clutter_init (void); + +-- +2.26.2 + diff --git a/SOURCES/0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch b/SOURCES/0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch new file mode 100644 index 0000000..a0f76ff --- /dev/null +++ b/SOURCES/0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch @@ -0,0 +1,40 @@ +From b32ae04c122f4f76ffad296c15ba00a13800db57 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 2 Jun 2020 16:33:05 +0000 +Subject: [PATCH 1/2] screen-cast-src: Destroy hash dmabuf table after stream + +The stream will clean up the buffers, so let it do that before we +destroy them under its feet. Note that it'll only do this after the +following PipeWire commit: + + commit fbaa4ddedd84afdffca16f090dcc4b0db8ccfc29 + Author: Wim Taymans + Date: Mon Jun 1 15:36:09 2020 +0200 + + stream: allow NULL param and 0 buffers in disconnect + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1283 + + +(cherry picked from commit 97175f8fa14171606ecb95d0bf107ef8b2d71b74) +--- + src/backends/meta-screen-cast-stream-src.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 0500bfec5..ff4af440c 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -988,8 +988,8 @@ meta_screen_cast_stream_src_finalize (GObject *object) + if (meta_screen_cast_stream_src_is_enabled (src)) + meta_screen_cast_stream_src_disable (src); + +- g_clear_pointer (&priv->dmabuf_handles, g_hash_table_destroy); + g_clear_pointer (&priv->pipewire_stream, pw_stream_destroy); ++ g_clear_pointer (&priv->dmabuf_handles, g_hash_table_destroy); + g_clear_pointer (&priv->pipewire_core, pw_core_disconnect); + g_clear_pointer (&priv->pipewire_context, pw_context_destroy); + g_source_destroy (&priv->pipewire_source->base); +-- +2.26.2 + diff --git a/SOURCES/0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch b/SOURCES/0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch new file mode 100644 index 0000000..095e0d2 --- /dev/null +++ b/SOURCES/0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch @@ -0,0 +1,101 @@ +From 639b7ba7f2729a95593c0b85d4789f76152e6099 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 18 Jun 2020 21:17:29 +0200 +Subject: [PATCH] stage/x11: Check that message is WM_PROTOCOLS before assuming + so + +When a touch sequence was rejected, we'd update the event timestamps of +incoming touch events to help with implementing grabs. This was done by +sending a ClientMessage with a counter, and comparing the counter to +decide whether we're seing a replayed event or not. + +This had the unforseen consequence that we would potentially end up +destroying all actors including the stage, since, when mutter receives a +ClientMessage event, it would assume that it's a WM_PROTOCOLS event, and +handle it as such. The problem with this approach is that it would +ignore fact that there might be other ClientMessage types sent to it, +for example the touch synchronization one. What could happen is that the +touch count value would match up with the value of the WM_DELETE_WINDOW +atom, clutter would treat this as WM_PROTOCOLS:WM_DELETE_WINDOW, which +it'd translate to clutter_actor_destroy(stage). + +Destroying the stage in such a way is not expected, and caused wierd +crashes in different places depending on what was going on. + +This commit make sure we only treat WM_PROTOCOLS client messages as +WM_PROTOCOLS client messages effectively avoiding the issue. + +This fixes crashes such as: + + #0 meta_window_get_buffer_rect (window=0x0, rect=rect@entry=0x7ffd7fc62e40) at core/window.c:4396 + #1 0x00007f1e2634837f in get_top_visible_window_actor (compositor=0x297d700, compositor=0x297d700) at compositor/compositor.c:1059 + #2 meta_compositor_sync_stack (compositor=0x297d700, stack=, stack@entry=0x26e3140) at compositor/compositor.c:1176 + #3 0x00007f1e263757ac in meta_stack_tracker_sync_stack (tracker=0x297dbc0) at core/stack-tracker.c:871 + #4 0x00007f1e26375899 in stack_tracker_sync_stack_later (data=) at core/stack-tracker.c:881 + #5 0x00007f1e26376914 in run_repaint_laters (laters_list=0x7f1e2663b7d8 ) at core/util.c:809 + #6 run_all_repaint_laters (data=) at core/util.c:826 + #7 0x00007f1e26b18325 in _clutter_run_repaint_functions (flags=flags@entry=CLUTTER_REPAINT_FLAGS_PRE_PAINT) at clutter-main.c:3448 + #8 0x00007f1e26b18fc5 in master_clock_update_stages (master_clock=0x32d6a80, stages=0x4e5a740) at clutter-master-clock-default.c:437 + #9 clutter_clock_dispatch (source=, callback=, user_data=) at clutter-master-clock-default.c:567 + #10 0x00007f1e27e48049 in g_main_dispatch (context=0x225b8d0) at gmain.c:3175 + #11 g_main_context_dispatch (context=context@entry=0x225b8d0) at gmain.c:3828 + #12 0x00007f1e27e483a8 in g_main_context_iterate (context=0x225b8d0, block=block@entry=1, dispatch=dispatch@entry=1, self=) at gmain.c:3901 + #13 0x00007f1e27e4867a in g_main_loop_run (loop=0x24e29f0) at gmain.c:4097 + #14 0x00007f1e2636a3dc in meta_run () at core/main.c:666 + #15 0x000000000040219c in main (argc=1, argv=0x7ffd7fc63238) at ../src/main.c:534 + +and + + #0 0x00007f93943c1f25 in raise () at /usr/lib/libc.so.6 + #1 0x00007f93943ab897 in abort () at /usr/lib/libc.so.6 + #2 0x00007f9393e1e062 in g_assertion_message (domain=, file=, line=, func=0x7f93933e6860 <__func__.116322> "meta_x11_get_stage_window", + #3 0x00007f9393e4ab1d in g_assertion_message_expr () + #4 0x00007f939338ecd7 in meta_x11_get_stage_window (stage=) at ../mutter/src/backends/x11/meta-stage-x11.c:923 + #5 0x00007f939339e599 in meta_backend_x11_cm_translate_device_event (x11=, device_event=0x55bc8bcfd6b0) at ../mutter/src/backends/x11/cm/meta-backend-x11-cm.c:381 + #6 0x00007f939339f2e2 in meta_backend_x11_translate_device_event (device_event=0x55bc8bcfd6b0, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:179 + #7 0x00007f939339f2e2 in translate_device_event (device_event=0x55bc8bcfd6b0, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:208 + #8 0x00007f939339f2e2 in maybe_spoof_event_as_stage_event (input_event=0x55bc8bcfd6b0, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:284 + #9 0x00007f939339f2e2 in handle_input_event (event=0x7fff62d60490, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:309 + #10 0x00007f939339f2e2 in handle_host_xevent (event=0x7fff62d60490, backend=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:413 + #11 0x00007f939339f2e2 in x_event_source_dispatch (source=, callback=, user_data=) at ../mutter/src/backends/x11/meta-backend-x11.c:467 + #12 0x00007f9393e6c39e in g_main_dispatch (context=0x55bc89dd03e0) at ../glib/glib/gmain.c:3179 + #13 0x00007f9393e6c39e in g_main_context_dispatch (context=context@entry=0x55bc89dd03e0) at ../glib/glib/gmain.c:3844 + #14 0x00007f9393e6e1b1 in g_main_context_iterate (context=0x55bc89dd03e0, block=block@entry=1, dispatch=dispatch@entry=1, self=) at ../glib/glib/gmain.c:3917 + #15 0x00007f9393e6f0c3 in g_main_loop_run (loop=0x55bc8a042640) at ../glib/glib/gmain.c:4111 + #16 0x00007f9393369a0c in meta_run () at ../mutter/src/core/main.c:676 + #17 0x000055bc880f2426 in main (argc=, argv=) at ../gnome-shell/src/main.c:552 + +Related: https://gitlab.gnome.org/GNOME/mutter/-/issues/338 +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/951 + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1317 +--- + clutter/clutter/x11/clutter-stage-x11.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/clutter/clutter/x11/clutter-stage-x11.c b/clutter/clutter/x11/clutter-stage-x11.c +index d043bcf31d..123078fc22 100644 +--- a/clutter/clutter/x11/clutter-stage-x11.c ++++ b/clutter/clutter/x11/clutter-stage-x11.c +@@ -1306,11 +1306,14 @@ clutter_stage_x11_translate_event (ClutterEventTranslator *translator, + _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), + stage, + (unsigned int) stage_xwindow); +- if (handle_wm_protocols_event (backend_x11, stage_x11, xevent)) ++ if (xevent->xclient.message_type == backend_x11->atom_WM_PROTOCOLS) + { +- event->any.type = CLUTTER_DELETE; +- event->any.stage = stage; +- res = CLUTTER_TRANSLATE_QUEUE; ++ if (handle_wm_protocols_event (backend_x11, stage_x11, xevent)) ++ { ++ event->any.type = CLUTTER_DELETE; ++ event->any.stage = stage; ++ res = CLUTTER_TRANSLATE_QUEUE; ++ } + } + break; + +-- +2.26.2 + diff --git a/SOURCES/0001-wayland-Allow-Xwayland-grabs-on-selected-apps.patch b/SOURCES/0001-wayland-Allow-Xwayland-grabs-on-selected-apps.patch new file mode 100644 index 0000000..83f42ff --- /dev/null +++ b/SOURCES/0001-wayland-Allow-Xwayland-grabs-on-selected-apps.patch @@ -0,0 +1,35 @@ +From 9dfe362f41b8811450cb563c39899fafe8ec2b63 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Fri, 26 Oct 2018 08:49:39 +0200 +Subject: [PATCH] wayland: Allow Xwayland grabs on selected apps + +Allow Xwayland grabs on a selected set of X11 applications. +--- + data/org.gnome.mutter.wayland.gschema.xml.in | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/data/org.gnome.mutter.wayland.gschema.xml.in b/data/org.gnome.mutter.wayland.gschema.xml.in +index 48241296e..7a6ab9288 100644 +--- a/data/org.gnome.mutter.wayland.gschema.xml.in ++++ b/data/org.gnome.mutter.wayland.gschema.xml.in +@@ -60,7 +60,7 @@ + gettext-domain="@GETTEXT_DOMAIN@"> + + +- false ++ true + Allow grabs with Xwayland + + Allow keyboard grabs issued by X11 applications running in Xwayland +@@ -73,7 +73,7 @@ + + + +- [] ++ ['@XWAYLAND_GRAB_DEFAULT_ACCESS_RULES@'] + Xwayland applications allowed to issue keyboard grabs + + List the resource names or resource class of X11 windows either +-- +2.21.0 + diff --git a/SOURCES/0001-wayland-Check-stylus-serials-on-meta_wayland_seat_ca.patch b/SOURCES/0001-wayland-Check-stylus-serials-on-meta_wayland_seat_ca.patch new file mode 100644 index 0000000..486df56 --- /dev/null +++ b/SOURCES/0001-wayland-Check-stylus-serials-on-meta_wayland_seat_ca.patch @@ -0,0 +1,109 @@ +From f2b3dd318f1165849b45a86251724939b100ef7d Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Mon, 28 Oct 2019 18:07:31 +0100 +Subject: [PATCH] wayland: Check stylus serials on + meta_wayland_seat_can_popup() + +This allows xdg_popup.grab() to work with styli. Without this check +we would bail out and emit xdg_popup.popup_done, leaving stylus users +unable to interact with popup menus, comboboxes, etc... + +Closes: https://gitlab.gnome.org/GNOME/mutter/issues/886 +--- + src/wayland/meta-wayland-seat.c | 10 +++++++++- + src/wayland/meta-wayland-tablet-seat.c | 17 +++++++++++++++++ + src/wayland/meta-wayland-tablet-seat.h | 2 ++ + src/wayland/meta-wayland-tablet-tool.c | 7 +++++++ + src/wayland/meta-wayland-tablet-tool.h | 2 ++ + 5 files changed, 37 insertions(+), 1 deletion(-) + +diff --git a/src/wayland/meta-wayland-seat.c b/src/wayland/meta-wayland-seat.c +index 91fe376ff..cf41d6eb8 100644 +--- a/src/wayland/meta-wayland-seat.c ++++ b/src/wayland/meta-wayland-seat.c +@@ -504,9 +504,17 @@ gboolean + meta_wayland_seat_can_popup (MetaWaylandSeat *seat, + uint32_t serial) + { ++ MetaWaylandCompositor *compositor; ++ MetaWaylandTabletSeat *tablet_seat; ++ ++ compositor = meta_wayland_compositor_get_default (); ++ tablet_seat = ++ meta_wayland_tablet_manager_ensure_seat (compositor->tablet_manager, seat); ++ + return (meta_wayland_pointer_can_popup (seat->pointer, serial) || + meta_wayland_keyboard_can_popup (seat->keyboard, serial) || +- meta_wayland_touch_can_popup (seat->touch, serial)); ++ meta_wayland_touch_can_popup (seat->touch, serial) || ++ meta_wayland_tablet_seat_can_popup (tablet_seat, serial)); + } + + gboolean +diff --git a/src/wayland/meta-wayland-tablet-seat.c b/src/wayland/meta-wayland-tablet-seat.c +index b4bc4aa58..b1964714a 100644 +--- a/src/wayland/meta-wayland-tablet-seat.c ++++ b/src/wayland/meta-wayland-tablet-seat.c +@@ -552,3 +552,20 @@ meta_wayland_tablet_seat_set_pad_focus (MetaWaylandTabletSeat *tablet_seat, + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &pad)) + meta_wayland_tablet_pad_set_focus (pad, surface); + } ++ ++gboolean ++meta_wayland_tablet_seat_can_popup (MetaWaylandTabletSeat *tablet_seat, ++ uint32_t serial) ++{ ++ MetaWaylandTabletTool *tool; ++ GHashTableIter iter; ++ ++ g_hash_table_iter_init (&iter, tablet_seat->tools); ++ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &tool)) ++ { ++ if (meta_wayland_tablet_tool_can_popup (tool, serial)) ++ return TRUE; ++ } ++ ++ return FALSE; ++} +diff --git a/src/wayland/meta-wayland-tablet-seat.h b/src/wayland/meta-wayland-tablet-seat.h +index c083dec5f..e3be5f264 100644 +--- a/src/wayland/meta-wayland-tablet-seat.h ++++ b/src/wayland/meta-wayland-tablet-seat.h +@@ -75,5 +75,7 @@ MetaWaylandTablet *meta_wayland_tablet_seat_lookup_paired_tablet (MetaWaylan + MetaWaylandTabletPad *pad); + GList *meta_wayland_tablet_seat_lookup_paired_pads (MetaWaylandTabletSeat *tablet_seat, + MetaWaylandTablet *tablet); ++gboolean meta_wayland_tablet_seat_can_popup (MetaWaylandTabletSeat *tablet_seat, ++ uint32_t serial); + + #endif /* META_WAYLAND_TABLET_SEAT_H */ +diff --git a/src/wayland/meta-wayland-tablet-tool.c b/src/wayland/meta-wayland-tablet-tool.c +index c02831d73..065c834bb 100644 +--- a/src/wayland/meta-wayland-tablet-tool.c ++++ b/src/wayland/meta-wayland-tablet-tool.c +@@ -1018,3 +1018,10 @@ meta_wayland_tablet_tool_can_grab_surface (MetaWaylandTabletTool *tool, + return ((tool->down_serial == serial || tool->button_serial == serial) && + tablet_tool_can_grab_surface (tool, surface)); + } ++ ++gboolean ++meta_wayland_tablet_tool_can_popup (MetaWaylandTabletTool *tool, ++ uint32_t serial) ++{ ++ return tool->down_serial == serial || tool->button_serial == serial; ++} +diff --git a/src/wayland/meta-wayland-tablet-tool.h b/src/wayland/meta-wayland-tablet-tool.h +index 71bc86643..315e26bde 100644 +--- a/src/wayland/meta-wayland-tablet-tool.h ++++ b/src/wayland/meta-wayland-tablet-tool.h +@@ -85,5 +85,7 @@ void meta_wayland_tablet_tool_set_cursor_position (MetaWaylandTabletTool *t + gboolean meta_wayland_tablet_tool_can_grab_surface (MetaWaylandTabletTool *tool, + MetaWaylandSurface *surface, + uint32_t serial); ++gboolean meta_wayland_tablet_tool_can_popup (MetaWaylandTabletTool *tool, ++ uint32_t serial); + + #endif /* META_WAYLAND_TABLET_TOOL_H */ +-- +2.23.0 + diff --git a/SOURCES/0001-wayland-Move-check-for-present-window-out-of-the-act.patch b/SOURCES/0001-wayland-Move-check-for-present-window-out-of-the-act.patch new file mode 100644 index 0000000..940bc76 --- /dev/null +++ b/SOURCES/0001-wayland-Move-check-for-present-window-out-of-the-act.patch @@ -0,0 +1,150 @@ +From 9269b09028ae51c7bb74e9cc9aefafd8eaa882d6 Mon Sep 17 00:00:00 2001 +From: Robert Mader +Date: Tue, 16 Apr 2019 23:35:28 +0200 +Subject: [PATCH 1/2] wayland: Move check for present window out of the + actor-surface class + +All child classes of `MetaWaylandShellSurface` as well as +`MetaWaylandSurfaceRoleXWayland` should only sync their actor if +their toplevel surface has a window. Currently this check is done +in the actor-surface class, but not all surface classes have a +toplevel window, e.g. dnd-surfaces. +Move the check to the right places. + +For subsurfaces this assumes that the subsurface is not the child of +a window-less surface (like, as stated above, e.g. a dnd-surface). +If we want to support subsurfaces of window-less surfaces in the future +we have to extend the check here. +But as this is not a regression, ignore this case for now. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/537 +(cherry picked from commit 7e2a0ede16bed5671fe55d3d81ccc9f82eebd94b) +--- + src/wayland/meta-wayland-actor-surface.c | 7 ------- + src/wayland/meta-wayland-shell-surface.c | 20 ++++++++++++++++++++ + src/wayland/meta-wayland-subsurface.c | 5 ++++- + src/wayland/meta-xwayland.c | 18 ++++++++++++++++++ + 4 files changed, 42 insertions(+), 8 deletions(-) + +diff --git a/src/wayland/meta-wayland-actor-surface.c b/src/wayland/meta-wayland-actor-surface.c +index 037dd901ab..e2143e51f1 100644 +--- a/src/wayland/meta-wayland-actor-surface.c ++++ b/src/wayland/meta-wayland-actor-surface.c +@@ -295,9 +295,6 @@ meta_wayland_actor_surface_commit (MetaWaylandSurfaceRole *surface_role, + META_WAYLAND_ACTOR_SURFACE (surface_role); + MetaWaylandActorSurfacePrivate *priv = + meta_wayland_actor_surface_get_instance_private (actor_surface); +- MetaWaylandSurface *surface = +- meta_wayland_surface_role_get_surface (surface_role); +- MetaWaylandSurface *toplevel_surface; + + if (!wl_list_empty (&pending->frame_callback_list) && + priv->actor && +@@ -311,10 +308,6 @@ meta_wayland_actor_surface_commit (MetaWaylandSurfaceRole *surface_role, + + meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); + +- toplevel_surface = meta_wayland_surface_get_toplevel (surface); +- if (!toplevel_surface || !toplevel_surface->window) +- return; +- + meta_wayland_actor_surface_sync_actor_state (actor_surface); + } + +diff --git a/src/wayland/meta-wayland-shell-surface.c b/src/wayland/meta-wayland-shell-surface.c +index 04f2aaeea8..f8354ab7c5 100644 +--- a/src/wayland/meta-wayland-shell-surface.c ++++ b/src/wayland/meta-wayland-shell-surface.c +@@ -175,6 +175,22 @@ meta_wayland_shell_surface_surface_commit (MetaWaylandSurfaceRole *surface_role + window->buffer_rect.height = cogl_texture_get_height (texture) * scale; + } + ++static void ++meta_wayland_shell_surface_sync_actor_state (MetaWaylandActorSurface *actor_surface) ++{ ++ MetaWaylandSurfaceRole *surface_role = ++ META_WAYLAND_SURFACE_ROLE (actor_surface); ++ MetaWaylandSurface *surface = ++ meta_wayland_surface_role_get_surface (surface_role); ++ MetaWaylandActorSurfaceClass *actor_surface_class = ++ META_WAYLAND_ACTOR_SURFACE_CLASS (meta_wayland_shell_surface_parent_class); ++ MetaWaylandSurface *toplevel_surface; ++ ++ toplevel_surface = meta_wayland_surface_get_toplevel (surface); ++ if (toplevel_surface && toplevel_surface->window) ++ actor_surface_class->sync_actor_state (actor_surface); ++} ++ + static void + meta_wayland_shell_surface_init (MetaWaylandShellSurface *role) + { +@@ -185,6 +201,10 @@ meta_wayland_shell_surface_class_init (MetaWaylandShellSurfaceClass *klass) + { + MetaWaylandSurfaceRoleClass *surface_role_class = + META_WAYLAND_SURFACE_ROLE_CLASS (klass); ++ MetaWaylandActorSurfaceClass *actor_surface_class = ++ META_WAYLAND_ACTOR_SURFACE_CLASS (klass); + + surface_role_class->commit = meta_wayland_shell_surface_surface_commit; ++ actor_surface_class->sync_actor_state = ++ meta_wayland_shell_surface_sync_actor_state; + } +diff --git a/src/wayland/meta-wayland-subsurface.c b/src/wayland/meta-wayland-subsurface.c +index c7059b99a2..9a7ff3ec12 100644 +--- a/src/wayland/meta-wayland-subsurface.c ++++ b/src/wayland/meta-wayland-subsurface.c +@@ -199,8 +199,11 @@ meta_wayland_subsurface_sync_actor_state (MetaWaylandActorSurface *actor_surface + meta_wayland_surface_role_get_surface (surface_role); + MetaWaylandActorSurfaceClass *actor_surface_class = + META_WAYLAND_ACTOR_SURFACE_CLASS (meta_wayland_subsurface_parent_class); ++ MetaWaylandSurface *toplevel_surface; + +- actor_surface_class->sync_actor_state (actor_surface); ++ toplevel_surface = meta_wayland_surface_get_toplevel (surface); ++ if (toplevel_surface && toplevel_surface->window) ++ actor_surface_class->sync_actor_state (actor_surface); + + sync_actor_subsurface_state (surface); + } +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index 6e4b9a8ffd..b71c638d93 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -794,6 +794,20 @@ xwayland_surface_get_toplevel (MetaWaylandSurfaceRole *surface_role) + return meta_wayland_surface_role_get_surface (surface_role); + } + ++static void ++xwayland_surface_sync_actor_state (MetaWaylandActorSurface *actor_surface) ++{ ++ MetaWaylandSurfaceRole *surface_role = ++ META_WAYLAND_SURFACE_ROLE (actor_surface); ++ MetaWaylandSurface *surface = ++ meta_wayland_surface_role_get_surface (surface_role); ++ MetaWaylandActorSurfaceClass *actor_surface_class = ++ META_WAYLAND_ACTOR_SURFACE_CLASS (meta_wayland_surface_role_xwayland_parent_class); ++ ++ if (surface->window) ++ actor_surface_class->sync_actor_state (actor_surface); ++} ++ + static void + meta_wayland_surface_role_xwayland_init (MetaWaylandSurfaceRoleXWayland *role) + { +@@ -804,9 +818,13 @@ meta_wayland_surface_role_xwayland_class_init (MetaWaylandSurfaceRoleXWaylandCla + { + MetaWaylandSurfaceRoleClass *surface_role_class = + META_WAYLAND_SURFACE_ROLE_CLASS (klass); ++ MetaWaylandActorSurfaceClass *actor_surface_class = ++ META_WAYLAND_ACTOR_SURFACE_CLASS (klass); + + surface_role_class->get_toplevel = xwayland_surface_get_toplevel; + ++ actor_surface_class->sync_actor_state = xwayland_surface_sync_actor_state; ++ + xwayland_surface_signals[XWAYLAND_SURFACE_WINDOW_ASSOCIATED] = + g_signal_new ("window-associated", + G_TYPE_FROM_CLASS (klass), +-- +2.31.1 + diff --git a/SOURCES/0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch b/SOURCES/0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch new file mode 100644 index 0000000..df533eb --- /dev/null +++ b/SOURCES/0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch @@ -0,0 +1,35 @@ +From 24ddf60768412fd3f5f7b432449b9ed2ea0d18b3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 18 Feb 2020 23:01:28 +0100 +Subject: [PATCH] window-actor: Don't show actor until meta_window_actor_show() + +By default clutter will show an actor as it is added to a parent. This +means that after we create the window actor, when it's added to the +window group, we implicitly show it. What we really want is to not show +it until the window is supposed to be shown, which happens when +meta_window_actor_show() is called, as showing prior to that, could +cause issues. + +Avoid the implicit show by setting the "show-on-set-parent" property on +the window actor to `FALSE` on window actor construction. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1066 +--- + src/compositor/compositor.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c +index a6ae55abb9..ce2c1b8a3b 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -810,6 +810,7 @@ meta_compositor_add_window (MetaCompositor *compositor, + + window_actor = g_object_new (window_actor_type, + "meta-window", window, ++ "show-on-set-parent", FALSE, + NULL); + + if (window->layer == META_LAYER_OVERRIDE_REDIRECT) +-- +2.26.2 + diff --git a/SOURCES/0001-window-actor-Special-case-shaped-Java-windows.patch b/SOURCES/0001-window-actor-Special-case-shaped-Java-windows.patch new file mode 100644 index 0000000..3cf01de --- /dev/null +++ b/SOURCES/0001-window-actor-Special-case-shaped-Java-windows.patch @@ -0,0 +1,35 @@ +From 6bca5f001338d4647e4e21d549c8cdea4bcad669 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 12 May 2017 13:40:31 +0200 +Subject: [PATCH] window-actor: Special-case shaped Java windows + +OpenJDK wrongly assumes that shaping a window implies no shadows. +They got lucky until commit b975676c changed the fallback case, +but now their compliance tests are broken. Make them happy again +by special-casing shaped Java windows. +--- + src/compositor/meta-window-actor.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index f850cb222..1c8dc8fe5 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -798,6 +798,14 @@ meta_window_actor_has_shadow (MetaWindowActor *self) + if (priv->window->has_custom_frame_extents) + return FALSE; + ++ /* ++ * OpenJDK wrongly assumes that shaping a window implies no compositor ++ * shadows; make its compliance tests happy to give it what it wants ... ++ */ ++ if (g_strcmp0 (priv->window->res_name, "sun-awt-X11-XWindowPeer") == 0 && ++ priv->window->shape_region != NULL) ++ return FALSE; ++ + /* + * Generate shadows for all other windows. + */ +-- +2.21.0 + diff --git a/SOURCES/0001-workspace-Downgrade-assert-to-warning-when-adding-wi.patch b/SOURCES/0001-workspace-Downgrade-assert-to-warning-when-adding-wi.patch new file mode 100644 index 0000000..7df90fd --- /dev/null +++ b/SOURCES/0001-workspace-Downgrade-assert-to-warning-when-adding-wi.patch @@ -0,0 +1,33 @@ +From d33a244603d1dd63e2e25255af98f489c65645f6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 29 Aug 2022 16:01:48 +0200 +Subject: [PATCH] workspace: Downgrade assert to warning when adding window + +An extension can by accident cause us to end up in a state where we try +to add the same window to a workspace twice. When this happens we +shouldn't crash, but instead complain loudly. + +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/992 +Related: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/merge_requests/157 +Part-of: +--- + src/core/workspace.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/core/workspace.c b/src/core/workspace.c +index 58fcfa78c5..ed16a83098 100644 +--- a/src/core/workspace.c ++++ b/src/core/workspace.c +@@ -370,7 +370,8 @@ void + meta_workspace_add_window (MetaWorkspace *workspace, + MetaWindow *window) + { +- g_assert (g_list_find (workspace->mru_list, window) == NULL); ++ g_return_if_fail (g_list_find (workspace->mru_list, window) == NULL); ++ + workspace->mru_list = g_list_prepend (workspace->mru_list, window); + + workspace->windows = g_list_prepend (workspace->windows, window); +-- +2.37.1 + diff --git a/SOURCES/0001-workspace-Focus-only-ancestors-that-are-focusable.patch b/SOURCES/0001-workspace-Focus-only-ancestors-that-are-focusable.patch new file mode 100644 index 0000000..94a73d8 --- /dev/null +++ b/SOURCES/0001-workspace-Focus-only-ancestors-that-are-focusable.patch @@ -0,0 +1,83 @@ +From eca25ab6a12770a2a767458d9b0129d4fde3995c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Tue, 13 Nov 2018 08:31:52 +0100 +Subject: [PATCH 1/2] workspace: Focus only ancestors that are focusable + +When destroying a window that has a parent, we initially try to focus one of +its ancestors. However if no ancestor can be focused, then we should instead +focus the default focus window instead of trying to request focus for a window +that can't get focus anyways. + +Fixes https://gitlab.gnome.org/GNOME/mutter/issues/308 +(cherry picked from commit eccc791f3b3451216f957e67fec47a73b65ed2b2) +--- + src/core/workspace.c | 37 +++++++++++++++++++++++++++---------- + 1 file changed, 27 insertions(+), 10 deletions(-) + +diff --git a/src/core/workspace.c b/src/core/workspace.c +index f2b2c2c48..58fcfa78c 100644 +--- a/src/core/workspace.c ++++ b/src/core/workspace.c +@@ -85,6 +85,12 @@ typedef struct _MetaWorkspaceLogicalMonitorData + MetaRectangle logical_monitor_work_area; + } MetaWorkspaceLogicalMonitorData; + ++typedef struct _MetaWorkspaceFocusableAncestorData ++{ ++ MetaWorkspace *workspace; ++ MetaWindow *out_window; ++} MetaWorkspaceFocusableAncestorData; ++ + static MetaWorkspaceLogicalMonitorData * + meta_workspace_get_logical_monitor_data (MetaWorkspace *workspace, + MetaLogicalMonitor *logical_monitor) +@@ -1322,13 +1328,20 @@ meta_workspace_focus_default_window (MetaWorkspace *workspace, + } + + static gboolean +-record_ancestor (MetaWindow *window, +- void *data) ++find_focusable_ancestor (MetaWindow *window, ++ gpointer user_data) + { +- MetaWindow **result = data; ++ MetaWorkspaceFocusableAncestorData *data = user_data; ++ ++ if (!window->unmanaging && meta_window_is_focusable (window) && ++ meta_window_located_on_workspace (window, data->workspace) && ++ meta_window_showing_on_its_workspace (window)) ++ { ++ data->out_window = window; ++ return FALSE; ++ } + +- *result = window; +- return FALSE; /* quit with the first ancestor we find */ ++ return TRUE; + } + + /* Focus ancestor of not_this_one if there is one */ +@@ -1350,11 +1363,15 @@ focus_ancestor_or_top_window (MetaWorkspace *workspace, + if (not_this_one) + { + MetaWindow *ancestor; +- ancestor = NULL; +- meta_window_foreach_ancestor (not_this_one, record_ancestor, &ancestor); +- if (ancestor != NULL && +- meta_window_located_on_workspace (ancestor, workspace) && +- meta_window_showing_on_its_workspace (ancestor)) ++ MetaWorkspaceFocusableAncestorData data; ++ ++ data = (MetaWorkspaceFocusableAncestorData) { ++ .workspace = workspace, ++ }; ++ meta_window_foreach_ancestor (not_this_one, find_focusable_ancestor, &data); ++ ancestor = data.out_window; ++ ++ if (ancestor) + { + meta_topic (META_DEBUG_FOCUS, + "Focusing %s, ancestor of %s\n", +-- +2.21.0 + diff --git a/SOURCES/0001-workspace-manager-Expose-layout-properties.patch b/SOURCES/0001-workspace-manager-Expose-layout-properties.patch new file mode 100644 index 0000000..908fe67 --- /dev/null +++ b/SOURCES/0001-workspace-manager-Expose-layout-properties.patch @@ -0,0 +1,80 @@ +From 52536a44e96aa34d3ec3b9332adaa15a6399fc3e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 4 Jun 2019 21:21:37 +0200 +Subject: [PATCH] workspace-manager: Expose layout properties + +gnome-shell hardcodes a vertical one-column workspace layout, and +while not supporting arbitrary grids is very much by design, it +currently doesn't have a choice: We simply don't expose the workspace +layout we use. + +Change that to allow gnome-shell to be a bit more flexible with the +workspace layouts it supports. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/618 +--- + src/core/meta-workspace-manager.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/src/core/meta-workspace-manager.c b/src/core/meta-workspace-manager.c +index 8e1f03fe8..fbae34c73 100644 +--- a/src/core/meta-workspace-manager.c ++++ b/src/core/meta-workspace-manager.c +@@ -50,6 +50,9 @@ enum + { + PROP_0, + ++ PROP_LAYOUT_COLUMNS, ++ PROP_LAYOUT_ROWS, ++ + PROP_N_WORKSPACES + }; + +@@ -68,6 +71,12 @@ meta_workspace_manager_get_property (GObject *object, + + switch (prop_id) + { ++ case PROP_LAYOUT_COLUMNS: ++ g_value_set_int (value, workspace_manager->columns_of_workspaces); ++ break; ++ case PROP_LAYOUT_ROWS: ++ g_value_set_int (value, workspace_manager->rows_of_workspaces); ++ break; + case PROP_N_WORKSPACES: + g_value_set_int (value, meta_workspace_manager_get_n_workspaces (workspace_manager)); + break; +@@ -154,6 +163,22 @@ meta_workspace_manager_class_init (MetaWorkspaceManagerClass *klass) + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + ++ g_object_class_install_property (object_class, ++ PROP_LAYOUT_COLUMNS, ++ g_param_spec_int ("layout-columns", ++ "Layout columns", ++ "Number of columns in layout", ++ -1, G_MAXINT, 1, ++ G_PARAM_READABLE)); ++ ++ g_object_class_install_property (object_class, ++ PROP_LAYOUT_ROWS, ++ g_param_spec_int ("layout-rows", ++ "Layout rows", ++ "Number of rows in layout", ++ -1, G_MAXINT, -1, ++ G_PARAM_READABLE)); ++ + g_object_class_install_property (object_class, + PROP_N_WORKSPACES, + g_param_spec_int ("n-workspaces", +@@ -474,6 +499,8 @@ meta_workspace_manager_update_workspace_layout (MetaWorkspaceManager *workspace_ + workspace_manager->columns_of_workspaces, + workspace_manager->vertical_workspaces, + workspace_manager->starting_corner); ++ g_object_notify (G_OBJECT (workspace_manager), "layout-columns"); ++ g_object_notify (G_OBJECT (workspace_manager), "layout-rows"); + } + + /** +-- +2.21.0 + diff --git a/SOURCES/0001-x11-Check-wacom-button-flags-to-determine-whether-bu.patch b/SOURCES/0001-x11-Check-wacom-button-flags-to-determine-whether-bu.patch new file mode 100644 index 0000000..5303bbc --- /dev/null +++ b/SOURCES/0001-x11-Check-wacom-button-flags-to-determine-whether-bu.patch @@ -0,0 +1,53 @@ +From 57b3a2ea620f754cfd38f1ed4851dd8223efbcab Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Thu, 28 Nov 2019 22:50:36 +0100 +Subject: [PATCH] x11: Check wacom button flags to determine whether button is + mode switch + +Checking the leds is not really accurate, since some devices have mode +switch buttons without leds. Check in the button flags whether they are +mode switch buttons for any of ring/ring2/strip/strip2, and return the +appropriate group. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/952 +--- + .../clutter/x11/clutter-input-device-xi2.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/clutter/clutter/x11/clutter-input-device-xi2.c b/clutter/clutter/x11/clutter-input-device-xi2.c +index 1254aca3ae..4e5e2fd12c 100644 +--- a/clutter/clutter/x11/clutter-input-device-xi2.c ++++ b/clutter/clutter/x11/clutter-input-device-xi2.c +@@ -155,14 +155,25 @@ clutter_input_device_xi2_get_button_group (ClutterInputDevice *device, + + if (device_xi2->wacom_device) + { ++ WacomButtonFlags flags; ++ + if (button >= libwacom_get_num_buttons (device_xi2->wacom_device)) + return -1; + +- return libwacom_get_button_led_group (device_xi2->wacom_device, +- 'A' + button); ++ flags = libwacom_get_button_flag (device_xi2->wacom_device, ++ 'A' + button); ++ ++ if (flags & ++ (WACOM_BUTTON_RING_MODESWITCH | ++ WACOM_BUTTON_TOUCHSTRIP_MODESWITCH)) ++ return 0; ++ if (flags & ++ (WACOM_BUTTON_RING2_MODESWITCH | ++ WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH)) ++ return 1; + } +- else +- return -1; ++ ++ return -1; + } + #endif + +-- +2.24.0 + diff --git a/SOURCES/0001-xwayland-Don-t-spew-warnings-when-looking-for-X11-di.patch b/SOURCES/0001-xwayland-Don-t-spew-warnings-when-looking-for-X11-di.patch new file mode 100644 index 0000000..b1bcbbe --- /dev/null +++ b/SOURCES/0001-xwayland-Don-t-spew-warnings-when-looking-for-X11-di.patch @@ -0,0 +1,304 @@ +From d366b2bc4e89ed5807f0221afc25e66cb3d289ed Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 9 Dec 2020 11:23:37 +0100 +Subject: [PATCH 1/2] xwayland: Don't spew warnings when looking for X11 + displays + +It's not important, so only show it when doing MUTTER_DEBUG=wayland. +Instead report what display numbers were eventually found. +--- + src/wayland/meta-xwayland.c | 123 +++++++++++++++++++++++++++--------- + 1 file changed, 92 insertions(+), 31 deletions(-) + +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index 15c85df69..699d5561c 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -146,9 +146,10 @@ meta_xwayland_is_xwayland_surface (MetaWaylandSurface *surface) + } + + static gboolean +-try_display (int display, +- char **filename_out, +- int *fd_out) ++try_display (int display, ++ char **filename_out, ++ int *fd_out, ++ GError **error) + { + gboolean ret = FALSE; + char *filename; +@@ -164,11 +165,32 @@ try_display (int display, + char pid[11]; + char *end; + pid_t other; ++ int read_bytes; + + fd = open (filename, O_CLOEXEC, O_RDONLY); +- if (fd < 0 || read (fd, pid, 11) != 11) ++ if (fd < 0) + { +- g_warning ("can't read lock file %s: %m", filename); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to open lock file %s: %s", ++ filename, g_strerror (errno)); ++ goto out; ++ } ++ ++ read_bytes = read (fd, pid, 11); ++ if (read_bytes != 11) ++ { ++ if (read_bytes < 0) ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to read lock file %s: %s", ++ filename, g_strerror (errno)); ++ } ++ else ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to read lock file %s", ++ filename); ++ } + goto out; + } + close (fd); +@@ -178,7 +200,8 @@ try_display (int display, + other = strtol (pid, &end, 0); + if (end != pid + 10) + { +- g_warning ("can't parse lock file %s", filename); ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, ++ "Can't parse lock file %s", filename); + goto out; + } + +@@ -187,18 +210,23 @@ try_display (int display, + /* Process is dead. Try unlinking the lock file and trying again. */ + if (unlink (filename) < 0) + { +- g_warning ("failed to unlink stale lock file %s: %m", filename); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to unlink stale lock file %s: %m", filename); + goto out; + } + + goto again; + } + ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Lock file %s already occupied", filename); + goto out; + } + else if (fd < 0) + { +- g_warning ("failed to create lock file %s: %m", filename); ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to create lock file %s: %s", ++ filename, g_strerror (errno)); + goto out; + } + +@@ -223,24 +251,34 @@ try_display (int display, + } + + static char * +-create_lock_file (int display, int *display_out) ++create_lock_file (int display, ++ int *display_out, ++ GError **error) + { ++ g_autoptr (GError) local_error = NULL; + char *filename; + int fd; +- + char pid[12]; + int size; + int number_of_tries = 0; + +- while (!try_display (display, &filename, &fd)) ++ while (!try_display (display, &filename, &fd, &local_error)) + { ++ meta_verbose ("Failed to open display %d: %s\n", ++ display, local_error->message); ++ g_clear_error (&local_error); ++ + display++; + number_of_tries++; + + /* If we can't get a display after 50 times, then something's wrong. Just + * abort in this case. */ + if (number_of_tries >= 50) +- return NULL; ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Tried to bind 50 display numbers, giving up"); ++ return NULL; ++ } + } + + /* Subtle detail: we use the pid of the wayland compositor, not the xserver +@@ -248,11 +286,22 @@ create_lock_file (int display, int *display_out) + * it _would've_ written without either the NUL or the size clamping, hence + * the disparity in size. */ + size = snprintf (pid, 12, "%10d\n", getpid ()); ++ errno = 0; + if (size != 11 || write (fd, pid, 11) != 11) + { ++ if (errno != 0) ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to write pid to lock file: %s", ++ g_strerror (errno)); ++ } ++ else ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to write pid to lock file"); ++ } + unlink (filename); + close (fd); +- g_warning ("failed to write pid to lock file %s", filename); + g_free (filename); + return NULL; + } +@@ -264,8 +313,8 @@ create_lock_file (int display, int *display_out) + } + + static int +-bind_to_abstract_socket (int display, +- gboolean *fatal) ++bind_to_abstract_socket (int display, ++ GError **error) + { + struct sockaddr_un addr; + socklen_t size, name_size; +@@ -274,8 +323,8 @@ bind_to_abstract_socket (int display, + fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) + { +- *fatal = TRUE; +- g_warning ("Failed to create socket: %m"); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to create socket: %s", g_strerror (errno)); + return -1; + } + +@@ -285,17 +334,18 @@ bind_to_abstract_socket (int display, + size = offsetof (struct sockaddr_un, sun_path) + name_size; + if (bind (fd, (struct sockaddr *) &addr, size) < 0) + { +- *fatal = errno != EADDRINUSE; +- g_warning ("failed to bind to @%s: %m", addr.sun_path + 1); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to bind to @%s: %s", ++ addr.sun_path + 1, g_strerror (errno)); + close (fd); + return -1; + } + + if (listen (fd, 1) < 0) + { +- *fatal = errno != EADDRINUSE; +- g_warning ("Failed to listen on abstract socket @%s: %m", +- addr.sun_path + 1); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to listen on abstract socket @%s: %s", ++ addr.sun_path + 1, g_strerror (errno)); + close (fd); + return -1; + } +@@ -304,7 +354,8 @@ bind_to_abstract_socket (int display, + } + + static int +-bind_to_unix_socket (int display) ++bind_to_unix_socket (int display, ++ GError **error) + { + struct sockaddr_un addr; + socklen_t size, name_size; +@@ -321,13 +372,18 @@ bind_to_unix_socket (int display) + unlink (addr.sun_path); + if (bind (fd, (struct sockaddr *) &addr, size) < 0) + { +- g_warning ("failed to bind to %s: %m\n", addr.sun_path); ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to bind to %s: %s", ++ addr.sun_path, g_strerror (errno)); + close (fd); + return -1; + } + + if (listen (fd, 1) < 0) + { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to listen on %s: %s", ++ addr.sun_path, g_strerror (errno)); + unlink (addr.sun_path); + close (fd); + return -1; +@@ -385,7 +441,6 @@ choose_xdisplay (MetaXWaylandManager *manager) + { + int display = 0; + char *lock_file = NULL; +- gboolean fatal = FALSE; + + if (display_number_override != -1) + display = display_number_override; +@@ -394,33 +449,37 @@ choose_xdisplay (MetaXWaylandManager *manager) + + do + { +- lock_file = create_lock_file (display, &display); ++ g_autoptr (GError) error = NULL; ++ ++ lock_file = create_lock_file (display, &display, &error); + if (!lock_file) + { +- g_warning ("Failed to create an X lock file"); ++ g_warning ("Failed to create an X lock file: %s", error->message); + return FALSE; + } + +- manager->abstract_fd = bind_to_abstract_socket (display, &fatal); ++ manager->abstract_fd = bind_to_abstract_socket (display, &error); + if (manager->abstract_fd < 0) + { + unlink (lock_file); + +- if (!fatal) ++ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE)) + { ++ meta_verbose ("Failed to bind abstract socket: %s\n", error->message); + display++; + continue; + } + else + { +- g_warning ("Failed to bind abstract socket"); ++ g_warning ("Failed to bind abstract socket: %s", error->message); + return FALSE; + } + } + +- manager->unix_fd = bind_to_unix_socket (display); ++ manager->unix_fd = bind_to_unix_socket (display, &error); + if (manager->unix_fd < 0) + { ++ meta_verbose ("Failed to bind unix socket: %s\n", error->message); + unlink (lock_file); + close (manager->abstract_fd); + display++; +@@ -435,6 +494,8 @@ choose_xdisplay (MetaXWaylandManager *manager) + manager->display_name = g_strdup_printf (":%d", manager->display_index); + manager->lock_file = lock_file; + ++ g_message ("Using X11 display %s for Xwayland", manager->display_name); ++ + return TRUE; + } + +-- +2.29.2 + diff --git a/SOURCES/0002-clutter-avoid-g_signal_emit_by_name-from-ClutterActo.patch b/SOURCES/0002-clutter-avoid-g_signal_emit_by_name-from-ClutterActo.patch new file mode 100644 index 0000000..d281b8b --- /dev/null +++ b/SOURCES/0002-clutter-avoid-g_signal_emit_by_name-from-ClutterActo.patch @@ -0,0 +1,192 @@ +From 6881aa5ca235ee0737c2a409ffab966a10e5971e Mon Sep 17 00:00:00 2001 +From: Christian Hergert +Date: Mon, 24 Feb 2020 22:36:27 +0000 +Subject: [PATCH 2/3] clutter: avoid g_signal_emit_by_name() from ClutterActor + +g_signal_emit_by_name() is used to emit signals on ClutterContainer when +actors are removed or added. It happens to do various interface lookups +which are a bit unneccessary and can allocate memory. + +Simply using emission wrappers makes all of that go away. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1083 +--- + clutter/clutter/cally/cally-actor.c | 5 +-- + clutter/clutter/clutter-actor.c | 17 ++++++++-- + clutter/clutter/clutter-actor.h | 5 ++- + clutter/clutter/clutter-container-private.h | 36 +++++++++++++++++++++ + clutter/clutter/clutter-container.c | 21 ++++++++++++ + 5 files changed, 78 insertions(+), 6 deletions(-) + create mode 100644 clutter/clutter/clutter-container-private.h + +diff --git a/clutter/clutter/cally/cally-actor.c b/clutter/clutter/cally/cally-actor.c +index 548615f48..517969625 100644 +--- a/clutter/clutter/cally/cally-actor.c ++++ b/clutter/clutter/cally/cally-actor.c +@@ -604,10 +604,11 @@ cally_actor_real_remove_actor (ClutterActor *container, + g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0); + + atk_parent = ATK_OBJECT (data); +- atk_child = clutter_actor_get_accessible (actor); + +- if (atk_child) ++ if (clutter_actor_has_accessible (actor)) + { ++ atk_child = clutter_actor_get_accessible (actor); ++ + g_value_init (&values.old_value, G_TYPE_POINTER); + g_value_set_pointer (&values.old_value, atk_parent); + +diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c +index 803f76aae..93d0a93ef 100644 +--- a/clutter/clutter/clutter-actor.c ++++ b/clutter/clutter/clutter-actor.c +@@ -624,7 +624,7 @@ + #include "clutter-color-static.h" + #include "clutter-color.h" + #include "clutter-constraint-private.h" +-#include "clutter-container.h" ++#include "clutter-container-private.h" + #include "clutter-content-private.h" + #include "clutter-debug.h" + #include "clutter-easing.h" +@@ -4372,7 +4372,7 @@ clutter_actor_remove_child_internal (ClutterActor *self, + + /* we need to emit the signal before dropping the reference */ + if (emit_actor_removed) +- g_signal_emit_by_name (self, "actor-removed", child); ++ _clutter_container_emit_actor_removed (CLUTTER_CONTAINER (self), child); + + if (notify_first_last) + { +@@ -13060,7 +13060,7 @@ clutter_actor_add_child_internal (ClutterActor *self, + } + + if (emit_actor_added) +- g_signal_emit_by_name (self, "actor-added", child); ++ _clutter_container_emit_actor_added (CLUTTER_CONTAINER (self), child); + + if (notify_first_last) + { +@@ -21448,3 +21448,14 @@ clutter_actor_create_texture_paint_node (ClutterActor *self, + + return node; + } ++ ++gboolean ++clutter_actor_has_accessible (ClutterActor *actor) ++{ ++ g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE); ++ ++ if (CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible) ++ return CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible (actor); ++ ++ return TRUE; ++} +diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h +index 7d2168af1..d286f2843 100644 +--- a/clutter/clutter/clutter-actor.h ++++ b/clutter/clutter/clutter-actor.h +@@ -296,10 +296,11 @@ struct _ClutterActorClass + + gboolean (* touch_event) (ClutterActor *self, + ClutterTouchEvent *event); ++ gboolean (* has_accessible) (ClutterActor *self); + + /*< private >*/ + /* padding for future expansion */ +- gpointer _padding_dummy[26]; ++ gpointer _padding_dummy[25]; + }; + + /** +@@ -369,6 +370,8 @@ CLUTTER_EXPORT + const gchar * clutter_actor_get_name (ClutterActor *self); + CLUTTER_EXPORT + AtkObject * clutter_actor_get_accessible (ClutterActor *self); ++CLUTTER_EXPORT ++gboolean clutter_actor_has_accessible (ClutterActor *self); + + CLUTTER_EXPORT + gboolean clutter_actor_is_visible (ClutterActor *self); +diff --git a/clutter/clutter/clutter-container-private.h b/clutter/clutter/clutter-container-private.h +new file mode 100644 +index 000000000..d619a6531 +--- /dev/null ++++ b/clutter/clutter/clutter-container-private.h +@@ -0,0 +1,36 @@ ++/* ++ * Clutter. ++ * ++ * An OpenGL based 'interactive canvas' library. ++ * ++ * Copyright 2020 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library. If not, see . ++ */ ++ ++#ifndef __CLUTTER_CONTAINER_PRIVATE_H__ ++#define __CLUTTER_CONTAINER_PRIVATE_H__ ++ ++#include ++ ++G_BEGIN_DECLS ++ ++void _clutter_container_emit_actor_added (ClutterContainer *container, ++ ClutterActor *actor); ++void _clutter_container_emit_actor_removed (ClutterContainer *container, ++ ClutterActor *actor); ++ ++G_END_DECLS ++ ++#endif /* __CLUTTER_CONTAINER_PRIVATE_H__ */ +diff --git a/clutter/clutter/clutter-container.c b/clutter/clutter/clutter-container.c +index 0f611ae55..79abb5b4f 100644 +--- a/clutter/clutter/clutter-container.c ++++ b/clutter/clutter/clutter-container.c +@@ -37,6 +37,7 @@ + + #include "clutter-actor-private.h" + #include "clutter-child-meta.h" ++#include "clutter-container-private.h" + #include "clutter-debug.h" + #include "clutter-main.h" + #include "clutter-marshal.h" +@@ -1446,3 +1447,23 @@ clutter_container_child_notify (ClutterContainer *container, + child, + pspec); + } ++ ++void ++_clutter_container_emit_actor_added (ClutterContainer *container, ++ ClutterActor *actor) ++{ ++ g_return_if_fail (CLUTTER_IS_CONTAINER (container)); ++ g_return_if_fail (CLUTTER_IS_ACTOR (actor)); ++ ++ g_signal_emit (container, container_signals[ACTOR_ADDED], 0, actor); ++} ++ ++void ++_clutter_container_emit_actor_removed (ClutterContainer *container, ++ ClutterActor *actor) ++{ ++ g_return_if_fail (CLUTTER_IS_CONTAINER (container)); ++ g_return_if_fail (CLUTTER_IS_ACTOR (actor)); ++ ++ g_signal_emit (container, container_signals[ACTOR_REMOVED], 0, actor); ++} +-- +2.26.0 + diff --git a/SOURCES/0002-cogl-Fix-doc-for-_cogl_blit_framebuffer.patch b/SOURCES/0002-cogl-Fix-doc-for-_cogl_blit_framebuffer.patch new file mode 100644 index 0000000..510b926 --- /dev/null +++ b/SOURCES/0002-cogl-Fix-doc-for-_cogl_blit_framebuffer.patch @@ -0,0 +1,42 @@ +From 801da0dab1d2928578e9b191ee1684bcc7154081 Mon Sep 17 00:00:00 2001 +From: Pekka Paalanen +Date: Tue, 30 Apr 2019 17:01:04 +0300 +Subject: [PATCH 02/12] cogl: Fix doc for _cogl_blit_framebuffer + +Commit 38921701e533b7fda38a236cc45aec2ed3afef8a added explicit source and +destination parameters. Fix the documentation to match. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/615 + +(cherry picked from commit fc0ce11fcd997af12fc2253eeb37e03cebb5964f) +--- + cogl/cogl/cogl-framebuffer-private.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/cogl/cogl/cogl-framebuffer-private.h b/cogl/cogl/cogl-framebuffer-private.h +index 296788c2b..de886b64f 100644 +--- a/cogl/cogl/cogl-framebuffer-private.h ++++ b/cogl/cogl/cogl-framebuffer-private.h +@@ -4,6 +4,7 @@ + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009 Intel Corporation. ++ * Copyright (C) 2019 DisplayLink (UK) Ltd. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation +@@ -377,9 +378,8 @@ _cogl_push_framebuffers (CoglFramebuffer *draw_buffer, + * @width: Width of region to copy + * @height: Height of region to copy + * +- * This blits a region of the color buffer of the current draw buffer +- * to the current read buffer. The draw and read buffers can be set up +- * using _cogl_push_framebuffers(). This function should only be ++ * This blits a region of the color buffer of the source buffer ++ * to the destination buffer. This function should only be + * called if the COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT feature is + * advertised. The two buffers must both be offscreen and have the + * same format. +-- +2.21.0 + diff --git a/SOURCES/0002-cogl-gpu-info-Fix-software-acceleration-detection.patch b/SOURCES/0002-cogl-gpu-info-Fix-software-acceleration-detection.patch new file mode 100644 index 0000000..106ebaa --- /dev/null +++ b/SOURCES/0002-cogl-gpu-info-Fix-software-acceleration-detection.patch @@ -0,0 +1,27 @@ +From 03c30b76bae4c2e3f51a6689ebb7c0c60bd7b29a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 9 Feb 2021 18:00:26 +0100 +Subject: [PATCH 2/2] cogl/gpu-info: Fix software acceleration detection + +The string used to match mesa changed; update to fix software rendering +detection. +--- + cogl/cogl/cogl-gpu-info.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/cogl/cogl/cogl-gpu-info.c b/cogl/cogl/cogl-gpu-info.c +index f44319e96..c1817b3b0 100644 +--- a/cogl/cogl/cogl-gpu-info.c ++++ b/cogl/cogl/cogl-gpu-info.c +@@ -192,6 +192,8 @@ check_mesa_vendor (const CoglGpuInfoStrings *strings) + return TRUE; + else if (strcmp (strings->vendor_string, "Mesa Project") == 0) + return TRUE; ++ else if (strcmp (strings->vendor_string, "Mesa/X.org") == 0) ++ return TRUE; + + return FALSE; + } +-- +2.29.2 + diff --git a/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch b/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch new file mode 100644 index 0000000..5a1c1ff --- /dev/null +++ b/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch @@ -0,0 +1,28 @@ +From a192b9abd77aa14ae794850e41d210472f86b9b0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 25 Jun 2020 10:09:48 +0200 +Subject: [PATCH 2/2] gpu-kms: Reset CRTC, mode and output list if no resources + +On device removal, the next resource retrieval will fail; handle this by +just clearing the CRTC, mode and outputs. +--- + src/backends/native/meta-gpu-kms.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c +index 93e509def5..dc93abb7b1 100644 +--- a/src/backends/native/meta-gpu-kms.c ++++ b/src/backends/native/meta-gpu-kms.c +@@ -871,6 +871,9 @@ meta_gpu_kms_read_current (MetaGpu *gpu, + local_error->message); + gpu_kms->resources_init_failed_before = TRUE; + } ++ meta_gpu_take_outputs (gpu, NULL); ++ meta_gpu_take_modes (gpu, NULL); ++ meta_gpu_take_crtcs (gpu, NULL); + return TRUE; + } + +-- +2.26.2 + diff --git a/SOURCES/0002-monitor-manager-xrandr-Create-dummy-screen-sized-mon.patch b/SOURCES/0002-monitor-manager-xrandr-Create-dummy-screen-sized-mon.patch new file mode 100644 index 0000000..93756e4 --- /dev/null +++ b/SOURCES/0002-monitor-manager-xrandr-Create-dummy-screen-sized-mon.patch @@ -0,0 +1,185 @@ +From 85484d8f5d75764ab74308da7b21411c3fe4a2da Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 3 Oct 2018 10:50:47 +0200 +Subject: [PATCH 2/2] monitor-manager/xrandr: Create dummy screen sized monitor + if no RANDR + +When there is no RANDR support enabled in the X server, we wont get +notified of any monitors, resulting in mutter believing we're being +headless. To get at least something working, although with no way +configuration ability, lets pretend the whole screen is just a single +monitor with a single output, crtc and mode. +--- + src/backends/x11/meta-gpu-xrandr.c | 60 +++++++++++++++++++ + .../x11/meta-monitor-manager-xrandr.c | 22 ++++++- + .../x11/meta-monitor-manager-xrandr.h | 4 ++ + 3 files changed, 85 insertions(+), 1 deletion(-) + +diff --git a/src/backends/x11/meta-gpu-xrandr.c b/src/backends/x11/meta-gpu-xrandr.c +index 1884278ca..22e7e70e0 100644 +--- a/src/backends/x11/meta-gpu-xrandr.c ++++ b/src/backends/x11/meta-gpu-xrandr.c +@@ -115,6 +115,63 @@ update_screen_size (MetaGpuXrandr *gpu_xrandr) + monitor_manager->screen_height = HeightOfScreen (screen); + } + ++static gboolean ++read_current_fallback (MetaGpuXrandr *gpu_xrandr, ++ MetaMonitorManagerXrandr *monitor_manager_xrandr) ++{ ++ MetaGpu *gpu = META_GPU (gpu_xrandr); ++ MetaMonitorManager *monitor_manager = ++ META_MONITOR_MANAGER (monitor_manager_xrandr); ++ MetaCrtcMode *mode; ++ MetaCrtc *crtc; ++ MetaOutput *output; ++ ++ meta_monitor_manager_xrandr_update_dpms_state (monitor_manager_xrandr); ++ update_screen_size (gpu_xrandr); ++ ++ mode = g_object_new (META_TYPE_CRTC_MODE, NULL); ++ mode->mode_id = 0; ++ mode->width = monitor_manager->screen_width; ++ mode->height = monitor_manager->screen_height; ++ mode->refresh_rate = 60.0; ++ mode->name = g_strdup_printf ("%dx%d", mode->width, mode->height); ++ ++ meta_gpu_take_modes (gpu, g_list_prepend (NULL, mode)); ++ ++ crtc = g_object_new (META_TYPE_CRTC, NULL); ++ crtc->gpu = gpu; ++ crtc->crtc_id = 0; ++ crtc->rect = (MetaRectangle) { .width = mode->width, .height = mode->height }; ++ crtc->current_mode = mode; ++ ++ meta_gpu_take_crtcs (gpu, g_list_prepend (NULL, crtc)); ++ ++ output = g_object_new (META_TYPE_OUTPUT, NULL); ++ output->gpu = gpu; ++ output->winsys_id = 0; ++ output->name = g_strdup ("X11 Screen"); ++ output->vendor = g_strdup ("unknown"); ++ output->product = g_strdup ("unknown"); ++ output->serial = g_strdup ("unknown"); ++ output->hotplug_mode_update = TRUE; ++ output->suggested_x = -1; ++ output->suggested_y = -1; ++ output->connector_type = META_CONNECTOR_TYPE_Unknown; ++ output->modes = g_new0 (MetaCrtcMode *, 1); ++ output->modes[0] = mode; ++ output->n_modes = 1; ++ output->preferred_mode = mode; ++ output->possible_crtcs = g_new0 (MetaCrtc *, 1); ++ output->possible_crtcs[0] = crtc; ++ output->n_possible_crtcs = 1; ++ meta_output_assign_crtc (output, crtc); ++ output->is_primary = TRUE; ++ ++ meta_gpu_take_outputs (gpu, g_list_prepend (NULL, output)); ++ ++ return TRUE; ++} ++ + static gboolean + meta_gpu_xrandr_read_current (MetaGpu *gpu, + GError **error) +@@ -133,6 +190,9 @@ meta_gpu_xrandr_read_current (MetaGpu *gpu, + GList *modes = NULL; + GList *crtcs = NULL; + ++ if (!meta_monitor_manager_xrandr_has_randr (monitor_manager_xrandr)) ++ return read_current_fallback (gpu_xrandr, monitor_manager_xrandr); ++ + if (gpu_xrandr->resources) + XRRFreeScreenResources (gpu_xrandr->resources); + gpu_xrandr->resources = NULL; +diff --git a/src/backends/x11/meta-monitor-manager-xrandr.c b/src/backends/x11/meta-monitor-manager-xrandr.c +index 7a0b43ac4..d6306faeb 100644 +--- a/src/backends/x11/meta-monitor-manager-xrandr.c ++++ b/src/backends/x11/meta-monitor-manager-xrandr.c +@@ -75,6 +75,7 @@ struct _MetaMonitorManagerXrandr + guint logind_watch_id; + guint logind_signal_sub_id; + ++ gboolean has_randr; + gboolean has_randr15; + + /* +@@ -114,6 +115,12 @@ meta_monitor_manager_xrandr_get_xdisplay (MetaMonitorManagerXrandr *manager_xran + return manager_xrandr->xdisplay; + } + ++gboolean ++meta_monitor_manager_xrandr_has_randr (MetaMonitorManagerXrandr *manager_xrandr) ++{ ++ return manager_xrandr->has_randr; ++} ++ + gboolean + meta_monitor_manager_xrandr_has_randr15 (MetaMonitorManagerXrandr *manager_xrandr) + { +@@ -145,7 +152,7 @@ x11_dpms_state_to_power_save (CARD16 dpms_state) + } + } + +-static void ++void + meta_monitor_manager_xrandr_update_dpms_state (MetaMonitorManagerXrandr *manager_xrandr) + { + MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr); +@@ -637,9 +644,18 @@ meta_monitor_manager_xrandr_apply_monitors_config (MetaMonitorManager *mana + MetaMonitorsConfigMethod method, + GError **error) + { ++ MetaMonitorManagerXrandr *manager_xrandr = ++ META_MONITOR_MANAGER_XRANDR (manager); + GPtrArray *crtc_infos; + GPtrArray *output_infos; + ++ if (!manager_xrandr->has_randr) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Tried to change configuration without XRANDR support"); ++ return FALSE; ++ } ++ + if (!config) + { + meta_monitor_manager_xrandr_rebuild_derived (manager, NULL); +@@ -1105,11 +1121,15 @@ meta_monitor_manager_xrandr_constructed (GObject *object) + &manager_xrandr->rr_event_base, + &manager_xrandr->rr_error_base)) + { ++ g_warning ("No RANDR support, monitor configuration disabled"); + return; + } + else + { + int major_version, minor_version; ++ ++ manager_xrandr->has_randr = TRUE; ++ + /* We only use ScreenChangeNotify, but GDK uses the others, + and we don't want to step on its toes */ + XRRSelectInput (manager_xrandr->xdisplay, +diff --git a/src/backends/x11/meta-monitor-manager-xrandr.h b/src/backends/x11/meta-monitor-manager-xrandr.h +index d55b3d2b8..dc75134a5 100644 +--- a/src/backends/x11/meta-monitor-manager-xrandr.h ++++ b/src/backends/x11/meta-monitor-manager-xrandr.h +@@ -33,9 +33,13 @@ G_DECLARE_FINAL_TYPE (MetaMonitorManagerXrandr, meta_monitor_manager_xrandr, + + Display * meta_monitor_manager_xrandr_get_xdisplay (MetaMonitorManagerXrandr *manager_xrandr); + ++gboolean meta_monitor_manager_xrandr_has_randr (MetaMonitorManagerXrandr *manager_xrandr); ++ + gboolean meta_monitor_manager_xrandr_has_randr15 (MetaMonitorManagerXrandr *manager_xrandr); + + gboolean meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager, + XEvent *event); + ++void meta_monitor_manager_xrandr_update_dpms_state (MetaMonitorManagerXrandr *manager_xrandr); ++ + #endif /* META_MONITOR_MANAGER_XRANDR_H */ +-- +2.23.0 + diff --git a/SOURCES/0002-renderer-native-Add-API-to-get-primary-GPU.patch b/SOURCES/0002-renderer-native-Add-API-to-get-primary-GPU.patch new file mode 100644 index 0000000..bd9f592 --- /dev/null +++ b/SOURCES/0002-renderer-native-Add-API-to-get-primary-GPU.patch @@ -0,0 +1,46 @@ +From 63455680096e015eaf023760466593b6f42f9cf5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 12 Sep 2019 11:50:34 +0200 +Subject: [PATCH 2/4] renderer/native: Add API to get primary GPU + +Will be used when acquiring scanouts from Wayland buffers. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/798 +--- + src/backends/native/meta-renderer-native.c | 6 ++++++ + src/backends/native/meta-renderer-native.h | 2 ++ + 2 files changed, 8 insertions(+) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index dbb88edb8e..25833b6cf6 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -317,6 +317,12 @@ meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms) + return renderer_gpu_data->gbm.device; + } + ++MetaGpuKms * ++meta_renderer_native_get_primary_gpu (MetaRendererNative *renderer_native) ++{ ++ return renderer_native->primary_gpu_kms; ++} ++ + static MetaRendererNativeGpuData * + meta_create_renderer_native_gpu_data (MetaGpuKms *gpu_kms) + { +diff --git a/src/backends/native/meta-renderer-native.h b/src/backends/native/meta-renderer-native.h +index 9eecdead1c..b59366e267 100644 +--- a/src/backends/native/meta-renderer-native.h ++++ b/src/backends/native/meta-renderer-native.h +@@ -53,6 +53,8 @@ struct gbm_device * meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms); + + gboolean meta_renderer_native_supports_mirroring (MetaRendererNative *renderer_native); + ++MetaGpuKms * meta_renderer_native_get_primary_gpu (MetaRendererNative *renderer_native); ++ + void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native); + + int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native); +-- +2.26.2 + diff --git a/SOURCES/0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch b/SOURCES/0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch new file mode 100644 index 0000000..4e383f2 --- /dev/null +++ b/SOURCES/0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch @@ -0,0 +1,44 @@ +From 2e4d3e22aff7cc8e41fa08d798c55a39a542476c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 2 Jun 2020 18:34:57 +0200 +Subject: [PATCH 2/2] renderer-native: Don't leak DMA buffer CoglFramebuffer + +When we created the DMA buffer backed CoglFramebuffer, we handed it over +to CoglDmaBufHandle which took its own reference. What we failed to do +was to release our own reference to it, effectively leaking it. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1283 +(cherry picked from commit c823b5ddba18d30e1fdb74d6764cd40637dc4054) +--- + src/backends/native/meta-renderer-native.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index ba98de650..dbb88edb8 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -2633,6 +2633,7 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, + case META_RENDERER_NATIVE_MODE_GBM: + { + CoglFramebuffer *dmabuf_fb; ++ CoglDmaBufHandle *dmabuf_handle; + struct gbm_bo *new_bo; + int dmabuf_fd = -1; + +@@ -2669,8 +2670,11 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, + if (!dmabuf_fb) + return NULL; + +- return cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, new_bo, +- (GDestroyNotify) gbm_bo_destroy); ++ dmabuf_handle = ++ cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, new_bo, ++ (GDestroyNotify) gbm_bo_destroy); ++ cogl_object_unref (dmabuf_fb); ++ return dmabuf_handle; + } + break; + #ifdef HAVE_EGL_DEVICE +-- +2.26.2 + diff --git a/SOURCES/0002-wayland-dnd-surface-Propagate-commit-to-parent-class.patch b/SOURCES/0002-wayland-dnd-surface-Propagate-commit-to-parent-class.patch new file mode 100644 index 0000000..2192f91 --- /dev/null +++ b/SOURCES/0002-wayland-dnd-surface-Propagate-commit-to-parent-class.patch @@ -0,0 +1,38 @@ +From f37ef55525777f742051cb988341fa1bab403666 Mon Sep 17 00:00:00 2001 +From: Robert Mader +Date: Mon, 15 Apr 2019 02:02:10 +0200 +Subject: [PATCH 2/2] wayland/dnd-surface: Propagate commit to parent class + +We need to call the underlying actor-surface so the actor +state is synced, otherwise surface state like the scale factor +does not get applied. + +Fixes https://gitlab.gnome.org/GNOME/mutter/issues/550 + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/537 +(cherry picked from commit 01d0316fd703872a2470a351f906ffa4605a647e) +--- + src/wayland/meta-wayland-dnd-surface.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/wayland/meta-wayland-dnd-surface.c b/src/wayland/meta-wayland-dnd-surface.c +index 8ddeb2a7bd..7aa7e3be2f 100644 +--- a/src/wayland/meta-wayland-dnd-surface.c ++++ b/src/wayland/meta-wayland-dnd-surface.c +@@ -51,9 +51,13 @@ dnd_surface_commit (MetaWaylandSurfaceRole *surface_role, + { + MetaWaylandSurface *surface = + meta_wayland_surface_role_get_surface (surface_role); ++ MetaWaylandSurfaceRoleClass *surface_role_class = ++ META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_surface_role_dnd_parent_class); + + meta_wayland_compositor_add_frame_callback_surface (surface->compositor, + surface); ++ ++ surface_role_class->commit (surface_role, pending); + } + + static void +-- +2.31.1 + diff --git a/SOURCES/0002-window-Emit-an-error-and-return-when-trying-to-activ.patch b/SOURCES/0002-window-Emit-an-error-and-return-when-trying-to-activ.patch new file mode 100644 index 0000000..fedf5ce --- /dev/null +++ b/SOURCES/0002-window-Emit-an-error-and-return-when-trying-to-activ.patch @@ -0,0 +1,42 @@ +From 9a8bb8a205656ca1089444a041c99c5591477642 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 3 May 2019 18:10:47 +0000 +Subject: [PATCH 2/2] window: Emit an error and return when trying to activate + an unmanaged + +If something (i.e. gnome-shell or an extension) tries to activate an unmanaged +window, we should warn about this and avoid to perform further actions as this +could lead to a crash of mutter, since the window has not valid flags (like +workspace) set anymore at this stage. + +Fixes https://gitlab.gnome.org/GNOME/mutter/issues/580 + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/564 + + +(cherry picked from commit a6fc656e917fd48b8708b8d9f4bf9f8b15581313) +--- + src/core/window.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/core/window.c b/src/core/window.c +index d2c24506b..725cca7ce 100644 +--- a/src/core/window.c ++++ b/src/core/window.c +@@ -3683,6 +3683,13 @@ meta_window_activate_full (MetaWindow *window, + { + MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; + gboolean allow_workspace_switch; ++ ++ if (window->unmanaging) ++ { ++ g_warning ("Trying to activate unmanaged window '%s'", window->desc); ++ return; ++ } ++ + meta_topic (META_DEBUG_FOCUS, + "_NET_ACTIVE_WINDOW message sent for %s at time %u " + "by client type %u.\n", +-- +2.21.0 + diff --git a/SOURCES/0002-xwayland-Make-sure-tmp-.X11-unix-exists.patch b/SOURCES/0002-xwayland-Make-sure-tmp-.X11-unix-exists.patch new file mode 100644 index 0000000..15d829c --- /dev/null +++ b/SOURCES/0002-xwayland-Make-sure-tmp-.X11-unix-exists.patch @@ -0,0 +1,90 @@ +From 56c2e4efdcef14531dcf752e89117d22a21ec8ad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 9 Dec 2020 15:18:29 +0100 +Subject: [PATCH 2/2] xwayland: Make sure /tmp/.X11-unix/ exists + +When we're running under a polyinstantiated SELinux environment, we'll +likely start with an isolated and empty /tmp, meannig no /tmp/.X11-unix +directory to add things to. To make it possible to still function in +this kind of setup, make sure said directory exists. +--- + src/wayland/meta-xwayland.c | 30 ++++++++++++++++++++++++++++-- + 1 file changed, 28 insertions(+), 2 deletions(-) + +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index 699d5561c..f3df9766e 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + #include + + #include "compositor/meta-surface-actor-wayland.h" +@@ -436,9 +437,27 @@ meta_xwayland_override_display_number (int number) + display_number_override = number; + } + ++static gboolean ++ensure_x11_unix_dir (GError **error) ++{ ++ if (mkdir ("/tmp/.X11-unix", 01777) != 0) ++ { ++ if (errno == EEXIST) ++ return TRUE; ++ ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "Failed to create directory \"/tmp/.X11-unix\": %s", ++ g_strerror (errno)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + static gboolean + choose_xdisplay (MetaXWaylandManager *manager) + { ++ g_autoptr (GError) error = NULL; + int display = 0; + char *lock_file = NULL; + +@@ -447,10 +466,15 @@ choose_xdisplay (MetaXWaylandManager *manager) + else if (g_getenv ("RUNNING_UNDER_GDM")) + display = 1024; + +- do ++ if (!ensure_x11_unix_dir (&error)) + { +- g_autoptr (GError) error = NULL; ++ g_warning ("Failed to ensure X11 socket directory: %s", ++ error->message); ++ return FALSE; ++ } + ++ do ++ { + lock_file = create_lock_file (display, &display, &error); + if (!lock_file) + { +@@ -466,6 +490,7 @@ choose_xdisplay (MetaXWaylandManager *manager) + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE)) + { + meta_verbose ("Failed to bind abstract socket: %s\n", error->message); ++ g_clear_error (&error); + display++; + continue; + } +@@ -480,6 +505,7 @@ choose_xdisplay (MetaXWaylandManager *manager) + if (manager->unix_fd < 0) + { + meta_verbose ("Failed to bind unix socket: %s\n", error->message); ++ g_clear_error (&error); + unlink (lock_file); + close (manager->abstract_fd); + display++; +-- +2.29.2 + diff --git a/SOURCES/0003-clutter-fix-hole-in-ClutterPaintNode.patch b/SOURCES/0003-clutter-fix-hole-in-ClutterPaintNode.patch new file mode 100644 index 0000000..557f1ff --- /dev/null +++ b/SOURCES/0003-clutter-fix-hole-in-ClutterPaintNode.patch @@ -0,0 +1,167 @@ +From df565fcb681a50aac5046981c5aba04073d14856 Mon Sep 17 00:00:00 2001 +From: Christian Hergert +Date: Fri, 21 Feb 2020 22:36:31 +0000 +Subject: [PATCH 3/3] clutter: fix hole in ClutterPaintNode + +Fixing the missalignment takes the structure from 80 bytes down to 72. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1081 +--- + clutter/clutter/clutter-actor.c | 8 +++---- + clutter/clutter/clutter-canvas.c | 2 +- + clutter/clutter/clutter-image.c | 2 +- + clutter/clutter/clutter-paint-node-private.h | 6 ++--- + clutter/clutter/clutter-paint-node.c | 23 +++++++++++++++----- + clutter/clutter/clutter-paint-node.h | 3 +++ + 6 files changed, 30 insertions(+), 14 deletions(-) + +diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c +index 93d0a93ef..ff5c4a69d 100644 +--- a/clutter/clutter/clutter-actor.c ++++ b/clutter/clutter/clutter-actor.c +@@ -3758,7 +3758,7 @@ clutter_actor_paint_node (ClutterActor *actor, + clear_flags |= COGL_BUFFER_BIT_COLOR; + + node = clutter_root_node_new (fb, &bg_color, clear_flags); +- clutter_paint_node_set_name (node, "stageClear"); ++ clutter_paint_node_set_static_name (node, "stageClear"); + clutter_paint_node_add_rectangle (node, &box); + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); +@@ -3773,7 +3773,7 @@ clutter_actor_paint_node (ClutterActor *actor, + / 255; + + node = clutter_color_node_new (&bg_color); +- clutter_paint_node_set_name (node, "backgroundColor"); ++ clutter_paint_node_set_static_name (node, "backgroundColor"); + clutter_paint_node_add_rectangle (node, &box); + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); +@@ -4069,7 +4069,7 @@ clutter_actor_continue_paint (ClutterActor *self) + * virtual function can then be called directly. + */ + dummy = _clutter_dummy_node_new (self); +- clutter_paint_node_set_name (dummy, "Root"); ++ clutter_paint_node_set_static_name (dummy, "Root"); + + /* XXX - for 1.12, we use the return value of paint_node() to + * decide whether we should emit the ::paint signal. +@@ -21427,7 +21427,7 @@ clutter_actor_create_texture_paint_node (ClutterActor *self, + color.alpha = clutter_actor_get_paint_opacity_internal (self); + + node = clutter_texture_node_new (texture, &color, priv->min_filter, priv->mag_filter); +- clutter_paint_node_set_name (node, "Texture"); ++ clutter_paint_node_set_static_name (node, "Texture"); + + if (priv->content_repeat == CLUTTER_REPEAT_NONE) + clutter_paint_node_add_rectangle (node, &box); +diff --git a/clutter/clutter/clutter-canvas.c b/clutter/clutter/clutter-canvas.c +index b0f1f080c..89c031be2 100644 +--- a/clutter/clutter/clutter-canvas.c ++++ b/clutter/clutter/clutter-canvas.c +@@ -351,7 +351,7 @@ clutter_canvas_paint_content (ClutterContent *content, + return; + + node = clutter_actor_create_texture_paint_node (actor, priv->texture); +- clutter_paint_node_set_name (node, "Canvas Content"); ++ clutter_paint_node_set_static_name (node, "Canvas Content"); + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); + +diff --git a/clutter/clutter/clutter-image.c b/clutter/clutter/clutter-image.c +index 266c68799..790e09521 100644 +--- a/clutter/clutter/clutter-image.c ++++ b/clutter/clutter/clutter-image.c +@@ -129,7 +129,7 @@ clutter_image_paint_content (ClutterContent *content, + return; + + node = clutter_actor_create_texture_paint_node (actor, priv->texture); +- clutter_paint_node_set_name (node, "Image Content"); ++ clutter_paint_node_set_static_name (node, "Image Content"); + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); + } +diff --git a/clutter/clutter/clutter-paint-node-private.h b/clutter/clutter/clutter-paint-node-private.h +index d61b89951..720df1458 100644 +--- a/clutter/clutter/clutter-paint-node-private.h ++++ b/clutter/clutter/clutter-paint-node-private.h +@@ -48,11 +48,11 @@ struct _ClutterPaintNode + ClutterPaintNode *next_sibling; + ClutterPaintNode *last_child; + +- guint n_children; +- + GArray *operations; + +- gchar *name; ++ const gchar *name; ++ ++ guint n_children; + + volatile int ref_count; + }; +diff --git a/clutter/clutter/clutter-paint-node.c b/clutter/clutter/clutter-paint-node.c +index 73765a4e9..1f9451a43 100644 +--- a/clutter/clutter/clutter-paint-node.c ++++ b/clutter/clutter/clutter-paint-node.c +@@ -171,8 +171,6 @@ clutter_paint_node_real_finalize (ClutterPaintNode *node) + { + ClutterPaintNode *iter; + +- g_free (node->name); +- + if (node->operations != NULL) + { + guint i; +@@ -294,7 +292,8 @@ clutter_paint_node_get_type (void) + * + * The @name will be used for debugging purposes. + * +- * The @node will copy the passed string. ++ * The @node will intern @name using g_intern_string(). If you have access to a ++ * static string, use clutter_paint_node_set_static_name() instead. + * + * Since: 1.10 + */ +@@ -304,8 +303,22 @@ clutter_paint_node_set_name (ClutterPaintNode *node, + { + g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); + +- g_free (node->name); +- node->name = g_strdup (name); ++ node->name = g_intern_string (name); ++} ++ ++/** ++ * clutter_paint_node_set_static_name: (skip) ++ * ++ * Like clutter_paint_node_set_name() but uses a static or interned string ++ * containing the name. ++ */ ++void ++clutter_paint_node_set_static_name (ClutterPaintNode *node, ++ const char *name) ++{ ++ g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); ++ ++ node->name = name; + } + + /** +diff --git a/clutter/clutter/clutter-paint-node.h b/clutter/clutter/clutter-paint-node.h +index c42abbc3d..7d25f1681 100644 +--- a/clutter/clutter/clutter-paint-node.h ++++ b/clutter/clutter/clutter-paint-node.h +@@ -55,6 +55,9 @@ void clutter_paint_node_paint (Clutter + CLUTTER_EXPORT + void clutter_paint_node_set_name (ClutterPaintNode *node, + const char *name); ++CLUTTER_EXPORT ++void clutter_paint_node_set_static_name (ClutterPaintNode *node, ++ const char *name); + + CLUTTER_EXPORT + void clutter_paint_node_add_child (ClutterPaintNode *node, +-- +2.26.0 + diff --git a/SOURCES/0003-cogl-Replace-ANGLE-with-GLES3-and-NV-framebuffer_bli.patch b/SOURCES/0003-cogl-Replace-ANGLE-with-GLES3-and-NV-framebuffer_bli.patch new file mode 100644 index 0000000..b41fa93 --- /dev/null +++ b/SOURCES/0003-cogl-Replace-ANGLE-with-GLES3-and-NV-framebuffer_bli.patch @@ -0,0 +1,77 @@ +From 04d921c2c1da571c8c61a4ca12a380bc3b9623fe Mon Sep 17 00:00:00 2001 +From: Pekka Paalanen +Date: Mon, 6 May 2019 13:40:31 +0300 +Subject: [PATCH 03/12] cogl: Replace ANGLE with GLES3 and NV framebuffer_blit + +ANGLE extensions are only provided by Google's Almost Native Graphics Layer +Engine (ANGLE) implementation. Therefore they do not seem too useful for +Mutter. + +The reason to drop GL_ANGLE_framebuffer_blit support is that it has more +limitations compared to the glBlitFramebuffer in GL_EXT_framebuffer_blit, +GL_NV_framebuffer_bit, OpenGL 3.0 and OpenGL ES 3.0. Most importantly, the +ANGLE version cannot flip the image while copying, which limits +_cogl_blit_framebuffer to only off-screen <-> off-screen copies. Follow-up work +will need off-screen <-> on-screen copies. + +Instead of adding yet more capability flags to Cogl, dropping ANGLE support +seems appropriate. + +The NV extension is added to the list of glBlitFramebuffer providers because it +provides the same support as ANGLE and more. + +Likewise OpenGL ES 3.0 is added to the list of glBlitFramebuffer providers +because e.g. Mesa GLES implementation usually provides it and that makes it +widely available, again surpassing the ANGLE supported features. + +Follow-up patches will lift some of the Cogl assumptions of what +glBlitFramebuffer cannot do. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/615 + +(cherry picked from commit 3e68c9e8faa78298039fa3583898f18550740812) +--- + cogl/cogl/cogl-framebuffer-private.h | 3 +-- + cogl/cogl/gl-prototypes/cogl-all-functions.h | 5 +++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/cogl/cogl/cogl-framebuffer-private.h b/cogl/cogl/cogl-framebuffer-private.h +index de886b64f..3aab852c4 100644 +--- a/cogl/cogl/cogl-framebuffer-private.h ++++ b/cogl/cogl/cogl-framebuffer-private.h +@@ -387,8 +387,7 @@ _cogl_push_framebuffers (CoglFramebuffer *draw_buffer, + * Note that this function differs a lot from the glBlitFramebuffer + * function provided by the GL_EXT_framebuffer_blit extension. Notably + * it doesn't support having different sizes for the source and +- * destination rectangle. This isn't supported by the corresponding +- * GL_ANGLE_framebuffer_blit extension on GLES2.0 and it doesn't seem ++ * destination rectangle. This doesn't seem + * like a particularly useful feature. If the application wanted to + * scale the results it may make more sense to draw a primitive + * instead. +diff --git a/cogl/cogl/gl-prototypes/cogl-all-functions.h b/cogl/cogl/gl-prototypes/cogl-all-functions.h +index 924ee349d..0af126059 100644 +--- a/cogl/cogl/gl-prototypes/cogl-all-functions.h ++++ b/cogl/cogl/gl-prototypes/cogl-all-functions.h +@@ -4,6 +4,7 @@ + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2009, 2011 Intel Corporation. ++ * Copyright (C) 2019 DisplayLink (UK) Ltd. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation +@@ -132,8 +133,8 @@ COGL_EXT_END () + + + COGL_EXT_BEGIN (offscreen_blit, 3, 0, +- 0, /* not in either GLES */ +- "EXT\0ANGLE\0", ++ COGL_EXT_IN_GLES3, ++ "EXT\0NV\0", + "framebuffer_blit\0") + COGL_EXT_FUNCTION (void, glBlitFramebuffer, + (GLint srcX0, +-- +2.21.0 + diff --git a/SOURCES/0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch b/SOURCES/0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch new file mode 100644 index 0000000..8ef52a5 --- /dev/null +++ b/SOURCES/0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch @@ -0,0 +1,108 @@ +From 914fd2bec65c2e9928b03d5bc94930bc0151f998 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 17 Jun 2020 17:48:05 +0200 +Subject: [PATCH 3/4] screen-cast: Move DMA buffer allocation to MetaScreenCast + +The aim with centralizing it is to be able to apply global policy to DMA +buffer allocations, e.g. disabling due to various hueristics. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1318 +--- + src/backends/meta-screen-cast-stream-src.c | 18 ++++++-------- + src/backends/meta-screen-cast.c | 28 ++++++++++++++++++++++ + src/backends/meta-screen-cast.h | 4 ++++ + 3 files changed, 39 insertions(+), 11 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index ff4af440c1..b77186415f 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -649,10 +649,10 @@ on_stream_add_buffer (void *data, + MetaScreenCastStreamSrc *src = data; + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); +- CoglContext *context = +- clutter_backend_get_cogl_context (clutter_get_default_backend ()); +- CoglRenderer *renderer = cogl_context_get_renderer (context); +- g_autoptr (GError) error = NULL; ++ MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src); ++ MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream); ++ MetaScreenCast *screen_cast = ++ meta_screen_cast_session_get_screen_cast (session); + CoglDmaBufHandle *dmabuf_handle; + struct spa_buffer *spa_buffer = buffer->buffer; + struct spa_data *spa_data = spa_buffer->datas; +@@ -664,13 +664,9 @@ on_stream_add_buffer (void *data, + spa_data[0].mapoffset = 0; + spa_data[0].maxsize = stride * priv->video_format.size.height; + +- dmabuf_handle = cogl_renderer_create_dma_buf (renderer, +- priv->stream_width, +- priv->stream_height, +- &error); +- +- if (error) +- g_debug ("Error exporting DMA buffer handle: %s", error->message); ++ dmabuf_handle = meta_screen_cast_create_dma_buf_handle (screen_cast, ++ priv->stream_width, ++ priv->stream_height); + + if (dmabuf_handle) + { +diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c +index 46bc268389..5f1ca8b5ca 100644 +--- a/src/backends/meta-screen-cast.c ++++ b/src/backends/meta-screen-cast.c +@@ -94,6 +94,34 @@ meta_screen_cast_get_backend (MetaScreenCast *screen_cast) + return screen_cast->backend; + } + ++CoglDmaBufHandle * ++meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast, ++ int width, ++ int height) ++{ ++ ClutterBackend *clutter_backend = ++ meta_backend_get_clutter_backend (screen_cast->backend); ++ CoglContext *cogl_context = ++ clutter_backend_get_cogl_context (clutter_backend); ++ CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context); ++ g_autoptr (GError) error = NULL; ++ CoglDmaBufHandle *dmabuf_handle; ++ ++ dmabuf_handle = cogl_renderer_create_dma_buf (cogl_renderer, ++ width, height, ++ &error); ++ if (!dmabuf_handle) ++ { ++ g_warning ("Failed to allocate DMA buffer, " ++ "disabling DMA buffer based screen casting: %s", ++ error->message); ++ screen_cast->disable_dma_bufs = TRUE; ++ return NULL; ++ } ++ ++ return dmabuf_handle; ++} ++ + static gboolean + register_remote_desktop_screen_cast_session (MetaScreenCastSession *session, + const char *remote_desktop_session_id, +diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h +index a3b650cd80..fb5a38f34f 100644 +--- a/src/backends/meta-screen-cast.h ++++ b/src/backends/meta-screen-cast.h +@@ -50,6 +50,10 @@ GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast); + + MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast); + ++CoglDmaBufHandle * meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast, ++ int width, ++ int height); ++ + MetaScreenCast * meta_screen_cast_new (MetaBackend *backend, + MetaDbusSessionWatcher *session_watcher); + +-- +2.26.2 + diff --git a/SOURCES/0004-cogl-Relax-formats-on-glBlitFramebuffer.patch b/SOURCES/0004-cogl-Relax-formats-on-glBlitFramebuffer.patch new file mode 100644 index 0000000..751e0cb --- /dev/null +++ b/SOURCES/0004-cogl-Relax-formats-on-glBlitFramebuffer.patch @@ -0,0 +1,100 @@ +From 6c6c6ad5412f5bb13592630d7cb3b7aed25d159b Mon Sep 17 00:00:00 2001 +From: Pekka Paalanen +Date: Mon, 6 May 2019 14:09:16 +0300 +Subject: [PATCH 04/12] cogl: Relax formats on glBlitFramebuffer + +Depends on: "cogl: Replace ANGLE with GLES3 and NV framebuffer_blit" + +As a possible ANGLE implementation is not longer limiting the pixel format +matching, lift the requirement of having the same pixel format. + +We still cannot do a premult <-> non-premult conversion during a blit, so guard +against that. + +This will be useful in follow-up work to copy from onscreen primary GPU +framebuffer to an offscreen secondary GPU framebuffer if the formats do not +match exactly. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/615 + +(cherry picked from commit 6df34eb4b7c65210f4066f7eb9bd462278b7279b) +--- + cogl/cogl/cogl-blit.c | 10 ++++++---- + cogl/cogl/cogl-framebuffer-private.h | 8 ++++++-- + cogl/cogl/cogl-framebuffer.c | 6 ++++-- + 3 files changed, 16 insertions(+), 8 deletions(-) + +diff --git a/cogl/cogl/cogl-blit.c b/cogl/cogl/cogl-blit.c +index 74f404f3d..a61eb66d2 100644 +--- a/cogl/cogl/cogl-blit.c ++++ b/cogl/cogl/cogl-blit.c +@@ -4,6 +4,7 @@ + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2011 Intel Corporation. ++ * Copyright (C) 2019 DisplayLink (UK) Ltd. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation +@@ -152,10 +153,11 @@ _cogl_blit_framebuffer_begin (CoglBlitData *data) + CoglFramebuffer *dst_fb, *src_fb; + CoglError *ignore_error = NULL; + +- /* We can only blit between FBOs if both textures are the same +- format and the blit framebuffer extension is supported */ +- if ((_cogl_texture_get_format (data->src_tex) & ~COGL_A_BIT) != +- (_cogl_texture_get_format (data->dst_tex) & ~COGL_A_BIT) || ++ /* We can only blit between FBOs if both textures have the same ++ premult convention and the blit framebuffer extension is ++ supported. */ ++ if ((_cogl_texture_get_format (data->src_tex) & COGL_PREMULT_BIT) != ++ (_cogl_texture_get_format (data->dst_tex) & COGL_PREMULT_BIT) || + !_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT)) + return FALSE; + +diff --git a/cogl/cogl/cogl-framebuffer-private.h b/cogl/cogl/cogl-framebuffer-private.h +index 3aab852c4..b06fbaee1 100644 +--- a/cogl/cogl/cogl-framebuffer-private.h ++++ b/cogl/cogl/cogl-framebuffer-private.h +@@ -381,8 +381,12 @@ _cogl_push_framebuffers (CoglFramebuffer *draw_buffer, + * This blits a region of the color buffer of the source buffer + * to the destination buffer. This function should only be + * called if the COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT feature is +- * advertised. The two buffers must both be offscreen and have the +- * same format. ++ * advertised. The two buffers must both be offscreen. ++ * ++ * The two buffers must have the same value types (e.g. floating-point, ++ * unsigned int, signed int, or fixed-point), but color formats do not ++ * need to match. This limitation comes from OpenGL ES 3.0 definition ++ * of glBlitFramebuffer. + * + * Note that this function differs a lot from the glBlitFramebuffer + * function provided by the GL_EXT_framebuffer_blit extension. Notably +diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c +index bd8a7fa42..0bc225945 100644 +--- a/cogl/cogl/cogl-framebuffer.c ++++ b/cogl/cogl/cogl-framebuffer.c +@@ -4,6 +4,7 @@ + * A Low Level GPU Graphics and Utilities API + * + * Copyright (C) 2007,2008,2009,2012 Intel Corporation. ++ * Copyright (C) 2019 DisplayLink (UK) Ltd. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation +@@ -1468,8 +1469,9 @@ _cogl_blit_framebuffer (CoglFramebuffer *src, + support this */ + _COGL_RETURN_IF_FAIL (cogl_is_offscreen (src)); + _COGL_RETURN_IF_FAIL (cogl_is_offscreen (dest)); +- /* The buffers must be the same format */ +- _COGL_RETURN_IF_FAIL (src->internal_format == dest->internal_format); ++ /* The buffers must use the same premult convention */ ++ _COGL_RETURN_IF_FAIL ((src->internal_format & COGL_PREMULT_BIT) == ++ (dest->internal_format & COGL_PREMULT_BIT)); + + /* Make sure the current framebuffers are bound. We explicitly avoid + flushing the clip state so we can bind our own empty state */ +-- +2.21.0 + diff --git a/SOURCES/0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch b/SOURCES/0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch new file mode 100644 index 0000000..6963888 --- /dev/null +++ b/SOURCES/0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch @@ -0,0 +1,209 @@ +From a239886e159e6609c3e298effbd0243af8d0e333 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 16 Jun 2020 11:30:11 +0200 +Subject: [PATCH 4/4] screen-cast: Disable DMA buffer based screen casting for + QXL + +QXL doesn't support mmap():ing a DMA buffer allocated in mutter inside +the PipeWire stream consumer process. To make screen casting work again +on QXL, disable DMA buffer based screen casting for QXL. + +Eventually, it should be the client that renegotiates the supported +buffer types, but until then we need this list. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1318 +--- + src/backends/meta-screen-cast.c | 11 ++++++ + src/backends/meta-screen-cast.h | 2 ++ + src/backends/native/meta-backend-native.c | 41 +++++++++++++++++++++++ + src/backends/native/meta-gpu-kms.c | 26 ++++++++++++++ + src/backends/native/meta-gpu-kms.h | 2 ++ + 5 files changed, 82 insertions(+) + +diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c +index 5f1ca8b5ca..268155e7b3 100644 +--- a/src/backends/meta-screen-cast.c ++++ b/src/backends/meta-screen-cast.c +@@ -46,6 +46,8 @@ struct _MetaScreenCast + + MetaDbusSessionWatcher *session_watcher; + MetaBackend *backend; ++ ++ gboolean disable_dma_bufs; + }; + + static void +@@ -94,6 +96,12 @@ meta_screen_cast_get_backend (MetaScreenCast *screen_cast) + return screen_cast->backend; + } + ++void ++meta_screen_cast_disable_dma_bufs (MetaScreenCast *screen_cast) ++{ ++ screen_cast->disable_dma_bufs = TRUE; ++} ++ + CoglDmaBufHandle * + meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast, + int width, +@@ -107,6 +115,9 @@ meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast, + g_autoptr (GError) error = NULL; + CoglDmaBufHandle *dmabuf_handle; + ++ if (screen_cast->disable_dma_bufs) ++ return NULL; ++ + dmabuf_handle = cogl_renderer_create_dma_buf (cogl_renderer, + width, height, + &error); +diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h +index fb5a38f34f..fa54be729f 100644 +--- a/src/backends/meta-screen-cast.h ++++ b/src/backends/meta-screen-cast.h +@@ -50,6 +50,8 @@ GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast); + + MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast); + ++void meta_screen_cast_disable_dma_bufs (MetaScreenCast *screen_cast); ++ + CoglDmaBufHandle * meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast, + int width, + int height); +diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c +index c473681cb0..2bf7f5e7e2 100644 +--- a/src/backends/native/meta-backend-native.c ++++ b/src/backends/native/meta-backend-native.c +@@ -57,6 +57,10 @@ + #include "core/meta-border.h" + #include "meta/main.h" + ++#ifdef HAVE_REMOTE_DESKTOP ++#include "backends/meta-screen-cast.h" ++#endif ++ + struct _MetaBackendNative + { + MetaBackend parent; +@@ -327,6 +331,39 @@ meta_backend_native_create_clutter_backend (MetaBackend *backend) + return g_object_new (META_TYPE_CLUTTER_BACKEND_NATIVE, NULL); + } + ++#ifdef HAVE_REMOTE_DESKTOP ++static void ++maybe_disable_screen_cast_dma_bufs (MetaBackendNative *native) ++{ ++ MetaBackend *backend = META_BACKEND (native); ++ MetaRenderer *renderer = meta_backend_get_renderer (backend); ++ MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); ++ MetaGpuKms *primary_gpu; ++ const char *driver_name; ++ int i; ++ static const char *disable_dma_buf_drivers[] = { ++ "qxl", ++ }; ++ ++ primary_gpu = meta_renderer_native_get_primary_gpu (renderer_native); ++ driver_name = meta_gpu_kms_get_driver_name (primary_gpu); ++ ++ for (i = 0; i < G_N_ELEMENTS (disable_dma_buf_drivers); i++) ++ { ++ if (g_strcmp0 (driver_name, disable_dma_buf_drivers[i]) == 0) ++ { ++ MetaScreenCast *screen_cast = meta_backend_get_screen_cast (backend); ++ ++ g_message ("The '%s' driver doesn't support DMA buffer screen sharing, disabling.", ++ driver_name); ++ ++ meta_screen_cast_disable_dma_bufs (screen_cast); ++ return; ++ } ++ } ++} ++#endif /* HAVE_REMOTE_DESKTOP */ ++ + static void + meta_backend_native_post_init (MetaBackend *backend) + { +@@ -338,6 +375,10 @@ meta_backend_native_post_init (MetaBackend *backend) + NULL, NULL); + clutter_evdev_set_relative_motion_filter (manager, relative_motion_filter, + meta_backend_get_monitor_manager (backend)); ++ ++#ifdef HAVE_REMOTE_DESKTOP ++ maybe_disable_screen_cast_dma_bufs (META_BACKEND_NATIVE (backend)); ++#endif + } + + static MetaMonitorManager * +diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c +index c569b948ef..93e509def5 100644 +--- a/src/backends/native/meta-gpu-kms.c ++++ b/src/backends/native/meta-gpu-kms.c +@@ -66,6 +66,8 @@ struct _MetaGpuKms + char *file_path; + GSource *source; + ++ char *driver_name; ++ + clockid_t clock_id; + + drmModeConnector **connectors; +@@ -790,6 +792,27 @@ init_outputs (MetaGpuKms *gpu_kms, + setup_output_clones (gpu); + } + ++static void ++init_info (MetaGpuKms *gpu_kms) ++{ ++ drmVersion *drm_version; ++ ++ drm_version = drmGetVersion (gpu_kms->fd); ++ if (!drm_version) ++ return; ++ ++ gpu_kms->driver_name = g_strndup (drm_version->name, ++ drm_version->name_len); ++ ++ drmFreeVersion (drm_version); ++} ++ ++const char * ++meta_gpu_kms_get_driver_name (MetaGpuKms *gpu_kms) ++{ ++ return gpu_kms->driver_name; ++} ++ + static gboolean + meta_kms_resources_init (MetaKmsResources *resources, + int fd, +@@ -865,6 +888,7 @@ meta_gpu_kms_read_current (MetaGpu *gpu, + init_crtcs (gpu_kms, &resources); + init_outputs (gpu_kms, &resources); + init_frame_clock (gpu_kms); ++ init_info (gpu_kms); + + meta_kms_resources_release (&resources); + +@@ -940,6 +964,8 @@ meta_gpu_kms_finalize (GObject *object) + + free_resources (gpu_kms); + ++ g_free (gpu_kms->driver_name); ++ + G_OBJECT_CLASS (meta_gpu_kms_parent_class)->finalize (object); + } + +diff --git a/src/backends/native/meta-gpu-kms.h b/src/backends/native/meta-gpu-kms.h +index 1f7a939e27..6096e58341 100644 +--- a/src/backends/native/meta-gpu-kms.h ++++ b/src/backends/native/meta-gpu-kms.h +@@ -108,4 +108,6 @@ MetaGpuKmsFlipClosureContainer * meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu + + void meta_gpu_kms_flip_closure_container_free (MetaGpuKmsFlipClosureContainer *closure_container); + ++const char * meta_gpu_kms_get_driver_name (MetaGpuKms *gpu_kms); ++ + #endif /* META_GPU_KMS_H */ +-- +2.26.2 + diff --git a/SOURCES/0005-cogl-Allow-glBlitFramebuffer-between-onscreen-offscr.patch b/SOURCES/0005-cogl-Allow-glBlitFramebuffer-between-onscreen-offscr.patch new file mode 100644 index 0000000..81d49d6 --- /dev/null +++ b/SOURCES/0005-cogl-Allow-glBlitFramebuffer-between-onscreen-offscr.patch @@ -0,0 +1,145 @@ +From e4b2234d9918e9d3357ac3c7ca3898599725d3da Mon Sep 17 00:00:00 2001 +From: Pekka Paalanen +Date: Mon, 6 May 2019 15:08:29 +0300 +Subject: [PATCH 05/12] cogl: Allow glBlitFramebuffer between + onscreen/offscreen + +Depends on "cogl: Replace ANGLE with GLES3 and NV framebuffer_blit" + +Allow blitting between onscreen and offscreen framebuffers by doing the y-flip +as necessary. This was not possible with ANGLE, but now with ANGLE gone, +glBlitFramebuffer supports flipping the copied image. + +This will be useful in follow-up work to copy from onscreen primary GPU +framebuffer to an offscreen secondary GPU framebuffer. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/615 + +(cherry picked from commit 45289b3d65e308117f1bc8fe6a4c88c1baaacca7) +--- + cogl/cogl/cogl-framebuffer-private.h | 14 +++---- + cogl/cogl/cogl-framebuffer.c | 46 ++++++++++++++++++----- + cogl/cogl/driver/gl/cogl-framebuffer-gl.c | 5 +-- + 3 files changed, 43 insertions(+), 22 deletions(-) + +diff --git a/cogl/cogl/cogl-framebuffer-private.h b/cogl/cogl/cogl-framebuffer-private.h +index b06fbaee1..f68153d8b 100644 +--- a/cogl/cogl/cogl-framebuffer-private.h ++++ b/cogl/cogl/cogl-framebuffer-private.h +@@ -381,7 +381,11 @@ _cogl_push_framebuffers (CoglFramebuffer *draw_buffer, + * This blits a region of the color buffer of the source buffer + * to the destination buffer. This function should only be + * called if the COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT feature is +- * advertised. The two buffers must both be offscreen. ++ * advertised. ++ * ++ * The source and destination rectangles are defined in offscreen ++ * framebuffer orientation. When copying between an offscreen and ++ * onscreen framebuffers, the image is y-flipped accordingly. + * + * The two buffers must have the same value types (e.g. floating-point, + * unsigned int, signed int, or fixed-point), but color formats do not +@@ -396,14 +400,6 @@ _cogl_push_framebuffers (CoglFramebuffer *draw_buffer, + * scale the results it may make more sense to draw a primitive + * instead. + * +- * We can only really support blitting between two offscreen buffers +- * for this function on GLES2.0. This is because we effectively render +- * upside down to offscreen buffers to maintain Cogl's representation +- * of the texture coordinate system where 0,0 is the top left of the +- * texture. If we were to blit from an offscreen to an onscreen buffer +- * then we would need to mirror the blit along the x-axis but the GLES +- * extension does not support this. +- * + * The GL function is documented to be affected by the scissor. This + * function therefore ensure that an empty clip stack is flushed + * before performing the blit which means the scissor is effectively +diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c +index 0bc225945..90976a611 100644 +--- a/cogl/cogl/cogl-framebuffer.c ++++ b/cogl/cogl/cogl-framebuffer.c +@@ -1460,15 +1460,12 @@ _cogl_blit_framebuffer (CoglFramebuffer *src, + int height) + { + CoglContext *ctx = src->context; ++ int src_x1, src_y1, src_x2, src_y2; ++ int dst_x1, dst_y1, dst_x2, dst_y2; + + _COGL_RETURN_IF_FAIL (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT)); + +- /* We can only support blitting between offscreen buffers because +- otherwise we would need to mirror the image and GLES2.0 doesn't +- support this */ +- _COGL_RETURN_IF_FAIL (cogl_is_offscreen (src)); +- _COGL_RETURN_IF_FAIL (cogl_is_offscreen (dest)); + /* The buffers must use the same premult convention */ + _COGL_RETURN_IF_FAIL ((src->internal_format & COGL_PREMULT_BIT) == + (dest->internal_format & COGL_PREMULT_BIT)); +@@ -1492,10 +1489,41 @@ _cogl_blit_framebuffer (CoglFramebuffer *src, + * as changed */ + ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; + +- ctx->glBlitFramebuffer (src_x, src_y, +- src_x + width, src_y + height, +- dst_x, dst_y, +- dst_x + width, dst_y + height, ++ /* Offscreens we do the normal way, onscreens need an y-flip. Even if ++ * we consider offscreens to be rendered upside-down, the offscreen ++ * orientation is in this function's API. */ ++ if (cogl_is_offscreen (src)) ++ { ++ src_x1 = src_x; ++ src_y1 = src_y; ++ src_x2 = src_x + width; ++ src_y2 = src_y + height; ++ } ++ else ++ { ++ src_x1 = src_x; ++ src_y1 = cogl_framebuffer_get_height (src) - src_y; ++ src_x2 = src_x + width; ++ src_y2 = src_y1 - height; ++ } ++ ++ if (cogl_is_offscreen (dest)) ++ { ++ dst_x1 = dst_x; ++ dst_y1 = dst_y; ++ dst_x2 = dst_x + width; ++ dst_y2 = dst_y + height; ++ } ++ else ++ { ++ dst_x1 = dst_x; ++ dst_y1 = cogl_framebuffer_get_height (dest) - dst_y; ++ dst_x2 = dst_x + width; ++ dst_y2 = dst_y1 - height; ++ } ++ ++ ctx->glBlitFramebuffer (src_x1, src_y1, src_x2, src_y2, ++ dst_x1, dst_y1, dst_x2, dst_y2, + GL_COLOR_BUFFER_BIT, + GL_NEAREST); + } +diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +index 5402a7075..83e1d263a 100644 +--- a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c ++++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +@@ -400,12 +400,9 @@ _cogl_framebuffer_gl_flush_state (CoglFramebuffer *draw_buffer, + else + { + /* NB: Currently we only take advantage of binding separate +- * read/write buffers for offscreen framebuffer blit +- * purposes. */ ++ * read/write buffers for framebuffer blit purposes. */ + _COGL_RETURN_IF_FAIL (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT)); +- _COGL_RETURN_IF_FAIL (draw_buffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN); +- _COGL_RETURN_IF_FAIL (read_buffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN); + + _cogl_framebuffer_gl_bind (draw_buffer, GL_DRAW_FRAMEBUFFER); + _cogl_framebuffer_gl_bind (read_buffer, GL_READ_FRAMEBUFFER); +-- +2.21.0 + diff --git a/SOURCES/0006-cogl-Rename-feature-OFFSCREEN_BLIT-to-BLIT_FRAMEBUFF.patch b/SOURCES/0006-cogl-Rename-feature-OFFSCREEN_BLIT-to-BLIT_FRAMEBUFF.patch new file mode 100644 index 0000000..6bc5e2c --- /dev/null +++ b/SOURCES/0006-cogl-Rename-feature-OFFSCREEN_BLIT-to-BLIT_FRAMEBUFF.patch @@ -0,0 +1,115 @@ +From 579c85d17b17fc7ad3d6c88af39932ce8faeaabe Mon Sep 17 00:00:00 2001 +From: Pekka Paalanen +Date: Mon, 6 May 2019 15:58:33 +0300 +Subject: [PATCH 06/12] cogl: Rename feature OFFSCREEN_BLIT to BLIT_FRAMEBUFFER + +The feature is not limited to offscreen framebuffer blits anymore since +"cogl: Allow glBlitFramebuffer between onscreen/offscreen". + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/615 + +(cherry picked from commit 55c084e6e1059d8f94c699b01c408523ed504196) +--- + cogl/cogl/cogl-blit.c | 2 +- + cogl/cogl/cogl-framebuffer-private.h | 2 +- + cogl/cogl/cogl-framebuffer.c | 2 +- + cogl/cogl/cogl-private.h | 2 +- + cogl/cogl/driver/gl/cogl-framebuffer-gl.c | 2 +- + cogl/cogl/driver/gl/gl/cogl-driver-gl.c | 2 +- + cogl/cogl/driver/gl/gles/cogl-driver-gles.c | 2 +- + 7 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/cogl/cogl/cogl-blit.c b/cogl/cogl/cogl-blit.c +index a61eb66d2..c561b2e45 100644 +--- a/cogl/cogl/cogl-blit.c ++++ b/cogl/cogl/cogl-blit.c +@@ -158,7 +158,7 @@ _cogl_blit_framebuffer_begin (CoglBlitData *data) + supported. */ + if ((_cogl_texture_get_format (data->src_tex) & COGL_PREMULT_BIT) != + (_cogl_texture_get_format (data->dst_tex) & COGL_PREMULT_BIT) || +- !_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT)) ++ !_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)) + return FALSE; + + dst_offscreen = _cogl_offscreen_new_with_texture_full +diff --git a/cogl/cogl/cogl-framebuffer-private.h b/cogl/cogl/cogl-framebuffer-private.h +index f68153d8b..cb1f87354 100644 +--- a/cogl/cogl/cogl-framebuffer-private.h ++++ b/cogl/cogl/cogl-framebuffer-private.h +@@ -380,7 +380,7 @@ _cogl_push_framebuffers (CoglFramebuffer *draw_buffer, + * + * This blits a region of the color buffer of the source buffer + * to the destination buffer. This function should only be +- * called if the COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT feature is ++ * called if the COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER feature is + * advertised. + * + * The source and destination rectangles are defined in offscreen +diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c +index 90976a611..5cc4eada4 100644 +--- a/cogl/cogl/cogl-framebuffer.c ++++ b/cogl/cogl/cogl-framebuffer.c +@@ -1464,7 +1464,7 @@ _cogl_blit_framebuffer (CoglFramebuffer *src, + int dst_x1, dst_y1, dst_x2, dst_y2; + + _COGL_RETURN_IF_FAIL (_cogl_has_private_feature +- (ctx, COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT)); ++ (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)); + + /* The buffers must use the same premult convention */ + _COGL_RETURN_IF_FAIL ((src->internal_format & COGL_PREMULT_BIT) == +diff --git a/cogl/cogl/cogl-private.h b/cogl/cogl/cogl-private.h +index 9f918b851..d9fbe68c7 100644 +--- a/cogl/cogl/cogl-private.h ++++ b/cogl/cogl/cogl-private.h +@@ -42,7 +42,7 @@ typedef enum + { + COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE, + COGL_PRIVATE_FEATURE_MESA_PACK_INVERT, +- COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT, ++ COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, + COGL_PRIVATE_FEATURE_FOUR_CLIP_PLANES, + COGL_PRIVATE_FEATURE_PBOS, + COGL_PRIVATE_FEATURE_VBOS, +diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +index 83e1d263a..90d08954d 100644 +--- a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c ++++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +@@ -402,7 +402,7 @@ _cogl_framebuffer_gl_flush_state (CoglFramebuffer *draw_buffer, + /* NB: Currently we only take advantage of binding separate + * read/write buffers for framebuffer blit purposes. */ + _COGL_RETURN_IF_FAIL (_cogl_has_private_feature +- (ctx, COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT)); ++ (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)); + + _cogl_framebuffer_gl_bind (draw_buffer, GL_DRAW_FRAMEBUFFER); + _cogl_framebuffer_gl_bind (read_buffer, GL_READ_FRAMEBUFFER); +diff --git a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c +index 4d46844d5..e06e27961 100644 +--- a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c ++++ b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c +@@ -467,7 +467,7 @@ _cogl_driver_update_features (CoglContext *ctx, + + if (ctx->glBlitFramebuffer) + COGL_FLAGS_SET (private_features, +- COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT, TRUE); ++ COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, TRUE); + + if (ctx->glRenderbufferStorageMultisampleIMG) + { +diff --git a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c +index 23158d5c7..bcb0bdf07 100644 +--- a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c ++++ b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c +@@ -326,7 +326,7 @@ _cogl_driver_update_features (CoglContext *context, + + if (context->glBlitFramebuffer) + COGL_FLAGS_SET (private_features, +- COGL_PRIVATE_FEATURE_OFFSCREEN_BLIT, TRUE); ++ COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, TRUE); + + if (_cogl_check_extension ("GL_OES_element_index_uint", gl_extensions)) + { +-- +2.21.0 + diff --git a/SOURCES/0007-cogl-Expose-cogl_blit_framebuffer.patch b/SOURCES/0007-cogl-Expose-cogl_blit_framebuffer.patch new file mode 100644 index 0000000..a5c9686 --- /dev/null +++ b/SOURCES/0007-cogl-Expose-cogl_blit_framebuffer.patch @@ -0,0 +1,261 @@ +From be13d3c844a6623563ae4e74dbb3409baf16fc9c Mon Sep 17 00:00:00 2001 +From: Pekka Paalanen +Date: Mon, 3 Dec 2018 14:34:41 +0200 +Subject: [PATCH 07/12] cogl: Expose cogl_blit_framebuffer + +The function will be used in copying from a primary GPU framebuffer to a +secondary GPU framebuffer using the primary GPU specifically when the +secondary GPU is not render-capable. + +To allow falling back in case glBlitFramebuffer cannot be used, add boolean +return value, and GError argument for debugging purposes. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/615 +(cherry picked from commit 6061abbf90cd1d62e262ebf3636470d2219e04a7) +--- + cogl/cogl/cogl-blit.c | 11 ++--- + cogl/cogl/cogl-framebuffer-private.h | 55 ----------------------- + cogl/cogl/cogl-framebuffer.c | 40 +++++++++++------ + cogl/cogl/cogl-framebuffer.h | 66 +++++++++++++++++++++++++++- + 4 files changed, 98 insertions(+), 74 deletions(-) + +diff --git a/cogl/cogl/cogl-blit.c b/cogl/cogl/cogl-blit.c +index c561b2e45..ae5a8a345 100644 +--- a/cogl/cogl/cogl-blit.c ++++ b/cogl/cogl/cogl-blit.c +@@ -207,11 +207,12 @@ _cogl_blit_framebuffer_blit (CoglBlitData *data, + int width, + int height) + { +- _cogl_blit_framebuffer (data->src_fb, +- data->dest_fb, +- src_x, src_y, +- dst_x, dst_y, +- width, height); ++ cogl_blit_framebuffer (data->src_fb, ++ data->dest_fb, ++ src_x, src_y, ++ dst_x, dst_y, ++ width, height, ++ NULL); + } + + static void +diff --git a/cogl/cogl/cogl-framebuffer-private.h b/cogl/cogl/cogl-framebuffer-private.h +index cb1f87354..7d71fb1dc 100644 +--- a/cogl/cogl/cogl-framebuffer-private.h ++++ b/cogl/cogl/cogl-framebuffer-private.h +@@ -367,61 +367,6 @@ void + _cogl_push_framebuffers (CoglFramebuffer *draw_buffer, + CoglFramebuffer *read_buffer); + +-/* +- * _cogl_blit_framebuffer: +- * @src: The source #CoglFramebuffer +- * @dest: The destination #CoglFramebuffer +- * @src_x: Source x position +- * @src_y: Source y position +- * @dst_x: Destination x position +- * @dst_y: Destination y position +- * @width: Width of region to copy +- * @height: Height of region to copy +- * +- * This blits a region of the color buffer of the source buffer +- * to the destination buffer. This function should only be +- * called if the COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER feature is +- * advertised. +- * +- * The source and destination rectangles are defined in offscreen +- * framebuffer orientation. When copying between an offscreen and +- * onscreen framebuffers, the image is y-flipped accordingly. +- * +- * The two buffers must have the same value types (e.g. floating-point, +- * unsigned int, signed int, or fixed-point), but color formats do not +- * need to match. This limitation comes from OpenGL ES 3.0 definition +- * of glBlitFramebuffer. +- * +- * Note that this function differs a lot from the glBlitFramebuffer +- * function provided by the GL_EXT_framebuffer_blit extension. Notably +- * it doesn't support having different sizes for the source and +- * destination rectangle. This doesn't seem +- * like a particularly useful feature. If the application wanted to +- * scale the results it may make more sense to draw a primitive +- * instead. +- * +- * The GL function is documented to be affected by the scissor. This +- * function therefore ensure that an empty clip stack is flushed +- * before performing the blit which means the scissor is effectively +- * ignored. +- * +- * The function also doesn't support specifying the buffers to copy +- * and instead only the color buffer is copied. When copying the depth +- * or stencil buffers the extension on GLES2.0 only supports copying +- * the full buffer which would be awkward to document with this +- * API. If we wanted to support that feature it may be better to have +- * a separate function to copy the entire buffer for a given mask. +- */ +-void +-_cogl_blit_framebuffer (CoglFramebuffer *src, +- CoglFramebuffer *dest, +- int src_x, +- int src_y, +- int dst_x, +- int dst_y, +- int width, +- int height); +- + void + _cogl_framebuffer_push_projection (CoglFramebuffer *framebuffer); + +diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c +index 5cc4eada4..6d35c6b13 100644 +--- a/cogl/cogl/cogl-framebuffer.c ++++ b/cogl/cogl/cogl-framebuffer.c +@@ -1449,26 +1449,38 @@ cogl_framebuffer_read_pixels (CoglFramebuffer *framebuffer, + return ret; + } + +-void +-_cogl_blit_framebuffer (CoglFramebuffer *src, +- CoglFramebuffer *dest, +- int src_x, +- int src_y, +- int dst_x, +- int dst_y, +- int width, +- int height) ++gboolean ++cogl_blit_framebuffer (CoglFramebuffer *src, ++ CoglFramebuffer *dest, ++ int src_x, ++ int src_y, ++ int dst_x, ++ int dst_y, ++ int width, ++ int height, ++ GError **error) + { + CoglContext *ctx = src->context; + int src_x1, src_y1, src_x2, src_y2; + int dst_x1, dst_y1, dst_x2, dst_y2; + +- _COGL_RETURN_IF_FAIL (_cogl_has_private_feature +- (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)); ++ if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)) ++ { ++ g_set_error_literal (error, COGL_SYSTEM_ERROR, ++ COGL_SYSTEM_ERROR_UNSUPPORTED, ++ "Cogl BLIT_FRAMEBUFFER is not supported by the system."); ++ return FALSE; ++ } + + /* The buffers must use the same premult convention */ +- _COGL_RETURN_IF_FAIL ((src->internal_format & COGL_PREMULT_BIT) == +- (dest->internal_format & COGL_PREMULT_BIT)); ++ if ((src->internal_format & COGL_PREMULT_BIT) != ++ (dest->internal_format & COGL_PREMULT_BIT)) ++ { ++ g_set_error_literal (error, COGL_SYSTEM_ERROR, ++ COGL_SYSTEM_ERROR_UNSUPPORTED, ++ "cogl_blit_framebuffer premult mismatch."); ++ return FALSE; ++ } + + /* Make sure the current framebuffers are bound. We explicitly avoid + flushing the clip state so we can bind our own empty state */ +@@ -1526,6 +1538,8 @@ _cogl_blit_framebuffer (CoglFramebuffer *src, + dst_x1, dst_y1, dst_x2, dst_y2, + GL_COLOR_BUFFER_BIT, + GL_NEAREST); ++ ++ return TRUE; + } + + void +diff --git a/cogl/cogl/cogl-framebuffer.h b/cogl/cogl/cogl-framebuffer.h +index 48a77e1ed..230a78627 100644 +--- a/cogl/cogl/cogl-framebuffer.h ++++ b/cogl/cogl/cogl-framebuffer.h +@@ -3,7 +3,8 @@ + * + * A Low Level GPU Graphics and Utilities API + * +- * Copyright (C) 2011 Intel Corporation. ++ * Copyright (C) 2007,2008,2009,2011 Intel Corporation. ++ * Copyright (C) 2019 DisplayLink (UK) Ltd. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation +@@ -1846,6 +1847,69 @@ typedef enum /*< prefix=COGL_FRAMEBUFFER_ERROR >*/ + gboolean + cogl_is_framebuffer (void *object); + ++/** ++ * cogl_blit_framebuffer: ++ * @src: The source #CoglFramebuffer ++ * @dest: The destination #CoglFramebuffer ++ * @src_x: Source x position ++ * @src_y: Source y position ++ * @dst_x: Destination x position ++ * @dst_y: Destination y position ++ * @width: Width of region to copy ++ * @height: Height of region to copy ++ * @error: optional error object ++ * ++ * @return FALSE for an immediately detected error, TRUE otherwise. ++ * ++ * This blits a region of the color buffer of the source buffer ++ * to the destination buffer. This function should only be ++ * called if the COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER feature is ++ * advertised. ++ * ++ * The source and destination rectangles are defined in offscreen ++ * framebuffer orientation. When copying between an offscreen and ++ * onscreen framebuffers, the image is y-flipped accordingly. ++ * ++ * The two buffers must have the same value types (e.g. floating-point, ++ * unsigned int, signed int, or fixed-point), but color formats do not ++ * need to match. This limitation comes from OpenGL ES 3.0 definition ++ * of glBlitFramebuffer. ++ * ++ * Note that this function differs a lot from the glBlitFramebuffer ++ * function provided by the GL_EXT_framebuffer_blit extension. Notably ++ * it doesn't support having different sizes for the source and ++ * destination rectangle. This doesn't seem ++ * like a particularly useful feature. If the application wanted to ++ * scale the results it may make more sense to draw a primitive ++ * instead. ++ * ++ * The GL function is documented to be affected by the scissor. This ++ * function therefore ensure that an empty clip stack is flushed ++ * before performing the blit which means the scissor is effectively ++ * ignored. ++ * ++ * The function also doesn't support specifying the buffers to copy ++ * and instead only the color buffer is copied. When copying the depth ++ * or stencil buffers the extension on GLES2.0 only supports copying ++ * the full buffer which would be awkward to document with this ++ * API. If we wanted to support that feature it may be better to have ++ * a separate function to copy the entire buffer for a given mask. ++ * ++ * The @c error argument is optional, it can be NULL. If it is not NULL ++ * and this function returns FALSE, an error object with a code from ++ * COGL_SYSTEM_ERROR will be created. ++ */ ++gboolean ++cogl_blit_framebuffer (CoglFramebuffer *src, ++ CoglFramebuffer *dest, ++ int src_x, ++ int src_y, ++ int dst_x, ++ int dst_y, ++ int width, ++ int height, ++ GError **error); ++ + G_END_DECLS + + #endif /* __COGL_FRAMEBUFFER_H */ +-- +2.21.0 + diff --git a/SOURCES/0008-clutter-stage-view-Use-cogl_blit_framebuffer-for-sha.patch b/SOURCES/0008-clutter-stage-view-Use-cogl_blit_framebuffer-for-sha.patch new file mode 100644 index 0000000..09e8db9 --- /dev/null +++ b/SOURCES/0008-clutter-stage-view-Use-cogl_blit_framebuffer-for-sha.patch @@ -0,0 +1,42 @@ +From bbeb161e8ab31bbef3c7d378e9a8d4ecc786c25d Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Thu, 26 Sep 2019 10:20:36 +0200 +Subject: [PATCH 08/12] clutter/stage-view: Use cogl_blit_framebuffer() for + shadow FB + +If there is no transformation, use `cogl_blit_framebuffer()` as a +shortcut in `clutter_stage_view_blit_offscreen()`, that dramatically +improves performance when using a shadow framebuffer. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/809 +(cherry picked from commit 3400c555a032832a689c208486891352a6cb92de) +--- + clutter/clutter/clutter-stage-view.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index cd6cd35cb..00cbfd1ce 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -126,6 +126,18 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + clutter_stage_view_get_instance_private (view); + CoglMatrix matrix; + ++ clutter_stage_view_get_offscreen_transformation_matrix (view, &matrix); ++ if (cogl_matrix_is_identity (&matrix)) ++ { ++ if (cogl_blit_framebuffer (priv->offscreen, ++ priv->framebuffer, ++ rect->x, rect->y, ++ rect->x, rect->y, ++ rect->width, rect->height, ++ NULL)) ++ return; ++ } ++ + clutter_stage_view_ensure_offscreen_blit_pipeline (view); + cogl_framebuffer_push_matrix (priv->framebuffer); + +-- +2.21.0 + diff --git a/SOURCES/0009-clutter-stage-view-Ignore-clipping-rectangle-for-off.patch b/SOURCES/0009-clutter-stage-view-Ignore-clipping-rectangle-for-off.patch new file mode 100644 index 0000000..ea67449 --- /dev/null +++ b/SOURCES/0009-clutter-stage-view-Ignore-clipping-rectangle-for-off.patch @@ -0,0 +1,49 @@ +From 46bb54bcd9c90f90dd170355209f8c379680d5c1 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Tue, 1 Oct 2019 14:16:25 +0200 +Subject: [PATCH 09/12] clutter/stage-view: Ignore clipping rectangle for + offscreen blit +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In `clutter_stage_view_blit_offscreen()`, the given clipping rectangle +is in “view” coordinates whereas we intend to copy the whole actual +framebuffer, meaning that we cannot use the clipping rectangle. + +Use the actual framebuffer size, starting at (0, 0) instead. + +That fixes the issue with partial repainting with shadow framebuffer +when fractional scaling is enabled. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/820 +(cherry picked from commit 0a3f25c3039b586f5b5721e91136c5d2fccecca1) +--- + clutter/clutter/clutter-stage-view.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 00cbfd1ce..503c31e78 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -129,11 +129,14 @@ clutter_stage_view_blit_offscreen (ClutterStageView *view, + clutter_stage_view_get_offscreen_transformation_matrix (view, &matrix); + if (cogl_matrix_is_identity (&matrix)) + { ++ int fb_width = cogl_framebuffer_get_width (priv->framebuffer); ++ int fb_height = cogl_framebuffer_get_height (priv->framebuffer); ++ + if (cogl_blit_framebuffer (priv->offscreen, + priv->framebuffer, +- rect->x, rect->y, +- rect->x, rect->y, +- rect->width, rect->height, ++ 0, 0, ++ 0, 0, ++ fb_width, fb_height, + NULL)) + return; + } +-- +2.21.0 + diff --git a/SOURCES/0010-cogl-Flush-journal-before-blitting.patch b/SOURCES/0010-cogl-Flush-journal-before-blitting.patch new file mode 100644 index 0000000..f3935eb --- /dev/null +++ b/SOURCES/0010-cogl-Flush-journal-before-blitting.patch @@ -0,0 +1,34 @@ +From 4c7fe200e05f9a028d440ed2032961d1b798c83b Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Tue, 1 Oct 2019 15:54:47 +0200 +Subject: [PATCH 10/12] cogl: Flush journal before blitting + +Make sure to submit all pending primitives before blitting, otherwise +rendering from the shell may be incomplete leaving partial drawing of +the shell widgets. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/820 +(cherry picked from commit 0cdf13ac12c570d38737fddb68946157c0b7a4d2) +--- + cogl/cogl/cogl-framebuffer.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c +index 6d35c6b13..948cd112d 100644 +--- a/cogl/cogl/cogl-framebuffer.c ++++ b/cogl/cogl/cogl-framebuffer.c +@@ -1482,6 +1482,11 @@ cogl_blit_framebuffer (CoglFramebuffer *src, + return FALSE; + } + ++ /* Make sure any batched primitives get submitted to the driver ++ * before blitting ++ */ ++ _cogl_framebuffer_flush_journal (src); ++ + /* Make sure the current framebuffers are bound. We explicitly avoid + flushing the clip state so we can bind our own empty state */ + _cogl_framebuffer_flush_state (dest, +-- +2.21.0 + diff --git a/SOURCES/0011-clutter-stage-view-Separate-offscreen-and-shadowfb.patch b/SOURCES/0011-clutter-stage-view-Separate-offscreen-and-shadowfb.patch new file mode 100644 index 0000000..9dd46a0 --- /dev/null +++ b/SOURCES/0011-clutter-stage-view-Separate-offscreen-and-shadowfb.patch @@ -0,0 +1,304 @@ +From cf8f1fb8478e4b76c91e825d1537396b014689a0 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Tue, 22 Oct 2019 17:03:03 +0200 +Subject: [PATCH 11/12] clutter/stage-view: Separate offscreen and shadowfb + +Previously, we would use a single offscreen framebuffer for both +transformations and when a shadow framebuffer should be used, but that +can be dreadfully slow when using software rendering with a discrete GPU +due to bandwidth limitations. + +Keep the offscreen framebuffer for transformations only and add another +intermediate shadow framebuffer used as a copy of the onscreen +framebuffer. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/917 + +(cherry picked from commit 2b8b450fe16c21f0f37a1779560c0e5da61a9b89) +--- + clutter/clutter/clutter-stage-view.c | 162 +++++++++++++++++----- + clutter/clutter/cogl/clutter-stage-cogl.c | 6 +- + 2 files changed, 128 insertions(+), 40 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index 503c31e78..c536ac720 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -29,6 +29,7 @@ enum + PROP_LAYOUT, + PROP_FRAMEBUFFER, + PROP_OFFSCREEN, ++ PROP_SHADOWFB, + PROP_SCALE, + + PROP_LAST +@@ -43,7 +44,10 @@ typedef struct _ClutterStageViewPrivate + CoglFramebuffer *framebuffer; + + CoglOffscreen *offscreen; +- CoglPipeline *pipeline; ++ CoglPipeline *offscreen_pipeline; ++ ++ CoglOffscreen *shadowfb; ++ CoglPipeline *shadowfb_pipeline; + + guint dirty_viewport : 1; + guint dirty_projection : 1; +@@ -69,6 +73,8 @@ clutter_stage_view_get_framebuffer (ClutterStageView *view) + + if (priv->offscreen) + return priv->offscreen; ++ else if (priv->shadowfb) ++ return priv->shadowfb; + else + return priv->framebuffer; + } +@@ -82,6 +88,24 @@ clutter_stage_view_get_onscreen (ClutterStageView *view) + return priv->framebuffer; + } + ++static CoglPipeline * ++clutter_stage_view_create_framebuffer_pipeline (CoglFramebuffer *framebuffer) ++{ ++ CoglPipeline *pipeline; ++ ++ pipeline = cogl_pipeline_new (cogl_framebuffer_get_context (framebuffer)); ++ ++ cogl_pipeline_set_layer_filters (pipeline, 0, ++ COGL_PIPELINE_FILTER_NEAREST, ++ COGL_PIPELINE_FILTER_NEAREST); ++ cogl_pipeline_set_layer_texture (pipeline, 0, ++ cogl_offscreen_get_texture (framebuffer)); ++ cogl_pipeline_set_layer_wrap_mode (pipeline, 0, ++ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); ++ ++ return pipeline; ++} ++ + static void + clutter_stage_view_ensure_offscreen_blit_pipeline (ClutterStageView *view) + { +@@ -92,71 +116,122 @@ clutter_stage_view_ensure_offscreen_blit_pipeline (ClutterStageView *view) + + g_assert (priv->offscreen != NULL); + +- if (priv->pipeline) ++ if (priv->offscreen_pipeline) + return; + +- priv->pipeline = +- cogl_pipeline_new (cogl_framebuffer_get_context (priv->offscreen)); +- cogl_pipeline_set_layer_filters (priv->pipeline, 0, +- COGL_PIPELINE_FILTER_NEAREST, +- COGL_PIPELINE_FILTER_NEAREST); +- cogl_pipeline_set_layer_texture (priv->pipeline, 0, +- cogl_offscreen_get_texture (priv->offscreen)); +- cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, +- COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); ++ priv->offscreen_pipeline = ++ clutter_stage_view_create_framebuffer_pipeline (priv->offscreen); + + if (view_class->setup_offscreen_blit_pipeline) +- view_class->setup_offscreen_blit_pipeline (view, priv->pipeline); ++ view_class->setup_offscreen_blit_pipeline (view, priv->offscreen_pipeline); + } + +-void +-clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view) ++static void ++clutter_stage_view_ensure_shadowfb_blit_pipeline (ClutterStageView *view) + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); + +- g_clear_pointer (&priv->pipeline, cogl_object_unref); ++ if (priv->shadowfb_pipeline) ++ return; ++ ++ priv->shadowfb_pipeline = ++ clutter_stage_view_create_framebuffer_pipeline (priv->shadowfb); + } + + void +-clutter_stage_view_blit_offscreen (ClutterStageView *view, +- const cairo_rectangle_int_t *rect) ++clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view) + { + ClutterStageViewPrivate *priv = + clutter_stage_view_get_instance_private (view); ++ ++ g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); ++} ++ ++static void ++clutter_stage_view_copy_to_framebuffer (ClutterStageView *view, ++ const cairo_rectangle_int_t *rect, ++ CoglPipeline *pipeline, ++ CoglFramebuffer *src_framebuffer, ++ CoglFramebuffer *dst_framebuffer, ++ gboolean can_blit) ++{ + CoglMatrix matrix; + +- clutter_stage_view_get_offscreen_transformation_matrix (view, &matrix); +- if (cogl_matrix_is_identity (&matrix)) ++ /* First, try with blit */ ++ if (can_blit) + { +- int fb_width = cogl_framebuffer_get_width (priv->framebuffer); +- int fb_height = cogl_framebuffer_get_height (priv->framebuffer); +- +- if (cogl_blit_framebuffer (priv->offscreen, +- priv->framebuffer, ++ if (cogl_blit_framebuffer (src_framebuffer, ++ dst_framebuffer, + 0, 0, + 0, 0, +- fb_width, fb_height, ++ cogl_framebuffer_get_width (dst_framebuffer), ++ cogl_framebuffer_get_height (dst_framebuffer), + NULL)) + return; + } + +- clutter_stage_view_ensure_offscreen_blit_pipeline (view); +- cogl_framebuffer_push_matrix (priv->framebuffer); ++ /* If blit fails, fallback to the slower painting method */ ++ cogl_framebuffer_push_matrix (dst_framebuffer); + +- /* Set transform so 0,0 is on the top left corner and 1,1 on +- * the bottom right corner. +- */ + cogl_matrix_init_identity (&matrix); + cogl_matrix_translate (&matrix, -1, 1, 0); + cogl_matrix_scale (&matrix, 2, -2, 0); +- cogl_framebuffer_set_projection_matrix (priv->framebuffer, &matrix); ++ cogl_framebuffer_set_projection_matrix (dst_framebuffer, &matrix); + +- cogl_framebuffer_draw_rectangle (priv->framebuffer, +- priv->pipeline, ++ cogl_framebuffer_draw_rectangle (dst_framebuffer, ++ pipeline, + 0, 0, 1, 1); + +- cogl_framebuffer_pop_matrix (priv->framebuffer); ++ cogl_framebuffer_pop_matrix (dst_framebuffer); ++} ++ ++void ++clutter_stage_view_blit_offscreen (ClutterStageView *view, ++ const cairo_rectangle_int_t *rect) ++{ ++ ClutterStageViewPrivate *priv = ++ clutter_stage_view_get_instance_private (view); ++ ++ if (priv->offscreen) ++ { ++ gboolean can_blit; ++ CoglMatrix matrix; ++ ++ clutter_stage_view_ensure_offscreen_blit_pipeline (view); ++ clutter_stage_view_get_offscreen_transformation_matrix (view, &matrix); ++ can_blit = cogl_matrix_is_identity (&matrix); ++ ++ if (priv->shadowfb) ++ { ++ clutter_stage_view_copy_to_framebuffer (view, ++ rect, ++ priv->offscreen_pipeline, ++ priv->offscreen, ++ priv->shadowfb, ++ can_blit); ++ } ++ else ++ { ++ clutter_stage_view_copy_to_framebuffer (view, ++ rect, ++ priv->offscreen_pipeline, ++ priv->offscreen, ++ priv->framebuffer, ++ can_blit); ++ } ++ } ++ ++ if (priv->shadowfb) ++ { ++ clutter_stage_view_ensure_shadowfb_blit_pipeline (view); ++ clutter_stage_view_copy_to_framebuffer (view, ++ rect, ++ priv->shadowfb_pipeline, ++ priv->shadowfb, ++ priv->framebuffer, ++ TRUE); ++ } + } + + float +@@ -256,6 +331,9 @@ clutter_stage_view_get_property (GObject *object, + case PROP_OFFSCREEN: + g_value_set_boxed (value, priv->offscreen); + break; ++ case PROP_SHADOWFB: ++ g_value_set_boxed (value, priv->shadowfb); ++ break; + case PROP_SCALE: + g_value_set_float (value, priv->scale); + break; +@@ -301,6 +379,9 @@ clutter_stage_view_set_property (GObject *object, + case PROP_OFFSCREEN: + priv->offscreen = g_value_dup_boxed (value); + break; ++ case PROP_SHADOWFB: ++ priv->shadowfb = g_value_dup_boxed (value); ++ break; + case PROP_SCALE: + priv->scale = g_value_get_float (value); + break; +@@ -317,8 +398,10 @@ clutter_stage_view_dispose (GObject *object) + clutter_stage_view_get_instance_private (view); + + g_clear_pointer (&priv->framebuffer, cogl_object_unref); ++ g_clear_pointer (&priv->shadowfb, cogl_object_unref); + g_clear_pointer (&priv->offscreen, cogl_object_unref); +- g_clear_pointer (&priv->pipeline, cogl_object_unref); ++ g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); ++ g_clear_pointer (&priv->shadowfb_pipeline, cogl_object_unref); + + G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object); + } +@@ -373,6 +456,15 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass) + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + ++ obj_props[PROP_SHADOWFB] = ++ g_param_spec_boxed ("shadowfb", ++ "Shadow framebuffer", ++ "Framebuffer used as intermediate shadow buffer", ++ COGL_TYPE_HANDLE, ++ G_PARAM_READWRITE | ++ G_PARAM_CONSTRUCT_ONLY | ++ G_PARAM_STATIC_STRINGS); ++ + obj_props[PROP_SCALE] = + g_param_spec_float ("scale", + "View scale", +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index e0c39185b..eab76e52f 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -477,11 +477,7 @@ paint_stage (ClutterStageCogl *stage_cogl, + _clutter_stage_maybe_setup_viewport (stage, view); + _clutter_stage_paint_view (stage, view, clip); + +- if (clutter_stage_view_get_onscreen (view) != +- clutter_stage_view_get_framebuffer (view)) +- { +- clutter_stage_view_blit_offscreen (view, clip); +- } ++ clutter_stage_view_blit_offscreen (view, clip); + } + + static void +-- +2.21.0 + diff --git a/SOURCES/0012-renderer-native-Separate-offscreen-and-shadowfb.patch b/SOURCES/0012-renderer-native-Separate-offscreen-and-shadowfb.patch new file mode 100644 index 0000000..cd748a9 --- /dev/null +++ b/SOURCES/0012-renderer-native-Separate-offscreen-and-shadowfb.patch @@ -0,0 +1,98 @@ +From ca3e9e3b3b84fe95affbe5485212c6ecfa1a4b51 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Tue, 22 Oct 2019 17:05:46 +0200 +Subject: [PATCH 12/12] renderer-native: Separate offscreen and shadowfb + +Create the intermediate shadow framebuffer for use exclusively when a +shadowfb is required. + +Keep the previous offscreen framebuffer is as an intermediate +framebuffer for transformations only. + +This way, we can apply transformations between in-memory framebuffers +prior to blit the result to screen, and achieve acceptable performance +even with software rendering on discrete GPU. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/917 + +(cherry picked from commit 551641c74822ca2e3c685e49603836ebf5397df2) +--- + src/backends/native/meta-renderer-native.c | 29 ++++++++++++++++++---- + 1 file changed, 24 insertions(+), 5 deletions(-) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index 3cd01bcb7..ffb64a6bd 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -3287,7 +3287,6 @@ meta_renderer_native_create_onscreen (MetaRendererNative *renderer_native, + static CoglOffscreen * + meta_renderer_native_create_offscreen (MetaRendererNative *renderer, + CoglContext *context, +- MetaMonitorTransform transform, + gint view_width, + gint view_height, + GError **error) +@@ -3489,6 +3488,7 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + MetaMonitorTransform view_transform; + CoglOnscreen *onscreen = NULL; + CoglOffscreen *offscreen = NULL; ++ CoglOffscreen *shadowfb = NULL; + float scale; + int width, height; + MetaRendererView *view; +@@ -3515,18 +3515,35 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + if (!onscreen) + g_error ("Failed to allocate onscreen framebuffer: %s", error->message); + +- if (view_transform != META_MONITOR_TRANSFORM_NORMAL || +- should_force_shadow_fb (renderer_native, +- renderer_native->primary_gpu_kms)) ++ if (view_transform != META_MONITOR_TRANSFORM_NORMAL) + { + offscreen = meta_renderer_native_create_offscreen (renderer_native, + cogl_context, +- view_transform, + width, + height, + &error); + if (!offscreen) + g_error ("Failed to allocate back buffer texture: %s", error->message); ++ ++ } ++ ++ if (should_force_shadow_fb (renderer_native, ++ renderer_native->primary_gpu_kms)) ++ { ++ int shadow_width; ++ int shadow_height; ++ ++ /* The shadowfb must be the same size as the on-screen framebuffer */ ++ shadow_width = cogl_framebuffer_get_width (COGL_FRAMEBUFFER (onscreen)); ++ shadow_height = cogl_framebuffer_get_height (COGL_FRAMEBUFFER (onscreen)); ++ ++ shadowfb = meta_renderer_native_create_offscreen (renderer_native, ++ cogl_context, ++ shadow_width, ++ shadow_height, ++ &error); ++ if (!shadowfb) ++ g_error ("Failed to allocate shadow buffer texture: %s", error->message); + } + + view = g_object_new (META_TYPE_RENDERER_VIEW, +@@ -3534,10 +3551,12 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + "scale", scale, + "framebuffer", onscreen, + "offscreen", offscreen, ++ "shadowfb", shadowfb, + "logical-monitor", logical_monitor, + "transform", view_transform, + NULL); + g_clear_pointer (&offscreen, cogl_object_unref); ++ g_clear_pointer (&shadowfb, cogl_object_unref); + + meta_onscreen_native_set_view (onscreen, view); + +-- +2.21.0 + diff --git a/SOURCES/add-support-for-plain-old-x-device-configuration.patch b/SOURCES/add-support-for-plain-old-x-device-configuration.patch new file mode 100644 index 0000000..ed479ab --- /dev/null +++ b/SOURCES/add-support-for-plain-old-x-device-configuration.patch @@ -0,0 +1,375 @@ +From 08734c2df6bee5426cf75cdf1fa3013af1664d94 Mon Sep 17 00:00:00 2001 +From: Rui Matos +Date: Mon, 9 Oct 2017 18:39:52 +0200 +Subject: [PATCH 1/3] backends/x11: Add a synaptics check for two finger scroll + availability + +Commit "backends/x11: Support synaptics configuration" added support +for synaptics two finger scrolling but didn't add the code to check +that it is available resulting in the upper layer always assuming it +isn't. +--- + src/backends/x11/meta-input-settings-x11.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/src/backends/x11/meta-input-settings-x11.c b/src/backends/x11/meta-input-settings-x11.c +index 16a3ff33d..7ad304704 100644 +--- a/src/backends/x11/meta-input-settings-x11.c ++++ b/src/backends/x11/meta-input-settings-x11.c +@@ -626,6 +626,17 @@ meta_input_settings_x11_has_two_finger_scroll (MetaInputSettings *settings, + guchar *available = NULL; + gboolean has_two_finger = TRUE; + ++ if (is_device_synaptics (device)) ++ { ++ available = get_property (device, "Synaptics Capabilities", ++ XA_INTEGER, 8, 4); ++ if (!available || !available[3]) ++ has_two_finger = FALSE; ++ ++ meta_XFree (available); ++ return has_two_finger; ++ } ++ + available = get_property (device, "libinput Scroll Methods Available", + XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS); + if (!available || !available[SCROLL_METHOD_FIELD_2FG]) +-- +2.36.1 + +From 11a8b8b33582a68e037e7b9e8396af0748bba67a Mon Sep 17 00:00:00 2001 +From: Rui Matos +Date: Mon, 9 Oct 2017 18:55:56 +0200 +Subject: [PATCH 2/3] backends/x11: Add disable while typing support for + synaptics + +This is basically a copy of the old g-s-d mouse plugin code to manage +syndaemon when the synaptics driver is being used. +--- + src/backends/x11/meta-input-settings-x11.c | 112 +++++++++++++++++++++ + 1 file changed, 112 insertions(+) + +diff --git a/src/backends/x11/meta-input-settings-x11.c b/src/backends/x11/meta-input-settings-x11.c +index 7ad304704..743f4a7ce 100644 +--- a/src/backends/x11/meta-input-settings-x11.c ++++ b/src/backends/x11/meta-input-settings-x11.c +@@ -35,6 +35,9 @@ + #ifdef HAVE_LIBGUDEV + #include + #endif ++#ifdef __linux ++#include ++#endif + + #include "backends/meta-logical-monitor.h" + #include "backends/x11/meta-backend-x11.h" +@@ -46,6 +49,8 @@ typedef struct _MetaInputSettingsX11Private + #ifdef HAVE_LIBGUDEV + GUdevClient *udev_client; + #endif ++ gboolean syndaemon_spawned; ++ GPid syndaemon_pid; + } MetaInputSettingsX11Private; + + G_DEFINE_TYPE_WITH_PRIVATE (MetaInputSettingsX11, meta_input_settings_x11, +@@ -333,6 +338,107 @@ change_synaptics_speed (ClutterInputDevice *device, + XCloseDevice (xdisplay, xdevice); + } + ++/* Ensure that syndaemon dies together with us, to avoid running several of ++ * them */ ++static void ++setup_syndaemon (gpointer user_data) ++{ ++#ifdef __linux ++ prctl (PR_SET_PDEATHSIG, SIGHUP); ++#endif ++} ++ ++static gboolean ++have_program_in_path (const char *name) ++{ ++ gchar *path; ++ gboolean result; ++ ++ path = g_find_program_in_path (name); ++ result = (path != NULL); ++ g_free (path); ++ return result; ++} ++ ++static void ++syndaemon_died (GPid pid, ++ gint status, ++ gpointer user_data) ++{ ++ MetaInputSettingsX11 *settings_x11 = META_INPUT_SETTINGS_X11 (user_data); ++ MetaInputSettingsX11Private *priv = ++ meta_input_settings_x11_get_instance_private (settings_x11); ++ GError *error = NULL; ++ ++ if (!g_spawn_check_exit_status (status, &error)) ++ { ++ if ((WIFSIGNALED (status) && WTERMSIG (status) != SIGHUP) || ++ error->domain == G_SPAWN_EXIT_ERROR) ++ g_warning ("Syndaemon exited unexpectedly: %s", error->message); ++ g_error_free (error); ++ } ++ ++ g_spawn_close_pid (pid); ++ priv->syndaemon_spawned = FALSE; ++} ++ ++static void ++set_synaptics_disable_w_typing (MetaInputSettings *settings, ++ gboolean state) ++{ ++ MetaInputSettingsX11 *settings_x11 = META_INPUT_SETTINGS_X11 (settings); ++ MetaInputSettingsX11Private *priv = ++ meta_input_settings_x11_get_instance_private (settings_x11); ++ ++ if (state) ++ { ++ GError *error = NULL; ++ GPtrArray *args; ++ ++ if (priv->syndaemon_spawned) ++ return; ++ ++ if (!have_program_in_path ("syndaemon")) ++ return; ++ ++ args = g_ptr_array_new (); ++ ++ g_ptr_array_add (args, (gpointer)"syndaemon"); ++ g_ptr_array_add (args, (gpointer)"-i"); ++ g_ptr_array_add (args, (gpointer)"1.0"); ++ g_ptr_array_add (args, (gpointer)"-t"); ++ g_ptr_array_add (args, (gpointer)"-K"); ++ g_ptr_array_add (args, (gpointer)"-R"); ++ g_ptr_array_add (args, NULL); ++ ++ /* we must use G_SPAWN_DO_NOT_REAP_CHILD to avoid ++ * double-forking, otherwise syndaemon will immediately get ++ * killed again through (PR_SET_PDEATHSIG when the intermediate ++ * process dies */ ++ g_spawn_async (g_get_home_dir (), (char **) args->pdata, NULL, ++ G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, setup_syndaemon, NULL, ++ &priv->syndaemon_pid, &error); ++ ++ priv->syndaemon_spawned = (error == NULL); ++ g_ptr_array_free (args, TRUE); ++ ++ if (error) ++ { ++ g_warning ("Failed to launch syndaemon: %s", error->message); ++ g_error_free (error); ++ } ++ else ++ { ++ g_child_watch_add (priv->syndaemon_pid, syndaemon_died, settings); ++ } ++ } ++ else if (priv->syndaemon_spawned) ++ { ++ kill (priv->syndaemon_pid, SIGHUP); ++ priv->syndaemon_spawned = FALSE; ++ } ++} ++ + static void + meta_input_settings_x11_set_send_events (MetaInputSettings *settings, + ClutterInputDevice *device, +@@ -457,6 +563,12 @@ meta_input_settings_x11_set_disable_while_typing (MetaInputSettings *settings, + { + guchar value = (enabled) ? 1 : 0; + ++ if (is_device_synaptics (device)) ++ { ++ set_synaptics_disable_w_typing (settings, enabled); ++ return; ++ } ++ + change_property (device, "libinput Disable While Typing Enabled", + XA_INTEGER, 8, &value, 1); + } +-- +2.36.1 + +From fd2923d5c79a60bbd80bf231adf5e1ae7f83d264 Mon Sep 17 00:00:00 2001 +From: Rui Matos +Date: Tue, 10 Oct 2017 19:07:27 +0200 +Subject: [PATCH 3/3] backends/x11: Support plain old X device configuration + +We re-use part of the code added to support synaptics and add a few +bits specific for xorg-x11-drv-evdev devices. +--- + src/backends/x11/meta-input-settings-x11.c | 97 +++++++++++++++++----- + 1 file changed, 74 insertions(+), 23 deletions(-) + +diff --git a/src/backends/x11/meta-input-settings-x11.c b/src/backends/x11/meta-input-settings-x11.c +index 743f4a7ce..85f24e92b 100644 +--- a/src/backends/x11/meta-input-settings-x11.c ++++ b/src/backends/x11/meta-input-settings-x11.c +@@ -182,35 +182,35 @@ is_device_synaptics (ClutterInputDevice *device) + return TRUE; + } + ++static gboolean ++is_device_libinput (ClutterInputDevice *device) ++{ ++ guchar *has_setting; ++ ++ /* We just need looking for a synaptics-specific property */ ++ has_setting = get_property (device, "libinput Send Events Modes Available", XA_INTEGER, 8, 2); ++ if (!has_setting) ++ return FALSE; ++ ++ meta_XFree (has_setting); ++ return TRUE; ++} ++ + static void +-change_synaptics_tap_left_handed (ClutterInputDevice *device, +- gboolean tap_enabled, +- gboolean left_handed) ++change_x_device_left_handed (ClutterInputDevice *device, ++ gboolean left_handed) + { + MetaDisplay *display = meta_get_display (); + MetaBackend *backend = meta_get_backend (); + Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); + XDevice *xdevice; +- guchar *tap_action, *buttons; ++ guchar *buttons; + guint buttons_capacity = 16, n_buttons; + + xdevice = XOpenDevice(xdisplay, clutter_input_device_get_device_id (device)); + if (!xdevice) + return; + +- tap_action = get_property (device, "Synaptics Tap Action", +- XA_INTEGER, 8, 7); +- if (!tap_action) +- goto out; +- +- tap_action[4] = tap_enabled ? (left_handed ? 3 : 1) : 0; +- tap_action[5] = tap_enabled ? (left_handed ? 1 : 3) : 0; +- tap_action[6] = tap_enabled ? 2 : 0; +- +- change_property (device, "Synaptics Tap Action", +- XA_INTEGER, 8, tap_action, 7); +- meta_XFree (tap_action); +- + clutter_x11_trap_x_errors(); + buttons = g_new (guchar, buttons_capacity); + n_buttons = XGetDeviceButtonMapping (xdisplay, xdevice, +@@ -233,17 +233,39 @@ change_synaptics_tap_left_handed (ClutterInputDevice *device, + + if (clutter_x11_untrap_x_errors()) + { +- g_warning ("Could not set synaptics touchpad left-handed for %s", ++ g_warning ("Could not set left-handed for %s", + clutter_input_device_get_device_name (device)); + } + +- out: + XCloseDevice (xdisplay, xdevice); + } + + static void +-change_synaptics_speed (ClutterInputDevice *device, +- gdouble speed) ++change_synaptics_tap_left_handed (ClutterInputDevice *device, ++ gboolean tap_enabled, ++ gboolean left_handed) ++{ ++ guchar *tap_action; ++ ++ tap_action = get_property (device, "Synaptics Tap Action", ++ XA_INTEGER, 8, 7); ++ if (!tap_action) ++ return; ++ ++ tap_action[4] = tap_enabled ? (left_handed ? 3 : 1) : 0; ++ tap_action[5] = tap_enabled ? (left_handed ? 1 : 3) : 0; ++ tap_action[6] = tap_enabled ? 2 : 0; ++ ++ change_property (device, "Synaptics Tap Action", ++ XA_INTEGER, 8, tap_action, 7); ++ meta_XFree (tap_action); ++ ++ change_x_device_left_handed (device, left_handed); ++} ++ ++static void ++change_x_device_speed (ClutterInputDevice *device, ++ gdouble speed) + { + MetaDisplay *display = meta_get_display (); + MetaBackend *backend = meta_get_backend (); +@@ -338,6 +360,23 @@ change_synaptics_speed (ClutterInputDevice *device, + XCloseDevice (xdisplay, xdevice); + } + ++static void ++change_x_device_scroll_button (ClutterInputDevice *device, ++ guint button) ++{ ++ guchar value; ++ ++ value = button > 0 ? 1 : 0; ++ change_property (device, "Evdev Wheel Emulation", ++ XA_INTEGER, 8, &value, 1); ++ if (button > 0) ++ { ++ value = button; ++ change_property (device, "Evdev Wheel Emulation Button", ++ XA_INTEGER, 8, &value, 1); ++ } ++} ++ + /* Ensure that syndaemon dies together with us, to avoid running several of + * them */ + static void +@@ -506,9 +545,10 @@ meta_input_settings_x11_set_speed (MetaInputSettings *settings, + Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); + gfloat value = speed; + +- if (is_device_synaptics (device)) ++ if (is_device_synaptics (device) || ++ !is_device_libinput (device)) + { +- change_synaptics_speed (device, speed); ++ change_x_device_speed (device, speed); + return; + } + +@@ -550,6 +590,11 @@ meta_input_settings_x11_set_left_handed (MetaInputSettings *settings, + g_object_unref (settings); + return; + } ++ else if (!is_device_libinput (device) && device_type != CLUTTER_PAD_DEVICE) ++ { ++ change_x_device_left_handed (device, enabled); ++ return; ++ } + + change_property (device, "libinput Left Handed Enabled", + XA_INTEGER, 8, &value, 1); +@@ -763,6 +808,12 @@ meta_input_settings_x11_set_scroll_button (MetaInputSettings *settings, + ClutterInputDevice *device, + guint button) + { ++ if (!is_device_libinput (device)) ++ { ++ change_x_device_scroll_button (device, button); ++ return; ++ } ++ + change_property (device, "libinput Button Scrolling Button", + XA_INTEGER, 32, &button, 1); + } +-- +2.36.1 + diff --git a/SOURCES/covscan-fixes.patch b/SOURCES/covscan-fixes.patch new file mode 100644 index 0000000..25bfe3c --- /dev/null +++ b/SOURCES/covscan-fixes.patch @@ -0,0 +1,246 @@ +From 55417eea4294210495eceebd6dd4b832f371f054 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= +Date: Sun, 14 Apr 2019 17:15:06 +0200 +Subject: [PATCH 1/5] display: Fix a possible bug in + meta_display_sync_wayland_focus + +The check for the focus xwindow is called, but not used. Fix that by +renaming the variable to reflect better what it does and actually using +the return value of the check. + +This was the original intention of the author in commit +05899596d10918df5359d89baa82e6fedd0ae208 and got broken in commit +8e7e1eeef59c4f74046e6783b6334c1432255c5a. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/535 +--- + src/core/display.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/core/display.c b/src/core/display.c +index 0de99edb2..4c8907f40 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -1208,15 +1208,15 @@ meta_display_sync_wayland_input_focus (MetaDisplay *display) + MetaWindow *focus_window = NULL; + MetaBackend *backend = meta_get_backend (); + MetaStage *stage = META_STAGE (meta_backend_get_stage (backend)); +- gboolean is_focus_xwindow = FALSE; ++ gboolean is_no_focus_xwindow = FALSE; + + if (display->x11_display) +- meta_x11_display_xwindow_is_a_no_focus_window (display->x11_display, +- display->x11_display->focus_xwindow); ++ is_no_focus_xwindow = meta_x11_display_xwindow_is_a_no_focus_window (display->x11_display, ++ display->x11_display->focus_xwindow); + + if (!meta_display_windows_are_interactable (display)) + focus_window = NULL; +- else if (is_focus_xwindow) ++ else if (is_no_focus_xwindow) + focus_window = NULL; + else if (display->focus_window && display->focus_window->surface) + focus_window = display->focus_window; +-- +2.21.0 + + +From 17cc0a2a21c504b8631bf2ce0f508f611f9b1d3e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 27 May 2019 20:03:25 +0000 +Subject: [PATCH 2/5] renderer-x11-nested: Fix copy-and-paste error + +The rounding added in commit c5471e5b8b1 mixed up some variables, +whoops. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/598 +--- + src/backends/x11/nested/meta-renderer-x11-nested.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/backends/x11/nested/meta-renderer-x11-nested.c b/src/backends/x11/nested/meta-renderer-x11-nested.c +index 71a85a8c2..5000bf357 100644 +--- a/src/backends/x11/nested/meta-renderer-x11-nested.c ++++ b/src/backends/x11/nested/meta-renderer-x11-nested.c +@@ -203,7 +203,7 @@ meta_renderer_x11_nested_create_view (MetaRenderer *renderer, + height = logical_monitor->rect.height; + } + width = roundf (width * view_scale); +- height = roundf (width * view_scale); ++ height = roundf (height * view_scale); + + fake_onscreen = create_offscreen (cogl_context, width, height); + +-- +2.21.0 + + +From a58fabbb0e3173359d3374b931815c21ce65032d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 27 May 2019 19:59:53 +0000 +Subject: [PATCH 3/5] input-mapper: Remove unnecessary return value + +Since commit ae6d9e35bd, there is a fallback to META_MATCH_IS_BUILTIN, +so the condition for returning FALSE is never met. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/598 +--- + src/backends/meta-input-mapper.c | 15 +++------------ + 1 file changed, 3 insertions(+), 12 deletions(-) + +diff --git a/src/backends/meta-input-mapper.c b/src/backends/meta-input-mapper.c +index acc9b1618..fc4f3bd59 100644 +--- a/src/backends/meta-input-mapper.c ++++ b/src/backends/meta-input-mapper.c +@@ -353,7 +353,7 @@ find_builtin_output (MetaInputMapper *mapper, + return panel != NULL; + } + +-static gboolean ++static void + guess_candidates (MetaInputMapper *mapper, + MetaMapperInputInfo *input, + DeviceCandidates *info) +@@ -387,15 +387,7 @@ guess_candidates (MetaInputMapper *mapper, + find_builtin_output (mapper, &info->candidates[META_MATCH_IS_BUILTIN]); + } + +- if (best < N_OUTPUT_MATCHES) +- { +- info->best = best; +- return TRUE; +- } +- else +- { +- return FALSE; +- } ++ info->best = best; + } + + static void +@@ -408,8 +400,7 @@ mapping_helper_add (MappingHelper *helper, + + info.input = input; + +- if (!guess_candidates (mapper, input, &info)) +- return; ++ guess_candidates (mapper, input, &info); + + for (i = 0; i < helper->device_maps->len; i++) + { +-- +2.21.0 + + +From 4eb025cf36a9118cc496ae9143ee2eb510b6228c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 27 May 2019 20:22:50 +0000 +Subject: [PATCH 4/5] workspace-manager: Remove unnecessary assignment + +The initialization to -1 is never used, instead the variables are +re-initialized to 0 before the loop that uses them. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/598 +--- + src/core/meta-workspace-manager.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/core/meta-workspace-manager.c b/src/core/meta-workspace-manager.c +index af7344709..8e1f03fe8 100644 +--- a/src/core/meta-workspace-manager.c ++++ b/src/core/meta-workspace-manager.c +@@ -600,8 +600,6 @@ meta_workspace_manager_calc_workspace_layout (MetaWorkspaceManager *workspace_ma + + grid = g_new (int, grid_area); + +- current_row = -1; +- current_col = -1; + i = 0; + + switch (workspace_manager->starting_corner) +-- +2.21.0 + + +From a854a337ac8807f310ac2c474f9be290089f79f3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 27 May 2019 20:43:21 +0000 +Subject: [PATCH 5/5] x11-display: Simplify bell handling + +Since commit 956ab4bd made libcanberra mandatory, we never use +the system bell for handling the `audible-bell` setting. So +instead of reacting to settings changes with the exact same call +to XkbChangeEnabledControls(), just call it once when initializing. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/598 +--- + src/x11/meta-x11-display.c | 39 +++++++------------------------------- + 1 file changed, 7 insertions(+), 32 deletions(-) + +diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c +index 8ce12b994..065ffcdda 100644 +--- a/src/x11/meta-x11-display.c ++++ b/src/x11/meta-x11-display.c +@@ -463,6 +463,13 @@ init_x11_bell (MetaX11Display *x11_display) + &mask); + } + } ++ ++ /* We are playing sounds using libcanberra support, we handle the ++ * bell whether its an audible bell or a visible bell */ ++ XkbChangeEnabledControls (x11_display->xdisplay, ++ XkbUseCoreKbd, ++ XkbAudibleBellMask, ++ 0); + } + + /* +@@ -480,32 +487,6 @@ shutdown_x11_bell (MetaX11Display *x11_display) + XkbAudibleBellMask); + } + +-/* +- * Turns the bell to audible or visual. This tells X what to do, but +- * not Mutter; you will need to set the "visual bell" pref for that. +- */ +-static void +-set_x11_bell_is_audible (MetaX11Display *x11_display, +- gboolean is_audible) +-{ +- /* When we are playing sounds using libcanberra support, we handle the +- * bell whether its an audible bell or a visible bell */ +- gboolean enable_system_bell = FALSE; +- +- XkbChangeEnabledControls (x11_display->xdisplay, +- XkbUseCoreKbd, +- XkbAudibleBellMask, +- enable_system_bell ? XkbAudibleBellMask : 0); +-} +- +-static void +-on_is_audible_changed (MetaBell *bell, +- gboolean is_audible, +- MetaX11Display *x11_display) +-{ +- set_x11_bell_is_audible (x11_display, is_audible); +-} +- + static void + set_desktop_geometry_hint (MetaX11Display *x11_display) + { +@@ -1320,12 +1301,6 @@ meta_x11_display_new (MetaDisplay *display, GError **error) + + init_x11_bell (x11_display); + +- g_signal_connect_object (display->bell, "is-audible-changed", +- G_CALLBACK (on_is_audible_changed), +- x11_display, 0); +- +- set_x11_bell_is_audible (x11_display, meta_prefs_bell_is_audible ()); +- + meta_x11_startup_notification_init (x11_display); + + return x11_display; +-- +2.21.0 + diff --git a/SOURCES/cursor-move-only-screen-cast-fixes.patch b/SOURCES/cursor-move-only-screen-cast-fixes.patch new file mode 100644 index 0000000..03d9814 --- /dev/null +++ b/SOURCES/cursor-move-only-screen-cast-fixes.patch @@ -0,0 +1,1115 @@ +From 30caca0cb389dcbbab3d7ba72b92fce8e243b30b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 16:42:45 +0200 +Subject: [PATCH 1/9] screen-cast-src: Make the two record vfuncs more + similarly named + +Both do more or less the same but with different methods - one puts +pixels into a buffer using the CPU, the other puts pixels into a buffer +using the GPU. + +However, they are behaving slightly different, which they shouldn't. +Lets first address the misleading disconnect in naming, and later we'll +make them behave more similarly. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit d07335cd4ca094d790eac75e75cff01a28fda827) +--- + .../meta-screen-cast-monitor-stream-src.c | 15 ++++++++------- + src/backends/meta-screen-cast-stream-src.c | 17 +++++++++-------- + src/backends/meta-screen-cast-stream-src.h | 8 ++++---- + .../meta-screen-cast-window-stream-src.c | 15 ++++++++------- + 4 files changed, 29 insertions(+), 26 deletions(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index 655b682610..a1a98eb05b 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -347,8 +347,8 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src) + } + + static gboolean +-meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data) + { + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); +@@ -368,8 +368,8 @@ meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src, + } + + static gboolean +-meta_screen_cast_monitor_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer) + { + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); +@@ -551,9 +551,10 @@ meta_screen_cast_monitor_stream_src_class_init (MetaScreenCastMonitorStreamSrcCl + src_class->get_specs = meta_screen_cast_monitor_stream_src_get_specs; + src_class->enable = meta_screen_cast_monitor_stream_src_enable; + src_class->disable = meta_screen_cast_monitor_stream_src_disable; +- src_class->record_frame = meta_screen_cast_monitor_stream_src_record_frame; +- src_class->blit_to_framebuffer = +- meta_screen_cast_monitor_stream_src_blit_to_framebuffer; ++ src_class->record_to_buffer = ++ meta_screen_cast_monitor_stream_src_record_to_buffer; ++ src_class->record_to_framebuffer = ++ meta_screen_cast_monitor_stream_src_record_to_framebuffer; + src_class->set_cursor_metadata = + meta_screen_cast_monitor_stream_src_set_cursor_metadata; + } +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index b77186415f..bafb82388d 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -133,23 +133,23 @@ meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src, + } + + static gboolean +-meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data) + { + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + +- return klass->record_frame (src, data); ++ return klass->record_to_buffer (src, data); + } + + static gboolean +-meta_screen_cast_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer) + { + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + +- return klass->blit_to_framebuffer (src, framebuffer); ++ return klass->record_to_framebuffer (src, framebuffer); + } + + static void +@@ -417,7 +417,7 @@ do_record_frame (MetaScreenCastStreamSrc *src, + if (spa_buffer->datas[0].data || + spa_buffer->datas[0].type == SPA_DATA_MemFd) + { +- return meta_screen_cast_stream_src_record_frame (src, data); ++ return meta_screen_cast_stream_src_record_to_buffer (src, data); + } + else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) + { +@@ -427,7 +427,8 @@ do_record_frame (MetaScreenCastStreamSrc *src, + CoglFramebuffer *dmabuf_fbo = + cogl_dma_buf_handle_get_framebuffer (dmabuf_handle); + +- return meta_screen_cast_stream_src_blit_to_framebuffer (src, dmabuf_fbo); ++ return meta_screen_cast_stream_src_record_to_framebuffer (src, ++ dmabuf_fbo); + } + + return FALSE; +diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h +index 3f6a1af2bb..0eda02f717 100644 +--- a/src/backends/meta-screen-cast-stream-src.h ++++ b/src/backends/meta-screen-cast-stream-src.h +@@ -53,10 +53,10 @@ struct _MetaScreenCastStreamSrcClass + float *frame_rate); + void (* enable) (MetaScreenCastStreamSrc *src); + void (* disable) (MetaScreenCastStreamSrc *src); +- gboolean (* record_frame) (MetaScreenCastStreamSrc *src, +- uint8_t *data); +- gboolean (* blit_to_framebuffer) (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer); ++ gboolean (* record_to_buffer) (MetaScreenCastStreamSrc *src, ++ uint8_t *data); ++ gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer); + gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src, + MetaRectangle *crop_rect); + void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src, +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index c252b4356b..281df5e7b2 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -462,8 +462,8 @@ meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src) + } + + static gboolean +-meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data) + { + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); +@@ -474,8 +474,8 @@ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src, + } + + static gboolean +-meta_screen_cast_window_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer) + { + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); +@@ -591,9 +591,10 @@ meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClas + src_class->get_specs = meta_screen_cast_window_stream_src_get_specs; + src_class->enable = meta_screen_cast_window_stream_src_enable; + src_class->disable = meta_screen_cast_window_stream_src_disable; +- src_class->record_frame = meta_screen_cast_window_stream_src_record_frame; +- src_class->blit_to_framebuffer = +- meta_screen_cast_window_stream_src_blit_to_framebuffer; ++ src_class->record_to_buffer = ++ meta_screen_cast_window_stream_src_record_to_buffer; ++ src_class->record_to_framebuffer = ++ meta_screen_cast_window_stream_src_record_to_framebuffer; + src_class->get_videocrop = meta_screen_cast_window_stream_src_get_videocrop; + src_class->set_cursor_metadata = meta_screen_cast_window_stream_src_set_cursor_metadata; + } +-- +2.26.2 + + +From ddc2094222fb55143922364cd4887cb18f856628 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 16:46:44 +0200 +Subject: [PATCH 2/9] screen-cast/window-stream-src: Fix indentation + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit b1d45820efc5c9136f12d8a3b97a573a2eede9e7) +--- + src/backends/meta-screen-cast-window-stream-src.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index 281df5e7b2..abdc791575 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -488,8 +488,8 @@ meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSr + stream_rect.height = get_stream_height (window_src); + + if (!meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window, +- &stream_rect, +- framebuffer)) ++ &stream_rect, ++ framebuffer)) + return FALSE; + + stream = meta_screen_cast_stream_src_get_stream (src); +-- +2.26.2 + + +From 59382848840aeb5c6491412742f474a3fb61639e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 16:48:52 +0200 +Subject: [PATCH 3/9] screen-cast/src: Add flag to maybe_record() + +Will later be used to make recording avoid recording actual pixel +content if e.g. only the cursor moved. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit 92db8902d9c3229a13d104bba71dd74f14d6dfac) +--- + src/backends/meta-screen-cast-monitor-stream-src.c | 8 ++++++-- + src/backends/meta-screen-cast-stream-src.c | 3 ++- + src/backends/meta-screen-cast-stream-src.h | 8 +++++++- + src/backends/meta-screen-cast-window-stream-src.c | 12 +++++++++--- + 4 files changed, 24 insertions(+), 7 deletions(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index a1a98eb05b..8d57fafc0f 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -120,8 +120,10 @@ stage_painted (MetaStage *stage, + gpointer user_data) + { + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (user_data); ++ MetaScreenCastRecordFlag flags; + +- meta_screen_cast_stream_src_maybe_record_frame (src); ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + + static MetaBackend * +@@ -180,6 +182,7 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src) + { + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src); + ClutterStage *stage = get_stage (monitor_src); ++ MetaScreenCastRecordFlag flags; + + if (!is_cursor_in_stream (monitor_src)) + return; +@@ -187,7 +190,8 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src) + if (clutter_stage_is_redraw_queued (stage)) + return; + +- meta_screen_cast_stream_src_maybe_record_frame (src); ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + + static void +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index bafb82388d..303c030be7 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -435,7 +435,8 @@ do_record_frame (MetaScreenCastStreamSrc *src, + } + + void +-meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) ++meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, ++ MetaScreenCastRecordFlag flags) + { + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); +diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h +index 0eda02f717..6c73d05c1d 100644 +--- a/src/backends/meta-screen-cast-stream-src.h ++++ b/src/backends/meta-screen-cast-stream-src.h +@@ -37,6 +37,11 @@ + + typedef struct _MetaScreenCastStream MetaScreenCastStream; + ++typedef enum _MetaScreenCastRecordFlag ++{ ++ META_SCREEN_CAST_RECORD_FLAG_NONE = 0, ++} MetaScreenCastRecordFlag; ++ + #define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ()) + G_DECLARE_DERIVABLE_TYPE (MetaScreenCastStreamSrc, + meta_screen_cast_stream_src, +@@ -63,7 +68,8 @@ struct _MetaScreenCastStreamSrcClass + struct spa_meta_cursor *spa_meta_cursor); + }; + +-void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src); ++void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, ++ MetaScreenCastRecordFlag flags); + + MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src); + +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index abdc791575..f64d00860a 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -338,8 +338,10 @@ screen_cast_window_damaged (MetaWindowActor *actor, + MetaScreenCastWindowStreamSrc *window_src) + { + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); ++ MetaScreenCastRecordFlag flags; + +- meta_screen_cast_stream_src_maybe_record_frame (src); ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + + static void +@@ -376,6 +378,7 @@ static void + sync_cursor_state (MetaScreenCastWindowStreamSrc *window_src) + { + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); ++ MetaScreenCastRecordFlag flags; + + if (!is_cursor_in_stream (window_src)) + return; +@@ -383,7 +386,8 @@ sync_cursor_state (MetaScreenCastWindowStreamSrc *window_src) + if (meta_screen_cast_window_has_damage (window_src->screen_cast_window)) + return; + +- meta_screen_cast_stream_src_maybe_record_frame (src); ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + + static void +@@ -412,6 +416,7 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src) + MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); + MetaWindowActor *window_actor; + MetaScreenCastStream *stream; ++ MetaScreenCastRecordFlag flags; + + window_actor = meta_window_actor_from_window (get_window (window_src)); + if (!window_actor) +@@ -449,7 +454,8 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src) + break; + } + +- meta_screen_cast_stream_src_maybe_record_frame (src); ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + + static void +-- +2.26.2 + + +From b8d76f2ded6a0c8b88403d97d4ea2c84993c0263 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 16:52:43 +0200 +Subject: [PATCH 4/9] screen-cast: Let the reason for recording determine what + to record + +E.g. we'll have pointer movement that, if no painting is already +scheduled, should only send new cursor metadata without any new pixel +buffer. When this happens, tell next step to not record the pixels if +this was the case, instead of having it rediscover this itself. + +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1323 +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit cf88d648822eb6b7d412c08d4038c657d415bfff) +--- + .../meta-screen-cast-monitor-stream-src.c | 5 +- + src/backends/meta-screen-cast-stream-src.c | 51 +++++++++++-------- + src/backends/meta-screen-cast-stream-src.h | 1 + + .../meta-screen-cast-window-stream-src.c | 4 +- + 4 files changed, 33 insertions(+), 28 deletions(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index 8d57fafc0f..2352c3b3d8 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -190,7 +190,7 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src) + if (clutter_stage_is_redraw_queued (stage)) + return; + +- flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY; + meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + +@@ -361,9 +361,6 @@ meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *s + MetaLogicalMonitor *logical_monitor; + + stage = get_stage (monitor_src); +- if (!clutter_stage_is_redraw_queued (stage)) +- return FALSE; +- + monitor = get_monitor (monitor_src); + logical_monitor = meta_monitor_get_logical_monitor (monitor); + clutter_stage_capture_into (stage, FALSE, &logical_monitor->rect, data); +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 303c030be7..aa4b03b180 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -470,34 +470,41 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + return; + } + +- if (do_record_frame (src, spa_buffer, data)) ++ if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY)) + { +- struct spa_meta_region *spa_meta_video_crop; ++ if (do_record_frame (src, spa_buffer, data)) ++ { ++ struct spa_meta_region *spa_meta_video_crop; + +- spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize; +- spa_buffer->datas[0].chunk->stride = priv->video_stride; ++ spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize; ++ spa_buffer->datas[0].chunk->stride = priv->video_stride; + +- /* Update VideoCrop if needed */ +- spa_meta_video_crop = +- spa_buffer_find_meta_data (spa_buffer, SPA_META_VideoCrop, +- sizeof (*spa_meta_video_crop)); +- if (spa_meta_video_crop) +- { +- if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect)) +- { +- spa_meta_video_crop->region.position.x = crop_rect.x; +- spa_meta_video_crop->region.position.y = crop_rect.y; +- spa_meta_video_crop->region.size.width = crop_rect.width; +- spa_meta_video_crop->region.size.height = crop_rect.height; +- } +- else ++ /* Update VideoCrop if needed */ ++ spa_meta_video_crop = ++ spa_buffer_find_meta_data (spa_buffer, SPA_META_VideoCrop, ++ sizeof (*spa_meta_video_crop)); ++ if (spa_meta_video_crop) + { +- spa_meta_video_crop->region.position.x = 0; +- spa_meta_video_crop->region.position.y = 0; +- spa_meta_video_crop->region.size.width = priv->stream_width; +- spa_meta_video_crop->region.size.height = priv->stream_height; ++ if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect)) ++ { ++ spa_meta_video_crop->region.position.x = crop_rect.x; ++ spa_meta_video_crop->region.position.y = crop_rect.y; ++ spa_meta_video_crop->region.size.width = crop_rect.width; ++ spa_meta_video_crop->region.size.height = crop_rect.height; ++ } ++ else ++ { ++ spa_meta_video_crop->region.position.x = 0; ++ spa_meta_video_crop->region.position.y = 0; ++ spa_meta_video_crop->region.size.width = priv->stream_width; ++ spa_meta_video_crop->region.size.height = priv->stream_height; ++ } + } + } ++ else ++ { ++ spa_buffer->datas[0].chunk->size = 0; ++ } + } + else + { +diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h +index 6c73d05c1d..87054eedf5 100644 +--- a/src/backends/meta-screen-cast-stream-src.h ++++ b/src/backends/meta-screen-cast-stream-src.h +@@ -40,6 +40,7 @@ typedef struct _MetaScreenCastStream MetaScreenCastStream; + typedef enum _MetaScreenCastRecordFlag + { + META_SCREEN_CAST_RECORD_FLAG_NONE = 0, ++ META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY = 1 << 0, + } MetaScreenCastRecordFlag; + + #define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ()) +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index f64d00860a..63c3429df0 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -340,7 +340,7 @@ screen_cast_window_damaged (MetaWindowActor *actor, + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); + MetaScreenCastRecordFlag flags; + +- flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY; + meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + +@@ -386,7 +386,7 @@ sync_cursor_state (MetaScreenCastWindowStreamSrc *window_src) + if (meta_screen_cast_window_has_damage (window_src->screen_cast_window)) + return; + +- flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY; + meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } + +-- +2.26.2 + + +From 5c925cf4de91c9fdd44cb1c13748b2f4d6187dd9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 16:57:01 +0200 +Subject: [PATCH 5/9] screen-cast/src: Make record functions return an error + when failing + +Now that we don't use the record function to early out depending on +implicit state (don't record pixels if only cursor moved for example), +let it simply report an error when it fails, as we should no longer ever +return without pixels if nothing failed. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit 2d899596e21d43ab241d0ba37c0a9f90c2e610be) +--- + .../meta-screen-cast-monitor-stream-src.c | 19 +++++------ + src/backends/meta-screen-cast-stream-src.c | 32 ++++++++++++------- + src/backends/meta-screen-cast-stream-src.h | 10 +++--- + .../meta-screen-cast-window-stream-src.c | 16 +++++++--- + 4 files changed, 45 insertions(+), 32 deletions(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index 2352c3b3d8..27b3ea37d8 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -351,8 +351,9 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src) + } + + static gboolean +-meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data, ++ GError **error) + { + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); +@@ -369,8 +370,9 @@ meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *s + } + + static gboolean +-meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer, ++ GError **error) + { + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); +@@ -394,7 +396,6 @@ meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamS + for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next) + { + ClutterStageView *view = CLUTTER_STAGE_VIEW (l->data); +- g_autoptr (GError) error = NULL; + CoglFramebuffer *view_framebuffer; + MetaRectangle view_layout; + int x, y; +@@ -415,12 +416,8 @@ meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamS + x, y, + cogl_framebuffer_get_width (view_framebuffer), + cogl_framebuffer_get_height (view_framebuffer), +- &error)) +- { +- g_warning ("Error blitting view into DMABuf framebuffer: %s", +- error->message); +- return FALSE; +- } ++ error)) ++ return FALSE; + } + + cogl_framebuffer_finish (framebuffer); +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index aa4b03b180..b930d5e7c0 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -133,23 +133,25 @@ meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src, + } + + static gboolean +-meta_screen_cast_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data, ++ GError **error) + { + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + +- return klass->record_to_buffer (src, data); ++ return klass->record_to_buffer (src, data, error); + } + + static gboolean +-meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer, ++ GError **error) + { + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + +- return klass->record_to_framebuffer (src, framebuffer); ++ return klass->record_to_framebuffer (src, framebuffer, error); + } + + static void +@@ -407,9 +409,10 @@ maybe_record_cursor (MetaScreenCastStreamSrc *src, + } + + static gboolean +-do_record_frame (MetaScreenCastStreamSrc *src, +- struct spa_buffer *spa_buffer, +- uint8_t *data) ++do_record_frame (MetaScreenCastStreamSrc *src, ++ struct spa_buffer *spa_buffer, ++ uint8_t *data, ++ GError **error) + { + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); +@@ -417,7 +420,7 @@ do_record_frame (MetaScreenCastStreamSrc *src, + if (spa_buffer->datas[0].data || + spa_buffer->datas[0].type == SPA_DATA_MemFd) + { +- return meta_screen_cast_stream_src_record_to_buffer (src, data); ++ return meta_screen_cast_stream_src_record_to_buffer (src, data, error); + } + else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) + { +@@ -428,9 +431,12 @@ do_record_frame (MetaScreenCastStreamSrc *src, + cogl_dma_buf_handle_get_framebuffer (dmabuf_handle); + + return meta_screen_cast_stream_src_record_to_framebuffer (src, +- dmabuf_fbo); ++ dmabuf_fbo, ++ error); + } + ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Unknown SPA buffer type %u", spa_buffer->datas[0].type); + return FALSE; + } + +@@ -445,6 +451,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + struct spa_buffer *spa_buffer; + uint8_t *data = NULL; + uint64_t now_us; ++ g_autoptr (GError) error = NULL; + + now_us = g_get_monotonic_time (); + if (priv->video_format.max_framerate.num > 0 && +@@ -472,7 +479,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + + if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY)) + { +- if (do_record_frame (src, spa_buffer, data)) ++ if (do_record_frame (src, spa_buffer, data, &error)) + { + struct spa_meta_region *spa_meta_video_crop; + +@@ -503,6 +510,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + } + else + { ++ g_warning ("Failed to record screen cast frame: %s", error->message); + spa_buffer->datas[0].chunk->size = 0; + } + } +diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h +index 87054eedf5..152790ecfb 100644 +--- a/src/backends/meta-screen-cast-stream-src.h ++++ b/src/backends/meta-screen-cast-stream-src.h +@@ -59,10 +59,12 @@ struct _MetaScreenCastStreamSrcClass + float *frame_rate); + void (* enable) (MetaScreenCastStreamSrc *src); + void (* disable) (MetaScreenCastStreamSrc *src); +- gboolean (* record_to_buffer) (MetaScreenCastStreamSrc *src, +- uint8_t *data); +- gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer); ++ gboolean (* record_to_buffer) (MetaScreenCastStreamSrc *src, ++ uint8_t *data, ++ GError **error); ++ gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer, ++ GError **error); + gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src, + MetaRectangle *crop_rect); + void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src, +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index 63c3429df0..70e868997e 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -468,8 +468,9 @@ meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src) + } + + static gboolean +-meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, +- uint8_t *data) ++meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, ++ uint8_t *data, ++ GError **error) + { + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); +@@ -480,8 +481,9 @@ meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *sr + } + + static gboolean +-meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, +- CoglFramebuffer *framebuffer) ++meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer, ++ GError **error) + { + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); +@@ -496,7 +498,11 @@ meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSr + if (!meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window, + &stream_rect, + framebuffer)) +- return FALSE; ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to blit window content to framebuffer"); ++ return FALSE; ++ } + + stream = meta_screen_cast_stream_src_get_stream (src); + switch (meta_screen_cast_stream_get_cursor_mode (stream)) +-- +2.26.2 + + +From 47b03793413aad449f021f64e66c68ca95be9a0f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 23:50:28 +0200 +Subject: [PATCH 6/9] screen-cast/src: Fix signedness of timestamp field + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit 449fa7bf81fe0bee63f497d896cbeffe84dca82d) +--- + src/backends/meta-screen-cast-stream-src.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index b930d5e7c0..f6f66daaa3 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -89,7 +89,7 @@ typedef struct _MetaScreenCastStreamSrcPrivate + struct spa_video_info_raw video_format; + int video_stride; + +- uint64_t last_frame_timestamp_us; ++ int64_t last_frame_timestamp_us; + + GHashTable *dmabuf_handles; + +-- +2.26.2 + + +From d8c8ea23e700f85a55a5cc0b79151fbed75ab191 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 23:57:31 +0200 +Subject: [PATCH 7/9] screen-cast/src: Record follow up frame after timeout + +During animation or other things that cause multiple frames in a row +being painted, we might skip recording frames if the max framerate is +reached. + +Doing so means we might end up skipping the last frame in a series, +ending with the last frame we sent was not the last one, making things +appear to get stuck sometimes. + +Handle this by creating a timeout if we ever throttle, and at the time +the timeout callback is triggered, make sure we eventually send an up to +date frame. + +This is handle differently depending on the source type. A monitor +source type reports 1x1 pixel damage on each view its monitor overlaps, +while a window source type simply records a frame from the surface +directly, except without recording a timestamp, so that timestamps +always refer to when damage actually happened. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit e8052f169b957a502bf86ca65071582692039b9c) +--- + .../meta-screen-cast-monitor-stream-src.c | 43 +++++++++++ + src/backends/meta-screen-cast-stream-src.c | 77 +++++++++++++++++-- + src/backends/meta-screen-cast-stream-src.h | 4 + + .../meta-screen-cast-window-stream-src.c | 11 +++ + 4 files changed, 130 insertions(+), 5 deletions(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index 27b3ea37d8..3079578d8d 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -190,6 +190,9 @@ sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src) + if (clutter_stage_is_redraw_queued (stage)) + return; + ++ if (meta_screen_cast_stream_src_pending_follow_up_frame (src)) ++ return; ++ + flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY; + meta_screen_cast_stream_src_maybe_record_frame (src, flags); + } +@@ -425,6 +428,44 @@ meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamS + return TRUE; + } + ++static void ++meta_screen_cast_monitor_stream_record_follow_up (MetaScreenCastStreamSrc *src) ++{ ++ MetaScreenCastMonitorStreamSrc *monitor_src = ++ META_SCREEN_CAST_MONITOR_STREAM_SRC (src); ++ MetaBackend *backend = get_backend (monitor_src); ++ MetaRenderer *renderer = meta_backend_get_renderer (backend); ++ ClutterStage *stage = get_stage (monitor_src); ++ MetaMonitor *monitor; ++ MetaLogicalMonitor *logical_monitor; ++ MetaRectangle logical_monitor_layout; ++ GList *l; ++ ++ monitor = get_monitor (monitor_src); ++ logical_monitor = meta_monitor_get_logical_monitor (monitor); ++ logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); ++ ++ for (l = meta_renderer_get_views (renderer); l; l = l->next) ++ { ++ MetaRendererView *view = l->data; ++ MetaRectangle view_layout; ++ MetaRectangle damage; ++ ++ clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (view), &view_layout); ++ ++ if (!meta_rectangle_overlap (&logical_monitor_layout, &view_layout)) ++ continue; ++ ++ damage = (cairo_rectangle_int_t) { ++ .x = view_layout.x, ++ .y = view_layout.y, ++ .width = 1, ++ .height = 1, ++ }; ++ clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &damage); ++ } ++} ++ + static void + meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor) +@@ -553,6 +594,8 @@ meta_screen_cast_monitor_stream_src_class_init (MetaScreenCastMonitorStreamSrcCl + meta_screen_cast_monitor_stream_src_record_to_buffer; + src_class->record_to_framebuffer = + meta_screen_cast_monitor_stream_src_record_to_framebuffer; ++ src_class->record_follow_up = ++ meta_screen_cast_monitor_stream_record_follow_up; + src_class->set_cursor_metadata = + meta_screen_cast_monitor_stream_src_set_cursor_metadata; + } +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index f6f66daaa3..55af56f8b9 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -90,6 +90,7 @@ typedef struct _MetaScreenCastStreamSrcPrivate + int video_stride; + + int64_t last_frame_timestamp_us; ++ guint follow_up_frame_source_id; + + GHashTable *dmabuf_handles; + +@@ -107,6 +108,12 @@ G_DEFINE_TYPE_WITH_CODE (MetaScreenCastStreamSrc, + meta_screen_cast_stream_src_init_initable_iface) + G_ADD_PRIVATE (MetaScreenCastStreamSrc)) + ++static inline uint32_t ++us2ms (uint64_t us) ++{ ++ return (uint32_t) (us / 1000); ++} ++ + static void + meta_screen_cast_stream_src_get_specs (MetaScreenCastStreamSrc *src, + int *width, +@@ -154,6 +161,15 @@ meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src + return klass->record_to_framebuffer (src, framebuffer, error); + } + ++static void ++meta_screen_cast_stream_src_record_follow_up (MetaScreenCastStreamSrc *src) ++{ ++ MetaScreenCastStreamSrcClass *klass = ++ META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); ++ ++ klass->record_follow_up (src); ++} ++ + static void + meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor) +@@ -440,6 +456,43 @@ do_record_frame (MetaScreenCastStreamSrc *src, + return FALSE; + } + ++gboolean ++meta_screen_cast_stream_src_pending_follow_up_frame (MetaScreenCastStreamSrc *src) ++{ ++ MetaScreenCastStreamSrcPrivate *priv = ++ meta_screen_cast_stream_src_get_instance_private (src); ++ ++ return priv->follow_up_frame_source_id != 0; ++} ++ ++static gboolean ++follow_up_frame_cb (gpointer user_data) ++{ ++ MetaScreenCastStreamSrc *src = user_data; ++ MetaScreenCastStreamSrcPrivate *priv = ++ meta_screen_cast_stream_src_get_instance_private (src); ++ ++ priv->follow_up_frame_source_id = 0; ++ meta_screen_cast_stream_src_record_follow_up (src); ++ ++ return G_SOURCE_REMOVE; ++} ++ ++static void ++maybe_schedule_follow_up_frame (MetaScreenCastStreamSrc *src, ++ int64_t timeout_us) ++{ ++ MetaScreenCastStreamSrcPrivate *priv = ++ meta_screen_cast_stream_src_get_instance_private (src); ++ ++ if (priv->follow_up_frame_source_id) ++ return; ++ ++ priv->follow_up_frame_source_id = g_timeout_add (us2ms (timeout_us), ++ follow_up_frame_cb, ++ src); ++} ++ + void + meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + MetaScreenCastRecordFlag flags) +@@ -455,11 +508,24 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + + now_us = g_get_monotonic_time (); + if (priv->video_format.max_framerate.num > 0 && +- priv->last_frame_timestamp_us != 0 && +- (now_us - priv->last_frame_timestamp_us < +- ((1000000 * priv->video_format.max_framerate.denom) / +- priv->video_format.max_framerate.num))) +- return; ++ priv->last_frame_timestamp_us != 0) ++ { ++ int64_t min_interval_us; ++ int64_t time_since_last_frame_us; ++ ++ min_interval_us = ((1000000 * priv->video_format.max_framerate.denom) / ++ priv->video_format.max_framerate.num); ++ ++ time_since_last_frame_us = now_us - priv->last_frame_timestamp_us; ++ if (time_since_last_frame_us < min_interval_us) ++ { ++ int64_t timeout_us; ++ ++ timeout_us = min_interval_us - time_since_last_frame_us; ++ maybe_schedule_follow_up_frame (src, timeout_us); ++ return; ++ } ++ } + + if (!priv->pipewire_stream) + return; +@@ -479,6 +545,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + + if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY)) + { ++ g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove); + if (do_record_frame (src, spa_buffer, data, &error)) + { + struct spa_meta_region *spa_meta_video_crop; +diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h +index 152790ecfb..81ea20b173 100644 +--- a/src/backends/meta-screen-cast-stream-src.h ++++ b/src/backends/meta-screen-cast-stream-src.h +@@ -65,6 +65,8 @@ struct _MetaScreenCastStreamSrcClass + gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc *src, + CoglFramebuffer *framebuffer, + GError **error); ++ void (* record_follow_up) (MetaScreenCastStreamSrc *src); ++ + gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src, + MetaRectangle *crop_rect); + void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src, +@@ -74,6 +76,8 @@ struct _MetaScreenCastStreamSrcClass + void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + MetaScreenCastRecordFlag flags); + ++gboolean meta_screen_cast_stream_src_pending_follow_up_frame (MetaScreenCastStreamSrc *src); ++ + MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src); + + gboolean meta_screen_cast_stream_src_draw_cursor_into (MetaScreenCastStreamSrc *src, +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index 70e868997e..7026ec3b4f 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -520,6 +520,15 @@ meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSr + return TRUE; + } + ++static void ++meta_screen_cast_window_stream_record_follow_up (MetaScreenCastStreamSrc *src) ++{ ++ MetaScreenCastRecordFlag flags; ++ ++ flags = META_SCREEN_CAST_RECORD_FLAG_NONE; ++ meta_screen_cast_stream_src_maybe_record_frame (src, flags); ++} ++ + static void + meta_screen_cast_window_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor) +@@ -607,6 +616,8 @@ meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClas + meta_screen_cast_window_stream_src_record_to_buffer; + src_class->record_to_framebuffer = + meta_screen_cast_window_stream_src_record_to_framebuffer; ++ src_class->record_follow_up = ++ meta_screen_cast_window_stream_record_follow_up; + src_class->get_videocrop = meta_screen_cast_window_stream_src_get_videocrop; + src_class->set_cursor_metadata = meta_screen_cast_window_stream_src_set_cursor_metadata; + } +-- +2.26.2 + + +From f2babf5129df9e948f471e3d464162888a99201d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 8 Jul 2020 15:08:23 +0200 +Subject: [PATCH 8/9] screen-cast/src: Use G_USEC_PER_SEC instead of 1000000 + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1351 +(cherry picked from commit 0c6ac287e6da91ba76bf3958befef4bec6ed28f6) +--- + src/backends/meta-screen-cast-stream-src.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 55af56f8b9..1d6c2b9d08 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -513,8 +513,9 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, + int64_t min_interval_us; + int64_t time_since_last_frame_us; + +- min_interval_us = ((1000000 * priv->video_format.max_framerate.denom) / +- priv->video_format.max_framerate.num); ++ min_interval_us = ++ ((G_USEC_PER_SEC * priv->video_format.max_framerate.denom) / ++ priv->video_format.max_framerate.num); + + time_since_last_frame_us = now_us - priv->last_frame_timestamp_us; + if (time_since_last_frame_us < min_interval_us) +-- +2.26.2 + + +From 950b3ea51391ffcb434f8f5380459174ba4c4853 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 10 Jul 2020 07:06:33 +0000 +Subject: [PATCH 9/9] screen-cast/src: Remove follow up timeout source on + disable + +We failed to remove the timeout source when disabling, meaning that if a +follow up was scheduled, and shortly after we disabled the source, the +timeout would be invoked after the source was freed causing +use-after-free bugs. + +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1337 + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1365 + +(cherry picked from commit d67ba3ea65717ceab3e0c91267191c6ed2aac2c2) +(cherry picked from commit 1fd53c480f9bb58bd4ac0efc2bbce17dfda8645b) +--- + src/backends/meta-screen-cast-stream-src.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 1d6c2b9d08..f39d348baa 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -622,6 +622,8 @@ meta_screen_cast_stream_src_disable (MetaScreenCastStreamSrc *src) + + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src)->disable (src); + ++ g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove); ++ + priv->is_enabled = FALSE; + } + +-- +2.26.2 + diff --git a/SOURCES/deal-more-gracefully-with-oversized-windows.patch b/SOURCES/deal-more-gracefully-with-oversized-windows.patch new file mode 100644 index 0000000..9a7a36d --- /dev/null +++ b/SOURCES/deal-more-gracefully-with-oversized-windows.patch @@ -0,0 +1,85 @@ +From 575490895047e0709bc84826fe6d6a73028d7bbc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 12 Mar 2014 02:04:13 +0100 +Subject: [PATCH] constraints: Enforce X11 size limits + +X11 limits windows to a maximum of 32767x32767, enforce that restriction +to keep insanely huge windows from crashing the WM. +--- + src/core/constraints.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 42 insertions(+) + +diff --git a/src/core/constraints.c b/src/core/constraints.c +index 117131b15..379372245 100644 +--- a/src/core/constraints.c ++++ b/src/core/constraints.c +@@ -109,6 +109,7 @@ typedef enum + PRIORITY_TITLEBAR_VISIBLE = 4, + PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA = 4, + PRIORITY_CUSTOM_RULE = 4, ++ PRIORITY_XLIMITS = 4, + PRIORITY_MAXIMUM = 4 /* Dummy value used for loop end = max(all priorities) */ + } ConstraintPriority; + +@@ -201,6 +202,10 @@ static gboolean constrain_partially_onscreen (MetaWindow *window, + ConstraintInfo *info, + ConstraintPriority priority, + gboolean check_only); ++static gboolean constrain_xlimits (MetaWindow *window, ++ ConstraintInfo *info, ++ ConstraintPriority priority, ++ gboolean check_only); + + static void setup_constraint_info (ConstraintInfo *info, + MetaWindow *window, +@@ -236,6 +241,7 @@ static const Constraint all_constraints[] = { + {constrain_fully_onscreen, "constrain_fully_onscreen"}, + {constrain_titlebar_visible, "constrain_titlebar_visible"}, + {constrain_partially_onscreen, "constrain_partially_onscreen"}, ++ {constrain_xlimits, "constrain_xlimits"}, + {NULL, NULL} + }; + +@@ -1780,3 +1786,39 @@ constrain_partially_onscreen (MetaWindow *window, + + return retval; + } ++ ++ ++#define MAX_WINDOW_SIZE 32767 ++ ++static gboolean ++constrain_xlimits (MetaWindow *window, ++ ConstraintInfo *info, ++ ConstraintPriority priority, ++ gboolean check_only) ++{ ++ int max_w, max_h; ++ gboolean constraint_already_satisfied; ++ ++ if (priority > PRIORITY_XLIMITS) ++ return TRUE; ++ ++ max_w = max_h = MAX_WINDOW_SIZE; ++ ++ if (window->frame) ++ { ++ MetaFrameBorders borders; ++ meta_frame_calc_borders (window->frame, &borders); ++ ++ max_w -= (borders.total.left + borders.total.right); ++ max_h -= (borders.total.top + borders.total.bottom); ++ } ++ ++ constraint_already_satisfied = info->current.width < max_w && info->current.height < max_h; ++ if (check_only || constraint_already_satisfied) ++ return constraint_already_satisfied; ++ ++ info->current.width = MIN (info->current.width, max_w); ++ info->current.height = MIN (info->current.height, max_h); ++ ++ return TRUE; ++} +-- +2.21.0 + diff --git a/SOURCES/eglstream-overview-fixes.patch b/SOURCES/eglstream-overview-fixes.patch new file mode 100644 index 0000000..717944c --- /dev/null +++ b/SOURCES/eglstream-overview-fixes.patch @@ -0,0 +1,333 @@ +From 529eb8fa3a15e0ae5bf131b1855a117c8a1a026e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 8 Feb 2022 17:14:06 +0100 +Subject: [PATCH 1/2] shaped-texture: Pass along the snippet to the texture + tower + +The snippet is used make sure the right source is sampled in the shader. +This wasn't done in the texture tower, meaning the textures from the +tower were not correct. + +Related: https://gitlab.gnome.org/GNOME/mutter/-/issues/528 +--- + src/compositor/meta-shaped-texture.c | 2 ++ + src/compositor/meta-texture-tower.c | 27 +++++++++++++++++++++++++++ + src/compositor/meta-texture-tower.h | 3 +++ + 3 files changed, 32 insertions(+) + +diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c +index 9cae4df07d74..32af6bdc19d7 100644 +--- a/src/compositor/meta-shaped-texture.c ++++ b/src/compositor/meta-shaped-texture.c +@@ -1204,6 +1204,8 @@ meta_shaped_texture_set_snippet (MetaShapedTexture *stex, + g_clear_pointer (&stex->snippet, cogl_object_unref); + if (snippet) + stex->snippet = cogl_object_ref (snippet); ++ ++ meta_texture_tower_set_snippet (stex->paint_tower, snippet); + } + + /** +diff --git a/src/compositor/meta-texture-tower.c b/src/compositor/meta-texture-tower.c +index a41cdc89dd94..374e1af151ad 100644 +--- a/src/compositor/meta-texture-tower.c ++++ b/src/compositor/meta-texture-tower.c +@@ -63,6 +63,7 @@ struct _MetaTextureTower + CoglOffscreen *fbos[MAX_TEXTURE_LEVELS]; + Box invalid[MAX_TEXTURE_LEVELS]; + CoglPipeline *pipeline_template; ++ CoglSnippet *snippet; + }; + + /** +@@ -98,6 +99,7 @@ meta_texture_tower_free (MetaTextureTower *tower) + cogl_object_unref (tower->pipeline_template); + + meta_texture_tower_set_base_texture (tower, NULL); ++ cogl_clear_object (&tower->snippet); + + g_slice_free (MetaTextureTower, tower); + } +@@ -226,6 +228,28 @@ meta_texture_tower_update_area (MetaTextureTower *tower, + } + } + ++void ++meta_texture_tower_set_snippet (MetaTextureTower *tower, ++ CoglSnippet *snippet) ++{ ++ int i; ++ ++ if (tower->snippet == snippet) ++ return; ++ ++ g_clear_pointer (&tower->snippet, cogl_object_unref); ++ ++ if (snippet) ++ tower->snippet = cogl_object_ref (snippet); ++ ++ for (i = 1; i < tower->n_levels; i++) ++ { ++ cogl_clear_object (&tower->textures[i]); ++ g_clear_object (&tower->fbos[i]); ++ } ++ cogl_clear_object (&tower->pipeline_template); ++} ++ + /* It generally looks worse if we scale up a window texture by even a + * small amount than if we scale it down using bilinear filtering, so + * we always pick the *larger* adjacent level. */ +@@ -420,6 +444,9 @@ texture_tower_revalidate (MetaTextureTower *tower, + pipeline = cogl_pipeline_copy (tower->pipeline_template); + cogl_pipeline_set_layer_texture (pipeline, 0, tower->textures[level - 1]); + ++ if (tower->snippet && level == 1) ++ cogl_pipeline_add_layer_snippet (pipeline, 0, tower->snippet); ++ + cogl_framebuffer_draw_textured_rectangle (fb, pipeline, + invalid->x1, invalid->y1, + invalid->x2, invalid->y2, +diff --git a/src/compositor/meta-texture-tower.h b/src/compositor/meta-texture-tower.h +index 6a39e4184200..e3cfe3608b8f 100644 +--- a/src/compositor/meta-texture-tower.h ++++ b/src/compositor/meta-texture-tower.h +@@ -62,6 +62,9 @@ void meta_texture_tower_update_area (MetaTextureTower *tower, + int height); + CoglTexture *meta_texture_tower_get_paint_texture (MetaTextureTower *tower); + ++void meta_texture_tower_set_snippet (MetaTextureTower *tower, ++ CoglSnippet *snippet); ++ + G_END_DECLS + + #endif /* __META_TEXTURE_TOWER_H__ */ +-- +2.34.1 + + +From 4827e201b341ac4dd0b4ca697df46946b19ae14c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 21 Feb 2022 18:12:25 +0100 +Subject: [PATCH 2/2] shaped-texture: Paint with the right layer snippet + +When we get passed a "snippet" to the shaped texture, it's added as a +pipeline layer snippet to change how the source texture is sampled. When +we draw from a texture tower however we have allocated regular textures +which doesn't need any special layer snippet, so create separate +pipelines for those that doesn't use that snippet. + +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/528 +--- + src/compositor/meta-shaped-texture.c | 135 +++++++++++++++++++++------ + 1 file changed, 104 insertions(+), 31 deletions(-) + +diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c +index 32af6bdc19d7..705d27d5b842 100644 +--- a/src/compositor/meta-shaped-texture.c ++++ b/src/compositor/meta-shaped-texture.c +@@ -96,8 +96,12 @@ struct _MetaShapedTexture + CoglSnippet *snippet; + + CoglPipeline *base_pipeline; ++ CoglPipeline *unmasked_pipeline; ++ CoglPipeline *unmasked_tower_pipeline; + CoglPipeline *masked_pipeline; ++ CoglPipeline *masked_tower_pipeline; + CoglPipeline *unblended_pipeline; ++ CoglPipeline *unblended_tower_pipeline; + + gboolean is_y_inverted; + +@@ -281,8 +285,12 @@ static void + meta_shaped_texture_reset_pipelines (MetaShapedTexture *stex) + { + g_clear_pointer (&stex->base_pipeline, cogl_object_unref); ++ g_clear_pointer (&stex->unmasked_pipeline, cogl_object_unref); ++ g_clear_pointer (&stex->unmasked_tower_pipeline, cogl_object_unref); + g_clear_pointer (&stex->masked_pipeline, cogl_object_unref); ++ g_clear_pointer (&stex->masked_tower_pipeline, cogl_object_unref); + g_clear_pointer (&stex->unblended_pipeline, cogl_object_unref); ++ g_clear_pointer (&stex->unblended_tower_pipeline, cogl_object_unref); + } + + static void +@@ -385,9 +393,6 @@ get_base_pipeline (MetaShapedTexture *stex, + cogl_pipeline_set_layer_matrix (pipeline, 1, &matrix); + } + +- if (stex->snippet) +- cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet); +- + stex->base_pipeline = pipeline; + + return stex->base_pipeline; +@@ -395,50 +400,118 @@ get_base_pipeline (MetaShapedTexture *stex, + + static CoglPipeline * + get_unmasked_pipeline (MetaShapedTexture *stex, +- CoglContext *ctx) ++ CoglContext *ctx, ++ CoglTexture *tex) + { +- return get_base_pipeline (stex, ctx); ++ if (stex->texture == tex) ++ { ++ CoglPipeline *pipeline; ++ ++ if (stex->unmasked_pipeline) ++ return stex->unmasked_pipeline; ++ ++ pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); ++ if (stex->snippet) ++ cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet); ++ ++ stex->unmasked_pipeline = pipeline; ++ return pipeline; ++ } ++ else ++ { ++ CoglPipeline *pipeline; ++ ++ if (stex->unmasked_tower_pipeline) ++ return stex->unmasked_tower_pipeline; ++ ++ pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); ++ stex->unmasked_tower_pipeline = pipeline; ++ return pipeline; ++ } + } + + static CoglPipeline * + get_masked_pipeline (MetaShapedTexture *stex, +- CoglContext *ctx) ++ CoglContext *ctx, ++ CoglTexture *tex) + { +- CoglPipeline *pipeline; ++ if (stex->texture == tex) ++ { ++ CoglPipeline *pipeline; + +- if (stex->masked_pipeline) +- return stex->masked_pipeline; ++ if (stex->masked_pipeline) ++ return stex->masked_pipeline; + +- pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); +- cogl_pipeline_set_layer_combine (pipeline, 1, +- "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", +- NULL); ++ pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); ++ cogl_pipeline_set_layer_combine (pipeline, 1, ++ "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", ++ NULL); ++ if (stex->snippet) ++ cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet); + +- stex->masked_pipeline = pipeline; ++ stex->masked_pipeline = pipeline; ++ return pipeline; ++ } ++ else ++ { ++ CoglPipeline *pipeline; ++ ++ if (stex->masked_tower_pipeline) ++ return stex->masked_tower_pipeline; + +- return pipeline; ++ pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); ++ cogl_pipeline_set_layer_combine (pipeline, 1, ++ "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", ++ NULL); ++ ++ stex->masked_tower_pipeline = pipeline; ++ return pipeline; ++ } + } + + static CoglPipeline * + get_unblended_pipeline (MetaShapedTexture *stex, +- CoglContext *ctx) ++ CoglContext *ctx, ++ CoglTexture *tex) + { +- CoglPipeline *pipeline; +- CoglColor color; ++ if (stex->texture == tex) ++ { ++ CoglPipeline *pipeline; ++ CoglColor color; + +- if (stex->unblended_pipeline) +- return stex->unblended_pipeline; ++ if (stex->unblended_pipeline) ++ return stex->unblended_pipeline; + +- pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); +- cogl_color_init_from_4ub (&color, 255, 255, 255, 255); +- cogl_pipeline_set_blend (pipeline, +- "RGBA = ADD (SRC_COLOR, 0)", +- NULL); +- cogl_pipeline_set_color (pipeline, &color); ++ pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); ++ cogl_color_init_from_4ub (&color, 255, 255, 255, 255); ++ cogl_pipeline_set_blend (pipeline, ++ "RGBA = ADD (SRC_COLOR, 0)", ++ NULL); ++ cogl_pipeline_set_color (pipeline, &color); ++ if (stex->snippet) ++ cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet); + +- stex->unblended_pipeline = pipeline; ++ stex->unblended_pipeline = pipeline; ++ return pipeline; ++ } ++ else ++ { ++ CoglPipeline *pipeline; ++ CoglColor color; + +- return pipeline; ++ if (stex->unblended_tower_pipeline) ++ return stex->unblended_tower_pipeline; ++ ++ pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); ++ cogl_color_init_from_4ub (&color, 255, 255, 255, 255); ++ cogl_pipeline_set_blend (pipeline, ++ "RGBA = ADD (SRC_COLOR, 0)", ++ NULL); ++ cogl_pipeline_set_color (pipeline, &color); ++ ++ stex->unblended_tower_pipeline = pipeline; ++ return pipeline; ++ } + } + + static void +@@ -714,7 +787,7 @@ do_paint (MetaShapedTexture *stex, + + if (!cairo_region_is_empty (region)) + { +- opaque_pipeline = get_unblended_pipeline (stex, ctx); ++ opaque_pipeline = get_unblended_pipeline (stex, ctx, paint_tex); + cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex); + cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter); + +@@ -750,11 +823,11 @@ do_paint (MetaShapedTexture *stex, + + if (stex->mask_texture == NULL) + { +- blended_pipeline = get_unmasked_pipeline (stex, ctx); ++ blended_pipeline = get_unmasked_pipeline (stex, ctx, paint_tex); + } + else + { +- blended_pipeline = get_masked_pipeline (stex, ctx); ++ blended_pipeline = get_masked_pipeline (stex, ctx, paint_tex); + cogl_pipeline_set_layer_texture (blended_pipeline, 1, stex->mask_texture); + cogl_pipeline_set_layer_filters (blended_pipeline, 1, filter, filter); + } +-- +2.34.1 + diff --git a/SOURCES/fix-text-selection-drawing.patch b/SOURCES/fix-text-selection-drawing.patch new file mode 100644 index 0000000..11554aa --- /dev/null +++ b/SOURCES/fix-text-selection-drawing.patch @@ -0,0 +1,138 @@ +From 30d6e3abe2a0251b11513d66d15a59cd0705a828 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 27 May 2019 17:48:41 +0000 +Subject: [PATCH 1/2] clutter-text: Fix selection color drawing + +Commit cabcad185 removed the call to cogl_set_source_color4ub() before +cogl_fill_path(), so instead of the previously assigned selection color, +the background is drawn with the last set source. + +In order to honour the newly added framebuffer parameter and still apply +the correct color, switch from cogl_fill_path() to the (deprecated!) +cogl_framebuffer_fill_path() method. + +https://gitlab.gnome.org/GNOME/mutter/issues/494 +--- + clutter/clutter/clutter-text.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/clutter/clutter/clutter-text.c b/clutter/clutter/clutter-text.c +index fb9d926df..000bbbbd4 100644 +--- a/clutter/clutter/clutter-text.c ++++ b/clutter/clutter/clutter-text.c +@@ -1975,6 +1975,7 @@ selection_paint (ClutterText *self, + else + { + /* Paint selection background first */ ++ CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline); + PangoLayout *layout = clutter_text_get_layout (self); + CoglPath *selection_path = cogl_path_new (); + CoglColor cogl_color = { 0, }; +@@ -1987,11 +1988,19 @@ selection_paint (ClutterText *self, + else + color = &priv->text_color; + ++ cogl_color_init_from_4ub (&cogl_color, ++ color->red, ++ color->green, ++ color->blue, ++ paint_opacity * color->alpha / 255); ++ cogl_color_premultiply (&cogl_color); ++ cogl_pipeline_set_color (color_pipeline, &cogl_color); ++ + clutter_text_foreach_selection_rectangle_prescaled (self, + add_selection_rectangle_to_path, + selection_path); + +- cogl_path_fill (selection_path); ++ cogl_framebuffer_fill_path (fb, color_pipeline, selection_path); + + /* Paint selected text */ + cogl_framebuffer_push_path_clip (fb, selection_path); +-- +2.21.0 + + +From 13a1624c1050c91cd4d8a298f7a10fafe56fe9e5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 27 May 2019 22:40:47 +0000 +Subject: [PATCH 2/2] cogl-path: Undeprecate framebuffer functions + +It looks like deprecating the functions with explicit framebuffer/pipeline +arguments made it to (cogl) master by mistake: + +https://mail.gnome.org/archives/clutter-list/2016-April/msg00008.html + +We now use one of them, so this is a good time to undeprecate the lot. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/597 +--- + cogl/cogl-path/cogl-path-functions.h | 6 ------ + cogl/cogl-path/cogl-path.c | 3 --- + 2 files changed, 9 deletions(-) + +diff --git a/cogl/cogl-path/cogl-path-functions.h b/cogl/cogl-path/cogl-path-functions.h +index d4ef328d2..318fed028 100644 +--- a/cogl/cogl-path/cogl-path-functions.h ++++ b/cogl/cogl-path/cogl-path-functions.h +@@ -460,9 +460,7 @@ cogl_path_fill (CoglPath *path); + * use while filling a path. + * + * Stability: unstable +- * Deprecated: 1.16: Use cogl_path_fill() instead + */ +-COGL_DEPRECATED_FOR (cogl_path_fill) + void + cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, +@@ -492,9 +490,7 @@ cogl_path_stroke (CoglPath *path); + * regardless of the current transformation matrix. + * + * Stability: unstable +- * Deprecated: 1.16: Use cogl_path_stroke() instead + */ +-COGL_DEPRECATED_FOR (cogl_path_stroke) + void + cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, +@@ -529,9 +525,7 @@ cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer, + * + * Since: 1.8 + * Stability: Unstable +- * Deprecated: 1.16: Use cogl_framebuffer_push_path_clip() instead + */ +-COGL_DEPRECATED_FOR (cogl_framebuffer_push_path_clip) + void + cogl_clip_push_from_path (CoglPath *path); + +diff --git a/cogl/cogl-path/cogl-path.c b/cogl/cogl-path/cogl-path.c +index 4d86c6fb5..8774406f8 100644 +--- a/cogl/cogl-path/cogl-path.c ++++ b/cogl/cogl-path/cogl-path.c +@@ -1504,7 +1504,6 @@ cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer, + COGL_FRAMEBUFFER_STATE_CLIP; + } + +-/* XXX: deprecated */ + void + cogl_clip_push_from_path (CoglPath *path) + { +@@ -1575,7 +1574,6 @@ _cogl_path_build_stroke_attribute_buffer (CoglPath *path) + data->stroke_n_attributes = n_attributes; + } + +-/* XXX: deprecated */ + void + cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, +@@ -1588,7 +1586,6 @@ cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, + _cogl_path_fill_nodes (path, framebuffer, pipeline, 0 /* flags */); + } + +-/* XXX: deprecated */ + void + cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, +-- +2.21.0 + diff --git a/SOURCES/geometric-picking.patch b/SOURCES/geometric-picking.patch new file mode 100644 index 0000000..6892dca --- /dev/null +++ b/SOURCES/geometric-picking.patch @@ -0,0 +1,2091 @@ +From ac946bf95ce3e4dc900f72dcb4189dd49bdb3155 Mon Sep 17 00:00:00 2001 +From: Daniel van Vugt +Date: Thu, 18 Jul 2019 16:56:41 +0800 +Subject: [PATCH 1/4] clutter/point: Add ClutterPoint quarilateral testing API + +Add a function to check whether a point is inside a quadrilateral +by checking the cross product of vectors with the quadrilateral +points, and the point being checked. + +If the passed quadrilateral is zero-sized, no point is ever reported +to be inside it. + +This will be used by the next commit when comparing the transformed +actor vertices. + +[feaneron: add a commit message and remove unecessary code] + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/189 +--- + clutter/clutter/clutter-base-types.c | 62 ++++++++++++++++++++++++++++ + clutter/clutter/clutter-types.h | 3 ++ + 2 files changed, 65 insertions(+) + +diff --git a/clutter/clutter/clutter-base-types.c b/clutter/clutter/clutter-base-types.c +index aeb25c90ef..c84f9aa64b 100644 +--- a/clutter/clutter/clutter-base-types.c ++++ b/clutter/clutter/clutter-base-types.c +@@ -570,6 +570,68 @@ G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterPoint, clutter_point, + clutter_point_free, + CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_point_progress)) + ++static int ++clutter_point_compare_line (const ClutterPoint *p, ++ const ClutterPoint *a, ++ const ClutterPoint *b) ++{ ++ float x1 = b->x - a->x; ++ float y1 = b->y - a->y; ++ float x2 = p->x - a->x; ++ float y2 = p->y - a->y; ++ float cross_z = x1 * y2 - y1 * x2; ++ ++ if (cross_z > 0.f) ++ return 1; ++ else if (cross_z < 0.f) ++ return -1; ++ else ++ return 0; ++} ++ ++/** ++ * clutter_point_inside_quadrilateral: ++ * @point: a #ClutterPoint to test ++ * @vertices: array of vertices of the quadrilateral, in clockwise order, ++ * from top-left to bottom-left ++ * ++ * Determines whether a point is inside the convex quadrilateral provided, ++ * and not on any of its edges or vertices. ++ * ++ * Returns: %TRUE if @point is inside the quadrilateral ++ */ ++gboolean ++clutter_point_inside_quadrilateral (const ClutterPoint *point, ++ const ClutterPoint *vertices) ++{ ++ unsigned int i; ++ int first_side; ++ ++ first_side = 0; ++ ++ for (i = 0; i < 4; i++) ++ { ++ int side; ++ ++ side = clutter_point_compare_line (point, ++ &vertices[i], ++ &vertices[(i + 1) % 4]); ++ ++ if (side) ++ { ++ if (first_side == 0) ++ first_side = side; ++ else if (side != first_side) ++ return FALSE; ++ } ++ } ++ ++ if (first_side == 0) ++ return FALSE; ++ ++ return TRUE; ++} ++ + + + /* +diff --git a/clutter/clutter/clutter-types.h b/clutter/clutter/clutter-types.h +index 0f0fb1c2ac..0508028273 100644 +--- a/clutter/clutter/clutter-types.h ++++ b/clutter/clutter/clutter-types.h +@@ -200,6 +200,9 @@ float clutter_point_distance (const ClutterPoint *a, + const ClutterPoint *b, + float *x_distance, + float *y_distance); ++CLUTTER_EXPORT ++gboolean clutter_point_inside_quadrilateral (const ClutterPoint *point, ++ const ClutterPoint *vertices); + + /** + * ClutterSize: +-- +2.29.2 + + +From 8abac81711cfef8317bb675349d6b5b0a79eb05d Mon Sep 17 00:00:00 2001 +From: Daniel van Vugt +Date: Thu, 2 Aug 2018 19:03:30 +0800 +Subject: [PATCH 2/4] clutter: Introduce geometric picking + +Currently, Clutter does picking by drawing with Cogl and reading +the pixel that's beneath the given point. Since Cogl has a journal +that records drawing operations, and has optimizations to read a +single pixel from a list of rectangle, it would be expected that +we would hit this fast path and not flush the journal while picking. + +However, that's not the case: dithering, clipping with scissors, etc, +can all flush the journal, issuing commands to the GPU and making +picking slow. On NVidia-based systems, this glReadPixels() call is +extremely costly. + +Introduce geometric picking, and avoid using the Cogl journal entirely. +Do this by introducing a stack of actors in ClutterStage. This stack +is cached, but for now, don't use the cache as much as possible. + +The picking routines are still tied to painting. + +When projecting the actor vertexes, do it manually and take the modelview +matrix of the framebuffer into account as well. + +CPU usage on an Intel i7-7700, tested with two different GPUs/drivers: + + | | Intel | Nvidia | + | ------: | --------: | -----: | + | Moving the mouse: | + | Before | 10% | 10% | + | After | 6% | 6% | + | Moving a window: | + | Before | 23% | 81% | + | After | 19% | 40% | + +Closes: https://gitlab.gnome.org/GNOME/mutter/issues/154, + https://gitlab.gnome.org/GNOME/mutter/issues/691 + +Helps significantly with: https://gitlab.gnome.org/GNOME/mutter/issues/283, + https://gitlab.gnome.org/GNOME/mutter/issues/590, + https://gitlab.gnome.org/GNOME/mutter/issues/700 + +v2: Fix code style issues + Simplify quadrilateral checks + Remove the 0.5f hack + Differentiate axis-aligned rectangles + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/189 +--- + clutter/clutter/clutter-actor-private.h | 2 - + clutter/clutter/clutter-actor.c | 232 ++++++---- + clutter/clutter/clutter-actor.h | 4 + + clutter/clutter/clutter-debug.h | 1 - + clutter/clutter/clutter-main.c | 120 ----- + clutter/clutter/clutter-private.h | 5 - + clutter/clutter/clutter-stage-private.h | 16 +- + clutter/clutter/clutter-stage-window.c | 18 - + clutter/clutter/clutter-stage-window.h | 8 - + clutter/clutter/clutter-stage.c | 459 +++++++++++-------- + clutter/clutter/cogl/clutter-stage-cogl.c | 52 --- + clutter/clutter/deprecated/clutter-texture.c | 93 +--- + clutter/tests/conform/actor-pick.c | 99 +--- + clutter/tests/conform/meson.build | 1 - + clutter/tests/conform/texture.c | 84 ---- + src/compositor/meta-surface-actor.c | 27 +- + 16 files changed, 439 insertions(+), 782 deletions(-) + delete mode 100644 clutter/tests/conform/texture.c + +diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h +index c44f6342fd..9bf1a30493 100644 +--- a/clutter/clutter/clutter-actor-private.h ++++ b/clutter/clutter/clutter-actor-private.h +@@ -297,8 +297,6 @@ const gchar * _clutter_actor_get_debug_name + void _clutter_actor_push_clone_paint (void); + void _clutter_actor_pop_clone_paint (void); + +-guint32 _clutter_actor_get_pick_id (ClutterActor *self); +- + void _clutter_actor_shader_pre_paint (ClutterActor *actor, + gboolean repeat); + void _clutter_actor_shader_post_paint (ClutterActor *actor); +diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c +index 43093fe79d..01ffa51caa 100644 +--- a/clutter/clutter/clutter-actor.c ++++ b/clutter/clutter/clutter-actor.c +@@ -730,8 +730,6 @@ struct _ClutterActorPrivate + + gchar *name; /* a non-unique name, used for debugging */ + +- gint32 pick_id; /* per-stage unique id, used for picking */ +- + /* a back-pointer to the Pango context that we can use + * to create pre-configured PangoLayout + */ +@@ -1281,6 +1279,105 @@ clutter_actor_verify_map_state (ClutterActor *self) + + #endif /* CLUTTER_ENABLE_DEBUG */ + ++static gboolean ++_clutter_actor_transform_local_box_to_stage (ClutterActor *self, ++ ClutterStage *stage, ++ const ClutterActorBox *box, ++ ClutterPoint vertices[4]) ++{ ++ CoglFramebuffer *fb = cogl_get_draw_framebuffer (); ++ CoglMatrix stage_transform, inv_stage_transform; ++ CoglMatrix modelview, transform_to_stage; ++ int v; ++ ++ clutter_actor_get_transform (CLUTTER_ACTOR (stage), &stage_transform); ++ if (!cogl_matrix_get_inverse (&stage_transform, &inv_stage_transform)) ++ return FALSE; ++ cogl_framebuffer_get_modelview_matrix (fb, &modelview); ++ cogl_matrix_multiply (&transform_to_stage, &inv_stage_transform, &modelview); ++ ++ vertices[0].x = box->x1; ++ vertices[0].y = box->y1; ++ ++ vertices[1].x = box->x2; ++ vertices[1].y = box->y1; ++ ++ vertices[2].x = box->x2; ++ vertices[2].y = box->y2; ++ ++ vertices[3].x = box->x1; ++ vertices[3].y = box->y2; ++ ++ for (v = 0; v < 4; v++) ++ { ++ float z = 0.f; ++ float w = 1.f; ++ ++ cogl_matrix_transform_point (&transform_to_stage, ++ &vertices[v].x, ++ &vertices[v].y, ++ &z, ++ &w); ++ } ++ ++ return TRUE; ++} ++ ++/** ++ * clutter_actor_pick_box: ++ * @self: The #ClutterActor being "pick" painted. ++ * @box: A rectangle in the actor's own local coordinates. ++ * ++ * Logs (does a virtual paint of) a rectangle for picking. Note that @box is ++ * in the actor's own local coordinates, so is usually {0,0,width,height} ++ * to include the whole actor. That is unless the actor has a shaped input ++ * region in which case you may wish to log the (multiple) smaller rectangles ++ * that make up the input region. ++ */ ++void ++clutter_actor_pick_box (ClutterActor *self, ++ const ClutterActorBox *box) ++{ ++ ClutterStage *stage; ++ ClutterPoint vertices[4]; ++ ++ g_return_if_fail (CLUTTER_IS_ACTOR (self)); ++ g_return_if_fail (box != NULL); ++ ++ if (box->x1 >= box->x2 || box->y1 >= box->y2) ++ return; ++ ++ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); ++ ++ if (_clutter_actor_transform_local_box_to_stage (self, stage, box, vertices)) ++ clutter_stage_log_pick (stage, vertices, self); ++} ++ ++static gboolean ++_clutter_actor_push_pick_clip (ClutterActor *self, ++ const ClutterActorBox *clip) ++{ ++ ClutterStage *stage; ++ ClutterPoint vertices[4]; ++ ++ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); ++ ++ if (!_clutter_actor_transform_local_box_to_stage (self, stage, clip, vertices)) ++ return FALSE; ++ ++ clutter_stage_push_pick_clip (stage, vertices); ++ return TRUE; ++} ++ ++static void ++_clutter_actor_pop_pick_clip (ClutterActor *self) ++{ ++ ClutterActor *stage; ++ ++ stage = _clutter_actor_get_stage_internal (self); ++ clutter_stage_pop_pick_clip (CLUTTER_STAGE (stage)); ++} ++ + static void + clutter_actor_set_mapped (ClutterActor *self, + gboolean mapped) +@@ -1509,8 +1606,7 @@ clutter_actor_update_map_state (ClutterActor *self, + static void + clutter_actor_real_map (ClutterActor *self) + { +- ClutterActorPrivate *priv = self->priv; +- ClutterActor *stage, *iter; ++ ClutterActor *iter; + + g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); + +@@ -1521,13 +1617,6 @@ clutter_actor_real_map (ClutterActor *self) + + self->priv->needs_paint_volume_update = TRUE; + +- stage = _clutter_actor_get_stage_internal (self); +- priv->pick_id = _clutter_stage_acquire_pick_id (CLUTTER_STAGE (stage), self); +- +- CLUTTER_NOTE (ACTOR, "Pick id '%d' for actor '%s'", +- priv->pick_id, +- _clutter_actor_get_debug_name (self)); +- + clutter_actor_ensure_resource_scale (self); + + /* notify on parent mapped before potentially mapping +@@ -1632,11 +1721,6 @@ clutter_actor_real_unmap (ClutterActor *self) + + stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); + +- if (stage != NULL) +- _clutter_stage_release_pick_id (stage, priv->pick_id); +- +- priv->pick_id = -1; +- + if (stage != NULL && + clutter_stage_get_key_focus (stage) == self) + { +@@ -2255,46 +2339,16 @@ static void + clutter_actor_real_pick (ClutterActor *self, + const ClutterColor *color) + { +- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); +- +- /* the default implementation is just to paint a rectangle +- * with the same size of the actor using the passed color +- */ + if (clutter_actor_should_pick_paint (self)) + { +- static CoglPipeline *default_pick_pipeline = NULL; +- ClutterActorBox box = { 0, }; +- CoglPipeline *pick_pipeline; +- float width, height; +- +- if (G_UNLIKELY (default_pick_pipeline == NULL)) +- { +- CoglContext *ctx = +- clutter_backend_get_cogl_context (clutter_get_default_backend ()); +- +- default_pick_pipeline = cogl_pipeline_new (ctx); +- } +- +- g_assert (default_pick_pipeline != NULL); +- pick_pipeline = cogl_pipeline_copy (default_pick_pipeline); ++ ClutterActorBox box = { ++ .x1 = 0, ++ .y1 = 0, ++ .x2 = clutter_actor_get_width (self), ++ .y2 = clutter_actor_get_height (self), ++ }; + +- clutter_actor_get_allocation_box (self, &box); +- +- width = box.x2 - box.x1; +- height = box.y2 - box.y1; +- +- cogl_pipeline_set_color4ub (pick_pipeline, +- color->red, +- color->green, +- color->blue, +- color->alpha); +- +- cogl_framebuffer_draw_rectangle (framebuffer, +- pick_pipeline, +- 0, 0, +- width, height); +- +- cogl_object_unref (pick_pipeline); ++ clutter_actor_pick_box (self, &box); + } + + /* XXX - this thoroughly sucks, but we need to maintain compatibility +@@ -3585,15 +3639,6 @@ _clutter_actor_update_last_paint_volume (ClutterActor *self) + priv->last_paint_volume_valid = TRUE; + } + +-guint32 +-_clutter_actor_get_pick_id (ClutterActor *self) +-{ +- if (self->priv->pick_id < 0) +- return 0; +- +- return self->priv->pick_id; +-} +- + /* This is the same as clutter_actor_add_effect except that it doesn't + queue a redraw and it doesn't notify on the effect property */ + static void +@@ -3826,6 +3871,7 @@ clutter_actor_paint (ClutterActor *self) + ClutterActorPrivate *priv; + ClutterPickMode pick_mode; + gboolean culling_inhibited; ++ ClutterActorBox clip; + gboolean clip_set = FALSE; + ClutterStage *stage; + +@@ -3919,24 +3965,38 @@ clutter_actor_paint (ClutterActor *self) + + if (priv->has_clip) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); +- cogl_framebuffer_push_rectangle_clip (fb, +- priv->clip.origin.x, +- priv->clip.origin.y, +- priv->clip.origin.x + priv->clip.size.width, +- priv->clip.origin.y + priv->clip.size.height); ++ clip.x1 = priv->clip.origin.x; ++ clip.y1 = priv->clip.origin.y; ++ clip.x2 = priv->clip.origin.x + priv->clip.size.width; ++ clip.y2 = priv->clip.origin.y + priv->clip.size.height; + clip_set = TRUE; + } + else if (priv->clip_to_allocation) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); +- gfloat width, height; ++ clip.x1 = 0.f; ++ clip.y1 = 0.f; ++ clip.x2 = priv->allocation.x2 - priv->allocation.x1; ++ clip.y2 = priv->allocation.y2 - priv->allocation.y1; ++ clip_set = TRUE; ++ } + +- width = priv->allocation.x2 - priv->allocation.x1; +- height = priv->allocation.y2 - priv->allocation.y1; ++ if (clip_set) ++ { ++ if (pick_mode == CLUTTER_PICK_NONE) ++ { ++ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); + +- cogl_framebuffer_push_rectangle_clip (fb, 0, 0, width, height); +- clip_set = TRUE; ++ cogl_framebuffer_push_rectangle_clip (fb, ++ clip.x1, ++ clip.y1, ++ clip.x2, ++ clip.y2); ++ } ++ else ++ { ++ if (!_clutter_actor_push_pick_clip (self, &clip)) ++ clip_set = FALSE; ++ } + } + + if (pick_mode == CLUTTER_PICK_NONE) +@@ -4020,9 +4080,16 @@ clutter_actor_paint (ClutterActor *self) + done: + if (clip_set) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); ++ if (pick_mode == CLUTTER_PICK_NONE) ++ { ++ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); + +- cogl_framebuffer_pop_clip (fb); ++ cogl_framebuffer_pop_clip (fb); ++ } ++ else ++ { ++ _clutter_actor_pop_pick_clip (self); ++ } + } + + cogl_pop_matrix (); +@@ -4093,11 +4160,12 @@ clutter_actor_continue_paint (ClutterActor *self) + { + ClutterColor col = { 0, }; + +- _clutter_id_to_color (_clutter_actor_get_pick_id (self), &col); +- +- /* Actor will then paint silhouette of itself in supplied +- * color. See clutter_stage_get_actor_at_pos() for where +- * picking is enabled. ++ /* The actor will log a silhouette of itself to the stage pick log. ++ * Note that the picking color is no longer used as the "log" instead ++ * keeps a weak pointer to the actor itself. But we keep the color ++ * parameter for now so as to maintain ABI compatibility. The color ++ * parameter can be removed when someone feels like breaking the ABI ++ * along with gnome-shell. + * + * XXX:2.0 - Call the pick() virtual directly + */ +@@ -8603,8 +8671,6 @@ clutter_actor_init (ClutterActor *self) + + self->priv = priv = clutter_actor_get_instance_private (self); + +- priv->pick_id = -1; +- + priv->opacity = 0xff; + priv->show_on_set_parent = TRUE; + priv->resource_scale = -1.0f; +diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h +index 3e7a59ac0c..16b438ba64 100644 +--- a/clutter/clutter/clutter-actor.h ++++ b/clutter/clutter/clutter-actor.h +@@ -910,6 +910,10 @@ void clutter_actor_bind_model_with_properties + const char *first_model_property, + ...); + ++CLUTTER_EXPORT ++void clutter_actor_pick_box (ClutterActor *self, ++ const ClutterActorBox *box); ++ + G_END_DECLS + + #endif /* __CLUTTER_ACTOR_H__ */ +diff --git a/clutter/clutter/clutter-debug.h b/clutter/clutter/clutter-debug.h +index 2462385f65..7d170d2d54 100644 +--- a/clutter/clutter/clutter-debug.h ++++ b/clutter/clutter/clutter-debug.h +@@ -30,7 +30,6 @@ typedef enum + typedef enum + { + CLUTTER_DEBUG_NOP_PICKING = 1 << 0, +- CLUTTER_DEBUG_DUMP_PICK_BUFFERS = 1 << 1 + } ClutterPickDebugFlag; + + typedef enum +diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c +index 645c8bceb6..11c221a65b 100644 +--- a/clutter/clutter/clutter-main.c ++++ b/clutter/clutter/clutter-main.c +@@ -131,7 +131,6 @@ static const GDebugKey clutter_debug_keys[] = { + + static const GDebugKey clutter_pick_debug_keys[] = { + { "nop-picking", CLUTTER_DEBUG_NOP_PICKING }, +- { "dump-pick-buffers", CLUTTER_DEBUG_DUMP_PICK_BUFFERS }, + }; + + static const GDebugKey clutter_paint_debug_keys[] = { +@@ -533,125 +532,6 @@ clutter_get_motion_events_enabled (void) + return _clutter_context_get_motion_events_enabled (); + } + +-void +-_clutter_id_to_color (guint id_, +- ClutterColor *col) +-{ +- ClutterMainContext *ctx; +- gint red, green, blue; +- +- ctx = _clutter_context_get_default (); +- +- if (ctx->fb_g_mask == 0) +- { +- /* Figure out framebuffer masks used for pick */ +- cogl_get_bitmasks (&ctx->fb_r_mask, +- &ctx->fb_g_mask, +- &ctx->fb_b_mask, NULL); +- +- ctx->fb_r_mask_used = ctx->fb_r_mask; +- ctx->fb_g_mask_used = ctx->fb_g_mask; +- ctx->fb_b_mask_used = ctx->fb_b_mask; +- +- /* XXX - describe what "fuzzy picking" is */ +- if (clutter_use_fuzzy_picking) +- { +- ctx->fb_r_mask_used--; +- ctx->fb_g_mask_used--; +- ctx->fb_b_mask_used--; +- } +- } +- +- /* compute the numbers we'll store in the components */ +- red = (id_ >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used)) +- & (0xff >> (8-ctx->fb_r_mask_used)); +- green = (id_ >> ctx->fb_b_mask_used) +- & (0xff >> (8-ctx->fb_g_mask_used)); +- blue = (id_) +- & (0xff >> (8-ctx->fb_b_mask_used)); +- +- /* shift left bits a bit and add one, this circumvents +- * at least some potential rounding errors in GL/GLES +- * driver / hw implementation. +- */ +- if (ctx->fb_r_mask_used != ctx->fb_r_mask) +- red = red * 2; +- if (ctx->fb_g_mask_used != ctx->fb_g_mask) +- green = green * 2; +- if (ctx->fb_b_mask_used != ctx->fb_b_mask) +- blue = blue * 2; +- +- /* shift up to be full 8bit values */ +- red = (red << (8 - ctx->fb_r_mask)) | (0x7f >> (ctx->fb_r_mask_used)); +- green = (green << (8 - ctx->fb_g_mask)) | (0x7f >> (ctx->fb_g_mask_used)); +- blue = (blue << (8 - ctx->fb_b_mask)) | (0x7f >> (ctx->fb_b_mask_used)); +- +- col->red = red; +- col->green = green; +- col->blue = blue; +- col->alpha = 0xff; +- +- /* XXX: We rotate the nibbles of the colors here so that there is a +- * visible variation between colors of sequential actor identifiers; +- * otherwise pick buffers dumped to an image will pretty much just look +- * black. +- */ +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) +- { +- col->red = (col->red << 4) | (col->red >> 4); +- col->green = (col->green << 4) | (col->green >> 4); +- col->blue = (col->blue << 4) | (col->blue >> 4); +- } +-} +- +-guint +-_clutter_pixel_to_id (guchar pixel[4]) +-{ +- ClutterMainContext *ctx; +- gint red, green, blue; +- guint retval; +- +- ctx = _clutter_context_get_default (); +- +- /* reduce the pixel components to the number of bits actually used of the +- * 8bits. +- */ +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) +- { +- guchar tmp; +- +- /* XXX: In _clutter_id_to_color we rotated the nibbles of the colors so +- * that there is a visible variation between colors of sequential actor +- * identifiers (otherwise pick buffers dumped to an image will pretty +- * much just look black.) Here we reverse that rotation. +- */ +- tmp = ((pixel[0] << 4) | (pixel[0] >> 4)); +- red = tmp >> (8 - ctx->fb_r_mask); +- tmp = ((pixel[1] << 4) | (pixel[1] >> 4)); +- green = tmp >> (8 - ctx->fb_g_mask); +- tmp = ((pixel[2] << 4) | (pixel[2] >> 4)); +- blue = tmp >> (8 - ctx->fb_b_mask); +- } +- else +- { +- red = pixel[0] >> (8 - ctx->fb_r_mask); +- green = pixel[1] >> (8 - ctx->fb_g_mask); +- blue = pixel[2] >> (8 - ctx->fb_b_mask); +- } +- +- /* divide potentially by two if 'fuzzy' */ +- red = red >> (ctx->fb_r_mask - ctx->fb_r_mask_used); +- green = green >> (ctx->fb_g_mask - ctx->fb_g_mask_used); +- blue = blue >> (ctx->fb_b_mask - ctx->fb_b_mask_used); +- +- /* combine the correct per component values into the final id */ +- retval = blue +- + (green << ctx->fb_b_mask_used) +- + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used)); +- +- return retval; +-} +- + static CoglPangoFontMap * + clutter_context_get_pango_fontmap (void) + { +diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h +index 5a0fed85c9..f2f870b014 100644 +--- a/clutter/clutter/clutter-private.h ++++ b/clutter/clutter/clutter-private.h +@@ -210,11 +210,6 @@ gboolean _clutter_feature_init (GError **error); + gboolean _clutter_diagnostic_enabled (void); + void _clutter_diagnostic_message (const char *fmt, ...) G_GNUC_PRINTF (1, 2); + +-/* Picking code */ +-guint _clutter_pixel_to_id (guchar pixel[4]); +-void _clutter_id_to_color (guint id, +- ClutterColor *col); +- + void _clutter_set_sync_to_vblank (gboolean sync_to_vblank); + + /* use this function as the accumulator if you have a signal with +diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h +index 42474687ad..51ae47af1d 100644 +--- a/clutter/clutter/clutter-stage-private.h ++++ b/clutter/clutter/clutter-stage-private.h +@@ -75,6 +75,15 @@ gint64 _clutter_stage_get_update_time (ClutterStage *stage); + void _clutter_stage_clear_update_time (ClutterStage *stage); + gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage); + ++void clutter_stage_log_pick (ClutterStage *stage, ++ const ClutterPoint *vertices, ++ ClutterActor *actor); ++ ++void clutter_stage_push_pick_clip (ClutterStage *stage, ++ const ClutterPoint *vertices); ++ ++void clutter_stage_pop_pick_clip (ClutterStage *stage); ++ + ClutterActor *_clutter_stage_do_pick (ClutterStage *stage, + gint x, + gint y, +@@ -93,13 +102,6 @@ void _clutter_stage_queue_redraw_entry_invalidate (Clut + + CoglFramebuffer *_clutter_stage_get_active_framebuffer (ClutterStage *stage); + +-gint32 _clutter_stage_acquire_pick_id (ClutterStage *stage, +- ClutterActor *actor); +-void _clutter_stage_release_pick_id (ClutterStage *stage, +- gint32 pick_id); +-ClutterActor * _clutter_stage_get_actor_by_pick_id (ClutterStage *stage, +- gint32 pick_id); +- + void _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, + ClutterInputDevice *device, + ClutterActor *actor); +diff --git a/clutter/clutter/clutter-stage-window.c b/clutter/clutter/clutter-stage-window.c +index e8fa976a7d..4c4ef9d643 100644 +--- a/clutter/clutter/clutter-stage-window.c ++++ b/clutter/clutter/clutter-stage-window.c +@@ -293,24 +293,6 @@ _clutter_stage_window_redraw (ClutterStageWindow *window) + iface->redraw (window); + } + +- +-void +-_clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window, +- ClutterStageView *view, +- int *x, int *y) +-{ +- ClutterStageWindowInterface *iface; +- +- *x = 0; +- *y = 0; +- +- g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window)); +- +- iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); +- if (iface->get_dirty_pixel) +- iface->get_dirty_pixel (window, view, x, y); +-} +- + gboolean + _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window) + { +diff --git a/clutter/clutter/clutter-stage-window.h b/clutter/clutter/clutter-stage-window.h +index 6c3601745f..aa0c5f71cc 100644 +--- a/clutter/clutter/clutter-stage-window.h ++++ b/clutter/clutter/clutter-stage-window.h +@@ -68,10 +68,6 @@ struct _ClutterStageWindowInterface + + void (* redraw) (ClutterStageWindow *stage_window); + +- void (* get_dirty_pixel) (ClutterStageWindow *stage_window, +- ClutterStageView *view, +- int *x, int *y); +- + gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window); + + GList *(* get_views) (ClutterStageWindow *stage_window); +@@ -119,10 +115,6 @@ void _clutter_stage_window_set_accept_focus (ClutterStageWin + + void _clutter_stage_window_redraw (ClutterStageWindow *window); + +-void _clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window, +- ClutterStageView *view, +- int *x, int *y); +- + gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window); + + GList * _clutter_stage_window_get_views (ClutterStageWindow *window); +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index aaa77d9ede..7d88d5752f 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -97,6 +97,11 @@ typedef enum /*< prefix=CLUTTER_STAGE >*/ + + #define STAGE_NO_CLEAR_ON_PAINT(s) ((((ClutterStage *) (s))->priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0) + ++#ifndef G_APPROX_VALUE ++#define G_APPROX_VALUE(a, b, epsilon) \ ++ (((a) > (b) ? (a) - (b) : (b) - (a)) < (epsilon)) ++#endif ++ + struct _ClutterStageQueueRedrawEntry + { + ClutterActor *actor; +@@ -104,6 +109,19 @@ struct _ClutterStageQueueRedrawEntry + ClutterPaintVolume clip; + }; + ++typedef struct _PickRecord ++{ ++ ClutterPoint vertex[4]; ++ ClutterActor *actor; ++ int clip_stack_top; ++} PickRecord; ++ ++typedef struct _PickClipRecord ++{ ++ int prev; ++ ClutterPoint vertex[4]; ++} PickClipRecord; ++ + struct _ClutterStagePrivate + { + /* the stage implementation */ +@@ -137,7 +155,11 @@ struct _ClutterStagePrivate + GTimer *fps_timer; + gint32 timer_n_frames; + +- ClutterIDPool *pick_id_pool; ++ GArray *pick_stack; ++ GArray *pick_clip_stack; ++ int pick_clip_stack_top; ++ gboolean pick_stack_frozen; ++ ClutterPickMode cached_pick_mode; + + #ifdef CLUTTER_ENABLE_DEBUG + gulong redraw_count; +@@ -326,6 +348,211 @@ clutter_stage_get_preferred_height (ClutterActor *self, + *natural_height_p = geom.height; + } + ++static void ++add_pick_stack_weak_refs (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ int i; ++ ++ if (priv->pick_stack_frozen) ++ return; ++ ++ for (i = 0; i < priv->pick_stack->len; i++) ++ { ++ PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); ++ ++ if (rec->actor) ++ g_object_add_weak_pointer (G_OBJECT (rec->actor), ++ (gpointer) &rec->actor); ++ } ++ ++ priv->pick_stack_frozen = TRUE; ++} ++ ++static void ++remove_pick_stack_weak_refs (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ int i; ++ ++ if (!priv->pick_stack_frozen) ++ return; ++ ++ for (i = 0; i < priv->pick_stack->len; i++) ++ { ++ PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); ++ ++ if (rec->actor) ++ g_object_remove_weak_pointer (G_OBJECT (rec->actor), ++ (gpointer) &rec->actor); ++ } ++ ++ priv->pick_stack_frozen = FALSE; ++} ++ ++static void ++_clutter_stage_clear_pick_stack (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ ++ remove_pick_stack_weak_refs (stage); ++ g_array_set_size (priv->pick_stack, 0); ++ g_array_set_size (priv->pick_clip_stack, 0); ++ priv->pick_clip_stack_top = -1; ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++} ++ ++void ++clutter_stage_log_pick (ClutterStage *stage, ++ const ClutterPoint *vertices, ++ ClutterActor *actor) ++{ ++ ClutterStagePrivate *priv; ++ PickRecord rec; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ g_return_if_fail (actor != NULL); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ ++ memcpy (rec.vertex, vertices, 4 * sizeof (ClutterPoint)); ++ rec.actor = actor; ++ rec.clip_stack_top = priv->pick_clip_stack_top; ++ ++ g_array_append_val (priv->pick_stack, rec); ++} ++ ++void ++clutter_stage_push_pick_clip (ClutterStage *stage, ++ const ClutterPoint *vertices) ++{ ++ ClutterStagePrivate *priv; ++ PickClipRecord clip; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ ++ clip.prev = priv->pick_clip_stack_top; ++ memcpy (clip.vertex, vertices, 4 * sizeof (ClutterPoint)); ++ ++ g_array_append_val (priv->pick_clip_stack, clip); ++ priv->pick_clip_stack_top = priv->pick_clip_stack->len - 1; ++} ++ ++void ++clutter_stage_pop_pick_clip (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv; ++ const PickClipRecord *top; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ g_assert (priv->pick_clip_stack_top >= 0); ++ ++ /* Individual elements of pick_clip_stack are not freed. This is so they ++ * can be shared as part of a tree of different stacks used by different ++ * actors in the pick_stack. The whole pick_clip_stack does however get ++ * freed later in _clutter_stage_clear_pick_stack. ++ */ ++ ++ top = &g_array_index (priv->pick_clip_stack, ++ PickClipRecord, ++ priv->pick_clip_stack_top); ++ ++ priv->pick_clip_stack_top = top->prev; ++} ++ ++static gboolean ++is_quadrilateral_axis_aligned_rectangle (const ClutterPoint *vertices) ++{ ++ int i; ++ ++ for (i = 0; i < 4; i++) ++ { ++ if (!G_APPROX_VALUE (vertices[i].x, ++ vertices[(i + 1) % 4].x, ++ FLT_EPSILON) && ++ !G_APPROX_VALUE (vertices[i].y, ++ vertices[(i + 1) % 4].y, ++ FLT_EPSILON)) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++static gboolean ++is_inside_axis_aligned_rectangle (const ClutterPoint *point, ++ const ClutterPoint *vertices) ++{ ++ float min_x = FLT_MAX; ++ float max_x = FLT_MIN; ++ float min_y = FLT_MAX; ++ float max_y = FLT_MIN; ++ int i; ++ ++ for (i = 0; i < 3; i++) ++ { ++ min_x = MIN (min_x, vertices[i].x); ++ min_y = MIN (min_y, vertices[i].y); ++ max_x = MAX (max_x, vertices[i].x); ++ max_y = MAX (max_y, vertices[i].y); ++ } ++ ++ return (point->x >= min_x && ++ point->y >= min_y && ++ point->x < max_x && ++ point->y < max_y); ++} ++ ++static gboolean ++is_inside_input_region (const ClutterPoint *point, ++ const ClutterPoint *vertices) ++{ ++ ++ if (is_quadrilateral_axis_aligned_rectangle (vertices)) ++ return is_inside_axis_aligned_rectangle (point, vertices); ++ else ++ return clutter_point_inside_quadrilateral (point, vertices); ++} ++ ++static gboolean ++pick_record_contains_pixel (ClutterStage *stage, ++ const PickRecord *rec, ++ int x, ++ int y) ++{ ++ const ClutterPoint point = CLUTTER_POINT_INIT (x, y); ++ ClutterStagePrivate *priv; ++ int clip_index; ++ ++ if (!is_inside_input_region (&point, rec->vertex)) ++ return FALSE; ++ ++ priv = stage->priv; ++ clip_index = rec->clip_stack_top; ++ while (clip_index >= 0) ++ { ++ const PickClipRecord *clip = &g_array_index (priv->pick_clip_stack, ++ PickClipRecord, ++ clip_index); ++ ++ if (!is_inside_input_region (&point, clip->vertex)) ++ return FALSE; ++ ++ clip_index = clip->prev; ++ } ++ ++ return TRUE; ++} ++ + static inline void + queue_full_redraw (ClutterStage *stage) + { +@@ -636,6 +863,12 @@ clutter_stage_do_paint_view (ClutterStage *stage, + float viewport[4]; + cairo_rectangle_int_t geom; + ++ /* Any mode of painting/picking invalidates the pick cache, unless we're ++ * in the middle of building it. So we reset the cached flag but don't ++ * completely clear the pick stack. ++ */ ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++ + _clutter_stage_window_get_geometry (priv->impl, &geom); + + viewport[0] = priv->viewport[0]; +@@ -1414,40 +1647,6 @@ clutter_stage_get_redraw_clip_bounds (ClutterStage *stage, + } + } + +-static void +-read_pixels_to_file (CoglFramebuffer *fb, +- char *filename_stem, +- int x, +- int y, +- int width, +- int height) +-{ +- guint8 *data; +- cairo_surface_t *surface; +- static int read_count = 0; +- char *filename = g_strdup_printf ("%s-%05d.png", +- filename_stem, +- read_count); +- +- data = g_malloc (4 * width * height); +- cogl_framebuffer_read_pixels (fb, +- x, y, width, height, +- CLUTTER_CAIRO_FORMAT_ARGB32, +- data); +- +- surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, +- width, height, +- width * 4); +- +- cairo_surface_write_to_png (surface, filename); +- cairo_surface_destroy (surface); +- +- g_free (data); +- g_free (filename); +- +- read_count++; +-} +- + static ClutterActor * + _clutter_stage_do_pick_on_view (ClutterStage *stage, + gint x, +@@ -1455,140 +1654,42 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, + ClutterPickMode mode, + ClutterStageView *view) + { +- ClutterActor *actor = CLUTTER_ACTOR (stage); ++ ClutterMainContext *context = _clutter_context_get_default (); + ClutterStagePrivate *priv = stage->priv; + CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); +- cairo_rectangle_int_t view_layout; +- ClutterMainContext *context; +- guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff }; +- CoglColor stage_pick_id; +- gboolean dither_enabled_save; +- ClutterActor *retval; +- gint dirty_x; +- gint dirty_y; +- gint read_x; +- gint read_y; +- float fb_width, fb_height; +- float fb_scale; +- float viewport_offset_x; +- float viewport_offset_y; +- +- priv = stage->priv; +- +- context = _clutter_context_get_default (); +- fb_scale = clutter_stage_view_get_scale (view); +- clutter_stage_view_get_layout (view, &view_layout); +- +- fb_width = view_layout.width * fb_scale; +- fb_height = view_layout.height * fb_scale; +- cogl_push_framebuffer (fb); +- +- /* needed for when a context switch happens */ +- _clutter_stage_maybe_setup_viewport (stage, view); +- +- /* FIXME: For some reason leaving the cogl clip stack empty causes the +- * picking to not work at all, so setting it the whole framebuffer content +- * for now. */ +- cogl_framebuffer_push_scissor_clip (fb, 0, 0, +- view_layout.width * fb_scale, +- view_layout.height * fb_scale); +- +- _clutter_stage_window_get_dirty_pixel (priv->impl, view, &dirty_x, &dirty_y); ++ int i; + +- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) +- { +- CLUTTER_NOTE (PICK, "Pushing pick scissor clip x: %d, y: %d, 1x1", +- (int) (dirty_x * fb_scale), +- (int) (dirty_y * fb_scale)); +- cogl_framebuffer_push_scissor_clip (fb, dirty_x * fb_scale, dirty_y * fb_scale, 1, 1); +- } ++ g_assert (context->pick_mode == CLUTTER_PICK_NONE); + +- viewport_offset_x = x * fb_scale - dirty_x * fb_scale; +- viewport_offset_y = y * fb_scale - dirty_y * fb_scale; +- CLUTTER_NOTE (PICK, "Setting viewport to %f, %f, %f, %f", +- priv->viewport[0] * fb_scale - viewport_offset_x, +- priv->viewport[1] * fb_scale - viewport_offset_y, +- priv->viewport[2] * fb_scale, +- priv->viewport[3] * fb_scale); +- cogl_framebuffer_set_viewport (fb, +- priv->viewport[0] * fb_scale - viewport_offset_x, +- priv->viewport[1] * fb_scale - viewport_offset_y, +- priv->viewport[2] * fb_scale, +- priv->viewport[3] * fb_scale); +- +- read_x = dirty_x * fb_scale; +- read_y = dirty_y * fb_scale; +- +- CLUTTER_NOTE (PICK, "Performing pick at %i,%i on view %dx%d+%d+%d s: %f", +- x, y, +- view_layout.width, view_layout.height, +- view_layout.x, view_layout.y, fb_scale); +- +- cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255); +- cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH, &stage_pick_id); +- +- /* Disable dithering (if any) when doing the painting in pick mode */ +- dither_enabled_save = cogl_framebuffer_get_dither_enabled (fb); +- cogl_framebuffer_set_dither_enabled (fb, FALSE); +- +- /* Render the entire scence in pick mode - just single colored silhouette's +- * are drawn offscreen (as we never swap buffers) +- */ +- context->pick_mode = mode; +- +- clutter_stage_do_paint_view (stage, view, NULL); +- context->pick_mode = CLUTTER_PICK_NONE; +- +- /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used +- even though we don't care about the alpha component because under +- GLES this is the only format that is guaranteed to work so Cogl +- will end up having to do a conversion if any other format is +- used. The format is requested as pre-multiplied because Cogl +- assumes that all pixels in the framebuffer are premultiplied so +- it avoids a conversion. */ +- cogl_framebuffer_read_pixels (fb, +- read_x, read_y, 1, 1, +- COGL_PIXEL_FORMAT_RGBA_8888_PRE, +- pixel); +- +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) ++ if (mode != priv->cached_pick_mode) + { +- char *file_name = +- g_strdup_printf ("pick-buffer-%s-view-x-%d", +- _clutter_actor_get_debug_name (actor), +- view_layout.x); ++ _clutter_stage_clear_pick_stack (stage); + +- read_pixels_to_file (fb, file_name, 0, 0, fb_width, fb_height); ++ cogl_push_framebuffer (fb); + +- g_free (file_name); +- } +- +- /* Restore whether GL_DITHER was enabled */ +- cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save); +- +- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) +- cogl_framebuffer_pop_clip (fb); ++ context->pick_mode = mode; ++ clutter_stage_do_paint_view (stage, view, NULL); ++ context->pick_mode = CLUTTER_PICK_NONE; ++ priv->cached_pick_mode = mode; + +- cogl_framebuffer_pop_clip (fb); ++ cogl_pop_framebuffer (); + +- _clutter_stage_dirty_viewport (stage); ++ add_pick_stack_weak_refs (stage); ++ } + +- if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) +- retval = actor; +- else ++ /* Search all "painted" pickable actors from front to back. A linear search ++ * is required, and also performs fine since there is typically only ++ * on the order of dozens of actors in the list (on screen) at a time. ++ */ ++ for (i = priv->pick_stack->len - 1; i >= 0; i--) + { +- guint32 id_ = _clutter_pixel_to_id (pixel); ++ const PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); + +- retval = _clutter_stage_get_actor_by_pick_id (stage, id_); +- CLUTTER_NOTE (PICK, "Picking actor %s with id %u (pixel: 0x%x%x%x%x", +- G_OBJECT_TYPE_NAME (retval), +- id_, +- pixel[0], pixel[1], pixel[2], pixel[3]); ++ if (rec->actor && pick_record_contains_pixel (stage, rec, x, y)) ++ return rec->actor; + } + +- cogl_pop_framebuffer (); +- +- return retval; ++ return CLUTTER_ACTOR (stage); + } + + static ClutterStageView * +@@ -1901,7 +2002,9 @@ clutter_stage_finalize (GObject *object) + + g_array_free (priv->paint_volume_stack, TRUE); + +- _clutter_id_pool_free (priv->pick_id_pool); ++ _clutter_stage_clear_pick_stack (stage); ++ g_array_free (priv->pick_clip_stack, TRUE); ++ g_array_free (priv->pick_stack, TRUE); + + if (priv->fps_timer != NULL) + g_timer_destroy (priv->fps_timer); +@@ -2435,7 +2538,10 @@ clutter_stage_init (ClutterStage *self) + priv->paint_volume_stack = + g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume)); + +- priv->pick_id_pool = _clutter_id_pool_new (256); ++ priv->pick_stack = g_array_new (FALSE, FALSE, sizeof (PickRecord)); ++ priv->pick_clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord)); ++ priv->pick_clip_stack_top = -1; ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; + } + + /** +@@ -4253,6 +4359,12 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage, + CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ", + _clutter_actor_get_debug_name (actor), clip); + ++ /* Queuing a redraw or clip change invalidates the pick cache, unless we're ++ * in the middle of building it. So we reset the cached flag but don't ++ * completely clear the pick stack... ++ */ ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++ + if (!priv->redraw_pending) + { + ClutterMasterClock *master_clock; +@@ -4513,39 +4625,6 @@ _clutter_stage_get_active_framebuffer (ClutterStage *stage) + return stage->priv->active_framebuffer; + } + +-gint32 +-_clutter_stage_acquire_pick_id (ClutterStage *stage, +- ClutterActor *actor) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- return _clutter_id_pool_add (priv->pick_id_pool, actor); +-} +- +-void +-_clutter_stage_release_pick_id (ClutterStage *stage, +- gint32 pick_id) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- _clutter_id_pool_remove (priv->pick_id_pool, pick_id); +-} +- +-ClutterActor * +-_clutter_stage_get_actor_by_pick_id (ClutterStage *stage, +- gint32 pick_id) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- return _clutter_id_pool_lookup (priv->pick_id_pool, pick_id); +-} +- + void + _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, + ClutterInputDevice *device, +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index 3f1f609c4e..effed79759 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -926,57 +926,6 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) + stage_cogl->frame_count++; + } + +-static void +-clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, +- ClutterStageView *view, +- int *x, +- int *y) +-{ +- CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view); +- ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); +- ClutterStageViewCoglPrivate *view_priv = +- clutter_stage_view_cogl_get_instance_private (view_cogl); +- gboolean has_buffer_age = +- cogl_is_onscreen (onscreen) && +- is_buffer_age_enabled (); +- float fb_scale; +- gboolean scale_is_fractional; +- +- fb_scale = clutter_stage_view_get_scale (view); +- if (fb_scale != floorf (fb_scale)) +- scale_is_fractional = TRUE; +- else +- scale_is_fractional = FALSE; +- +- /* +- * Buffer damage is tracked in the framebuffer coordinate space +- * using the damage history. When fractional scaling is used, a +- * coordinate on the stage might not correspond to the exact position of any +- * physical pixel, which causes issues when painting using the pick mode. +- * +- * For now, always use the (0, 0) pixel for picking when using fractional +- * framebuffer scaling. +- */ +- if (!has_buffer_age || +- scale_is_fractional || +- !clutter_damage_history_is_age_valid (view_priv->damage_history, 0)) +- { +- *x = 0; +- *y = 0; +- } +- else +- { +- cairo_rectangle_int_t view_layout; +- const cairo_rectangle_int_t *fb_damage; +- +- clutter_stage_view_get_layout (view, &view_layout); +- +- fb_damage = clutter_damage_history_lookup (view_priv->damage_history, 0); +- *x = fb_damage->x / fb_scale; +- *y = fb_damage->y / fb_scale; +- } +-} +- + static void + clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) + { +@@ -994,7 +943,6 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) + iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips; + iface->get_redraw_clip_bounds = clutter_stage_cogl_get_redraw_clip_bounds; + iface->redraw = clutter_stage_cogl_redraw; +- iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel; + } + + static void +diff --git a/clutter/clutter/deprecated/clutter-texture.c b/clutter/clutter/deprecated/clutter-texture.c +index bea239f454..2c677b8a44 100644 +--- a/clutter/clutter/deprecated/clutter-texture.c ++++ b/clutter/clutter/deprecated/clutter-texture.c +@@ -572,83 +572,6 @@ gen_texcoords_and_draw_cogl_rectangle (ClutterActor *self, + 0, 0, t_w, t_h); + } + +-static CoglPipeline * +-create_pick_pipeline (ClutterActor *self) +-{ +- ClutterTexture *texture = CLUTTER_TEXTURE (self); +- ClutterTexturePrivate *priv = texture->priv; +- CoglPipeline *pick_pipeline = cogl_pipeline_copy (texture_template_pipeline); +- GError *error = NULL; +- +- if (!cogl_pipeline_set_layer_combine (pick_pipeline, 0, +- "RGBA = " +- " MODULATE (CONSTANT, TEXTURE[A])", +- &error)) +- { +- if (!priv->seen_create_pick_pipeline_warning) +- g_warning ("Error setting up texture combine for shaped " +- "texture picking: %s", error->message); +- priv->seen_create_pick_pipeline_warning = TRUE; +- g_error_free (error); +- cogl_object_unref (pick_pipeline); +- return NULL; +- } +- +- cogl_pipeline_set_blend (pick_pipeline, +- "RGBA = ADD (SRC_COLOR[RGBA], 0)", +- NULL); +- +- cogl_pipeline_set_alpha_test_function (pick_pipeline, +- COGL_PIPELINE_ALPHA_FUNC_EQUAL, +- 1.0); +- +- return pick_pipeline; +-} +- +-static void +-clutter_texture_pick (ClutterActor *self, +- const ClutterColor *color) +-{ +- ClutterTexture *texture = CLUTTER_TEXTURE (self); +- ClutterTexturePrivate *priv = texture->priv; +- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); +- +- if (!clutter_actor_should_pick_paint (self)) +- return; +- +- if (G_LIKELY (priv->pick_with_alpha_supported) && priv->pick_with_alpha) +- { +- CoglColor pick_color; +- +- if (priv->pick_pipeline == NULL) +- priv->pick_pipeline = create_pick_pipeline (self); +- +- if (priv->pick_pipeline == NULL) +- { +- priv->pick_with_alpha_supported = FALSE; +- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, +- color); +- return; +- } +- +- if (priv->fbo_handle != NULL) +- update_fbo (self); +- +- cogl_color_init_from_4ub (&pick_color, +- color->red, +- color->green, +- color->blue, +- 0xff); +- cogl_pipeline_set_layer_combine_constant (priv->pick_pipeline, +- 0, &pick_color); +- cogl_pipeline_set_layer_texture (priv->pick_pipeline, 0, +- clutter_texture_get_cogl_texture (texture)); +- gen_texcoords_and_draw_cogl_rectangle (self, priv->pick_pipeline, framebuffer); +- } +- else +- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, color); +-} +- + static void + clutter_texture_paint (ClutterActor *self) + { +@@ -767,12 +690,6 @@ clutter_texture_dispose (GObject *object) + priv->pipeline = NULL; + } + +- if (priv->pick_pipeline != NULL) +- { +- cogl_object_unref (priv->pick_pipeline); +- priv->pick_pipeline = NULL; +- } +- + G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object); + } + +@@ -944,7 +861,6 @@ clutter_texture_class_init (ClutterTextureClass *klass) + GParamSpec *pspec; + + actor_class->paint = clutter_texture_paint; +- actor_class->pick = clutter_texture_pick; + actor_class->get_paint_volume = clutter_texture_get_paint_volume; + actor_class->realize = clutter_texture_realize; + actor_class->unrealize = clutter_texture_unrealize; +@@ -1263,11 +1179,9 @@ clutter_texture_init (ClutterTexture *self) + priv->repeat_y = FALSE; + priv->sync_actor_size = TRUE; + priv->fbo_handle = NULL; +- priv->pick_pipeline = NULL; + priv->keep_aspect_ratio = FALSE; + priv->pick_with_alpha = FALSE; + priv->pick_with_alpha_supported = TRUE; +- priv->seen_create_pick_pipeline_warning = FALSE; + + if (G_UNLIKELY (texture_template_pipeline == NULL)) + { +@@ -3052,13 +2966,8 @@ clutter_texture_set_pick_with_alpha (ClutterTexture *texture, + if (priv->pick_with_alpha == pick_with_alpha) + return; + +- if (!pick_with_alpha && priv->pick_pipeline != NULL) +- { +- cogl_object_unref (priv->pick_pipeline); +- priv->pick_pipeline = NULL; +- } ++ g_assert (!pick_with_alpha); /* No longer supported */ + +- /* NB: the pick pipeline is created lazily when we first pick */ + priv->pick_with_alpha = pick_with_alpha; + + /* NB: actors are expected to call clutter_actor_queue_redraw when +diff --git a/clutter/tests/conform/actor-pick.c b/clutter/tests/conform/actor-pick.c +index 969b4920ac..2bf5954c73 100644 +--- a/clutter/tests/conform/actor-pick.c ++++ b/clutter/tests/conform/actor-pick.c +@@ -5,7 +5,6 @@ + #define STAGE_HEIGHT 480 + #define ACTORS_X 12 + #define ACTORS_Y 16 +-#define SHIFT_STEP STAGE_WIDTH / ACTORS_X + + typedef struct _State State; + +@@ -20,84 +19,11 @@ struct _State + gboolean pass; + }; + +-struct _ShiftEffect +-{ +- ClutterShaderEffect parent_instance; +-}; +- +-struct _ShiftEffectClass +-{ +- ClutterShaderEffectClass parent_class; +-}; +- +-typedef struct _ShiftEffect ShiftEffect; +-typedef struct _ShiftEffectClass ShiftEffectClass; +- +-#define TYPE_SHIFT_EFFECT (shift_effect_get_type ()) +- +-GType shift_effect_get_type (void); +- +-G_DEFINE_TYPE (ShiftEffect, +- shift_effect, +- CLUTTER_TYPE_SHADER_EFFECT); +- +-static void +-shader_paint (ClutterEffect *effect, +- ClutterEffectPaintFlags flags) +-{ +- ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect); +- float tex_width; +- ClutterActor *actor = +- clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); +- +- if (g_test_verbose ()) +- g_debug ("shader_paint"); +- +- clutter_shader_effect_set_shader_source (shader, +- "uniform sampler2D tex;\n" +- "uniform float step;\n" +- "void main (void)\n" +- "{\n" +- " cogl_color_out = texture2D(tex, vec2 (cogl_tex_coord_in[0].s + step,\n" +- " cogl_tex_coord_in[0].t));\n" +- "}\n"); +- +- tex_width = clutter_actor_get_width (actor); +- +- clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0); +- clutter_shader_effect_set_uniform (shader, "step", G_TYPE_FLOAT, 1, +- SHIFT_STEP / tex_width); +- +- CLUTTER_EFFECT_CLASS (shift_effect_parent_class)->paint (effect, flags); +-} +- +-static void +-shader_pick (ClutterEffect *effect, +- ClutterEffectPaintFlags flags) +-{ +- shader_paint (effect, flags); +-} +- +-static void +-shift_effect_class_init (ShiftEffectClass *klass) +-{ +- ClutterEffectClass *shader_class = CLUTTER_EFFECT_CLASS (klass); +- +- shader_class->paint = shader_paint; +- shader_class->pick = shader_pick; +-} +- +-static void +-shift_effect_init (ShiftEffect *self) +-{ +-} +- + static const char *test_passes[] = { + "No covering actor", + "Invisible covering actor", + "Clipped covering actor", + "Blur effect", +- "Shift effect", + }; + + static gboolean +@@ -165,30 +91,10 @@ on_timeout (gpointer data) + if (g_test_verbose ()) + g_print ("With blur effect:\n"); + } +- else if (test_num == 4) +- { +- if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) +- continue; +- +- clutter_actor_hide (over_actor); +- clutter_actor_remove_effect_by_name (CLUTTER_ACTOR (state->stage), +- "blur"); +- +- clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage), +- "shift", +- g_object_new (TYPE_SHIFT_EFFECT, +- NULL)); +- +- if (g_test_verbose ()) +- g_print ("With shift effect:\n"); +- } + + for (y = 0; y < ACTORS_Y; y++) + { +- if (test_num == 4) +- x = 1; +- else +- x = 0; ++ x = 0; + + for (; x < ACTORS_X; x++) + { +@@ -198,9 +104,6 @@ on_timeout (gpointer data) + + pick_x = x * state->actor_width + state->actor_width / 2; + +- if (test_num == 4) +- pick_x -= SHIFT_STEP; +- + actor = + clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage), + CLUTTER_PICK_ALL, +diff --git a/clutter/tests/conform/meson.build b/clutter/tests/conform/meson.build +index a9f2d7e20c..fffc9014c4 100644 +--- a/clutter/tests/conform/meson.build ++++ b/clutter/tests/conform/meson.build +@@ -42,7 +42,6 @@ clutter_conform_tests_deprecated_tests = [ + 'behaviours', + 'group', + 'rectangle', +- 'texture', + ] + + clutter_conform_tests = [] +diff --git a/clutter/tests/conform/texture.c b/clutter/tests/conform/texture.c +deleted file mode 100644 +index 392fd5c47e..0000000000 +--- a/clutter/tests/conform/texture.c ++++ /dev/null +@@ -1,84 +0,0 @@ +-#define CLUTTER_DISABLE_DEPRECATION_WARNINGS +-#include +-#include +- +-static CoglHandle +-make_texture (void) +-{ +- guint32 *data = g_malloc (100 * 100 * 4); +- int x; +- int y; +- +- for (y = 0; y < 100; y ++) +- for (x = 0; x < 100; x++) +- { +- if (x < 50 && y < 50) +- data[y * 100 + x] = 0xff00ff00; +- else +- data[y * 100 + x] = 0xff00ffff; +- } +- return cogl_texture_new_from_data (100, +- 100, +- COGL_TEXTURE_NONE, +- COGL_PIXEL_FORMAT_ARGB_8888, +- COGL_PIXEL_FORMAT_ARGB_8888, +- 400, +- (guchar *)data); +-} +- +-static void +-texture_pick_with_alpha (void) +-{ +- ClutterTexture *tex = CLUTTER_TEXTURE (clutter_texture_new ()); +- ClutterStage *stage = CLUTTER_STAGE (clutter_test_get_stage ()); +- ClutterActor *actor; +- +- clutter_texture_set_cogl_texture (tex, make_texture ()); +- +- clutter_actor_add_child (CLUTTER_ACTOR (stage), CLUTTER_ACTOR (tex)); +- +- clutter_actor_show (CLUTTER_ACTOR (stage)); +- +- if (g_test_verbose ()) +- { +- g_print ("\nstage = %p\n", stage); +- g_print ("texture = %p\n\n", tex); +- } +- +- clutter_texture_set_pick_with_alpha (tex, TRUE); +- if (g_test_verbose ()) +- g_print ("Testing with pick-with-alpha enabled:\n"); +- +- /* This should fall through and hit the stage: */ +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10); +- if (g_test_verbose ()) +- g_print ("actor @ (10, 10) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (stage)); +- +- /* The rest should hit the texture */ +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 10); +- if (g_test_verbose ()) +- g_print ("actor @ (90, 10) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 90); +- if (g_test_verbose ()) +- g_print ("actor @ (90, 90) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 90); +- if (g_test_verbose ()) +- g_print ("actor @ (10, 90) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +- +- clutter_texture_set_pick_with_alpha (tex, FALSE); +- if (g_test_verbose ()) +- g_print ("Testing with pick-with-alpha disabled:\n"); +- +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10); +- if (g_test_verbose ()) +- g_print ("actor @ (10, 10) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +-} +- +-CLUTTER_TEST_SUITE ( +- CLUTTER_TEST_UNIT ("/texture/pick-with-alpha", texture_pick_with_alpha) +-) +diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c +index ca4ca19a99..814199145a 100644 +--- a/src/compositor/meta-surface-actor.c ++++ b/src/compositor/meta-surface-actor.c +@@ -70,38 +70,23 @@ meta_surface_actor_pick (ClutterActor *actor, + else + { + int n_rects; +- float *rectangles; + int i; +- CoglPipeline *pipeline; +- CoglContext *ctx; +- CoglFramebuffer *fb; +- CoglColor cogl_color; + + n_rects = cairo_region_num_rectangles (priv->input_region); +- rectangles = g_alloca (sizeof (float) * 4 * n_rects); + + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; +- int pos = i * 4; ++ ClutterActorBox box; + + cairo_region_get_rectangle (priv->input_region, i, &rect); + +- rectangles[pos + 0] = rect.x; +- rectangles[pos + 1] = rect.y; +- rectangles[pos + 2] = rect.x + rect.width; +- rectangles[pos + 3] = rect.y + rect.height; ++ box.x1 = rect.x; ++ box.y1 = rect.y; ++ box.x2 = rect.x + rect.width; ++ box.y2 = rect.y + rect.height; ++ clutter_actor_pick_box (actor, &box); + } +- +- ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); +- fb = cogl_get_draw_framebuffer (); +- +- cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha); +- +- pipeline = cogl_pipeline_new (ctx); +- cogl_pipeline_set_color (pipeline, &cogl_color); +- cogl_framebuffer_draw_rectangles (fb, pipeline, rectangles, n_rects); +- cogl_object_unref (pipeline); + } + + clutter_actor_iter_init (&iter, actor); +-- +2.29.2 + + +From 254e93de8d60393ca94fa430c0acc6f6a7b9516e Mon Sep 17 00:00:00 2001 +From: Iain Lane +Date: Mon, 9 Sep 2019 10:17:22 +0100 +Subject: [PATCH 3/4] build: Compile with `-ffloat-store` on x86 (32 bit) + +GCC's manpage says that this flag does the following: + + Do not store floating-point variables in registers, and inhibit other + options that might change whether a floating-point value is taken from + a register or memory. + + This option prevents undesirable excess precision on machines such as + the 68000 where the floating registers (of the 68881) keep more + precision than a "double" is supposed to have. Similarly for the x86 + architecture. For most programs, the excess precision does only good, + but a few programs rely on the precise definition of IEEE floating + point. + +We rely on this behaviour in our fork of clutter. When performing +floating point computations on x86, we are getting the wrong results +because of this architecture's use of the CPU's extended (x87, non-IEEE +confirming) precision by default. If we enable `-ffloat-store` here, +then we'll get the same results everywhere by storing into variables +instead of registers. This does not remove the need to be correct when +handling floats, but it does mean we don't need to be more correct than +the IEEE spec requires. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/785 +--- + meson.build | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/meson.build b/meson.build +index 8ef592bc58..e1edb78ba7 100644 +--- a/meson.build ++++ b/meson.build +@@ -267,6 +267,9 @@ foreach function : required_functions + endif + endforeach + ++if host_machine.cpu_family() == 'x86' ++ add_project_arguments('-ffloat-store', language: 'c') ++endif + add_project_arguments('-D_GNU_SOURCE', language: 'c') + + all_warnings = [ +-- +2.29.2 + + +From 2d42caef14772984344e62ce40957d3b40e1f2b6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Thu, 19 Sep 2019 11:27:50 +0200 +Subject: [PATCH 4/4] stage: Compute view perspective when parameters changed + +Clutter stage used to compute the initial projection using a fixed z +translation which wasn't matching the one we computed in +calculate_z_translation(). +This caused to have a wrong initial projection on startup which was then +correctly recomputed only at the first paint. + +However, since this calculation doesn't depend on view, but only on viewport +size, perspective's fovy and z_near we can safely do this at startup and +only when any of those parameters change. + +Then we can move the computation out _clutter_stage_maybe_setup_viewport() +since the cogl framebuffer viewport sizes aren't affecting this. + +Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1639 +https://gitlab.gnome.org/GNOME/mutter/merge_requests/803 +--- + clutter/clutter/clutter-stage.c | 104 +++++++++++++++----------------- + 1 file changed, 47 insertions(+), 57 deletions(-) + +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 7d88d5752f..0cfa87486e 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -234,6 +234,7 @@ static void capture_view_into (ClutterStage *stage, + cairo_rectangle_int_t *rect, + uint8_t *data, + int stride); ++static void clutter_stage_update_view_perspective (ClutterStage *stage); + + static void clutter_container_iface_init (ClutterContainerIface *iface); + +@@ -2492,29 +2493,6 @@ clutter_stage_init (ClutterStage *self) + clutter_actor_set_background_color (CLUTTER_ACTOR (self), + &default_stage_color); + +- priv->perspective.fovy = 60.0; /* 60 Degrees */ +- priv->perspective.aspect = (float) geom.width / (float) geom.height; +- priv->perspective.z_near = 0.1; +- priv->perspective.z_far = 100.0; +- +- cogl_matrix_init_identity (&priv->projection); +- cogl_matrix_perspective (&priv->projection, +- priv->perspective.fovy, +- priv->perspective.aspect, +- priv->perspective.z_near, +- priv->perspective.z_far); +- cogl_matrix_get_inverse (&priv->projection, +- &priv->inverse_projection); +- cogl_matrix_init_identity (&priv->view); +- cogl_matrix_view_2d_in_perspective (&priv->view, +- priv->perspective.fovy, +- priv->perspective.aspect, +- priv->perspective.z_near, +- 50, /* distance to 2d plane */ +- geom.width, +- geom.height); +- +- + /* FIXME - remove for 2.0 */ + priv->fog.z_near = 1.0; + priv->fog.z_far = 2.0; +@@ -2682,6 +2660,7 @@ clutter_stage_set_perspective (ClutterStage *stage, + priv->has_custom_perspective = TRUE; + + clutter_stage_set_perspective_internal (stage, perspective); ++ clutter_stage_update_view_perspective (stage); + } + + /** +@@ -2808,6 +2787,7 @@ _clutter_stage_set_viewport (ClutterStage *stage, + priv->viewport[2] = width; + priv->viewport[3] = height; + ++ clutter_stage_update_view_perspective (stage); + _clutter_stage_dirty_viewport (stage); + + queue_full_redraw (stage); +@@ -3788,6 +3768,50 @@ calculate_z_translation (float z_near) + + z_near; + } + ++static void ++clutter_stage_update_view_perspective (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ ClutterPerspective perspective; ++ float z_2d; ++ ++ perspective = priv->perspective; ++ ++ /* Ideally we want to regenerate the perspective matrix whenever ++ * the size changes but if the user has provided a custom matrix ++ * then we don't want to override it */ ++ if (!priv->has_custom_perspective) ++ { ++ perspective.fovy = 60.0; /* 60 Degrees */ ++ perspective.z_near = 0.1; ++ perspective.aspect = priv->viewport[2] / priv->viewport[3]; ++ z_2d = calculate_z_translation (perspective.z_near); ++ ++ /* NB: z_2d is only enough room for 85% of the stage_height between ++ * the stage and the z_near plane. For behind the stage plane we ++ * want a more consistent gap of 10 times the stage_height before ++ * hitting the far plane so we calculate that relative to the final ++ * height of the stage plane at the z_2d_distance we got... */ ++ perspective.z_far = z_2d + ++ tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f; ++ ++ clutter_stage_set_perspective_internal (stage, &perspective); ++ } ++ else ++ { ++ z_2d = calculate_z_translation (perspective.z_near); ++ } ++ ++ cogl_matrix_init_identity (&priv->view); ++ cogl_matrix_view_2d_in_perspective (&priv->view, ++ perspective.fovy, ++ perspective.aspect, ++ perspective.z_near, ++ z_2d, ++ priv->viewport[2], ++ priv->viewport[3]); ++} ++ + void + _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + ClutterStageView *view) +@@ -3797,7 +3821,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + if (clutter_stage_view_is_dirty_viewport (view)) + { + cairo_rectangle_int_t view_layout; +- ClutterPerspective perspective; + float fb_scale; + float viewport_offset_x; + float viewport_offset_y; +@@ -3805,7 +3828,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + float viewport_y; + float viewport_width; + float viewport_height; +- float z_2d; + + CLUTTER_NOTE (PAINT, + "Setting up the viewport { w:%f, h:%f }", +@@ -3825,38 +3847,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + clutter_stage_view_set_viewport (view, + viewport_x, viewport_y, + viewport_width, viewport_height); +- +- perspective = priv->perspective; +- +- /* Ideally we want to regenerate the perspective matrix whenever +- * the size changes but if the user has provided a custom matrix +- * then we don't want to override it */ +- if (!priv->has_custom_perspective) +- { +- perspective.aspect = priv->viewport[2] / priv->viewport[3]; +- z_2d = calculate_z_translation (perspective.z_near); +- +- /* NB: z_2d is only enough room for 85% of the stage_height between +- * the stage and the z_near plane. For behind the stage plane we +- * want a more consistent gap of 10 times the stage_height before +- * hitting the far plane so we calculate that relative to the final +- * height of the stage plane at the z_2d_distance we got... */ +- perspective.z_far = z_2d + +- tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f; +- +- clutter_stage_set_perspective_internal (stage, &perspective); +- } +- else +- z_2d = calculate_z_translation (perspective.z_near); +- +- cogl_matrix_init_identity (&priv->view); +- cogl_matrix_view_2d_in_perspective (&priv->view, +- perspective.fovy, +- perspective.aspect, +- perspective.z_near, +- z_2d, +- priv->viewport[2], +- priv->viewport[3]); + } + + if (clutter_stage_view_is_dirty_projection (view)) +-- +2.29.2 + diff --git a/SOURCES/handle-hotplug-better.patch b/SOURCES/handle-hotplug-better.patch new file mode 100644 index 0000000..57668d9 --- /dev/null +++ b/SOURCES/handle-hotplug-better.patch @@ -0,0 +1,614 @@ +From d442ef48412e3dc1b24a9f97b02ee3383404d501 Mon Sep 17 00:00:00 2001 +From: Emil Velikov +Date: Wed, 12 Jun 2019 16:58:54 +0000 +Subject: [PATCH 1/8] renderer/native: add missing eglTerminate in EGLDevice + error path +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently the EGLDevice code gets the display and calls eglInitialize. +As a follow-up it checks the required EGL extensions - technically it +could check the EGL device extensions earlier. + +In either case, eglTerminate is missing. Thus the connection to the +display was still bound. + +This was highlighted with Mesa commit d6edccee8da ("egl: add +EGL_platform_device support") + amdgpu. + +In that case, since the eglTerminate is missing, we end up reusing the +underlying amdgpu_device due to some caching in libdrm_amdgpu. The +latter in itself being a good solution since it allows buffer sharing +across primary and render node of the same device. + +Note: we should really get this in branches all the way back to 3.30. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/619 + +Fixes: 934184e23 ("MetaRendererNative: Add EGLDevice based rendering support") +Cc: Jonas Ådahl +Signed-off-by: Emil Velikov + + +(cherry picked from commit 9213574870faee7fe40609791fc48f4b44f861c0) +--- + src/backends/native/meta-renderer-native.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index dbfc97aae..207b654fa 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -4038,6 +4038,7 @@ create_renderer_gpu_data_egl_device (MetaRendererNative *renderer_native, + G_IO_ERROR_FAILED, + "Missing EGL extensions required for EGLDevice renderer: %s", + missing_extensions_str); ++ meta_egl_terminate (egl, egl_display, NULL); + g_free (missing_extensions_str); + g_free (missing_extensions); + return NULL; +-- +2.24.1 + + +From e18dfc888343585d21b3f64568571009c4967a95 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 17 Jun 2019 18:18:12 +0200 +Subject: [PATCH 2/8] renderer/native: Use g_set_error() instead of + _cogl_set_error() + +It's even a GError, so lets use the proper API. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/622 +(cherry picked from commit 1efb32d3000ca06ee3cfcc146dc812866d243619) +--- + src/backends/native/meta-renderer-native.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index 207b654fa..e7aa6f389 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -1277,7 +1277,7 @@ meta_renderer_native_egl_context_created (CoglDisplay *cogl_display, + cogl_display_egl->dummy_surface, + cogl_display_egl->egl_context)) + { +- _cogl_set_error (error, COGL_WINSYS_ERROR, ++ g_set_error (error, COGL_WINSYS_ERROR, + COGL_WINSYS_ERROR_CREATE_CONTEXT, + "Failed to make context current"); + return FALSE; +-- +2.24.1 + + +From 1947a81db93624d57471ce1edf5548c7774c3569 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 17 Jun 2019 18:18:42 +0200 +Subject: [PATCH 3/8] renderer/native: Make sure we're not destroying an active + EGLSurface + +When making a new surface/context pair current, mesa may want to flush +the old context. Make sure we don't try to flush any freed memory by +unmaking a surface/context pair current before freeing it. + +Not doing this results in the following valgrind warnings: + +==15986== Invalid read of size 8 +==15986== at 0x69A6D80: dri_flush_front_buffer (gbm_dri.c:92) +==15986== by 0x1750D458: intel_flush_front (brw_context.c:251) +==15986== by 0x1750D4BB: intel_glFlush (brw_context.c:296) +==15986== by 0x1739D8DD: dri2_make_current (egl_dri2.c:1461) +==15986== by 0x17393A3A: eglMakeCurrent (eglapi.c:869) +==15986== by 0x54381FB: InternalMakeCurrentVendor (in /home/jonas/Dev/gnome/install/lib/libEGL.so.1.1.0) +==15986== by 0x5438515: eglMakeCurrent (in /home/jonas/Dev/gnome/install/lib/libEGL.so.1.1.0) +==15986== by 0x522A782: _cogl_winsys_egl_make_current (cogl-winsys-egl.c:303) +==15986== by 0x49B64C8: meta_renderer_native_create_view (meta-renderer-native.c:3076) +==15986== by 0x48D26E7: meta_renderer_create_view (meta-renderer.c:78) +==15986== by 0x48D277A: meta_renderer_rebuild_views (meta-renderer.c:111) +==15986== by 0x49BF46E: meta_stage_native_rebuild_views (meta-stage-native.c:142) +==15986== Address 0x1b076600 is 0 bytes inside a block of size 48 free'd +==15986== at 0x4839A0C: free (vg_replace_malloc.c:540) +==15986== by 0x49B59F3: meta_renderer_native_release_onscreen (meta-renderer-native.c:2651) +==15986== by 0x5211441: _cogl_onscreen_free (cogl-onscreen.c:167) +==15986== by 0x5210D81: _cogl_object_onscreen_indirect_free (cogl-onscreen.c:51) +==15986== by 0x51D0066: _cogl_object_default_unref (cogl-object.c:103) +==15986== by 0x520F989: _cogl_framebuffer_unref (cogl-framebuffer.c:1814) +==15986== by 0x51D00B1: cogl_object_unref (cogl-object.c:115) +==15986== by 0x536F3C7: clutter_stage_view_dispose (clutter-stage-view.c:304) +==15986== by 0x4B7DAF2: g_object_unref (gobject.c:3309) +==15986== by 0x4A9596C: g_list_foreach (glist.c:1013) +==15986== by 0x4A9599A: g_list_free_full (glist.c:223) +==15986== by 0x48D2737: meta_renderer_rebuild_views (meta-renderer.c:100) +==15986== Block was alloc'd at +==15986== at 0x483AB1A: calloc (vg_replace_malloc.c:762) +==15986== by 0x69A76B2: gbm_dri_surface_create (gbm_dri.c:1252) +==15986== by 0x69A6BFE: gbm_surface_create (gbm.c:600) +==15986== by 0x49B4E29: meta_renderer_native_create_surface_gbm (meta-renderer-native.c:2221) +==15986== by 0x49B57DB: meta_onscreen_native_allocate (meta-renderer-native.c:2569) +==15986== by 0x49B6423: meta_renderer_native_create_view (meta-renderer-native.c:3062) +==15986== by 0x48D26E7: meta_renderer_create_view (meta-renderer.c:78) +==15986== by 0x48D277A: meta_renderer_rebuild_views (meta-renderer.c:111) +==15986== by 0x49BF46E: meta_stage_native_rebuild_views (meta-stage-native.c:142) +==15986== by 0x49A75B5: meta_backend_native_update_screen_size (meta-backend-native.c:520) +==15986== by 0x48B01BB: meta_backend_sync_screen_size (meta-backend.c:224) +==15986== by 0x48B09B7: meta_backend_real_post_init (meta-backend.c:501) + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/622 +(cherry picked from commit 56ddaaa3809240a357b5e19b5789d1aa49aaecc3) +--- + src/backends/native/meta-renderer-native.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index e7aa6f389..b7bc3121a 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -3040,6 +3040,8 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) + { + CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); + CoglContext *cogl_context = framebuffer->context; ++ CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); ++ CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; + CoglRenderer *cogl_renderer = cogl_context->display->renderer; + CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; + CoglOnscreenEGL *onscreen_egl = onscreen->winsys; +@@ -3052,6 +3054,17 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) + + onscreen_native = onscreen_egl->platform; + ++ if (onscreen_egl->egl_surface != EGL_NO_SURFACE && ++ (cogl_display_egl->current_draw_surface == onscreen_egl->egl_surface || ++ cogl_display_egl->current_read_surface == onscreen_egl->egl_surface)) ++ { ++ if (!_cogl_winsys_egl_make_current (cogl_display, ++ cogl_display_egl->dummy_surface, ++ cogl_display_egl->dummy_surface, ++ cogl_display_egl->egl_context)) ++ g_warning ("Failed to clear current context"); ++ } ++ + g_list_free_full (onscreen_native->pending_page_flip_retries, + (GDestroyNotify) retry_page_flip_data_free); + if (onscreen_native->retry_page_flips_source) +-- +2.24.1 + + +From 60551e5e6f984a7ed3ba3339f027ed7b37f802c4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 17 Jun 2019 19:16:12 +0200 +Subject: [PATCH 4/8] renderer/native: Fix EGLSurface destruction order + +Make sure to destroy the EGL surface after releasing held buffers, +otherwise we'll get the following valgrind warnings: + +==24016== Invalid read of size 8 +==24016== at 0x1739943F: release_buffer (platform_drm.c:73) +==24016== by 0x49AC355: meta_drm_buffer_gbm_finalize (meta-drm-buffer-gbm.c:213) +==24016== by 0x4B75B61: g_object_unref (gobject.c:3346) +==24016== by 0x49B4B41: free_current_bo (meta-renderer-native.c:991) +==24016== by 0x49B816F: meta_renderer_native_release_onscreen (meta-renderer-native.c:2971) +==24016== by 0x5209441: _cogl_onscreen_free (cogl-onscreen.c:167) +==24016== by 0x5208D81: _cogl_object_onscreen_indirect_free (cogl-onscreen.c:51) +==24016== by 0x51C8066: _cogl_object_default_unref (cogl-object.c:103) +==24016== by 0x5207989: _cogl_framebuffer_unref (cogl-framebuffer.c:1814) +==24016== by 0x51C80B1: cogl_object_unref (cogl-object.c:115) +==24016== by 0x53673C7: clutter_stage_view_dispose (clutter-stage-view.c:304) +==24016== by 0x4B75AF2: g_object_unref (gobject.c:3309) +==24016== Address 0x18e742a8 is 536 bytes inside a block of size 784 free'd +==24016== at 0x4839A0C: free (vg_replace_malloc.c:540) +==24016== by 0x17399764: dri2_drm_destroy_surface (platform_drm.c:231) +==24016== by 0x1738550A: eglDestroySurface (eglapi.c:1145) +==24016== by 0x5440286: eglDestroySurface (in /home/jonas/Dev/gnome/install/lib/libEGL.so.1.1.0) +==24016== by 0x49613A5: meta_egl_destroy_surface (meta-egl.c:432) +==24016== by 0x49B80F9: meta_renderer_native_release_onscreen (meta-renderer-native.c:2954) +==24016== by 0x5209441: _cogl_onscreen_free (cogl-onscreen.c:167) +==24016== by 0x5208D81: _cogl_object_onscreen_indirect_free (cogl-onscreen.c:51) +==24016== by 0x51C8066: _cogl_object_default_unref (cogl-object.c:103) +==24016== by 0x5207989: _cogl_framebuffer_unref (cogl-framebuffer.c:1814) +==24016== by 0x51C80B1: cogl_object_unref (cogl-object.c:115) +==24016== by 0x53673C7: clutter_stage_view_dispose (clutter-stage-view.c:304) +==24016== Block was alloc'd at +==24016== at 0x483AB1A: calloc (vg_replace_malloc.c:762) +==24016== by 0x173997AE: dri2_drm_create_window_surface (platform_drm.c:145) +==24016== by 0x17388906: _eglCreateWindowSurfaceCommon (eglapi.c:929) +==24016== by 0x5440197: eglCreateWindowSurface (in /home/jonas/Dev/gnome/install/lib/libEGL.so.1.1.0) +==24016== by 0x49612FF: meta_egl_create_window_surface (meta-egl.c:396) +==24016== by 0x49B752E: meta_renderer_native_create_surface_gbm (meta-renderer-native.c:2538) +==24016== by 0x49B7E6C: meta_onscreen_native_allocate (meta-renderer-native.c:2870) +==24016== by 0x49B8BCF: meta_renderer_native_create_view (meta-renderer-native.c:3387) +==24016== by 0x48D274B: meta_renderer_create_view (meta-renderer.c:78) +==24016== by 0x48D27DE: meta_renderer_rebuild_views (meta-renderer.c:111) +==24016== by 0x49BB4FB: meta_stage_native_rebuild_views (meta-stage-native.c:142) +==24016== by 0x49A733C: meta_backend_native_update_screen_size (meta-backend-native.c:517) + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/622 +(cherry picked from commit d9fb11b04319c00fd89715dd9207fe54e1d18c2d) +--- + src/backends/native/meta-renderer-native.c | 38 +++++++++++++++------- + 1 file changed, 27 insertions(+), 11 deletions(-) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index b7bc3121a..62c27c191 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -3035,6 +3035,28 @@ meta_onscreen_native_allocate (CoglOnscreen *onscreen, + return TRUE; + } + ++static void ++destroy_egl_surface (CoglOnscreen *onscreen) ++{ ++ CoglOnscreenEGL *onscreen_egl = onscreen->winsys; ++ ++ if (onscreen_egl->egl_surface != EGL_NO_SURFACE) ++ { ++ MetaOnscreenNative *onscreen_native = onscreen_egl->platform; ++ MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); ++ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); ++ CoglContext *cogl_context = framebuffer->context; ++ CoglRenderer *cogl_renderer = cogl_context->display->renderer; ++ CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; ++ ++ meta_egl_destroy_surface (egl, ++ cogl_renderer_egl->edpy, ++ onscreen_egl->egl_surface, ++ NULL); ++ onscreen_egl->egl_surface = EGL_NO_SURFACE; ++ } ++} ++ + static void + meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) + { +@@ -3077,17 +3099,6 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) + g_source_destroy); + } + +- if (onscreen_egl->egl_surface != EGL_NO_SURFACE) +- { +- MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); +- +- meta_egl_destroy_surface (egl, +- cogl_renderer_egl->edpy, +- onscreen_egl->egl_surface, +- NULL); +- onscreen_egl->egl_surface = EGL_NO_SURFACE; +- } +- + renderer_gpu_data = + meta_renderer_native_get_gpu_data (onscreen_native->renderer_native, + onscreen_native->render_gpu); +@@ -3100,6 +3111,8 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) + + free_current_bo (onscreen); + ++ destroy_egl_surface (onscreen); ++ + if (onscreen_native->gbm.surface) + { + gbm_surface_destroy (onscreen_native->gbm.surface); +@@ -3110,6 +3123,9 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) + case META_RENDERER_NATIVE_MODE_EGL_DEVICE: + release_dumb_fb (&onscreen_native->egl.dumb_fb, + onscreen_native->render_gpu); ++ ++ destroy_egl_surface (onscreen); ++ + if (onscreen_native->egl.stream != EGL_NO_STREAM_KHR) + { + MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); +-- +2.24.1 + + +From c447010a23edc03c7a1103b477972ad666c2600f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 19 Jun 2019 20:55:48 +0200 +Subject: [PATCH 5/8] renderer/native: Remove left-over function declarations + +There are no callers and no definitions of these. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/655 +--- + src/backends/native/meta-renderer-native.h | 12 ------------ + 1 file changed, 12 deletions(-) + +diff --git a/src/backends/native/meta-renderer-native.h b/src/backends/native/meta-renderer-native.h +index a006dcbe7..8468208e1 100644 +--- a/src/backends/native/meta-renderer-native.h ++++ b/src/backends/native/meta-renderer-native.h +@@ -55,18 +55,6 @@ gboolean meta_renderer_native_supports_mirroring (MetaRendererNative *renderer_n + + void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native); + +-gboolean meta_renderer_native_set_legacy_view_size (MetaRendererNative *renderer_native, +- MetaRendererView *view, +- int width, +- int height, +- GError **error); +- +-void meta_renderer_native_set_ignore_crtc (MetaRendererNative *renderer_native, +- uint32_t id, +- gboolean ignore); +- +-MetaRendererView * meta_renderer_native_create_legacy_view (MetaRendererNative *renderer_native); +- + void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native); + + int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native); +-- +2.24.1 + + +From 7f97403d12df19cf936a341cc218743ec339aa0a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 19 Jun 2019 20:57:14 +0200 +Subject: [PATCH 6/8] renderer/native: Queue mode reset from new rebuild_views + vfunc + +Simplify the call site a bit and make the native renderer know it should +queue mode reset itself when views have been rebuilt. This is done +partly due to more things needing to be dealt with after views have been +rebuilt. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/655 +--- + src/backends/meta-renderer.c | 8 ++++++++ + src/backends/meta-renderer.h | 1 + + src/backends/native/meta-renderer-native.c | 17 ++++++++++++++++- + src/backends/native/meta-renderer-native.h | 2 -- + src/backends/native/meta-stage-native.c | 1 - + 5 files changed, 25 insertions(+), 4 deletions(-) + +diff --git a/src/backends/meta-renderer.c b/src/backends/meta-renderer.c +index 28637437b..87ba9f9f0 100644 +--- a/src/backends/meta-renderer.c ++++ b/src/backends/meta-renderer.c +@@ -90,6 +90,12 @@ meta_renderer_create_view (MetaRenderer *renderer, + */ + void + meta_renderer_rebuild_views (MetaRenderer *renderer) ++{ ++ return META_RENDERER_GET_CLASS (renderer)->rebuild_views (renderer); ++} ++ ++static void ++meta_renderer_real_rebuild_views (MetaRenderer *renderer) + { + MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); + MetaBackend *backend = meta_get_backend (); +@@ -181,4 +187,6 @@ meta_renderer_class_init (MetaRendererClass *klass) + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_renderer_finalize; ++ ++ klass->rebuild_views = meta_renderer_real_rebuild_views; + } +diff --git a/src/backends/meta-renderer.h b/src/backends/meta-renderer.h +index dae52cb9a..478baee91 100644 +--- a/src/backends/meta-renderer.h ++++ b/src/backends/meta-renderer.h +@@ -43,6 +43,7 @@ struct _MetaRendererClass + CoglRenderer * (* create_cogl_renderer) (MetaRenderer *renderer); + MetaRendererView * (* create_view) (MetaRenderer *renderer, + MetaLogicalMonitor *logical_monitor); ++ void (* rebuild_views) (MetaRenderer *renderer); + }; + + CoglRenderer * meta_renderer_create_cogl_renderer (MetaRenderer *renderer); +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index 62c27c191..70e1c4f9d 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -258,6 +258,9 @@ cogl_pixel_format_from_drm_format (uint32_t drm_format, + CoglPixelFormat *out_format, + CoglTextureComponents *out_components); + ++static void ++meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native); ++ + static MetaBackend * + backend_from_renderer_native (MetaRendererNative *renderer_native) + { +@@ -3186,7 +3189,7 @@ meta_renderer_native_supports_mirroring (MetaRendererNative *renderer_native) + return TRUE; + } + +-void ++static void + meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native) + { + MetaRenderer *renderer = META_RENDERER (renderer_native); +@@ -3552,6 +3555,17 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + return view; + } + ++static void ++meta_renderer_native_rebuild_views (MetaRenderer *renderer) ++{ ++ MetaRendererClass *parent_renderer_class = ++ META_RENDERER_CLASS (meta_renderer_native_parent_class); ++ ++ parent_renderer_class->rebuild_views (renderer); ++ ++ meta_renderer_native_queue_modes_reset (META_RENDERER_NATIVE (renderer)); ++} ++ + void + meta_renderer_native_finish_frame (MetaRendererNative *renderer_native) + { +@@ -4350,6 +4364,7 @@ meta_renderer_native_class_init (MetaRendererNativeClass *klass) + + renderer_class->create_cogl_renderer = meta_renderer_native_create_cogl_renderer; + renderer_class->create_view = meta_renderer_native_create_view; ++ renderer_class->rebuild_views = meta_renderer_native_rebuild_views; + + obj_props[PROP_MONITOR_MANAGER] = + g_param_spec_object ("monitor-manager", +diff --git a/src/backends/native/meta-renderer-native.h b/src/backends/native/meta-renderer-native.h +index 8468208e1..9eecdead1 100644 +--- a/src/backends/native/meta-renderer-native.h ++++ b/src/backends/native/meta-renderer-native.h +@@ -53,8 +53,6 @@ struct gbm_device * meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms); + + gboolean meta_renderer_native_supports_mirroring (MetaRendererNative *renderer_native); + +-void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native); +- + void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native); + + int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native); +diff --git a/src/backends/native/meta-stage-native.c b/src/backends/native/meta-stage-native.c +index add3e81fd..9b9c45ef3 100644 +--- a/src/backends/native/meta-stage-native.c ++++ b/src/backends/native/meta-stage-native.c +@@ -140,7 +140,6 @@ meta_stage_native_rebuild_views (MetaStageNative *stage_native) + ClutterActor *stage = meta_backend_get_stage (backend); + + meta_renderer_rebuild_views (renderer); +- meta_renderer_native_queue_modes_reset (META_RENDERER_NATIVE (renderer)); + clutter_stage_update_resource_scales (CLUTTER_STAGE (stage)); + ensure_frame_callbacks (stage_native); + } +-- +2.24.1 + + +From 025054c93e43e8359c9ecafb6edea1eb4b7ad681 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 19 Jun 2019 21:14:05 +0200 +Subject: [PATCH 7/8] renderer/native: Discard page flip retries when + rebuilding views + +Rebuilding views means we don't care to retry page flip attempts for +previous views, especially since connectors may have been disconnected, +making a page flip retry hit an assert a flipped CRTC has connectors +associated with it. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/655 +--- + src/backends/native/meta-renderer-native.c | 50 +++++++++++++++++----- + 1 file changed, 39 insertions(+), 11 deletions(-) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index 70e1c4f9d..3cd01bcb7 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -3060,6 +3060,24 @@ destroy_egl_surface (CoglOnscreen *onscreen) + } + } + ++static void ++discard_onscreen_page_flip_retries (MetaOnscreenNative *onscreen_native) ++{ ++ g_list_free_full (onscreen_native->pending_page_flip_retries, ++ (GDestroyNotify) retry_page_flip_data_free); ++ onscreen_native->pending_page_flip_retries = NULL; ++ ++ if (onscreen_native->retry_page_flips_source) ++ { ++ MetaBackend *backend = ++ backend_from_renderer_native (onscreen_native->renderer_native); ++ ++ meta_backend_thaw_updates (backend); ++ g_clear_pointer (&onscreen_native->retry_page_flips_source, ++ g_source_destroy); ++ } ++} ++ + static void + meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) + { +@@ -3090,17 +3108,7 @@ meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) + g_warning ("Failed to clear current context"); + } + +- g_list_free_full (onscreen_native->pending_page_flip_retries, +- (GDestroyNotify) retry_page_flip_data_free); +- if (onscreen_native->retry_page_flips_source) +- { +- MetaBackend *backend = +- backend_from_renderer_native (onscreen_native->renderer_native); +- +- meta_backend_thaw_updates (backend); +- g_clear_pointer (&onscreen_native->retry_page_flips_source, +- g_source_destroy); +- } ++ discard_onscreen_page_flip_retries (onscreen_native); + + renderer_gpu_data = + meta_renderer_native_get_gpu_data (onscreen_native->renderer_native, +@@ -3555,12 +3563,32 @@ meta_renderer_native_create_view (MetaRenderer *renderer, + return view; + } + ++static void ++discard_page_flip_retries (MetaRenderer *renderer) ++{ ++ GList *l; ++ ++ for (l = meta_renderer_get_views (renderer); l; l = l->next) ++ { ++ ClutterStageView *stage_view = l->data; ++ CoglFramebuffer *framebuffer = ++ clutter_stage_view_get_onscreen (stage_view); ++ CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); ++ CoglOnscreenEGL *onscreen_egl = onscreen->winsys; ++ MetaOnscreenNative *onscreen_native = onscreen_egl->platform; ++ ++ discard_onscreen_page_flip_retries (onscreen_native); ++ } ++} ++ + static void + meta_renderer_native_rebuild_views (MetaRenderer *renderer) + { + MetaRendererClass *parent_renderer_class = + META_RENDERER_CLASS (meta_renderer_native_parent_class); + ++ discard_page_flip_retries (renderer); ++ + parent_renderer_class->rebuild_views (renderer); + + meta_renderer_native_queue_modes_reset (META_RENDERER_NATIVE (renderer)); +-- +2.24.1 + + +From f4fdec6003e2cf9fa4b1882e92faf1da64e6052e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 27 Nov 2019 17:34:35 +0100 +Subject: [PATCH 8/8] =?UTF-8?q?crtc-kms:=20Ignore=2090=C2=B0=20rotations?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +They tend to require special modifiers or won't work at all; ignore +them. +--- + src/backends/native/meta-crtc-kms.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/src/backends/native/meta-crtc-kms.c b/src/backends/native/meta-crtc-kms.c +index 8c2fbfe3c..8374376d5 100644 +--- a/src/backends/native/meta-crtc-kms.c ++++ b/src/backends/native/meta-crtc-kms.c +@@ -368,12 +368,8 @@ parse_transforms (MetaCrtc *crtc, + + if (strcmp (prop->enums[i].name, "rotate-0") == 0) + transform = META_MONITOR_TRANSFORM_NORMAL; +- else if (strcmp (prop->enums[i].name, "rotate-90") == 0) +- transform = META_MONITOR_TRANSFORM_90; + else if (strcmp (prop->enums[i].name, "rotate-180") == 0) + transform = META_MONITOR_TRANSFORM_180; +- else if (strcmp (prop->enums[i].name, "rotate-270") == 0) +- transform = META_MONITOR_TRANSFORM_270; + + if (transform != -1) + { +-- +2.24.1 + diff --git a/SOURCES/idle-monitor-reset-fix.patch b/SOURCES/idle-monitor-reset-fix.patch new file mode 100644 index 0000000..b67e015 --- /dev/null +++ b/SOURCES/idle-monitor-reset-fix.patch @@ -0,0 +1,128 @@ +From 35333114a991440d671e3642170aa080df45a171 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 16 Sep 2019 16:17:48 +0200 +Subject: [PATCH 1/3] idle-monitor: Make helper function static + +It wasn't used outside the file, so no reason to not have it static. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/799 +--- + src/backends/meta-idle-monitor-private.h | 1 - + src/backends/meta-idle-monitor.c | 8 ++++---- + 2 files changed, 4 insertions(+), 5 deletions(-) + +diff --git a/src/backends/meta-idle-monitor-private.h b/src/backends/meta-idle-monitor-private.h +index 93948b14b..cc08f8c8e 100644 +--- a/src/backends/meta-idle-monitor-private.h ++++ b/src/backends/meta-idle-monitor-private.h +@@ -54,7 +54,6 @@ struct _MetaIdleMonitorClass + GObjectClass parent_class; + }; + +-void _meta_idle_monitor_watch_fire (MetaIdleMonitorWatch *watch); + void meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor); + + #endif /* META_IDLE_MONITOR_PRIVATE_H */ +diff --git a/src/backends/meta-idle-monitor.c b/src/backends/meta-idle-monitor.c +index e83d6c778..de1c7e0ba 100644 +--- a/src/backends/meta-idle-monitor.c ++++ b/src/backends/meta-idle-monitor.c +@@ -54,8 +54,8 @@ static GParamSpec *obj_props[PROP_LAST]; + + G_DEFINE_TYPE (MetaIdleMonitor, meta_idle_monitor, G_TYPE_OBJECT) + +-void +-_meta_idle_monitor_watch_fire (MetaIdleMonitorWatch *watch) ++static void ++meta_idle_monitor_watch_fire (MetaIdleMonitorWatch *watch) + { + MetaIdleMonitor *monitor; + guint id; +@@ -324,7 +324,7 @@ idle_monitor_dispatch_timeout (GSource *source, + if (ready_time > now) + return G_SOURCE_CONTINUE; + +- _meta_idle_monitor_watch_fire (watch); ++ meta_idle_monitor_watch_fire (watch); + g_source_set_ready_time (watch->timeout_source, -1); + + return G_SOURCE_CONTINUE; +@@ -511,7 +511,7 @@ meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor) + + if (watch->timeout_msec == 0) + { +- _meta_idle_monitor_watch_fire ((MetaIdleMonitorWatch *) watch); ++ meta_idle_monitor_watch_fire ((MetaIdleMonitorWatch *) watch); + } + else + { +-- +2.23.0 + + +From 07276cf94d84489d450c17b7dec5a8075c60440a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 16 Sep 2019 16:36:05 +0200 +Subject: [PATCH 2/3] idle-monitor: Remove redundant type cast + +No need to type cast a `MetaIdleMonitorWatch *` to a +`MetaIdleMonitorWatch *`. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/799 +--- + src/backends/meta-idle-monitor.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/backends/meta-idle-monitor.c b/src/backends/meta-idle-monitor.c +index de1c7e0ba..e5124abc1 100644 +--- a/src/backends/meta-idle-monitor.c ++++ b/src/backends/meta-idle-monitor.c +@@ -511,7 +511,7 @@ meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor) + + if (watch->timeout_msec == 0) + { +- meta_idle_monitor_watch_fire ((MetaIdleMonitorWatch *) watch); ++ meta_idle_monitor_watch_fire (watch); + } + else + { +-- +2.23.0 + + +From 73c1f387765ef528c7323e6e7ca3c05899cfcc4a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 16 Sep 2019 16:36:51 +0200 +Subject: [PATCH 3/3] idle-monitor: Reset timeout before firing watch + +The watch might be removed during firing, meaning the source is +destroyed after returning. Avoid use-after-free by unsetting the timeout +before firing. Returning G_SOURCE_CONTINUE in that case is harmless, as +source is destroyed. + +Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/796 + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/799 +--- + src/backends/meta-idle-monitor.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/backends/meta-idle-monitor.c b/src/backends/meta-idle-monitor.c +index e5124abc1..9fa481742 100644 +--- a/src/backends/meta-idle-monitor.c ++++ b/src/backends/meta-idle-monitor.c +@@ -324,9 +324,10 @@ idle_monitor_dispatch_timeout (GSource *source, + if (ready_time > now) + return G_SOURCE_CONTINUE; + +- meta_idle_monitor_watch_fire (watch); + g_source_set_ready_time (watch->timeout_source, -1); + ++ meta_idle_monitor_watch_fire (watch); ++ + return G_SOURCE_CONTINUE; + } + +-- +2.23.0 + diff --git a/SOURCES/inherit-xrandr-metamodes.patch b/SOURCES/inherit-xrandr-metamodes.patch new file mode 100644 index 0000000..9f3f6c9 --- /dev/null +++ b/SOURCES/inherit-xrandr-metamodes.patch @@ -0,0 +1,365 @@ +From 2fd3910c29d2af2a7c64b82f075cd3647d7e4bee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 18 Mar 2019 17:08:11 +0100 +Subject: [PATCH 1/2] monitor-config-manager: Use current mode when deriving + current config + +Instead of overriding the existing mode with the preferred mode of the monitor, +use the one already configured. Also use the MetaMonitor API for deriving the +position of the monitor in the screen coordinate space. +--- + src/backends/meta-monitor-config-manager.c | 77 +++++++++++++--------- + 1 file changed, 47 insertions(+), 30 deletions(-) + +diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c +index c09edbe00..a3387aa0f 100644 +--- a/src/backends/meta-monitor-config-manager.c ++++ b/src/backends/meta-monitor-config-manager.c +@@ -592,20 +592,19 @@ create_monitor_config (MetaMonitor *monitor, + } + + static MetaLogicalMonitorConfig * +-create_preferred_logical_monitor_config (MetaMonitorManager *monitor_manager, +- MetaMonitor *monitor, +- int x, +- int y, +- MetaLogicalMonitorConfig *primary_logical_monitor_config, +- MetaLogicalMonitorLayoutMode layout_mode) ++create_logical_monitor_config (MetaMonitorManager *monitor_manager, ++ MetaMonitor *monitor, ++ MetaMonitorMode *mode, ++ int x, ++ int y, ++ MetaLogicalMonitorConfig *primary_logical_monitor_config, ++ MetaLogicalMonitorLayoutMode layout_mode) + { +- MetaMonitorMode *mode; + int width, height; + float scale; + MetaMonitorConfig *monitor_config; + MetaLogicalMonitorConfig *logical_monitor_config; + +- mode = meta_monitor_get_preferred_mode (monitor); + meta_monitor_mode_get_resolution (mode, &width, &height); + + if ((meta_monitor_manager_get_capabilities (monitor_manager) & +@@ -645,22 +644,40 @@ create_preferred_logical_monitor_config (MetaMonitorManager *monitor_ma + } + + static MetaLogicalMonitorConfig * +-create_logical_monitor_config_from_output (MetaMonitorManager *monitor_manager, +- MetaMonitor *monitor, +- MetaLogicalMonitorConfig *primary_logical_monitor_config, +- MetaLogicalMonitorLayoutMode layout_mode) ++create_preferred_logical_monitor_config (MetaMonitorManager *monitor_manager, ++ MetaMonitor *monitor, ++ int x, ++ int y, ++ MetaLogicalMonitorConfig *primary_logical_monitor_config, ++ MetaLogicalMonitorLayoutMode layout_mode) + { +- MetaOutput *output; +- MetaCrtc *crtc; ++ return create_logical_monitor_config (monitor_manager, ++ monitor, ++ meta_monitor_get_preferred_mode (monitor), ++ x, y, ++ primary_logical_monitor_config, ++ layout_mode); ++} + +- output = meta_monitor_get_main_output (monitor); +- crtc = meta_output_get_assigned_crtc (output); +- return create_preferred_logical_monitor_config (monitor_manager, +- monitor, +- crtc->rect.x, +- crtc->rect.y, +- primary_logical_monitor_config, +- layout_mode); ++static MetaLogicalMonitorConfig * ++create_logical_monitor_config_from_monitor (MetaMonitorManager *monitor_manager, ++ MetaMonitor *monitor, ++ MetaLogicalMonitorConfig *primary_logical_monitor_config, ++ MetaLogicalMonitorLayoutMode layout_mode) ++{ ++ MetaRectangle monitor_layout; ++ MetaMonitorMode *mode; ++ ++ meta_monitor_derive_layout (monitor, &monitor_layout); ++ mode = meta_monitor_get_current_mode (monitor); ++ ++ return create_logical_monitor_config (monitor_manager, ++ monitor, ++ mode, ++ monitor_layout.x, ++ monitor_layout.y, ++ primary_logical_monitor_config, ++ layout_mode); + } + + MetaMonitorsConfig * +@@ -688,10 +705,10 @@ meta_monitor_config_manager_create_initial (MetaMonitorConfigManager *config_man + layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); + + primary_logical_monitor_config = +- create_logical_monitor_config_from_output (monitor_manager, +- primary_monitor, +- NULL, +- layout_mode); ++ create_logical_monitor_config_from_monitor (monitor_manager, ++ primary_monitor, ++ NULL, ++ layout_mode); + + primary_logical_monitor_config->is_primary = TRUE; + logical_monitor_configs = g_list_append (NULL, +@@ -710,10 +727,10 @@ meta_monitor_config_manager_create_initial (MetaMonitorConfigManager *config_man + continue; + + logical_monitor_config = +- create_logical_monitor_config_from_output (monitor_manager, +- monitor, +- primary_logical_monitor_config, +- layout_mode); ++ create_logical_monitor_config_from_monitor (monitor_manager, ++ monitor, ++ primary_logical_monitor_config, ++ layout_mode); + + logical_monitor_configs = g_list_append (logical_monitor_configs, + logical_monitor_config); +-- +2.21.0 + + +From d8c34e4cd7e500567e72e0f219295d7c2162dcf3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 18 Mar 2019 17:10:37 +0100 +Subject: [PATCH 2/2] monitor-manager: Don't try to derive current config on + non-X11 + +This commit also reworks the initial config state reading some. Appart from +avoiding trying to inherit from backends where it doesn't make sense, it does +the following changes: + + * Replace the name "initial" with "inherited", as the initial config in the + context of monitor management is the one used initialization. E.g. if there is + a applicable configuration in monitors.xml, the initial config is taken from + there. + + * Don't make "_create_()" functions have side effects. Previously + meta_monitor_config_manager_create_initial() also set state on the config + manager object. Instead, add a meta_monitor_config_manager_ensure_inherited() + and meta_monitor_manager_get_inherited_config() function to make things more + explicit. + + * Don't recreate "is-applicable" logic, just use the existing helper. +--- + src/backends/meta-monitor-config-manager.c | 39 +++++++++++-------- + src/backends/meta-monitor-config-manager.h | 5 +++ + src/backends/meta-monitor-manager-private.h | 4 +- + src/backends/meta-monitor-manager.c | 32 ++++++++------- + .../x11/meta-monitor-manager-xrandr.c | 3 +- + 5 files changed, 49 insertions(+), 34 deletions(-) + +diff --git a/src/backends/meta-monitor-config-manager.c b/src/backends/meta-monitor-config-manager.c +index a3387aa0f..bc1a39db8 100644 +--- a/src/backends/meta-monitor-config-manager.c ++++ b/src/backends/meta-monitor-config-manager.c +@@ -42,7 +42,7 @@ struct _MetaMonitorConfigManager + MetaMonitorConfigStore *config_store; + + MetaMonitorsConfig *current_config; +- MetaMonitorsConfig *initial_config; ++ MetaMonitorsConfig *inherited_config; + GQueue config_history; + }; + +@@ -680,11 +680,10 @@ create_logical_monitor_config_from_monitor (MetaMonitorManager *monito + layout_mode); + } + +-MetaMonitorsConfig * +-meta_monitor_config_manager_create_initial (MetaMonitorConfigManager *config_manager) ++static MetaMonitorsConfig * ++meta_monitor_config_manager_derive_current (MetaMonitorConfigManager *config_manager) + { + MetaMonitorManager *monitor_manager = config_manager->monitor_manager; +- MetaMonitorsConfig *initial_config; + GList *logical_monitor_configs; + MetaMonitor *primary_monitor; + MetaLogicalMonitorLayoutMode layout_mode; +@@ -692,12 +691,6 @@ meta_monitor_config_manager_create_initial (MetaMonitorConfigManager *config_man + GList *monitors; + GList *l; + +- if (config_manager->initial_config != NULL) +- return g_object_ref (config_manager->initial_config); +- +- if (meta_monitor_config_store_get_config_count (config_manager->config_store) > 0) +- return NULL; +- + primary_monitor = find_primary_monitor (monitor_manager); + if (!primary_monitor || !meta_monitor_is_active (primary_monitor)) + return NULL; +@@ -736,14 +729,26 @@ meta_monitor_config_manager_create_initial (MetaMonitorConfigManager *config_man + logical_monitor_config); + } + +- initial_config = meta_monitors_config_new (monitor_manager, +- logical_monitor_configs, +- layout_mode, +- META_MONITORS_CONFIG_FLAG_NONE); ++ return meta_monitors_config_new (monitor_manager, ++ logical_monitor_configs, ++ layout_mode, ++ META_MONITORS_CONFIG_FLAG_NONE); ++} ++ ++void ++meta_monitor_config_manager_ensure_inherited_config (MetaMonitorConfigManager *config_manager) ++{ ++ if (config_manager->inherited_config) ++ return; + +- config_manager->initial_config = g_object_ref (initial_config); ++ config_manager->inherited_config = ++ meta_monitor_config_manager_derive_current (config_manager); ++} + +- return initial_config; ++MetaMonitorsConfig * ++meta_monitor_config_manager_get_inherited_config (MetaMonitorConfigManager *config_manager) ++{ ++ return config_manager->inherited_config; + } + + MetaMonitorsConfig * +@@ -1282,7 +1287,7 @@ meta_monitor_config_manager_dispose (GObject *object) + META_MONITOR_CONFIG_MANAGER (object); + + g_clear_object (&config_manager->current_config); +- g_clear_object (&config_manager->initial_config); ++ g_clear_object (&config_manager->inherited_config); + meta_monitor_config_manager_clear_history (config_manager); + + G_OBJECT_CLASS (meta_monitor_config_manager_parent_class)->dispose (object); +diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h +index 409611bb0..bb847b96e 100644 +--- a/src/backends/meta-monitor-config-manager.h ++++ b/src/backends/meta-monitor-config-manager.h +@@ -96,6 +96,11 @@ MetaMonitorsConfig * meta_monitor_config_manager_get_stored (MetaMonitorConfigMa + + META_EXPORT_TEST + MetaMonitorsConfig * meta_monitor_config_manager_create_initial (MetaMonitorConfigManager *config_manager); ++ ++void meta_monitor_config_manager_ensure_inherited_config (MetaMonitorConfigManager *config_manager); ++ ++MetaMonitorsConfig * meta_monitor_config_manager_get_inherited_config (MetaMonitorConfigManager *config_manager); ++ + META_EXPORT_TEST + MetaMonitorsConfig * meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager); + +diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h +index cdb8f4209..223b5dfbd 100644 +--- a/src/backends/meta-monitor-manager-private.h ++++ b/src/backends/meta-monitor-manager-private.h +@@ -44,7 +44,8 @@ typedef enum _MetaMonitorManagerCapability + META_MONITOR_MANAGER_CAPABILITY_NONE = 0, + META_MONITOR_MANAGER_CAPABILITY_MIRRORING = (1 << 0), + META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE = (1 << 1), +- META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED = (1 << 2) ++ META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED = (1 << 2), ++ META_MONITOR_MANAGER_CAPABILITY_CAN_DERIVE_CURRENT = (1 << 3), + } MetaMonitorManagerCapability; + + /* Equivalent to the 'method' enum in org.gnome.Mutter.DisplayConfig */ +@@ -133,6 +134,7 @@ struct _MetaMonitorManager + int persistent_timeout_id; + + MetaMonitorConfigManager *config_manager; ++ MetaMonitorsConfig *initial_config; + + GnomePnpIds *pnp_ids; + +diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c +index bb4b44188..076dca8cb 100644 +--- a/src/backends/meta-monitor-manager.c ++++ b/src/backends/meta-monitor-manager.c +@@ -531,14 +531,21 @@ should_use_stored_config (MetaMonitorManager *manager) + !meta_monitor_manager_has_hotplug_mode_update (manager)); + } + ++static gboolean ++can_derive_current_config (MetaMonitorManager *manager) ++{ ++ MetaMonitorManagerCapability capabilities; ++ ++ capabilities = meta_monitor_manager_get_capabilities (manager); ++ return !!(capabilities & META_MONITOR_MANAGER_CAPABILITY_CAN_DERIVE_CURRENT); ++} ++ + MetaMonitorsConfig * + meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) + { +- g_autoptr (MetaMonitorsConfig) initial_config = NULL; + MetaMonitorsConfig *config = NULL; + GError *error = NULL; + gboolean use_stored_config; +- MetaMonitorsConfigKey *current_state_key; + MetaMonitorsConfigMethod method; + MetaMonitorsConfigMethod fallback_method = + META_MONITORS_CONFIG_METHOD_TEMPORARY; +@@ -549,17 +556,8 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) + else + method = META_MONITORS_CONFIG_METHOD_TEMPORARY; + +- initial_config = meta_monitor_config_manager_create_initial (manager->config_manager); +- +- if (initial_config) +- { +- current_state_key = meta_create_monitors_config_key_for_current_state (manager); +- +- /* don't ever reuse initial configuration, if the monitor topology changed +- */ +- if (current_state_key && !meta_monitors_config_key_equal (current_state_key, initial_config->key)) +- g_clear_object (&initial_config); +- } ++ if (can_derive_current_config (manager)) ++ meta_monitor_config_manager_ensure_inherited_config (manager->config_manager); + + if (use_stored_config) + { +@@ -628,9 +626,13 @@ meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) + g_clear_object (&config); + } + +- config = g_steal_pointer (&initial_config); +- if (config) ++ config = ++ meta_monitor_config_manager_get_inherited_config (manager->config_manager); ++ if (config && ++ meta_monitor_manager_is_config_complete (manager, config)) + { ++ config = g_object_ref (config); ++ + if (!meta_monitor_manager_apply_monitors_config (manager, + config, + method, +diff --git a/src/backends/x11/meta-monitor-manager-xrandr.c b/src/backends/x11/meta-monitor-manager-xrandr.c +index d60f00325..b8d6342b6 100644 +--- a/src/backends/x11/meta-monitor-manager-xrandr.c ++++ b/src/backends/x11/meta-monitor-manager-xrandr.c +@@ -999,7 +999,8 @@ static MetaMonitorManagerCapability + meta_monitor_manager_xrandr_get_capabilities (MetaMonitorManager *manager) + { + return (META_MONITOR_MANAGER_CAPABILITY_MIRRORING | +- META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED); ++ META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED | ++ META_MONITOR_MANAGER_CAPABILITY_CAN_DERIVE_CURRENT); + } + + static gboolean +-- +2.21.0 + diff --git a/SOURCES/input-after-long-idle-fix.patch b/SOURCES/input-after-long-idle-fix.patch new file mode 100644 index 0000000..9c4c2a6 --- /dev/null +++ b/SOURCES/input-after-long-idle-fix.patch @@ -0,0 +1,374 @@ +From 05bca153bb92c5daa5b961214ff7f80af88cb7cf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 24 Oct 2019 21:19:36 +0200 +Subject: [PATCH 1/2] display: Move finishing of touch sequence to the backend + +We need to manipulate an X11 grab when a touch sequence ends; move that +logic to where it belongs - in the X11 backend. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/886 +--- + src/backends/meta-backend-private.h | 16 ++++++++++++ + src/backends/meta-backend.c | 14 +++++++++++ + src/backends/x11/meta-backend-x11.c | 23 +++++++++++++++++ + src/core/display.c | 33 +++++++++++-------------- + src/core/meta-gesture-tracker-private.h | 9 +------ + 5 files changed, 69 insertions(+), 26 deletions(-) + +diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h +index 7eba1806b..81ec81e5f 100644 +--- a/src/backends/meta-backend-private.h ++++ b/src/backends/meta-backend-private.h +@@ -49,6 +49,14 @@ + #define DEFAULT_XKB_RULES_FILE "evdev" + #define DEFAULT_XKB_MODEL "pc105+inet" + ++typedef enum ++{ ++ META_SEQUENCE_NONE, ++ META_SEQUENCE_ACCEPTED, ++ META_SEQUENCE_REJECTED, ++ META_SEQUENCE_PENDING_END ++} MetaSequenceState; ++ + struct _MetaBackendClass + { + GObjectClass parent_class; +@@ -71,6 +79,10 @@ struct _MetaBackendClass + int device_id, + uint32_t timestamp); + ++ void (* finish_touch_sequence) (MetaBackend *backend, ++ ClutterEventSequence *sequence, ++ MetaSequenceState state); ++ + void (* warp_pointer) (MetaBackend *backend, + int x, + int y); +@@ -135,6 +147,10 @@ gboolean meta_backend_ungrab_device (MetaBackend *backend, + int device_id, + uint32_t timestamp); + ++void meta_backend_finish_touch_sequence (MetaBackend *backend, ++ ClutterEventSequence *sequence, ++ MetaSequenceState state); ++ + void meta_backend_warp_pointer (MetaBackend *backend, + int x, + int y); +diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c +index c980cf150..bb7d66f2a 100644 +--- a/src/backends/meta-backend.c ++++ b/src/backends/meta-backend.c +@@ -1086,6 +1086,20 @@ meta_backend_ungrab_device (MetaBackend *backend, + return META_BACKEND_GET_CLASS (backend)->ungrab_device (backend, device_id, timestamp); + } + ++/** ++ * meta_backend_finish_touch_sequence: (skip) ++ */ ++void ++meta_backend_finish_touch_sequence (MetaBackend *backend, ++ ClutterEventSequence *sequence, ++ MetaSequenceState state) ++{ ++ if (META_BACKEND_GET_CLASS (backend)->finish_touch_sequence) ++ META_BACKEND_GET_CLASS (backend)->finish_touch_sequence (backend, ++ sequence, ++ state); ++} ++ + /** + * meta_backend_warp_pointer: (skip) + */ +diff --git a/src/backends/x11/meta-backend-x11.c b/src/backends/x11/meta-backend-x11.c +index c10365f9d..cdefa50a9 100644 +--- a/src/backends/x11/meta-backend-x11.c ++++ b/src/backends/x11/meta-backend-x11.c +@@ -591,6 +591,28 @@ meta_backend_x11_ungrab_device (MetaBackend *backend, + return (ret == Success); + } + ++static void ++meta_backend_x11_finish_touch_sequence (MetaBackend *backend, ++ ClutterEventSequence *sequence, ++ MetaSequenceState state) ++{ ++ MetaBackendX11 *x11 = META_BACKEND_X11 (backend); ++ MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); ++ int event_mode; ++ ++ if (state == META_SEQUENCE_ACCEPTED) ++ event_mode = XIAcceptTouch; ++ else if (state == META_SEQUENCE_REJECTED) ++ event_mode = XIRejectTouch; ++ else ++ g_return_if_reached (); ++ ++ XIAllowTouchEvents (priv->xdisplay, ++ META_VIRTUAL_CORE_POINTER_ID, ++ clutter_x11_event_sequence_get_touch_detail (sequence), ++ DefaultRootWindow (priv->xdisplay), event_mode); ++} ++ + static void + meta_backend_x11_warp_pointer (MetaBackend *backend, + int x, +@@ -776,6 +798,7 @@ meta_backend_x11_class_init (MetaBackendX11Class *klass) + backend_class->post_init = meta_backend_x11_post_init; + backend_class->grab_device = meta_backend_x11_grab_device; + backend_class->ungrab_device = meta_backend_x11_ungrab_device; ++ backend_class->finish_touch_sequence = meta_backend_x11_finish_touch_sequence; + backend_class->warp_pointer = meta_backend_x11_warp_pointer; + backend_class->get_current_logical_monitor = meta_backend_x11_get_current_logical_monitor; + backend_class->get_keymap = meta_backend_x11_get_keymap; +diff --git a/src/core/display.c b/src/core/display.c +index 4c8907f40..eb7dc43b6 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -42,6 +42,7 @@ + #include + #include + ++#include "backends/meta-backend-private.h" + #include "backends/meta-cursor-sprite-xcursor.h" + #include "backends/meta-cursor-tracker-private.h" + #include "backends/meta-idle-monitor-dbus.h" +@@ -598,27 +599,23 @@ gesture_tracker_state_changed (MetaGestureTracker *tracker, + MetaSequenceState state, + MetaDisplay *display) + { +- if (meta_is_wayland_compositor ()) ++ switch (state) + { +- if (state == META_SEQUENCE_ACCEPTED) +- meta_display_cancel_touch (display); +- } +- else +- { +- MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); +- int event_mode; ++ case META_SEQUENCE_NONE: ++ case META_SEQUENCE_PENDING_END: ++ return; ++ case META_SEQUENCE_ACCEPTED: ++ meta_display_cancel_touch (display); + +- if (state == META_SEQUENCE_ACCEPTED) +- event_mode = XIAcceptTouch; +- else if (state == META_SEQUENCE_REJECTED) +- event_mode = XIRejectTouch; +- else +- return; ++ /* Intentional fall-through */ ++ case META_SEQUENCE_REJECTED: ++ { ++ MetaBackend *backend; + +- XIAllowTouchEvents (meta_backend_x11_get_xdisplay (backend), +- META_VIRTUAL_CORE_POINTER_ID, +- clutter_x11_event_sequence_get_touch_detail (sequence), +- DefaultRootWindow (display->x11_display->xdisplay), event_mode); ++ backend = meta_get_backend (); ++ meta_backend_finish_touch_sequence (backend, sequence, state); ++ break; ++ } + } + } + +diff --git a/src/core/meta-gesture-tracker-private.h b/src/core/meta-gesture-tracker-private.h +index a9db35ebc..e7bfc5472 100644 +--- a/src/core/meta-gesture-tracker-private.h ++++ b/src/core/meta-gesture-tracker-private.h +@@ -26,6 +26,7 @@ + + #include + ++#include "backends/meta-backend-private.h" + #include "clutter/clutter.h" + #include "meta/window.h" + +@@ -39,14 +40,6 @@ + typedef struct _MetaGestureTracker MetaGestureTracker; + typedef struct _MetaGestureTrackerClass MetaGestureTrackerClass; + +-typedef enum +-{ +- META_SEQUENCE_NONE, +- META_SEQUENCE_ACCEPTED, +- META_SEQUENCE_REJECTED, +- META_SEQUENCE_PENDING_END +-} MetaSequenceState; +- + struct _MetaGestureTracker + { + GObject parent_instance; +-- +2.23.0 + + +From 8cf4f500defb421d5c96f2c1f9fcf7bb5545d70d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 25 Oct 2019 10:06:55 +0200 +Subject: [PATCH 2/2] x11: Limit touch replay pointer events to when replaying + +When a touch sequence was rejected, the emulated pointer events would be +replayed with old timestamps. This caused issues with grabs as they +would be ignored due to being too old. This was mitigated by making sure +device event timestamps never travelled back in time by tampering with +any event that had a timestamp seemingly in the past. + +This failed when the most recent timestamp that had been received were +much older than the timestamp of the new event. This could for example +happen when a session was left not interacted with for 40+ days or so; +when interacted with again, as any new timestamp would according to +XSERVER_TIME_IS_BEFORE() still be in the past compared to the "most +recent" one. The effect is that we'd always use the `latest_evtime` for +all new device events without ever updating it. + +The end result of this was that passive grabs would become active when +interacted with, but would then newer be released, as the timestamps to +XIAllowEvents() would out of date, resulting in the desktop effectively +freezing, as the Shell would have an active pointer grab. + +To avoid the situation where we get stuck with an old `latest_evtime` +timestamp, limit the tampering with device event timestamp to 1) only +pointer events, and 2) only during the replay sequence. The second part +is implemented by sending an asynchronous message via the X server after +rejecting a touch sequence, only potentially tampering with the device +event timestamps until the reply. This should avoid the stuck timestamp +as in those situations, we'll always have a relatively up to date +`latest_evtime` meaning XSERVER_TIME_IS_BEFORE() will not get confused. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/886 +--- + src/backends/x11/meta-backend-x11.c | 71 +++++++++++++++++++++++------ + 1 file changed, 58 insertions(+), 13 deletions(-) + +diff --git a/src/backends/x11/meta-backend-x11.c b/src/backends/x11/meta-backend-x11.c +index cdefa50a9..821b30f5b 100644 +--- a/src/backends/x11/meta-backend-x11.c ++++ b/src/backends/x11/meta-backend-x11.c +@@ -66,6 +66,10 @@ struct _MetaBackendX11Private + XSyncAlarm user_active_alarm; + XSyncCounter counter; + ++ int current_touch_replay_sync_serial; ++ int pending_touch_replay_sync_serial; ++ Atom touch_replay_sync_atom; ++ + int xinput_opcode; + int xinput_event_base; + int xinput_error_base; +@@ -174,6 +178,26 @@ meta_backend_x11_translate_device_event (MetaBackendX11 *x11, + backend_x11_class->translate_device_event (x11, device_event); + } + ++static void ++maybe_translate_touch_replay_pointer_event (MetaBackendX11 *x11, ++ XIDeviceEvent *device_event) ++{ ++ MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); ++ ++ if (!device_event->send_event && ++ device_event->time != META_CURRENT_TIME && ++ priv->current_touch_replay_sync_serial != ++ priv->pending_touch_replay_sync_serial && ++ XSERVER_TIME_IS_BEFORE (device_event->time, priv->latest_evtime)) ++ { ++ /* Emulated pointer events received after XIRejectTouch is received ++ * on a passive touch grab will contain older timestamps, update those ++ * so we dont get InvalidTime at grabs. ++ */ ++ device_event->time = priv->latest_evtime; ++ } ++} ++ + static void + translate_device_event (MetaBackendX11 *x11, + XIDeviceEvent *device_event) +@@ -183,19 +207,7 @@ translate_device_event (MetaBackendX11 *x11, + meta_backend_x11_translate_device_event (x11, device_event); + + if (!device_event->send_event && device_event->time != META_CURRENT_TIME) +- { +- if (XSERVER_TIME_IS_BEFORE (device_event->time, priv->latest_evtime)) +- { +- /* Emulated pointer events received after XIRejectTouch is received +- * on a passive touch grab will contain older timestamps, update those +- * so we dont get InvalidTime at grabs. +- */ +- device_event->time = priv->latest_evtime; +- } +- +- /* Update the internal latest evtime, for any possible later use */ +- priv->latest_evtime = device_event->time; +- } ++ priv->latest_evtime = device_event->time; + } + + static void +@@ -260,6 +272,9 @@ maybe_spoof_event_as_stage_event (MetaBackendX11 *x11, + case XI_Motion: + case XI_ButtonPress: + case XI_ButtonRelease: ++ maybe_translate_touch_replay_pointer_event (x11, ++ (XIDeviceEvent *) input_event); ++ /* Intentional fall-through */ + case XI_KeyPress: + case XI_KeyRelease: + case XI_TouchBegin: +@@ -327,6 +342,17 @@ handle_host_xevent (MetaBackend *backend, + MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); + gboolean bypass_clutter = FALSE; + ++ switch (event->type) ++ { ++ case ClientMessage: ++ if (event->xclient.window == meta_backend_x11_get_xwindow (x11) && ++ event->xclient.message_type == priv->touch_replay_sync_atom) ++ priv->current_touch_replay_sync_serial = event->xclient.data.l[0]; ++ break; ++ default: ++ break; ++ } ++ + XGetEventData (priv->xdisplay, &event->xcookie); + + { +@@ -534,6 +560,10 @@ meta_backend_x11_post_init (MetaBackend *backend) + monitor_manager = meta_backend_get_monitor_manager (backend); + g_signal_connect (monitor_manager, "monitors-changed-internal", + G_CALLBACK (on_monitors_changed), backend); ++ ++ priv->touch_replay_sync_atom = XInternAtom (priv->xdisplay, ++ "_MUTTER_TOUCH_SEQUENCE_SYNC", ++ False); + } + + static ClutterBackend * +@@ -611,6 +641,21 @@ meta_backend_x11_finish_touch_sequence (MetaBackend *backend, + META_VIRTUAL_CORE_POINTER_ID, + clutter_x11_event_sequence_get_touch_detail (sequence), + DefaultRootWindow (priv->xdisplay), event_mode); ++ ++ if (state == META_SEQUENCE_REJECTED) ++ { ++ XClientMessageEvent ev; ++ ++ ev = (XClientMessageEvent) { ++ .type = ClientMessage, ++ .window = meta_backend_x11_get_xwindow (x11), ++ .message_type = priv->touch_replay_sync_atom, ++ .format = 32, ++ .data.l[0] = ++priv->pending_touch_replay_sync_serial, ++ }; ++ XSendEvent (priv->xdisplay, meta_backend_x11_get_xwindow (x11), ++ False, 0, (XEvent *) &ev); ++ } + } + + static void +-- +2.23.0 + diff --git a/SOURCES/monitor-config-policy.patch b/SOURCES/monitor-config-policy.patch new file mode 100644 index 0000000..c0472bd --- /dev/null +++ b/SOURCES/monitor-config-policy.patch @@ -0,0 +1,2356 @@ +From 9cf4d7b6d8a279a0d2a047f52d9fa5b0586b1ab9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 14 Jan 2022 22:11:17 +0100 +Subject: [PATCH 1/9] test/utils: Add helper to set custom monitors config + +Make the existing implementation a wrapper to avoid changing monitor +config tests. + +Part-of: + +Cherry-picked from 57d1d82ead6392a104a9e9d6c7f1f4f14ad54e48 +--- + src/tests/monitor-test-utils.c | 18 +----------------- + src/tests/test-utils.c | 23 +++++++++++++++++++++++ + src/tests/test-utils.h | 3 +++ + 3 files changed, 27 insertions(+), 17 deletions(-) + +diff --git a/src/tests/monitor-test-utils.c b/src/tests/monitor-test-utils.c +index 54881569102e..d2dc3b2658c6 100644 +--- a/src/tests/monitor-test-utils.c ++++ b/src/tests/monitor-test-utils.c +@@ -28,23 +28,7 @@ + void + set_custom_monitor_config (const char *filename) + { +- MetaBackend *backend = meta_get_backend (); +- MetaMonitorManager *monitor_manager = +- meta_backend_get_monitor_manager (backend); +- MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; +- MetaMonitorConfigStore *config_store; +- GError *error = NULL; +- const char *path; +- +- g_assert_nonnull (config_manager); +- +- config_store = meta_monitor_config_manager_get_store (config_manager); +- +- path = g_test_get_filename (G_TEST_DIST, "tests", "monitor-configs", +- filename, NULL); +- if (!meta_monitor_config_store_set_custom (config_store, path, NULL, +- &error)) +- g_error ("Failed to set custom config: %s", error->message); ++ meta_set_custom_monitor_config (meta_get_backend (), filename); + } + + char * +diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c +index 980b20acdcb0..dd6230dc15ec 100644 +--- a/src/tests/test-utils.c ++++ b/src/tests/test-utils.c +@@ -24,6 +24,7 @@ + #include + #include + ++#include "backends/meta-monitor-config-store.h" + #include "core/display-private.h" + #include "core/window-private.h" + #include "wayland/meta-wayland.h" +@@ -483,3 +484,25 @@ test_get_plugin_name (void) + else + return "libdefault"; + } ++ ++void ++meta_set_custom_monitor_config (MetaBackend *backend, ++ const char *filename) ++{ ++ MetaMonitorManager *monitor_manager = ++ meta_backend_get_monitor_manager (backend); ++ MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; ++ MetaMonitorConfigStore *config_store; ++ GError *error = NULL; ++ const char *path; ++ ++ g_assert_nonnull (config_manager); ++ ++ config_store = meta_monitor_config_manager_get_store (config_manager); ++ ++ path = g_test_get_filename (G_TEST_DIST, "tests", "monitor-configs", ++ filename, NULL); ++ if (!meta_monitor_config_store_set_custom (config_store, path, NULL, ++ &error)) ++ g_error ("Failed to set custom config: %s", error->message); ++} +diff --git a/src/tests/test-utils.h b/src/tests/test-utils.h +index e11f25353a5b..5092f766fdf6 100644 +--- a/src/tests/test-utils.h ++++ b/src/tests/test-utils.h +@@ -81,4 +81,7 @@ void test_client_destroy (TestClient *client); + + const char * test_get_plugin_name (void); + ++void meta_set_custom_monitor_config (MetaBackend *backend, ++ const char *filename); ++ + #endif /* TEST_UTILS_H */ +-- +2.33.1 + + +From 8133ff828e46e19d4afa277c1e013a846b443b9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 14 Jan 2022 22:12:36 +0100 +Subject: [PATCH 2/9] tests/utils: Add meta_wait_for_paint() helper + +This function queues a full stage redraw, then waits for every view to +receive the "presented" signal before returning. + +Part-of: +(cherry picked from commit d84f7971e476a1e2d727310d9a33ac4080137f58) +--- + src/tests/test-utils.c | 27 +++++++++++++++++++++++++++ + src/tests/test-utils.h | 2 ++ + 2 files changed, 29 insertions(+) + +diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c +index dd6230dc15ec..f1a688478f67 100644 +--- a/src/tests/test-utils.c ++++ b/src/tests/test-utils.c +@@ -506,3 +506,30 @@ meta_set_custom_monitor_config (MetaBackend *backend, + &error)) + g_error ("Failed to set custom config: %s", error->message); + } ++ ++static void ++on_view_presented (ClutterStage *stage, ++ ClutterStageView *view, ++ ClutterFrameInfo *frame_info, ++ GList **presented_views) ++{ ++ *presented_views = g_list_remove (*presented_views, view); ++} ++ ++void ++meta_wait_for_paint (MetaBackend *backend) ++{ ++ ClutterActor *stage = meta_backend_get_stage (backend); ++ MetaRenderer *renderer = meta_backend_get_renderer (backend); ++ GList *views; ++ gulong handler_id; ++ ++ clutter_actor_queue_redraw (stage); ++ ++ views = g_list_copy (meta_renderer_get_views (renderer)); ++ handler_id = g_signal_connect (stage, "presented", ++ G_CALLBACK (on_view_presented), &views); ++ while (views) ++ g_main_context_iteration (NULL, TRUE); ++ g_signal_handler_disconnect (stage, handler_id); ++} +diff --git a/src/tests/test-utils.h b/src/tests/test-utils.h +index 5092f766fdf6..6d6cdb924d01 100644 +--- a/src/tests/test-utils.h ++++ b/src/tests/test-utils.h +@@ -84,4 +84,6 @@ const char * test_get_plugin_name (void); + void meta_set_custom_monitor_config (MetaBackend *backend, + const char *filename); + ++void meta_wait_for_paint (MetaBackend *backend); ++ + #endif /* TEST_UTILS_H */ +-- +2.33.1 + + +From 61e384f2b652e5ac774a0cb46daaba3e89ddefdc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Sep 2021 08:59:03 +0200 +Subject: [PATCH 3/9] monitor-config-store: Make parsing a bit more forgiving + +Allow unknown XML elements inside . This makes extending in +the future easier. + +(cherry picked from commit 3cd666c657fa716f06dee69df59356b53b6c5d72) +--- + src/backends/meta-monitor-config-store.c | 54 +++++++++++++++--- + .../monitor-configs/unknown-elements.xml | 31 ++++++++++ + src/tests/monitor-store-unit-tests.c | 56 +++++++++++++++++++ + 3 files changed, 133 insertions(+), 8 deletions(-) + create mode 100644 src/tests/monitor-configs/unknown-elements.xml + +diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c +index 770bef734e6f..9a3daf561a69 100644 +--- a/src/backends/meta-monitor-config-store.c ++++ b/src/backends/meta-monitor-config-store.c +@@ -136,6 +136,7 @@ G_DEFINE_QUARK (meta-monitor-config-store-error-quark, + typedef enum + { + STATE_INITIAL, ++ STATE_UNKNOWN, + STATE_MONITORS, + STATE_CONFIGURATION, + STATE_MIGRATED, +@@ -180,12 +181,28 @@ typedef struct + MetaLogicalMonitorConfig *current_logical_monitor_config; + GList *current_disabled_monitor_specs; + ++ ParserState unknown_state_root; ++ int unknown_level; ++ + MetaMonitorsConfigFlag extra_config_flags; + } ConfigParser; + + G_DEFINE_TYPE (MetaMonitorConfigStore, meta_monitor_config_store, + G_TYPE_OBJECT) + ++static void ++enter_unknown_element (ConfigParser *parser, ++ const char *element_name, ++ const char *root_element_name, ++ ParserState root_state) ++{ ++ parser->state = STATE_UNKNOWN; ++ parser->unknown_level = 1; ++ parser->unknown_state_root = root_state; ++ g_warning ("Unknown element <%s> under <%s>, ignoring", ++ element_name, root_element_name); ++} ++ + static void + handle_start_element (GMarkupParseContext *context, + const char *element_name, +@@ -242,8 +259,8 @@ handle_start_element (GMarkupParseContext *context, + { + if (!g_str_equal (element_name, "configuration")) + { +- g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, +- "Invalid toplevel element '%s'", element_name); ++ enter_unknown_element (parser, element_name, ++ "monitors", STATE_MONITORS); + return; + } + +@@ -253,6 +270,13 @@ handle_start_element (GMarkupParseContext *context, + return; + } + ++ case STATE_UNKNOWN: ++ { ++ parser->unknown_level++; ++ ++ return; ++ } ++ + case STATE_CONFIGURATION: + { + if (g_str_equal (element_name, "logicalmonitor")) +@@ -274,9 +298,8 @@ handle_start_element (GMarkupParseContext *context, + } + else + { +- g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, +- "Invalid configuration element '%s'", element_name); +- return; ++ enter_unknown_element (parser, element_name, ++ "configuration", STATE_CONFIGURATION); + } + + return; +@@ -323,9 +346,8 @@ handle_start_element (GMarkupParseContext *context, + } + else + { +- g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, +- "Invalid monitor logicalmonitor element '%s'", element_name); +- return; ++ enter_unknown_element (parser, element_name, ++ "logicalmonitor", STATE_LOGICAL_MONITOR); + } + + return; +@@ -793,6 +815,18 @@ handle_end_element (GMarkupParseContext *context, + return; + } + ++ case STATE_UNKNOWN: ++ { ++ parser->unknown_level--; ++ if (parser->unknown_level == 0) ++ { ++ g_assert (parser->unknown_state_root >= 0); ++ parser->state = parser->unknown_state_root; ++ parser->unknown_state_root = -1; ++ } ++ return; ++ } ++ + case STATE_MONITORS: + { + g_assert (g_str_equal (element_name, "monitors")); +@@ -912,6 +946,9 @@ handle_text (GMarkupParseContext *context, + + switch (parser->state) + { ++ case STATE_UNKNOWN: ++ return; ++ + case STATE_INITIAL: + case STATE_MONITORS: + case STATE_CONFIGURATION: +@@ -1099,6 +1136,7 @@ read_config_file (MetaMonitorConfigStore *config_store, + .state = STATE_INITIAL, + .config_store = config_store, + .extra_config_flags = extra_config_flags, ++ .unknown_state_root = -1, + }; + + parse_context = g_markup_parse_context_new (&config_parser, +diff --git a/src/tests/monitor-configs/unknown-elements.xml b/src/tests/monitor-configs/unknown-elements.xml +new file mode 100644 +index 000000000000..f81be95dd9df +--- /dev/null ++++ b/src/tests/monitor-configs/unknown-elements.xml +@@ -0,0 +1,31 @@ ++ ++ ++ text ++ ++ ++ ++ text ++ ++ ++ ++ text ++ ++ 0 ++ 0 ++ yes ++ ++ ++ DP-1 ++ MetaProduct's Inc. ++ MetaMonitor ++ 0x123456 ++ ++ ++ 1920 ++ 1080 ++ 60.000495910644531 ++ ++ ++ ++ ++ +diff --git a/src/tests/monitor-store-unit-tests.c b/src/tests/monitor-store-unit-tests.c +index 4a73d89db4c6..4ff0e396dda7 100644 +--- a/src/tests/monitor-store-unit-tests.c ++++ b/src/tests/monitor-store-unit-tests.c +@@ -836,6 +836,60 @@ meta_test_monitor_store_interlaced (void) + check_monitor_configurations (&expect); + } + ++static void ++meta_test_monitor_store_unknown_elements (void) ++{ ++ MonitorStoreTestExpect expect = { ++ .configurations = { ++ { ++ .logical_monitors = { ++ { ++ .layout = { ++ .x = 0, ++ .y = 0, ++ .width = 1920, ++ .height = 1080 ++ }, ++ .scale = 1, ++ .is_primary = TRUE, ++ .is_presentation = FALSE, ++ .monitors = { ++ { ++ .connector = "DP-1", ++ .vendor = "MetaProduct's Inc.", ++ .product = "MetaMonitor", ++ .serial = "0x123456", ++ .mode = { ++ .width = 1920, ++ .height = 1080, ++ .refresh_rate = 60.000495910644531 ++ } ++ } ++ }, ++ .n_monitors = 1, ++ } ++ }, ++ .n_logical_monitors = 1 ++ } ++ }, ++ .n_configurations = 1 ++ }; ++ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, ++ "Unknown element " ++ "under , ignoring"); ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, ++ "Unknown element " ++ "under , ignoring"); ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, ++ "Unknown element " ++ "under , ignoring"); ++ set_custom_monitor_config ("unknown-elements.xml"); ++ g_test_assert_expected_messages (); ++ ++ check_monitor_configurations (&expect); ++} ++ + void + init_monitor_store_tests (void) + { +@@ -861,4 +915,6 @@ init_monitor_store_tests (void) + meta_test_monitor_store_second_rotated); + g_test_add_func ("/backends/monitor-store/interlaced", + meta_test_monitor_store_interlaced); ++ g_test_add_func ("/backends/monitor-store/unknown-elements", ++ meta_test_monitor_store_unknown_elements); + } +-- +2.33.1 + + +From cb3f23d6bdc01f16a0c9236873057faede0a4b32 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 24 Sep 2021 16:29:47 +0200 +Subject: [PATCH 4/9] monitor-config-store: Fix incorrect string comparison + with empty string + +strncmp() always return 0 if the passed length is 0. What this means is +that whatever the first string check happens to be, if the parsed XML +cdata was empty (e.g. if we got ), the first +condition would evaluate to true, which is rather unexpected. + +Fix this by making sure the string length is correct first. Also move it +into a helper so we don't need to repeat the same strlen() check every +time. + +(cherry picked from commit f798e49502313dd3e7dd67143513a7a6a91b49f8) +--- + src/backends/meta-monitor-config-store.c | 25 +++++++++++++++++------- + 1 file changed, 18 insertions(+), 7 deletions(-) + +diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c +index 9a3daf561a69..f6b5256aa798 100644 +--- a/src/backends/meta-monitor-config-store.c ++++ b/src/backends/meta-monitor-config-store.c +@@ -190,6 +190,17 @@ typedef struct + G_DEFINE_TYPE (MetaMonitorConfigStore, meta_monitor_config_store, + G_TYPE_OBJECT) + ++static gboolean ++text_equals (const char *text, ++ int len, ++ const char *expect) ++{ ++ if (strlen (expect) != len) ++ return FALSE; ++ ++ return strncmp (text, expect, len) == 0; ++} ++ + static void + enter_unknown_element (ConfigParser *parser, + const char *element_name, +@@ -904,12 +915,12 @@ read_bool (const char *text, + gboolean *out_value, + GError **error) + { +- if (strncmp (text, "no", text_len) == 0) ++ if (text_equals (text, text_len, "no")) + { + *out_value = FALSE; + return TRUE; + } +- else if (strncmp (text, "yes", text_len) == 0) ++ else if (text_equals (text, text_len, "yes")) + { + *out_value = TRUE; + return TRUE; +@@ -1039,13 +1050,13 @@ handle_text (GMarkupParseContext *context, + + case STATE_TRANSFORM_ROTATION: + { +- if (strncmp (text, "normal", text_len) == 0) ++ if (text_equals (text, text_len, "normal")) + parser->current_transform = META_MONITOR_TRANSFORM_NORMAL; +- else if (strncmp (text, "left", text_len) == 0) ++ else if (text_equals (text, text_len, "left")) + parser->current_transform = META_MONITOR_TRANSFORM_90; +- else if (strncmp (text, "upside_down", text_len) == 0) ++ else if (text_equals (text, text_len, "upside_down")) + parser->current_transform = META_MONITOR_TRANSFORM_180; +- else if (strncmp (text, "right", text_len) == 0) ++ else if (text_equals (text, text_len, "right")) + parser->current_transform = META_MONITOR_TRANSFORM_270; + else + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, +@@ -1088,7 +1099,7 @@ handle_text (GMarkupParseContext *context, + + case STATE_MONITOR_MODE_FLAG: + { +- if (strncmp (text, "interlace", text_len) == 0) ++ if (text_equals (text, text_len, "interlace")) + { + parser->current_monitor_mode_spec->flags |= + META_CRTC_MODE_FLAG_INTERLACE; +-- +2.33.1 + + +From 73ae3806396840213eb72376a954262d7132a097 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 17 Jan 2022 11:45:53 +0100 +Subject: [PATCH 5/9] monitor-config-store: Add way to define config store + loading policy + +This adds a way to define a way, at the system level, to define a policy +of how monitor configuration files are loaded. + +The intended use case is to e.g. either prefer system level monitor +configurations before user levels, or only allow system level +configurations. + +Examples: + +Prefer system over user level configurations: + + + + + system + user + + + + ... + + + +Only allow system level configurations: + + + + + system + + + + ... + + + +(cherry picked from commit b747884c1eaf309bb2d9395a655c85c968bd1829) +--- + src/backends/meta-backend-types.h | 2 + + src/backends/meta-monitor-config-manager.h | 4 +- + src/backends/meta-monitor-config-store.c | 421 ++++++++++++++---- + src/backends/meta-monitor-config-store.h | 21 +- + .../monitor-config-migration-unit-tests.c | 1 + + src/tests/monitor-configs/policy.xml | 27 ++ + src/tests/monitor-store-unit-tests.c | 33 ++ + src/tests/monitor-test-utils.c | 16 +- + src/tests/monitor-test-utils.h | 2 + + src/tests/monitor-unit-tests.c | 3 + + src/tests/test-utils.c | 8 +- + src/tests/test-utils.h | 6 +- + 12 files changed, 450 insertions(+), 94 deletions(-) + create mode 100644 src/tests/monitor-configs/policy.xml + +diff --git a/src/backends/meta-backend-types.h b/src/backends/meta-backend-types.h +index 98cac8b9e2a9..15184f513289 100644 +--- a/src/backends/meta-backend-types.h ++++ b/src/backends/meta-backend-types.h +@@ -27,6 +27,8 @@ typedef struct _MetaMonitorConfigManager MetaMonitorConfigManager; + typedef struct _MetaMonitorConfigStore MetaMonitorConfigStore; + typedef struct _MetaMonitorsConfig MetaMonitorsConfig; + ++typedef enum _MetaMonitorsConfigFlag MetaMonitorsConfigFlag; ++ + typedef struct _MetaMonitor MetaMonitor; + typedef struct _MetaMonitorNormal MetaMonitorNormal; + typedef struct _MetaMonitorTiled MetaMonitorTiled; +diff --git a/src/backends/meta-monitor-config-manager.h b/src/backends/meta-monitor-config-manager.h +index bb847b96e314..a3047b8c6639 100644 +--- a/src/backends/meta-monitor-config-manager.h ++++ b/src/backends/meta-monitor-config-manager.h +@@ -51,12 +51,12 @@ typedef struct _MetaMonitorsConfigKey + GList *monitor_specs; + } MetaMonitorsConfigKey; + +-typedef enum _MetaMonitorsConfigFlag ++enum _MetaMonitorsConfigFlag + { + META_MONITORS_CONFIG_FLAG_NONE = 0, + META_MONITORS_CONFIG_FLAG_MIGRATED = (1 << 0), + META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG = (1 << 1), +-} MetaMonitorsConfigFlag; ++}; + + struct _MetaMonitorsConfig + { +diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c +index f6b5256aa798..b320746a4418 100644 +--- a/src/backends/meta-monitor-config-store.c ++++ b/src/backends/meta-monitor-config-store.c +@@ -120,6 +120,9 @@ struct _MetaMonitorConfigStore + GFile *user_file; + GFile *custom_read_file; + GFile *custom_write_file; ++ ++ gboolean has_stores_policy; ++ GList *stores_policy; + }; + + #define META_MONITOR_CONFIG_STORE_ERROR (meta_monitor_config_store_error_quark ()) +@@ -162,12 +165,18 @@ typedef enum + STATE_MONITOR_MODE_FLAG, + STATE_MONITOR_UNDERSCANNING, + STATE_DISABLED, ++ STATE_POLICY, ++ STATE_STORES, ++ STATE_STORE, + } ParserState; + + typedef struct + { + ParserState state; + MetaMonitorConfigStore *config_store; ++ GFile *file; ++ ++ GHashTable *pending_configs; + + ParserState monitor_spec_parent_state; + +@@ -180,6 +189,10 @@ typedef struct + MetaMonitorConfig *current_monitor_config; + MetaLogicalMonitorConfig *current_logical_monitor_config; + GList *current_disabled_monitor_specs; ++ gboolean seen_policy; ++ gboolean seen_stores; ++ MetaConfigStore pending_store; ++ GList *stores; + + ParserState unknown_state_root; + int unknown_level; +@@ -268,16 +281,31 @@ handle_start_element (GMarkupParseContext *context, + + case STATE_MONITORS: + { +- if (!g_str_equal (element_name, "configuration")) ++ if (g_str_equal (element_name, "configuration")) ++ { ++ parser->state = STATE_CONFIGURATION; ++ parser->current_was_migrated = FALSE; ++ } ++ else if (g_str_equal (element_name, "policy")) ++ { ++ if (parser->seen_policy) ++ { ++ g_set_error (error, ++ G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, ++ "Multiple policy definitions"); ++ return; ++ } ++ ++ parser->seen_policy = TRUE; ++ parser->state = STATE_POLICY; ++ } ++ else + { + enter_unknown_element (parser, element_name, + "monitors", STATE_MONITORS); + return; + } + +- parser->state = STATE_CONFIGURATION; +- parser->current_was_migrated = FALSE; +- + return; + } + +@@ -523,6 +551,59 @@ handle_start_element (GMarkupParseContext *context, + + return; + } ++ ++ case STATE_POLICY: ++ { ++ if (!(parser->extra_config_flags & ++ META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG)) ++ { ++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, ++ "Policy can only be defined in system level configurations"); ++ return; ++ } ++ ++ if (g_str_equal (element_name, "stores")) ++ { ++ if (parser->seen_stores) ++ { ++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, ++ "Multiple stores elements under policy"); ++ return; ++ } ++ ++ parser->seen_stores = TRUE; ++ parser->state = STATE_STORES; ++ } ++ else ++ { ++ enter_unknown_element (parser, element_name, ++ "policy", STATE_POLICY); ++ } ++ ++ return; ++ } ++ ++ case STATE_STORES: ++ { ++ if (g_str_equal (element_name, "store")) ++ { ++ parser->state = STATE_STORE; ++ } ++ else ++ { ++ enter_unknown_element (parser, element_name, ++ "stores", STATE_STORES); ++ } ++ ++ return; ++ } ++ ++ case STATE_STORE: ++ { ++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, ++ "Invalid store sub element '%s'", element_name); ++ return; ++ } + } + } + +@@ -819,13 +900,65 @@ handle_end_element (GMarkupParseContext *context, + return; + } + +- g_hash_table_replace (parser->config_store->configs, ++ g_hash_table_replace (parser->pending_configs, + config->key, config); + + parser->state = STATE_MONITORS; + return; + } + ++ case STATE_STORE: ++ g_assert (g_str_equal (element_name, "store")); ++ ++ if (parser->pending_store == -1) ++ { ++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, ++ "Got an empty store"); ++ return; ++ } ++ ++ if (g_list_find (parser->stores, ++ GINT_TO_POINTER (parser->pending_store))) ++ { ++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, ++ "Multiple identical stores in policy"); ++ return; ++ } ++ ++ parser->stores = ++ g_list_append (parser->stores, ++ GINT_TO_POINTER (parser->pending_store)); ++ parser->pending_store = -1; ++ ++ parser->state = STATE_STORES; ++ return; ++ ++ case STATE_STORES: ++ g_assert (g_str_equal (element_name, "stores")); ++ ++ if (parser->config_store->has_stores_policy) ++ { ++ g_warning ("Ignoring stores policy from '%s', " ++ "it has already been configured", ++ g_file_peek_path (parser->file)); ++ g_clear_pointer (&parser->stores, g_list_free); ++ } ++ else ++ { ++ parser->config_store->stores_policy = ++ g_steal_pointer (&parser->stores); ++ parser->config_store->has_stores_policy = TRUE; ++ } ++ ++ parser->state = STATE_POLICY; ++ return; ++ ++ case STATE_POLICY: ++ g_assert (g_str_equal (element_name, "policy")); ++ ++ parser->state = STATE_MONITORS; ++ return; ++ + case STATE_UNKNOWN: + { + parser->unknown_level--; +@@ -970,6 +1103,8 @@ handle_text (GMarkupParseContext *context, + case STATE_MONITOR_MODE: + case STATE_TRANSFORM: + case STATE_DISABLED: ++ case STATE_POLICY: ++ case STATE_STORES: + { + if (!is_all_whitespace (text, text_len)) + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, +@@ -1120,6 +1255,36 @@ handle_text (GMarkupParseContext *context, + error); + return; + } ++ ++ case STATE_STORE: ++ { ++ MetaConfigStore store; ++ ++ if (parser->pending_store != -1) ++ { ++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, ++ "Multiple store strings"); ++ return; ++ } ++ ++ if (text_equals (text, text_len, "system")) ++ { ++ store = META_CONFIG_STORE_SYSTEM; ++ } ++ else if (text_equals (text, text_len, "user")) ++ { ++ store = META_CONFIG_STORE_USER; ++ } ++ else ++ { ++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, ++ "Invalid store %.*s", (int) text_len, text); ++ return; ++ } ++ ++ parser->pending_store = store; ++ return; ++ } + } + } + +@@ -1133,6 +1298,7 @@ static gboolean + read_config_file (MetaMonitorConfigStore *config_store, + GFile *file, + MetaMonitorsConfigFlag extra_config_flags, ++ GHashTable **out_configs, + GError **error) + { + char *buffer; +@@ -1145,9 +1311,15 @@ read_config_file (MetaMonitorConfigStore *config_store, + + parser = (ConfigParser) { + .state = STATE_INITIAL, ++ .file = file, + .config_store = config_store, ++ .pending_configs = g_hash_table_new_full (meta_monitors_config_key_hash, ++ meta_monitors_config_key_equal, ++ NULL, ++ g_object_unref), + .extra_config_flags = extra_config_flags, + .unknown_state_root = -1, ++ .pending_store = -1, + }; + + parse_context = g_markup_parse_context_new (&config_parser, +@@ -1165,9 +1337,13 @@ read_config_file (MetaMonitorConfigStore *config_store, + meta_monitor_config_free); + g_clear_pointer (&parser.current_logical_monitor_config, + meta_logical_monitor_config_free); ++ g_list_free (parser.stores); ++ g_hash_table_unref (parser.pending_configs); + return FALSE; + } + ++ *out_configs = g_steal_pointer (&parser.pending_configs); ++ + g_markup_parse_context_free (parse_context); + g_free (buffer); + +@@ -1511,23 +1687,34 @@ meta_monitor_config_store_remove (MetaMonitorConfigStore *config_store, + } + + gboolean +-meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store, +- const char *read_path, +- const char *write_path, +- GError **error) ++meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store, ++ const char *read_path, ++ const char *write_path, ++ MetaMonitorsConfigFlag config_flags, ++ GError **error) + { ++ GHashTable *new_configs = NULL; ++ + g_clear_object (&config_store->custom_read_file); + g_clear_object (&config_store->custom_write_file); +- g_hash_table_remove_all (config_store->configs); + + config_store->custom_read_file = g_file_new_for_path (read_path); + if (write_path) + config_store->custom_write_file = g_file_new_for_path (write_path); + +- return read_config_file (config_store, +- config_store->custom_read_file, +- META_MONITORS_CONFIG_FLAG_NONE, +- error); ++ g_clear_pointer (&config_store->stores_policy, g_list_free); ++ config_store->has_stores_policy = FALSE; ++ ++ if (!read_config_file (config_store, ++ config_store->custom_read_file, ++ config_flags, ++ &new_configs, ++ error)) ++ return FALSE; ++ ++ g_clear_pointer (&config_store->configs, g_hash_table_unref); ++ config_store->configs = g_steal_pointer (&new_configs); ++ return TRUE; + } + + int +@@ -1536,6 +1723,12 @@ meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_store + return (int) g_hash_table_size (config_store->configs); + } + ++GList * ++meta_monitor_config_store_get_stores_policy (MetaMonitorConfigStore *config_store) ++{ ++ return config_store->stores_policy; ++} ++ + MetaMonitorManager * + meta_monitor_config_store_get_monitor_manager (MetaMonitorConfigStore *config_store) + { +@@ -1554,75 +1747,8 @@ static void + meta_monitor_config_store_constructed (GObject *object) + { + MetaMonitorConfigStore *config_store = META_MONITOR_CONFIG_STORE (object); +- const char * const *system_dirs; +- char *user_file_path; +- GError *error = NULL; +- +- for (system_dirs = g_get_system_config_dirs (); +- system_dirs && *system_dirs; +- system_dirs++) +- { +- g_autofree char *system_file_path = NULL; +- +- system_file_path = g_build_filename (*system_dirs, "monitors.xml", NULL); +- if (g_file_test (system_file_path, G_FILE_TEST_EXISTS)) +- { +- g_autoptr (GFile) system_file = NULL; +- +- system_file = g_file_new_for_path (system_file_path); +- if (!read_config_file (config_store, +- system_file, +- META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG, +- &error)) +- { +- if (g_error_matches (error, +- META_MONITOR_CONFIG_STORE_ERROR, +- META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION)) +- g_warning ("System monitor configuration file (%s) is " +- "incompatible; ask your administrator to migrate " +- "the system monitor configuation.", +- system_file_path); +- else +- g_warning ("Failed to read monitors config file '%s': %s", +- system_file_path, error->message); +- g_clear_error (&error); +- } +- } +- } + +- user_file_path = g_build_filename (g_get_user_config_dir (), +- "monitors.xml", +- NULL); +- config_store->user_file = g_file_new_for_path (user_file_path); +- +- if (g_file_test (user_file_path, G_FILE_TEST_EXISTS)) +- { +- if (!read_config_file (config_store, +- config_store->user_file, +- META_MONITORS_CONFIG_FLAG_NONE, +- &error)) +- { +- if (error->domain == META_MONITOR_CONFIG_STORE_ERROR && +- error->code == META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION) +- { +- g_clear_error (&error); +- if (!meta_migrate_old_user_monitors_config (config_store, &error)) +- { +- g_warning ("Failed to migrate old monitors config file: %s", +- error->message); +- g_error_free (error); +- } +- } +- else +- { +- g_warning ("Failed to read monitors config file '%s': %s", +- user_file_path, error->message); +- g_error_free (error); +- } +- } +- } +- +- g_free (user_file_path); ++ meta_monitor_config_store_reset (config_store); + + G_OBJECT_CLASS (meta_monitor_config_store_parent_class)->constructed (object); + } +@@ -1645,6 +1771,7 @@ meta_monitor_config_store_dispose (GObject *object) + g_clear_object (&config_store->user_file); + g_clear_object (&config_store->custom_read_file); + g_clear_object (&config_store->custom_write_file); ++ g_clear_pointer (&config_store->stores_policy, g_list_free); + + G_OBJECT_CLASS (meta_monitor_config_store_parent_class)->dispose (object); + } +@@ -1715,3 +1842,133 @@ meta_monitor_config_store_class_init (MetaMonitorConfigStoreClass *klass) + + g_object_class_install_properties (object_class, PROP_LAST, obj_props); + } ++ ++static void ++replace_configs (MetaMonitorConfigStore *config_store, ++ GHashTable *configs) ++{ ++ GHashTableIter iter; ++ MetaMonitorsConfigKey *key; ++ MetaMonitorsConfig *config; ++ ++ g_hash_table_iter_init (&iter, configs); ++ while (g_hash_table_iter_next (&iter, ++ (gpointer *) &key, ++ (gpointer *) &config)) ++ { ++ g_hash_table_iter_steal (&iter); ++ g_hash_table_replace (config_store->configs, key, config); ++ } ++} ++ ++void ++meta_monitor_config_store_reset (MetaMonitorConfigStore *config_store) ++{ ++ g_autoptr (GHashTable) system_configs = NULL; ++ g_autoptr (GHashTable) user_configs = NULL; ++ const char * const *system_dirs; ++ char *user_file_path; ++ GError *error = NULL; ++ ++ g_clear_object (&config_store->user_file); ++ g_clear_object (&config_store->custom_read_file); ++ g_clear_object (&config_store->custom_write_file); ++ g_hash_table_remove_all (config_store->configs); ++ ++ for (system_dirs = g_get_system_config_dirs (); ++ system_dirs && *system_dirs; ++ system_dirs++) ++ { ++ g_autofree char *system_file_path = NULL; ++ ++ system_file_path = g_build_filename (*system_dirs, "monitors.xml", NULL); ++ if (g_file_test (system_file_path, G_FILE_TEST_EXISTS)) ++ { ++ g_autoptr (GFile) system_file = NULL; ++ ++ system_file = g_file_new_for_path (system_file_path); ++ if (!read_config_file (config_store, ++ system_file, ++ META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG, ++ &system_configs, ++ &error)) ++ { ++ if (g_error_matches (error, ++ META_MONITOR_CONFIG_STORE_ERROR, ++ META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION)) ++ g_warning ("System monitor configuration file (%s) is " ++ "incompatible; ask your administrator to migrate " ++ "the system monitor configuration.", ++ system_file_path); ++ else ++ g_warning ("Failed to read monitors config file '%s': %s", ++ system_file_path, error->message); ++ g_clear_error (&error); ++ } ++ } ++ } ++ ++ user_file_path = g_build_filename (g_get_user_config_dir (), ++ "monitors.xml", ++ NULL); ++ config_store->user_file = g_file_new_for_path (user_file_path); ++ ++ if (g_file_test (user_file_path, G_FILE_TEST_EXISTS)) ++ { ++ if (!read_config_file (config_store, ++ config_store->user_file, ++ META_MONITORS_CONFIG_FLAG_NONE, ++ &user_configs, ++ &error)) ++ { ++ if (error->domain == META_MONITOR_CONFIG_STORE_ERROR && ++ error->code == META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION) ++ { ++ g_clear_error (&error); ++ if (!meta_migrate_old_user_monitors_config (config_store, &error)) ++ { ++ g_warning ("Failed to migrate old monitors config file: %s", ++ error->message); ++ g_error_free (error); ++ } ++ } ++ else ++ { ++ g_warning ("Failed to read monitors config file '%s': %s", ++ user_file_path, error->message); ++ g_error_free (error); ++ } ++ } ++ } ++ ++ if (config_store->has_stores_policy) ++ { ++ GList *l; ++ ++ for (l = g_list_last (config_store->stores_policy); l; l = l->prev) ++ { ++ MetaConfigStore store = GPOINTER_TO_INT (l->data); ++ ++ switch (store) ++ { ++ case META_CONFIG_STORE_SYSTEM: ++ if (system_configs) ++ replace_configs (config_store, system_configs); ++ break; ++ case META_CONFIG_STORE_USER: ++ if (user_configs) ++ replace_configs (config_store, user_configs); ++ } ++ } ++ } ++ else ++ { ++ if (system_configs) ++ replace_configs (config_store, system_configs); ++ if (user_configs) ++ replace_configs (config_store, user_configs); ++ } ++ ++ ++ g_free (user_file_path); ++} +diff --git a/src/backends/meta-monitor-config-store.h b/src/backends/meta-monitor-config-store.h +index 92c24ecaa8b6..cb6759dca00f 100644 +--- a/src/backends/meta-monitor-config-store.h ++++ b/src/backends/meta-monitor-config-store.h +@@ -26,6 +26,12 @@ + + #include "backends/meta-monitor-config-manager.h" + ++typedef enum _MetaConfigStore ++{ ++ META_CONFIG_STORE_SYSTEM, ++ META_CONFIG_STORE_USER, ++} MetaConfigStore; ++ + #define META_TYPE_MONITOR_CONFIG_STORE (meta_monitor_config_store_get_type ()) + G_DECLARE_FINAL_TYPE (MetaMonitorConfigStore, meta_monitor_config_store, + META, MONITOR_CONFIG_STORE, GObject) +@@ -46,10 +52,14 @@ void meta_monitor_config_store_remove (MetaMonitorConfigStore *config_store, + MetaMonitorsConfig *config); + + META_EXPORT_TEST +-gboolean meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store, +- const char *read_path, +- const char *write_path, +- GError **error); ++gboolean meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store, ++ const char *read_path, ++ const char *write_path, ++ MetaMonitorsConfigFlag flags, ++ GError **error); ++ ++META_EXPORT_TEST ++GList * meta_monitor_config_store_get_stores_policy (MetaMonitorConfigStore *config_store); + + META_EXPORT_TEST + int meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_store); +@@ -57,4 +67,7 @@ int meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_s + META_EXPORT_TEST + MetaMonitorManager * meta_monitor_config_store_get_monitor_manager (MetaMonitorConfigStore *config_store); + ++META_EXPORT_TEST ++void meta_monitor_config_store_reset (MetaMonitorConfigStore *config_store); ++ + #endif /* META_MONITOR_CONFIG_STORE_H */ +diff --git a/src/tests/monitor-config-migration-unit-tests.c b/src/tests/monitor-config-migration-unit-tests.c +index 461035d6304a..096869c84c2a 100644 +--- a/src/tests/monitor-config-migration-unit-tests.c ++++ b/src/tests/monitor-config-migration-unit-tests.c +@@ -55,6 +55,7 @@ test_migration (const char *old_config, + NULL); + if (!meta_monitor_config_store_set_custom (config_store, "/dev/null", + migrated_path, ++ META_MONITORS_CONFIG_FLAG_NONE, + &error)) + g_error ("Failed to set custom config store: %s", error->message); + +diff --git a/src/tests/monitor-configs/policy.xml b/src/tests/monitor-configs/policy.xml +new file mode 100644 +index 000000000000..760046513e6e +--- /dev/null ++++ b/src/tests/monitor-configs/policy.xml +@@ -0,0 +1,27 @@ ++ ++ ++ ++ system ++ ++ ++ ++ ++ 0 ++ 0 ++ yes ++ ++ ++ DP-1 ++ MetaProduct's Inc. ++ MetaMonitor ++ 0x123456 ++ ++ ++ 1920 ++ 1080 ++ 60 ++ ++ ++ ++ ++ +diff --git a/src/tests/monitor-store-unit-tests.c b/src/tests/monitor-store-unit-tests.c +index 4ff0e396dda7..478193742ce8 100644 +--- a/src/tests/monitor-store-unit-tests.c ++++ b/src/tests/monitor-store-unit-tests.c +@@ -890,6 +890,35 @@ meta_test_monitor_store_unknown_elements (void) + check_monitor_configurations (&expect); + } + ++static void ++meta_test_monitor_store_policy_not_allowed (void) ++{ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, ++ "*Policy can only be defined in system level " ++ "configurations*"); ++ set_custom_monitor_config ("policy.xml"); ++ g_test_assert_expected_messages (); ++} ++ ++static void ++meta_test_monitor_store_policy (void) ++{ ++ MetaBackend *backend = meta_get_backend (); ++ MetaMonitorManager *monitor_manager = ++ meta_backend_get_monitor_manager (backend); ++ MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; ++ MetaMonitorConfigStore *config_store = ++ meta_monitor_config_manager_get_store (config_manager); ++ GList *stores_policy; ++ ++ set_custom_monitor_system_config ("policy.xml"); ++ stores_policy = meta_monitor_config_store_get_stores_policy (config_store); ++ g_assert_cmpuint (g_list_length (stores_policy), ==, 1); ++ g_assert_cmpint (GPOINTER_TO_INT (stores_policy->data), ++ ==, ++ META_CONFIG_STORE_SYSTEM); ++} ++ + void + init_monitor_store_tests (void) + { +@@ -917,4 +946,8 @@ init_monitor_store_tests (void) + meta_test_monitor_store_interlaced); + g_test_add_func ("/backends/monitor-store/unknown-elements", + meta_test_monitor_store_unknown_elements); ++ g_test_add_func ("/backends/monitor-store/policy-not-allowed", ++ meta_test_monitor_store_policy_not_allowed); ++ g_test_add_func ("/backends/monitor-store/policy", ++ meta_test_monitor_store_policy); + } +diff --git a/src/tests/monitor-test-utils.c b/src/tests/monitor-test-utils.c +index d2dc3b2658c6..c50fa910e2ad 100644 +--- a/src/tests/monitor-test-utils.c ++++ b/src/tests/monitor-test-utils.c +@@ -25,10 +25,24 @@ + #include "backends/meta-monitor-config-manager.h" + #include "backends/meta-monitor-config-store.h" + ++static void ++set_custom_monitor_config_common (const char *filename, ++ MetaMonitorsConfigFlag configs_flags) ++{ ++ meta_set_custom_monitor_config (meta_get_backend (), filename, configs_flags); ++} ++ + void + set_custom_monitor_config (const char *filename) + { +- meta_set_custom_monitor_config (meta_get_backend (), filename); ++ set_custom_monitor_config_common (filename, META_MONITORS_CONFIG_FLAG_NONE); ++} ++ ++void ++set_custom_monitor_system_config (const char *filename) ++{ ++ set_custom_monitor_config_common (filename, ++ META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG); + } + + char * +diff --git a/src/tests/monitor-test-utils.h b/src/tests/monitor-test-utils.h +index 4b9b3f0c790d..2869f271d525 100644 +--- a/src/tests/monitor-test-utils.h ++++ b/src/tests/monitor-test-utils.h +@@ -26,6 +26,8 @@ gboolean is_using_monitor_config_manager (void); + + void set_custom_monitor_config (const char *filename); + ++void set_custom_monitor_system_config (const char *filename); ++ + char * read_file (const char *file_path); + + #endif /* MONITOR_TEST_UTILS_H */ +diff --git a/src/tests/monitor-unit-tests.c b/src/tests/monitor-unit-tests.c +index 725f84173e86..83066289ebe3 100644 +--- a/src/tests/monitor-unit-tests.c ++++ b/src/tests/monitor-unit-tests.c +@@ -5721,6 +5721,7 @@ meta_test_monitor_migrated_rotated (void) + if (!meta_monitor_config_store_set_custom (config_store, + "/dev/null", + migrated_path, ++ META_MONITORS_CONFIG_FLAG_NONE, + &error)) + g_error ("Failed to set custom config store files: %s", error->message); + +@@ -5861,6 +5862,7 @@ meta_test_monitor_migrated_wiggle_discard (void) + if (!meta_monitor_config_store_set_custom (config_store, + "/dev/null", + migrated_path, ++ META_MONITORS_CONFIG_FLAG_NONE, + &error)) + g_error ("Failed to set custom config store files: %s", error->message); + +@@ -6006,6 +6008,7 @@ meta_test_monitor_migrated_wiggle (void) + if (!meta_monitor_config_store_set_custom (config_store, + "/dev/null", + migrated_path, ++ META_MONITORS_CONFIG_FLAG_NONE, + &error)) + g_error ("Failed to set custom config store files: %s", error->message); + +diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c +index f1a688478f67..23e7d92f91d6 100644 +--- a/src/tests/test-utils.c ++++ b/src/tests/test-utils.c +@@ -486,8 +486,9 @@ test_get_plugin_name (void) + } + + void +-meta_set_custom_monitor_config (MetaBackend *backend, +- const char *filename) ++meta_set_custom_monitor_config (MetaBackend *backend, ++ const char *filename, ++ MetaMonitorsConfigFlag configs_flags) + { + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); +@@ -503,8 +504,9 @@ meta_set_custom_monitor_config (MetaBackend *backend, + path = g_test_get_filename (G_TEST_DIST, "tests", "monitor-configs", + filename, NULL); + if (!meta_monitor_config_store_set_custom (config_store, path, NULL, ++ configs_flags, + &error)) +- g_error ("Failed to set custom config: %s", error->message); ++ g_warning ("Failed to set custom config: %s", error->message); + } + + static void +diff --git a/src/tests/test-utils.h b/src/tests/test-utils.h +index 6d6cdb924d01..c426661e38eb 100644 +--- a/src/tests/test-utils.h ++++ b/src/tests/test-utils.h +@@ -24,6 +24,7 @@ + #include + #include + ++#include "backends/meta-backend-types.h" + #include "meta/window.h" + + #define TEST_RUNNER_ERROR test_runner_error_quark () +@@ -81,8 +82,9 @@ void test_client_destroy (TestClient *client); + + const char * test_get_plugin_name (void); + +-void meta_set_custom_monitor_config (MetaBackend *backend, +- const char *filename); ++void meta_set_custom_monitor_config (MetaBackend *backend, ++ const char *filename, ++ MetaMonitorsConfigFlag configs_flags); + + void meta_wait_for_paint (MetaBackend *backend); + +-- +2.33.1 + + +From 5e8e431de7d6874d112d6dcee14ad1736d745a3b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 24 Sep 2021 19:07:32 +0200 +Subject: [PATCH 6/9] tests: Add more monitor config policy parsing tests + +(cherry picked from commit 48a3ff845fabf0f23568d3c798e047e9b303bffd) +--- + .../monitor-configs/policy-duplicate.xml | 8 ++++ + src/tests/monitor-configs/policy-empty.xml | 7 +++ + src/tests/monitor-configs/policy-invalid.xml | 8 ++++ + src/tests/monitor-configs/policy-multiple.xml | 12 +++++ + src/tests/monitor-store-unit-tests.c | 44 +++++++++++++++++++ + 5 files changed, 79 insertions(+) + create mode 100644 src/tests/monitor-configs/policy-duplicate.xml + create mode 100644 src/tests/monitor-configs/policy-empty.xml + create mode 100644 src/tests/monitor-configs/policy-invalid.xml + create mode 100644 src/tests/monitor-configs/policy-multiple.xml + +diff --git a/src/tests/monitor-configs/policy-duplicate.xml b/src/tests/monitor-configs/policy-duplicate.xml +new file mode 100644 +index 000000000000..d93cc81a4906 +--- /dev/null ++++ b/src/tests/monitor-configs/policy-duplicate.xml +@@ -0,0 +1,8 @@ ++ ++ ++ ++ user ++ user ++ ++ ++ +diff --git a/src/tests/monitor-configs/policy-empty.xml b/src/tests/monitor-configs/policy-empty.xml +new file mode 100644 +index 000000000000..f56026b66846 +--- /dev/null ++++ b/src/tests/monitor-configs/policy-empty.xml +@@ -0,0 +1,7 @@ ++ ++ ++ ++ ++ ++ ++ +diff --git a/src/tests/monitor-configs/policy-invalid.xml b/src/tests/monitor-configs/policy-invalid.xml +new file mode 100644 +index 000000000000..fc4552fbefc7 +--- /dev/null ++++ b/src/tests/monitor-configs/policy-invalid.xml +@@ -0,0 +1,8 @@ ++ ++ ++ ++ user ++ not-a-store ++ ++ ++ +diff --git a/src/tests/monitor-configs/policy-multiple.xml b/src/tests/monitor-configs/policy-multiple.xml +new file mode 100644 +index 000000000000..ffeb79aafe8a +--- /dev/null ++++ b/src/tests/monitor-configs/policy-multiple.xml +@@ -0,0 +1,12 @@ ++ ++ ++ ++ user ++ system ++ ++ ++ system ++ user ++ ++ ++ +diff --git a/src/tests/monitor-store-unit-tests.c b/src/tests/monitor-store-unit-tests.c +index 478193742ce8..50c220737b20 100644 +--- a/src/tests/monitor-store-unit-tests.c ++++ b/src/tests/monitor-store-unit-tests.c +@@ -919,6 +919,42 @@ meta_test_monitor_store_policy (void) + META_CONFIG_STORE_SYSTEM); + } + ++static void ++meta_test_monitor_store_policy_empty (void) ++{ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, ++ "*Invalid store*"); ++ set_custom_monitor_system_config ("policy-empty.xml"); ++ g_test_assert_expected_messages (); ++} ++ ++static void ++meta_test_monitor_store_policy_duplicate (void) ++{ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, ++ "*Multiple identical stores*"); ++ set_custom_monitor_system_config ("policy-duplicate.xml"); ++ g_test_assert_expected_messages (); ++} ++ ++static void ++meta_test_monitor_store_policy_invalid (void) ++{ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, ++ "*Invalid store*"); ++ set_custom_monitor_system_config ("policy-invalid.xml"); ++ g_test_assert_expected_messages (); ++} ++ ++static void ++meta_test_monitor_store_policy_multiple (void) ++{ ++ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, ++ "*Multiple stores elements under policy*"); ++ set_custom_monitor_system_config ("policy-multiple.xml"); ++ g_test_assert_expected_messages (); ++} ++ + void + init_monitor_store_tests (void) + { +@@ -950,4 +986,12 @@ init_monitor_store_tests (void) + meta_test_monitor_store_policy_not_allowed); + g_test_add_func ("/backends/monitor-store/policy", + meta_test_monitor_store_policy); ++ g_test_add_func ("/backends/monitor-store/policy-empty", ++ meta_test_monitor_store_policy_empty); ++ g_test_add_func ("/backends/monitor-store/policy-duplicate", ++ meta_test_monitor_store_policy_duplicate); ++ g_test_add_func ("/backends/monitor-store/policy-invalid", ++ meta_test_monitor_store_policy_invalid); ++ g_test_add_func ("/backends/monitor-store/policy-multiple", ++ meta_test_monitor_store_policy_multiple); + } +-- +2.33.1 + + +From a8f7700df399481d7bbbe42d13127e38dc193143 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Sep 2021 17:32:31 +0200 +Subject: [PATCH 7/9] monitor-config-store: Add test for monitor configuration + policy + +The test aims to verify that setting the following policy + + + + system + + + +only applies monitor configurations from the system level. + +(cherry picked from commit 9969a8aa25623dbff51e120d85ab202026571bb1) +--- + src/tests/monitor-configs/system/monitors.xml | 27 ++++ + src/tests/monitor-configs/user/monitors.xml | 22 +++ + src/tests/monitor-store-unit-tests.c | 17 +++ + src/tests/monitor-unit-tests.c | 132 ++++++++++++++++++ + 4 files changed, 198 insertions(+) + create mode 100644 src/tests/monitor-configs/system/monitors.xml + create mode 100644 src/tests/monitor-configs/user/monitors.xml + +diff --git a/src/tests/monitor-configs/system/monitors.xml b/src/tests/monitor-configs/system/monitors.xml +new file mode 100644 +index 000000000000..4d2eafec1327 +--- /dev/null ++++ b/src/tests/monitor-configs/system/monitors.xml +@@ -0,0 +1,27 @@ ++ ++ ++ ++ system ++ ++ ++ ++ ++ 0 ++ 0 ++ yes ++ ++ ++ DP-1 ++ MetaProduct's Inc. ++ MetaMonitor ++ 0x123456 ++ ++ ++ 640 ++ 480 ++ 60 ++ ++ ++ ++ ++ +diff --git a/src/tests/monitor-configs/user/monitors.xml b/src/tests/monitor-configs/user/monitors.xml +new file mode 100644 +index 000000000000..f125972e01e7 +--- /dev/null ++++ b/src/tests/monitor-configs/user/monitors.xml +@@ -0,0 +1,22 @@ ++ ++ ++ ++ 0 ++ 0 ++ yes ++ ++ ++ DP-1 ++ MetaProduct's Inc. ++ MetaMonitor ++ 0x123456 ++ ++ ++ 800 ++ 600 ++ 60 ++ ++ ++ ++ ++ +diff --git a/src/tests/monitor-store-unit-tests.c b/src/tests/monitor-store-unit-tests.c +index 50c220737b20..20860e7aa510 100644 +--- a/src/tests/monitor-store-unit-tests.c ++++ b/src/tests/monitor-store-unit-tests.c +@@ -958,6 +958,23 @@ meta_test_monitor_store_policy_multiple (void) + void + init_monitor_store_tests (void) + { ++ char *path; ++ ++ path = g_test_build_filename (G_TEST_DIST, ++ "tests", ++ "monitor-configs", ++ "system", ++ NULL); ++ g_setenv ("XDG_CONFIG_DIRS", path, TRUE); ++ g_free (path); ++ path = g_test_build_filename (G_TEST_DIST, ++ "tests", ++ "monitor-configs", ++ "user", ++ NULL); ++ g_setenv ("XDG_CONFIG_HOME", path, TRUE); ++ g_free (path); ++ + g_test_add_func ("/backends/monitor-store/single", + meta_test_monitor_store_single); + g_test_add_func ("/backends/monitor-store/vertical", +diff --git a/src/tests/monitor-unit-tests.c b/src/tests/monitor-unit-tests.c +index 83066289ebe3..5401edbc6d7c 100644 +--- a/src/tests/monitor-unit-tests.c ++++ b/src/tests/monitor-unit-tests.c +@@ -6043,6 +6043,135 @@ meta_test_monitor_migrated_wiggle (void) + g_error ("Failed to remove test data output file: %s", error->message); + } + ++static void ++meta_test_monitor_policy_system_only (void) ++{ ++ MetaMonitorTestSetup *test_setup; ++ MonitorTestCase test_case = { ++ .setup = { ++ .modes = { ++ { ++ .width = 1024, ++ .height = 768, ++ .refresh_rate = 60.0 ++ }, ++ { ++ .width = 800, ++ .height = 600, ++ .refresh_rate = 60.0 ++ }, ++ { ++ .width = 640, ++ .height = 480, ++ .refresh_rate = 60.0 ++ } ++ }, ++ .n_modes = 3, ++ .outputs = { ++ { ++ .crtc = 0, ++ .modes = { 0, 1, 2 }, ++ .n_modes = 3, ++ .preferred_mode = 0, ++ .possible_crtcs = { 0 }, ++ .n_possible_crtcs = 1, ++ .width_mm = 222, ++ .height_mm = 125 ++ }, ++ }, ++ .n_outputs = 1, ++ .crtcs = { ++ { ++ .current_mode = 0 ++ } ++ }, ++ .n_crtcs = 1 ++ }, ++ ++ .expect = { ++ .monitors = { ++ { ++ .outputs = { 0 }, ++ .n_outputs = 1, ++ .modes = { ++ { ++ .width = 1024, ++ .height = 768, ++ .refresh_rate = 60.0, ++ .crtc_modes = { ++ { ++ .output = 0, ++ .crtc_mode = 0 ++ } ++ } ++ }, ++ { ++ .width = 800, ++ .height = 600, ++ .refresh_rate = 60.0, ++ .crtc_modes = { ++ { ++ .output = 0, ++ .crtc_mode = 1 ++ } ++ } ++ }, ++ { ++ .width = 640, ++ .height = 480, ++ .refresh_rate = 60.0, ++ .crtc_modes = { ++ { ++ .output = 0, ++ .crtc_mode = 2 ++ } ++ } ++ } ++ }, ++ .n_modes = 3, ++ .current_mode = 2, ++ .width_mm = 222, ++ .height_mm = 125 ++ }, ++ }, ++ .n_monitors = 1, ++ .logical_monitors = { ++ { ++ .monitors = { 0 }, ++ .n_monitors = 1, ++ .layout = { .x = 0, .y = 0, .width = 640, .height = 480 }, ++ .scale = 1 ++ }, ++ }, ++ .n_logical_monitors = 1, ++ .primary_logical_monitor = 0, ++ .n_outputs = 1, ++ .crtcs = { ++ { ++ .current_mode = 2, ++ .x = 0, ++ } ++ }, ++ .n_crtcs = 1, ++ .screen_width = 640, ++ .screen_height = 480, ++ } ++ }; ++ MetaBackend *backend = meta_get_backend (); ++ MetaMonitorManager *monitor_manager = ++ meta_backend_get_monitor_manager (backend); ++ MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; ++ MetaMonitorConfigStore *config_store = ++ meta_monitor_config_manager_get_store (config_manager); ++ ++ test_setup = create_monitor_test_setup (&test_case.setup, ++ MONITOR_TEST_FLAG_NONE); ++ ++ meta_monitor_config_store_reset (config_store); ++ emulate_hotplug (test_setup); ++ check_monitor_configuration (&test_case); ++} ++ + static void + test_case_setup (void **fixture, + const void *data) +@@ -6159,6 +6288,9 @@ init_monitor_tests (void) + meta_test_monitor_migrated_wiggle); + add_monitor_test ("/backends/monitor/migrated/wiggle-discard", + meta_test_monitor_migrated_wiggle_discard); ++ ++ add_monitor_test ("/backends/monitor/policy/system-only", ++ meta_test_monitor_policy_system_only); + } + + void +-- +2.33.1 + + +From e4de67a35a70cfe0886b1932c442517ccfa0ca93 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 30 Sep 2021 21:06:38 +0200 +Subject: [PATCH 8/9] monitor-config-store: Allow changing D-Bus configuration + policy + +Adding a element containing a boolean (yes/no) determines +whether org.gnome.Mutter.DisplayConfig ApplyMonitorsConfig will be +callable. The state is also introspectable via the +ApplyMonitorsConfigAllowed property on the same interface. + +For example + + + + no + + + +(cherry picked from commit f2c7ae821b7af7e732e588e7548238dd814e5f84) +--- + src/backends/meta-monitor-config-store.c | 68 +++++++++++++++++++ + src/backends/meta-monitor-config-store.h | 8 +++ + src/backends/meta-monitor-manager-private.h | 1 + + src/backends/meta-monitor-manager.c | 24 +++++++ + src/org.gnome.Mutter.DisplayConfig.xml | 7 ++ + .../monitor-configs/policy-dbus-invalid.xml | 6 ++ + src/tests/monitor-configs/policy-dbus.xml | 5 ++ + src/tests/monitor-store-unit-tests.c | 49 +++++++++++++ + 8 files changed, 168 insertions(+) + create mode 100644 src/tests/monitor-configs/policy-dbus-invalid.xml + create mode 100644 src/tests/monitor-configs/policy-dbus.xml + +diff --git a/src/backends/meta-monitor-config-store.c b/src/backends/meta-monitor-config-store.c +index b320746a4418..cc866587c219 100644 +--- a/src/backends/meta-monitor-config-store.c ++++ b/src/backends/meta-monitor-config-store.c +@@ -123,6 +123,9 @@ struct _MetaMonitorConfigStore + + gboolean has_stores_policy; + GList *stores_policy; ++ ++ gboolean has_dbus_policy; ++ MetaMonitorConfigPolicy policy; + }; + + #define META_MONITOR_CONFIG_STORE_ERROR (meta_monitor_config_store_error_quark ()) +@@ -168,6 +171,7 @@ typedef enum + STATE_POLICY, + STATE_STORES, + STATE_STORE, ++ STATE_DBUS, + } ParserState; + + typedef struct +@@ -191,9 +195,13 @@ typedef struct + GList *current_disabled_monitor_specs; + gboolean seen_policy; + gboolean seen_stores; ++ gboolean seen_dbus; + MetaConfigStore pending_store; + GList *stores; + ++ gboolean enable_dbus_set; ++ gboolean enable_dbus; ++ + ParserState unknown_state_root; + int unknown_level; + +@@ -574,6 +582,19 @@ handle_start_element (GMarkupParseContext *context, + parser->seen_stores = TRUE; + parser->state = STATE_STORES; + } ++ else if (g_str_equal (element_name, "dbus")) ++ { ++ if (parser->seen_dbus) ++ { ++ g_set_error (error, ++ G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, ++ "Multiple dbus elements under policy"); ++ return; ++ } ++ ++ parser->seen_dbus = TRUE; ++ parser->state = STATE_DBUS; ++ } + else + { + enter_unknown_element (parser, element_name, +@@ -604,6 +625,13 @@ handle_start_element (GMarkupParseContext *context, + "Invalid store sub element '%s'", element_name); + return; + } ++ ++ case STATE_DBUS: ++ { ++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, ++ "Invalid dbus sub element '%s'", element_name); ++ return; ++ } + } + } + +@@ -953,6 +981,23 @@ handle_end_element (GMarkupParseContext *context, + parser->state = STATE_POLICY; + return; + ++ case STATE_DBUS: ++ if (!parser->config_store->has_dbus_policy) ++ { ++ parser->config_store->has_dbus_policy = TRUE; ++ parser->config_store->policy.enable_dbus = parser->enable_dbus; ++ parser->enable_dbus_set = FALSE; ++ } ++ else ++ { ++ g_warning ("Policy for monitor configuration via D-Bus " ++ "has already been set, ignoring policy from '%s'", ++ g_file_get_path (parser->file)); ++ } ++ parser->state = STATE_POLICY; ++ ++ return; ++ + case STATE_POLICY: + g_assert (g_str_equal (element_name, "policy")); + +@@ -1285,6 +1330,15 @@ handle_text (GMarkupParseContext *context, + parser->pending_store = store; + return; + } ++ ++ case STATE_DBUS: ++ { ++ parser->enable_dbus_set = TRUE; ++ read_bool (text, text_len, ++ &parser->enable_dbus, ++ error); ++ return; ++ } + } + } + +@@ -1628,6 +1682,11 @@ meta_monitor_config_store_save (MetaMonitorConfigStore *config_store) + return; + } + ++ if (config_store->has_stores_policy && ++ !g_list_find (config_store->stores_policy, ++ GINT_TO_POINTER (META_CONFIG_STORE_USER))) ++ return; ++ + config_store->save_cancellable = g_cancellable_new (); + + buffer = generate_config_xml (config_store); +@@ -1704,6 +1763,8 @@ meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store, + + g_clear_pointer (&config_store->stores_policy, g_list_free); + config_store->has_stores_policy = FALSE; ++ config_store->policy.enable_dbus = TRUE; ++ config_store->has_dbus_policy = FALSE; + + if (!read_config_file (config_store, + config_store->custom_read_file, +@@ -1819,6 +1880,7 @@ meta_monitor_config_store_init (MetaMonitorConfigStore *config_store) + meta_monitors_config_key_equal, + NULL, + g_object_unref); ++ config_store->policy.enable_dbus = TRUE; + } + + static void +@@ -1972,3 +2034,9 @@ meta_monitor_config_store_reset (MetaMonitorConfigStore *config_store) + + g_free (user_file_path); + } ++ ++const MetaMonitorConfigPolicy * ++meta_monitor_config_store_get_policy (MetaMonitorConfigStore *config_store) ++{ ++ return &config_store->policy; ++} +diff --git a/src/backends/meta-monitor-config-store.h b/src/backends/meta-monitor-config-store.h +index cb6759dca00f..a255e370baaf 100644 +--- a/src/backends/meta-monitor-config-store.h ++++ b/src/backends/meta-monitor-config-store.h +@@ -32,6 +32,11 @@ typedef enum _MetaConfigStore + META_CONFIG_STORE_USER, + } MetaConfigStore; + ++typedef struct _MetaMonitorConfigPolicy ++{ ++ gboolean enable_dbus; ++} MetaMonitorConfigPolicy; ++ + #define META_TYPE_MONITOR_CONFIG_STORE (meta_monitor_config_store_get_type ()) + G_DECLARE_FINAL_TYPE (MetaMonitorConfigStore, meta_monitor_config_store, + META, MONITOR_CONFIG_STORE, GObject) +@@ -70,4 +75,7 @@ MetaMonitorManager * meta_monitor_config_store_get_monitor_manager (MetaMonitorC + META_EXPORT_TEST + void meta_monitor_config_store_reset (MetaMonitorConfigStore *config_store); + ++META_EXPORT_TEST ++const MetaMonitorConfigPolicy * meta_monitor_config_store_get_policy (MetaMonitorConfigStore *config_store); ++ + #endif /* META_MONITOR_CONFIG_STORE_H */ +diff --git a/src/backends/meta-monitor-manager-private.h b/src/backends/meta-monitor-manager-private.h +index 223b5dfbda31..030eaf7b9ea1 100644 +--- a/src/backends/meta-monitor-manager-private.h ++++ b/src/backends/meta-monitor-manager-private.h +@@ -383,6 +383,7 @@ gboolean meta_monitor_manager_get_max_screen_size (MetaMonitorManager + MetaLogicalMonitorLayoutMode + meta_monitor_manager_get_default_layout_mode (MetaMonitorManager *manager); + ++META_EXPORT_TEST + MetaMonitorConfigManager * + meta_monitor_manager_get_config_manager (MetaMonitorManager *manager); + +diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c +index 0adf2100db18..f00e7c2abaad 100644 +--- a/src/backends/meta-monitor-manager.c ++++ b/src/backends/meta-monitor-manager.c +@@ -51,6 +51,7 @@ + #include "backends/meta-logical-monitor.h" + #include "backends/meta-monitor.h" + #include "backends/meta-monitor-config-manager.h" ++#include "backends/meta-monitor-config-store.h" + #include "backends/meta-orientation-manager.h" + #include "backends/meta-output.h" + #include "backends/x11/meta-monitor-manager-xrandr.h" +@@ -776,9 +777,18 @@ experimental_features_changed (MetaSettings *settings, + void + meta_monitor_manager_setup (MetaMonitorManager *manager) + { ++ MetaMonitorConfigStore *config_store; ++ const MetaMonitorConfigPolicy *policy; ++ + manager->in_init = TRUE; + + manager->config_manager = meta_monitor_config_manager_new (manager); ++ config_store = ++ meta_monitor_config_manager_get_store (manager->config_manager); ++ policy = meta_monitor_config_store_get_policy (config_store); ++ meta_dbus_display_config_set_apply_monitors_config_allowed (manager->display_config, ++ policy->enable_dbus); ++ + + meta_monitor_manager_read_current_state (manager); + +@@ -2033,6 +2043,8 @@ meta_monitor_manager_handle_apply_monitors_config (MetaDBusDisplayConfig *skelet + GVariant *properties_variant, + MetaMonitorManager *manager) + { ++ MetaMonitorConfigStore *config_store; ++ const MetaMonitorConfigPolicy *policy; + MetaMonitorManagerCapability capabilities; + GVariant *layout_mode_variant = NULL; + MetaLogicalMonitorLayoutMode layout_mode; +@@ -2049,6 +2061,18 @@ meta_monitor_manager_handle_apply_monitors_config (MetaDBusDisplayConfig *skelet + return TRUE; + } + ++ config_store = ++ meta_monitor_config_manager_get_store (manager->config_manager); ++ policy = meta_monitor_config_store_get_policy (config_store); ++ ++ if (!policy->enable_dbus) ++ { ++ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, ++ G_DBUS_ERROR_ACCESS_DENIED, ++ "Monitor configuration via D-Bus is disabled"); ++ return TRUE; ++ } ++ + capabilities = meta_monitor_manager_get_capabilities (manager); + + if (properties_variant) +diff --git a/src/org.gnome.Mutter.DisplayConfig.xml b/src/org.gnome.Mutter.DisplayConfig.xml +index 3abfa15d7816..07e2e58df758 100644 +--- a/src/org.gnome.Mutter.DisplayConfig.xml ++++ b/src/org.gnome.Mutter.DisplayConfig.xml +@@ -282,6 +282,13 @@ + --> + + ++ ++ ++ +