Zakero's C++ Header Libraries
A collection of reusable C++ libraries
Zakero_Xenium.h
Go to the documentation of this file.
1 /******************************************************************************
2  * Copyright 2020 Andrew Moore
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7  */
8 
9 #ifndef zakero_Xenium_h
10 #define zakero_Xenium_h
11 
172 /******************************************************************************
173  * Includes
174  */
175 
176 // C++
177 #include <array>
178 #include <future>
179 #include <iostream>
180 #include <thread>
181 #include <unordered_map>
182 
183 // Linux
184 #include <linux/input-event-codes.h>
185 
186 // X11/XCB
187 #include <xcb/xcb.h>
188 #include <xcb/xcb_icccm.h>
189 #include <xcb/randr.h>
190 
194 #define explicit explicit_
195 #include <xcb/xkb.h>
196 #undef explicit
197 
198 // Zakero
199 #include "Zakero_Base.h"
200 
201 
202 /******************************************************************************
203  * Macros
204  */
205 
206 // {{{ Macros
207 
224 #define ZAKERO_XENIUM__ERROR_DATA \
225  X(Error_None , 0 , "No Error" ) \
226  X(Error_Unknown , 1 , "An unknown error has occurred" ) \
227  X(Error_Connection_Failed , 2 , "Failed due to socket, pipe, or other stream errors" ) \
228  X(Error_Extension_Not_Supported , 3 , "The requested XCB extension is not supported" ) \
229  X(Error_Invalid_Display_Name , 4 , "An error occured while parsing the X11 display name" ) \
230  X(Error_Invalid_Screen , 5 , "The X11 server does not have a screen matching the display" ) \
231  X(Error_Minimum_Size_Greater_Than_Maximum_Size , 6 , "The minimum window size is larger than the maximum window size." ) \
232  X(Error_Not_Enough_Memory , 7 , "Insufficient memory" ) \
233  X(Error_Request_Too_Long , 8 , "The request was longer than what is excepted by the X11 server" ) \
234  X(Error_Window_Size_Too_Small , 9 , "The window size was too small." ) \
235  X(Error_RandR_CRTC_Info_Not_Found , 10 , "XCB RandR CRTC Information was not found" ) \
236  X(Error_RandR_Invalid_CRTC_Id , 11 , "XCB RandR CRTC ID is not valid" ) \
237  X(Error_RandR_Invalid_Output_Id , 12 , "XCB RandR Output ID is not valid" ) \
238  X(Error_RandR_Not_Available , 13 , "XCB RandR extenstion is not available" ) \
239  X(Error_RandR_Output_Info_Is_Incomplete , 14 , "XCB RandR Output Information does not have enough data" ) \
240  X(Error_RandR_Output_Info_Not_Found , 15 , "XCB RandR Output Information was not found" ) \
241  X(Error_RandR_Screen_Resources_Not_Found , 16 , "XCB RandR could not locate any screen resources" ) \
242  X(Error_RandR_Version_Too_Old , 17 , "XCB RandR version is too old" ) \
243  X(Error_Xcb_Fullscreen_Not_Available , 18 , "The XCB Window Manager does not support fullscreen windows." ) \
244  X(Error_Xcb_Hidden_Not_Available , 19 , "The XCB Window Manager does not support hiding windows." ) \
245  X(Error_Xcb_Maximized_Window_Not_Available , 20 , "The XCB Window Manager does not support maximized windows." ) \
246  X(Error_Xcb_NETWM_State_Not_Available , 21 , "The XCB NETWM protocol extention is not supported." ) \
247  X(Error_Xcb_WM_Delete_Window_Not_Available , 22 , "The XCB Window Manager does not support the delete protocol." ) \
248  X(Error_Xcb_WM_Protocols_Not_Available , 23 , "The XCB Window Manager protocols are not available." ) \
249  X(Error_Xcb_Xkb_Not_Available , 24 , "The XCB XKB Extiension v1.0 is not available." ) \
250 
251 // }}}
252 
253 namespace zakero
254 {
255  // {{{ Declaration
256 
257  class Xenium
258  {
259  public:
260 #define X(name_, val_, mesg_) \
261  static constexpr int name_ = val_;
262  ZAKERO_XENIUM__ERROR_DATA
263 #undef X
264 
265  static constexpr int32_t Window_Size_Minimum = 100;
266 
267  virtual ~Xenium() noexcept;
268 
269  // {{{ Type : Key
270 
271  enum struct KeyState
272  { Released = 0
273  , Pressed = 1
274  , Repeat = 2
275  };
276 
277  struct Key
278  {
279  uint32_t time;
280  uint32_t code;
282  };
283 
284  static constexpr uint32_t KeyModifier_Shift = 0x00000001;
285  static constexpr uint32_t KeyModifier_CapsLock = 0x00000002;
286  static constexpr uint32_t KeyModifier_Control = 0x00000004;
287  static constexpr uint32_t KeyModifier_Alt = 0x00000008;
288  static constexpr uint32_t KeyModifier_NumLock = 0x00000010;
289  static constexpr uint32_t KeyModifier_Meta = 0x00000040;
290 
291  struct KeyModifier
292  {
293  uint32_t pressed = 0;
294  uint32_t latched = 0;
295  uint32_t locked = 0;
296  uint32_t group = 0;
297  };
298 
299  // }}}
300  // {{{ Type : Point
301 
302  struct PointMm
303  {
304  uint32_t time;
305  float x;
306  float y;
307 
308  friend bool operator==(Xenium::PointMm&, Xenium::PointMm&) noexcept;
309  };
310 
312  {
313  uint32_t time;
314  float x;
315  float y;
316 
317  friend bool operator==(Xenium::PointPercent&, Xenium::PointPercent&) noexcept;
318  };
319 
320  struct PointPixel
321  {
322  uint32_t time;
323  int32_t x;
324  int32_t y;
325 
326  friend bool operator==(Xenium::PointPixel&, Xenium::PointPixel&) noexcept;
327  };
328 
329  // }}}
330  // {{{ Type : Pointer Axis
331 
332  enum struct PointerAxisSource
333  { Unknown
334  , Continuous
335  , Finger
336  , Wheel
337  , Wheel_Tilt
338  };
339 
340  enum struct PointerAxisType
341  { Unknown
342  , Horizontal
343  , Vertical
344  };
345 
346  struct PointerAxis
347  {
348  uint32_t time;
349  int32_t steps;
350  float distance;
353  };
354 
355  // }}}
356  // {{{ Type : Pointer Button
357 
358  enum struct PointerButtonState
359  { Released = 0
360  , Pressed = 1
361  };
362 
364  {
365  uint32_t code;
367  };
368 
369  // }}}
370  // {{{ Type : Size
371 
372  struct SizeMm
373  {
374  float width;
375  float height;
376 
377  friend bool operator==(Xenium::SizeMm&, Xenium::SizeMm&) noexcept;
378  };
379 
380  struct SizePercent
381  {
382  float width;
383  float height;
384 
385  friend bool operator==(Xenium::SizePercent&, Xenium::SizePercent&) noexcept;
386  };
387 
388  struct SizePixel
389  {
390  int32_t width;
391  int32_t height;
392 
393  friend bool operator==(Xenium::SizePixel&, Xenium::SizePixel&) noexcept;
394  };
395 
396  // }}}
397  // {{{ Connection
398 
399  [[nodiscard]] static Xenium* connect() noexcept;
400  [[nodiscard]] static Xenium* connect(const std::string&) noexcept;
401  [[nodiscard]] static Xenium* connect(std::error_code&) noexcept;
402  [[nodiscard]] static Xenium* connect(const std::string&, std::error_code&) noexcept;
403 
404  // }}}
405  // {{{ Cursor : TODO
406 
407  // }}}
408  // {{{ Keyboard
409 
410  [[nodiscard]] int32_t keyRepeatDelay() const noexcept;
411  [[nodiscard]] int32_t keyRepeatRate() const noexcept;
412 
413  // }}}
414  // {{{ Output : X11/XCB
415 
416  struct Output
417  {
418  std::string name = "";
419  int32_t x = 0;
420  int32_t y = 0;
421  int32_t width = 0;
422  int32_t height = 0;
423  uint32_t physical_width_mm = 0;
424  uint32_t physical_height_mm = 0;
425  int32_t subpixel = 0;
426  int32_t transform = 0;
427  float pixels_per_mm_horizontal = 0.0;
428  float pixels_per_mm_vertical = 0.0;
429  //std::string make = ""; // Not Available?
430  //std::string model = ""; // Not Available?
431  //int32_t refresh_mHz = 0; // Not Available?
432  //int32_t scale_factor = 0; // Not Available?
433  };
434 
435  // -------------------------------------------------- //
436 
437  using OutputId = uint32_t;
438 
439  using LambdaOutputId = std::function<void(const Xenium::OutputId)>;
440 
441  using VectorOutputId = std::vector<Xenium::OutputId>;
442 
443  // -------------------------------------------------- //
444 
445  [[nodiscard]] Xenium::Output output(const Xenium::OutputId) const noexcept;
446  [[nodiscard]] Xenium::VectorOutputId outputVector() const noexcept;
447  [[nodiscard]] static std::string outputSubpixelName(int32_t) noexcept;
448  [[nodiscard]] static std::string outputTransformName(int32_t) noexcept;
449 
450  [[nodiscard]] Xenium::PointMm outputConvertToMm(const Xenium::OutputId, const Xenium::PointPixel&) const noexcept;
451  [[nodiscard]] Xenium::PointPercent outputConvertToPercent(const Xenium::OutputId, const Xenium::PointPixel&) const noexcept;
452  [[nodiscard]] Xenium::PointPixel outputConvertToPixel(const Xenium::OutputId, const Xenium::PointMm&) const noexcept;
453  [[nodiscard]] Xenium::PointPixel outputConvertToPixel(const Xenium::OutputId, const Xenium::PointPercent&) const noexcept;
454 
455  [[nodiscard]] Xenium::SizeMm outputConvertToMm(const Xenium::OutputId, const Xenium::SizePixel&) const noexcept;
456  [[nodiscard]] Xenium::SizePercent outputConvertToPercent(const Xenium::OutputId, const Xenium::SizePixel&) const noexcept;
457  [[nodiscard]] Xenium::SizePixel outputConvertToPixel(const Xenium::OutputId, const Xenium::SizeMm&) const noexcept;
458  [[nodiscard]] Xenium::SizePixel outputConvertToPixel(const Xenium::OutputId, const Xenium::SizePercent&) const noexcept;
459 
460  void outputOnAdd(Xenium::LambdaOutputId) noexcept;
461  void outputOnChange(Xenium::LambdaOutputId) noexcept;
462  void outputOnRemove(Xenium::LambdaOutputId) noexcept;
463 
464  // }}}
465  // {{{ Window
466 
467  enum struct WindowDecorations
468  { Client_Side
469  , Server_Side
470  };
471 
472  enum struct WindowMode
473  { Normal
474  , Fullscreen
475  , Maximized
476  };
477 
478  // -------------------------------------------------- //
479 
480  using Lambda = std::function<void()>;
481  using LambdaAxis = std::function<void(const Xenium::PointerAxis&, const Xenium::KeyModifier&)>;
482  using LambdaBool = std::function<void(bool)>;
483  using LambdaButtonMm = std::function<void(const Xenium::PointerButton&, const Xenium::PointMm&, const Xenium::KeyModifier&)>;
484  using LambdaButtonPercent = std::function<void(const Xenium::PointerButton&, const Xenium::PointPercent&, const Xenium::KeyModifier&)>;
485  using LambdaButtonPixel = std::function<void(const Xenium::PointerButton&, const Xenium::PointPixel&, const Xenium::KeyModifier&)>;
486  using LambdaKey = std::function<void(const Xenium::Key&, const Xenium::KeyModifier&)>;
487  using LambdaPointMm = std::function<void(const Xenium::PointMm&, const Xenium::KeyModifier&)>;
488  using LambdaPointPercent = std::function<void(const Xenium::PointPercent&, const Xenium::KeyModifier&)>;
489  using LambdaPointPixel = std::function<void(const Xenium::PointPixel&, const Xenium::KeyModifier&)>;
490  using LambdaSizeMm = std::function<void(const Xenium::SizeMm&)>;
491  using LambdaSizePercent = std::function<void(const Xenium::SizePercent&)>;
492  using LambdaSizePixel = std::function<void(const Xenium::SizePixel&)>;
494  using LambdaWindowMode = std::function<void(Xenium::WindowMode)>;
495 
496  using WindowId = uint32_t;
497 
498  // -------------------------------------------------- //
499 
500  class Window
501  {
502  public:
503  Window(Xenium*, void*);
504  virtual ~Window();
505 
506  // {{{ Configuration
507 
508  void classSet(const std::string&) noexcept;
509  void titleSet(const std::string&) noexcept;
510 
511  // }}}
512  // {{{ Events
513 
514  void onCloseRequest(Xenium::Lambda) noexcept;
515  void onFocusChange(Xenium::LambdaBool) noexcept;
516 
517  // }}}
518  // {{{ Decorations
519 
520  std::error_code decorationsSet(const Xenium::WindowDecorations) noexcept;
522 
523  // }}}
524  // {{{ Size
525 
526  std::error_code sizeSet(const Xenium::SizeMm&) noexcept;
527  std::error_code sizeSet(const Xenium::SizePercent&) noexcept;
528  std::error_code sizeSet(const Xenium::SizePixel&) noexcept;
529  std::error_code sizeSetMinMax(const Xenium::SizeMm&, const Xenium::SizeMm&) noexcept;
530  std::error_code sizeSetMinMax(const Xenium::SizePercent&, const Xenium::SizePercent&) noexcept;
531  std::error_code sizeSetMinMax(const Xenium::SizePixel&, const Xenium::SizePixel&) noexcept;
532  void sizeOnChange(Xenium::LambdaSizeMm) noexcept;
534  void sizeOnChange(Xenium::LambdaSizePixel) noexcept;
535 
536  // }}}
537  // {{{ Conversion
538 
539  [[nodiscard]] Xenium::PointMm convertToMm(const Xenium::PointPixel&) const noexcept;
540  [[nodiscard]] Xenium::PointPercent convertToPercent(const Xenium::PointPixel&) const noexcept;
541  [[nodiscard]] Xenium::PointPixel convertToPixel(const Xenium::PointMm&) const noexcept;
542  [[nodiscard]] Xenium::PointPixel convertToPixel(const Xenium::PointPercent&) const noexcept;
543 
544  [[nodiscard]] Xenium::SizeMm convertToMm(const Xenium::SizePixel&) const noexcept;
545  [[nodiscard]] Xenium::SizePercent convertToPercent(const Xenium::SizePixel&) const noexcept;
546  [[nodiscard]] Xenium::SizePixel convertToPixel(const Xenium::SizeMm&) const noexcept;
547  [[nodiscard]] Xenium::SizePixel convertToPixel(const Xenium::SizePercent&) const noexcept;
548 
549  // }}}
550  // {{{ Window Mode
551 
552  [[nodiscard]] Xenium::WindowMode windowMode() const noexcept;
553  [[nodiscard]] bool windowModeIs(const Xenium::WindowMode) const noexcept;
554  std::error_code windowModeSet(const Xenium::WindowMode) noexcept;
556 
557  [[nodiscard]] std::error_code minimize() noexcept;
558 
559  // }}}
560  // {{{ Cursor : TODO
561 
562  // }}}
563  // {{{ Keyboard
564 
565  void keyboardOnEnter(Xenium::Lambda) noexcept;
566  void keyboardOnLeave(Xenium::Lambda) noexcept;
567  void keyboardOnKey(Xenium::LambdaKey) noexcept;
568 
569  // }}}
570  // {{{ Pointer
571 
572  void pointerOnAxis(Xenium::LambdaAxis) noexcept;
576  void pointerOnEnter(Xenium::LambdaPointMm) noexcept;
579  void pointerOnLeave(Xenium::Lambda) noexcept;
580  void pointerOnMotion(Xenium::LambdaPointMm) noexcept;
583 
584  // Future?
585  //void pointerOnAxisSource(Xenium::Lambda) noexcept;
586  //void pointerOnAxisStop(Xenium::Lambda) noexcept;
587  //void pointerOnAxisDiscrete(Xenium::Lambda) noexcept;
588 
589  // }}}
590  // {{{ Rendering
591 
592  std::error_code imageNext(uint8_t*&, Xenium::SizePixel&) noexcept;
593  void imagePresent() noexcept;
594  [[nodiscard]] uint32_t time() const noexcept;
595  [[nodiscard]] uint8_t bytesPerPixel() const noexcept;
596 
597  // }}}
598 
599  private:
600  Xenium* xenium;
601  uint8_t* frame_buffer;
602  Xenium::SizePixel frame_buffer_size;
603  Xenium::WindowId window_id;
604  xcb_gcontext_t gc;
605  size_t frame_buffer_length;
606  uint32_t frame_time;
607 
608  Window(const Xenium::Window&) = delete;
609  Window& operator=(const Xenium::Window&) = delete;
610  };
611 
612  // -------------------------------------------------- //
613 
614  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizeMm&, std::error_code&) noexcept;
615  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizeMm&, const uint32_t, xcb_create_window_value_list_t&, std::error_code&) noexcept;
616  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizePercent&, std::error_code&) noexcept;
617  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizePercent&, const uint32_t, xcb_create_window_value_list_t&, std::error_code&) noexcept;
618  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizePixel&, std::error_code&) noexcept;
619  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizePixel&, const uint32_t, xcb_create_window_value_list_t&, std::error_code&) noexcept;
620 
621  // }}}
622 
623  private:
624  Xenium() noexcept;
625 
626  // {{{ Connection
627 
628  void disconnect() noexcept;
629 
630  // }}}
631  // {{{ Initialization
632 
633  [[nodiscard]] std::error_code init(xcb_connection_t*, int) noexcept;
634 
635  // }}}
636  // {{{ Cursor : TODO
637 
638  // }}}
639  // {{{ Event Loop
640 
641  std::jthread event_loop;
642  std::atomic<bool> event_loop_is_running;
643 
644  // -------------------------------------------------- //
645 
646  void eventLoopStart() noexcept;
647  static void eventLoop(std::stop_token, Xenium*) noexcept;
648 
649  // }}}
650  // {{{ Output
651 
652  using Map_OutputId_Output = std::unordered_map<Xenium::OutputId, Xenium::Output>;
653 
654  // -------------------------------------------------- //
655 
656  Xenium::LambdaOutputId output_on_add = {};
657  Xenium::LambdaOutputId output_on_change = {};
658  Xenium::LambdaOutputId output_on_remove = {};
659  Xenium::Map_OutputId_Output output_map = {};
660  mutable std::mutex output_mutex = {};
661 
662  // -------------------------------------------------- //
663 
664  [[nodiscard]] const Xenium::Output& output(const int16_t, const int16_t, Xenium::OutputId&) noexcept;
665  [[nodiscard]] std::error_code outputInit() noexcept;
666  [[nodiscard]] std::error_code outputAdd(xcb_randr_crtc_t, xcb_randr_output_t) noexcept;
667  void outputAdd(const xcb_randr_get_crtc_info_reply_t*, const xcb_randr_get_output_info_reply_t*) noexcept;
668 
669  // }}}
670  // {{{ Utility
671 
672  std::mutex xenium_window_mutex = {};
673 
674  // -------------------------------------------------- //
675 
676  [[nodiscard]] std::pair<float, float> convertPixelToMm(const Xenium::Output&, int32_t, int32_t) const noexcept;
677  [[nodiscard]] std::pair<float, float> convertPixelToPercent(const Xenium::Output&, int32_t, int32_t) const noexcept;
678  [[nodiscard]] std::pair<int32_t, int32_t> convertMmToPixel(const Xenium::Output&, float, float) const noexcept;
679  [[nodiscard]] std::pair<int32_t, int32_t> convertPercentToPixel(const Xenium::Output&, float, float) const noexcept;
680 
681  // }}}
682  // {{{ Window
683 
684  enum struct SizeUnit
685  { Millimeter
686  , Percent
687  , Pixel
688  };
689 
690  struct MotifWmHints
691  {
692  uint32_t flags;
693  uint32_t functions;
694  uint32_t decorations;
695  int32_t input_mode;
696  uint32_t status;
697  };
698 
699  struct WindowCreateData
700  {
701  std::promise<void> barrier;
702  std::error_code error;
703  Xenium::WindowId window_id;
704  Xenium::OutputId output_id;
705  xcb_atom_t atom_close_request;
706  xcb_gcontext_t gc;
707  Xenium::SizeUnit size_unit;
708  Xenium::SizeMm size_mm;
709  Xenium::SizePercent size_percent;
710  Xenium::SizePixel size_pixel;
711  uint32_t value_mask;
712  xcb_create_window_value_list_t& value_list;
713  };
714 
715  struct WindowDestroyData
716  {
717  std::promise<void> barrier;
718  Xenium::WindowId window_id;
719  xcb_gcontext_t gc;
720  };
721 
722  struct WindowDeleteData
723  {
724  Xenium::Lambda close_request_lambda = { };
725  xcb_atom_t atom_close_request = XCB_ATOM_NONE;
726  };
727 
728  struct WindowSizeData
729  {
730  Xenium::SizeMm mm = { };
731  Xenium::SizeMm mm_minimum = { };
732  Xenium::SizeMm mm_maximum = { };
733  Xenium::LambdaSizeMm mm_lambda = { };
734  Xenium::SizePercent percent = { };
735  Xenium::SizePercent percent_minimum = { };
736  Xenium::SizePercent percent_maximum = { };
737  Xenium::LambdaSizePercent percent_lambda = { };
738  Xenium::SizePixel pixel = { };
739  Xenium::SizePixel pixel_minimum = { };
740  Xenium::SizePixel pixel_maximum = { };
741  Xenium::LambdaSizePixel pixel_lambda = { };
742  Xenium::SizeUnit unit = { };
743  };
744 
745  struct WindowModeData
746  {
747  Xenium::WindowMode window_mode = Xenium::WindowMode::Normal;
748  Xenium::LambdaWindowMode lambda = { };
749  };
750 
751  struct WindowDecorationsData
752  {
753  Xenium::WindowDecorations window_decorations = Xenium::WindowDecorations::Server_Side;
754  Xenium::LambdaWindowDecorations lambda = { };
755  };
756 
757  struct WindowOnButtonData
758  {
759  Xenium::LambdaButtonMm lambda_mm = { };
760  Xenium::LambdaButtonPercent lambda_percent = { };
761  Xenium::LambdaButtonPixel lambda_pixel = { };
762  };
763 
764  struct WindowOnEnterData
765  {
766  Xenium::LambdaPointMm lambda_mm = { };
767  Xenium::LambdaPointPercent lambda_percent = { };
768  Xenium::LambdaPointPixel lambda_pixel = { };
769  };
770 
771  struct WindowOnMotionData
772  {
773  Xenium::LambdaPointMm lambda_mm = { };
774  Xenium::LambdaPointPercent lambda_percent = { };
775  Xenium::LambdaPointPixel lambda_pixel = { };
776  };
777 
778  struct WindowKeyboardData
779  {
780  Xenium::Lambda on_enter = { };
781  Xenium::Lambda on_leave = { };
782  };
783 
784  using WindowDecorationsMap = std::unordered_map<Xenium::WindowId, Xenium::WindowDecorationsData>;
785  using WindowDeleteMap = std::unordered_map<Xenium::WindowId, Xenium::WindowDeleteData>;
786  using WindowFocusMap = std::unordered_map<Xenium::WindowId, Xenium::LambdaBool>;
787  using WindowKeyboard = std::unordered_map<Xenium::WindowId, Xenium::WindowKeyboardData>;
788  using WindowMap = std::unordered_map<Xenium::WindowId, Xenium::Window*>;
789  using WindowModeMap = std::unordered_map<Xenium::WindowId, Xenium::WindowModeData>;
790  using WindowOnAxisMap = std::unordered_map<Xenium::WindowId, Xenium::LambdaAxis>;
791  using WindowOnButtonMap = std::unordered_map<Xenium::WindowId, Xenium::WindowOnButtonData>;
792  using WindowOnEnterMap = std::unordered_map<Xenium::WindowId, Xenium::WindowOnEnterData>;
793  using WindowOnKeyMap = std::unordered_map<Xenium::WindowId, Xenium::LambdaKey>;
794  using WindowOnLeaveMap = std::unordered_map<Xenium::WindowId, Xenium::Lambda>;
795  using WindowOnMotionMap = std::unordered_map<Xenium::WindowId, Xenium::WindowOnMotionData>;
796  using WindowOutputMap = std::unordered_map<Xenium::WindowId, Xenium::OutputId>;
797  using WindowReadyMap = std::unordered_map<Xenium::WindowId, bool>;
798  using WindowSizeMap = std::unordered_map<Xenium::WindowId, Xenium::WindowSizeData>;
799  using WindowToCreate = std::vector<Xenium::WindowCreateData*>;
800  using WindowToDestroy = std::vector<Xenium::WindowDestroyData*>;
801 
802  // -------------------------------------------------- //
803 
804  Xenium::WindowDecorationsMap window_decorations_map = {};
805  Xenium::WindowDeleteMap window_delete_map = {};
806  Xenium::WindowFocusMap window_focus_map = {};
807  Xenium::WindowKeyboard window_keyboard = {};
808  Xenium::WindowMap window_map = {};
809  Xenium::WindowModeMap window_mode_map = {};
810  Xenium::WindowOnAxisMap window_on_axis_map = {};
811  Xenium::WindowOnButtonMap window_on_button_map = {};
812  Xenium::WindowOnEnterMap window_on_enter_map = {};
813  Xenium::WindowOnKeyMap window_on_key_map = {};
814  Xenium::WindowOnLeaveMap window_on_leave_map = {};
815  Xenium::WindowOnMotionMap window_on_motion_map = {};
816  Xenium::WindowOutputMap window_output_map = {};
817  Xenium::WindowReadyMap window_ready_map = {};
818  Xenium::WindowSizeMap window_size_map = {};
819  Xenium::WindowToCreate window_to_create = {};
820  Xenium::WindowToDestroy window_to_destroy = {};
821 
822  // -------------------------------------------------- //
823 
824  [[nodiscard]] std::error_code windowBorder(const Xenium::WindowId, const bool) noexcept;
825  void windowCreateAddToQueue(Xenium::WindowCreateData*) noexcept;
826  void windowDestroyAddToQueue(Xenium::WindowDestroyData*) noexcept;
827  [[nodiscard]] std::error_code windowLocationSet(const Xenium::WindowId, const Xenium::PointPixel&) noexcept;
828  [[nodiscard]] std::error_code windowMinimize(const Xenium::WindowId) noexcept;
829  [[nodiscard]] std::error_code windowModeSet(const Xenium::WindowId, const Xenium::WindowMode, const Xenium::WindowMode) noexcept;
830  bool windowPropertySet(Xenium::WindowId, const xcb_atom_t, const xcb_atom_t, xcb_generic_error_t&) noexcept;
831  bool windowPropertySet(Xenium::WindowId, const xcb_atom_t, const std::string&, xcb_generic_error_t&) noexcept;
832  void windowReadySet(const Xenium::WindowId) noexcept;
833  void windowReadyWait(const Xenium::WindowId) noexcept;
834  void windowResizeTo(const Xenium::Output&, Xenium::WindowSizeData&, const xcb_configure_notify_event_t*) noexcept;
835  std::error_code windowSizeSet(const Xenium::WindowId, const Xenium::SizePixel&) noexcept;
836  [[nodiscard]] std::error_code windowSizeSetMinMax(const Xenium::WindowId, const int32_t, const int32_t, const int32_t, const int32_t) noexcept;
837  std::error_code windowSizeSetMinMax(const Xenium::Output&, const Xenium::WindowId, Xenium::WindowSizeData&) noexcept;
838 
839  // }}}
840  // {{{ XCB
841 
842  xcb_connection_t* connection = nullptr;
843  const xcb_setup_t* setup = nullptr;
844  xcb_screen_t* screen = nullptr;
845 
846  // -------------------------------------------------- //
847 
848  void xcbEvent(const xcb_button_press_event_t*) noexcept;
849  void xcbEvent(const xcb_client_message_event_t*) noexcept;
850  void xcbEvent(const xcb_configure_notify_event_t*) noexcept;
851  void xcbEvent(const xcb_enter_notify_event_t*) noexcept;
852  void xcbEvent(const xcb_expose_event_t*) noexcept;
853  void xcbEvent(const xcb_focus_in_event_t*) noexcept;
854  void xcbEvent(const xcb_gravity_notify_event_t*) noexcept;
855  void xcbEvent(const xcb_key_press_event_t*) noexcept;
856  void xcbEvent(const xcb_map_notify_event_t*) noexcept;
857  void xcbEvent(const xcb_motion_notify_event_t*) noexcept;
858  void xcbEvent(const xcb_property_notify_event_t*) noexcept;
859  void xcbEvent(const xcb_reparent_notify_event_t*) noexcept;
860  void xcbEvent(const xcb_unmap_notify_event_t*) noexcept;
861 
862  void xcbWindowCreate(Xenium::WindowCreateData*) noexcept;
863  [[nodiscard]] std::error_code xcbWindowCreateValidate(Xenium::WindowCreateData*) noexcept;
864  [[nodiscard]] std::error_code xcbWindowCreateClient(Xenium::WindowCreateData*) noexcept;
865  [[nodiscard]] std::error_code xcbWindowCreateInit(Xenium::WindowCreateData*) noexcept;
866  void xcbWindowDestroy(Xenium::WindowDestroyData*) noexcept;
867 
868  // }}}
869  // {{{ XCB : Atom
870 
871  xcb_atom_t atom_motif_wm_hints = XCB_ATOM_NONE;
872  xcb_atom_t atom_net_frame_extents = XCB_ATOM_NONE;
873  xcb_atom_t atom_net_wm_state = XCB_ATOM_NONE;
874  xcb_atom_t atom_net_wm_state_fullscreen = XCB_ATOM_NONE;
875  xcb_atom_t atom_net_wm_state_hidden = XCB_ATOM_NONE;
876  xcb_atom_t atom_net_wm_state_maximized_horz = XCB_ATOM_NONE;
877  xcb_atom_t atom_net_wm_state_maximized_vert = XCB_ATOM_NONE;
878  xcb_atom_t atom_wm_change_state = XCB_ATOM_NONE;
879  xcb_atom_t atom_wm_delete_window = XCB_ATOM_NONE;
880  xcb_atom_t atom_wm_protocols = XCB_ATOM_NONE;
881 
882  // -------------------------------------------------- //
883 
884  [[nodiscard]] std::error_code atomInit() noexcept;
885  [[nodiscard]] xcb_atom_t atomCreateDeleteWindow(const WindowId, xcb_generic_error_t&) noexcept;
886  [[nodiscard]] std::string atomName(const xcb_atom_t) noexcept;
887  [[nodiscard]] std::vector<xcb_atom_t> atomValueAtom(const Xenium::WindowId, const xcb_atom_t, xcb_generic_error_t&) noexcept;
888  [[nodiscard]] std::vector<int32_t> atomValueData(const Xenium::WindowId, const xcb_atom_t, const xcb_atom_t, const size_t, xcb_generic_error_t&) noexcept;
889  [[nodiscard]] xcb_atom_t internAtom(const std::string&, const bool, xcb_generic_error_t&) noexcept;
890  [[nodiscard]] xcb_intern_atom_cookie_t internAtomRequest(const std::string&, const bool = true) noexcept;
891  [[nodiscard]] xcb_atom_t internAtomReply(const xcb_intern_atom_cookie_t, xcb_generic_error_t&) noexcept;
892 
893  // }}}
894  // {{{ XCB : RandR
895 
896  int randr_error_base = 0;
897  int randr_event_base = 0;
898  int randr_query_version_major = 0;
899  int randr_query_version_minor = 0;
900 
901  // -------------------------------------------------- //
902 
903  [[nodiscard]] std::error_code randrInit() noexcept;
904  void randrEvent(const xcb_randr_crtc_change_t*) noexcept;
905  void randrEvent(const xcb_randr_output_change_t*) noexcept;
906  void randrEvent(const xcb_randr_notify_event_t*) noexcept;
907  void randrEvent(const xcb_randr_screen_change_notify_event_t*) noexcept;
908 
909  // }}}
910  // {{{ XCB : XKB
911 
912  struct XkbControls
913  {
914  uint32_t repeat_delay_ms = 600;
915  uint32_t repeat_interval_ms = 50;
916  };
917 
918  struct KeyData
919  {
920  Xenium::Key key = { 0, 0, Xenium::KeyState::Released };
921  Xenium::KeyModifier modifier = { 0 };
922  Xenium::WindowId window_id = { 0 };
923  uint32_t repeat_time = { 0 };
924  };
925 
926  using KeyDataArray = std::array<Xenium::KeyData, 256>;
927 
928  // -------------------------------------------------- //
929 
930  Xenium::KeyDataArray key_data_array = { };
931  Xenium::KeyModifier key_modifier = { 0 };
932  Xenium::XkbControls xkb_controls = { };
933  uint16_t xkb_modifier_pressed = 0;
934 
935  // -------------------------------------------------- //
936 
937  [[nodiscard]] std::error_code xkbInit() noexcept;
938  inline void keyDataArrayClear() noexcept;
939  inline void keyDataArrayProcess() noexcept;
940  void xkbControlsUpdate() noexcept;
941  void xkbIndicatorStateUpdate() noexcept;
942 
943  // }}}
944  // {{{ XCB : Utility
945 
946  [[nodiscard]] bool requestCheckHasError(const xcb_void_cookie_t&, xcb_generic_error_t&) noexcept;
947 
948  // }}}
949 
950  Xenium(const Xenium&) = delete;
951  Xenium& operator=(const Xenium&) = delete;
952  }; // class Xenium
953 
954  // }}}
955  // {{{ Convenience
956 
957  [[nodiscard]] std::string to_string(const std::vector<xcb_atom_t>&) noexcept;
958  [[nodiscard]] std::string to_string(const std::vector<int32_t>&) noexcept;
959  [[nodiscard]] std::string to_string(const xcb_generic_error_t&) noexcept;
960  [[nodiscard]] std::string to_string(const xcb_button_press_event_t&) noexcept;
961  [[nodiscard]] std::string to_string(const xcb_client_message_event_t&) noexcept;
962  [[nodiscard]] std::string to_string(const xcb_configure_notify_event_t&) noexcept;
963  [[nodiscard]] std::string to_string(const xcb_enter_notify_event_t&) noexcept;
964  [[nodiscard]] std::string to_string(const xcb_expose_event_t&) noexcept;
965  [[nodiscard]] std::string to_string(const xcb_focus_in_event_t&) noexcept;
966  [[nodiscard]] std::string to_string(const xcb_generic_event_t&) noexcept;
967  [[nodiscard]] std::string to_string(const xcb_gravity_notify_event_t&) noexcept;
968  [[nodiscard]] std::string to_string(const xcb_key_press_event_t&) noexcept;
969  [[nodiscard]] std::string to_string(const xcb_map_notify_event_t&) noexcept;
970  [[nodiscard]] std::string to_string(const xcb_motion_notify_event_t&) noexcept;
971  [[nodiscard]] std::string to_string(const xcb_property_notify_event_t&) noexcept;
972  [[nodiscard]] std::string to_string(const xcb_reparent_notify_event_t&) noexcept;
973  [[nodiscard]] std::string to_string(const xcb_unmap_notify_event_t&) noexcept;
974  [[nodiscard]] std::string to_string(const xcb_format_t&) noexcept;
975  [[nodiscard]] std::string to_string(const xcb_screen_t&) noexcept;
976  [[nodiscard]] std::string to_string(const xcb_setup_t&) noexcept;
977  [[nodiscard]] std::string to_string(const xcb_randr_screen_change_notify_event_t&) noexcept;
978  [[nodiscard]] std::string to_string(const Xenium::Key&) noexcept;
979  [[nodiscard]] std::string to_string(const Xenium::KeyModifier&) noexcept;
980  [[nodiscard]] std::string to_string(const Xenium::KeyState) noexcept;
981  [[nodiscard]] std::string to_string(const Xenium::Output&) noexcept;
982  [[nodiscard]] std::string to_string(const Xenium::PointMm) noexcept;
983  [[nodiscard]] std::string to_string(const Xenium::PointPercent) noexcept;
984  [[nodiscard]] std::string to_string(const Xenium::PointPixel) noexcept;
985  [[nodiscard]] std::string to_string(const Xenium::PointerAxis&) noexcept;
986  [[nodiscard]] std::string to_string(const Xenium::PointerAxisSource) noexcept;
987  [[nodiscard]] std::string to_string(const Xenium::PointerAxisType) noexcept;
988  [[nodiscard]] std::string to_string(const Xenium::PointerButton&) noexcept;
989  [[nodiscard]] std::string to_string(const Xenium::PointerButtonState&) noexcept;
990  [[nodiscard]] std::string to_string(const Xenium::SizeMm&) noexcept;
991  [[nodiscard]] std::string to_string(const Xenium::SizePercent&) noexcept;
992  [[nodiscard]] std::string to_string(const Xenium::SizePixel&) noexcept;
993  [[nodiscard]] std::string to_string(const Xenium::WindowDecorations) noexcept;
994  [[nodiscard]] std::string to_string(const Xenium::WindowMode) noexcept;
995 
996  // }}}
997 }
998 
999 
1000 // {{{ Implementation
1001 
1002 #ifdef ZAKERO_XENIUM_IMPLEMENTATION
1003 
1004 // {{{ Macros
1005 
1006 /******************************************************************************
1007  * Macros
1008  */
1009 
1010 // {{{ Macros : Doxygen
1011 
1012 #ifdef ZAKERO__DOXYGEN_DEFINE_DOCS
1013 
1014 // Only used for generating Doxygen documentation
1015 
1035 #define ZAKERO_XENIUM_IMPLEMENTATION
1036 
1045 #define ZAKERO_XENIUM_ENABLE_DEBUG
1046 
1055 #define ZAKERO_XENIUM_ENABLE_SAFE_MODE
1056 
1057 #endif // ZAKERO__DOXYGEN_DEFINE_DOCS
1058 
1059 // }}}
1060 // {{{ Macros : XCB
1061 
1062 #ifndef _NET_WM_STATE_REMOVE
1066 #define _NET_WM_STATE_REMOVE 0
1067 #endif
1068 
1069 #ifndef _NET_WM_STATE_ADD
1073 #define _NET_WM_STATE_ADD 1
1074 #endif
1075 
1076 #ifndef _NET_WM_STATE_TOGGLE
1080 #define _NET_WM_STATE_TOGGLE 2
1081 #endif
1082 
1083 // }}}
1084 // {{{ Macros : Debugging
1085 
1098 #ifdef ZAKERO_XENIUM_ENABLE_DEBUG
1099 #define ZAKERO_XENIUM__DEBUG_ENABLED true
1100 #else
1101 #define ZAKERO_XENIUM__DEBUG_ENABLED false
1102 #endif
1103 
1119 #ifdef ZAKERO_XENIUM_ENABLE_DEBUG
1120 #define ZAKERO_XENIUM__DEBUG_DISABLED false
1121 #else
1122 #define ZAKERO_XENIUM__DEBUG_DISABLED true
1123 #endif
1124 
1125 
1138 #ifndef ZAKERO_XENIUM_DEBUG_STREAM
1139 #define ZAKERO_XENIUM_DEBUG_STREAM std::cerr
1140 #endif
1141 
1142 
1160 #define ZAKERO_XENIUM__DEBUG \
1161  if(ZAKERO_XENIUM__DEBUG_DISABLED) {} \
1162  else ZAKERO_XENIUM_DEBUG_STREAM \
1163  << "pid(" << std::to_string(int64_t(ZAKERO_PID)) \
1164  << ") " __FILE__ "(" \
1165  << std::to_string(__LINE__) \
1166  << ") " \
1167  << __PRETTY_FUNCTION__ \
1168  << " "
1169 
1186 #define ZAKERO_XENIUM__DEBUG_VAR(var_) \
1187  ZAKERO_XENIUM__DEBUG \
1188  << #var_ << ": " << var_ \
1189  << "\n";
1190 
1206 #define ZAKERO_XENIUM__DEBUG_BOOL(var_) \
1207  ZAKERO_XENIUM__DEBUG \
1208  << #var_ << ": " << std::boolalpha << var_ \
1209  << "\n";
1210 
1226 #define ZAKERO_XENIUM__DEBUG_ERROR(var_) \
1227  ZAKERO_XENIUM__DEBUG \
1228  << "Error: " << var_ \
1229  << "\n";
1230 
1231 // }}}
1232 // {{{ Macros : Data Conversion Lookup Tables
1233 
1239 #define ZAKERO_XENIUM__OUTPUT_SUBPIXEL \
1240  X(XCB_RENDER_SUB_PIXEL_UNKNOWN , "Unkown Geometry" ) \
1241  X(XCB_RENDER_SUB_PIXEL_HORIZONTAL_RGB , "Horizontal RGB" ) \
1242  X(XCB_RENDER_SUB_PIXEL_HORIZONTAL_BGR , "Horizontal BGR" ) \
1243  X(XCB_RENDER_SUB_PIXEL_VERTICAL_RGB , "Vertical RGB" ) \
1244  X(XCB_RENDER_SUB_PIXEL_VERTICAL_BGR , "Vertical BGR" ) \
1245  X(XCB_RENDER_SUB_PIXEL_NONE , "No Geometry" ) \
1246 
1252 #define ZAKERO_XENIUM__OUTPUT_TRANSFORM \
1253  X(XCB_RANDR_TRANSFORM_UNIT , "Unit" ) \
1254  X(XCB_RANDR_TRANSFORM_SCALE_UP , "Scale Up" ) \
1255  X(XCB_RANDR_TRANSFORM_SCALE_DOWN , "Scale Down" ) \
1256  X(XCB_RANDR_TRANSFORM_PROJECTIVE , "Projective" ) \
1257 
1258 // }}}
1259 
1270 #define ZAKERO_XENIUM__ERROR(err_) std::error_code(err_, XeniumErrorCategory)
1271 
1272 // }}}
1273 
1274 namespace zakero
1275 {
1276 // {{{ Anonymous Namespace
1277 
1278 namespace
1279 {
1280  // {{{ Cursor Names (Not used yet)
1281  /*
1282  * \brief Common cursor names
1283  *
1284  * These were found in:
1285  * - gdkcursor-wayland.c
1286  * - /usr/share/icons/whiteglass/cursors
1287  const std::array<const char*, 131> common_cursor_names =
1288  { "X_cursor"
1289  , "alias"
1290  , "all-scroll"
1291  , "arrow"
1292  , "base_arrow_down"
1293  , "base_arrow_up"
1294  , "bd_double_arrow"
1295  , "boat"
1296  , "bottom_left_corner"
1297  , "bottom_right_corner"
1298  , "bottom_side"
1299  , "bottom_tee"
1300  , "cell"
1301  , "center_ptr"
1302  , "circle"
1303  , "closedhand"
1304  , "col-resize"
1305  , "color-picker"
1306  , "context-menu"
1307  , "copy"
1308  , "cross"
1309  , "cross_reverse"
1310  , "crossed_circle"
1311  , "crosshair"
1312  , "default"
1313  , "dnd-copy"
1314  , "dnd-link"
1315  , "dnd-move"
1316  , "dnd-no-drop"
1317  , "dnd-none"
1318  , "dot"
1319  , "dot_box_mask"
1320  , "double_arrow"
1321  , "down-arrow"
1322  , "draft"
1323  , "draft_large"
1324  , "draft_small"
1325  , "draped_box"
1326  , "e-resize"
1327  , "ew-resize"
1328  , "exchange"
1329  , "fd_double_arrow"
1330  , "fleur"
1331  , "forbidden"
1332  , "grab"
1333  , "grabbing"
1334  , "gumby"
1335  , "h_double_arrow"
1336  , "half-busy"
1337  , "hand"
1338  , "hand1"
1339  , "hand2"
1340  , "help"
1341  , "ibeam"
1342  , "left-arrow"
1343  , "left_ptr"
1344  , "left_ptr_help"
1345  , "left_ptr_watch"
1346  , "left_side"
1347  , "left_tee"
1348  , "link"
1349  , "ll_angle"
1350  , "lr_angle"
1351  , "move"
1352  , "n-resize"
1353  , "ne-resize"
1354  , "nesw-resize"
1355  , "no-drop"
1356  , "not-allowed"
1357  , "ns-resize"
1358  , "nw-resize"
1359  , "nwse-resize"
1360  , "openhand"
1361  , "pencil"
1362  , "pirate"
1363  , "plus"
1364  , "pointer"
1365  , "pointing_hand"
1366  , "progress"
1367  , "question_arrow"
1368  , "right-arrow"
1369  , "right_ptr"
1370  , "right_side"
1371  , "right_tee"
1372  , "row-resize"
1373  , "s-resize"
1374  , "sailboat"
1375  , "sb_down_arrow"
1376  , "sb_h_double_arrow"
1377  , "sb_left_arrow"
1378  , "sb_right_arrow"
1379  , "sb_up_arrow"
1380  , "sb_v_double_arrow"
1381  , "se-resize"
1382  , "shuttle"
1383  , "size-bdiag"
1384  , "size-fdiag"
1385  , "size-hor"
1386  , "size-ver"
1387  , "size_all"
1388  , "size_bdiag"
1389  , "size_fdiag"
1390  , "size_hor"
1391  , "size_ver"
1392  , "sizing"
1393  , "split_h"
1394  , "split_v"
1395  , "sw-resize"
1396  , "target"
1397  , "tcross"
1398  , "text"
1399  , "top_left_arrow"
1400  , "top_left_corner"
1401  , "top_right_corner"
1402  , "top_side"
1403  , "top_tee"
1404  , "trek"
1405  , "ul_angle"
1406  , "up-arrow"
1407  , "ur_angle"
1408  , "v_double_arrow"
1409  , "vertical-text"
1410  , "w-resize"
1411  , "wait"
1412  , "watch"
1413  , "wayland-cursor"
1414  , "whats_this"
1415  , "x-cursor"
1416  , "xterm"
1417  , "zoom-in"
1418  , "zoom-out"
1419  };
1420  */
1421  // }}}
1422 
1426  constexpr uint32_t Size_Max = (uint32_t)std::numeric_limits<int32_t>::max();
1427 
1428  //constexpr uint8_t XCB_KEY_REPEAT = XCB_KEY_PRESS | 0x80;
1429 
1433  constexpr uint32_t XCB_XKB_INDICATOR_STATE_CAPSLOCK = 0x00000001;
1434 
1438  constexpr uint32_t XCB_XKB_INDICATOR_STATE_NUMLOCK = 0x00000002;
1439 
1443  constexpr std::array<uint32_t, 8> Pointer_Button_Event_Code =
1444  { BTN_LEFT // 0x110 272
1445  , BTN_MIDDLE // 0x112 274
1446  , BTN_RIGHT // 0x111 273
1447  , BTN_SIDE // 0x113 275
1448  , BTN_EXTRA // 0x114 276
1449  , BTN_FORWARD // 0x115 277
1450  , BTN_BACK // 0x116 278
1451  , BTN_TASK // 0x116 279
1452  };
1453 
1457  const uint32_t Default_Value_Mask = 0
1458  //| XCB_CW_BACK_PIXMAP // Not Used
1459  | XCB_CW_BACK_PIXEL // Not Used
1460  //| XCB_CW_BORDER_PIXMAP // Not Used
1461  //| XCB_CW_BORDER_PIXEL // Not Used
1462  | XCB_CW_BIT_GRAVITY
1463  | XCB_CW_WIN_GRAVITY
1464  | XCB_CW_BACKING_STORE
1465  //| XCB_CW_BACKING_PLANES // Not Used
1466  //| XCB_CW_BACKING_PIXEL // Not Used
1467  //| XCB_CW_OVERRIDE_REDIRECT // Future?
1468  | XCB_CW_SAVE_UNDER
1469  | XCB_CW_EVENT_MASK
1470  //| XCB_CW_DONT_PROPAGATE // Future?
1471  | XCB_CW_COLORMAP
1472  //| XCB_CW_CURSOR // Future?
1473  ;
1474 
1478  xcb_create_window_value_list_t Default_Value_List =
1479  { .background_pixmap = XCB_BACK_PIXMAP_NONE
1480  , .background_pixel = 0
1481  , .border_pixmap = XCB_BACK_PIXMAP_NONE
1482  , .border_pixel = 0
1483  , .bit_gravity = XCB_GRAVITY_CENTER
1484  , .win_gravity = XCB_GRAVITY_NORTH_EAST
1485  , .backing_store = XCB_BACKING_STORE_NOT_USEFUL
1486  , .backing_planes = 0
1487  , .backing_pixel = 0
1488  , .override_redirect = 0
1489  , .save_under = 0
1490  , .event_mask = 0
1491  | XCB_EVENT_MASK_KEY_PRESS
1492  | XCB_EVENT_MASK_KEY_RELEASE
1493  | XCB_EVENT_MASK_BUTTON_PRESS
1494  | XCB_EVENT_MASK_BUTTON_RELEASE
1495  | XCB_EVENT_MASK_ENTER_WINDOW
1496  | XCB_EVENT_MASK_LEAVE_WINDOW
1497  | XCB_EVENT_MASK_POINTER_MOTION
1506  | XCB_EVENT_MASK_EXPOSURE
1507  //| XCB_EVENT_MASK_VISIBILITY_CHANGE
1508  | XCB_EVENT_MASK_STRUCTURE_NOTIFY
1512  | XCB_EVENT_MASK_FOCUS_CHANGE
1513  | XCB_EVENT_MASK_PROPERTY_CHANGE
1516  , .do_not_propogate_mask = XCB_EVENT_MASK_NO_EVENT
1517  , .colormap = XCB_COPY_FROM_PARENT
1518  , .cursor = 0
1519  };
1520 
1521 
1531  class XeniumErrorCategory_
1532  : public std::error_category
1533  {
1534  public:
1541  constexpr XeniumErrorCategory_() noexcept
1542  {
1543  }
1544 
1550  const char* name() const noexcept override
1551  {
1552  return "zakero.Xenium";
1553  }
1554 
1561  std::string message(int condition
1562  ) const noexcept override
1563  {
1564  switch(condition)
1565  {
1566 #define X(name_, val_, mesg_) \
1567  case val_: return mesg_;
1568  ZAKERO_XENIUM__ERROR_DATA
1569 #undef X
1570  }
1571 
1572  return "Unknown error condition";
1573  }
1574  } XeniumErrorCategory;
1575 
1576 
1581  Xenium::Lambda Lambda_DoNothing = []() noexcept {};
1582  Xenium::LambdaKey LambdaKey_DoNothing = [](const Xenium::Key&, const Xenium::KeyModifier&) noexcept {};
1583  Xenium::LambdaAxis LambdaAxis_DoNothing = [](const Xenium::PointerAxis&, const Xenium::KeyModifier&) noexcept {};
1584  Xenium::LambdaButtonMm LambdaButtonMm_DoNothing = [](const Xenium::PointerButton&, const Xenium::PointMm&, const Xenium::KeyModifier&) noexcept {};
1585  Xenium::LambdaButtonPercent LambdaButtonPercent_DoNothing = [](const Xenium::PointerButton&, const Xenium::PointPercent&, const Xenium::KeyModifier&) noexcept {};
1586  Xenium::LambdaButtonPixel LambdaButtonPixel_DoNothing = [](const Xenium::PointerButton&, const Xenium::PointPixel&, const Xenium::KeyModifier&) noexcept {};
1587  Xenium::LambdaPointMm LambdaPointMm_DoNothing = [](const Xenium::PointMm&, const Xenium::KeyModifier&) noexcept {};
1588  Xenium::LambdaPointPercent LambdaPointPercent_DoNothing = [](const Xenium::PointPercent&, const Xenium::KeyModifier&) noexcept {};
1589  Xenium::LambdaPointPixel LambdaPointPixel_DoNothing = [](const Xenium::PointPixel&, const Xenium::KeyModifier&) noexcept {};
1590  Xenium::LambdaBool LambdaBool_DoNothing = [](const bool) noexcept {};
1591  Xenium::LambdaOutputId LambdaOutputId_DoNothing = [](const Xenium::OutputId) noexcept {};
1592  Xenium::LambdaWindowDecorations LambdaWindowDecorations_DoNothing = [](const Xenium::WindowDecorations) noexcept {};
1593  Xenium::LambdaWindowMode LambdaWindowMode_DoNothing = [](const Xenium::WindowMode) noexcept {};
1594  Xenium::LambdaSizeMm LambdaSizeMm_DoNothing = [](const Xenium::SizeMm&) noexcept {};
1595  Xenium::LambdaSizePercent LambdaSizePercent_DoNothing = [](const Xenium::SizePercent&) noexcept {};
1596  Xenium::LambdaSizePixel LambdaSizePixel_DoNothing = [](const Xenium::SizePixel&) noexcept {};
1610  std::error_code convertConnectionError(int xcb_error_code
1611  ) noexcept
1612  {
1613  switch(xcb_error_code)
1614  {
1615  case 0:
1616  return ZAKERO_XENIUM__ERROR(Xenium::Error_None);
1617 
1618  case XCB_CONN_ERROR:
1619  return ZAKERO_XENIUM__ERROR(Xenium::Error_Connection_Failed);
1620 
1621  case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
1622  return ZAKERO_XENIUM__ERROR(Xenium::Error_Extension_Not_Supported);
1623 
1624  case XCB_CONN_CLOSED_MEM_INSUFFICIENT:
1625  return ZAKERO_XENIUM__ERROR(Xenium::Error_Not_Enough_Memory);
1626 
1627  case XCB_CONN_CLOSED_REQ_LEN_EXCEED:
1628  return ZAKERO_XENIUM__ERROR(Xenium::Error_Request_Too_Long);
1629 
1630  case XCB_CONN_CLOSED_PARSE_ERR:
1631  return ZAKERO_XENIUM__ERROR(Xenium::Error_Invalid_Display_Name);
1632 
1633  case XCB_CONN_CLOSED_INVALID_SCREEN:
1634  return ZAKERO_XENIUM__ERROR(Xenium::Error_Invalid_Screen);
1635 
1636  default:
1637  return ZAKERO_XENIUM__ERROR(Xenium::Error_Unknown);
1638  }
1639  }
1640 
1641 
1654  template<class Type
1655  >
1656  std::error_code validateMinMax(const Type& min
1657  , const Type& max
1658  ) noexcept
1659  {
1660  if((min.width < 0)
1661  || (min.height < 0)
1662  || (max.width < 0)
1663  || (max.height < 0)
1664  )
1665  {
1666  return ZAKERO_XENIUM__ERROR(Xenium::Error_Window_Size_Too_Small);
1667  }
1668 
1669  if((max.width > 0)
1670  && (min.width > max.width)
1671  )
1672  {
1673  return ZAKERO_XENIUM__ERROR(Xenium::Error_Minimum_Size_Greater_Than_Maximum_Size);
1674  }
1675 
1676  if((max.height > 0)
1677  && (min.height > max.height)
1678  )
1679  {
1680  return ZAKERO_XENIUM__ERROR(Xenium::Error_Minimum_Size_Greater_Than_Maximum_Size);
1681  }
1682 
1683  return ZAKERO_XENIUM__ERROR(Xenium::Error_None);
1684  }
1685 }
1686 
1687 // }}}
1688 // {{{ Documentation
1689 
1815 /* Disabled because Doxygen does not support "enum classes"
1816  *
1817  * \var Xenium::Released
1818  * \brief The key was released
1819  *
1820  * \var Xenium::Pressed
1821  * \brief The key was pressed
1822  *
1823  * \var Xenium::Repeat
1824  * \brief The key is being held down
1825  */
1826 
2074 /* Disabled because Doxygen does not support "enum classes"
2075  *
2076  * \var Xenium::Unknown
2077  * \brief Unknown
2078  *
2079  * \var Xenium::Continuous
2080  * \brief Continuous
2081  *
2082  * \var Xenium::Finger
2083  * \brief Finger
2084  *
2085  * \var Xenium::Wheel
2086  * \brief Wheel
2087  *
2088  * \var Xenium::Wheel_Tilt
2089  * \brief Wheel Tilt
2090  */
2091 
2097 /* Disabled because Doxygen does not support "enum classes"
2098  *
2099  * \var Xenium::Unknown
2100  * \brief Unknown
2101  *
2102  * \var Xenium::Horizontal
2103  * \brief Horizontal
2104  *
2105  * \var Xenium::Vertical
2106  * \brief Vertical
2107  */
2108 
2135 /* Disabled because Doxygen does not support "enum classes"
2136  *
2137  * \var Xenium::Released
2138  * \brief Released
2139  *
2140  * \var Xenium::Pressed
2141  * \brief Pressed
2142  */
2143 
2200 /* Disabled because Doxygen does not support "enum classes"
2201  *
2202  * \var Xenium::Client_Side
2203  * \brief The user app must draw the decorations.
2204  *
2205  * \var Xenium::Server_Side
2206  * \brief The X11 server will draw the decorations.
2207  */
2208 
2214 /* Disabled because Doxygen does not support "enum classes"
2215  *
2216  * \var Xenium::Normal
2217  * \brief A normal window.
2218  *
2219  * \var Xenium::Fullscreen
2220  * \brief A window that uses the entire screen, no borders.
2221  *
2222  * \var Xenium::Maximized
2223  * \brief A window that uses as much of the screen as possible.
2224  */
2225 
2226 
2333 // }}}
2334 // {{{ Constructor / Destructor
2335 
2341 Xenium::Xenium() noexcept
2342  : event_loop()
2343  , event_loop_is_running(false)
2344 {
2345 }
2346 
2347 
2358 {
2359  if(event_loop_is_running || event_loop.joinable())
2360  {
2361  event_loop.request_stop();
2362  event_loop.join();
2363  }
2364 
2365  disconnect();
2366 }
2367 
2368 // }}}
2369 // {{{ Connection
2370 
2391 {
2392  std::error_code error;
2393 
2394  return Xenium::connect("", error);
2395 }
2396 
2397 
2418 Xenium* Xenium::connect(const std::string& display
2419  ) noexcept
2420 {
2421  std::error_code error;
2422 
2423  return Xenium::connect(display, error);
2424 }
2425 
2426 
2451 Xenium* Xenium::connect(std::error_code& error
2452  ) noexcept
2453 {
2454  return Xenium::connect("", error);
2455 }
2456 
2457 
2483 Xenium* Xenium::connect(const std::string& display
2484  , std::error_code& error
2485  ) noexcept
2486 {
2487  const char* display_name = nullptr;
2488 
2489  if(display.empty() == false)
2490  {
2491  display_name = display.c_str();
2492  }
2493 
2494  // --- Connect To X11 Server --- //
2495  int screen_number = 0;
2496  xcb_connection_t* connection = xcb_connect(display_name, &screen_number);
2497  int xcb_error = xcb_connection_has_error(connection);
2498  if(xcb_error)
2499  {
2500  error = convertConnectionError(xcb_error);
2501 
2502  ZAKERO_XENIUM__DEBUG_ERROR(error);
2503 
2504  return nullptr;
2505  }
2506 
2507  // --- Xenium --- //
2508  Xenium* xenium = new Xenium();
2509 
2510  error = xenium->init(connection, screen_number);
2511  if(error)
2512  {
2513  delete xenium;
2514 
2515  ZAKERO_XENIUM__DEBUG_ERROR(error);
2516 
2517  return nullptr;
2518  }
2519 
2520  xenium->eventLoopStart();
2521 
2522  error = xenium->atomInit();
2523  if(error)
2524  {
2525  delete xenium;
2526 
2527  ZAKERO_XENIUM__DEBUG_ERROR(error);
2528 
2529  return nullptr;
2530  }
2531 
2532  error = ZAKERO_XENIUM__ERROR(Error_None);
2533 
2534  return xenium;
2535 }
2536 
2537 
2545 void Xenium::disconnect() noexcept
2546 {
2547  this->setup = nullptr;
2548 
2549  if(connection)
2550  {
2551  xcb_disconnect(this->connection);
2552  connection = nullptr;
2553  }
2554 
2555  return;
2556 }
2557 
2558 // }}}
2559 // {{{ Initialization
2560 
2566 std::error_code Xenium::init(xcb_connection_t* connection
2567  , int screen_number
2568  ) noexcept
2569 {
2570  this->connection = connection;
2571 
2572  // --- X11 Server Setup Information --- //
2573  this->setup = xcb_get_setup(this->connection);
2574  ZAKERO_XENIUM__DEBUG_VAR(to_string(*setup));
2575 
2576  // --- Find the current screen --- //
2577  xcb_screen_iterator_t screen_iterator = xcb_setup_roots_iterator(this->setup);
2578 
2579  for(int i = 0; i < screen_number; i++)
2580  {
2581  xcb_screen_next(&screen_iterator);
2582  }
2583 
2584  this->screen = screen_iterator.data;
2585  ZAKERO_XENIUM__DEBUG_VAR(to_string(*screen));
2586 
2587  // --- XXB Extension --- //
2588  std::error_code error;
2589 
2590  error = xkbInit();
2591  if(error)
2592  {
2593  ZAKERO_XENIUM__DEBUG_ERROR(error);
2594 
2595  return error;
2596  }
2597 
2598  // --- Initialize The Internals --- //
2599  error = randrInit();
2600  if(error)
2601  {
2602  ZAKERO_XENIUM__DEBUG_ERROR(error);
2603 
2604  return error;
2605  }
2606 
2607  error = outputInit();
2608  if(error)
2609  {
2610  ZAKERO_XENIUM__DEBUG_ERROR(error);
2611 
2612  return error;
2613  }
2614 
2615  return ZAKERO_XENIUM__ERROR(Error_None);
2616 }
2617 
2618 // }}}
2619 // {{{ Cursor
2620 
2621 // }}}
2622 // {{{ Event Loop
2623 
2624 //#define ZAKERO_XENIUM__ENABLE_THREAD_SCHEDULER
2625 
2632 void Xenium::eventLoopStart() noexcept
2633 {
2634  event_loop = std::jthread(&Xenium::eventLoop, this);
2635 
2636  while(event_loop_is_running.load() == false)
2637  {
2638  // Wait for the thread to start
2639  std::this_thread::sleep_for(std::chrono::nanoseconds(42));
2640  }
2641 
2642  //#ifdef ZAKERO_XENIUM__ENABLE_THREAD_SCHEDULER
2643  //int policy = SCHED_FIFO;
2644  //int priority_min = sched_get_priority_min(policy);
2645  //int priority_max = sched_get_priority_max(policy);
2646 
2647  //sched_param sched =
2648  //{ .sched_priority = (priority_min + priority_max) / 2
2649  //};
2650 
2651  //pthread_setschedparam(event_loop.native_handle(), policy, &sched);
2652  //#endif
2653 }
2654 
2655 
2663 void Xenium::eventLoop(std::stop_token thread_token
2664  , Xenium* xenium
2665  ) noexcept
2666 {
2667  const int Randr_Notify_Event = xenium->randr_event_base + XCB_RANDR_NOTIFY;
2668 
2669  xcb_generic_event_t* event = nullptr;
2670 
2671  xenium->event_loop_is_running.store(true);
2672 
2673  // Handle events and render window contents
2674  while(thread_token.stop_requested() == false)
2675  {
2676  // Process all events
2677  while(true)
2678  {
2679  event = xcb_poll_for_event(xenium->connection);
2680 
2681  if(event == nullptr)
2682  {
2683  /*
2684  * No more events from the server, check if
2685  * there any remaining key events to process.
2686  */
2687  xenium->keyDataArrayProcess();
2688 
2689  break;
2690  }
2691 
2692  switch(event->response_type & 0x7f)
2693  {
2694  case XCB_CLIENT_MESSAGE:
2695  xenium->xcbEvent(
2696  (xcb_client_message_event_t*)event
2697  );
2698  break;
2699 
2700  // --- XCB_EVENT_MASK_BUTTON_PRESS ------------ //
2701  // --- XCB_EVENT_MASK_BUTTON_RELEASE ---------- //
2702  case XCB_BUTTON_PRESS:
2703  [[fallthrough]];
2704  case XCB_BUTTON_RELEASE:
2705  xenium->xcbEvent(
2706  (xcb_button_press_event_t*)event
2707  );
2708  break;
2709 
2710  // --- XCB_EVENT_MASK_ENTER_WINDOW ------------ //
2711  // --- XCB_EVENT_MASK_LEAVE_WINDOW ------------ //
2712  case XCB_ENTER_NOTIFY: [[fallthrough]];
2713  case XCB_LEAVE_NOTIFY:
2714  xenium->xcbEvent(
2715  (xcb_enter_notify_event_t*)event
2716  );
2717  break;
2718 
2719  // --- XCB_EVENT_MASK_EXPOSURE ---------------- //
2720  case XCB_EXPOSE:
2721  xenium->xcbEvent(
2722  (xcb_expose_event_t*)event
2723  );
2724  break;
2725 
2726  // --- XCB_EVENT_MASK_FOCUS_CHANGE ------------ //
2727  case XCB_FOCUS_IN: [[fallthrough]];
2728  case XCB_FOCUS_OUT:
2729  xenium->xcbEvent(
2730  (xcb_focus_in_event_t*)event
2731  );
2732  break;
2733 
2734  // --- XCB_EVENT_MASK_KEY_PRESS --------------- //
2735  // --- XCB_EVENT_MASK_KEY_RELEASE ------------- //
2736  case XCB_KEY_PRESS: [[fallthrough]];
2737  case XCB_KEY_RELEASE:
2738  xenium->xcbEvent(
2739  (xcb_key_press_event_t*)event
2740  );
2741  break;
2742 
2743  // --- XCB_EVENT_MASK_POINTER_MOTION ---------- //
2744  case XCB_MOTION_NOTIFY:
2745  xenium->xcbEvent(
2746  (xcb_motion_notify_event_t*)event
2747  );
2748  break;
2749 
2750  // --- XCB_EVENT_MASK_PROPERTY_CHANGE --------- //
2751  case XCB_PROPERTY_NOTIFY:
2752  xenium->xcbEvent(
2753  (xcb_property_notify_event_t*)event
2754  );
2755  break;
2756 
2757  // --- XCB_EVENT_MASK_STRUCTURE_NOTIFY -------- //
2758  case XCB_CONFIGURE_NOTIFY:
2759  xenium->xcbEvent(
2760  (xcb_configure_notify_event_t*)event
2761  );
2762  break;
2763 
2764  case XCB_GRAVITY_NOTIFY:
2765  xenium->xcbEvent(
2766  (xcb_gravity_notify_event_t*)event
2767  );
2768  break;
2769 
2770  case XCB_MAP_NOTIFY:
2771  xenium->xcbEvent(
2772  (xcb_map_notify_event_t*)event
2773  );
2774  break;
2775 
2776  case XCB_REPARENT_NOTIFY:
2777  xenium->xcbEvent(
2778  (xcb_reparent_notify_event_t*)event
2779  );
2780  break;
2781 
2782  case XCB_UNMAP_NOTIFY:
2783  xenium->xcbEvent(
2784  (xcb_unmap_notify_event_t*)event
2785  );
2786  break;
2787 
2788  // --- Other Event Types ---------------------- //
2789  default:
2790  if(event->response_type == Randr_Notify_Event)
2791  {
2792  ZAKERO_XENIUM__DEBUG << "RandR Event: " << to_string(*event) << '\n';
2793  xenium->randrEvent(
2794  (xcb_randr_notify_event_t*)event
2795  );
2796  }
2797  else
2798  {
2799  ZAKERO_XENIUM__DEBUG << "Unknown Event: " << to_string(*event) << '\n';
2800  }
2801 
2802  // --- ------------------------------------ --- //
2803  // --- Unused event masks at this time --- //
2804  // --- ------------------------------------ --- //
2805  // --- XCB_EVENT_MASK_BUTTON_1_MOTION --------- //
2806  // --- XCB_EVENT_MASK_BUTTON_2_MOTION --------- //
2807  // --- XCB_EVENT_MASK_BUTTON_3_MOTION --------- //
2808  // --- XCB_EVENT_MASK_BUTTON_4_MOTION --------- //
2809  // --- XCB_EVENT_MASK_BUTTON_5_MOTION --------- //
2810  // --- XCB_EVENT_MASK_BUTTON_MOTION ----------- //
2811  // --- XCB_EVENT_MASK_COLOR_MAP_CHANGE -------- //
2812  // --- XCB_EVENT_MASK_KEYMAP_STATE ------------ //
2813  // --- XCB_EVENT_MASK_OWNER_GRAB_BUTTON ------- //
2814  // --- XCB_EVENT_MASK_POINTER_MOTION_HINT ----- //
2815  // --- XCB_EVENT_MASK_RESIZE_REDIRECT --------- //
2816  // --- XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY ----- //
2817  // --- XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT --- //
2818  // --- XCB_EVENT_MASK_VISIBILITY_CHANGE ------- //
2819  }
2820 
2821  free(event);
2822  }
2823 
2824  /*
2825  * All events are done, check if there are any windows to
2826  * create or destory.
2827  */
2828  xenium->xenium_window_mutex.lock();
2829  {
2830  for(Xenium::WindowCreateData* window_data : xenium->window_to_create)
2831  {
2832  xenium->xcbWindowCreate(window_data);
2833  window_data->barrier.set_value();
2834  }
2835 
2836  xenium->window_to_create.clear();
2837 
2838  for(Xenium::WindowDestroyData* window_data : xenium->window_to_destroy)
2839  {
2840  xenium->xcbWindowDestroy(window_data);
2841  window_data->barrier.set_value();
2842  }
2843 
2844  xenium->window_to_destroy.clear();
2845  }
2846  xenium->xenium_window_mutex.unlock();
2847 
2848  std::this_thread::yield();
2849  }
2850 
2851  xenium->event_loop_is_running.store(false);
2852 }
2853 
2854 // }}}
2855 // {{{ Keyboard
2856 
2865 int32_t Xenium::keyRepeatDelay() const noexcept
2866 {
2867  return xkb_controls.repeat_delay_ms;
2868 }
2869 
2878 int32_t Xenium::keyRepeatRate() const noexcept
2879 {
2880  return 1000 / xkb_controls.repeat_interval_ms;
2881 }
2882 
2883 // }}}
2884 // {{{ Output
2885 
2898 Xenium::Output Xenium::output(const Xenium::OutputId output_id
2899  ) const noexcept
2900 {
2901  std::lock_guard<std::mutex> lock(output_mutex);
2902 
2903  if(output_map.contains(output_id) == false)
2904  {
2905  ZAKERO_XENIUM__DEBUG
2906  << "Invalid output_id: "
2907  << std::to_string(output_id)
2908  ;
2909 
2910  return {};
2911  }
2912 
2913  return output_map.at(output_id);
2914 }
2915 
2916 
2926 Xenium::VectorOutputId Xenium::outputVector() const noexcept
2927 {
2928  Xenium::VectorOutputId vector(output_map.size());
2929  vector.clear();
2930 
2931  std::lock_guard<std::mutex> lock(output_mutex);
2932 
2933  for(const auto& iter : output_map)
2934  {
2935  vector.push_back(iter.first);
2936  }
2937 
2938  return vector;
2939 }
2940 
2941 
2951 std::string Xenium::outputSubpixelName(int32_t subpixel_format
2952  ) noexcept
2953 {
2954  switch(subpixel_format)
2955  {
2956 #define X(value_, name_) \
2957  case value_: return name_;
2958  ZAKERO_XENIUM__OUTPUT_SUBPIXEL
2959 #undef X
2960  default: return "";
2961  }
2962 }
2963 
2964 
2974 std::string Xenium::outputTransformName(int32_t transform
2975  ) noexcept
2976 {
2977  switch(transform)
2978  {
2979 #define X(value_, name_) \
2980  case value_: return name_;
2981  ZAKERO_XENIUM__OUTPUT_TRANSFORM
2982 #undef X
2983  default: return "";
2984  }
2985 }
2986 
2987 
2996 Xenium::PointMm Xenium::outputConvertToMm(const Xenium::OutputId output_id
2997  , const Xenium::PointPixel& point
2998  ) const noexcept
2999 {
3000  std::lock_guard<std::mutex> lock(output_mutex);
3001 
3002  if(output_map.contains(output_id) == false)
3003  {
3004  return { point.time, 0, 0 };
3005  }
3006 
3007  const Xenium::Output& output = output_map.at(output_id);
3008 
3009  auto p = convertPixelToMm(output, point.x, point.y);
3010 
3011  return { point.time, p.first, p.second };
3012 }
3013 
3014 
3023 Xenium::PointPercent Xenium::outputConvertToPercent(const Xenium::OutputId output_id
3024  , const Xenium::PointPixel& point
3025  ) const noexcept
3026 {
3027  std::lock_guard<std::mutex> lock(output_mutex);
3028 
3029  if(output_map.contains(output_id) == false)
3030  {
3031  return { point.time, 0, 0 };
3032  }
3033 
3034  const Xenium::Output& output = output_map.at(output_id);
3035 
3036  auto p = convertPixelToPercent(output, point.x, point.y);
3037 
3038  return { point.time, p.first, p.second };
3039 }
3040 
3041 
3050 Xenium::PointPixel Xenium::outputConvertToPixel(const Xenium::OutputId output_id
3051  , const Xenium::PointMm& point
3052  ) const noexcept
3053 {
3054  std::lock_guard<std::mutex> lock(output_mutex);
3055 
3056  if(output_map.contains(output_id) == false)
3057  {
3058  return { point.time, 0, 0 };
3059  }
3060 
3061  const Xenium::Output& output = output_map.at(output_id);
3062 
3063  auto p = convertMmToPixel(output, point.x, point.y);
3064 
3065  return { point.time, p.first, p.second };
3066 }
3067 
3068 
3077 Xenium::PointPixel Xenium::outputConvertToPixel(const Xenium::OutputId output_id
3078  , const Xenium::PointPercent& point
3079  ) const noexcept
3080 {
3081  std::lock_guard<std::mutex> lock(output_mutex);
3082 
3083  if(output_map.contains(output_id) == false)
3084  {
3085  return { point.time, 0, 0 };
3086  }
3087 
3088  const Xenium::Output& output = output_map.at(output_id);
3089 
3090  auto p = convertPercentToPixel(output, point.x, point.y);
3091 
3092  return { point.time, p.first, p.second };
3093 }
3094 
3095 
3104 Xenium::SizeMm Xenium::outputConvertToMm(const Xenium::OutputId output_id
3105  , const Xenium::SizePixel& size
3106  ) const noexcept
3107 {
3108  std::lock_guard<std::mutex> lock(output_mutex);
3109 
3110  if(output_map.contains(output_id) == false)
3111  {
3112  return { 0, 0 };
3113  }
3114 
3115  const Xenium::Output& output = output_map.at(output_id);
3116 
3117  auto p = convertPixelToMm(output, size.width, size.height);
3118 
3119  return { p.first, p.second };
3120 }
3121 
3122 
3131 Xenium::SizePercent Xenium::outputConvertToPercent(const Xenium::OutputId output_id
3132  , const Xenium::SizePixel& size
3133  ) const noexcept
3134 {
3135  std::lock_guard<std::mutex> lock(output_mutex);
3136 
3137  if(output_map.contains(output_id) == false)
3138  {
3139  return { 0, 0 };
3140  }
3141 
3142  const Xenium::Output& output = output_map.at(output_id);
3143 
3144  auto p = convertPixelToPercent(output, size.width, size.height);
3145 
3146  return { p.first, p.second };
3147 }
3148 
3149 
3157 Xenium::SizePixel Xenium::outputConvertToPixel(const Xenium::OutputId output_id
3158  , const Xenium::SizeMm& size
3159  ) const noexcept
3160 {
3161  std::lock_guard<std::mutex> lock(output_mutex);
3162 
3163  if(output_map.contains(output_id) == false)
3164  {
3165  return { 0, 0 };
3166  }
3167 
3168  const Xenium::Output& output = output_map.at(output_id);
3169 
3170  auto p = convertMmToPixel(output, size.width, size.height);
3171 
3172  return { p.first, p.second };
3173 }
3174 
3175 
3183 Xenium::SizePixel Xenium::outputConvertToPixel(const Xenium::OutputId output_id
3184  , const Xenium::SizePercent& size
3185  ) const noexcept
3186 {
3187  std::lock_guard<std::mutex> lock(output_mutex);
3188 
3189  if(output_map.contains(output_id) == false)
3190  {
3191  return { 0, 0 };
3192  }
3193 
3194  const Xenium::Output& output = output_map.at(output_id);
3195 
3196  auto p = convertPercentToPixel(output, size.width, size.height);
3197 
3198  return { p.first, p.second };
3199 }
3200 
3201 
3209 void Xenium::outputOnAdd(LambdaOutputId lambda
3210  ) noexcept
3211 {
3212  if(lambda == nullptr)
3213  {
3214  output_on_add = LambdaOutputId_DoNothing;
3215  }
3216  else
3217  {
3218  output_on_add = lambda;
3219  }
3220 }
3221 
3222 
3230 void Xenium::outputOnChange(LambdaOutputId lambda
3231  ) noexcept
3232 {
3233  if(lambda == nullptr)
3234  {
3235  output_on_change = LambdaOutputId_DoNothing;
3236  }
3237  else
3238  {
3239  output_on_change = lambda;
3240  }
3241 }
3242 
3243 
3251 void Xenium::outputOnRemove(LambdaOutputId lambda
3252  ) noexcept
3253 {
3254  if(lambda == nullptr)
3255  {
3256  output_on_remove = LambdaOutputId_DoNothing;
3257  }
3258  else
3259  {
3260  output_on_remove = lambda;
3261  }
3262 }
3263 
3264 
3287 const Xenium::Output& Xenium::output(const int16_t x
3288  , const int16_t y
3289  , OutputId& output_id
3290  ) noexcept
3291 {
3292  for(const auto& iter : output_map)
3293  {
3294  const Output& output = iter.second;
3295 
3296  if(x >= output.x
3297  && x < (output.x + output.width)
3298  && y >= output.y
3299  && y < (output.y + output.height)
3300  )
3301  {
3302  output_id = iter.first;
3303 
3304  return output;
3305  }
3306  }
3307 
3308  uint64_t distance = std::numeric_limits<uint64_t>::max();
3309 
3310  for(const auto& iter : output_map)
3311  {
3312  const Output& output = iter.second;
3313 
3314  int64_t output_x = (std::abs(output.x) + output.width) / 2;
3315  int64_t output_y = (std::abs(output.y) + output.height) / 2;
3316 
3317  uint64_t dist = std::abs(x - output_x) + std::abs(y - output_y);
3318 
3319  if(dist < distance)
3320  {
3321  output_id = iter.first;
3322  }
3323  }
3324 
3325  return output_map[output_id];
3326 }
3327 
3328 
3337 std::error_code Xenium::outputInit() noexcept
3338 {
3339  xcb_generic_error_t* xcb_generic_error = nullptr;
3340 
3341  xcb_randr_get_screen_resources_current_reply_t* screen_resources =
3342  xcb_randr_get_screen_resources_current_reply(this->connection
3343  , xcb_randr_get_screen_resources_current(this->connection
3344  , this->screen->root
3345  )
3346  , &xcb_generic_error
3347  );
3348 
3349  if(screen_resources == nullptr)
3350  {
3351  ZAKERO_XENIUM__DEBUG_ERROR(
3352  ZAKERO_XENIUM__ERROR(Error_RandR_Screen_Resources_Not_Found)
3353  );
3354 
3355  return ZAKERO_XENIUM__ERROR(Error_RandR_Screen_Resources_Not_Found);
3356  }
3357 
3358  xcb_randr_output_t* output_list =
3359  xcb_randr_get_screen_resources_current_outputs(screen_resources
3360  );
3361 
3362  int output_list_size =
3363  xcb_randr_get_screen_resources_current_outputs_length(screen_resources
3364  );
3365 
3366 # if ZAKERO_XENIUM__DEBUG_ENABLED
3367  if(output_list_size == 0)
3368  {
3369  ZAKERO_XENIUM__DEBUG
3370  << "No outputs where found in the Screen Resources"
3371  ;
3372  }
3373 # endif
3374 
3375  for(int i = 0; i < output_list_size; i++)
3376  {
3377  xcb_randr_get_output_info_reply_t* output_info =
3378  xcb_randr_get_output_info_reply(this->connection
3379  , xcb_randr_get_output_info(this->connection
3380  , output_list[i]
3381  , screen_resources->config_timestamp
3382  )
3383  , &xcb_generic_error
3384  );
3385 
3386  if(output_info == nullptr
3387  || output_info->connection != XCB_RANDR_CONNECTION_CONNECTED
3388  || output_info->crtc == XCB_NONE
3389  )
3390  {
3391  // Output Info is not usable
3392  free(output_info);
3393  continue;
3394  }
3395 
3396  xcb_randr_get_crtc_info_reply_t* crtc_info =
3397  xcb_randr_get_crtc_info_reply(this->connection
3398  , xcb_randr_get_crtc_info(this->connection
3399  , output_info->crtc
3400  , screen_resources->config_timestamp
3401  )
3402  , &xcb_generic_error
3403  );
3404 
3405  if(crtc_info == nullptr)
3406  {
3407  free(crtc_info);
3408  free(output_info);
3409  continue;
3410  }
3411 
3412  // Some Virtual Machines do not fully populate the output
3413  // devices for X11. Calculate the `mm` size if it is missing.
3414  if(output_info->mm_width == 0 || output_info->mm_height == 0)
3415  {
3416  ZAKERO_XENIUM__DEBUG_ERROR(
3417  ZAKERO_XENIUM__ERROR(Error_RandR_Output_Info_Is_Incomplete)
3418  );
3419 
3420  const float pixels_per_mm_horizontal = (float)this->screen->width_in_pixels / this->screen->width_in_millimeters;
3421  const float pixels_per_mm_vertical = (float)this->screen->height_in_pixels / this->screen->height_in_millimeters;
3422 
3423  output_info->mm_width = crtc_info->width / pixels_per_mm_horizontal;
3424  output_info->mm_height = crtc_info->height / pixels_per_mm_vertical;
3425  }
3426 
3427  outputAdd(crtc_info, output_info);
3428 
3429  free(crtc_info);
3430  free(output_info);
3431  }
3432 
3433  free(screen_resources);
3434 
3435  return ZAKERO_XENIUM__ERROR(Error_None);
3436 }
3437 
3438 
3448 std::error_code Xenium::outputAdd(xcb_randr_crtc_t randr_crtc
3449  , xcb_randr_output_t randr_output
3450  ) noexcept
3451 {
3452  if(randr_crtc == XCB_NONE)
3453  {
3454  ZAKERO_XENIUM__DEBUG_ERROR(
3455  ZAKERO_XENIUM__ERROR(Error_RandR_Invalid_CRTC_Id)
3456  );
3457 
3458  return ZAKERO_XENIUM__ERROR(Error_RandR_Invalid_CRTC_Id);
3459  }
3460 
3461  if(randr_output == XCB_NONE)
3462  {
3463  ZAKERO_XENIUM__DEBUG_ERROR(
3464  ZAKERO_XENIUM__ERROR(Error_RandR_Invalid_Output_Id)
3465  );
3466 
3467  return ZAKERO_XENIUM__ERROR(Error_RandR_Invalid_Output_Id);
3468  }
3469 
3470  xcb_generic_error_t* xcb_generic_error = nullptr;
3471 
3472  xcb_randr_get_screen_resources_current_reply_t* screen_resources =
3473  xcb_randr_get_screen_resources_current_reply(this->connection
3474  , xcb_randr_get_screen_resources_current(this->connection
3475  , this->screen->root
3476  )
3477  , &xcb_generic_error
3478  );
3479 
3480  if(screen_resources == nullptr)
3481  {
3482  ZAKERO_XENIUM__DEBUG_ERROR(
3483  ZAKERO_XENIUM__ERROR(Error_RandR_Screen_Resources_Not_Found)
3484  );
3485 
3486  return ZAKERO_XENIUM__ERROR(Error_RandR_Screen_Resources_Not_Found);
3487  }
3488 
3489  xcb_randr_get_output_info_reply_t* output_info =
3490  xcb_randr_get_output_info_reply(this->connection
3491  , xcb_randr_get_output_info(this->connection
3492  , randr_output
3493  , screen_resources->config_timestamp
3494  )
3495  , &xcb_generic_error
3496  );
3497 
3498  if(output_info == nullptr)
3499  {
3500  free(screen_resources);
3501 
3502  ZAKERO_XENIUM__DEBUG_ERROR(
3503  ZAKERO_XENIUM__ERROR(Error_RandR_Output_Info_Not_Found)
3504  );
3505 
3506  return ZAKERO_XENIUM__ERROR(Error_RandR_Output_Info_Not_Found);
3507  }
3508 
3509  xcb_randr_get_crtc_info_reply_t* crtc_info =
3510  xcb_randr_get_crtc_info_reply(this->connection
3511  , xcb_randr_get_crtc_info(this->connection
3512  , randr_crtc
3513  , screen_resources->config_timestamp
3514  )
3515  , &xcb_generic_error
3516  );
3517 
3518  if(crtc_info == nullptr)
3519  {
3520  free(output_info);
3521  free(screen_resources);
3522 
3523  ZAKERO_XENIUM__DEBUG_ERROR(
3524  ZAKERO_XENIUM__ERROR(Error_RandR_CRTC_Info_Not_Found)
3525  );
3526 
3527  return ZAKERO_XENIUM__ERROR(Error_RandR_CRTC_Info_Not_Found);
3528  }
3529 
3530  // Some Virtual Machines do not fully populate the output
3531  // devices for X11. Calculate the `mm` size if it is missing.
3532  if(output_info->mm_width == 0 || output_info->mm_height == 0)
3533  {
3534  ZAKERO_XENIUM__DEBUG_ERROR(
3535  ZAKERO_XENIUM__ERROR(Error_RandR_Output_Info_Is_Incomplete)
3536  );
3537 
3538  const float pixels_per_mm_horizontal = (float)this->screen->width_in_pixels / this->screen->width_in_millimeters;
3539  const float pixels_per_mm_vertical = (float)this->screen->height_in_pixels / this->screen->height_in_millimeters;
3540 
3541  output_info->mm_width = crtc_info->width / pixels_per_mm_horizontal;
3542  output_info->mm_height = crtc_info->height / pixels_per_mm_vertical;
3543  }
3544 
3545  outputAdd(crtc_info, output_info);
3546 
3547  free(crtc_info);
3548  free(output_info);
3549  free(screen_resources);
3550 
3551  return ZAKERO_XENIUM__ERROR(Error_None);
3552 }
3553 
3554 
3561 void Xenium::outputAdd(const xcb_randr_get_crtc_info_reply_t* crtc_info
3562  , const xcb_randr_get_output_info_reply_t* output_info
3563  ) noexcept
3564 {
3565  const std::string output_name(
3566  (const char*)xcb_randr_get_output_info_name(output_info),
3567  xcb_randr_get_output_info_name_length(output_info)
3568  );
3569 
3570  OutputId output_id = output_info->crtc;
3571 
3572  this->output_map[output_id] =
3573  { .name = output_name
3574  //, .make = ""
3575  //, .model = ""
3576  , .x = crtc_info->x
3577  , .y = crtc_info->y
3578  , .width = crtc_info->width
3579  , .height = crtc_info->height
3580  , .physical_width_mm = output_info->mm_width
3581  , .physical_height_mm = output_info->mm_height
3582  , .subpixel = output_info->subpixel_order
3583  //, .refresh_mHz = 0 // ????
3584  //, .scale_factor = 0 // ????
3585  , .transform = crtc_info->rotation
3586  , .pixels_per_mm_horizontal = (float)crtc_info->width / output_info->mm_width
3587  , .pixels_per_mm_vertical = (float)crtc_info->height / output_info->mm_height
3588  };
3589 
3590  ZAKERO_XENIUM__DEBUG_VAR(output_id)
3591  ZAKERO_XENIUM__DEBUG_VAR(this->output_map[output_id].name)
3592 }
3593 
3594 
3595 // }}}
3596 // {{{ Utility
3597 
3606 std::pair<float, float> Xenium::convertPixelToMm(const Xenium::Output& output
3607  , int32_t xw
3608  , int32_t yh
3609  ) const noexcept
3610 {
3611  const float ratio_h = output.pixels_per_mm_horizontal;
3612  const float ratio_v = output.pixels_per_mm_vertical;
3613 
3614  return
3615  { xw / ratio_h
3616  , yh / ratio_v
3617  };
3618 }
3619 
3620 
3629 std::pair<float, float> Xenium::convertPixelToPercent(const Xenium::Output& output
3630  , int32_t xw
3631  , int32_t yh
3632  ) const noexcept
3633 {
3634  return
3635  { float(xw) / output.width
3636  , float(yh) / output.height
3637  };
3638 }
3639 
3640 
3649 std::pair<int32_t, int32_t> Xenium::convertMmToPixel(const Xenium::Output& output
3650  , float xw
3651  , float yh
3652  ) const noexcept
3653 {
3654  const float ratio_h = output.pixels_per_mm_horizontal;
3655  const float ratio_v = output.pixels_per_mm_vertical;
3656 
3657  return
3658  { int32_t(xw * ratio_h)
3659  , int32_t(yh * ratio_v)
3660  };
3661 }
3662 
3663 
3672 std::pair<int32_t, int32_t> Xenium::convertPercentToPixel(const Xenium::Output& output
3673  , float xw
3674  , float yh
3675  ) const noexcept
3676 {
3677  return
3678  { int32_t(xw * output.width)
3679  , int32_t(yh * output.height)
3680  };
3681 }
3682 
3683 // }}}
3684 // {{{ Window
3685 
3702  , std::error_code& error
3703  ) noexcept
3704 {
3705  return windowCreate(size, Default_Value_Mask, Default_Value_List, error);
3706 }
3707 
3708 
3728  , const uint32_t value_mask
3729  , xcb_create_window_value_list_t& value_list
3730  , std::error_code& error
3731  ) noexcept
3732 {
3733  Xenium::WindowCreateData data =
3734  { .barrier = {}
3735  , .error = {}
3736  , .window_id = 0
3737  , .output_id = 0
3738  , .atom_close_request = 0
3739  , .gc = 0
3740  , .size_unit = Xenium::SizeUnit::Millimeter
3741  , .size_mm = size_mm
3742  , .size_percent = {}
3743  , .size_pixel = {}
3744  , .value_mask = value_mask
3745  , .value_list = value_list
3746  };
3747 
3748  std::future<void> barrier = data.barrier.get_future();
3749 
3750  windowCreateAddToQueue(&data);
3751 
3752  barrier.wait();
3753 
3754  if(data.error)
3755  {
3756  error = data.error;
3757 
3758  ZAKERO_XENIUM__DEBUG_ERROR(error);
3759 
3760  return nullptr;
3761  }
3762 
3763  Xenium::Window* window = new Xenium::Window(this, (void*)&data);
3764 
3765  windowReadyWait(data.window_id);
3766 
3767  return window;
3768 }
3769 
3770 
3788  , std::error_code& error
3789  ) noexcept
3790 {
3791  return windowCreate(size, Default_Value_Mask, Default_Value_List, error);
3792 }
3793 
3794 
3813  , const uint32_t value_mask
3814  , xcb_create_window_value_list_t& value_list
3815  , std::error_code& error
3816  ) noexcept
3817 {
3818  Xenium::WindowCreateData data =
3819  { .barrier = {}
3820  , .error = {}
3821  , .window_id = 0
3822  , .output_id = 0
3823  , .atom_close_request = 0
3824  , .gc = 0
3825  , .size_unit = Xenium::SizeUnit::Percent
3826  , .size_mm = {}
3827  , .size_percent = size_percent
3828  , .size_pixel = {}
3829  , .value_mask = value_mask
3830  , .value_list = value_list
3831  };
3832 
3833  std::future<void> barrier = data.barrier.get_future();
3834 
3835  windowCreateAddToQueue(&data);
3836 
3837  barrier.wait();
3838 
3839  if(data.error)
3840  {
3841  error = data.error;
3842 
3843  ZAKERO_XENIUM__DEBUG_ERROR(error);
3844 
3845  return nullptr;
3846  }
3847 
3848  Xenium::Window* window = new Xenium::Window(this, (void*)&data);
3849 
3850  windowReadyWait(data.window_id);
3851 
3852  return window;
3853 }
3854 
3855 
3872  , std::error_code& error
3873  ) noexcept
3874 {
3875  return windowCreate(size, Default_Value_Mask, Default_Value_List, error);
3876 }
3877 
3878 
3897  , const uint32_t value_mask
3898  , xcb_create_window_value_list_t& value_list
3899  , std::error_code& error
3900  ) noexcept
3901 {
3902  Xenium::WindowCreateData data =
3903  { .barrier = {}
3904  , .error = {}
3905  , .window_id = 0
3906  , .output_id = 0
3907  , .atom_close_request = 0
3908  , .gc = 0
3909  , .size_unit = Xenium::SizeUnit::Pixel
3910  , .size_mm = {}
3911  , .size_percent = {}
3912  , .size_pixel = size_pixel
3913  , .value_mask = value_mask
3914  , .value_list = value_list
3915  };
3916 
3917  std::future<void> barrier = data.barrier.get_future();
3918 
3919  windowCreateAddToQueue(&data);
3920 
3921  barrier.wait();
3922 
3923  if(data.error)
3924  {
3925  error = data.error;
3926 
3927  ZAKERO_XENIUM__DEBUG_ERROR(error);
3928 
3929  return nullptr;
3930  }
3931 
3932  Xenium::Window* window = new Xenium::Window(this, (void*)&data);
3933 
3934  windowReadyWait(data.window_id);
3935 
3936  return window;
3937 }
3938 
3939 
3949 std::error_code Xenium::windowBorder(const WindowId window_id
3950  , const bool enable
3951  ) noexcept
3952 {
3953  MotifWmHints hints_data =
3954  { .flags = 2
3955  , .functions = 0
3956  , .decorations = enable
3957  , .input_mode = 0
3958  , .status = 0
3959  };
3960 
3961  xcb_void_cookie_t void_cookie =
3962  xcb_change_property_checked(this->connection
3963  , XCB_PROP_MODE_REPLACE // mode
3964  , window_id // window
3965  , atom_motif_wm_hints // property
3966  , atom_motif_wm_hints // type
3967  , 32 // format : pointer to 32-bit data
3968  , 5 // data_len
3969  , &hints_data // data
3970  );
3971 
3972  xcb_generic_error_t generic_error;
3973 
3974  if(requestCheckHasError(void_cookie, generic_error))
3975  {
3976  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
3977 
3978  return ZAKERO_XENIUM__ERROR(Error_Unknown);
3979  }
3980 
3981  return ZAKERO_XENIUM__ERROR(Error_None);
3982 }
3983 
3984 
3988 void Xenium::windowCreateAddToQueue(Xenium::WindowCreateData* window_data
3989  ) noexcept
3990 {
3991  xenium_window_mutex.lock();
3992 
3993  window_to_create.push_back(window_data);
3994 
3995  xenium_window_mutex.unlock();
3996 }
3997 
3998 
4002 void Xenium::windowDestroyAddToQueue(Xenium::WindowDestroyData* window_data
4003  ) noexcept
4004 {
4005  xenium_window_mutex.lock();
4006 
4007  window_to_destroy.push_back(window_data);
4008 
4009  xenium_window_mutex.unlock();
4010 }
4011 
4012 
4022 std::error_code Xenium::windowLocationSet(const WindowId window_id
4023  , const Xenium::PointPixel& point
4024  ) noexcept
4025 {
4026  xcb_configure_window_value_list_t value_list =
4027  { .x = point.x
4028  , .y = point.y
4029  , .width = 0
4030  , .height = 0
4031  , .border_width = 0
4032  , .sibling = 0
4033  , .stack_mode = 0
4034  };
4035 
4036  xcb_void_cookie_t void_cookie =
4037  xcb_configure_window_aux_checked(this->connection
4038  , window_id
4039  , 0
4040  | XCB_CONFIG_WINDOW_X
4041  | XCB_CONFIG_WINDOW_Y
4042  , &value_list
4043  );
4044 
4045  xcb_generic_error_t generic_error;
4046  if(requestCheckHasError(void_cookie, generic_error))
4047  {
4048  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
4049 
4050  return ZAKERO_XENIUM__ERROR(Error_Unknown);
4051  }
4052 
4053  return ZAKERO_XENIUM__ERROR(Error_None);
4054 }
4055 
4056 
4065 std::error_code Xenium::windowMinimize(const Xenium::WindowId window_id
4066  ) noexcept
4067 {
4068  xcb_client_message_event_t event =
4069  { .response_type = XCB_CLIENT_MESSAGE
4070  , .format = 32
4071  , .sequence = 0
4072  , .window = window_id
4073  , .type = atom_wm_change_state
4074  , .data = { }
4075  };
4076  event.data.data32[0] = XCB_ICCCM_WM_STATE_ICONIC;
4077 
4078  xcb_send_event(this->connection
4079  , 0 // do not propagate
4080  , this->screen->root
4081  , XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
4082  , (const char*)&event
4083  );
4084 
4085  xcb_flush(this->connection);
4086 
4087  return ZAKERO_XENIUM__ERROR(Error_None);
4088 }
4089 
4090 
4100 std::error_code Xenium::windowModeSet(const Xenium::WindowId window_id
4101  , const Xenium::WindowMode current_mode
4102  , const Xenium::WindowMode new_mode
4103  ) noexcept
4104 {
4105  xcb_client_message_event_t event =
4106  { .response_type = XCB_CLIENT_MESSAGE
4107  , .format = 32
4108  , .sequence = 0
4109  , .window = window_id
4110  , .type = atom_net_wm_state
4111  , .data = { }
4112  };
4113 
4114  if(current_mode == Xenium::WindowMode::Normal)
4115  {
4116  // Do nothing
4117  }
4118  else
4119  {
4120  event.data.data32[0] = _NET_WM_STATE_REMOVE;
4121 
4122  if(current_mode == Xenium::WindowMode::Fullscreen)
4123  {
4124  event.data.data32[1] = atom_net_wm_state_fullscreen;
4125  event.data.data32[2] = 0;
4126  }
4127  else // if(current_mode == Xenium::WindowMode::Maximized)
4128  {
4129  event.data.data32[1] = atom_net_wm_state_maximized_horz;
4130  event.data.data32[2] = atom_net_wm_state_maximized_vert;
4131  }
4132 
4133  xcb_send_event(this->connection
4134  , 0 // do not propagate
4135  , this->screen->root
4136  , XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
4137  , (const char*)&event
4138  );
4139  }
4140 
4141  if(new_mode == Xenium::WindowMode::Normal)
4142  {
4143  // Do nothing
4144  }
4145  else
4146  {
4147  event.data.data32[0] = _NET_WM_STATE_ADD;
4148 
4149  if(new_mode == Xenium::WindowMode::Fullscreen)
4150  {
4151  event.data.data32[1] = atom_net_wm_state_fullscreen;
4152  event.data.data32[2] = 0;
4153  }
4154  else // if(current_mode == Xenium::WindowMode::Maximized)
4155  {
4156  event.data.data32[1] = atom_net_wm_state_maximized_horz;
4157  event.data.data32[2] = atom_net_wm_state_maximized_vert;
4158  }
4159 
4160  xcb_send_event(this->connection
4161  , 0 // do not propagate
4162  , this->screen->root
4163  , XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
4164  , (const char*)&event
4165  );
4166  }
4167 
4168  xcb_flush(this->connection);
4169 
4170  return ZAKERO_XENIUM__ERROR(Error_None);
4171 }
4172 
4173 
4185 bool Xenium::windowPropertySet(WindowId window_id
4186  , const xcb_atom_t property
4187  , const xcb_atom_t value
4188  , xcb_generic_error_t& generic_error
4189  ) noexcept
4190 {
4191  xcb_void_cookie_t void_cookie =
4192  xcb_change_property_checked(this->connection
4193  , XCB_PROP_MODE_REPLACE // mode
4194  , window_id // window
4195  , property // property
4196  , XCB_ATOM_ATOM // type
4197  , 32 // format : 32-bit pointer
4198  , 1 // data_len
4199  , &value // data
4200  );
4201 
4202  if(requestCheckHasError(void_cookie, generic_error))
4203  {
4204  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
4205  return false;
4206  }
4207 
4208  return true;
4209 }
4210 
4211 
4223 bool Xenium::windowPropertySet(WindowId window_id
4224  , const xcb_atom_t property
4225  , const std::string& value
4226  , xcb_generic_error_t& generic_error
4227  ) noexcept
4228 {
4229  xcb_void_cookie_t void_cookie =
4230  xcb_change_property_checked(this->connection
4231  , XCB_PROP_MODE_REPLACE // mode
4232  , window_id // window
4233  , property // property
4234  , XCB_ATOM_STRING // type
4235  , 8 // format : 32-bit pointer
4236  , value.length() // data_len
4237  , value.data() // data
4238  );
4239  if(requestCheckHasError(void_cookie, generic_error))
4240  {
4241  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
4242  return false;
4243  }
4244 
4245  return true;
4246 }
4247 
4248 
4259 void Xenium::windowReadySet(const WindowId window_id
4260  ) noexcept
4261 {
4262  window_ready_map[window_id] = true;
4263 }
4264 
4265 
4275 void Xenium::windowReadyWait(const WindowId window_id
4276  ) noexcept
4277 {
4278  xcb_map_window(this->connection, window_id);
4279  xcb_flush(this->connection);
4280 
4281  while(window_ready_map[window_id] == false)
4282  {
4283  usleep(42);
4284  }
4285 }
4286 
4287 
4294 void Xenium::windowResizeTo(const Output& output
4295  , Xenium::WindowSizeData& window_size
4296  , const xcb_configure_notify_event_t* event
4297  ) noexcept
4298 {
4299  bool update_size = false;
4300 
4301  if(window_size.unit == SizeUnit::Millimeter)
4302  {
4303  auto pixel = convertMmToPixel(output
4304  , window_size.mm.width
4305  , window_size.mm.height
4306  );
4307 
4308  if(pixel.first != window_size.pixel.width
4309  || pixel.second != window_size.pixel.height
4310  )
4311  {
4312  update_size = true;
4313  }
4314 
4315  window_size.pixel = {pixel.first, pixel.second};
4316 
4317  auto percent = convertPixelToPercent(output
4318  , window_size.pixel.width
4319  , window_size.pixel.height
4320  );
4321  window_size.percent = {percent.first, percent.second};
4322  }
4323  else if(window_size.unit == SizeUnit::Percent)
4324  {
4325  auto pixel = convertPercentToPixel(output
4326  , window_size.percent.width
4327  , window_size.percent.height
4328  );
4329 
4330  if(pixel.first != window_size.pixel.width
4331  || pixel.second != window_size.pixel.height
4332  )
4333  {
4334  update_size = true;
4335  window_size.pixel = {pixel.first, pixel.second};
4336  }
4337 
4338  auto mm = convertPixelToMm(output
4339  , window_size.pixel.width
4340  , window_size.pixel.height
4341  );
4342 
4343  window_size.mm = {mm.first, mm.second};
4344  }
4345  else
4346  {
4347  if(event->width != window_size.pixel.width
4348  || event->height != window_size.pixel.height
4349  )
4350  {
4351  update_size = true;
4352  }
4353 
4354  window_size.pixel = {event->width, event->height};
4355 
4356  auto mm = convertPixelToMm(output
4357  , window_size.pixel.width
4358  , window_size.pixel.height
4359  );
4360  window_size.mm = {mm.first, mm.second};
4361 
4362  auto percent = convertPixelToPercent(output
4363  , window_size.pixel.width
4364  , window_size.pixel.height
4365  );
4366  window_size.percent = {percent.first, percent.second};
4367  }
4368 
4369  windowSizeSetMinMax(output, event->window, window_size);
4370 
4371  if(update_size)
4372  {
4373  window_size.pixel_lambda(window_size.pixel);
4374  window_size.percent_lambda(window_size.percent);
4375  window_size.mm_lambda(window_size.mm);
4376 
4377  windowSizeSet(event->window, window_size.pixel);
4378  }
4379 }
4380 
4381 
4389 std::error_code Xenium::windowSizeSet(const WindowId window_id
4390  , const Xenium::SizePixel& size
4391  ) noexcept
4392 {
4393  xcb_configure_window_value_list_t value_list =
4394  { .x = 0
4395  , .y = 0
4396  , .width = (uint32_t)size.width
4397  , .height = (uint32_t)size.height
4398  , .border_width = 0
4399  , .sibling = 0
4400  , .stack_mode = 0
4401  };
4402 
4403  xcb_void_cookie_t void_cookie =
4404  xcb_configure_window_aux_checked(this->connection
4405  , window_id
4406  , 0
4407  | XCB_CONFIG_WINDOW_WIDTH
4408  | XCB_CONFIG_WINDOW_HEIGHT
4409  , &value_list
4410  );
4411 
4412  xcb_generic_error_t generic_error;
4413  if(requestCheckHasError(void_cookie, generic_error))
4414  {
4415  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
4416 
4417  return ZAKERO_XENIUM__ERROR(Error_Unknown);
4418  }
4419 
4420  return ZAKERO_XENIUM__ERROR(Error_None);
4421 }
4422 
4423 
4432 std::error_code Xenium::windowSizeSetMinMax(const WindowId window_id
4433  , const int32_t min_width
4434  , const int32_t min_height
4435  , const int32_t max_width
4436  , const int32_t max_height
4437  ) noexcept
4438 {
4439  xcb_get_property_cookie_t property_cookie =
4440  xcb_get_property(this->connection
4441  , 0 // delete
4442  , window_id // window
4443  , XCB_ATOM_WM_NORMAL_HINTS // property
4444  , XCB_ATOM_WM_SIZE_HINTS // type
4445  , 0 // offset to data (32-bit)
4446  , 18 // number of 32-bit values
4447  );
4448 
4449  xcb_generic_error_t* error_ptr = nullptr;
4450  xcb_get_property_reply_t* property_reply =
4451  xcb_get_property_reply(this->connection
4452  , property_cookie
4453  , &error_ptr
4454  );
4455  if(error_ptr)
4456  {
4457  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(*error_ptr) << '\n';
4458 
4459  return ZAKERO_XENIUM__ERROR(Error_Unknown);
4460  }
4461 
4462  xcb_size_hints_t* size_hints =
4463  (xcb_size_hints_t*)xcb_get_property_value(property_reply);
4464 
4465  if(min_width == 0 && min_height == 0)
4466  {
4467  size_hints->flags &= (~XCB_ICCCM_SIZE_HINT_P_MIN_SIZE);
4468  }
4469  else
4470  {
4471  size_hints->flags |= XCB_ICCCM_SIZE_HINT_P_MIN_SIZE;
4472  }
4473 
4474  size_hints->min_width = min_width;
4475  size_hints->min_height = min_height;
4476 
4477  if(max_width == 0 && max_height == 0)
4478  {
4479  size_hints->flags &= (~XCB_ICCCM_SIZE_HINT_P_MAX_SIZE);
4480  }
4481  else
4482  {
4483  size_hints->flags |= XCB_ICCCM_SIZE_HINT_P_MAX_SIZE;
4484  }
4485 
4486  size_hints->max_width = max_width;
4487  size_hints->max_height = max_height;
4488 
4489  xcb_void_cookie_t cookie =
4490  xcb_change_property_checked(this->connection
4491  , XCB_PROP_MODE_REPLACE
4492  , window_id
4493  , XCB_ATOM_WM_NORMAL_HINTS
4494  , XCB_ATOM_WM_SIZE_HINTS
4495  , 32 // 32-bit values
4496  , 18 // 18 values
4497  , size_hints
4498  );
4499 
4500  xcb_generic_error_t generic_error;
4501 
4502  if(requestCheckHasError(cookie, generic_error))
4503  {
4504  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
4505 
4506  return ZAKERO_XENIUM__ERROR(Error_Unknown);
4507  }
4508 
4509  return ZAKERO_XENIUM__ERROR(Error_None);
4510 }
4511 
4512 
4531 std::error_code Xenium::windowSizeSetMinMax(const Xenium::Output& output
4532  , const Xenium::WindowId window_id
4533  , Xenium::WindowSizeData& window_size
4534  ) noexcept
4535 {
4536  if(window_size.unit == SizeUnit::Millimeter)
4537  {
4538  // --- Window Minimum Size --- //
4539  auto pixel = convertMmToPixel(output
4540  , window_size.mm_minimum.width
4541  , window_size.mm_minimum.height
4542  );
4543  window_size.pixel_minimum = {pixel.first, pixel.second};
4544 
4545  auto percent = convertPixelToPercent(output
4546  , window_size.pixel_minimum.width
4547  , window_size.pixel_minimum.height
4548  );
4549  window_size.percent_minimum = {percent.first, percent.second};
4550 
4551  // --- Window Maximum Size --- //
4552  pixel = convertMmToPixel(output
4553  , window_size.mm_maximum.width
4554  , window_size.mm_maximum.height
4555  );
4556  window_size.pixel_maximum = {pixel.first, pixel.second};
4557 
4558  percent = convertPixelToPercent(output
4559  , window_size.pixel_maximum.width
4560  , window_size.pixel_maximum.height
4561  );
4562  window_size.percent_maximum = {percent.first, percent.second};
4563  }
4564  else if(window_size.unit == SizeUnit::Percent)
4565  {
4566  // --- Window Minimum Size --- //
4567  auto pixel = convertPercentToPixel(output
4568  , window_size.percent_minimum.width
4569  , window_size.percent_minimum.height
4570  );
4571  window_size.pixel_minimum = {pixel.first, pixel.second};
4572 
4573  auto mm = convertPixelToMm(output
4574  , window_size.pixel_minimum.width
4575  , window_size.pixel_minimum.height
4576  );
4577  window_size.mm_minimum = {mm.first, mm.second};
4578 
4579  // --- Window Maximum Size --- //
4580  pixel = convertPercentToPixel(output
4581  , window_size.percent_maximum.width
4582  , window_size.percent_maximum.height
4583  );
4584  window_size.pixel_maximum = {pixel.first, pixel.second};
4585 
4586  mm = convertPixelToMm(output
4587  , window_size.pixel_maximum.width
4588  , window_size.pixel_maximum.height
4589  );
4590  window_size.mm_maximum = {mm.first, mm.second};
4591  }
4592  else
4593  {
4594  // --- Window Minimum Size --- //
4595  auto mm = convertPixelToMm(output
4596  , window_size.pixel_minimum.width
4597  , window_size.pixel_minimum.height
4598  );
4599  window_size.mm_minimum = {mm.first, mm.second};
4600 
4601  auto percent = convertPixelToPercent(output
4602  , window_size.pixel_minimum.width
4603  , window_size.pixel_minimum.height
4604  );
4605  window_size.percent_minimum = {percent.first, percent.second};
4606 
4607  // --- Window Maximum Size --- //
4608  mm = convertPixelToMm(output
4609  , window_size.pixel_maximum.width
4610  , window_size.pixel_maximum.height
4611  );
4612  window_size.mm_maximum = {mm.first, mm.second};
4613 
4614  percent = convertPixelToPercent(output
4615  , window_size.pixel_maximum.width
4616  , window_size.pixel_maximum.height
4617  );
4618  window_size.percent_maximum = {percent.first, percent.second};
4619  }
4620 
4621  // --- Set Window Min/Max Size --- //
4622  std::error_code error;
4623 
4624  error = windowSizeSetMinMax(window_id
4625  , window_size.pixel_minimum.width, window_size.pixel_minimum.height
4626  , window_size.pixel_maximum.width, window_size.pixel_maximum.height
4627  );
4628 
4629 # if ZAKERO_XENIUM__DEBUG_ENABLED
4630  if(error)
4631  {
4632  ZAKERO_XENIUM__DEBUG_ERROR(error);
4633  }
4634 # endif
4635 
4636  return error;
4637 }
4638 
4639 // }}}
4640 // {{{ XCB
4641 
4645 void Xenium::xcbEvent(const xcb_button_press_event_t* event
4646  ) noexcept
4647 {
4648 //std::cout << "Button Press: " << to_string(*event) << '\n';
4649 
4650  const WindowId window_id = event->event;
4651 
4652  uint32_t button_code = event->detail;
4653 
4654  if(button_code <= 3 || button_code >= 8)
4655  {
4656  if(button_code > 9)
4657  {
4658  button_code = 0;
4659  }
4660  else if(button_code >= 8)
4661  {
4662  button_code -= 5;
4663  }
4664  else
4665  {
4666  button_code--;
4667  }
4668 
4669  PointerButton button =
4670  { .code = Pointer_Button_Event_Code[button_code]
4671  , .state = event->response_type == XCB_BUTTON_PRESS
4672  ? Xenium::PointerButtonState::Pressed
4673  : Xenium::PointerButtonState::Released
4674  };
4675 
4676  OutputId output_id = window_output_map[window_id];
4677  Output output = output_map.at(output_id);
4678 
4679  PointPixel point_pixel = {0, event->event_x, event->event_y};
4680 
4681  auto mm = convertPixelToMm(output
4682  , event->event_x
4683  , event->event_y
4684  );
4685  PointMm point_mm = {0, mm.first, mm.second};
4686 
4687  Xenium::WindowSizeData& window_size = window_size_map[window_id];
4688  PointPercent point_percent =
4689  { event->time
4690  , (float)event->event_x / (float)window_size.pixel.width
4691  , (float)event->event_y / (float)window_size.pixel.height
4692  };
4693 
4694  window_on_button_map[window_id].lambda_mm(button, point_mm, key_modifier);
4695  window_on_button_map[window_id].lambda_percent(button, point_percent, key_modifier);
4696  window_on_button_map[window_id].lambda_pixel(button, point_pixel, key_modifier);
4697  }
4698  else if(event->response_type == XCB_BUTTON_PRESS)
4699  {
4700  if(button_code == 4 || button_code == 5)
4701  {
4702  PointerAxis pointer_axis =
4703  { .time = event->time
4704  , .steps = button_code == 4 ? -1 : 1
4705  , .distance = button_code == 4 ? -15.0f : 15.0f
4706  , .source = PointerAxisSource::Wheel
4707  , .type = PointerAxisType::Vertical
4708  };
4709 
4710  window_on_axis_map[window_id](pointer_axis, key_modifier);
4711  }
4712  else if(button_code == 6 || button_code == 7)
4713  {
4714  PointerAxis pointer_axis =
4715  { .time = event->time
4716  , .steps = button_code == 6 ? -1 : 1
4717  , .distance = button_code == 6 ? -15.0f : 15.0f
4718  , .source = PointerAxisSource::Wheel
4719  , .type = PointerAxisType::Horizontal
4720  };
4721 
4722  window_on_axis_map[window_id](pointer_axis, key_modifier);
4723  }
4724  }
4725 }
4726 
4727 
4731 void Xenium::xcbEvent(const xcb_client_message_event_t* event
4732  ) noexcept
4733 {
4734 //std::cout << "Client Message: " << to_string(*event) << '\n';
4735  if(window_delete_map.contains(event->window))
4736  {
4737  WindowDeleteData& window_delete = window_delete_map[event->window];
4738  if(event->data.data32[0] == window_delete.atom_close_request)
4739  {
4740  window_delete.close_request_lambda();
4741  }
4742  }
4743 }
4744 
4745 
4769 void Xenium::xcbEvent(const xcb_configure_notify_event_t* event
4770  ) noexcept
4771 {
4772 //std::cout << "Configure Notify: " << to_string(*event) << '\n';
4773  if((event->response_type & 0x80) == 0)
4774  {
4775  // Only care about events with the "synthetic bit" set
4776  return;
4777  }
4778 
4779  const WindowId window_id = event->window;
4780 
4781  auto iter = window_size_map.find(window_id);
4782 
4783  if(iter == std::end(window_size_map))
4784  {
4785  return;
4786  }
4787 
4788  Xenium::WindowSizeData& window_size = iter->second;
4789 
4790  OutputId output_id;
4791  const Output& output = this->output(event->x, event->y, output_id);
4792 
4793  if(window_output_map[window_id] != output_id)
4794  {
4795  window_output_map[window_id] = output_id;
4796 
4797  windowResizeTo(output, window_size, event);
4798 
4799  return;
4800  }
4801 
4802  if(window_size.pixel.width == event->width
4803  && window_size.pixel.height == event->height
4804  )
4805  {
4806  return;
4807  }
4808 
4809  window_size.pixel = {event->width, event->height};
4810 
4811  auto mm = convertPixelToMm(output
4812  , window_size.pixel.width
4813  , window_size.pixel.height
4814  );
4815  window_size.mm = {mm.first, mm.second};
4816 
4817  auto percent = convertPixelToPercent(output
4818  , window_size.pixel.width
4819  , window_size.pixel.height
4820  );
4821  window_size.percent = {percent.first, percent.second};
4822 
4823  window_size.pixel_lambda(window_size.pixel);
4824  window_size.percent_lambda(window_size.percent);
4825  window_size.mm_lambda(window_size.mm);
4826 }
4827 
4828 
4836 void Xenium::xcbEvent(const xcb_enter_notify_event_t* event
4837  ) noexcept
4838 {
4839 //std::cout << "Enter Notify: " << to_string(*event) << '\n';
4840 
4841  const WindowId window_id = event->event;
4842 
4843  if(event->response_type == XCB_LEAVE_NOTIFY)
4844  {
4845  window_on_leave_map[window_id]();
4846  window_keyboard[window_id].on_leave();
4847  return;
4848  }
4849 
4850  xkbIndicatorStateUpdate();
4851 
4852  OutputId output_id = window_output_map[window_id];
4853  Output output = output_map.at(output_id);
4854 
4855  PointPixel point_pixel = {0, event->event_x, event->event_y};
4856 
4857  auto mm = convertPixelToMm(output
4858  , event->event_x
4859  , event->event_y
4860  );
4861  PointMm point_mm = {0, mm.first, mm.second};
4862 
4863  Xenium::WindowSizeData& window_size = window_size_map[window_id];
4864  PointPercent point_percent =
4865  { 0
4866  , (float)event->event_x / (float)window_size.pixel.width
4867  , (float)event->event_y / (float)window_size.pixel.height
4868  };
4869 
4870  window_on_enter_map[window_id].lambda_mm(point_mm, key_modifier);
4871  window_on_enter_map[window_id].lambda_percent(point_percent, key_modifier);
4872  window_on_enter_map[window_id].lambda_pixel(point_pixel, key_modifier);
4873 
4874  window_keyboard[window_id].on_enter();
4875 }
4876 
4877 
4881 void Xenium::xcbEvent(const xcb_expose_event_t* event
4882  ) noexcept
4883 {
4884 //std::cout << "Expose: " << to_string(*event) << '\n';
4885  windowReadySet(event->window);
4886 }
4887 
4888 
4892 void Xenium::xcbEvent(const xcb_focus_in_event_t* event
4893  ) noexcept
4894 {
4895 //std::cout << "Pocus: " << to_string(*event) << '\n';
4896 
4897  const WindowId window_id = event->event;
4898 
4899  if(window_focus_map.contains(window_id) == false)
4900  {
4901  return;
4902  }
4903 
4904  if(event->response_type == XCB_FOCUS_IN)
4905  {
4906  xkbControlsUpdate();
4907  xkbIndicatorStateUpdate();
4908  window_focus_map[window_id](true);
4909  }
4910  else
4911  {
4912  keyDataArrayClear();
4913  window_focus_map[window_id](false);
4914  }
4915 }
4916 
4917 
4921 void Xenium::xcbEvent(const xcb_gravity_notify_event_t* //event ///< The XCB Event
4922  ) noexcept
4923 {
4924 //std::cout << "Gravity Notify: " << to_string(*event) << '\n';
4925 }
4926 
4927 
4955 void Xenium::xcbEvent(const xcb_key_press_event_t* event
4956  ) noexcept
4957 {
4958 //std::cout << "Key Press: " << to_string(*event) << '\n';
4959 
4960  uint32_t key_code = (uint32_t)event->detail - 8;
4961 
4962  constexpr uint16_t CapsLock = 0b0000'0010'0000'0000;
4963  constexpr uint16_t NumLock = 0b0000'0001'0000'0000;
4964  constexpr uint16_t Alt_Left = 0b0000'0000'1000'0000;
4965  constexpr uint16_t Alt_Right = 0b0000'0000'0100'0000;
4966  constexpr uint16_t Control_Left = 0b0000'0000'0010'0000;
4967  constexpr uint16_t Control_Right = 0b0000'0000'0001'0000;
4968  constexpr uint16_t Meta_Left = 0b0000'0000'0000'1000;
4969  constexpr uint16_t Meta_Right = 0b0000'0000'0000'0100;
4970  constexpr uint16_t Shift_Left = 0b0000'0000'0000'0010;
4971  constexpr uint16_t Shift_Right = 0b0000'0000'0000'0001;
4972 
4973  if(event->response_type == XCB_KEY_PRESS)
4974  {
4975  switch(key_code)
4976  {
4977  case KEY_CAPSLOCK:
4978  xkbIndicatorStateUpdate();
4979  xkb_modifier_pressed |= CapsLock;
4980  break;
4981 
4982  case KEY_NUMLOCK:
4983  xkbIndicatorStateUpdate();
4984  xkb_modifier_pressed |= NumLock;
4985  break;
4986 
4987  case KEY_LEFTALT:
4988  xkb_modifier_pressed |= Alt_Left;
4989  break;
4990 
4991  case KEY_RIGHTALT:
4992  xkb_modifier_pressed |= Alt_Right;
4993  break;
4994 
4995  case KEY_LEFTCTRL:
4996  xkb_modifier_pressed |= Control_Left;
4997  break;
4998 
4999  case KEY_RIGHTCTRL:
5000  xkb_modifier_pressed |= Control_Right;
5001  break;
5002 
5003  case KEY_LEFTMETA:
5004  xkb_modifier_pressed |= Meta_Left;
5005  break;
5006 
5007  case KEY_RIGHTMETA:
5008  xkb_modifier_pressed |= Meta_Right;
5009  break;
5010 
5011  case KEY_LEFTSHIFT:
5012  xkb_modifier_pressed |= Shift_Left;
5013  break;
5014 
5015  case KEY_RIGHTSHIFT:
5016  xkb_modifier_pressed |= Shift_Right;
5017  break;
5018  }
5019  }
5020  else
5021  {
5022  switch(key_code)
5023  {
5024  case KEY_CAPSLOCK:
5025  xkbIndicatorStateUpdate();
5026  xkb_modifier_pressed &= (~CapsLock);
5027  break;
5028 
5029  case KEY_NUMLOCK:
5030  xkbIndicatorStateUpdate();
5031  xkb_modifier_pressed &= (~NumLock);
5032  break;
5033 
5034  case KEY_LEFTALT:
5035  xkb_modifier_pressed &= (~Alt_Left);
5036  break;
5037 
5038  case KEY_RIGHTALT:
5039  xkb_modifier_pressed &= (~Alt_Right);
5040  break;
5041 
5042  case KEY_LEFTCTRL:
5043  xkb_modifier_pressed &= (~Control_Left);
5044  break;
5045 
5046  case KEY_RIGHTCTRL:
5047  xkb_modifier_pressed &= (~Control_Right);
5048  break;
5049 
5050  case KEY_LEFTMETA:
5051  xkb_modifier_pressed &= (~Meta_Left);
5052  break;
5053 
5054  case KEY_RIGHTMETA:
5055  xkb_modifier_pressed &= (~Meta_Right);
5056  break;
5057 
5058  case KEY_LEFTSHIFT:
5059  xkb_modifier_pressed &= (~Shift_Left);
5060  break;
5061 
5062  case KEY_RIGHTSHIFT:
5063  xkb_modifier_pressed &= (~Shift_Right);
5064  break;
5065  }
5066  }
5067 
5068  key_modifier.pressed = 0;
5069 
5070  if(xkb_modifier_pressed & CapsLock)
5071  {
5072  key_modifier.pressed |= KeyModifier_CapsLock;
5073  }
5074 
5075  if(xkb_modifier_pressed & NumLock)
5076  {
5077  key_modifier.pressed |= KeyModifier_NumLock;
5078  }
5079 
5080  if(xkb_modifier_pressed & (Alt_Left | Alt_Right))
5081  {
5082  key_modifier.pressed |= KeyModifier_Alt;
5083  }
5084 
5085  if(xkb_modifier_pressed & (Control_Left | Control_Right))
5086  {
5087  key_modifier.pressed |= KeyModifier_Control;
5088  }
5089 
5090  if(xkb_modifier_pressed & (Meta_Left | Meta_Right))
5091  {
5092  key_modifier.pressed |= KeyModifier_Meta;
5093  }
5094 
5095  if(xkb_modifier_pressed & (Shift_Left | Shift_Right))
5096  {
5097  key_modifier.pressed |= KeyModifier_Shift;
5098  }
5099 
5100  const WindowId window_id = event->event;
5101 
5102  key_data_array[key_code].window_id = window_id;
5103 
5104  Key& key = key_data_array[key_code].key;
5105  key.code = key_code;
5106 
5107  if(event->response_type == XCB_KEY_PRESS)
5108  {
5109  key_data_array[key_code].modifier = key_modifier;
5110 
5111  if(key.time == event->time)
5112  {
5113  key.state = KeyState::Repeat;
5114  }
5115  else
5116  {
5117  key.time = event->time;
5118  key.state = KeyState::Pressed;
5119 
5120  key_data_array[key_code].repeat_time =
5121  event->time + xkb_controls.repeat_delay_ms
5122  ;
5123 
5124  window_on_key_map[window_id](key, key_modifier);
5125  }
5126  }
5127  /* If another window is active and a key is pressed then the cursor is
5128  * moved into a Xenium window while the key remains pressed, it is
5129  * possible for the first key event to be recieved is the
5130  * XCB_KEY_RELEASE. This event must be ignored because there was no
5131  * precedding XCB_KEY_PRESS event. To identify the bad XCB_KEY_RELEASE
5132  * event from the good event, the key.time value will be `0` if the the
5133  * pressed event is missing. A non-`0` key.time value means there was
5134  * a preceding XCB_KEY_PRESS event.
5135  */
5136  else if(key.time != 0) // && (event->response_type == XCB_KEY_RELEASE)
5137  {
5138  key.time = event->time;
5139  key.state = KeyState::Released;
5140 
5141  key_data_array[key_code].modifier = key_modifier;
5142  }
5143 }
5144 
5145 
5149 void Xenium::xcbEvent(const xcb_map_notify_event_t* //event ///< The XCB Event
5150  ) noexcept
5151 {
5152 //std::cout << "Map Notify: " << to_string(*event) << '\n';
5153 }
5154 
5155 
5159 void Xenium::xcbEvent(const xcb_motion_notify_event_t* event
5160  ) noexcept
5161 {
5162 //std::cout << "Motion Notify: " << to_string(*event) << '\n';
5163 
5164  const WindowId window_id = event->event;
5165 
5166  OutputId output_id = window_output_map[window_id];
5167  Output output = output_map.at(output_id);
5168 
5169  PointPixel point_pixel = {0, event->event_x, event->event_y};
5170 
5171  auto mm = convertPixelToMm(output
5172  , event->event_x
5173  , event->event_y
5174  );
5175  PointMm point_mm = {0, mm.first, mm.second};
5176 
5177  Xenium::WindowSizeData& window_size = window_size_map[window_id];
5178  PointPercent point_percent =
5179  { 0
5180  , (float)event->event_x / (float)window_size.pixel.width
5181  , (float)event->event_y / (float)window_size.pixel.height
5182  };
5183 
5184  window_on_motion_map[window_id].lambda_mm(point_mm, key_modifier);
5185  window_on_motion_map[window_id].lambda_percent(point_percent, key_modifier);
5186  window_on_motion_map[window_id].lambda_pixel(point_pixel, key_modifier);
5187 }
5188 
5189 
5193 void Xenium::xcbEvent(const xcb_property_notify_event_t* event
5194  ) noexcept
5195 {
5196 //std::cout << "Property Notify: " << to_string(*event) << '\n';
5197 
5198  xcb_generic_error_t generic_error;
5199 
5200  if(event->atom == atom_net_frame_extents)
5201  {
5202  std::vector<int32_t> atom_data = Xenium::atomValueData(event->window
5203  , atom_net_frame_extents
5204  , XCB_ATOM_CARDINAL
5205  , 4
5206  , generic_error
5207  );
5208 
5209  struct
5210  {
5211  int32_t left;
5212  int32_t right;
5213  int32_t top;
5214  int32_t bottom;
5215  }
5216  frame_extents =
5217  { .left = atom_data[0]
5218  , .right = atom_data[1]
5219  , .top = atom_data[2]
5220  , .bottom = atom_data[3]
5221  };
5222 
5223  WindowDecorationsData& window = window_decorations_map[event->window];
5224 
5225  WindowDecorations window_decorations = window.window_decorations;
5226 
5227  if(frame_extents.left == 0
5228  && frame_extents.right == 0
5229  && frame_extents.top == 0
5230  && frame_extents.bottom == 0
5231  )
5232  {
5233  window.window_decorations = WindowDecorations::Client_Side;
5234  }
5235  else
5236  {
5237  window.window_decorations = WindowDecorations::Server_Side;
5238  }
5239 
5240  if(window.window_decorations != window_decorations)
5241  {
5242  window.lambda(window.window_decorations);
5243  }
5244 
5245  return;
5246  }
5247 
5248  if(event->atom == atom_net_wm_state)
5249  {
5250  std::vector<xcb_atom_t> value =
5251  atomValueAtom(event->window, event->atom, generic_error);
5252 
5253  WindowMode new_window_mode = WindowMode::Normal;
5254 
5255  if(zakero::vectorContains(value, atom_net_wm_state_maximized_horz)
5256  && zakero::vectorContains(value, atom_net_wm_state_maximized_vert)
5257  )
5258  {
5259  new_window_mode = WindowMode::Maximized;
5260  }
5261  else if(zakero::vectorContains(value, atom_net_wm_state_fullscreen))
5262  {
5263  new_window_mode = WindowMode::Fullscreen;
5264  }
5265 
5266  if(window_mode_map.contains(event->window))
5267  {
5268  WindowModeData& data = window_mode_map[event->window];
5269  LambdaWindowMode lambda = LambdaWindowMode_DoNothing;
5270  if(data.window_mode != new_window_mode)
5271  {
5272  data.window_mode = new_window_mode;
5273  lambda = data.lambda;
5274  }
5275 
5276  lambda(new_window_mode);
5277  }
5278 
5279  return;
5280  }
5281 }
5282 
5286 void Xenium::xcbEvent(const xcb_reparent_notify_event_t* //event ///< The XCB Event
5287  ) noexcept
5288 {
5289 //std::cout << "Reparent Notify: " << to_string(*event) << '\n';
5290 }
5291 
5292 
5296 void Xenium::xcbEvent(const xcb_unmap_notify_event_t* //event ///< The XCB Event
5297  ) noexcept
5298 {
5299 //std::cout << "Unmap Notify: " << to_string(*event) << '\n';
5300 }
5301 
5302 
5310 void Xenium::xcbWindowCreate(Xenium::WindowCreateData* window_data
5311  ) noexcept
5312 {
5313  window_data->error = xcbWindowCreateValidate(window_data);
5314  if(window_data->error)
5315  {
5316  ZAKERO_XENIUM__DEBUG_ERROR(window_data->error);
5317 
5318  return;
5319  }
5320 
5321  window_data->error = xcbWindowCreateClient(window_data);
5322  if(window_data->error)
5323  {
5324  ZAKERO_XENIUM__DEBUG_ERROR(window_data->error);
5325 
5326  Xenium::WindowDestroyData data =
5327  { .barrier = {}
5328  , .window_id = window_data->window_id
5329  , .gc = window_data->gc
5330  };
5331 
5332  xcbWindowDestroy(&data);
5333 
5334  return;
5335  }
5336 
5337  window_data->error = xcbWindowCreateInit(window_data);
5338  if(window_data->error)
5339  {
5340  ZAKERO_XENIUM__DEBUG_ERROR(window_data->error);
5341 
5342  Xenium::WindowDestroyData data =
5343  { .barrier = {}
5344  , .window_id = window_data->window_id
5345  , .gc = window_data->gc
5346  };
5347 
5348  xcbWindowDestroy(&data);
5349 
5350  return;
5351  }
5352 
5353  return;
5354 }
5355 
5356 
5364 std::error_code Xenium::xcbWindowCreateValidate(Xenium::WindowCreateData* window_data
5365  ) noexcept
5366 {
5367  window_data->output_id = output_map.begin()->first;
5368  Xenium::Output& output = output_map.begin()->second;
5369 
5370  Xenium::SizePixel size_pixel;
5371 
5372  if(window_data->size_unit == Xenium::SizeUnit::Millimeter)
5373  {
5374  auto pixel = convertMmToPixel(output
5375  , window_data->size_mm.width
5376  , window_data->size_mm.height
5377  );
5378  size_pixel = {pixel.first, pixel.second};
5379  }
5380  else if(window_data->size_unit == Xenium::SizeUnit::Percent)
5381  {
5382  auto pixel = convertPercentToPixel(output
5383  , window_data->size_percent.width
5384  , window_data->size_percent.height
5385  );
5386  size_pixel = {pixel.first, pixel.second};
5387  }
5388  else // if(window_data->size_unit == Xenium::SizeUnit::Pixel)
5389  {
5390  size_pixel = window_data->size_pixel;
5391  }
5392 
5393  if((size_pixel.width < Window_Size_Minimum)
5394  || (size_pixel.width < Window_Size_Minimum)
5395  )
5396  {
5397  ZAKERO_XENIUM__DEBUG_ERROR(
5398  ZAKERO_XENIUM__ERROR(Error_Window_Size_Too_Small)
5399  );
5400 
5401  return ZAKERO_XENIUM__ERROR(Error_Window_Size_Too_Small);
5402  }
5403 
5404  if(window_data->size_unit == Xenium::SizeUnit::Millimeter)
5405  {
5406  window_data->size_pixel = size_pixel;
5407 
5408  auto percent = convertPixelToPercent(output
5409  , window_data->size_pixel.width
5410  , window_data->size_pixel.height
5411  );
5412  window_data->size_percent = {percent.first, percent.second};
5413  }
5414  else if(window_data->size_unit == Xenium::SizeUnit::Percent)
5415  {
5416  window_data->size_pixel = size_pixel;
5417 
5418  auto mm = convertPixelToMm(output
5419  , window_data->size_pixel.width
5420  , window_data->size_pixel.height
5421  );
5422  window_data->size_mm = {mm.first, mm.second};
5423  }
5424  else // if(window_data->size_unit == Xenium::SizeUnit::Pixel)
5425  {
5426  auto mm = convertPixelToMm(output
5427  , window_data->size_pixel.width
5428  , window_data->size_pixel.height
5429  );
5430  window_data->size_mm = {mm.first, mm.second};
5431 
5432  auto percent = convertPixelToPercent(output
5433  , window_data->size_pixel.width
5434  , window_data->size_pixel.height
5435  );
5436  window_data->size_percent = {percent.first, percent.second};
5437  }
5438 
5439  return ZAKERO_XENIUM__ERROR(Error_None);
5440 }
5441 
5442 
5450 std::error_code Xenium::xcbWindowCreateClient(Xenium::WindowCreateData* data
5451  ) noexcept
5452 {
5453  data->window_id = xcb_generate_id(this->connection);
5454 
5455  xcb_void_cookie_t cookie =
5456  xcb_create_window_aux_checked(this->connection
5457  , this->screen->root_depth // depth
5458  , data->window_id // requested id
5459  , this->screen->root // parent window id
5460  , 0, 0 // location x,y
5461  , data->size_pixel.width // width
5462  , data->size_pixel.height // height
5463  , 0 // border width
5464  , XCB_WINDOW_CLASS_INPUT_OUTPUT // "class"
5465  , this->screen->root_visual // visual
5466  , data->value_mask
5467  , &data->value_list
5468  );
5469 
5470  xcb_generic_error_t generic_error;
5471 
5472  if(requestCheckHasError(cookie, generic_error))
5473  {
5474  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
5475 
5476  data->window_id = 0;
5477 
5478  return ZAKERO_XENIUM__ERROR(Error_Unknown);
5479  }
5480 
5481  data->atom_close_request = atomCreateDeleteWindow(data->window_id
5482  , generic_error
5483  );
5484 
5485  if(data->atom_close_request == XCB_ATOM_NONE)
5486  {
5487  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
5488 
5489  return ZAKERO_XENIUM__ERROR(Error_Unknown);
5490  }
5491 
5492  xcb_size_hints_t size_hints =
5493  { .flags = 0
5494  , .x = 0
5495  , .y = 0
5496  , .width = 0
5497  , .height = 0
5498  , .min_width = 0
5499  , .min_height = 0
5500  , .max_width = 0
5501  , .max_height = 0
5502  , .width_inc = 0
5503  , .height_inc = 0
5504  , .min_aspect_num = 0
5505  , .min_aspect_den = 0
5506  , .max_aspect_num = 0
5507  , .max_aspect_den = 0
5508  , .base_width = 0
5509  , .base_height = 0
5510  , .win_gravity = 0
5511  };
5512 
5513  xcb_change_property_checked(this->connection
5514  , XCB_PROP_MODE_REPLACE
5515  , data->window_id
5516  , XCB_ATOM_WM_NORMAL_HINTS
5517  , XCB_ATOM_WM_SIZE_HINTS
5518  , 32 // 32-bit values
5519  , 18 // 18 values
5520  , &size_hints
5521  );
5522 
5523  if(requestCheckHasError(cookie, generic_error))
5524  {
5525  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
5526 
5527  return ZAKERO_XENIUM__ERROR(Error_Unknown);
5528  }
5529 
5530  data->gc = xcb_generate_id(this->connection);
5531  cookie = xcb_create_gc_checked(this->connection
5532  , data->gc
5533  , data->window_id
5534  , 0
5535  , nullptr
5536  );
5537 
5538  if(requestCheckHasError(cookie, generic_error))
5539  {
5540  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
5541 
5542  data->gc = 0;
5543 
5544  return ZAKERO_XENIUM__ERROR(Error_Unknown);
5545  }
5546 
5547  return ZAKERO_XENIUM__ERROR(Error_None);
5548 }
5549 
5550 
5554 void Xenium::xcbWindowDestroy(Xenium::WindowDestroyData* window_data
5555  ) noexcept
5556 {
5557  if(window_data->window_id == 0)
5558  {
5559  return;
5560  }
5561 
5562  if(window_data->gc != 0)
5563  {
5564  xcb_free_gc(this->connection, window_data->gc);
5565 
5566  window_data->gc = 0;
5567  }
5568 
5569  xcb_destroy_window(this->connection, window_data->window_id);
5570 
5571  window_decorations_map.erase(window_data->window_id);
5572  window_delete_map.erase(window_data->window_id);
5573  window_focus_map.erase(window_data->window_id);
5574  window_keyboard.erase(window_data->window_id);
5575  window_map.erase(window_data->window_id);
5576  window_mode_map.erase(window_data->window_id);
5577  window_on_axis_map.erase(window_data->window_id);
5578  window_on_button_map.erase(window_data->window_id);
5579  window_on_enter_map.erase(window_data->window_id);
5580  window_on_key_map.erase(window_data->window_id);
5581  window_on_leave_map.erase(window_data->window_id);
5582  window_on_motion_map.erase(window_data->window_id);
5583  window_output_map.erase(window_data->window_id);
5584  window_ready_map.erase(window_data->window_id);
5585  window_size_map.erase(window_data->window_id);
5586 
5587  window_data->window_id = 0;
5588 }
5589 
5590 
5598 std::error_code Xenium::xcbWindowCreateInit(Xenium::WindowCreateData* data
5599  ) noexcept
5600 {
5601  window_size_map[data->window_id] =
5602  { .mm = data->size_mm
5603  , .mm_minimum = {0, 0}
5604  , .mm_maximum = {0, 0}
5605  , .mm_lambda = LambdaSizeMm_DoNothing
5606  , .percent = data->size_percent
5607  , .percent_minimum = {0, 0}
5608  , .percent_maximum = {0, 0}
5609  , .percent_lambda = LambdaSizePercent_DoNothing
5610  , .pixel = data->size_pixel
5611  , .pixel_minimum = {0, 0}
5612  , .pixel_maximum = {0, 0}
5613  , .pixel_lambda = LambdaSizePixel_DoNothing
5614  , .unit = data->size_unit
5615  };
5616 
5617  window_mode_map[data->window_id] =
5618  { .window_mode = WindowMode::Normal
5619  , .lambda = LambdaWindowMode_DoNothing
5620  };
5621 
5622  window_output_map[data->window_id] = data->output_id;
5623 
5624  window_keyboard[data->window_id] =
5625  { .on_enter = Lambda_DoNothing
5626  , .on_leave = Lambda_DoNothing
5627  };
5628 
5629  window_on_key_map[data->window_id] = LambdaKey_DoNothing;
5630  window_on_leave_map[data->window_id] = Lambda_DoNothing;
5631  window_on_axis_map[data->window_id] = LambdaAxis_DoNothing;
5632 
5633  window_on_motion_map[data->window_id] =
5634  { .lambda_mm = LambdaPointMm_DoNothing
5635  , .lambda_percent = LambdaPointPercent_DoNothing
5636  , .lambda_pixel = LambdaPointPixel_DoNothing
5637  };
5638 
5639  window_on_button_map[data->window_id] =
5640  { .lambda_mm = LambdaButtonMm_DoNothing
5641  , .lambda_percent = LambdaButtonPercent_DoNothing
5642  , .lambda_pixel = LambdaButtonPixel_DoNothing
5643  };
5644 
5645  window_on_enter_map[data->window_id] =
5646  { .lambda_mm = LambdaPointMm_DoNothing
5647  , .lambda_percent = LambdaPointPercent_DoNothing
5648  , .lambda_pixel = LambdaPointPixel_DoNothing
5649  };
5650 
5651  window_decorations_map[data->window_id] =
5652  { .window_decorations = WindowDecorations::Server_Side
5653  , .lambda = LambdaWindowDecorations_DoNothing
5654  };
5655 
5656  window_focus_map[data->window_id] = LambdaBool_DoNothing;
5657 
5658  window_delete_map[data->window_id] =
5659  { .close_request_lambda = Lambda_DoNothing
5660  , .atom_close_request = data->atom_close_request
5661  };
5662 
5663  window_ready_map[data->window_id] = false;
5664 
5665  return ZAKERO_XENIUM__ERROR(Error_None);
5666 }
5667 
5668 // }}}
5669 // {{{ XCB : Atom
5670 
5679 std::error_code Xenium::atomInit() noexcept
5680 {
5681  auto cookie_motif_wm_hints = internAtomRequest("_MOTIF_WM_HINTS");
5682  auto cookie_net_frame_extents = internAtomRequest("_NET_FRAME_EXTENTS");
5683  auto cookie_net_wm_state = internAtomRequest("_NET_WM_STATE");
5684  auto cookie_net_wm_state_fullscreen = internAtomRequest("_NET_WM_STATE_FULLSCREEN");
5685  auto cookie_net_wm_state_hidden = internAtomRequest("_NET_WM_STATE_HIDDEN");
5686  auto cookie_net_wm_state_maximized_horz = internAtomRequest("_NET_WM_STATE_MAXIMIZED_HORZ");
5687  auto cookie_net_wm_state_maximized_vert = internAtomRequest("_NET_WM_STATE_MAXIMIZED_VERT");
5688  auto cookie_wm_change_state = internAtomRequest("WM_CHANGE_STATE");
5689  auto cookie_wm_delete_window = internAtomRequest("WM_DELETE_WINDOW");
5690  auto cookie_wm_protocols = internAtomRequest("WM_PROTOCOLS");
5691 
5692  xcb_generic_error_t generic_error;
5693 
5694  atom_motif_wm_hints = internAtomReply(cookie_motif_wm_hints , generic_error);
5695  atom_net_frame_extents = internAtomReply(cookie_net_frame_extents , generic_error);
5696  atom_net_wm_state = internAtomReply(cookie_net_wm_state , generic_error);
5697  atom_net_wm_state_fullscreen = internAtomReply(cookie_net_wm_state_fullscreen , generic_error);
5698  atom_net_wm_state_hidden = internAtomReply(cookie_net_wm_state_hidden , generic_error);
5699  atom_net_wm_state_maximized_horz = internAtomReply(cookie_net_wm_state_maximized_horz , generic_error);
5700  atom_net_wm_state_maximized_vert = internAtomReply(cookie_net_wm_state_maximized_vert , generic_error);
5701  atom_wm_change_state = internAtomReply(cookie_wm_change_state , generic_error);
5702  atom_wm_delete_window = internAtomReply(cookie_wm_delete_window , generic_error);
5703  atom_wm_protocols = internAtomReply(cookie_wm_protocols , generic_error);
5704 
5705  if(atom_wm_delete_window == XCB_ATOM_NONE)
5706  {
5707  ZAKERO_XENIUM__DEBUG_ERROR(
5708  ZAKERO_XENIUM__ERROR(Error_Xcb_WM_Delete_Window_Not_Available)
5709  );
5710 
5711  return ZAKERO_XENIUM__ERROR(Error_Xcb_WM_Delete_Window_Not_Available);
5712  }
5713 
5714  if(atom_wm_protocols == XCB_ATOM_NONE)
5715  {
5716  ZAKERO_XENIUM__DEBUG_ERROR(
5717  ZAKERO_XENIUM__ERROR(Error_Xcb_WM_Protocols_Not_Available)
5718  );
5719 
5720  return ZAKERO_XENIUM__ERROR(Error_Xcb_WM_Protocols_Not_Available);
5721  }
5722 
5723  if(atom_net_wm_state == XCB_ATOM_NONE)
5724  {
5725  ZAKERO_XENIUM__DEBUG_ERROR(
5726  ZAKERO_XENIUM__ERROR(Error_Xcb_NETWM_State_Not_Available)
5727  );
5728 
5729  return ZAKERO_XENIUM__ERROR(Error_Xcb_NETWM_State_Not_Available);
5730  }
5731 
5732  if(atom_net_wm_state_fullscreen == XCB_ATOM_NONE)
5733  {
5734  ZAKERO_XENIUM__DEBUG_ERROR(
5735  ZAKERO_XENIUM__ERROR(Error_Xcb_Fullscreen_Not_Available)
5736  );
5737 
5738  return ZAKERO_XENIUM__ERROR(Error_Xcb_Fullscreen_Not_Available);
5739  }
5740 
5741  if(atom_net_wm_state_hidden == XCB_ATOM_NONE)
5742  {
5743  ZAKERO_XENIUM__DEBUG_ERROR(
5744  ZAKERO_XENIUM__ERROR(Error_Xcb_Hidden_Not_Available)
5745  );
5746 
5747  return ZAKERO_XENIUM__ERROR(Error_Xcb_Hidden_Not_Available);
5748  }
5749 
5750  if((atom_net_wm_state_maximized_horz == XCB_ATOM_NONE)
5751  || (atom_net_wm_state_maximized_vert == XCB_ATOM_NONE)
5752  )
5753  {
5754  ZAKERO_XENIUM__DEBUG_ERROR(
5755  ZAKERO_XENIUM__ERROR(Error_Xcb_Maximized_Window_Not_Available)
5756  );
5757 
5758  return ZAKERO_XENIUM__ERROR(Error_Xcb_Maximized_Window_Not_Available);
5759  }
5760 
5761  return ZAKERO_XENIUM__ERROR(Error_None);
5762 }
5763 
5764 
5778 xcb_atom_t Xenium::atomCreateDeleteWindow(const WindowId window_id
5779  , xcb_generic_error_t& generic_error
5780  ) noexcept
5781 {
5782  bool property_was_set = windowPropertySet(window_id
5783  , atom_wm_protocols
5784  , atom_wm_delete_window
5785  , generic_error
5786  );
5787 
5788  if(property_was_set == false)
5789  {
5790  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
5791  return XCB_ATOM_NONE;
5792  }
5793 
5794  return atom_wm_delete_window;
5795 }
5796 
5797 
5809 std::string Xenium::atomName(const xcb_atom_t atom
5810  ) noexcept
5811 {
5812  if(atom == XCB_ATOM_NONE)
5813  {
5814  return "";
5815  }
5816 
5817  xcb_get_atom_name_cookie_t cookie =
5818  xcb_get_atom_name(this->connection, atom);
5819 
5820  xcb_generic_error_t* error = nullptr;
5821 
5822  xcb_get_atom_name_reply_t* reply =
5823  xcb_get_atom_name_reply(this->connection
5824  , cookie
5825  , &error
5826  );
5827 
5828  if(reply == nullptr)
5829  {
5830  return "";
5831  }
5832 
5833  char* name = xcb_get_atom_name_name(reply);
5834 
5835  std::string atom_name(name);
5836 
5837  free(reply);
5838 
5839  return atom_name;
5840 }
5841 
5842 
5855 std::vector<xcb_atom_t> Xenium::atomValueAtom(const WindowId window_id
5856  , const xcb_atom_t property_atom
5857  , xcb_generic_error_t& generic_error
5858  ) noexcept
5859 {
5860  xcb_get_property_cookie_t property_cookie =
5861  xcb_get_property(this->connection
5862  , 0 // Don't Delete
5863  , window_id
5864  , property_atom
5865  , XCB_ATOM_ATOM
5866  , 0 // Data Offset
5867  , 2 // Number of 32-bit values
5868  );
5869 
5870  xcb_generic_error_t* error = nullptr;
5871 
5872  xcb_get_property_reply_t* property =
5873  xcb_get_property_reply(this->connection
5874  , property_cookie
5875  , &error
5876  );
5877 
5878  if(property == nullptr)
5879  {
5880  generic_error = *error;
5881 
5882  return {};
5883  }
5884 
5885  int length = xcb_get_property_value_length(property) / 4;
5886 
5887  std::vector<xcb_atom_t> retval(length);
5888 
5889  xcb_atom_t* value = (xcb_atom_t*)xcb_get_property_value(property);
5890 
5891  for(int i = 0; i < length; i++)
5892  {
5893  retval[i] = value[i];
5894  }
5895 
5896  free(property);
5897 
5898  return retval;
5899 }
5900 
5901 
5919 std::vector<int32_t> Xenium::atomValueData(const WindowId window_id
5920  , const xcb_atom_t property_atom
5921  , const xcb_atom_t type
5922  , const size_t count
5923  , xcb_generic_error_t& generic_error
5924  ) noexcept
5925 {
5926  xcb_get_property_cookie_t property_cookie =
5927  xcb_get_property(this->connection
5928  , 0 // Don't Delete
5929  , window_id
5930  , property_atom
5931  , type
5932  , 0 // Data Offset
5933  , count // Number of 32-bit values
5934  );
5935 
5936  xcb_generic_error_t* error = nullptr;
5937 
5938  xcb_get_property_reply_t* property =
5939  xcb_get_property_reply(this->connection
5940  , property_cookie
5941  , &error
5942  );
5943 
5944  if(property == nullptr)
5945  {
5946  generic_error = *error;
5947 
5948  return {};
5949  }
5950 
5951  int length = xcb_get_property_value_length(property) / 4;
5952 
5953  std::vector<int32_t> vector(length);
5954 
5955  int32_t* value = (int32_t*)xcb_get_property_value(property);
5956 
5957  for(int i = 0; i < length; i++)
5958  {
5959  vector[i] = value[i];
5960  }
5961 
5962  free(property);
5963 
5964  return vector;
5965 }
5966 
5967 
5986 xcb_atom_t Xenium::internAtom(const std::string& atom_name
5987  , const bool create_if_needed
5988  , xcb_generic_error_t& generic_error
5989  ) noexcept
5990 {
5991  xcb_generic_error_t* error = nullptr;
5992 
5993  xcb_intern_atom_reply_t* atom_reply =
5994  xcb_intern_atom_reply(this->connection
5995  , xcb_intern_atom(this->connection
5996  // 0: Create if needed
5997  // 1: result XCB_ATOM_NONE if not exist
5998  , create_if_needed ? 0 : 1
5999  , atom_name.length()
6000  , atom_name.c_str()
6001  )
6002  , &error
6003  );
6004 
6005  xcb_atom_t atom;
6006 
6007  if(error != nullptr)
6008  {
6009  atom = XCB_ATOM_NONE;
6010  generic_error = *error;
6011  free(error);
6012 
6013  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
6014  }
6015  else if(atom_reply->atom == XCB_ATOM_NONE
6016  && atom_name != "XCB_ATOM_NONE"
6017  )
6018  {
6019  ZAKERO_XENIUM__DEBUG << "Error: Failed to get \""
6020  << atom_name
6021  << "\" atom.\n"
6022  ;
6023 
6024  atom = XCB_ATOM_NONE;
6025  }
6026  else
6027  {
6028  atom = atom_reply->atom;
6029  }
6030 
6031  free(atom_reply);
6032 
6033  return atom;
6034 }
6035 
6036 
6051 xcb_intern_atom_cookie_t Xenium::internAtomRequest(const std::string& atom_name
6052  , const bool create_if_needed
6053  ) noexcept
6054 {
6055  xcb_intern_atom_cookie_t cookie =
6056  xcb_intern_atom(this->connection
6057  // 0: Create if needed
6058  // 1: result XCB_ATOM_NONE if not exist
6059  , !create_if_needed
6060  , atom_name.length()
6061  , atom_name.c_str()
6062  );
6063 
6064  return cookie;
6065 }
6066 
6067 
6079 xcb_atom_t Xenium::internAtomReply(const xcb_intern_atom_cookie_t intern_atom_cookie
6080  , xcb_generic_error_t& generic_error
6081  ) noexcept
6082 {
6083  xcb_generic_error_t* error = nullptr;
6084 
6085  xcb_intern_atom_reply_t* atom_reply =
6086  xcb_intern_atom_reply(this->connection
6087  , intern_atom_cookie
6088  , &error
6089  );
6090 
6091  xcb_atom_t atom;
6092 
6093  if(error != nullptr)
6094  {
6095  atom = XCB_ATOM_NONE;
6096  generic_error = *error;
6097  free(error);
6098 
6099  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
6100  }
6101  else if(atom_reply->atom == XCB_ATOM_NONE)
6102  {
6103  ZAKERO_XENIUM__DEBUG << "Error: Failed to get atom.\n";
6104 
6105  atom = XCB_ATOM_NONE;
6106  }
6107  else
6108  {
6109  atom = atom_reply->atom;
6110  }
6111 
6112  free(atom_reply);
6113 
6114  return atom;
6115 }
6116 
6117 // }}}
6118 // {{{ XCB : RandR
6119 
6128 std::error_code Xenium::randrInit() noexcept
6129 {
6130  const xcb_query_extension_reply_t* randr =
6131  xcb_get_extension_data(this->connection, &xcb_randr_id);
6132 
6133  if(randr->present == 0)
6134  {
6135  ZAKERO_XENIUM__DEBUG_ERROR(
6136  ZAKERO_XENIUM__ERROR(Error_RandR_Not_Available)
6137  );
6138 
6139  return ZAKERO_XENIUM__ERROR(Error_RandR_Not_Available);
6140  }
6141 
6142  randr_error_base = randr->first_error;
6143  randr_event_base = randr->first_event;
6144 
6145  xcb_generic_error_t* xcb_generic_error = nullptr;
6146 
6147  xcb_randr_query_version_reply_t* randr_query_version =
6148  xcb_randr_query_version_reply(this->connection
6149  , xcb_randr_query_version(this->connection
6150  , std::numeric_limits<uint32_t>::max()
6151  , std::numeric_limits<uint32_t>::max()
6152  )
6153  , &xcb_generic_error
6154  );
6155 
6156  randr_query_version_major = randr_query_version->major_version;
6157  randr_query_version_minor = randr_query_version->minor_version;
6158 
6159  ZAKERO_FREE(randr_query_version);
6160 
6161  if(randr_query_version_major < 1
6162  || (randr_query_version_major == 1
6163  && randr_query_version_minor < 1
6164  )
6165  )
6166  {
6167  ZAKERO_XENIUM__DEBUG_ERROR(
6168  ZAKERO_XENIUM__ERROR(Error_RandR_Version_Too_Old)
6169  );
6170 
6171  return ZAKERO_XENIUM__ERROR(Error_RandR_Version_Too_Old);
6172  }
6173 
6174  xcb_randr_select_input(this->connection
6175  , this->screen->root
6176  , 0
6177  | XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE
6178  | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE
6179  /* Might be of future use
6180  | XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE
6181  | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY
6182  | XCB_RANDR_NOTIFY_MASK_PROVIDER_CHANGE
6183  | XCB_RANDR_NOTIFY_MASK_PROVIDER_PROPERTY
6184  | XCB_RANDR_NOTIFY_MASK_RESOURCE_CHANGE
6185  */
6186  );
6187 
6188  return ZAKERO_XENIUM__ERROR(Error_None);
6189 }
6190 
6191 
6195 void Xenium::randrEvent(const xcb_randr_crtc_change_t* event
6196  ) noexcept
6197 {
6198 //std::cout << "RandR CRTC Chang:" << *event << '\n';
6199  OutputId output_id = event->crtc;
6200 
6201  if(this->screen->root != event->window
6202  || output_map.contains(output_id) == false
6203  )
6204  {
6205  return;
6206  }
6207 
6208  Output& output = output_map[output_id];
6209 
6210  if(output.x == event->x
6211  && output.y == event->y
6212  && output.width == event->width
6213  && output.height == event->height
6214  && output.transform == event->rotation
6215  )
6216  {
6217  return;
6218  }
6219 
6220  output.x = event->x;
6221  output.y = event->y;
6222  output.width = event->width;
6223  output.height = event->height;
6224  output.transform = event->rotation;
6225 
6226  output.pixels_per_mm_horizontal = (float)event->width / output.physical_width_mm;
6227  output.pixels_per_mm_vertical = (float)event->height / output.physical_height_mm;
6228 
6229  output_on_change(output_id);
6230 }
6231 
6232 
6236 void Xenium::randrEvent(const xcb_randr_output_change_t* event
6237  ) noexcept
6238 {
6239 //std::cout << "RandR Output Chg:" << *event << '\n';
6240 
6241  if(this->screen->root != event->window)
6242  {
6243  return;
6244  }
6245 
6246  OutputId output_id = event->crtc;
6247 
6248  if(event->connection == XCB_RANDR_CONNECTION_DISCONNECTED)
6249  {
6250  if(output_map.contains(output_id))
6251  {
6252  output_on_remove(output_id);
6253  }
6254 
6255  std::lock_guard<std::mutex> lock(output_mutex);
6256 
6257  output_map.erase(output_id);
6258  }
6259  else if((event->connection == XCB_RANDR_CONNECTION_CONNECTED)
6260  || (output_map.contains(output_id) == false)
6261  )
6262  {
6263  std::error_code error;
6264  {
6265  std::lock_guard<std::mutex> lock(output_mutex);
6266 
6267  error = outputAdd(event->crtc, event->output);
6268  }
6269 
6270  if(!error)
6271  {
6272  output_on_add(output_id);
6273  }
6274 # if ZAKERO_XENIUM__DEBUG_ENABLED
6275  else
6276  {
6277  ZAKERO_XENIUM__DEBUG_ERROR(error);
6278  }
6279 # endif
6280 
6281  }
6282  else
6283  {
6284  Output& output = output_map[output_id];
6285 
6286  if(output.subpixel != event->subpixel_order
6287  || output.transform != event->rotation
6288  )
6289  {
6290  output.subpixel = event->subpixel_order;
6291  output.transform = event->rotation;
6292 
6293  output_on_change(output_id);
6294  }
6295  }
6296 }
6297 
6298 
6305 void Xenium::randrEvent(const xcb_randr_notify_event_t* event
6306  ) noexcept
6307 {
6308  switch(event->subCode)
6309  {
6310  case XCB_RANDR_NOTIFY_CRTC_CHANGE:
6311  randrEvent(&event->u.cc);
6312  break;
6313  case XCB_RANDR_NOTIFY_OUTPUT_CHANGE:
6314  randrEvent(&event->u.oc);
6315  break;
6316  case XCB_RANDR_NOTIFY_OUTPUT_PROPERTY:
6317  // Not Used
6318  break;
6319  case XCB_RANDR_NOTIFY_PROVIDER_CHANGE:
6320  // Not Used
6321  break;
6322  case XCB_RANDR_NOTIFY_PROVIDER_PROPERTY:
6323  // Not Used
6324  break;
6325  case XCB_RANDR_NOTIFY_RESOURCE_CHANGE:
6326  // Not Used
6327  break;
6328  case XCB_RANDR_NOTIFY_LEASE:
6329  // Not Used
6330  break;
6331  default:
6332  fprintf(stderr, "Unhandled Sub-Event %d\n", event->subCode);
6333  }
6334 }
6335 
6336 
6340 void Xenium::randrEvent(const xcb_randr_screen_change_notify_event_t* //event ///< The event
6341  ) noexcept
6342 {
6343 //std::cout << "RandR ScrnChange:" << to_string(*event) << '\n';
6344 }
6345 
6346 // }}}
6347 // {{{ XCB : XKB
6348 
6356 std::error_code Xenium::xkbInit() noexcept
6357 {
6358  xcb_xkb_use_extension_reply_t* extension_reply =
6359  xcb_xkb_use_extension_reply(this->connection
6360  , xcb_xkb_use_extension(this->connection, 1, 0)
6361  , nullptr
6362  );
6363 
6364  if(extension_reply == nullptr)
6365  {
6366  ZAKERO_XENIUM__DEBUG_ERROR(
6367  ZAKERO_XENIUM__ERROR(Error_Xcb_Xkb_Not_Available)
6368  );
6369 
6370  return ZAKERO_XENIUM__ERROR(Error_Xcb_Xkb_Not_Available);
6371  }
6372 
6373  free(extension_reply);
6374 
6375  return ZAKERO_XENIUM__ERROR(Error_None);
6376 }
6377 
6378 
6386 void Xenium::keyDataArrayClear() noexcept
6387 {
6388  const auto time_now = ZAKERO_STEADY_TIME_NOW(milliseconds);
6389 
6390  for(auto& key_data : key_data_array)
6391  {
6392  Key& key = key_data.key;
6393 
6394  if(key.time == 0)
6395  {
6396  continue;
6397  }
6398 
6399  key.state = KeyState::Released;
6400  key.time = time_now;
6401 
6402  const KeyModifier& modifier = key_data.modifier;
6403  const WindowId& window_id = key_data.window_id;
6404 
6405  window_on_key_map[window_id](key, modifier);
6406 
6407  key.time = 0;
6408  }
6409 }
6410 
6411 
6420 void Xenium::keyDataArrayProcess() noexcept
6421 {
6422  for(auto& key_data : key_data_array)
6423  {
6424  Key& key = key_data.key;
6425 
6426  if(key.time == 0)
6427  {
6428  continue;
6429  }
6430 
6431  if(key.state == KeyState::Pressed)
6432  {
6433  key.state = KeyState::Repeat;
6434 
6435  continue;
6436  }
6437 
6438  const WindowId& window_id = key_data.window_id;
6439 
6440  if(key.state == KeyState::Released)
6441  {
6442  window_on_key_map[window_id](key, key_data.modifier);
6443  key.time = 0;
6444 
6445  continue;
6446  }
6447 
6448  auto& repeat_time = key_data.repeat_time;
6449 
6450  const auto time_now = ZAKERO_STEADY_TIME_NOW(milliseconds);
6451 
6452  if(key.state == KeyState::Repeat
6453  && repeat_time < time_now
6454  )
6455  {
6456  window_on_key_map[window_id](key, key_modifier);
6457  key.time = repeat_time;
6458  repeat_time += xkb_controls.repeat_interval_ms;
6459 
6460  continue;
6461  }
6462  }
6463 }
6464 
6465 
6469 void Xenium::xkbControlsUpdate() noexcept
6470 {
6471  xcb_xkb_get_controls_reply_t* controls_reply =
6472  xcb_xkb_get_controls_reply(this->connection
6473  , xcb_xkb_get_controls(this->connection
6474  , XCB_XKB_ID_USE_CORE_KBD
6475  )
6476  , nullptr
6477  );
6478 
6479  if(controls_reply == nullptr)
6480  {
6481  return;
6482  }
6483 
6484  xkb_controls.repeat_delay_ms = controls_reply->repeatDelay;
6485  xkb_controls.repeat_interval_ms = controls_reply->repeatInterval;
6486 
6487  free(controls_reply);
6488 }
6489 
6490 
6494 void Xenium::xkbIndicatorStateUpdate() noexcept
6495 {
6496  xcb_xkb_get_indicator_state_reply_t* reply =
6497  xcb_xkb_get_indicator_state_reply(this->connection
6498  , xcb_xkb_get_indicator_state(this->connection
6499  , XCB_XKB_ID_USE_CORE_KBD
6500  )
6501  , nullptr
6502  );
6503 
6504  if(reply == nullptr)
6505  {
6506  return;
6507  }
6508 
6509  const uint32_t state = reply->state;
6510  const uint32_t caps_lock = KeyModifier_CapsLock;
6511  const uint32_t num_lock = KeyModifier_NumLock;
6512 
6513  key_modifier.locked = 0
6514  | (bool(state & XCB_XKB_INDICATOR_STATE_CAPSLOCK) * caps_lock)
6515  | (bool(state & XCB_XKB_INDICATOR_STATE_NUMLOCK) * num_lock)
6516  ;
6517 
6518  free(reply);
6519 
6520  return;
6521 }
6522 
6523 // }}}
6524 // {{{ XCB : Utility
6525 
6537 bool Xenium::requestCheckHasError(const xcb_void_cookie_t& void_cookie
6538  , xcb_generic_error_t& generic_error
6539  ) noexcept
6540 {
6541  xcb_generic_error_t* error = xcb_request_check(this->connection, void_cookie);
6542 
6543  if(error != nullptr)
6544  {
6545  std::cout << "requestCheck Error: " << to_string(*error) << '\n';
6546 
6547  generic_error = *error;
6548 
6549  free(error);
6550 
6551  return true;
6552  }
6553 
6554  return false;
6555 }
6556 
6557 // }}}
6558 // {{{ Class Window : Documentation
6559 
6636 // }}}
6637 // {{{ Class Window : Constructor / Destructor
6638 
6659  , void* data
6660  )
6661  : xenium(xenium)
6662  , frame_buffer(nullptr)
6663  , frame_buffer_size{0, 0}
6664  , window_id(((WindowCreateData*)data)->window_id)
6665  , gc(((WindowCreateData*)data)->gc)
6666  , frame_buffer_length(0)
6667  , frame_time(0)
6668 {
6669 }
6670 
6671 
6676 {
6677  Xenium::WindowDestroyData data =
6678  { .barrier = {}
6679  , .window_id = this->window_id
6680  , .gc = this->gc
6681  };
6682 
6683  std::future<void> barrier = data.barrier.get_future();
6684 
6685  xenium->windowDestroyAddToQueue(&data);
6686 
6687  barrier.wait();
6688 
6689  ZAKERO_FREE(frame_buffer);
6690 
6691  window_id = 0;
6692  xenium = nullptr;
6693 }
6694 
6695 // }}}
6696 // {{{ Class Window : Configuration
6697 
6714 void Xenium::Window::classSet(const std::string& class_name
6715  ) noexcept
6716 {
6717  bool success;
6718  xcb_generic_error_t generic_error;
6719 
6720  success = xenium->windowPropertySet(window_id
6721  , XCB_ATOM_WM_CLASS
6722  , class_name
6723  , generic_error
6724  );
6725 
6726  if(success == false)
6727  {
6728  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
6729  }
6730 }
6731 
6732 
6739 void Xenium::Window::titleSet(const std::string& title
6740  ) noexcept
6741 {
6742  bool success;
6743  xcb_generic_error_t generic_error;
6744 
6745  success = xenium->windowPropertySet(window_id
6746  , XCB_ATOM_WM_NAME
6747  , title
6748  , generic_error
6749  );
6750 
6751  if(success == false)
6752  {
6753  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
6754  }
6755 }
6756 
6757 // }}}
6758 // {{{ Class Window : Events
6759 
6772  ) noexcept
6773 {
6774  WindowDeleteData& window_delete = xenium->window_delete_map[window_id];
6775 
6776  if(lambda == nullptr)
6777  {
6778  window_delete.close_request_lambda = Lambda_DoNothing;
6779  }
6780  else
6781  {
6782  window_delete.close_request_lambda = lambda;
6783  }
6784 }
6785 
6786 
6801  ) noexcept
6802 {
6803  if(lambda == nullptr)
6804  {
6805  xenium->window_focus_map[window_id] = LambdaBool_DoNothing;
6806  }
6807  else
6808  {
6809  xenium->window_focus_map[window_id] = lambda;
6810  }
6811 }
6812 
6813 // }}}
6814 // {{{ Class Window : Decorations
6815 
6832  ) noexcept
6833 {
6834  std::error_code error;
6835 
6836  if(decorations == WindowDecorations::Client_Side)
6837  {
6838  // The application will render the borders
6839  error = xenium->windowBorder(window_id, false);
6840  }
6841  else
6842  {
6843  // The X11 will render the borders
6844  error = xenium->windowBorder(window_id, true);
6845  }
6846 
6847 # if ZAKERO_XENIUM__DEBUG_ENABLED
6848  if(error)
6849  {
6850  ZAKERO_XENIUM__DEBUG_ERROR(error);
6851  }
6852 # endif
6853 
6854  return error;
6855 }
6856 
6857 
6866  ) noexcept
6867 {
6868  WindowDecorationsData& window_decorations = xenium->window_decorations_map[window_id];
6869 
6870  if(lambda)
6871  {
6872  window_decorations.lambda = lambda;
6873  }
6874  else
6875  {
6876  window_decorations.lambda = LambdaWindowDecorations_DoNothing;
6877  }
6878 }
6879 
6880 // }}}
6881 // {{{ Class Window : Size
6882 
6900 std::error_code Xenium::Window::sizeSet(const Xenium::SizeMm& size
6901  ) noexcept
6902 {
6903  std::lock_guard lock(xenium->output_mutex);
6904 
6905  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
6906 
6907  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
6908  window_size.unit = SizeUnit::Millimeter;
6909 
6910  auto pixel = xenium->convertMmToPixel(output
6911  , size.width
6912  , size.height
6913  );
6914 
6915  if(pixel.first < Window_Size_Minimum
6916  || pixel.second < Window_Size_Minimum
6917  )
6918  {
6919  return ZAKERO_XENIUM__ERROR(Error_Window_Size_Too_Small);
6920  }
6921 
6922  if(pixel.first == window_size.pixel.width
6923  && pixel.second == window_size.pixel.height
6924  )
6925  {
6926  return ZAKERO_XENIUM__ERROR(Error_None);
6927  }
6928 
6929  SizePixel size_pixel = {pixel.first, pixel.second};
6930 
6931  std::error_code error = xenium->windowSizeSet(window_id, size_pixel);
6932 
6933 # if ZAKERO_XENIUM__DEBUG_ENABLED
6934  if(error)
6935  {
6936  ZAKERO_XENIUM__DEBUG_ERROR(error);
6937  }
6938 # endif
6939 
6940  return error;
6941 }
6942 
6943 
6961 std::error_code Xenium::Window::sizeSet(const Xenium::SizePercent& size
6962  ) noexcept
6963 {
6964  std::lock_guard lock(xenium->output_mutex);
6965 
6966  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
6967 
6968  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
6969  window_size.unit = SizeUnit::Percent;
6970 
6971  auto pixel = xenium->convertPercentToPixel(output
6972  , size.width
6973  , size.height
6974  );
6975 
6976  if(pixel.first < Window_Size_Minimum
6977  || pixel.second < Window_Size_Minimum
6978  )
6979  {
6980  return ZAKERO_XENIUM__ERROR(Error_Window_Size_Too_Small);
6981  }
6982 
6983  if(pixel.first == window_size.pixel.width
6984  && pixel.second == window_size.pixel.height
6985  )
6986  {
6987  return ZAKERO_XENIUM__ERROR(Error_None);
6988  }
6989 
6990  SizePixel size_pixel = {pixel.first, pixel.second};
6991 
6992  std::error_code error = xenium->windowSizeSet(window_id, size_pixel);
6993 
6994 # if ZAKERO_XENIUM__DEBUG_ENABLED
6995  if(error)
6996  {
6997  ZAKERO_XENIUM__DEBUG_ERROR(error);
6998  }
6999 # endif
7000 
7001  return error;
7002 }
7003 
7004 
7021 std::error_code Xenium::Window::sizeSet(const Xenium::SizePixel& size
7022  ) noexcept
7023 {
7024  if(size.width < Window_Size_Minimum
7025  || size.height < Window_Size_Minimum
7026  )
7027  {
7028  return ZAKERO_XENIUM__ERROR(Error_Window_Size_Too_Small);
7029  }
7030 
7031  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7032  window_size.unit = Xenium::SizeUnit::Pixel;
7033 
7034  if(window_size.pixel.width == size.width
7035  && window_size.pixel.height == size.height
7036  )
7037  {
7038  return ZAKERO_XENIUM__ERROR(Error_None);
7039  }
7040 
7041  std::error_code error = xenium->windowSizeSet(window_id, size);
7042 
7043 # if ZAKERO_XENIUM__DEBUG_ENABLED
7044  if(error)
7045  {
7046  ZAKERO_XENIUM__DEBUG_ERROR(error);
7047  }
7048 # endif
7049 
7050  return error;
7051 }
7052 
7053 
7065 std::error_code Xenium::Window::sizeSetMinMax(const Xenium::SizeMm& size_min
7066  , const Xenium::SizeMm& size_max
7067  ) noexcept
7068 {
7069  std::error_code error;
7070 
7071  error = validateMinMax<Xenium::SizeMm>(size_min, size_max);
7072  if(error)
7073  {
7074  ZAKERO_XENIUM__DEBUG_ERROR(error);
7075 
7076  return error;
7077  }
7078 
7079  std::lock_guard lock(xenium->output_mutex);
7080 
7081  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7082 
7083  window_size.unit = Xenium::SizeUnit::Millimeter;
7084  window_size.mm_minimum = size_min;
7085  window_size.mm_maximum = size_max;
7086 
7087  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7088 
7089  error = xenium->windowSizeSetMinMax(output, window_id, window_size);
7090 
7091 # if ZAKERO_XENIUM__DEBUG_ENABLED
7092  if(error)
7093  {
7094  ZAKERO_XENIUM__DEBUG_ERROR(error);
7095  }
7096 # endif
7097 
7098  return error;
7099 }
7100 
7101 
7113 std::error_code Xenium::Window::sizeSetMinMax(const Xenium::SizePercent& size_min
7114  , const Xenium::SizePercent& size_max
7115  ) noexcept
7116 {
7117  std::error_code error;
7118 
7119  error = validateMinMax<Xenium::SizePercent>(size_min, size_max);
7120  if(error)
7121  {
7122  ZAKERO_XENIUM__DEBUG_ERROR(error);
7123 
7124  return error;
7125  }
7126 
7127  std::lock_guard lock(xenium->output_mutex);
7128 
7129  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7130 
7131  window_size.unit = Xenium::SizeUnit::Percent;
7132  window_size.percent_minimum = size_min;
7133  window_size.percent_maximum = size_max;
7134 
7135  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7136 
7137  error = xenium->windowSizeSetMinMax(output, window_id, window_size);
7138 
7139 # if ZAKERO_XENIUM__DEBUG_ENABLED
7140  if(error)
7141  {
7142  ZAKERO_XENIUM__DEBUG_ERROR(error);
7143  }
7144 # endif
7145 
7146  return error;
7147 }
7148 
7149 
7161 std::error_code Xenium::Window::sizeSetMinMax(const Xenium::SizePixel& size_min
7162  , const Xenium::SizePixel& size_max
7163  ) noexcept
7164 {
7165  std::error_code error;
7166 
7167  error = validateMinMax<Xenium::SizePixel>(size_min, size_max);
7168  if(error)
7169  {
7170  ZAKERO_XENIUM__DEBUG_ERROR(error);
7171 
7172  return error;
7173  }
7174 
7175  std::lock_guard lock(xenium->output_mutex);
7176 
7177  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7178 
7179  window_size.unit = Xenium::SizeUnit::Pixel;
7180  window_size.pixel_minimum = size_min;
7181  window_size.pixel_maximum = size_max;
7182 
7183  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7184 
7185  error = xenium->windowSizeSetMinMax(output, window_id, window_size);
7186 
7187 # if ZAKERO_XENIUM__DEBUG_ENABLED
7188  if(error)
7189  {
7190  ZAKERO_XENIUM__DEBUG_ERROR(error);
7191  }
7192 # endif
7193 
7194  return error;
7195 }
7196 
7197 
7212  ) noexcept
7213 {
7214  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7215 
7216  if(lambda)
7217  {
7218  window_size.mm_lambda = lambda;
7219  }
7220  else
7221  {
7222  window_size.mm_lambda = LambdaSizeMm_DoNothing;
7223  }
7224 }
7225 
7226 
7241  ) noexcept
7242 {
7243  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7244 
7245  if(lambda)
7246  {
7247  window_size.percent_lambda = lambda;
7248  }
7249  else
7250  {
7251  window_size.percent_lambda = LambdaSizePercent_DoNothing;
7252  }
7253 }
7254 
7255 
7270  ) noexcept
7271 {
7272  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7273 
7274  if(lambda)
7275  {
7276  window_size.pixel_lambda = lambda;
7277  }
7278  else
7279  {
7280  window_size.pixel_lambda = LambdaSizePixel_DoNothing;
7281  }
7282 }
7283 
7284 // }}}
7285 // {{{ Class Window : Conversion
7286 
7295  ) const noexcept
7296 {
7297  std::lock_guard lock(xenium->output_mutex);
7298 
7299  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7300 
7301  auto value = xenium->convertPixelToMm(output, point.x, point.y);
7302 
7303  return {0, value.first, value.second};
7304 }
7305 
7306 
7315  ) const noexcept
7316 {
7317  std::lock_guard lock(xenium->output_mutex);
7318 
7319  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7320 
7321  auto value = xenium->convertPixelToPercent(output, point.x, point.y);
7322 
7323  return {0, value.first, value.second};
7324 }
7325 
7326 
7335  ) const noexcept
7336 {
7337  std::lock_guard lock(xenium->output_mutex);
7338 
7339  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7340 
7341  auto value = xenium->convertMmToPixel(output, point.x, point.y);
7342 
7343  return {0, value.first, value.second};
7344 }
7345 
7346 
7355  ) const noexcept
7356 {
7357  std::lock_guard lock(xenium->output_mutex);
7358 
7359  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7360 
7361  auto value = xenium->convertPercentToPixel(output, point.x, point.y);
7362 
7363  return {0, value.first, value.second};
7364 
7365 }
7366 
7367 
7376  ) const noexcept
7377 {
7378  std::lock_guard lock(xenium->output_mutex);
7379 
7380  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7381 
7382  auto value = xenium->convertPixelToMm(output, size.width, size.height);
7383 
7384  return {value.first, value.second};
7385 }
7386 
7387 
7396  ) const noexcept
7397 {
7398  std::lock_guard lock(xenium->output_mutex);
7399 
7400  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7401 
7402  auto value = xenium->convertPixelToPercent(output, size.width, size.height);
7403 
7404  return {value.first, value.second};
7405 }
7406 
7407 
7416  ) const noexcept
7417 {
7418  std::lock_guard lock(xenium->output_mutex);
7419 
7420  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7421 
7422  auto value = xenium->convertMmToPixel(output, size.width, size.height);
7423 
7424  return {value.first, value.second};
7425 }
7426 
7427 
7436  ) const noexcept
7437 {
7438  std::lock_guard lock(xenium->output_mutex);
7439 
7440  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7441 
7442  auto value = xenium->convertPercentToPixel(output, size.width, size.height);
7443 
7444  return {value.first, value.second};
7445 }
7446 
7447 // }}}
7448 // {{{ Class Window : Window Mode
7449 
7458  ) const noexcept
7459 {
7460  return xenium->window_mode_map[window_id].window_mode;
7461 }
7462 
7463 
7473  ) const noexcept
7474 {
7475  return (window_mode == this->windowMode());
7476 }
7477 
7478 
7489 std::error_code Xenium::Window::windowModeSet(const Xenium::WindowMode window_mode
7490  ) noexcept
7491 {
7492  Xenium::WindowModeData& data = xenium->window_mode_map[window_id];
7493 
7494  if(data.window_mode == window_mode)
7495  {
7496  return ZAKERO_XENIUM__ERROR(Error_None);
7497  }
7498 
7499  std::error_code error = xenium->windowModeSet(window_id
7500  , data.window_mode // current window mode
7501  , window_mode // new window mode
7502  );
7503 
7504  if(error)
7505  {
7506  ZAKERO_XENIUM__DEBUG_ERROR(error);
7507 
7508  return error;
7509  }
7510 
7511  data.window_mode = window_mode;
7512 
7513  return ZAKERO_XENIUM__ERROR(Error_None);
7514 }
7515 
7516 
7524  ) noexcept
7525 {
7526  Xenium::WindowModeData& data = xenium->window_mode_map[window_id];
7527 
7528  if(lambda == nullptr)
7529  {
7530  data.lambda = LambdaWindowMode_DoNothing;
7531  }
7532  else
7533  {
7534  data.lambda = lambda;
7535  }
7536 }
7537 
7538 
7548 std::error_code Xenium::Window::minimize() noexcept
7549 {
7550  std::error_code error = xenium->windowMinimize(window_id);
7551 
7552 # if ZAKERO_XENIUM__DEBUG_ENABLED
7553  if(error)
7554  {
7555  ZAKERO_XENIUM__DEBUG_ERROR(error);
7556  }
7557 # endif
7558 
7559  return error;
7560 }
7561 
7562 // }}}
7563 // {{{ Class Window : Cursor
7564 
7565 // }}}
7566 // {{{ Class Window : Keyboard
7567 
7574  ) noexcept
7575 {
7576  if(lambda == nullptr)
7577  {
7578  xenium->window_keyboard[window_id].on_enter = Lambda_DoNothing;
7579  }
7580  else
7581  {
7582  xenium->window_keyboard[window_id].on_enter = lambda;
7583  }
7584 }
7585 
7586 
7593  ) noexcept
7594 {
7595  if(lambda == nullptr)
7596  {
7597  xenium->window_keyboard[window_id].on_leave = Lambda_DoNothing;
7598  }
7599  else
7600  {
7601  xenium->window_keyboard[window_id].on_leave = lambda;
7602  }
7603 }
7604 
7605 
7614  ) noexcept
7615 {
7616  if(lambda == nullptr)
7617  {
7618  xenium->window_on_key_map[window_id] = LambdaKey_DoNothing;
7619  }
7620  else
7621  {
7622  xenium->window_on_key_map[window_id] = lambda;
7623  }
7624 }
7625 
7626 // }}}
7627 // {{{ Class Window : Pointer
7628 
7636  ) noexcept
7637 {
7638  if(lambda == nullptr)
7639  {
7640  xenium->window_on_axis_map[window_id] = LambdaAxis_DoNothing;
7641  }
7642  else
7643  {
7644  xenium->window_on_axis_map[window_id] = lambda;
7645  }
7646 }
7647 
7648 
7657  ) noexcept
7658 {
7659  WindowOnButtonData& on_button = xenium->window_on_button_map[window_id];
7660 
7661  if(lambda == nullptr)
7662  {
7663  on_button.lambda_mm = LambdaButtonMm_DoNothing;
7664  }
7665  else
7666  {
7667  on_button.lambda_mm = lambda;
7668  }
7669 }
7670 
7671 
7680  ) noexcept
7681 {
7682  WindowOnButtonData& on_button = xenium->window_on_button_map[window_id];
7683 
7684  if(lambda == nullptr)
7685  {
7686  on_button.lambda_percent = LambdaButtonPercent_DoNothing;
7687  }
7688  else
7689  {
7690  on_button.lambda_percent = lambda;
7691  }
7692 
7693 }
7694 
7695 
7704  ) noexcept
7705 {
7706  WindowOnButtonData& on_button = xenium->window_on_button_map[window_id];
7707 
7708  if(lambda == nullptr)
7709  {
7710  on_button.lambda_pixel = LambdaButtonPixel_DoNothing;
7711  }
7712  else
7713  {
7714  on_button.lambda_pixel = lambda;
7715  }
7716 }
7717 
7718 
7730  ) noexcept
7731 {
7732  WindowOnEnterData& on_enter = xenium->window_on_enter_map[window_id];
7733 
7734  if(lambda == nullptr)
7735  {
7736  on_enter.lambda_mm = LambdaPointMm_DoNothing;
7737  }
7738  else
7739  {
7740  on_enter.lambda_mm = lambda;
7741  }
7742 }
7743 
7744 
7756  ) noexcept
7757 {
7758  WindowOnEnterData& on_enter = xenium->window_on_enter_map[window_id];
7759 
7760  if(lambda == nullptr)
7761  {
7762  on_enter.lambda_percent = LambdaPointPercent_DoNothing;
7763  }
7764  else
7765  {
7766  on_enter.lambda_percent = lambda;
7767  }
7768 }
7769 
7770 
7782  ) noexcept
7783 {
7784  WindowOnEnterData& on_enter = xenium->window_on_enter_map[window_id];
7785 
7786  if(lambda == nullptr)
7787  {
7788  on_enter.lambda_pixel = LambdaPointPixel_DoNothing;
7789  }
7790  else
7791  {
7792  on_enter.lambda_pixel = lambda;
7793  }
7794 }
7795 
7796 
7805  ) noexcept
7806 {
7807  if(lambda == nullptr)
7808  {
7809  xenium->window_on_leave_map[window_id] = Lambda_DoNothing;
7810  }
7811  else
7812  {
7813  xenium->window_on_leave_map[window_id] = lambda;
7814  }
7815 }
7816 
7817 
7826  ) noexcept
7827 {
7828  WindowOnMotionData& on_motion = xenium->window_on_motion_map[window_id];
7829 
7830  if(lambda == nullptr)
7831  {
7832  on_motion.lambda_mm = LambdaPointMm_DoNothing;
7833  }
7834  else
7835  {
7836  on_motion.lambda_mm = lambda;
7837  }
7838 }
7839 
7840 
7849  ) noexcept
7850 {
7851  WindowOnMotionData& on_motion = xenium->window_on_motion_map[window_id];
7852 
7853  if(lambda == nullptr)
7854  {
7855  on_motion.lambda_percent = LambdaPointPercent_DoNothing;
7856  }
7857  else
7858  {
7859  on_motion.lambda_percent = lambda;
7860  }
7861 }
7862 
7863 
7872  ) noexcept
7873 {
7874  WindowOnMotionData& on_motion = xenium->window_on_motion_map[window_id];
7875 
7876  if(lambda == nullptr)
7877  {
7878  on_motion.lambda_pixel = LambdaPointPixel_DoNothing;
7879  }
7880  else
7881  {
7882  on_motion.lambda_pixel = lambda;
7883  }
7884 }
7885 
7886 
7887 /*
7888  * \brief Respond to "Pointer Axis Source" events.
7889  *
7890  * The provided \p lambda will be called when the "Pointer Axis Source" events
7891  * occur.
7892 void Xenium::Window::pointerOnAxisSource(Xenium::Lambda lambda ///< The lambda
7893  ) noexcept
7894 {
7895  ZAKERO_UNUSED(lambda);
7896 }
7897  */
7898 
7899 
7900 /*
7901  * \brief Respond to "Pointer Axis Stop" events.
7902  *
7903  * The provided \p lambda will be called when the "Pointer Axis Stop" events
7904  * occur.
7905 void Xenium::Window::pointerOnAxisStop(Xenium::Lambda lambda ///< The lambda
7906  ) noexcept
7907 {
7908  ZAKERO_UNUSED(lambda);
7909 }
7910  */
7911 
7912 
7913 /*
7914  * \brief Respond to "Pointer Axis Discrete" events.
7915  *
7916  * The provided \p lambda will be called when the "Pointer Axis Discrete"
7917  * events occur.
7918 void Xenium::Window::pointerOnAxisDiscrete(Xenium::Lambda lambda ///< The lambda
7919  ) noexcept
7920 {
7921  ZAKERO_UNUSED(lambda);
7922 }
7923  */
7924 
7925 // }}}
7926 // {{{ Class Window : Rendering
7927 
7964 std::error_code Xenium::Window::imageNext(uint8_t*& image
7965  , Xenium::SizePixel& size
7966  ) noexcept
7967 {
7968  if(frame_buffer)
7969  {
7970  free(frame_buffer);
7971  }
7972 
7973  frame_buffer_size = xenium->window_size_map[window_id].pixel;
7974  frame_buffer_length = frame_buffer_size.width * frame_buffer_size.height * 4;
7975  frame_buffer = (uint8_t*)malloc(sizeof(uint8_t) * frame_buffer_length);
7976 
7977  image = frame_buffer;
7978  size = frame_buffer_size;
7979 
7980  return ZAKERO_XENIUM__ERROR(Error_None);
7981 }
7982 
7983 
7991 {
7992  /*
7993  Xenium::SizePixel size = xenium->window_size_map[window_id].pixel;
7994  if(size.width != frame_buffer_size.width
7995  || size.height != frame_buffer_size.height
7996  )
7997  {
7998  return;
7999  }
8000  */
8001 
8002  xcb_put_image(xenium->connection
8003  , XCB_IMAGE_FORMAT_Z_PIXMAP
8004  , window_id
8005  , gc
8006  , frame_buffer_size.width
8007  , frame_buffer_size.height
8008  , 0
8009  , 0
8010  , 0
8011  , xenium->screen->root_depth
8012  , frame_buffer_length
8013  , frame_buffer
8014  );
8015 
8016  frame_time = ZAKERO_STEADY_TIME_NOW(milliseconds);
8017 }
8018 
8019 
8031 uint32_t Xenium::Window::time() const noexcept
8032 {
8033  return frame_time;
8034 }
8035 
8036 
8045 uint8_t Xenium::Window::bytesPerPixel() const noexcept
8046 {
8047  return 4; // ARGB8888
8048 }
8049 
8050 // }}}
8051 // {{{ Convenience
8052 
8058 std::string to_string(const xcb_generic_error_t& generic_error
8059  ) noexcept
8060 {
8061  return std::string()
8062  + "{ \"response_type\": " + std::to_string(generic_error.response_type)
8063  + ", \"error_code\": " + std::to_string(generic_error.error_code)
8064  + ", \"sequence\": " + std::to_string(generic_error.sequence)
8065  + ", \"resource_id\": " + std::to_string(generic_error.resource_id)
8066  + ", \"minor_code\": " + std::to_string(generic_error.minor_code)
8067  + ", \"major_code\": " + std::to_string(generic_error.major_code)
8068  + ", \"full_sequence\": " + std::to_string(generic_error.full_sequence)
8069  + " }";
8070 }
8071 
8072 
8078 std::string to_string(const xcb_randr_screen_change_notify_event_t& event
8079  ) noexcept
8080 {
8081  return std::string()
8082  + "{ \"response_type\": " + std::to_string(event.response_type)
8083  + ", \"rotation\": " + std::to_string(event.rotation)
8084  + ", \"sequence\": " + std::to_string(event.sequence)
8085  + ", \"timestamp\": " + std::to_string(event.timestamp)
8086  + ", \"config_timestamp\": " + std::to_string(event.config_timestamp)
8087  + ", \"root\": " + std::to_string(event.root)
8088  + ", \"request_window\": " + std::to_string(event.request_window)
8089  + ", \"sizeID\": " + std::to_string(event.sizeID)
8090  + ", \"subpixel_order\": " + std::to_string(event.subpixel_order)
8091  + ", \"width\": " + std::to_string(event.width)
8092  + ", \"height\": " + std::to_string(event.height)
8093  + ", \"mwidth\": " + std::to_string(event.mwidth)
8094  + ", \"mheight\": " + std::to_string(event.mheight)
8095  + " }";
8096 }
8097 
8103 std::string to_string(const xcb_button_press_event_t& event
8104  ) noexcept
8105 {
8106  return std::string()
8107  + "{ \"response_type\": " + std::to_string(event.response_type)
8108  + ", \"detail\": " + std::to_string(event.detail)
8109  + ", \"sequence\": " + std::to_string(event.sequence)
8110  + ", \"time\": " + std::to_string(event.time)
8111  + ", \"root\": " + std::to_string(event.root)
8112  + ", \"event\": " + std::to_string(event.event)
8113  + ", \"child\": " + std::to_string(event.child)
8114  + ", \"root_x\": " + std::to_string(event.root_x)
8115  + ", \"root_y\": " + std::to_string(event.root_y)
8116  + ", \"event_x\": " + std::to_string(event.event_x)
8117  + ", \"event_y\": " + std::to_string(event.event_y)
8118  + ", \"state\": " + std::to_string(event.state)
8119  + ", \"same_screen\": " + std::to_string(event.same_screen)
8120  + ", \"pad0\": " + std::to_string(event.pad0)
8121  + " }";
8122 }
8123 
8124 
8130 std::string to_string(const xcb_client_message_event_t& event
8131  ) noexcept
8132 {
8133  return std::string()
8134  + "{ \"response_type\": " + std::to_string(event.response_type)
8135  + ", \"format\": " + std::to_string(event.format)
8136  + ", \"sequence\": " + std::to_string(event.sequence)
8137  + ", \"window\": " + std::to_string(event.window)
8138  + ", \"type\": " + std::to_string(event.type)
8139  + ", \"data\": [ 0x" + std::to_string(event.data.data8[ 0])
8140  + ", 0x" + std::to_string(event.data.data8[ 1])
8141  + ", 0x" + std::to_string(event.data.data8[ 2])
8142  + ", 0x" + std::to_string(event.data.data8[ 3])
8143  + ", 0x" + std::to_string(event.data.data8[ 4])
8144  + ", 0x" + std::to_string(event.data.data8[ 5])
8145  + ", 0x" + std::to_string(event.data.data8[ 6])
8146  + ", 0x" + std::to_string(event.data.data8[ 7])
8147  + ", 0x" + std::to_string(event.data.data8[ 8])
8148  + ", 0x" + std::to_string(event.data.data8[ 9])
8149  + ", 0x" + std::to_string(event.data.data8[10])
8150  + ", 0x" + std::to_string(event.data.data8[11])
8151  + ", 0x" + std::to_string(event.data.data8[12])
8152  + ", 0x" + std::to_string(event.data.data8[13])
8153  + ", 0x" + std::to_string(event.data.data8[14])
8154  + ", 0x" + std::to_string(event.data.data8[15])
8155  + ", 0x" + std::to_string(event.data.data8[16])
8156  + ", 0x" + std::to_string(event.data.data8[17])
8157  + ", 0x" + std::to_string(event.data.data8[18])
8158  + ", 0x" + std::to_string(event.data.data8[19])
8159  + " ]"
8160  + " }";
8161 }
8162 
8163 
8169 std::string to_string(const xcb_configure_notify_event_t& event
8170  ) noexcept
8171 {
8172  return std::string()
8173  + "{ \"response_type\": " + std::to_string(event.response_type)
8174  + ", \"pad0\": " + std::to_string(event.pad0)
8175  + ", \"sequence\": " + std::to_string(event.sequence)
8176  + ", \"event\": " + std::to_string(event.event)
8177  + ", \"window\": " + std::to_string(event.window)
8178  + ", \"above_sibling\": " + std::to_string(event.above_sibling)
8179  + ", \"x\": " + std::to_string(event.x)
8180  + ", \"y\": " + std::to_string(event.y)
8181  + ", \"width\": " + std::to_string(event.width)
8182  + ", \"height\": " + std::to_string(event.height)
8183  + ", \"border_width\": " + std::to_string(event.border_width)
8184  + ", \"override_redirect\": " + std::to_string(event.override_redirect)
8185  + ", \"pad1\": " + std::to_string(event.pad1)
8186  + " }";
8187 }
8188 
8189 
8195 std::string to_string(const xcb_enter_notify_event_t& event
8196  ) noexcept
8197 {
8198  return std::string()
8199  + "{ \"response_type\": " + std::to_string(event.response_type)
8200  + ", \"detail\": " + std::to_string(event.detail)
8201  + ", \"sequence\": " + std::to_string(event.sequence)
8202  + ", \"time\": " + std::to_string(event.time)
8203  + ", \"root\": " + std::to_string(event.root)
8204  + ", \"event\": " + std::to_string(event.event)
8205  + ", \"root_x\": " + std::to_string(event.root_x)
8206  + ", \"root_y\": " + std::to_string(event.root_y)
8207  + ", \"event_x\": " + std::to_string(event.event_x)
8208  + ", \"event_y\": " + std::to_string(event.event_y)
8209  + ", \"state\": " + std::to_string(event.state)
8210  + ", \"mode\": " + std::to_string(event.mode)
8211  + ", \"same_screen_focus\": " + std::to_string(event.same_screen_focus)
8212  + " }";
8213 }
8214 
8215 
8221 std::string to_string(const xcb_expose_event_t& event
8222  ) noexcept
8223 {
8224  return std::string()
8225  + "{ \"response_type\": " + std::to_string(event.response_type)
8226  + ", \"pad0\": " + std::to_string(event.pad0)
8227  + ", \"sequence\": " + std::to_string(event.sequence)
8228  + ", \"window\": " + std::to_string(event.window)
8229  + ", \"x\": " + std::to_string(event.x)
8230  + ", \"y\": " + std::to_string(event.y)
8231  + ", \"width\": " + std::to_string(event.width)
8232  + ", \"height\": " + std::to_string(event.height)
8233  + ", \"count\": " + std::to_string(event.count)
8234  + ", \"pad1\": [ 0x" + std::to_string(event.pad1[0])
8235  + ", 0x" + std::to_string(event.pad1[1])
8236  + " ]"
8237  + " }";
8238 }
8239 
8240 
8246 std::string to_string(const xcb_focus_in_event_t& event
8247  ) noexcept
8248 {
8249  return std::string()
8250  + "{ \"response_type\": " + std::to_string(event.response_type)
8251  + ", \"detail\": " + std::to_string(event.detail)
8252  + ", \"sequence\": " + std::to_string(event.sequence)
8253  + ", \"event\": " + std::to_string(event.event)
8254  + ", \"mode\": " + std::to_string(event.mode)
8255  + ", \"pad0\": [ 0x" + std::to_string(event.pad0[0])
8256  + ", 0x" + std::to_string(event.pad0[1])
8257  + ", 0x" + std::to_string(event.pad0[2])
8258  + " ]"
8259  + " }";
8260 }
8261 
8262 
8268 std::string to_string(const xcb_generic_event_t& event
8269  ) noexcept
8270 {
8271  return std::string()
8272  + "{ \"response_type\": " + std::to_string(event.response_type)
8273  + ", \"pad0\": " + std::to_string(event.pad0)
8274  + ", \"sequence\": " + std::to_string(event.sequence)
8275  + ", \"pad\": [ 0x" + std::to_string(event.pad[0])
8276  + ", 0x" + std::to_string(event.pad[1])
8277  + ", 0x" + std::to_string(event.pad[2])
8278  + ", 0x" + std::to_string(event.pad[3])
8279  + ", 0x" + std::to_string(event.pad[4])
8280  + ", 0x" + std::to_string(event.pad[5])
8281  + ", 0x" + std::to_string(event.pad[6])
8282  + " ]"
8283  + ", \"full_sequence\": " + std::to_string(event.full_sequence)
8284  + " }";
8285 }
8286 
8287 
8293 std::string to_string(const xcb_gravity_notify_event_t& event
8294  ) noexcept
8295 {
8296  return std::string()
8297  + "{ \"response_type\": " + std::to_string(event.response_type)
8298  + ", \"pad0\": " + std::to_string(event.pad0)
8299  + ", \"sequence\": " + std::to_string(event.sequence)
8300  + ", \"event\": " + std::to_string(event.event)
8301  + ", \"window\": " + std::to_string(event.window)
8302  + ", \"x\": " + std::to_string(event.x)
8303  + ", \"y\": " + std::to_string(event.y)
8304  + " }";
8305 }
8306 
8307 
8313 std::string to_string(const xcb_key_press_event_t& event
8314  ) noexcept
8315 {
8316  return std::string()
8317  + "{ \"response_type\": " + std::to_string(event.response_type)
8318  + ", \"detail\": " + std::to_string(event.detail)
8319  + ", \"sequence\": " + std::to_string(event.sequence)
8320  + ", \"time\": " + std::to_string(event.time)
8321  + ", \"root\": " + std::to_string(event.root)
8322  + ", \"event\": " + std::to_string(event.event)
8323  + ", \"child\": " + std::to_string(event.child)
8324  + ", \"root_x\": " + std::to_string(event.root_x)
8325  + ", \"root_y\": " + std::to_string(event.root_y)
8326  + ", \"event_x\": " + std::to_string(event.event_x)
8327  + ", \"event_y\": " + std::to_string(event.event_y)
8328  + ", \"state\": " + std::to_string(event.state)
8329  + ", \"same_screen\": " + std::to_string(event.same_screen)
8330  + ", \"pad0\": " + std::to_string(event.pad0)
8331  + " }";
8332 }
8333 
8334 
8340 std::string to_string(const xcb_map_notify_event_t& event
8341  ) noexcept
8342 {
8343  return std::string()
8344  + "{ \"response_type\": " + std::to_string(event.response_type)
8345  + ", \"pad0\": " + std::to_string(event.pad0)
8346  + ", \"sequence\": " + std::to_string(event.sequence)
8347  + ", \"event\": " + std::to_string(event.event)
8348  + ", \"window\": " + std::to_string(event.window)
8349  + ", \"override_redirect\": " + std::to_string(event.override_redirect)
8350  + ", \"pad1\": [ 0x" + std::to_string(event.pad1[0])
8351  + ", 0x" + std::to_string(event.pad1[1])
8352  + ", 0x" + std::to_string(event.pad1[2])
8353  + " ]"
8354  + " }";
8355 }
8356 
8357 
8363 std::string to_string(const xcb_motion_notify_event_t& event
8364  ) noexcept
8365 {
8366  return std::string()
8367  + "{ \"response_type\": " + std::to_string(event.response_type)
8368  + ", \"detail\": " + std::to_string(event.detail)
8369  + ", \"sequence\": " + std::to_string(event.sequence)
8370  + ", \"time\": " + std::to_string(event.time)
8371  + ", \"root\": " + std::to_string(event.root)
8372  + ", \"event\": " + std::to_string(event.event)
8373  + ", \"child\": " + std::to_string(event.child)
8374  + ", \"root_x\": " + std::to_string(event.root_x)
8375  + ", \"root_y\": " + std::to_string(event.root_y)
8376  + ", \"event_x\": " + std::to_string(event.event_x)
8377  + ", \"event_y\": " + std::to_string(event.event_y)
8378  + ", \"state\": " + std::to_string(event.state)
8379  + ", \"same_screen\": " + std::to_string(event.same_screen)
8380  + ", \"pad0\": " + std::to_string(event.pad0)
8381  + " }";
8382 }
8383 
8384 
8390 std::string to_string(const xcb_property_notify_event_t& event
8391  ) noexcept
8392 {
8393  return std::string()
8394  + "{ \"response_type\": " + std::to_string(event.response_type)
8395  + ", \"pad0\": " + std::to_string(event.pad0)
8396  + ", \"sequence\": " + std::to_string(event.sequence)
8397  + ", \"window\": " + std::to_string(event.window)
8398  + ", \"atom\": " + std::to_string(event.atom)
8399  + ", \"time\": " + std::to_string(event.time)
8400  + ", \"state\": " + std::to_string(event.state)
8401  + ", \"pad1\": [ 0x" + std::to_string(event.pad1[0])
8402  + ", 0x" + std::to_string(event.pad1[1])
8403  + ", 0x" + std::to_string(event.pad1[2])
8404  + " ]"
8405  + " }";
8406 }
8407 
8408 
8414 std::string to_string(const xcb_reparent_notify_event_t& event
8415  ) noexcept
8416 {
8417  return std::string()
8418  + "{ \"response_type\": " + std::to_string(event.response_type)
8419  + ", \"pad0\": " + std::to_string(event.pad0)
8420  + ", \"sequence\": " + std::to_string(event.sequence)
8421  + ", \"event\": " + std::to_string(event.event)
8422  + ", \"window\": " + std::to_string(event.window)
8423  + ", \"parent\": " + std::to_string(event.parent)
8424  + ", \"x\": " + std::to_string(event.x)
8425  + ", \"y\": " + std::to_string(event.y)
8426  + ", \"override_redirect\": " + std::to_string(event.override_redirect)
8427  + ", \"pad1\": [ 0x" + std::to_string(event.pad1[0])
8428  + ", 0x" + std::to_string(event.pad1[1])
8429  + ", 0x" + std::to_string(event.pad1[2])
8430  + " ]"
8431  + " }";
8432 }
8433 
8434 
8440 std::string to_string(const xcb_unmap_notify_event_t& event
8441  ) noexcept
8442 {
8443  return std::string()
8444  + "{ \"response_type\": " + std::to_string(event.response_type)
8445  + ", \"pad0\": " + std::to_string(event.pad0)
8446  + ", \"sequence\": " + std::to_string(event.sequence)
8447  + ", \"event\": " + std::to_string(event.event)
8448  + ", \"window\": " + std::to_string(event.window)
8449  + ", \"from_configure\": " + std::to_string(event.from_configure)
8450  + ", \"pad1\": [ 0x" + std::to_string(event.pad1[0])
8451  + ", 0x" + std::to_string(event.pad1[1])
8452  + ", 0x" + std::to_string(event.pad1[2])
8453  + " ]"
8454  + " }";
8455 }
8456 
8457 
8463 std::string to_string(const xcb_format_t& format
8464  ) noexcept
8465 {
8466  return std::string()
8467  + "{ \"depth\": " + std::to_string(format.depth)
8468  + ", \"bits_per_pixel\": " + std::to_string(format.bits_per_pixel)
8469  + ", \"scanline_pad\": " + std::to_string(format.scanline_pad)
8470  + ", \"pad0\": [ 0x" + std::to_string(format.pad0[0])
8471  + ", 0x" + std::to_string(format.pad0[1])
8472  + ", 0x" + std::to_string(format.pad0[2])
8473  + ", 0x" + std::to_string(format.pad0[3])
8474  + ", 0x" + std::to_string(format.pad0[4])
8475  + " ]"
8476  + " }";
8477 }
8478 
8479 
8485 std::string to_string(const xcb_screen_t& screen
8486  ) noexcept
8487 {
8488  return std::string()
8489  + "{ \"root\": " + std::to_string(screen.root)
8490  + ", \"default_colormap\": " + std::to_string(screen.default_colormap)
8491  + ", \"white_pixel\": " + std::to_string(screen.white_pixel)
8492  + ", \"black_pixel\": " + std::to_string(screen.black_pixel)
8493  + ", \"current_input_masks\": " + std::to_string(screen.current_input_masks)
8494  + ", \"width_in_pixels\": " + std::to_string(screen.width_in_pixels)
8495  + ", \"height_in_pixels\": " + std::to_string(screen.height_in_pixels)
8496  + ", \"width_in_millimeters\": " + std::to_string(screen.width_in_millimeters)
8497  + ", \"height_in_millimeters\": " + std::to_string(screen.height_in_millimeters)
8498  + ", \"min_installed_maps\": " + std::to_string(screen.min_installed_maps)
8499  + ", \"max_installed_maps\": " + std::to_string(screen.max_installed_maps)
8500  + ", \"root_visual\": " + std::to_string(screen.root_visual)
8501  + ", \"backing_stores\": " + std::to_string(screen.backing_stores)
8502  + ", \"save_unders\": " + std::to_string(screen.save_unders)
8503  + ", \"root_depth\": " + std::to_string(screen.root_depth)
8504  + ", \"allowed_depths_len\": " + std::to_string(screen.allowed_depths_len)
8505  + " }";
8506 }
8507 
8508 
8514 std::string to_string(const std::vector<xcb_atom_t>& vector
8515  ) noexcept
8516 {
8517  std::string string = "[ ";
8518  std::string delim = "";
8519 
8520  for(const auto& atom : vector)
8521  {
8522  string += delim + ' ' + std::to_string(atom);
8523 
8524  delim = ",";
8525  }
8526 
8527  string += " ]";
8528 
8529  return string;
8530 }
8531 
8532 
8538 std::string to_string(const std::vector<int32_t>& vector
8539  ) noexcept
8540 {
8541  std::string string = "[ ";
8542  std::string delim = "";
8543 
8544  for(const int32_t& value : vector)
8545  {
8546  string += delim + ' ' + std::to_string(value);
8547 
8548  delim = ",";
8549  }
8550 
8551  string += " ]";
8552 
8553  return string;
8554 }
8555 
8556 
8562 std::string to_string(const xcb_setup_t& setup
8563  ) noexcept
8564 {
8565  return std::string()
8566  + "{ \"status\": " + std::to_string(setup.status)
8567  + ", \"pad0\": " + std::to_string(setup.pad0)
8568  + ", \"protocol_major_version\": " + std::to_string(setup.protocol_major_version)
8569  + ", \"protocol_minor_version\": " + std::to_string(setup.protocol_minor_version)
8570  + ", \"length\": " + std::to_string(setup.length)
8571  + ", \"release_number\": " + std::to_string(setup.release_number)
8572  + ", \"resource_id_base\": " + std::to_string(setup.resource_id_base)
8573  + ", \"resource_id_mask\": " + std::to_string(setup.resource_id_mask)
8574  + ", \"motion_buffer_size\": " + std::to_string(setup.motion_buffer_size)
8575  + ", \"vendor_len\": " + std::to_string(setup.vendor_len)
8576  + ", \"maximum_request_length\": " + std::to_string(setup.maximum_request_length)
8577  + ", \"roots_len\": " + std::to_string(setup.roots_len)
8578  + ", \"pixmap_formats_len\": " + std::to_string(setup.pixmap_formats_len)
8579  + ", \"image_byte_order\": " + std::to_string(setup.image_byte_order)
8580  + ", \"bitmap_format_bit_order\": " + std::to_string(setup.bitmap_format_bit_order)
8581  + ", \"bitmap_format_scanline_unit\": " + std::to_string(setup.bitmap_format_scanline_unit)
8582  + ", \"bitmap_format_scanline_pad\": " + std::to_string(setup.bitmap_format_scanline_pad)
8583  + ", \"min_keycode\": " + std::to_string(setup.min_keycode)
8584  + ", \"max_keycode\": " + std::to_string(setup.max_keycode)
8585  + ", \"pad1\": [ 0x" + std::to_string(setup.pad1[0])
8586  + ", 0x" + std::to_string(setup.pad1[1])
8587  + ", 0x" + std::to_string(setup.pad1[2])
8588  + ", 0x" + std::to_string(setup.pad1[3])
8589  + " ]"
8590  + " }";
8591 }
8592 
8593 
8601 std::string to_string(const Xenium::Key& key
8602  ) noexcept
8603 {
8604  return std::string()
8605  + "{ \"time\": " + std::to_string(key.time)
8606  + ", \"code\": " + std::to_string(key.code)
8607  + ", \"state\": \"" + zakero::to_string(key.state) + "\""
8608  + " }"
8609  ;
8610 }
8611 
8612 
8621 std::string to_string(const Xenium::KeyModifier& key_modifier
8622  ) noexcept
8623 {
8624  auto mod_to_str = [](std::string& s, uint32_t m)
8625  {
8626  s += "[";
8627  std::string delim = "";
8628 
8630  {
8631  s += delim + "\"Shift\"";
8632  delim = ",";
8633  }
8634 
8636  {
8637  s += delim + "\"CapsLock\"";
8638  delim = ",";
8639  }
8640 
8642  {
8643  s += delim + "\"Control\"";
8644  delim = ",";
8645  }
8646 
8647  if(m & Xenium::KeyModifier_Alt)
8648  {
8649  s += delim + "\"Alt\"";
8650  delim = ",";
8651  }
8652 
8653  if(m & Xenium::KeyModifier_Meta)
8654  {
8655  s += delim + "\"Meta\"";
8656  }
8657 
8659  {
8660  s += delim + "\"NumLock\"";
8661  }
8662 
8663  s += "]";
8664  };
8665 
8666  std::string str = "{ \"pressed\": ";
8667  mod_to_str(str, key_modifier.pressed);
8668 
8669  str += ", \"latched\": ";
8670  mod_to_str(str, key_modifier.latched);
8671 
8672  str += ", \"locked\": ";
8673  mod_to_str(str, key_modifier.locked);
8674 
8675  str += " }";
8676 
8677  return str;
8678 }
8679 
8680 
8688 std::string to_string(const Xenium::KeyState key_state
8689  ) noexcept
8690 {
8691  switch(key_state)
8692  {
8693  case Xenium::KeyState::Pressed: return "Pressed";
8694  case Xenium::KeyState::Released: return "Released";
8695  case Xenium::KeyState::Repeat: return "Repeat";
8696  default: return "";
8697  }
8698 }
8699 
8700 
8708 std::string to_string(const Xenium::Output& output
8709  ) noexcept
8710 {
8711  return std::string()
8712  + "{ \"name\": \"" + output.name + "\""
8713  + ", \"x\": " + std::to_string(output.x)
8714  + ", \"y\": " + std::to_string(output.y)
8715  + ", \"width\": " + std::to_string(output.width)
8716  + ", \"height\": " + std::to_string(output.height)
8717  + ", \"physical_width_mm\": " + std::to_string(output.physical_width_mm)
8718  + ", \"physical_height_mm:\" " + std::to_string(output.physical_height_mm)
8719  + ", \"subpixel\": " + std::to_string(output.subpixel)
8720  + ", \"transform\": " + zakero::Xenium::outputTransformName(output.transform)
8721  + ", \"pixels_per_mm_horizontal\": " + std::to_string(output.pixels_per_mm_horizontal)
8722  + ", \"pixels_per_mm_vertical\": " + std::to_string(output.pixels_per_mm_vertical)
8723  //+ ", \"make\": \"" + output.make + "\""
8724  //+ ", \"model\": \"" + output.model + "\""
8725  //+ ", \"refresh_mHz\": " + std::to_string(output.refresh_mHz)
8726  //+ ", \"scale_factor\": " + std::to_string(output.scale_factor)
8727  + " }";
8728 }
8729 
8730 
8738 std::string to_string(const Xenium::PointMm point
8739  ) noexcept
8740 {
8741  return std::string()
8742  + "{ \"time\": " + std::to_string(point.time)
8743  + ", \"x\": " + std::to_string(point.x)
8744  + ", \"y\": " + std::to_string(point.y)
8745  + " }"
8746  ;
8747 }
8748 
8749 
8757 std::string to_string(const Xenium::PointPercent point
8758  ) noexcept
8759 {
8760  return std::string()
8761  + "{ \"time\": " + std::to_string(point.time)
8762  + ", \"x\": " + std::to_string(point.x)
8763  + ", \"y\": " + std::to_string(point.y)
8764  + " }"
8765  ;
8766 }
8767 
8768 
8776 std::string to_string(const Xenium::PointPixel point
8777  ) noexcept
8778 {
8779  return std::string()
8780  + "{ \"time\": " + std::to_string(point.time)
8781  + ", \"x\": " + std::to_string(point.x)
8782  + ", \"y\": " + std::to_string(point.y)
8783  + " }"
8784  ;
8785 }
8786 
8787 
8795 std::string to_string(const Xenium::PointerAxis& pointer_axis
8796  ) noexcept
8797 {
8798  return std::string()
8799  + "{ \"time\": " + std::to_string(pointer_axis.time)
8800  + ", \"steps\": " + std::to_string(pointer_axis.steps)
8801  + ", \"distance\": " + std::to_string(pointer_axis.distance)
8802  + ", \"source\": \"" + zakero::to_string(pointer_axis.source) + "\""
8803  + ", \"type\": \"" + zakero::to_string(pointer_axis.type) + "\""
8804  + " }"
8805  ;
8806 }
8807 
8808 
8816 std::string to_string(const Xenium::PointerAxisSource source
8817  ) noexcept
8818 {
8819  switch(source)
8820  {
8821  case Xenium::PointerAxisSource::Continuous: return "Continuous";
8822  case Xenium::PointerAxisSource::Finger: return "Finger";
8823  case Xenium::PointerAxisSource::Wheel: return "Wheel";
8824  case Xenium::PointerAxisSource::Wheel_Tilt: return "Wheel Tilt";
8825  case Xenium::PointerAxisSource::Unknown: [[fallthrough]];
8826  default: return "";
8827  }
8828 }
8829 
8830 
8838 std::string to_string(const Xenium::PointerAxisType type
8839  ) noexcept
8840 {
8841  switch(type)
8842  {
8843  case Xenium::PointerAxisType::Horizontal: return "Horizontal";
8844  case Xenium::PointerAxisType::Vertical: return "Vertical";
8845  case Xenium::PointerAxisType::Unknown: [[fallthrough]];
8846  default: return "";
8847  }
8848 }
8849 
8850 
8858 std::string to_string(const Xenium::PointerButton& button
8859  ) noexcept
8860 {
8861  std::string str = std::string()
8862  + "{ \"code\": " + std::to_string(button.code)
8863  + ", \"state\": " + zakero::to_string(button.state)
8864  + " }"
8865  ;
8866 
8867  return str;
8868 }
8869 
8870 
8878 std::string to_string(const Xenium::PointerButtonState& button_state
8879  ) noexcept
8880 {
8881  switch(button_state)
8882  {
8883  case Xenium::PointerButtonState::Pressed: return "Pressed";
8884  case Xenium::PointerButtonState::Released: return "Released";
8885  default: return "";
8886  }
8887 }
8888 
8889 
8897 std::string to_string(const Xenium::SizeMm& size
8898  ) noexcept
8899 {
8900  return std::string()
8901  + "{ \"width\": " + std::to_string(size.width)
8902  + ", \"height\": " + std::to_string(size.height)
8903  + " }";
8904 }
8905 
8906 
8914 std::string to_string(const Xenium::SizePercent& size
8915  ) noexcept
8916 {
8917  return std::string()
8918  + "{ \"width\": " + std::to_string(size.width)
8919  + ", \"height\": " + std::to_string(size.height)
8920  + " }";
8921 }
8922 
8923 
8931 std::string to_string(const Xenium::SizePixel& size
8932  ) noexcept
8933 {
8934  return std::string()
8935  + "{ \"width\": " + std::to_string(size.width)
8936  + ", \"height\": " + std::to_string(size.height)
8937  + " }";
8938 }
8939 
8940 
8948 std::string to_string(const Xenium::WindowDecorations window_decorations
8949  ) noexcept
8950 {
8951  switch(window_decorations)
8952  {
8953  case Xenium::WindowDecorations::Client_Side: return "Client Side";
8954  case Xenium::WindowDecorations::Server_Side: return "Server Side";
8955  default: return "";
8956  }
8957 }
8958 
8959 
8967 std::string to_string(const Xenium::WindowMode window_mode
8968  ) noexcept
8969 {
8970  switch(window_mode)
8971  {
8972  case Xenium::WindowMode::Fullscreen: return "Fullscreen";
8973  case Xenium::WindowMode::Maximized: return "Maximized";
8974  case Xenium::WindowMode::Normal: return "Normal";
8975  default: return "";
8976  }
8977 }
8978 
8979 
8994  , Xenium::PointMm& rhs
8995  ) noexcept
8996 {
8997  return zakero::equalish(lhs.x, rhs.x, 0.001)
8998  && zakero::equalish(lhs.y, rhs.y, 0.001)
8999  ;
9000 }
9001 
9002 
9017  , Xenium::PointPercent& rhs
9018  ) noexcept
9019 {
9020  return zakero::equalish(lhs.x, rhs.x, 0.00001)
9021  && zakero::equalish(lhs.y, rhs.y, 0.00001)
9022  ;
9023 }
9024 
9025 
9037  , Xenium::PointPixel& rhs
9038  ) noexcept
9039 {
9040  return (lhs.x == rhs.x) && (lhs.y == rhs.y);
9041 }
9042 
9043 
9056  , Xenium::SizeMm& rhs
9057  ) noexcept
9058 {
9059  return zakero::equalish(lhs.width, rhs.width, 0.001)
9060  && zakero::equalish(lhs.height, rhs.height, 0.001)
9061  ;
9062 }
9063 
9064 
9077  , Xenium::SizePercent& rhs
9078  ) noexcept
9079 {
9080  return zakero::equalish(lhs.width, rhs.width, 0.00001)
9081  && zakero::equalish(lhs.height, rhs.height, 0.00001)
9082  ;
9083 }
9084 
9085 
9095  , Xenium::SizePixel& rhs
9096  ) noexcept
9097 {
9098  return (lhs.width == rhs.width) && (lhs.height == rhs.height);
9099 }
9100 
9101 // }}}
9102 
9103 }
9104 
9105 #endif // ZAKERO_XENIUM_IMPLEMENTATION
9106 
9107 // }}}
9108 
9109 #endif // zakero_Xenium_h
Zakero Base.
std::string to_string(const bool value) noexcept
Convert a bool into a string.
Definition: Zakero_Base.h:534
#define ZAKERO_STEADY_TIME_NOW(unit_)
Get the current time.
Definition: Zakero_Base.h:205
#define ZAKERO_FREE(ptr_)
Free memory.
Definition: Zakero_Base.h:138
bool equalish(const float a, const float b, const float delta) noexcept
Compare two floats.
Definition: Zakero_Base.h:348
bool operator==(Xenium::PointMm &lhs, Xenium::PointMm &rhs) noexcept
Compare two Point objects.
Definition: Zakero_Xenium.h:8993
#define _NET_WM_STATE_REMOVE
A NET-WM Macro (that may not be defined).
Definition: Zakero_Xenium.h:1066
#define _NET_WM_STATE_ADD
A NET-WM Macro (that may not be defined).
Definition: Zakero_Xenium.h:1073
A Window.
Definition: Zakero_Xenium.h:501
void keyboardOnKey(Xenium::LambdaKey) noexcept
Respond to "Keyboard Key" events.
Definition: Zakero_Xenium.h:7613
void decorationsOnChange(Xenium::LambdaWindowDecorations) noexcept
Respond to "Decoration Change" events.
Definition: Zakero_Xenium.h:6865
void pointerOnEnter(Xenium::LambdaPointMm) noexcept
Respond to "Pointer Enter" events.
Definition: Zakero_Xenium.h:7729
void titleSet(const std::string &) noexcept
Change the window title.
Definition: Zakero_Xenium.h:6739
void pointerOnAxis(Xenium::LambdaAxis) noexcept
Respond to "Pointer Axis" events.
Definition: Zakero_Xenium.h:7635
std::error_code sizeSetMinMax(const Xenium::SizeMm &, const Xenium::SizeMm &) noexcept
Set the minimum window size.
Definition: Zakero_Xenium.h:7065
void sizeOnChange(Xenium::LambdaSizeMm) noexcept
Respond to "Resize" events.
Definition: Zakero_Xenium.h:7211
std::error_code sizeSet(const Xenium::SizeMm &) noexcept
Set the window size.
Definition: Zakero_Xenium.h:6900
void onCloseRequest(Xenium::Lambda) noexcept
Respond to "Close Request" events.
Definition: Zakero_Xenium.h:6771
virtual ~Window()
Destroy a Window.
Definition: Zakero_Xenium.h:6675
Xenium::PointPercent convertToPercent(const Xenium::PointPixel &) const noexcept
Unit conversion.
Definition: Zakero_Xenium.h:7314
void pointerOnButton(Xenium::LambdaButtonMm) noexcept
Respond to "Pointer Button" events.
Definition: Zakero_Xenium.h:7656
uint8_t bytesPerPixel() const noexcept
Get the number of bytes per pixel.
Definition: Zakero_Xenium.h:8045
void keyboardOnLeave(Xenium::Lambda) noexcept
Respond to "Keyboard Leave" events.
Definition: Zakero_Xenium.h:7592
bool windowModeIs(const Xenium::WindowMode) const noexcept
Check the WindowMode.
Definition: Zakero_Xenium.h:7472
std::error_code minimize() noexcept
Minimize the window.
Definition: Zakero_Xenium.h:7548
std::error_code decorationsSet(const Xenium::WindowDecorations) noexcept
Use the Desktop Environment borders.
Definition: Zakero_Xenium.h:6831
Window(Xenium *, void *)
Construct a Window.
Definition: Zakero_Xenium.h:6658
Xenium::PointPixel convertToPixel(const Xenium::PointMm &) const noexcept
Unit conversion.
Definition: Zakero_Xenium.h:7334
void pointerOnMotion(Xenium::LambdaPointMm) noexcept
Respond to "Pointer Motion" events.
Definition: Zakero_Xenium.h:7825
uint32_t time() const noexcept
When the last frame was rendered.
Definition: Zakero_Xenium.h:8031
void keyboardOnEnter(Xenium::Lambda) noexcept
Respond to "Keyboard Enter" events.
Definition: Zakero_Xenium.h:7573
void imagePresent() noexcept
Render the image.
Definition: Zakero_Xenium.h:7990
Xenium::WindowMode windowMode() const noexcept
Get the current WindowMode.
Definition: Zakero_Xenium.h:7457
std::error_code imageNext(uint8_t *&, Xenium::SizePixel &) noexcept
Get an image buffer.
Definition: Zakero_Xenium.h:7964
Xenium::PointMm convertToMm(const Xenium::PointPixel &) const noexcept
Unit conversion.
Definition: Zakero_Xenium.h:7294
std::error_code windowModeSet(const Xenium::WindowMode) noexcept
Change the window mode.
Definition: Zakero_Xenium.h:7489
void pointerOnLeave(Xenium::Lambda) noexcept
Respond to "Pointer Leave" events.
Definition: Zakero_Xenium.h:7804
void onFocusChange(Xenium::LambdaBool) noexcept
Respond to "Active" change events.
Definition: Zakero_Xenium.h:6800
void classSet(const std::string &) noexcept
Change the window class.
Definition: Zakero_Xenium.h:6714
void windowModeOnChange(Xenium::LambdaWindowMode) noexcept
Respond to "Window Mode" events.
Definition: Zakero_Xenium.h:7523
A wrapper class for X11/XCB.
Definition: Zakero_Xenium.h:258
static constexpr uint32_t KeyModifier_Shift
Key Modifier flag.
Definition: Zakero_Xenium.h:284
uint32_t locked
A collection of locked modifiers.
Definition: Zakero_Xenium.h:295
uint32_t physical_height_mm
The height of the device in millimeters.
Definition: Zakero_Xenium.h:424
Xenium::Output output(const Xenium::OutputId) const noexcept
Get a copy of the Output information.
Definition: Zakero_Xenium.h:2898
uint32_t latched
A collection of latched modifiers.
Definition: Zakero_Xenium.h:294
int32_t x
The X position within the global compositor.
Definition: Zakero_Xenium.h:419
int32_t steps
The number of rotation steps.
Definition: Zakero_Xenium.h:349
int32_t keyRepeatRate() const noexcept
The key repeat rate.
Definition: Zakero_Xenium.h:2878
void outputOnRemove(Xenium::LambdaOutputId) noexcept
Notification of removing an Output device.
Definition: Zakero_Xenium.h:3251
Xenium::PointMm outputConvertToMm(const Xenium::OutputId, const Xenium::PointPixel &) const noexcept
Convert Pixel to Millimeter.
Definition: Zakero_Xenium.h:2996
std::function< void(Xenium::WindowMode)> LambdaWindowMode
A Lambda that has a parameter: WindowMode.
Definition: Zakero_Xenium.h:494
static std::string outputSubpixelName(int32_t) noexcept
Get a human readable string.
Definition: Zakero_Xenium.h:2951
Xenium::PointerAxisType type
The type of Axis.
Definition: Zakero_Xenium.h:352
std::function< void(const Xenium::PointerButton &, const Xenium::PointMm &, const Xenium::KeyModifier &)> LambdaButtonMm
A Lambda that has parameters: PointerButton, PointMm and KeyModifier.
Definition: Zakero_Xenium.h:483
int32_t y
The Y position within the global compositor.
Definition: Zakero_Xenium.h:420
std::function< void(const Xenium::SizePercent &)> LambdaSizePercent
A Lambda that has a parameter: SizePercent.
Definition: Zakero_Xenium.h:491
static constexpr uint32_t KeyModifier_Control
Key Modifier flag.
Definition: Zakero_Xenium.h:286
float distance
The distance traveled.
Definition: Zakero_Xenium.h:350
PointerButtonState
Mouse button state.
Definition: Zakero_Xenium.h:359
std::function< void(const Xenium::PointerButton &, const Xenium::PointPercent &, const Xenium::KeyModifier &)> LambdaButtonPercent
A Lambda that has parameters: PointerButton, PointPercent and KeyModifier.
Definition: Zakero_Xenium.h:484
WindowMode
All the available window modes.
Definition: Zakero_Xenium.h:473
uint32_t physical_width_mm
The width of the device in millimeters.
Definition: Zakero_Xenium.h:423
PointerAxisSource
Where the axis information came from.
Definition: Zakero_Xenium.h:333
std::function< void(const Xenium::PointPercent &, const Xenium::KeyModifier &)> LambdaPointPercent
A Lambda that has parameters: PointPercent and KeyModifier.
Definition: Zakero_Xenium.h:488
float pixels_per_mm_horizontal
A pre-calculated value.
Definition: Zakero_Xenium.h:427
std::function< void(const Xenium::SizeMm &)> LambdaSizeMm
A Lambda that has a parameter: SizeMm.
Definition: Zakero_Xenium.h:490
int32_t height
The height of the device in hardware units.
Definition: Zakero_Xenium.h:422
std::function< void(const Xenium::PointerButton &, const Xenium::PointPixel &, const Xenium::KeyModifier &)> LambdaButtonPixel
A Lambda that has parameters: PointerButton, PointPixel and KeyModifier.
Definition: Zakero_Xenium.h:485
Xenium::VectorOutputId outputVector() const noexcept
Get a list of the Output Id's.
Definition: Zakero_Xenium.h:2926
std::string name
The name of the output.
Definition: Zakero_Xenium.h:418
std::function< void(const Xenium::Key &, const Xenium::KeyModifier &)> LambdaKey
A Lambda that has parameters: Key and KeyModifier.
Definition: Zakero_Xenium.h:486
std::function< void()> Lambda
A Lambda that has no parameters.
Definition: Zakero_Xenium.h:480
Xenium::PointerButtonState state
The button state.
Definition: Zakero_Xenium.h:366
std::function< void(bool)> LambdaBool
A Lambda that has a parameter: bool.
Definition: Zakero_Xenium.h:482
uint32_t WindowId
A type for better readablity.
Definition: Zakero_Xenium.h:496
Xenium::PointPixel outputConvertToPixel(const Xenium::OutputId, const Xenium::PointMm &) const noexcept
Convert Millimeter to Pixel.
Definition: Zakero_Xenium.h:3050
int32_t transform
Transform that maps framebuffer to output.
Definition: Zakero_Xenium.h:426
Xenium::Window * windowCreate(const Xenium::SizeMm &, std::error_code &) noexcept
Create a window.
Definition: Zakero_Xenium.h:3701
KeyState
Keyboard key state
Definition: Zakero_Xenium.h:272
static constexpr uint32_t KeyModifier_Meta
Key Modifier flag.
Definition: Zakero_Xenium.h:289
WindowDecorations
Who is responsible for rendering the decorations.
Definition: Zakero_Xenium.h:468
void outputOnChange(Xenium::LambdaOutputId) noexcept
Notification that an Output device has changed.
Definition: Zakero_Xenium.h:3230
static Xenium * connect() noexcept
Establish a connection with the X11 server.
Definition: Zakero_Xenium.h:2390
virtual ~Xenium() noexcept
Destructor.
Definition: Zakero_Xenium.h:2357
std::function< void(Xenium::WindowDecorations)> LambdaWindowDecorations
A Lambda that has a parameter: WindowDecorations.
Definition: Zakero_Xenium.h:493
int32_t subpixel
The device's subpixel orientation.
Definition: Zakero_Xenium.h:425
uint32_t pressed
A collection of pressed modifiers.
Definition: Zakero_Xenium.h:293
int32_t keyRepeatDelay() const noexcept
The key repeat delay.
Definition: Zakero_Xenium.h:2865
int32_t width
The width of the device in hardware units.
Definition: Zakero_Xenium.h:421
uint32_t code
The key code of the event.
Definition: Zakero_Xenium.h:280
std::function< void(const Xenium::PointPixel &, const Xenium::KeyModifier &)> LambdaPointPixel
A Lambda that has parameters: PointPixel and KeyModifier.
Definition: Zakero_Xenium.h:489
static constexpr uint32_t KeyModifier_Alt
Key Modifier flag.
Definition: Zakero_Xenium.h:287
std::function< void(const Xenium::SizePixel &)> LambdaSizePixel
A Lambda that has a parameter: SizePixel.
Definition: Zakero_Xenium.h:492
Xenium::KeyState state
The state of the key.
Definition: Zakero_Xenium.h:281
Xenium::PointPercent outputConvertToPercent(const Xenium::OutputId, const Xenium::PointPixel &) const noexcept
Convert Pixel to a Percentage.
Definition: Zakero_Xenium.h:3023
void outputOnAdd(Xenium::LambdaOutputId) noexcept
Notification of adding an Output device.
Definition: Zakero_Xenium.h:3209
uint32_t time
When the event occurred.
Definition: Zakero_Xenium.h:348
std::function< void(const Xenium::PointerAxis &, const Xenium::KeyModifier &)> LambdaAxis
A Lambda that has parameters: PointerAxis and KeyModifier.
Definition: Zakero_Xenium.h:481
static constexpr uint32_t KeyModifier_CapsLock
Key Modifier flag.
Definition: Zakero_Xenium.h:285
std::function< void(const Xenium::PointMm &, const Xenium::KeyModifier &)> LambdaPointMm
A Lambda that has parameters: PointMm and KeyModifier.
Definition: Zakero_Xenium.h:487
uint32_t code
The event code.
Definition: Zakero_Xenium.h:365
uint32_t time
When the key event happened.
Definition: Zakero_Xenium.h:279
static constexpr uint32_t KeyModifier_NumLock
Key Modifier flag.
Definition: Zakero_Xenium.h:288
Xenium::PointerAxisSource source
The source of the event.
Definition: Zakero_Xenium.h:351
uint32_t group
The keyboard layout.
Definition: Zakero_Xenium.h:296
float pixels_per_mm_vertical
A pre-calculated value.
Definition: Zakero_Xenium.h:428
PointerAxisType
The direction of the axis movement.
Definition: Zakero_Xenium.h:341
static std::string outputTransformName(int32_t) noexcept
Get a human readable string.
Definition: Zakero_Xenium.h:2974
Key event information.
Definition: Zakero_Xenium.h:278
A collection modifier flags.
Definition: Zakero_Xenium.h:292
Information about a output device.
Definition: Zakero_Xenium.h:417
Information about an Axis event.
Definition: Zakero_Xenium.h:347
Information about a pointer button event.
Definition: Zakero_Xenium.h:364
A location that uses millimeters.
Definition: Zakero_Xenium.h:303
float y
Where in the Y-Axis the point is.
Definition: Zakero_Xenium.h:306
float x
Where in the X-Axis the point is.
Definition: Zakero_Xenium.h:305
uint32_t time
Where in time the point is (if > 0).
Definition: Zakero_Xenium.h:304
friend bool operator==(Xenium::PointMm &, Xenium::PointMm &) noexcept
Compare two Point objects.
Definition: Zakero_Xenium.h:8993
A location that uses percentages.
Definition: Zakero_Xenium.h:312
float x
Where in the X-Axis the point is.
Definition: Zakero_Xenium.h:314
uint32_t time
Where in time the point is (if > 0).
Definition: Zakero_Xenium.h:313
float y
Where in the Y-Axis the point is.
Definition: Zakero_Xenium.h:315
friend bool operator==(Xenium::PointPercent &, Xenium::PointPercent &) noexcept
Compare two Point objects.
Definition: Zakero_Xenium.h:9016
A location that uses pixels.
Definition: Zakero_Xenium.h:321
friend bool operator==(Xenium::PointPixel &, Xenium::PointPixel &) noexcept
Compare two Point objects.
Definition: Zakero_Xenium.h:9036
uint32_t time
Where in time the point is (if > 0).
Definition: Zakero_Xenium.h:322
int32_t y
Where in the Y-Axis the point is.
Definition: Zakero_Xenium.h:324
int32_t x
Where in the X-Axis the point is.
Definition: Zakero_Xenium.h:323
Size measured in millimeters.
Definition: Zakero_Xenium.h:373
friend bool operator==(Xenium::SizeMm &, Xenium::SizeMm &) noexcept
Compare two Point objects.
Definition: Zakero_Xenium.h:9055
Size measured as a percentage of the Output (Monitor) resolution.
Definition: Zakero_Xenium.h:381
friend bool operator==(Xenium::SizePercent &, Xenium::SizePercent &) noexcept
Compare two Point objects.
Definition: Zakero_Xenium.h:9076
Size measured in pixels.
Definition: Zakero_Xenium.h:389
friend bool operator==(Xenium::SizePixel &, Xenium::SizePixel &) noexcept
Compare two Size objects.
Definition: Zakero_Xenium.h:9094