File: wayland-server.hpp

package info (click to toggle)
waylandpp 1.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,460 kB
  • sloc: xml: 10,327; cpp: 4,365; makefile: 26
file content (888 lines) | stat: -rw-r--r-- 33,138 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
/*
 * Copyright (c) 2022, Nils Christopher Brause
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef WAYLAND_SERVER_HPP
#define WAYLAND_SERVER_HPP

#include <atomic>
#include <functional>
#include <list>
#include <memory>
#include <string>
#include <cstdint>

#include <wayland-server-core.h>
#include <wayland-util.hpp>

/** \file */

namespace wayland
{
  namespace server
  {
    namespace detail
    {
      struct listener_t
      {
        wl_listener listener = { { nullptr, nullptr }, nullptr };
        void *user = nullptr;
      };
    }

    /** \brief Type for functions that handle log messages
     *
     * Log message is the first argument
     */
    using log_handler = std::function<void(std::string)>;

    /** \brief Set C library log handler
     *
     * The C library sometimes logs important information such as protocol
     * error messages, by default to the standard output. This can be used
     * to set an alternate function that will receive those messages.
     *
     * \param handler function that should be called for C library log messages
     */
    void set_log_handler(const log_handler& handler);

    class client_t;
    class global_base_t;
    template <class resource> class global_t;
    class event_loop_t;
    class event_source_t;

    class display_t
    {
    private:
      struct data_t
      {
        std::function<void()> destroy;
        std::function<void(client_t&)> client_created;
        detail::listener_t destroy_listener;
        detail::listener_t client_created_listener;
        std::function<bool(client_t, global_base_t)> filter_func;
        wayland::detail::any user_data;
        std::atomic<unsigned int> counter{1};
      };

      wl_display *display = nullptr;
      data_t *data = nullptr;

      static void destroy_func(wl_listener *listener, void *data);
      static void client_created_func(wl_listener *listener, void *cl);
      static data_t *wl_display_get_user_data(wl_display *display);
      static bool c_filter_func(const wl_client *client, const wl_global *global, void *data);

    protected:
      display_t(wl_display *c);
      void init();
      void fini();

      friend class client_t;

    public:
      /** Create Wayland display object.
       *
       * This creates the display object.
       */
      display_t();

      /** Destroy Wayland display object.
       *
       * This function emits the wl_display destroy signal, releases
       * all the sockets added to this display, free's all the globals associated
       * with this display, free's memory of additional shared memory formats and
       * destroy the display object.
       *
       * \sa display_t::on_destroy
       */
      ~display_t();
      display_t(const display_t& d);
      display_t(display_t&& d) noexcept;
      display_t &operator=(const display_t& d);
      display_t &operator=(display_t&& d) noexcept;
      bool operator==(const display_t& d) const;
      wl_display *c_ptr() const;
      wayland::detail::any &user_data();

      /** Retruns the event loop
       *
       * This function return the event loop associated with the display.
       * It can be used to manually dispatch events instead of using run().
       */
      event_loop_t get_event_loop() const;

      /** Add a socket to Wayland display for the clients to connect.
       *
       * \param name Name of the Unix socket.
       * \return 0 if success. -1 if failed.
       *
       * This adds a Unix socket to Wayland display which can be used by clients to
       * connect to Wayland display.
       *
       * If "" is passed as name, then it would look for WAYLAND_DISPLAY env
       * variable for the socket name. If WAYLAND_DISPLAY is not set, then default
       * wayland-0 is used.
       *
       * The Unix socket will be created in the directory pointed to by environment
       * variable XDG_RUNTIME_DIR. If XDG_RUNTIME_DIR is not set, then this function
       * fails and returns -1.
       *
       * The length of socket path, i.e., the path set in XDG_RUNTIME_DIR and the
       * socket name, must not exceed the maximum length of a Unix socket path.
       * The function also fails if the user do not have write permission in the
       * XDG_RUNTIME_DIR path or if the socket name is already in use.
       */
      int add_socket(const std::string& name) const;

      /** Add the default socket to Wayland display for the clients to connect.
       *
       * This uses add_socket() to add the default socket 'wayland-0' to the
       * Wayland display, incrementing the number if it already exists.
       */
      std::string add_socket_auto() const;

      /**  Add a socket with an existing fd to Wayland display for the clients to connect.
       *
       * \param sock_fd The existing socket file descriptor to be used
       * \return 0 if success. -1 if failed.
       *
       * The existing socket fd must already be created, opened, and locked.
       * The fd must be properly set to CLOEXEC and bound to a socket file
       * with both bind() and listen() already called.
       */
      int add_socket_fd(int sock_fd) const;

      /** Stops the event dispatching loop
       *
       * This funtion terminates the loop in the function run() and thus stops
       * further dispatching of events.
       */
      void terminate() const;

      /** Runs the internal event dispatching loop
       * This function is the internal event dispatching loop and can be used
       * in case the events shall not be manually dispatched using
       * get_event_loop(). This function returns when terminate() is called.
       */
      void run() const;

      /** Sends buffered requests to the clients.
       *
       * Requests that are sent to a client are buffered. This flushes the
       * buffers of all client connections and sends pendings requests to
       * the clients. This is an integral part of every event loop.
       */
      void flush_clients() const;

      /** Get the current serial number
       *
       * This function returns the most recent serial number, but does not
       * increment it.
       */
      uint32_t get_serial() const;

      /** Get the next serial number
       *
       * This function increments the display serial number and returns the
       * new value.
       */
      uint32_t next_serial() const;
      std::function<void()> &on_destroy();

      /** Registers a listener for the client connection signal.
       *  When a new client object is created, \a listener will
       *  be notified, carrying the new client_t object.
       */
      std::function<void(client_t&)> &on_client_created();

      /** Create client from a file descriptor
       *
       * Normally, clients connect to the socket created with add_socket()
       * or add_socket_auto(). Here, a new client can be created with an
       * existing connection using the associated file descriptor.
       */
      client_t client_create(int fd);

      /** Get the list of currently connected clients
       *
       * \param display The display object
       *
       * This function returns the list of clients currently
       * connected to the display.
       */
      std::list<client_t> get_client_list() const;

      /** Set a filter function for global objects
       *
       * \param filter  The global filter funtion.
       *
       * Set a filter for the display to advertise or hide global objects
       * to clients.
       * The set filter will be used during global advertisment to
       * determine whether a global object should be advertised to a
       * given client, and during global binding to determine whether
       * a given client should be allowed to bind to a global.
       *
       * Clients that try to bind to a global that was filtered out will
       * have an error raised.
       */
      void set_global_filter(const std::function<bool(client_t, global_base_t)>& filter);
    };

    class resource_t;

    class client_t
    {
    private:
      struct data_t
      {
        wl_client *client = nullptr;
        std::function<void()> destroy;
        detail::listener_t destroy_listener;
        wayland::detail::any user_data;
        std::atomic<unsigned int> counter{1};
        bool destroyed = false;
      };

      wl_client *client = nullptr;
      data_t *data = nullptr;

      static void destroy_func(wl_listener *listener, void *data);
      static wl_iterator_result resource_iterator(wl_resource *resource, void *data);
      static data_t *wl_client_get_user_data(wl_client *client);

    protected:
      client_t(wl_client *c);
      void init();
      void fini();

      friend class display_t;
      friend class resource_t;
      template <class resource> friend class global_t;

    public:
      /** Create a client for the given file descriptor
       *
       * \param display The display object
       * \param fd The file descriptor for the socket to the client
       * \return The new client object or NULL on failure.
       *
       * Given a file descriptor corresponding to one end of a socket, this
       * function will create a wl_client struct and add the new client to
       * the compositors client list.  At that point, the client is
       * initialized and ready to run, as if the client had connected to the
       * servers listening socket.  When the client eventually sends
       * requests to the compositor, the wl_client argument to the request
       * handler will be the wl_client returned from this function.
       *
       * The other end of the socket can be passed to
       * wl_display_connect_to_fd() on the client side or used with the
       * WAYLAND_SOCKET environment variable on the client side.
       *
       * Listeners added with wl_display_add_client_created_listener() will
       * be notified by this function after the client is fully constructed.
       *
       * On failure this function sets errno accordingly and returns NULL.
       */
      client_t(display_t &display, int fd);
      client_t() = delete;
      ~client_t();
      client_t(const client_t &c);
      client_t(client_t &&c) noexcept;
      client_t &operator=(const client_t& c);
      client_t &operator=(client_t&& c) noexcept;
      bool operator==(const client_t &c) const;
      wl_client *c_ptr() const;
      wayland::detail::any &user_data();

      /** Flush pending events to the client
       *
       * Events sent to clients are queued in a buffer and written to the
       * socket later - typically when the compositor has handled all
       * requests and goes back to block in the event loop.  This function
       * flushes all queued up events for a client immediately.
       */
      void flush() const;

      /** Return Unix credentials for the client
       *
       * \param pid Returns the process ID
       * \param uid Returns the user ID
       * \param gid Returns the group ID
       *
       * This function returns the process ID, the user ID and the group ID
       * for the given client.  The credentials come from getsockopt() with
       * SO_PEERCRED, on the client socket fd.
       *
       * Be aware that for clients that a compositor forks and execs and
       * then connects using socketpair(), this function will return the
       * credentials for the compositor.  The credentials for the socketpair
       * are set at creation time in the compositor.
       */
      void get_credentials(pid_t &pid, uid_t &uid, gid_t &gid) const;

      /** Get the file descriptor for the client
       *
       * \return The file descriptor to use for the connection
       *
       * This function returns the file descriptor for the given client.
       *
       * Be sure to use the file descriptor from the client for inspection only.
       * If the caller does anything to the file descriptor that changes its state,
       * it will likely cause problems.
       *
       * See also client_t::get_credentials().
       * It is recommended that you evaluate whether client_t::get_credentials()
       * can be applied to your use case instead of this function.
       *
       * If you would like to distinguish just between the client and the compositor
       * itself from the client's request, it can be done by getting the client
       * credentials and by checking the PID of the client and the compositor's PID.
       * Regarding the case in which the socketpair() is being used, you need to be
       * careful. Please note the documentation for client_t::get_credentials().
       *
       * This function can be used for a compositor to validate a request from
       * a client if there are additional information provided from the client's
       * file descriptor. For instance, suppose you can get the security contexts
       * from the client's file descriptor. The compositor can validate the client's
       * request with the contexts and make a decision whether it permits or deny it.
       */
      int get_fd() const;
      std::function<void()> &on_destroy();

      /** Look up an object in the client name space
       *
       * \param id The object id
       * \return The object or NULL if there is not object for the given ID
       *
       * This looks up an object in the client object name space by its
       * object ID.
       */
      resource_t get_object(uint32_t id);

      /** Post "not enough memory" error to the client
       *
       * If the compositor has not enough memory to fulfill a certail request
       * of the client, this function can be called to notify the client of
       * this circumstance.
       */
      void post_no_memory() const;

      /** Report an internal server error
       *
       * \param msg A message string
       *
       * Report an unspecified internal implementation error and disconnect
       * the client.
       */
      void post_implementation_error(std::string const& msg) const;

      /** Report an internal server error
       *
       * \param msg A printf-style format string
       * \param args Format string arguments
       *
       * Report an unspecified internal implementation error and disconnect
       * the client.
       */
      template <typename...types>
      void post_implementation_error(std::string const& msg, types&&... args)
      {
        wl_client_post_implementation_error(c_ptr(), msg.c_str(), std::forward<types...>(args...));
      }

      /** Get the display object for the given client
       *
       * \return The display object the client is associated with.
       */
      display_t get_display() const;

      /** Get a list of the clients resources.
       *
       * \return A list of resources used by the clienzt
       */
      std::list<resource_t> get_resource_list() const;
    };

    class resource_t
    {
    protected:
      // base class for event listener storage.
      struct events_base_t
      {
        events_base_t() = default;
        events_base_t(const events_base_t& e) = default;
        events_base_t(events_base_t&& e) = default;
        events_base_t& operator=(const events_base_t& e) = default;
        events_base_t& operator=(events_base_t&& e) = default;
        virtual ~events_base_t() = default;
      };

    private:
      struct data_t
      {
        std::shared_ptr<events_base_t> events;
        std::function<void()> destroy;
        detail::listener_t destroy_listener;
        wayland::detail::any user_data;
        std::atomic<unsigned int> counter{1};
        bool destroyed = false;
      };

      wl_resource *resource = nullptr;
      data_t *data = nullptr;

      static void destroy_func(wl_listener *listener, void *data);
      static int c_dispatcher(const void *implementation, void *target,
                              uint32_t opcode, const wl_message *message,
                              wl_argument *args);
      static int dummy_dispatcher(int opcode, const std::vector<wayland::detail::any>& args, const std::shared_ptr<resource_t::events_base_t>& events);

    protected:
      // Interface desctiption filled in by the each interface class
      static constexpr const wl_interface *interface = nullptr;

      /*
        Sets the dispatcher and its user data. User data must be an
        instance of a class derived from events_base_t, allocated with
        new. Will automatically be deleted upon destruction.
      */
      void set_events(const std::shared_ptr<events_base_t>& events,
                      int(*dispatcher)(int, const std::vector<wayland::detail::any>&, const std::shared_ptr<resource_t::events_base_t>&));

      // Retrieve the perviously set user data
      std::shared_ptr<events_base_t> get_events() const;

      void post_event_array(uint32_t opcode, const std::vector<wayland::detail::argument_t>& v) const;
      void queue_event_array(uint32_t opcode, const std::vector<wayland::detail::argument_t>& v) const;

      template <typename...T>
      void post_event(uint32_t opcode, T...args) const
      {
        std::vector<wayland::detail::argument_t> v = { wayland::detail::argument_t(args)... };
        if(c_ptr())
          post_event_array(opcode, v);
      }

      template <typename...T>
      void queue_event(uint32_t opcode, T...args) const
      {
        std::vector<wayland::detail::argument_t> v = { wayland::detail::argument_t(args)... };
        if(c_ptr())
          queue_event_array(opcode, v);
      }

      template <typename...T>
      void send_event(bool post, uint32_t opcode, T...args) const
      {
        if(post)
          post_event(opcode, args...);
        else
          queue_event(opcode, args...);
      }

      void post_error(uint32_t code, const std::string& msg) const;

      resource_t(wl_resource *c);
      void init();
      void fini();

      friend class client_t;

    public:
      resource_t() = default;

      /** Create a new resource object
       *
       * \param client The client owner of the new resource.
       * \param interface The interface of the new resource.
       * \param version The version of the new resource.
       * \param id The id of the new resource. If 0, an available id will be used.
       *
       * Listeners added with \a client_t::on_resource_created will be
       * notified at the end of this function.
       */
      resource_t(const client_t& client, const wl_interface *interface, int version, uint32_t id);
      ~resource_t();
      resource_t(const resource_t &r);
      resource_t(resource_t &&r) noexcept;
      resource_t &operator=(const resource_t& r);
      resource_t &operator=(resource_t&& r) noexcept;
      bool operator==(const resource_t& r) const;
      operator bool() const;
      wl_resource *c_ptr() const;
      wayland::detail::any &user_data();

      /** \brief Check whether this wrapper actually wraps an object
       *  \return true if there is an underlying object, false if this wrapper is
       *          empty
       */
      bool proxy_has_object() const;

      /** Post "not enough memory" error to the client
       *
       * If the compositor has not enough memory to fulfill a certail request
       * of the client, this function can be called to notify the client of
       * this circumstance.
       */
      void post_no_memory() const;

      /** Get the internal ID of the resource
       *
       * \return the internal ID of the resource
       */
      uint32_t get_id() const;

      /** Get the associated client
       *
       * \return the client that owns the resource.
       */
      client_t get_client() const;

      /** Get interface version
       *
       * \return Interface version this resource has been constructed with.
       */
      unsigned int get_version() const;

      /** Retrieve the interface name (class) of a resource object.
       *
       * \return Interface name of the resource object.
       */
      std::string get_class();
      std::function<void()> &on_destroy();
    };

    /** Global object base class */
    class global_base_t
    {
    private:
      void fini();
      bool has_interface(const wl_interface *interface) const;

      wl_global *global = nullptr;

    protected:
      struct data_t
      {
        wayland::detail::any user_data;
        std::atomic<unsigned int> counter{1};
      } *data = nullptr;

      global_base_t(display_t &display, const wl_interface* interface, int version, data_t *dat, wl_global_bind_func_t func);

    public:
      global_base_t(wl_global *g);
      global_base_t(const global_base_t& g);
      global_base_t(global_base_t&& g) noexcept;
      ~global_base_t();
      global_base_t &operator=(const global_base_t& g);
      global_base_t &operator=(global_base_t&& g) noexcept;
      bool operator==(const global_base_t& g) const;
      wl_global *c_ptr() const;
      wayland::detail::any &user_data();

      /** Check for specific interface.
       *
       * \tparam resource Resource class for comparison
       * \return true if the global has the same interface as the resource class
       */
      template <typename resource>
      bool has_interface() // instead if wl_global_get_interface
      {
        return has_interface(resource::interface);
      }
    };

    /** Global object.
     *
     * \tparam resource Resource class whose interface shall be used
     */
    template <class resource>
    class global_t : public global_base_t
    {
    private:
      struct data_t : public global_base_t::data_t
      {
        std::function<void(client_t, resource)> bind;
      };

      static void bind_func(wl_client *cl, void *d, uint32_t ver, uint32_t id)
      {
        auto *data = reinterpret_cast<data_t*>(d);
        client_t client(cl);
        resource res(client, ver, id);
        if(data->bind)
          data->bind(client, res);
      }

    public:
      global_t() = delete;

      /** Create a global object
       *
       * \param display Parent display object
       * \param version Interface version
       */
      global_t(display_t &display, unsigned int version = resource::max_version)
        : global_base_t(display, resource::interface, version, new data_t, bind_func)
      {
      }

      /** Adds a listener for the bind signal.
       *
       *  When a client binds to a global object, registered listeners
       *  will be notified, carrying the client_t object and the new
       *  resource_t object.
       */
      std::function<void(client_t, resource)> &on_bind()
      {
        return static_cast<data_t*>(data)->bind;
      }
    };

    struct fd_event_mask_t : public wayland::detail::bitfield<2, -1>
    {
      fd_event_mask_t(const wayland::detail::bitfield<2, -1> &b)
        : wayland::detail::bitfield<2, -1>(b) {}
      fd_event_mask_t(const uint32_t value)
        : wayland::detail::bitfield<2, -1>(value) {}
      static const wayland::detail::bitfield<2, -1> readable;
      static const wayland::detail::bitfield<2, -1> writable;
      static const wayland::detail::bitfield<2, -1> hangup;
      static const wayland::detail::bitfield<2, -1> error;
    };

    class event_loop_t
    {
    private:
      struct data_t
      {
        std::function<void()> destroy;
        detail::listener_t destroy_listener;
        std::list<std::function<int(int, uint32_t)>> fd_funcs;
        std::list<std::function<int()>> timer_funcs;
        std::list<std::function<int(int)>> signal_funcs;
        std::list<std::function<void()>> idle_funcs;
        wayland::detail::any user_data;
        bool do_delete = true;
        std::atomic<unsigned int> counter{1};
      };

      wl_event_loop *event_loop = nullptr;
      data_t *data = nullptr;

      static data_t *wl_event_loop_get_user_data(wl_event_loop *client);
      static void destroy_func(wl_listener *listener, void *data);
      static int event_loop_fd_func(int fd, uint32_t mask, void *data);
      static int event_loop_timer_func(void *data);
      static int event_loop_signal_func(int signal_number, void *data);
      static void event_loop_idle_func(void *data);

    protected:
      event_loop_t(wl_event_loop *p);
      void init();
      void fini();

      friend class display_t;

    public:
      event_loop_t();
      ~event_loop_t();
      event_loop_t(const event_loop_t& e);
      event_loop_t(event_loop_t&& e) noexcept;
      event_loop_t &operator=(const event_loop_t& e);
      event_loop_t &operator=(event_loop_t&& e) noexcept;
      bool operator==(const event_loop_t& e) const;
      wl_event_loop *c_ptr() const;
      wayland::detail::any &user_data();

      /** Create a file descriptor event source
       *
       * \param fd The file descriptor to watch.
       * \param mask A bitwise-or of which events to watch for.
       * \param func The file descriptor dispatch function.
       * \return A new file descriptor event source.
       *
       * The given file descriptor is initially watched for the events given in
       * \c mask. This can be changed as needed with event_source_t::fd_update().
       *
       * If it is possible that program execution causes the file descriptor to be
       * read while leaving the data in a buffer without actually processing it,
       * it may be necessary to register the file descriptor source to be re-checked,
       * see event_source_t::check(). This will ensure that the dispatch function
       * gets called even if the file descriptor is not readable or writable
       * anymore. This is especially useful with IPC libraries that automatically
       * buffer incoming data, possibly as a side-effect of other operations.
       */
      event_source_t add_fd(int fd, const fd_event_mask_t& mask, const std::function<int(int, uint32_t)> &func);

      /** Create a timer event source
       *
       * \param func The timer dispatch function.
       * \return A new timer event source.
       *
       * The timer is initially disarmed. It needs to be armed with a call to
       * event_source_t::timer_update() before it can trigger a dispatch call.
       */
      event_source_t add_timer(const std::function<int()> &func);

      /** Create a POSIX signal event source
       *
       * \param signal_number Number of the signal to watch for.
       * \param func The signal dispatch function.
       * \return A new signal event source.
       *
       * This function blocks the normal delivery of the given signal in the calling
       * thread, and creates a "watch" for it. Signal delivery no longer happens
       * asynchronously, but by wl_event_loop_dispatch() calling the dispatch
       * callback function \c func.
       *
       * It is the caller's responsibility to ensure that all other threads have
       * also blocked the signal.
       */
      event_source_t add_signal(int signal_number, const std::function<int(int)> &func);

      /** Create an idle task
       *
       * \param func The idle task dispatch function.
       * \return A new idle task (an event source).
       *
       * Idle tasks are dispatched before wl_event_loop_dispatch() goes to sleep.
       * See wl_event_loop_dispatch() for more details.
       *
       * Idle tasks fire once, and are automatically destroyed right after the
       * callback function has been called.
       *
       * An idle task can be cancelled before the callback has been called by
       * event_source_t::remove(). Calling event_source_t::remove() after or from
       * within the callback results in undefined behaviour.
       */
      event_source_t add_idle(const std::function<void()> &func);
      const std::function<void()> &on_destroy();

      /** Wait for events and dispatch them
       *
       * \param timeout The polling timeout in milliseconds.
       * \return 0 for success, -1 for polling error.
       *
       * All the associated event sources are polled. This function blocks until
       * any event source delivers an event (idle sources excluded), or the timeout
       * expires. A timeout of -1 disables the timeout, causing the function to block
       * indefinitely. A timeout of zero causes the poll to always return immediately.
       *
       * All idle sources are dispatched before blocking. An idle source is destroyed
       * when it is dispatched. After blocking, all other ready sources are
       * dispatched. Then, idle sources are dispatched again, in case the dispatched
       * events created idle sources. Finally, all sources marked with
       * event_source_t::check() are dispatched in a loop until their dispatch
       * functions all return zero.
       */
      int dispatch(int timeout) const;

      /** Dispatch the idle sources
       */
      void dispatch_idle() const;

      /** Get the event loop file descriptor
       *
       * \return The aggregate file descriptor.
       *
       * This function returns the aggregate file descriptor, that represents all
       * the event sources (idle sources excluded) associated with the given event
       * loop context. When any event source makes an event available, it will be
       * reflected in the aggregate file descriptor.
       *
       * When the aggregate file descriptor delivers an event, one can call
       * event_loop_t::dispatch() on the event loop context to dispatch all the
       * available events.
       */
      int get_fd() const;
    };

    class event_source_t : public wayland::detail::refcounted_wrapper<wl_event_source>
    {
    private:
      wl_event_source *event_source = nullptr;

    protected:
      event_source_t(wl_event_source *p);
      friend class event_loop_t;

    public:
      event_source_t() = delete;
      wl_event_source *c_ptr() const;

      /** Arm or disarm a timer
       *
       * \param ms_delay The timeout in milliseconds.
       * \return 0 on success, -1 on failure.
       *
       * If the timeout is zero, the timer is disarmed.
       *
       * If the timeout is non-zero, the timer is set to expire after the given
       * timeout in milliseconds. When the timer expires, the dispatch function
       * set with event_loop_t::add_timer() is called once from
       * event_loop_t::dispatch(). If another dispatch is desired after another
       * expiry, event_source_t::timer_update() needs to be called again.
       */
      int timer_update(int ms_delay) const;

      /** Update a file descriptor source's event mask
       *
       * \param mask The new mask.
       * \return 0 on success, -1 on failure.
       *
       * This changes which events, readable and/or writable, cause the dispatch
       * callback to be called on.
       *
       * File descriptors are usually writable to begin with, so they do not need to
       * be polled for writable until a write actually fails. When a write fails,
       * the event mask can be changed to poll for readable and writable, delivering
       * a dispatch callback when it is possible to write more. Once all data has
       * been written, the mask can be changed to poll only for readable to avoid
       * busy-looping on dispatch.
       */
      int fd_update(const fd_event_mask_t& mask) const;

      /** Mark event source to be re-checked
       *
       * This function permanently marks the event source to be re-checked after
       * the normal dispatch of sources in event_loop_t::dispatch(). Re-checking
       * will keep iterating over all such event sources until the dispatch
       * function for them all returns zero.
       *
       * Re-checking is used on sources that may become ready to dispatch as a
       * side-effect of dispatching themselves or other event sources, including idle
       * sources. Re-checking ensures all the incoming events have been fully drained
       * before event_loop_t::dispatch() returns.
       */
      void check() const;
    };
  }
}

#endif