Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsWindow.h"
#include "nsWindowX11.h"
#include "mozilla/gfx/gfxVars.h"
#include <gdk/gdkkeysyms-compat.h>
#include <X11/Xatom.h>
#include <X11/extensions/XShm.h>
#include <X11/extensions/shape.h>
#include "gfxXlibSurface.h"
#include "GLContextGLX.h" // for GLContextGLX::FindVisual()
#include "GLContextEGL.h" // for GLContextEGL::FindVisual()
#include "WindowSurfaceX11Image.h"
#include "WindowSurfaceX11SHM.h"
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace mozilla::widget;
using mozilla::gl::GLContextEGL;
using mozilla::gl::GLContextGLX;
void nsWindowX11::GetWorkspaceID(nsAString& workspaceID) {
workspaceID.Truncate();
if (!mShell) {
return;
}
LOG("nsWindow::GetWorkspaceID()\n");
// Get the gdk window for this widget.
GdkWindow* gdk_window = GetToplevelGdkWindow();
if (!gdk_window) {
LOG(" missing Gdk window, quit.");
return;
}
if (WorkspaceManagementDisabled()) {
LOG(" WorkspaceManagementDisabled, quit.");
return;
}
GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
GdkAtom type_returned;
int format_returned;
int length_returned;
long* wm_desktop;
if (!gdk_property_get(gdk_window, gdk_atom_intern("_NET_WM_DESKTOP", FALSE),
cardinal_atom,
0, // offset
INT32_MAX, // length
FALSE, // delete
&type_returned, &format_returned, &length_returned,
(guchar**)&wm_desktop)) {
LOG(" gdk_property_get() failed, quit.");
return;
}
LOG(" got workspace ID %d", (int32_t)wm_desktop[0]);
workspaceID.AppendInt((int32_t)wm_desktop[0]);
g_free(wm_desktop);
}
void nsWindowX11::MoveToWorkspace(const nsAString& workspaceIDStr) {
nsresult rv = NS_OK;
int32_t workspaceID = workspaceIDStr.ToInteger(&rv);
LOG("nsWindow::MoveToWorkspace() ID %d", workspaceID);
if (NS_FAILED(rv) || !workspaceID || !mShell) {
LOG(" MoveToWorkspace disabled, quit");
return;
}
// Get the gdk window for this widget.
GdkWindow* gdk_window = GetToplevelGdkWindow();
if (!gdk_window) {
LOG(" failed to get GdkWindow, quit.");
return;
}
// This code is inspired by some found in the 'gxtuner' project.
XEvent xevent;
Display* xdisplay = gdk_x11_get_default_xdisplay();
GdkScreen* screen = gdk_window_get_screen(gdk_window);
Window root_win = GDK_WINDOW_XID(gdk_screen_get_root_window(screen));
GdkDisplay* display = gdk_window_get_display(gdk_window);
Atom type = gdk_x11_get_xatom_by_name_for_display(display, "_NET_WM_DESKTOP");
xevent.type = ClientMessage;
xevent.xclient.type = ClientMessage;
xevent.xclient.serial = 0;
xevent.xclient.send_event = TRUE;
xevent.xclient.display = xdisplay;
xevent.xclient.window = GDK_WINDOW_XID(gdk_window);
xevent.xclient.message_type = type;
xevent.xclient.format = 32;
xevent.xclient.data.l[0] = workspaceID;
xevent.xclient.data.l[1] = X11CurrentTime;
xevent.xclient.data.l[2] = 0;
xevent.xclient.data.l[3] = 0;
xevent.xclient.data.l[4] = 0;
XSendEvent(xdisplay, root_win, FALSE,
SubstructureNotifyMask | SubstructureRedirectMask, &xevent);
XFlush(xdisplay);
LOG(" moved to workspace");
}
Window nsWindowX11::GetX11Window() {
return gdk_x11_window_get_xid(mGdkWindow);
}
// Configure GL visual on X11.
bool nsWindowX11::ConfigureX11GLVisual() {
auto* screen = gtk_widget_get_screen(mShell);
int visualId = 0;
bool haveVisual = false;
if (gfxVars::UseEGL()) {
haveVisual = GLContextEGL::FindVisual(&visualId);
}
// We are on GLX or use it as a fallback on Mesa, see
if (!haveVisual) {
auto* display = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(mShell));
int screenNumber = GDK_SCREEN_XNUMBER(screen);
haveVisual = GLContextGLX::FindVisual(display, screenNumber, &visualId);
}
GdkVisual* gdkVisual = nullptr;
if (haveVisual) {
// If we're using CSD, rendering will go through mContainer, but
// it will inherit this visual as it is a child of mShell.
gdkVisual = gdk_x11_screen_lookup_visual(screen, visualId);
}
if (!gdkVisual) {
NS_WARNING("We're missing X11 Visual!");
// We try to use a fallback alpha visual
GdkScreen* screen = gtk_widget_get_screen(mShell);
gdkVisual = gdk_screen_get_rgba_visual(screen);
}
if (gdkVisual) {
gtk_widget_set_visual(mShell, gdkVisual);
mHasAlphaVisual = true;
return true;
}
return false;
}
void nsWindowX11::CreateNative() {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
gtk_widget_set_double_buffered(GTK_WIDGET(mContainer), FALSE);
#pragma GCC diagnostic pop
mSurfaceProvider.Initialize(GetX11Window());
// Set window manager hint to keep fullscreen windows composited.
//
// If the window were to get unredirected, there could be visible
// tearing because Gecko does not align its framebuffer updates with
// vblank.
SetCompositorHint(GTK_WIDGET_COMPOSITED_ENABLED);
}
void nsWindowX11::DestroyNative() {}
void nsWindowX11::SetCompositorHint(WindowComposeRequest aState) {
gulong value = aState;
GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
gdk_property_change(GetToplevelGdkWindow(),
gdk_atom_intern("_NET_WM_BYPASS_COMPOSITOR", FALSE),
cardinal_atom,
32, // format
GDK_PROP_MODE_REPLACE, (guchar*)&value, 1);
}
/* XApp progress support currently works by setting a property
* on a window with this Atom name. A supporting window manager
* will notice this and pass it along to whatever handling has
* been implemented on that end (e.g. passing it on to a taskbar
* widget.) There is no issue if WM support is lacking, this is
* simply ignored in that case.
*
* for further details.
*/
#define PROGRESS_HINT "_NET_WM_XAPP_PROGRESS"
static void set_window_hint_cardinal(Window xid, const gchar* atom_name,
gulong cardinal) {
GdkDisplay* display;
display = gdk_display_get_default();
if (cardinal > 0) {
XChangeProperty(GDK_DISPLAY_XDISPLAY(display), xid,
gdk_x11_get_xatom_by_name_for_display(display, atom_name),
XA_CARDINAL, 32, PropModeReplace, (guchar*)&cardinal, 1);
} else {
XDeleteProperty(GDK_DISPLAY_XDISPLAY(display), xid,
gdk_x11_get_xatom_by_name_for_display(display, atom_name));
}
}
void nsWindowX11::SetProgress(unsigned long progressPercent) {
progressPercent = MIN(progressPercent, 100);
set_window_hint_cardinal(GDK_WINDOW_XID(GetToplevelGdkWindow()),
PROGRESS_HINT, progressPercent);
}