blob: ba4d0659115299ac7d3195246e1ca95adbf4d415 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gtk/wayland/gtk_ui_platform_wayland.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "ui/base/glib/glib_cast.h"
#include "ui/events/event_utils.h"
#include "ui/gtk/gtk_compat.h"
#include "ui/gtk/gtk_util.h"
#include "ui/gtk/input_method_context_impl_gtk.h"
#include "ui/linux/linux_ui_delegate.h"
namespace gtk {
GtkUiPlatformWayland::GtkUiPlatformWayland() {
gdk_set_allowed_backends("wayland");
// GDK_BACKEND takes precedence over gdk_set_allowed_backends(), so override
// it to ensure we get the wayland backend.
base::Environment::Create()->SetVar("GDK_BACKEND", "wayland");
}
GtkUiPlatformWayland::~GtkUiPlatformWayland() = default;
void GtkUiPlatformWayland::OnInitialized(GtkWidget* widget) {
// Nothing to do upon initialization for Wayland.
}
GdkKeymap* GtkUiPlatformWayland::GetGdkKeymap() {
NOTIMPLEMENTED_LOG_ONCE();
return nullptr;
}
GdkModifierType GtkUiPlatformWayland::GetGdkKeyEventState(
const ui::KeyEvent& key_event) {
// We first reconstruct the state that was stored as a property by
// ui::WaylandEventSource. It is incomplete, however, and includes only the
// modifier state. Therefore, we compute and add the group manually, if
// possible.
const ui::Event::Properties* properties = key_event.properties();
if (!properties)
return static_cast<GdkModifierType>(0);
auto it = properties->find(ui::kPropertyKeyboardState);
if (it == properties->end())
return static_cast<GdkModifierType>(0);
DCHECK_EQ(it->second.size(), 4u);
// Stored in little endian.
int flags = 0;
int bitshift = 0;
for (uint8_t value : it->second) {
flags |= value << bitshift;
bitshift += 8;
}
auto state = ExtractGdkEventStateFromKeyEventFlags(flags);
// We use the default group 0 in the following three cases:
// - we are using GTK 3 (gdk_display_map_keycode() is only available in
// GTK 4);
// - the pressed/released key is not a character (e.g. a modifier);
// - no entry in |keyvals| matches |keyval|.
unsigned int group = 0;
if (gtk::GtkCheckVersion(4) && key_event.GetDomKey().IsCharacter()) {
guint keycode =
GetKeyEventProperty(key_event, ui::kPropertyKeyboardHwKeyCode);
guint keyval = gdk_unicode_to_keyval(key_event.GetDomKey().ToCharacter());
GdkKeymapKey* keys;
guint* keyvals;
int n_entries;
gdk_display_map_keycode(GetDefaultGdkDisplay(), keycode, &keys, &keyvals,
&n_entries);
for (int i = 0; i < n_entries; ++i) {
if (keyvals[i] == keyval) {
group = keys[i].group;
break;
}
}
g_free(keys);
g_free(keyvals);
}
// As per Section 2.2.2 "Computing A State Field from an XKB State" of the XKB
// protocol specification.
return static_cast<GdkModifierType>(state | (group << 13));
}
int GtkUiPlatformWayland::GetGdkKeyEventGroup(const ui::KeyEvent& key_event) {
auto state = GetGdkKeyEventState(key_event);
// As per Section 2.2.2 "Computing A State Field from an XKB State" of the XKB
// protocol specification.
return (state >> 13) & 0x3;
}
GdkWindow* GtkUiPlatformWayland::GetGdkWindow(
gfx::AcceleratedWidget window_id) {
NOTIMPLEMENTED_LOG_ONCE();
return nullptr;
}
bool GtkUiPlatformWayland::SetGtkWidgetTransientFor(
GtkWidget* widget,
gfx::AcceleratedWidget parent) {
return ui::LinuxUiDelegate::GetInstance()->ExportWindowHandle(
parent, base::BindOnce(&GtkUiPlatformWayland::OnHandleSetTransient,
weak_factory_.GetWeakPtr(), widget));
}
void GtkUiPlatformWayland::ClearTransientFor(gfx::AcceleratedWidget parent) {
// Nothing to do here.
}
void GtkUiPlatformWayland::ShowGtkWindow(GtkWindow* window) {
// TODO(crbug.com/1008755): Check if gtk_window_present_with_time is needed
// here as well, similarly to what is done in X11 impl.
gtk_window_present(window);
}
GdkDisplay* GtkUiPlatformWayland::GetDefaultGdkDisplay() {
if (!default_display_)
default_display_ = gdk_display_get_default();
return default_display_;
}
void GtkUiPlatformWayland::OnHandleSetTransient(GtkWidget* widget,
const std::string& handle) {
char* parent = const_cast<char*>(handle.c_str());
if (gtk::GtkCheckVersion(4)) {
auto* toplevel = GlibCast<GdkToplevel>(
gtk_native_get_surface(gtk_widget_get_native(widget)),
gdk_toplevel_get_type());
gdk_wayland_toplevel_set_transient_for_exported(toplevel, parent);
} else if (gtk::GtkCheckVersion(3, 22)) {
gdk_wayland_window_set_transient_for_exported(gtk_widget_get_window(widget),
parent);
} else {
LOG(WARNING) << "set_transient_for_exported not supported in GTK version "
<< gtk_get_major_version() << '.' << gtk_get_minor_version()
<< '.' << gtk_get_micro_version();
}
}
std::unique_ptr<ui::LinuxInputMethodContext>
GtkUiPlatformWayland::CreateInputMethodContext(
ui::LinuxInputMethodContextDelegate* delegate) const {
// GDK3 doesn't have a way to create foreign wayland windows, so we can't
// translate from ui::KeyEvent to GdkEventKey for InputMethodContextImplGtk.
if (!GtkCheckVersion(4))
return nullptr;
return std::make_unique<InputMethodContextImplGtk>(delegate);
}
bool GtkUiPlatformWayland::IncludeFontScaleInDeviceScale() const {
return false;
}
} // namespace gtk