wayland如何处理事件

2025-07-13 17:25:23

wayland采用事件回调机制。

与X在服务端渲染不同,wayland中客户端负责渲染操作,而wayland服务器只负责将客户端渲染的内容同步到屏幕上,也就是合成。所以wayland服务器也可以叫做合成器/混成器,或compositor。

wayland要使客户端和服务端能够沟通,就需要协商好信息的格式,这也就是协议。协议包括了函数名和参数名称以及格式。一个wayland协议是一个xml文件,它可以通过扫描器(wayland-scanner)转成.h格式的C头文件,从而被C/C++调用。

协议中,将服务端发往客户端的通知叫做event,而把客户端发往服务端的消息叫做request。

为了处理客户端请求,服务端需要运行一个event_loop,而在这个循环中的主要工作就是处理事件回调。

事件回调处理中有几个主要概念:

通常先将callback传递进wl_listener的notify中,然后用wl_add_signal函数将signal绑定到listener。表明该listener将监听请求,并在请求发生时执行回调函数。

回调函数的声明大致如下:

void callback(wl_listener* listener, void* data);

在编写一个callback时,需要获取与这个事件有关的一些对象,并对这些对象执行操作。我们可以使用一个神奇的宏wl_container_of(listener, sample, member)来获取到存放着这个listener的结构体/类的实例指针。宏的定义如下:

#define wl_container_of(ptr, sample, member)				\
	(WL_TYPEOF(sample))((char *)(ptr) -				\
			     offsetof(WL_TYPEOF(*sample), member))

虽然很神奇但也很简单,就是传入的listener地址减去listener在对象中的偏移,从而获取到这个对象的地址。

data也就是客户端的传参,根据事件不同,一般可以将其强转成协议头文件提供的某个结构体指针。

在实际代码中,使用这个宏需要将事件的listener和所有事件需要用到的成员包裹在同一个结构体当中,因此一旦事件一多,就会导致代码冗长,样板代码多,且很难抽象。

为了应对这种情况,可以使用一个对象,可以取名叫做EventManager(或其他的什么东西),然后创建一些方法:

class EventManager {
public:
  template<typename F>
  void register_callback(wl_signal* signal, F f, void* data) {
    auto listener = std::make_unique<wl_listener>();
    listener->notify = f;
    wl_signal_add(signal, listener.get());

    this->storage[listener.get()] = data;
    if (!this->listeners.contains(data)) {
      this->listeners[data] = std::vector<std::unique_ptr<wl_listener>> {};
    }
    this->listeners[data].push_back(std::move(listener));
  }

  inline static std::unique_ptr<UraRuntime> init() {
    return std::make_unique<UraRuntime>();
  }

  // fetch resource by listener
  template<typename T>
  inline T fetch(wl_listener* listener) {
    return static_cast<T>(this->storage[listener]);
  }

  // remove all items linked with data, this will also delete data itself
  template<typename T>
  void remove(T data) {
    for (auto& listener : this->listeners[data]) {
      wl_list_remove(&listener->link);
      this->storage.erase(listener.get());
    }
    this->listeners.erase(data);
  }

private:
  // listener to data
  std::unordered_map<wl_listener*, void*> storage;
  // data to listeners
  std::unordered_map<void*, std::vector<std::unique_ptr<wl_listener>>>
    listeners;
};

这样做的好处在于无需在结构体中创建很多的listener,而只需要在注册回调时指定关联的结构体即可。