зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1521012 - [Linux/GNOME] Use X shape mask to draw transparent corners when we draw to titlebar, r=lsalzman
To support rounded corners of Gtk+ titlebar themes (Adwaita, Radiance..) in GNOME we need to use X shape mask as fully transparent toplevel window causes various issues (like Bug 1516224). We draw mShell as transparent and mContainer as non-transparent with shape mask applied. The shape mask is generated only when titlebar rendering is enabled and it's generated from GtkHeaderBar Widget to match the exact look. We use existing mTransparencyBitmap for the shape mask where mTransparencyBitmapForTitlebar controls whether it's a general shape mask or our specialised shape for titlebar only. This is already enabled for GNOME environment by default. So there's a new preference widget.default-hidden-titlebar added to easily disable it if any issue appears during testing. Differential Revision: https://phabricator.services.mozilla.com/D17283 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
618075899f
Коммит
59a8114e76
|
@ -5995,3 +5995,7 @@ pref("prio.enabled", false);
|
|||
// External.AddSearchProvider is deprecated and it will be removed in the next
|
||||
// cycles.
|
||||
pref("dom.sidebar.enabled", true);
|
||||
|
||||
#if defined(MOZ_WIDGET_GTK)
|
||||
pref("widget.default-hidden-titlebar", true);
|
||||
#endif
|
||||
|
|
|
@ -312,8 +312,12 @@ void moz_container_realize(GtkWidget *widget) {
|
|||
attributes.width = allocation.width;
|
||||
attributes.height = allocation.height;
|
||||
attributes.wclass = GDK_INPUT_OUTPUT;
|
||||
attributes.visual = gtk_widget_get_visual(widget);
|
||||
attributes.window_type = GDK_WINDOW_CHILD;
|
||||
MozContainer *container = MOZ_CONTAINER(widget);
|
||||
attributes.visual =
|
||||
container->force_default_visual
|
||||
? gdk_screen_get_system_visual(gtk_widget_get_screen(widget))
|
||||
: gtk_widget_get_visual(widget);
|
||||
|
||||
window = gdk_window_new(parent, &attributes, attributes_mask);
|
||||
gdk_window_set_user_data(window, widget);
|
||||
|
@ -549,3 +553,7 @@ gboolean moz_container_surface_needs_clear(MozContainer *container) {
|
|||
return state;
|
||||
}
|
||||
#endif
|
||||
|
||||
void moz_container_force_default_visual(MozContainer *container) {
|
||||
container->force_default_visual = true;
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@ struct _MozContainer {
|
|||
gboolean surface_needs_clear;
|
||||
gboolean ready_to_draw;
|
||||
#endif
|
||||
gboolean force_default_visual;
|
||||
};
|
||||
|
||||
struct _MozContainerClass {
|
||||
|
@ -89,6 +90,7 @@ GType moz_container_get_type(void);
|
|||
GtkWidget *moz_container_new(void);
|
||||
void moz_container_put(MozContainer *container, GtkWidget *child_widget, gint x,
|
||||
gint y);
|
||||
void moz_container_force_default_visual(MozContainer *container);
|
||||
|
||||
#ifdef MOZ_WAYLAND
|
||||
struct wl_surface *moz_container_get_wl_surface(MozContainer *container);
|
||||
|
|
|
@ -425,6 +425,7 @@ nsWindow::nsWindow() {
|
|||
|
||||
mIsTransparent = false;
|
||||
mTransparencyBitmap = nullptr;
|
||||
mTransparencyBitmapForTitlebar = false;
|
||||
|
||||
mTransparencyBitmapWidth = 0;
|
||||
mTransparencyBitmapHeight = 0;
|
||||
|
@ -1925,6 +1926,10 @@ gboolean nsWindow::OnExposeEvent(cairo_t *cr) {
|
|||
|
||||
bool shaped = false;
|
||||
if (eTransparencyTransparent == GetTransparencyMode()) {
|
||||
if (mTransparencyBitmapForTitlebar) {
|
||||
static_cast<nsWindow *>(GetTopLevelWidget())
|
||||
->UpdateTitlebarTransparencyBitmap();
|
||||
} else {
|
||||
if (mHasAlphaVisual) {
|
||||
// Remove possible shape mask from when window manger was not
|
||||
// previously compositing.
|
||||
|
@ -1933,6 +1938,7 @@ gboolean nsWindow::OnExposeEvent(cairo_t *cr) {
|
|||
shaped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!shaped) {
|
||||
GList *children = gdk_window_peek_children(mGdkWindow);
|
||||
|
@ -3265,15 +3271,24 @@ nsresult nsWindow::Create(nsIWidget *aParent, nsNativeWidget aNativeParent,
|
|||
bool shouldAccelerate = ComputeShouldAccelerate();
|
||||
MOZ_ASSERT(shouldAccelerate | !useWebRender);
|
||||
|
||||
// Some Gtk+ themes use non-rectangular toplevel windows. To fully support
|
||||
// such themes we need to make toplevel window transparent with ARGB
|
||||
// visual. It may cause performanance issue so make it configurable and
|
||||
// enable it by default for selected window managers. Also disable it for
|
||||
// X11 SW rendering (Bug 1516224) by default.
|
||||
if (mWindowType == eWindowType_toplevel &&
|
||||
(shouldAccelerate || !mIsX11Display ||
|
||||
Preferences::HasUserValue("mozilla.widget.use-argb-visuals"))) {
|
||||
if (mWindowType == eWindowType_toplevel) {
|
||||
// We enable titlebar rendering for toplevel windows only.
|
||||
mCSDSupportLevel = GetSystemCSDSupportLevel();
|
||||
|
||||
// Some Gtk+ themes use non-rectangular toplevel windows. To fully
|
||||
// support such themes we need to make toplevel window transparent
|
||||
// with ARGB visual.
|
||||
// It may cause performanance issue so make it configurable
|
||||
// and enable it by default for selected window managers.
|
||||
needsAlphaVisual = TopLevelWindowUseARGBVisual();
|
||||
if (needsAlphaVisual && mIsX11Display && !shouldAccelerate) {
|
||||
// We want to draw a transparent titlebar but we can't use
|
||||
// ARGB visual due to Bug 1516224.
|
||||
// We use ARGB visual for mShell only and shape mask
|
||||
// for mContainer where is all our content drawn.
|
||||
mTransparencyBitmapForTitlebar = true;
|
||||
mCSDSupportLevel = CSD_SUPPORT_CLIENT;
|
||||
}
|
||||
}
|
||||
|
||||
bool isSetVisual = false;
|
||||
|
@ -3404,9 +3419,6 @@ nsresult nsWindow::Create(nsIWidget *aParent, nsNativeWidget aNativeParent,
|
|||
GtkWindowGroup *group = gtk_window_group_new();
|
||||
gtk_window_group_add_window(group, GTK_WINDOW(mShell));
|
||||
g_object_unref(group);
|
||||
|
||||
// We enable titlebar rendering for toplevel windows only.
|
||||
mCSDSupportLevel = GetSystemCSDSupportLevel();
|
||||
}
|
||||
|
||||
// Create a container to hold child windows and child GtkWidgets.
|
||||
|
@ -3442,6 +3454,9 @@ nsresult nsWindow::Create(nsIWidget *aParent, nsNativeWidget aNativeParent,
|
|||
gtk_widget_add_events(mShell, GDK_PROPERTY_CHANGE_MASK);
|
||||
gtk_widget_set_app_paintable(mShell, TRUE);
|
||||
}
|
||||
if (mTransparencyBitmapForTitlebar) {
|
||||
moz_container_force_default_visual(mContainer);
|
||||
}
|
||||
|
||||
// If we draw to mContainer window then configure it now because
|
||||
// gtk_container_add() realizes the child widget.
|
||||
|
@ -4295,6 +4310,8 @@ nsresult nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect &aRect,
|
|||
}
|
||||
|
||||
NS_ASSERTION(mIsTransparent, "Window is not transparent");
|
||||
NS_ASSERTION(!mTransparencyBitmapForTitlebar,
|
||||
"Transparency bitmap is already used for titlebar rendering");
|
||||
|
||||
if (mTransparencyBitmap == nullptr) {
|
||||
int32_t size = GetBitmapStride(mBounds.width) * mBounds.height;
|
||||
|
@ -4324,6 +4341,91 @@ nsresult nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect &aRect,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// We need to shape only a few pixels of the titlebar as we care about
|
||||
// the corners only
|
||||
#define TITLEBAR_SHAPE_MASK_HEIGHT 10
|
||||
|
||||
void nsWindow::UpdateTitlebarTransparencyBitmap() {
|
||||
NS_ASSERTION(mTransparencyBitmapForTitlebar,
|
||||
"Transparency bitmap is already used to draw window shape");
|
||||
|
||||
if (!mDrawInTitlebar || (mBounds.width == mTransparencyBitmapWidth &&
|
||||
mBounds.height == mTransparencyBitmapHeight)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool maskCreate =
|
||||
!mTransparencyBitmap || mBounds.width > mTransparencyBitmapWidth;
|
||||
|
||||
bool maskUpdate =
|
||||
!mTransparencyBitmap || mBounds.width != mTransparencyBitmapWidth;
|
||||
|
||||
if (maskCreate) {
|
||||
if (mTransparencyBitmap) {
|
||||
delete[] mTransparencyBitmap;
|
||||
}
|
||||
int32_t size = GetBitmapStride(mBounds.width) * TITLEBAR_SHAPE_MASK_HEIGHT;
|
||||
mTransparencyBitmap = new gchar[size];
|
||||
mTransparencyBitmapWidth = mBounds.width;
|
||||
} else {
|
||||
mTransparencyBitmapWidth = mBounds.width;
|
||||
}
|
||||
mTransparencyBitmapHeight = mBounds.height;
|
||||
|
||||
if (maskUpdate) {
|
||||
cairo_surface_t *surface = cairo_image_surface_create(
|
||||
CAIRO_FORMAT_A8, mTransparencyBitmapWidth, TITLEBAR_SHAPE_MASK_HEIGHT);
|
||||
if (!surface) return;
|
||||
|
||||
cairo_t *cr = cairo_create(surface);
|
||||
|
||||
GtkWidgetState state;
|
||||
memset((void *)&state, 0, sizeof(state));
|
||||
GdkRectangle rect = {0, 0, mTransparencyBitmapWidth,
|
||||
TITLEBAR_SHAPE_MASK_HEIGHT};
|
||||
|
||||
moz_gtk_widget_paint(MOZ_GTK_HEADER_BAR, cr, &rect, &state, 0,
|
||||
GTK_TEXT_DIR_NONE);
|
||||
|
||||
cairo_destroy(cr);
|
||||
cairo_surface_mark_dirty(surface);
|
||||
cairo_surface_flush(surface);
|
||||
|
||||
UpdateMaskBits(
|
||||
mTransparencyBitmap, mTransparencyBitmapWidth,
|
||||
TITLEBAR_SHAPE_MASK_HEIGHT,
|
||||
nsIntRect(0, 0, mTransparencyBitmapWidth, TITLEBAR_SHAPE_MASK_HEIGHT),
|
||||
cairo_image_surface_get_data(surface),
|
||||
cairo_format_stride_for_width(CAIRO_FORMAT_A8,
|
||||
mTransparencyBitmapWidth));
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
if (!mNeedsShow) {
|
||||
Display *xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
|
||||
Window xDrawable = GDK_WINDOW_XID(mGdkWindow);
|
||||
|
||||
Pixmap maskPixmap = XCreateBitmapFromData(
|
||||
xDisplay, xDrawable, mTransparencyBitmap, mTransparencyBitmapWidth,
|
||||
TITLEBAR_SHAPE_MASK_HEIGHT);
|
||||
|
||||
XShapeCombineMask(xDisplay, xDrawable, ShapeBounding, 0, 0, maskPixmap,
|
||||
ShapeSet);
|
||||
|
||||
if (mTransparencyBitmapHeight > TITLEBAR_SHAPE_MASK_HEIGHT) {
|
||||
XRectangle rect = {0, 0, (unsigned short)mTransparencyBitmapWidth,
|
||||
(unsigned short)(mTransparencyBitmapHeight -
|
||||
TITLEBAR_SHAPE_MASK_HEIGHT)};
|
||||
XShapeCombineRectangles(xDisplay, xDrawable, ShapeBounding, 0,
|
||||
TITLEBAR_SHAPE_MASK_HEIGHT, &rect, 1, ShapeUnion,
|
||||
0);
|
||||
}
|
||||
|
||||
XFreePixmap(xDisplay, maskPixmap);
|
||||
}
|
||||
}
|
||||
|
||||
void nsWindow::GrabPointer(guint32 aTime) {
|
||||
LOG(("GrabPointer time=0x%08x retry=%d\n", (unsigned int)aTime,
|
||||
mRetryPointerGrab));
|
||||
|
@ -6027,6 +6129,14 @@ void nsWindow::SetDrawsInTitlebar(bool aState) {
|
|||
}
|
||||
|
||||
mDrawInTitlebar = aState;
|
||||
|
||||
if (mTransparencyBitmapForTitlebar) {
|
||||
if (mDrawInTitlebar) {
|
||||
UpdateTitlebarTransparencyBitmap();
|
||||
} else {
|
||||
ClearTransparencyBitmap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gint nsWindow::GdkScaleFactor() {
|
||||
|
@ -6367,6 +6477,11 @@ bool nsWindow::HideTitlebarByDefault() {
|
|||
return hideTitlebar;
|
||||
}
|
||||
|
||||
if (!Preferences::GetBool("widget.default-hidden-titlebar", false)) {
|
||||
hideTitlebar = false;
|
||||
return hideTitlebar;
|
||||
}
|
||||
|
||||
const char *currentDesktop = getenv("XDG_CURRENT_DESKTOP");
|
||||
hideTitlebar =
|
||||
(currentDesktop && GetSystemCSDSupportLevel() != CSD_SUPPORT_NONE);
|
||||
|
@ -6386,6 +6501,11 @@ bool nsWindow::TopLevelWindowUseARGBVisual() {
|
|||
return useARGBVisual;
|
||||
}
|
||||
|
||||
GdkScreen *screen = gdk_screen_get_default();
|
||||
if (!gdk_screen_is_composited(screen)) {
|
||||
useARGBVisual = false;
|
||||
}
|
||||
|
||||
if (Preferences::HasUserValue("mozilla.widget.use-argb-visuals")) {
|
||||
useARGBVisual =
|
||||
Preferences::GetBool("mozilla.widget.use-argb-visuals", false);
|
||||
|
|
|
@ -308,6 +308,7 @@ class nsWindow final : public nsBaseWidget {
|
|||
nsresult UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect,
|
||||
uint8_t* aAlphas,
|
||||
int32_t aStride);
|
||||
void UpdateTitlebarTransparencyBitmap();
|
||||
|
||||
virtual void ReparentNativeWidget(nsIWidget* aNewParent) override;
|
||||
|
||||
|
@ -474,9 +475,6 @@ class nsWindow final : public nsBaseWidget {
|
|||
uint32_t mHasMappedToplevel : 1, mIsFullyObscured : 1, mRetryPointerGrab : 1;
|
||||
nsSizeMode mSizeState;
|
||||
|
||||
int32_t mTransparencyBitmapWidth;
|
||||
int32_t mTransparencyBitmapHeight;
|
||||
|
||||
nsIntPoint mClientOffset;
|
||||
|
||||
#if GTK_CHECK_VERSION(3, 4, 0)
|
||||
|
@ -565,6 +563,12 @@ class nsWindow final : public nsBaseWidget {
|
|||
// full translucency at this time; each pixel is either fully opaque
|
||||
// or fully transparent.
|
||||
gchar* mTransparencyBitmap;
|
||||
int32_t mTransparencyBitmapWidth;
|
||||
int32_t mTransparencyBitmapHeight;
|
||||
// The transparency bitmap is used instead of ARGB visual for toplevel
|
||||
// window to draw titlebar.
|
||||
bool mTransparencyBitmapForTitlebar;
|
||||
|
||||
// True when we're on compositing window manager and this
|
||||
// window is using visual with alpha channel.
|
||||
bool mHasAlphaVisual;
|
||||
|
|
Загрузка…
Ссылка в новой задаче