Skip to content

Instantly share code, notes, and snippets.

@alebastr
Last active February 10, 2021 21:58
Show Gist options
  • Save alebastr/0100f74b7d0df44d24e10347dfe0170e to your computer and use it in GitHub Desktop.
Save alebastr/0100f74b7d0df44d24e10347dfe0170e to your computer and use it in GitHub Desktop.
wl-output.cpp
CXXFLAGS += -std=c++17
CXXFLAGS += -Wall -Wextra -pedantic
CXXFLAGS += $(shell pkg-config --cflags wayland-client)
CFLAGS += $(shell pkg-config --cflags wayland-client)
LDFLAGS += $(shell pkg-config --libs wayland-client)
SCANNER = $(shell pkg-config --variable=wayland_scanner wayland-scanner)
PROTOCOLS_DIR = $(shell pkg-config --variable=pkgdatadir wayland-protocols)
XDG_OUTPUT_PROTO = $(PROTOCOLS_DIR)/unstable/xdg-output/xdg-output-unstable-v1.xml
all: wl-output
clean:
$(RM) wl-output *.o xdg-output-unstable-v1*.[ch]
xdg-output-unstable-v1-client-header.h: $(XDG_OUTPUT_PROTO)
$(SCANNER) client-header $< $@
xdg-output-unstable-v1.c: $(XDG_OUTPUT_PROTO) xdg-output-unstable-v1-client-header.h
$(SCANNER) private-code $< $@
wl-output: wl-output.o xdg-output-unstable-v1.o
$(CXX) $(LDFLAGS) -o $@ $^
/**
* g++ -std=c++17 $(pkg-config --cflags --libs wayland-client) -o wl-output wl-output.cpp
*/
#include <wayland-client.h>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <string>
#include <unordered_map>
#if __has_include("xdg-output-unstable-v1-client-header.h")
#define HAVE_XDG_OUTPUT 1
#include "xdg-output-unstable-v1-client-header.h"
#endif
template <auto fn> using deleter_fn = std::integral_constant<decltype(fn), fn>;
template <typename... Args> void ignore(Args...) {}
const auto start = std::chrono::steady_clock::now();
struct output {
static const wl_output_listener wl_listener;
std::unique_ptr<wl_output, deleter_fn<wl_output_destroy>> wlo;
std::string name = "?";
std::string model = "<undetected>";
std::string description;
bool ready = false;
output(struct wl_output *wlo_) : wlo{wlo_} {
wl_output_add_listener(wlo.get(), &wl_listener, this);
}
#ifdef HAVE_XDG_OUTPUT
std::unique_ptr<zxdg_output_v1, deleter_fn<zxdg_output_v1_destroy>> xdg_output;
output(struct wl_output *wlo_, struct zxdg_output_manager_v1 *xdg_output_manager)
: wlo{wlo_} {
static const zxdg_output_v1_listener xdg_listener = {
ignore, ignore,
[](void *that, auto *) { static_cast<output *>(that)->handle_done(); },
[](void *that, auto *, const char *name_) {
static_cast<output *>(that)->name = name_;
},
[](void *that, auto *, const char *description_) {
static_cast<output *>(that)->description = description_;
}};
if (xdg_output_manager) {
xdg_output.reset(zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, wlo_));
zxdg_output_v1_add_listener(xdg_output.get(), &xdg_listener, this);
} else {
wl_output_add_listener(wlo.get(), &wl_listener, this);
}
}
#endif
void handle_geometry(int, int, int, int, int, const char *make_, const char *model_,
int) {
model = std::string{make_} + " " + model_;
}
void handle_done() {
if (!ready) {
ready = true;
auto now = std::chrono::steady_clock::now() - start;
printf("[% 12ld] output %s (%p) added\n",
std::chrono::duration_cast<std::chrono::milliseconds>(now).count(),
(description.empty() ? model : description).data(), (void *)wlo.get());
}
}
~output() {
auto now = std::chrono::steady_clock::now() - start;
printf("[% 12ld] output %s (%p) removed\n",
std::chrono::duration_cast<std::chrono::milliseconds>(now).count(),
(description.empty() ? model : description).data(), (void *)wlo.get());
}
};
const wl_output_listener output::wl_listener = {
[](void *that, auto *, auto... args) {
static_cast<output *>(that)->handle_geometry(args...);
},
ignore, [](void *that, auto *) { static_cast<output *>(that)->handle_done(); },
ignore};
std::unordered_map<uint32_t, struct output> outputs;
void handle_global(void *, wl_registry *registry, uint32_t name, const char *interface,
uint32_t version) {
#ifdef HAVE_XDG_OUTPUT
static std::unique_ptr<zxdg_output_manager_v1,
deleter_fn<zxdg_output_manager_v1_destroy>>
xdg_output_manager;
if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
xdg_output_manager.reset((struct zxdg_output_manager_v1 *)wl_registry_bind(
registry, name, &zxdg_output_manager_v1_interface, 2));
}
#endif
if (strcmp(interface, wl_output_interface.name) == 0) {
auto output = (struct wl_output *)wl_registry_bind(
registry, name, &wl_output_interface,
std::min<uint32_t>(wl_output_interface.version, version));
#ifdef HAVE_XDG_OUTPUT
outputs.try_emplace(name, output, xdg_output_manager.get());
#else
outputs.try_emplace(name, output);
#endif
}
}
void handle_global_remove(void *, wl_registry *, uint32_t name) { outputs.erase(name); }
int main(int, char **) {
wl_display *display = wl_display_connect(nullptr);
wl_registry *registry = wl_display_get_registry(display);
static const wl_registry_listener listener = {handle_global, handle_global_remove};
wl_registry_add_listener(registry, &listener, nullptr);
while (wl_display_dispatch(display) != -1) {
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment