blob: 8e74cfccd2fec417e8e793eb68ebaa5ff27c04e9 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#include <wayland-client-protocol.h>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/writable_shared_memory_region.h"
#include "base/strings/string_number_conversions.h"
#include "brillo/syslog_logging.h"
constexpr char kBgColorFlag[] = "bgcolor";
constexpr char kWidthFlag[] = "width";
constexpr char kHeightFlag[] = "height";
constexpr char kTitleFlag[] = "title";
struct demo_data {
uint32_t bgcolor;
uint32_t width;
uint32_t height;
std::string title;
int scale;
struct wl_compositor* compositor;
struct wl_shell* shell;
struct wl_shm* shm;
struct wl_surface* surface;
struct wl_shell_surface* shell_surface;
struct wl_buffer* buffer;
struct wl_callback* callback;
struct wl_callback_listener* callback_listener;
struct wl_output* output;
struct wl_output_listener* output_listener;
struct wl_keyboard_listener* keyboard_listener;
void* shm_ptr;
bool done;
};
void keyboard_keymap(void* data,
struct wl_keyboard* keyboard,
uint32_t format,
int32_t fd,
uint32_t size) {}
void keyboard_enter(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
struct wl_surface* surface,
struct wl_array* keys) {}
void keyboard_leave(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
struct wl_surface* surface) {}
void keyboard_key(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
uint32_t time,
uint32_t key,
uint32_t state) {
struct demo_data* data_ptr = reinterpret_cast<struct demo_data*>(data);
// Key pressed.
if (state == 1) {
LOG(INFO) << "wayland_demo application detected keypress";
data_ptr->done = true;
}
}
void keyboard_modifiers(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
uint32_t mods_depressed,
uint32_t mods_latched,
uint32_t mods_locked,
uint32_t group) {}
void keyboard_repeat_info(void* data,
struct wl_keyboard* keyboard,
int32_t rate,
int32_t delay) {}
void demo_registry_listener(void* data,
struct wl_registry* registry,
uint32_t id,
const char* interface,
uint32_t version) {
struct demo_data* data_ptr = reinterpret_cast<struct demo_data*>(data);
if (!strcmp("wl_compositor", interface)) {
data_ptr->compositor = reinterpret_cast<struct wl_compositor*>(
wl_registry_bind(registry, id, &wl_compositor_interface, version));
} else if (!strcmp("wl_shell", interface)) {
data_ptr->shell = reinterpret_cast<struct wl_shell*>(
wl_registry_bind(registry, id, &wl_shell_interface, version));
} else if (!strcmp("wl_shm", interface)) {
data_ptr->shm = reinterpret_cast<struct wl_shm*>(
wl_registry_bind(registry, id, &wl_shm_interface, version));
} else if (!strcmp("wl_output", interface)) {
data_ptr->output = reinterpret_cast<struct wl_output*>(
wl_registry_bind(registry, id, &wl_output_interface, version));
wl_output_add_listener(data_ptr->output, data_ptr->output_listener,
data_ptr);
} else if (!strcmp("wl_seat", interface)) {
struct wl_seat* seat = reinterpret_cast<struct wl_seat*>(
wl_registry_bind(registry, id, &wl_seat_interface, version));
wl_keyboard_add_listener(wl_seat_get_keyboard(seat),
data_ptr->keyboard_listener, data_ptr);
}
}
void demo_registry_remover(void* data,
struct wl_registry* registry,
uint32_t id) {}
void shell_surface_ping(void* data,
struct wl_shell_surface* shell_surface,
uint32_t serial) {
wl_shell_surface_pong(shell_surface, serial);
}
void shell_surface_configure(void* data,
struct wl_shell_surface* shell_surface,
uint32_t edges,
int32_t width,
int32_t height) {}
void shell_surface_popup_done(void* data,
struct wl_shell_surface* shell_surface) {}
void demo_draw(void* data, struct wl_callback* callback, uint32_t time) {
struct demo_data* data_ptr = reinterpret_cast<struct demo_data*>(data);
wl_callback_destroy(data_ptr->callback);
wl_surface_damage(data_ptr->surface, 0, 0, data_ptr->width, data_ptr->height);
uint32_t* surface_data = reinterpret_cast<uint32_t*>(data_ptr->shm_ptr);
for (int i = 0; i < data_ptr->width * data_ptr->height; ++i) {
surface_data[i] = data_ptr->bgcolor;
}
data_ptr->callback = wl_surface_frame(data_ptr->surface);
wl_surface_attach(data_ptr->surface, data_ptr->buffer, 0, 0);
wl_callback_add_listener(data_ptr->callback, data_ptr->callback_listener,
data_ptr);
wl_surface_commit(data_ptr->surface);
}
void output_geometry(void* data,
struct wl_output* output,
int32_t x,
int32_t y,
int32_t physical_width,
int32_t physical_height,
int32_t subpixel,
const char* make,
const char* model,
int32_t transform) {}
void output_mode(void* data,
struct wl_output* output,
uint32_t flags,
int32_t width,
int32_t height,
int32_t refresh) {
struct demo_data* data_ptr = reinterpret_cast<struct demo_data*>(data);
if (data_ptr->width == 0) {
data_ptr->width = width;
if (data_ptr->scale != 0) {
data_ptr->width /= data_ptr->scale;
}
}
if (data_ptr->height == 0) {
data_ptr->height = height;
if (data_ptr->scale != 0) {
data_ptr->height /= data_ptr->scale;
}
}
}
void output_done(void* data, struct wl_output* output) {}
void output_scale(void* data, struct wl_output* output, int32_t factor) {
struct demo_data* data_ptr = reinterpret_cast<struct demo_data*>(data);
data_ptr->scale = factor;
if (data_ptr->width != 0) {
data_ptr->width /= factor;
}
if (data_ptr->height != 0) {
data_ptr->height /= factor;
}
}
int main(int argc, char* argv[]) {
brillo::InitLog(brillo::kLogToSyslog);
LOG(INFO) << "Starting wayland_demo application";
base::CommandLine::Init(argc, argv);
base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
struct demo_data data;
memset(&data, 0, sizeof(data));
data.done = false;
data.bgcolor = 0x3388DD;
if (cl->HasSwitch(kBgColorFlag)) {
data.bgcolor =
strtoul(cl->GetSwitchValueASCII(kBgColorFlag).c_str(), nullptr, 0);
}
if (cl->HasSwitch(kWidthFlag)) {
if (!base::StringToUint(cl->GetSwitchValueASCII(kWidthFlag), &data.width)) {
LOG(ERROR) << "Invalid width parameter passed";
return -1;
}
}
if (cl->HasSwitch(kHeightFlag)) {
if (!base::StringToUint(cl->GetSwitchValueASCII(kHeightFlag),
&data.height)) {
LOG(ERROR) << "Invalid height parameter passed";
return -1;
}
}
data.title = "wayland_demo";
if (cl->HasSwitch(kTitleFlag)) {
data.title = cl->GetSwitchValueASCII(kTitleFlag);
}
struct wl_display* display = wl_display_connect(nullptr);
if (!display) {
LOG(ERROR) << "Failed connecting to display";
return -1;
}
struct wl_output_listener output_listener = {output_geometry, output_mode,
output_done, output_scale};
data.output_listener = &output_listener;
struct wl_registry_listener registry_listener = {
demo_registry_listener,
demo_registry_remover,
};
struct wl_keyboard_listener keyboard_listener = {
keyboard_keymap, keyboard_enter, keyboard_leave,
keyboard_key, keyboard_modifiers, keyboard_repeat_info};
data.keyboard_listener = &keyboard_listener;
struct wl_registry* registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, &data);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (!data.compositor) {
LOG(ERROR) << "Failed to find compositor";
return -1;
}
if (!data.output) {
LOG(ERROR) << "Failed to get output";
return -1;
}
// Do another roundtrip to ensure we get the wl_output callbacks.
wl_display_roundtrip(display);
data.surface = wl_compositor_create_surface(data.compositor);
if (!data.surface) {
LOG(ERROR) << "Failed creating surface";
return -1;
}
if (!data.shell) {
LOG(ERROR) << "Failed getting shell";
return -1;
}
data.shell_surface = wl_shell_get_shell_surface(data.shell, data.surface);
if (!data.shell_surface) {
LOG(ERROR) << "Failed getting shell surface";
return -1;
}
const struct wl_shell_surface_listener shell_surface_listener = {
shell_surface_ping, shell_surface_configure, shell_surface_popup_done};
wl_shell_surface_add_listener(data.shell_surface, &shell_surface_listener,
nullptr);
wl_shell_surface_set_toplevel(data.shell_surface);
wl_shell_surface_set_class(data.shell_surface, data.title.c_str());
wl_shell_surface_set_title(data.shell_surface, data.title.c_str());
data.callback = wl_surface_frame(data.surface);
struct wl_callback_listener callback_listener = {demo_draw};
data.callback_listener = &callback_listener;
wl_callback_add_listener(data.callback, data.callback_listener, &data);
if (!data.shm) {
LOG(ERROR) << "Failed getting shared memory";
return -1;
}
size_t stride = data.width * 4 /* 32bpp */;
size_t shm_size = stride * data.height;
base::WritableSharedMemoryRegion shm_region =
base::WritableSharedMemoryRegion::Create(shm_size);
base::WritableSharedMemoryMapping shm_mapping = shm_region.Map();
data.shm_ptr = shm_mapping.memory();
base::subtle::PlatformSharedMemoryRegion platform_region =
base::WritableSharedMemoryRegion::TakeHandleForSerialization(
std::move(shm_region));
base::ScopedFD handle = std::move(platform_region.PassPlatformHandle().fd);
struct wl_shm_pool* pool =
wl_shm_create_pool(data.shm, handle.get(), shm_size);
data.buffer = wl_shm_pool_create_buffer(pool, 0, data.width, data.height,
stride, WL_SHM_FORMAT_XRGB8888);
wl_shm_pool_destroy(pool);
wl_surface_attach(data.surface, data.buffer, 0, 0);
wl_surface_commit(data.surface);
demo_draw(&data, nullptr, 0);
LOG(INFO) << "wayland_demo application displaying, waiting for keypress";
do {
} while (wl_display_dispatch(display) != -1 && !data.done);
wl_display_disconnect(display);
LOG(INFO) << "wayland_demo application exiting";
return 0;
}