blob: 32101e56383a59cf06a1aa9a836248cfb377e62f [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gtk/x/gtk_ui_delegate_x11.h"
#include <gtk/gtk.h>
#include "base/check.h"
#include "base/environment.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/platform/x11/x11_event_source.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/x/xlib_support.h"
#include "ui/gfx/x/xproto.h"
#include "ui/gfx/x/xproto_util.h"
#include "ui/gtk/x/gtk_event_loop_x11.h"
#include "ui/platform_window/x11/x11_window.h"
#include "ui/platform_window/x11/x11_window_manager.h"
extern "C" {
GdkWindow* gdk_x11_window_foreign_new_for_display(GdkDisplay* display,
unsigned long window);
GdkWindow* gdk_x11_window_lookup_for_display(GdkDisplay* display,
unsigned long window);
unsigned long gdk_x11_window_get_xid(GdkWindow* window);
}
namespace ui {
GtkUiDelegateX11::GtkUiDelegateX11(x11::Connection* connection)
: connection_(connection) {
DCHECK(connection_);
gdk_set_allowed_backends("x11");
// GDK_BACKEND takes precedence over gdk_set_allowed_backends(), so override
// it to ensure we get the x11 backend.
base::Environment::Create()->SetVar("GDK_BACKEND", "x11");
x11::InitXlib();
}
GtkUiDelegateX11::~GtkUiDelegateX11() = default;
void GtkUiDelegateX11::OnInitialized() {
// Ensure the singleton instance of GtkEventLoopX11 is created and started.
GtkEventLoopX11::EnsureInstance();
// GTK sets an Xlib error handler that exits the process on any async errors.
// We don't want this behavior, so reset the error handler to something that
// just logs the error.
x11::SetXlibErrorHandler();
}
GdkKeymap* GtkUiDelegateX11::GetGdkKeymap() {
return gdk_keymap_get_for_display(GetGdkDisplay());
}
GdkWindow* GtkUiDelegateX11::GetGdkWindow(gfx::AcceleratedWidget window_id) {
GdkDisplay* display = GetGdkDisplay();
GdkWindow* gdk_window = gdk_x11_window_lookup_for_display(
display, static_cast<uint32_t>(window_id));
if (gdk_window)
g_object_ref(gdk_window);
else
gdk_window = gdk_x11_window_foreign_new_for_display(
display, static_cast<uint32_t>(window_id));
return gdk_window;
}
bool GtkUiDelegateX11::SetGdkWindowTransientFor(GdkWindow* window,
gfx::AcceleratedWidget parent) {
auto x11_window = static_cast<x11::Window>(gdk_x11_window_get_xid(window));
x11::SetProperty(x11_window, x11::Atom::WM_TRANSIENT_FOR, x11::Atom::WINDOW,
parent);
ui::X11Window* parent_window =
ui::X11WindowManager::GetInstance()->GetWindow(parent);
parent_window->SetTransientWindow(x11_window);
return true;
}
void GtkUiDelegateX11::ClearTransientFor(gfx::AcceleratedWidget parent) {
ui::X11Window* parent_window =
ui::X11WindowManager::GetInstance()->GetWindow(parent);
// parent_window might be dead if there was a top-down window close
if (parent_window)
parent_window->SetTransientWindow(x11::Window::None);
}
GdkDisplay* GtkUiDelegateX11::GetGdkDisplay() {
if (!display_)
display_ = gdk_display_get_default();
return display_;
}
void GtkUiDelegateX11::ShowGtkWindow(GtkWindow* window) {
// We need to call gtk_window_present after making the widgets visible to make
// sure window gets correctly raised and gets focus.
DCHECK(X11EventSource::HasInstance());
gtk_window_present_with_time(
window,
static_cast<uint32_t>(X11EventSource::GetInstance()->GetTimestamp()));
}
int GtkUiDelegateX11::GetGdkKeyState() {
auto* xevent =
X11EventSource::GetInstance()->connection()->dispatching_event();
if (!xevent)
return ui::EF_NONE;
auto* key_xevent = xevent->As<x11::KeyEvent>();
DCHECK(key_xevent);
return static_cast<int>(key_xevent->state);
}
} // namespace ui