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 
178 /******************************************************************************
179  * Includes
180  */
181 
182 // C++
183 #include <array>
184 #include <future>
185 #include <iostream>
186 #include <thread>
187 #include <unordered_map>
188 
189 // Linux
190 #include <linux/input-event-codes.h>
191 
192 // X11/XCB
193 #include <xcb/xcb.h>
194 #include <xcb/xcb_icccm.h>
195 #include <xcb/randr.h>
196 
200 #define explicit explicit_
201 #include <xcb/xkb.h>
202 #undef explicit
203 
204 // Zakero
205 #include "Zakero_Base.h"
206 
207 
208 /******************************************************************************
209  * Macros
210  */
211 
212 // {{{ Macros
213 
230 #define ZAKERO_XENIUM__ERROR_DATA \
231  X(Error_None , 0 , "No Error" ) \
232  X(Error_Unknown , 1 , "An unknown error has occurred" ) \
233  X(Error_Connection_Failed , 2 , "Failed due to socket, pipe, or other stream errors" ) \
234  X(Error_Extension_Not_Supported , 3 , "The requested XCB extension is not supported" ) \
235  X(Error_Invalid_Display_Name , 4 , "An error occured while parsing the X11 display name" ) \
236  X(Error_Invalid_Screen , 5 , "The X11 server does not have a screen matching the display" ) \
237  X(Error_Minimum_Size_Greater_Than_Maximum_Size , 6 , "The minimum window size is larger than the maximum window size." ) \
238  X(Error_Not_Enough_Memory , 7 , "Insufficient memory" ) \
239  X(Error_Request_Too_Long , 8 , "The request was longer than what is excepted by the X11 server" ) \
240  X(Error_Window_Size_Too_Small , 9 , "The window size was too small." ) \
241  X(Error_RandR_CRTC_Info_Not_Found , 10 , "XCB RandR CRTC Information was not found" ) \
242  X(Error_RandR_Invalid_CRTC_Id , 11 , "XCB RandR CRTC ID is not valid" ) \
243  X(Error_RandR_Invalid_Output_Id , 12 , "XCB RandR Output ID is not valid" ) \
244  X(Error_RandR_Not_Available , 13 , "XCB RandR extenstion is not available" ) \
245  X(Error_RandR_Output_Info_Is_Incomplete , 14 , "XCB RandR Output Information does not have enough data" ) \
246  X(Error_RandR_Output_Info_Not_Found , 15 , "XCB RandR Output Information was not found" ) \
247  X(Error_RandR_Screen_Resources_Not_Found , 16 , "XCB RandR could not locate any screen resources" ) \
248  X(Error_RandR_Version_Too_Old , 17 , "XCB RandR version is too old" ) \
249  X(Error_Xcb_Fullscreen_Not_Available , 18 , "The XCB Window Manager does not support fullscreen windows." ) \
250  X(Error_Xcb_Hidden_Not_Available , 19 , "The XCB Window Manager does not support hiding windows." ) \
251  X(Error_Xcb_Maximized_Window_Not_Available , 20 , "The XCB Window Manager does not support maximized windows." ) \
252  X(Error_Xcb_NETWM_State_Not_Available , 21 , "The XCB NETWM protocol extention is not supported." ) \
253  X(Error_Xcb_WM_Delete_Window_Not_Available , 22 , "The XCB Window Manager does not support the delete protocol." ) \
254  X(Error_Xcb_WM_Protocols_Not_Available , 23 , "The XCB Window Manager protocols are not available." ) \
255  X(Error_Xcb_Xkb_Not_Available , 24 , "The XCB XKB Extiension v1.0 is not available." ) \
256 
257 // }}}
258 
259 namespace zakero
260 {
261  // {{{ Declaration
262 
263  class Xenium
264  {
265  public:
266 #define X(name_, val_, mesg_) \
267  static constexpr int name_ = val_;
268  ZAKERO_XENIUM__ERROR_DATA
269 #undef X
270 
271  static constexpr int32_t Window_Size_Minimum = 100;
272 
273  virtual ~Xenium() noexcept;
274 
275  // {{{ Type : Key
276 
277  enum struct KeyState
278  { Released = 0
279  , Pressed = 1
280  , Repeat = 2
281  };
282 
283  struct Key
284  {
285  uint32_t time;
286  uint32_t code;
288  };
289 
290  static constexpr uint32_t KeyModifier_Shift = 0x00000001;
291  static constexpr uint32_t KeyModifier_CapsLock = 0x00000002;
292  static constexpr uint32_t KeyModifier_Control = 0x00000004;
293  static constexpr uint32_t KeyModifier_Alt = 0x00000008;
294  static constexpr uint32_t KeyModifier_NumLock = 0x00000010;
295  static constexpr uint32_t KeyModifier_Meta = 0x00000040;
296 
297  struct KeyModifier
298  {
299  uint32_t pressed = 0;
300  uint32_t latched = 0;
301  uint32_t locked = 0;
302  uint32_t group = 0;
303  };
304 
305  // }}}
306  // {{{ Type : Point
307 
308  struct PointMm
309  {
310  uint32_t time;
311  float x;
312  float y;
313 
314  friend bool operator==(Xenium::PointMm&, Xenium::PointMm&) noexcept;
315  };
316 
318  {
319  uint32_t time;
320  float x;
321  float y;
322 
323  friend bool operator==(Xenium::PointPercent&, Xenium::PointPercent&) noexcept;
324  };
325 
326  struct PointPixel
327  {
328  uint32_t time;
329  int32_t x;
330  int32_t y;
331 
332  friend bool operator==(Xenium::PointPixel&, Xenium::PointPixel&) noexcept;
333  };
334 
335  // }}}
336  // {{{ Type : Pointer Axis
337 
338  enum struct PointerAxisSource
339  { Unknown
340  , Continuous
341  , Finger
342  , Wheel
343  , Wheel_Tilt
344  };
345 
346  enum struct PointerAxisType
347  { Unknown
348  , Horizontal
349  , Vertical
350  };
351 
352  struct PointerAxis
353  {
354  uint32_t time;
355  int32_t steps;
356  float distance;
359  };
360 
361  // }}}
362  // {{{ Type : Pointer Button
363 
364  enum struct PointerButtonState
365  { Released = 0
366  , Pressed = 1
367  };
368 
370  {
371  uint32_t code;
373  };
374 
375  // }}}
376  // {{{ Type : Size
377 
378  struct SizeMm
379  {
380  float width;
381  float height;
382 
383  friend bool operator==(Xenium::SizeMm&, Xenium::SizeMm&) noexcept;
384  };
385 
386  struct SizePercent
387  {
388  float width;
389  float height;
390 
391  friend bool operator==(Xenium::SizePercent&, Xenium::SizePercent&) noexcept;
392  };
393 
394  struct SizePixel
395  {
396  int32_t width;
397  int32_t height;
398 
399  friend bool operator==(Xenium::SizePixel&, Xenium::SizePixel&) noexcept;
400  };
401 
402  // }}}
403  // {{{ Connection
404 
405  [[nodiscard]] static Xenium* connect() noexcept;
406  [[nodiscard]] static Xenium* connect(const std::string&) noexcept;
407  [[nodiscard]] static Xenium* connect(std::error_code&) noexcept;
408  [[nodiscard]] static Xenium* connect(const std::string&, std::error_code&) noexcept;
409 
410  // }}}
411  // {{{ Cursor : TODO
412 
413  // }}}
414  // {{{ Keyboard
415 
416  [[nodiscard]] int32_t keyRepeatDelay() const noexcept;
417  [[nodiscard]] int32_t keyRepeatRate() const noexcept;
418 
419  // }}}
420  // {{{ Output : X11/XCB
421 
422  struct Output
423  {
424  std::string name = "";
425  int32_t x = 0;
426  int32_t y = 0;
427  int32_t width = 0;
428  int32_t height = 0;
429  uint32_t physical_width_mm = 0;
430  uint32_t physical_height_mm = 0;
431  int32_t subpixel = 0;
432  int32_t transform = 0;
433  float pixels_per_mm_horizontal = 0.0;
434  float pixels_per_mm_vertical = 0.0;
435  //std::string make = ""; // Not Available?
436  //std::string model = ""; // Not Available?
437  //int32_t refresh_mHz = 0; // Not Available?
438  //int32_t scale_factor = 0; // Not Available?
439  };
440 
441  // -------------------------------------------------- //
442 
443  using OutputId = uint32_t;
444 
445  using LambdaOutputId = std::function<void(const Xenium::OutputId)>;
446 
447  using VectorOutputId = std::vector<Xenium::OutputId>;
448 
449  // -------------------------------------------------- //
450 
451  [[nodiscard]] Xenium::Output output(const Xenium::OutputId) const noexcept;
452  [[nodiscard]] Xenium::VectorOutputId outputVector() const noexcept;
453  [[nodiscard]] static std::string outputSubpixelName(int32_t) noexcept;
454  [[nodiscard]] static std::string outputTransformName(int32_t) noexcept;
455 
456  [[nodiscard]] Xenium::PointMm outputConvertToMm(const Xenium::OutputId, const Xenium::PointPixel&) const noexcept;
457  [[nodiscard]] Xenium::PointPercent outputConvertToPercent(const Xenium::OutputId, const Xenium::PointPixel&) const noexcept;
458  [[nodiscard]] Xenium::PointPixel outputConvertToPixel(const Xenium::OutputId, const Xenium::PointMm&) const noexcept;
459  [[nodiscard]] Xenium::PointPixel outputConvertToPixel(const Xenium::OutputId, const Xenium::PointPercent&) const noexcept;
460 
461  [[nodiscard]] Xenium::SizeMm outputConvertToMm(const Xenium::OutputId, const Xenium::SizePixel&) const noexcept;
462  [[nodiscard]] Xenium::SizePercent outputConvertToPercent(const Xenium::OutputId, const Xenium::SizePixel&) const noexcept;
463  [[nodiscard]] Xenium::SizePixel outputConvertToPixel(const Xenium::OutputId, const Xenium::SizeMm&) const noexcept;
464  [[nodiscard]] Xenium::SizePixel outputConvertToPixel(const Xenium::OutputId, const Xenium::SizePercent&) const noexcept;
465 
466  void outputOnAdd(Xenium::LambdaOutputId) noexcept;
467  void outputOnChange(Xenium::LambdaOutputId) noexcept;
468  void outputOnRemove(Xenium::LambdaOutputId) noexcept;
469 
470  // }}}
471  // {{{ Window
472 
473  enum struct WindowDecorations
474  { Client_Side
475  , Server_Side
476  };
477 
478  enum struct WindowMode
479  { Normal
480  , Fullscreen
481  , Maximized
482  };
483 
484  // -------------------------------------------------- //
485 
486  using Lambda = std::function<void()>;
487  using LambdaAxis = std::function<void(const Xenium::PointerAxis&, const Xenium::KeyModifier&)>;
488  using LambdaBool = std::function<void(bool)>;
489  using LambdaButtonMm = std::function<void(const Xenium::PointerButton&, const Xenium::PointMm&, const Xenium::KeyModifier&)>;
490  using LambdaButtonPercent = std::function<void(const Xenium::PointerButton&, const Xenium::PointPercent&, const Xenium::KeyModifier&)>;
491  using LambdaButtonPixel = std::function<void(const Xenium::PointerButton&, const Xenium::PointPixel&, const Xenium::KeyModifier&)>;
492  using LambdaKey = std::function<void(const Xenium::Key&, const Xenium::KeyModifier&)>;
493  using LambdaPointMm = std::function<void(const Xenium::PointMm&, const Xenium::KeyModifier&)>;
494  using LambdaPointPercent = std::function<void(const Xenium::PointPercent&, const Xenium::KeyModifier&)>;
495  using LambdaPointPixel = std::function<void(const Xenium::PointPixel&, const Xenium::KeyModifier&)>;
496  using LambdaSizeMm = std::function<void(const Xenium::SizeMm&)>;
497  using LambdaSizePercent = std::function<void(const Xenium::SizePercent&)>;
498  using LambdaSizePixel = std::function<void(const Xenium::SizePixel&)>;
500  using LambdaWindowMode = std::function<void(Xenium::WindowMode)>;
501 
502  using WindowId = uint32_t;
503 
504  // -------------------------------------------------- //
505 
506  class Window
507  {
508  public:
509  Window(Xenium*, void*);
510  virtual ~Window();
511 
512  // {{{ Configuration
513 
514  void classSet(const std::string&) noexcept;
515  void titleSet(const std::string&) noexcept;
516 
517  // }}}
518  // {{{ Events
519 
520  void onCloseRequest(Xenium::Lambda) noexcept;
521  void onFocusChange(Xenium::LambdaBool) noexcept;
522 
523  // }}}
524  // {{{ Decorations
525 
526  std::error_code decorationsSet(const Xenium::WindowDecorations) noexcept;
528 
529  // }}}
530  // {{{ Size
531 
532  std::error_code sizeSet(const Xenium::SizeMm&) noexcept;
533  std::error_code sizeSet(const Xenium::SizePercent&) noexcept;
534  std::error_code sizeSet(const Xenium::SizePixel&) noexcept;
535  std::error_code sizeSetMinMax(const Xenium::SizeMm&, const Xenium::SizeMm&) noexcept;
536  std::error_code sizeSetMinMax(const Xenium::SizePercent&, const Xenium::SizePercent&) noexcept;
537  std::error_code sizeSetMinMax(const Xenium::SizePixel&, const Xenium::SizePixel&) noexcept;
538  void sizeOnChange(Xenium::LambdaSizeMm) noexcept;
540  void sizeOnChange(Xenium::LambdaSizePixel) noexcept;
541 
542  // }}}
543  // {{{ Conversion
544 
545  [[nodiscard]] Xenium::PointMm convertToMm(const Xenium::PointPixel&) const noexcept;
546  [[nodiscard]] Xenium::PointPercent convertToPercent(const Xenium::PointPixel&) const noexcept;
547  [[nodiscard]] Xenium::PointPixel convertToPixel(const Xenium::PointMm&) const noexcept;
548  [[nodiscard]] Xenium::PointPixel convertToPixel(const Xenium::PointPercent&) const noexcept;
549 
550  [[nodiscard]] Xenium::SizeMm convertToMm(const Xenium::SizePixel&) const noexcept;
551  [[nodiscard]] Xenium::SizePercent convertToPercent(const Xenium::SizePixel&) const noexcept;
552  [[nodiscard]] Xenium::SizePixel convertToPixel(const Xenium::SizeMm&) const noexcept;
553  [[nodiscard]] Xenium::SizePixel convertToPixel(const Xenium::SizePercent&) const noexcept;
554 
555  // }}}
556  // {{{ Window Mode
557 
558  [[nodiscard]] Xenium::WindowMode windowMode() const noexcept;
559  [[nodiscard]] bool windowModeIs(const Xenium::WindowMode) const noexcept;
560  std::error_code windowModeSet(const Xenium::WindowMode) noexcept;
562 
563  [[nodiscard]] std::error_code minimize() noexcept;
564 
565  // }}}
566  // {{{ Cursor : TODO
567 
568  // }}}
569  // {{{ Keyboard
570 
571  void keyboardOnEnter(Xenium::Lambda) noexcept;
572  void keyboardOnLeave(Xenium::Lambda) noexcept;
573  void keyboardOnKey(Xenium::LambdaKey) noexcept;
574 
575  // }}}
576  // {{{ Pointer
577 
578  void pointerOnAxis(Xenium::LambdaAxis) noexcept;
582  void pointerOnEnter(Xenium::LambdaPointMm) noexcept;
585  void pointerOnLeave(Xenium::Lambda) noexcept;
586  void pointerOnMotion(Xenium::LambdaPointMm) noexcept;
589 
590  // Future?
591  //void pointerOnAxisSource(Xenium::Lambda) noexcept;
592  //void pointerOnAxisStop(Xenium::Lambda) noexcept;
593  //void pointerOnAxisDiscrete(Xenium::Lambda) noexcept;
594 
595  // }}}
596  // {{{ Rendering
597 
598  std::error_code imageNext(uint8_t*&, Xenium::SizePixel&) noexcept;
599  void imagePresent() noexcept;
600  [[nodiscard]] uint32_t time() const noexcept;
601  [[nodiscard]] uint8_t bytesPerPixel() const noexcept;
602 
603  // }}}
604 
605  private:
606  Xenium* xenium;
607  uint8_t* frame_buffer;
608  Xenium::SizePixel frame_buffer_size;
609  Xenium::WindowId window_id;
610  xcb_gcontext_t gc;
611  size_t frame_buffer_length;
612  uint32_t frame_time;
613 
614  Window(const Xenium::Window&) = delete;
615  Window& operator=(const Xenium::Window&) = delete;
616  };
617 
618  // -------------------------------------------------- //
619 
620  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizeMm&, std::error_code&) noexcept;
621  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizeMm&, const uint32_t, xcb_create_window_value_list_t&, std::error_code&) noexcept;
622  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizePercent&, std::error_code&) noexcept;
623  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizePercent&, const uint32_t, xcb_create_window_value_list_t&, std::error_code&) noexcept;
624  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizePixel&, std::error_code&) noexcept;
625  [[nodiscard]] Xenium::Window* windowCreate(const Xenium::SizePixel&, const uint32_t, xcb_create_window_value_list_t&, std::error_code&) noexcept;
626 
627  // }}}
628 
629  private:
630  Xenium() noexcept;
631 
632  // {{{ Connection
633 
634  void disconnect() noexcept;
635 
636  // }}}
637  // {{{ Initialization
638 
639  [[nodiscard]] std::error_code init(xcb_connection_t*, int) noexcept;
640 
641  // }}}
642  // {{{ Cursor : TODO
643 
644  // }}}
645  // {{{ Event Loop
646 
647  std::jthread event_loop;
648  std::atomic<bool> event_loop_is_running;
649 
650  // -------------------------------------------------- //
651 
652  void eventLoopStart() noexcept;
653  static void eventLoop(std::stop_token, Xenium*) noexcept;
654 
655  // }}}
656  // {{{ Output
657 
658  using Map_OutputId_Output = std::unordered_map<Xenium::OutputId, Xenium::Output>;
659 
660  // -------------------------------------------------- //
661 
662  Xenium::LambdaOutputId output_on_add = {};
663  Xenium::LambdaOutputId output_on_change = {};
664  Xenium::LambdaOutputId output_on_remove = {};
665  Xenium::Map_OutputId_Output output_map = {};
666  mutable std::mutex output_mutex = {};
667 
668  // -------------------------------------------------- //
669 
670  [[nodiscard]] const Xenium::Output& output(const int16_t, const int16_t, Xenium::OutputId&) noexcept;
671  [[nodiscard]] std::error_code outputInit() noexcept;
672  [[nodiscard]] std::error_code outputAdd(xcb_randr_crtc_t, xcb_randr_output_t) noexcept;
673  void outputAdd(const xcb_randr_get_crtc_info_reply_t*, const xcb_randr_get_output_info_reply_t*) noexcept;
674 
675  // }}}
676  // {{{ Utility
677 
678  std::mutex xenium_window_mutex = {};
679 
680  // -------------------------------------------------- //
681 
682  [[nodiscard]] std::pair<float, float> convertPixelToMm(const Xenium::Output&, int32_t, int32_t) const noexcept;
683  [[nodiscard]] std::pair<float, float> convertPixelToPercent(const Xenium::Output&, int32_t, int32_t) const noexcept;
684  [[nodiscard]] std::pair<int32_t, int32_t> convertMmToPixel(const Xenium::Output&, float, float) const noexcept;
685  [[nodiscard]] std::pair<int32_t, int32_t> convertPercentToPixel(const Xenium::Output&, float, float) const noexcept;
686 
687  // }}}
688  // {{{ Window
689 
690  enum struct SizeUnit
691  { Millimeter
692  , Percent
693  , Pixel
694  };
695 
696  struct MotifWmHints
697  {
698  uint32_t flags;
699  uint32_t functions;
700  uint32_t decorations;
701  int32_t input_mode;
702  uint32_t status;
703  };
704 
705  struct WindowCreateData
706  {
707  std::promise<void> barrier;
708  std::error_code error;
709  Xenium::WindowId window_id;
710  Xenium::OutputId output_id;
711  xcb_atom_t atom_close_request;
712  xcb_gcontext_t gc;
713  Xenium::SizeUnit size_unit;
714  Xenium::SizeMm size_mm;
715  Xenium::SizePercent size_percent;
716  Xenium::SizePixel size_pixel;
717  uint32_t value_mask;
718  xcb_create_window_value_list_t& value_list;
719  };
720 
721  struct WindowDestroyData
722  {
723  std::promise<void> barrier;
724  Xenium::WindowId window_id;
725  xcb_gcontext_t gc;
726  };
727 
728  struct WindowDeleteData
729  {
730  Xenium::Lambda close_request_lambda = { };
731  xcb_atom_t atom_close_request = XCB_ATOM_NONE;
732  };
733 
734  struct WindowSizeData
735  {
736  Xenium::SizeMm mm = { };
737  Xenium::SizeMm mm_minimum = { };
738  Xenium::SizeMm mm_maximum = { };
739  Xenium::LambdaSizeMm mm_lambda = { };
740  Xenium::SizePercent percent = { };
741  Xenium::SizePercent percent_minimum = { };
742  Xenium::SizePercent percent_maximum = { };
743  Xenium::LambdaSizePercent percent_lambda = { };
744  Xenium::SizePixel pixel = { };
745  Xenium::SizePixel pixel_minimum = { };
746  Xenium::SizePixel pixel_maximum = { };
747  Xenium::LambdaSizePixel pixel_lambda = { };
748  Xenium::SizeUnit unit = { };
749  };
750 
751  struct WindowModeData
752  {
753  Xenium::WindowMode window_mode = Xenium::WindowMode::Normal;
754  Xenium::LambdaWindowMode lambda = { };
755  };
756 
757  struct WindowDecorationsData
758  {
759  Xenium::WindowDecorations window_decorations = Xenium::WindowDecorations::Server_Side;
760  Xenium::LambdaWindowDecorations lambda = { };
761  };
762 
763  struct WindowOnButtonData
764  {
765  Xenium::LambdaButtonMm lambda_mm = { };
766  Xenium::LambdaButtonPercent lambda_percent = { };
767  Xenium::LambdaButtonPixel lambda_pixel = { };
768  };
769 
770  struct WindowOnEnterData
771  {
772  Xenium::LambdaPointMm lambda_mm = { };
773  Xenium::LambdaPointPercent lambda_percent = { };
774  Xenium::LambdaPointPixel lambda_pixel = { };
775  };
776 
777  struct WindowOnMotionData
778  {
779  Xenium::LambdaPointMm lambda_mm = { };
780  Xenium::LambdaPointPercent lambda_percent = { };
781  Xenium::LambdaPointPixel lambda_pixel = { };
782  };
783 
784  struct WindowKeyboardData
785  {
786  Xenium::Lambda on_enter = { };
787  Xenium::Lambda on_leave = { };
788  };
789 
790  using WindowDecorationsMap = std::unordered_map<Xenium::WindowId, Xenium::WindowDecorationsData>;
791  using WindowDeleteMap = std::unordered_map<Xenium::WindowId, Xenium::WindowDeleteData>;
792  using WindowFocusMap = std::unordered_map<Xenium::WindowId, Xenium::LambdaBool>;
793  using WindowKeyboard = std::unordered_map<Xenium::WindowId, Xenium::WindowKeyboardData>;
794  using WindowMap = std::unordered_map<Xenium::WindowId, Xenium::Window*>;
795  using WindowModeMap = std::unordered_map<Xenium::WindowId, Xenium::WindowModeData>;
796  using WindowOnAxisMap = std::unordered_map<Xenium::WindowId, Xenium::LambdaAxis>;
797  using WindowOnButtonMap = std::unordered_map<Xenium::WindowId, Xenium::WindowOnButtonData>;
798  using WindowOnEnterMap = std::unordered_map<Xenium::WindowId, Xenium::WindowOnEnterData>;
799  using WindowOnKeyMap = std::unordered_map<Xenium::WindowId, Xenium::LambdaKey>;
800  using WindowOnLeaveMap = std::unordered_map<Xenium::WindowId, Xenium::Lambda>;
801  using WindowOnMotionMap = std::unordered_map<Xenium::WindowId, Xenium::WindowOnMotionData>;
802  using WindowOutputMap = std::unordered_map<Xenium::WindowId, Xenium::OutputId>;
803  using WindowReadyMap = std::unordered_map<Xenium::WindowId, bool>;
804  using WindowSizeMap = std::unordered_map<Xenium::WindowId, Xenium::WindowSizeData>;
805  using WindowToCreate = std::vector<Xenium::WindowCreateData*>;
806  using WindowToDestroy = std::vector<Xenium::WindowDestroyData*>;
807 
808  // -------------------------------------------------- //
809 
810  Xenium::WindowDecorationsMap window_decorations_map = {};
811  Xenium::WindowDeleteMap window_delete_map = {};
812  Xenium::WindowFocusMap window_focus_map = {};
813  Xenium::WindowKeyboard window_keyboard = {};
814  Xenium::WindowMap window_map = {};
815  Xenium::WindowModeMap window_mode_map = {};
816  Xenium::WindowOnAxisMap window_on_axis_map = {};
817  Xenium::WindowOnButtonMap window_on_button_map = {};
818  Xenium::WindowOnEnterMap window_on_enter_map = {};
819  Xenium::WindowOnKeyMap window_on_key_map = {};
820  Xenium::WindowOnLeaveMap window_on_leave_map = {};
821  Xenium::WindowOnMotionMap window_on_motion_map = {};
822  Xenium::WindowOutputMap window_output_map = {};
823  Xenium::WindowReadyMap window_ready_map = {};
824  Xenium::WindowSizeMap window_size_map = {};
825  Xenium::WindowToCreate window_to_create = {};
826  Xenium::WindowToDestroy window_to_destroy = {};
827 
828  // -------------------------------------------------- //
829 
830  [[nodiscard]] std::error_code windowBorder(const Xenium::WindowId, const bool) noexcept;
831  void windowCreateAddToQueue(Xenium::WindowCreateData*) noexcept;
832  void windowDestroyAddToQueue(Xenium::WindowDestroyData*) noexcept;
833  [[nodiscard]] std::error_code windowLocationSet(const Xenium::WindowId, const Xenium::PointPixel&) noexcept;
834  [[nodiscard]] std::error_code windowMinimize(const Xenium::WindowId) noexcept;
835  [[nodiscard]] std::error_code windowModeSet(const Xenium::WindowId, const Xenium::WindowMode, const Xenium::WindowMode) noexcept;
836  bool windowPropertySet(Xenium::WindowId, const xcb_atom_t, const xcb_atom_t, xcb_generic_error_t&) noexcept;
837  bool windowPropertySet(Xenium::WindowId, const xcb_atom_t, const std::string&, xcb_generic_error_t&) noexcept;
838  void windowReadySet(const Xenium::WindowId) noexcept;
839  void windowReadyWait(const Xenium::WindowId) noexcept;
840  void windowResizeTo(const Xenium::Output&, Xenium::WindowSizeData&, const xcb_configure_notify_event_t*) noexcept;
841  std::error_code windowSizeSet(const Xenium::WindowId, const Xenium::SizePixel&) noexcept;
842  [[nodiscard]] std::error_code windowSizeSetMinMax(const Xenium::WindowId, const int32_t, const int32_t, const int32_t, const int32_t) noexcept;
843  std::error_code windowSizeSetMinMax(const Xenium::Output&, const Xenium::WindowId, Xenium::WindowSizeData&) noexcept;
844 
845  // }}}
846  // {{{ XCB
847 
848  xcb_connection_t* connection = nullptr;
849  const xcb_setup_t* setup = nullptr;
850  xcb_screen_t* screen = nullptr;
851 
852  // -------------------------------------------------- //
853 
854  void xcbEvent(const xcb_button_press_event_t*) noexcept;
855  void xcbEvent(const xcb_client_message_event_t*) noexcept;
856  void xcbEvent(const xcb_configure_notify_event_t*) noexcept;
857  void xcbEvent(const xcb_enter_notify_event_t*) noexcept;
858  void xcbEvent(const xcb_expose_event_t*) noexcept;
859  void xcbEvent(const xcb_focus_in_event_t*) noexcept;
860  void xcbEvent(const xcb_gravity_notify_event_t*) noexcept;
861  void xcbEvent(const xcb_key_press_event_t*) noexcept;
862  void xcbEvent(const xcb_map_notify_event_t*) noexcept;
863  void xcbEvent(const xcb_motion_notify_event_t*) noexcept;
864  void xcbEvent(const xcb_property_notify_event_t*) noexcept;
865  void xcbEvent(const xcb_reparent_notify_event_t*) noexcept;
866  void xcbEvent(const xcb_unmap_notify_event_t*) noexcept;
867 
868  void xcbWindowCreate(Xenium::WindowCreateData*) noexcept;
869  [[nodiscard]] std::error_code xcbWindowCreateValidate(Xenium::WindowCreateData*) noexcept;
870  [[nodiscard]] std::error_code xcbWindowCreateClient(Xenium::WindowCreateData*) noexcept;
871  [[nodiscard]] std::error_code xcbWindowCreateInit(Xenium::WindowCreateData*) noexcept;
872  void xcbWindowDestroy(Xenium::WindowDestroyData*) noexcept;
873 
874  // }}}
875  // {{{ XCB : Atom
876 
877  xcb_atom_t atom_motif_wm_hints = XCB_ATOM_NONE;
878  xcb_atom_t atom_net_frame_extents = XCB_ATOM_NONE;
879  xcb_atom_t atom_net_wm_state = XCB_ATOM_NONE;
880  xcb_atom_t atom_net_wm_state_fullscreen = XCB_ATOM_NONE;
881  xcb_atom_t atom_net_wm_state_hidden = XCB_ATOM_NONE;
882  xcb_atom_t atom_net_wm_state_maximized_horz = XCB_ATOM_NONE;
883  xcb_atom_t atom_net_wm_state_maximized_vert = XCB_ATOM_NONE;
884  xcb_atom_t atom_wm_change_state = XCB_ATOM_NONE;
885  xcb_atom_t atom_wm_delete_window = XCB_ATOM_NONE;
886  xcb_atom_t atom_wm_protocols = XCB_ATOM_NONE;
887 
888  // -------------------------------------------------- //
889 
890  [[nodiscard]] std::error_code atomInit() noexcept;
891  [[nodiscard]] xcb_atom_t atomCreateDeleteWindow(const WindowId, xcb_generic_error_t&) noexcept;
892  [[nodiscard]] std::string atomName(const xcb_atom_t) noexcept;
893  [[nodiscard]] std::vector<xcb_atom_t> atomValueAtom(const Xenium::WindowId, const xcb_atom_t, xcb_generic_error_t&) noexcept;
894  [[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;
895  [[nodiscard]] xcb_atom_t internAtom(const std::string&, const bool, xcb_generic_error_t&) noexcept;
896  [[nodiscard]] xcb_intern_atom_cookie_t internAtomRequest(const std::string&, const bool = true) noexcept;
897  [[nodiscard]] xcb_atom_t internAtomReply(const xcb_intern_atom_cookie_t, xcb_generic_error_t&) noexcept;
898 
899  // }}}
900  // {{{ XCB : RandR
901 
902  int randr_error_base = 0;
903  int randr_event_base = 0;
904  int randr_query_version_major = 0;
905  int randr_query_version_minor = 0;
906 
907  // -------------------------------------------------- //
908 
909  [[nodiscard]] std::error_code randrInit() noexcept;
910  void randrEvent(const xcb_randr_crtc_change_t*) noexcept;
911  void randrEvent(const xcb_randr_output_change_t*) noexcept;
912  void randrEvent(const xcb_randr_notify_event_t*) noexcept;
913  void randrEvent(const xcb_randr_screen_change_notify_event_t*) noexcept;
914 
915  // }}}
916  // {{{ XCB : XKB
917 
918  struct XkbControls
919  {
920  uint32_t repeat_delay_ms = 600;
921  uint32_t repeat_interval_ms = 50;
922  };
923 
924  struct KeyData
925  {
926  Xenium::Key key = { 0, 0, Xenium::KeyState::Released };
927  Xenium::KeyModifier modifier = { 0 };
928  Xenium::WindowId window_id = { 0 };
929  uint32_t repeat_time = { 0 };
930  };
931 
932  using KeyDataArray = std::array<Xenium::KeyData, 256>;
933 
934  // -------------------------------------------------- //
935 
936  Xenium::KeyDataArray key_data_array = { };
937  Xenium::KeyModifier key_modifier = { 0 };
938  Xenium::XkbControls xkb_controls = { };
939  uint16_t xkb_modifier_pressed = 0;
940 
941  // -------------------------------------------------- //
942 
943  [[nodiscard]] std::error_code xkbInit() noexcept;
944  inline void keyDataArrayClear() noexcept;
945  inline void keyDataArrayProcess() noexcept;
946  void xkbControlsUpdate() noexcept;
947  void xkbIndicatorStateUpdate() noexcept;
948 
949  // }}}
950  // {{{ XCB : Utility
951 
952  [[nodiscard]] bool requestCheckHasError(const xcb_void_cookie_t&, xcb_generic_error_t&) noexcept;
953 
954  // }}}
955 
956  Xenium(const Xenium&) = delete;
957  Xenium& operator=(const Xenium&) = delete;
958  }; // class Xenium
959 
960  // }}}
961  // {{{ Convenience
962 
963  [[nodiscard]] std::string to_string(const std::vector<xcb_atom_t>&) noexcept;
964  [[nodiscard]] std::string to_string(const std::vector<int32_t>&) noexcept;
965  [[nodiscard]] std::string to_string(const xcb_generic_error_t&) noexcept;
966  [[nodiscard]] std::string to_string(const xcb_button_press_event_t&) noexcept;
967  [[nodiscard]] std::string to_string(const xcb_client_message_event_t&) noexcept;
968  [[nodiscard]] std::string to_string(const xcb_configure_notify_event_t&) noexcept;
969  [[nodiscard]] std::string to_string(const xcb_enter_notify_event_t&) noexcept;
970  [[nodiscard]] std::string to_string(const xcb_expose_event_t&) noexcept;
971  [[nodiscard]] std::string to_string(const xcb_focus_in_event_t&) noexcept;
972  [[nodiscard]] std::string to_string(const xcb_generic_event_t&) noexcept;
973  [[nodiscard]] std::string to_string(const xcb_gravity_notify_event_t&) noexcept;
974  [[nodiscard]] std::string to_string(const xcb_key_press_event_t&) noexcept;
975  [[nodiscard]] std::string to_string(const xcb_map_notify_event_t&) noexcept;
976  [[nodiscard]] std::string to_string(const xcb_motion_notify_event_t&) noexcept;
977  [[nodiscard]] std::string to_string(const xcb_property_notify_event_t&) noexcept;
978  [[nodiscard]] std::string to_string(const xcb_reparent_notify_event_t&) noexcept;
979  [[nodiscard]] std::string to_string(const xcb_unmap_notify_event_t&) noexcept;
980  [[nodiscard]] std::string to_string(const xcb_format_t&) noexcept;
981  [[nodiscard]] std::string to_string(const xcb_screen_t&) noexcept;
982  [[nodiscard]] std::string to_string(const xcb_setup_t&) noexcept;
983  [[nodiscard]] std::string to_string(const xcb_randr_screen_change_notify_event_t&) noexcept;
984  [[nodiscard]] std::string to_string(const Xenium::Key&) noexcept;
985  [[nodiscard]] std::string to_string(const Xenium::KeyModifier&) noexcept;
986  [[nodiscard]] std::string to_string(const Xenium::KeyState) noexcept;
987  [[nodiscard]] std::string to_string(const Xenium::Output&) noexcept;
988  [[nodiscard]] std::string to_string(const Xenium::PointMm) noexcept;
989  [[nodiscard]] std::string to_string(const Xenium::PointPercent) noexcept;
990  [[nodiscard]] std::string to_string(const Xenium::PointPixel) noexcept;
991  [[nodiscard]] std::string to_string(const Xenium::PointerAxis&) noexcept;
992  [[nodiscard]] std::string to_string(const Xenium::PointerAxisSource) noexcept;
993  [[nodiscard]] std::string to_string(const Xenium::PointerAxisType) noexcept;
994  [[nodiscard]] std::string to_string(const Xenium::PointerButton&) noexcept;
995  [[nodiscard]] std::string to_string(const Xenium::PointerButtonState&) noexcept;
996  [[nodiscard]] std::string to_string(const Xenium::SizeMm&) noexcept;
997  [[nodiscard]] std::string to_string(const Xenium::SizePercent&) noexcept;
998  [[nodiscard]] std::string to_string(const Xenium::SizePixel&) noexcept;
999  [[nodiscard]] std::string to_string(const Xenium::WindowDecorations) noexcept;
1000  [[nodiscard]] std::string to_string(const Xenium::WindowMode) noexcept;
1001 
1002  // }}}
1003 }
1004 
1005 
1006 // {{{ Implementation
1007 
1008 #ifdef ZAKERO_XENIUM_IMPLEMENTATION
1009 
1010 // {{{ Macros
1011 
1012 /******************************************************************************
1013  * Macros
1014  */
1015 
1016 // {{{ Macros : Doxygen
1017 
1018 #ifdef ZAKERO__DOXYGEN_DEFINE_DOCS
1019 
1020 // Only used for generating Doxygen documentation
1021 
1041 #define ZAKERO_XENIUM_IMPLEMENTATION
1042 
1051 #define ZAKERO_XENIUM_ENABLE_DEBUG
1052 
1061 #define ZAKERO_XENIUM_ENABLE_SAFE_MODE
1062 
1063 #endif // ZAKERO__DOXYGEN_DEFINE_DOCS
1064 
1065 // }}}
1066 // {{{ Macros : XCB
1067 
1068 #ifndef _NET_WM_STATE_REMOVE
1072 #define _NET_WM_STATE_REMOVE 0
1073 #endif
1074 
1075 #ifndef _NET_WM_STATE_ADD
1079 #define _NET_WM_STATE_ADD 1
1080 #endif
1081 
1082 #ifndef _NET_WM_STATE_TOGGLE
1086 #define _NET_WM_STATE_TOGGLE 2
1087 #endif
1088 
1089 // }}}
1090 // {{{ Macros : Debugging
1091 
1104 #ifdef ZAKERO_XENIUM_ENABLE_DEBUG
1105 #define ZAKERO_XENIUM__DEBUG_ENABLED true
1106 #else
1107 #define ZAKERO_XENIUM__DEBUG_ENABLED false
1108 #endif
1109 
1125 #ifdef ZAKERO_XENIUM_ENABLE_DEBUG
1126 #define ZAKERO_XENIUM__DEBUG_DISABLED false
1127 #else
1128 #define ZAKERO_XENIUM__DEBUG_DISABLED true
1129 #endif
1130 
1131 
1144 #ifndef ZAKERO_XENIUM_DEBUG_STREAM
1145 #define ZAKERO_XENIUM_DEBUG_STREAM std::cerr
1146 #endif
1147 
1148 
1166 #define ZAKERO_XENIUM__DEBUG \
1167  if(ZAKERO_XENIUM__DEBUG_DISABLED) {} \
1168  else ZAKERO_XENIUM_DEBUG_STREAM \
1169  << "pid(" << std::to_string(int64_t(ZAKERO_PID)) \
1170  << ") " __FILE__ "(" \
1171  << std::to_string(__LINE__) \
1172  << ") " \
1173  << __PRETTY_FUNCTION__ \
1174  << " "
1175 
1192 #define ZAKERO_XENIUM__DEBUG_VAR(var_) \
1193  ZAKERO_XENIUM__DEBUG \
1194  << #var_ << ": " << var_ \
1195  << "\n";
1196 
1212 #define ZAKERO_XENIUM__DEBUG_BOOL(var_) \
1213  ZAKERO_XENIUM__DEBUG \
1214  << #var_ << ": " << std::boolalpha << var_ \
1215  << "\n";
1216 
1232 #define ZAKERO_XENIUM__DEBUG_ERROR(var_) \
1233  ZAKERO_XENIUM__DEBUG \
1234  << "Error: " << var_ \
1235  << "\n";
1236 
1237 // }}}
1238 // {{{ Macros : Data Conversion Lookup Tables
1239 
1245 #define ZAKERO_XENIUM__OUTPUT_SUBPIXEL \
1246  X(XCB_RENDER_SUB_PIXEL_UNKNOWN , "Unkown Geometry" ) \
1247  X(XCB_RENDER_SUB_PIXEL_HORIZONTAL_RGB , "Horizontal RGB" ) \
1248  X(XCB_RENDER_SUB_PIXEL_HORIZONTAL_BGR , "Horizontal BGR" ) \
1249  X(XCB_RENDER_SUB_PIXEL_VERTICAL_RGB , "Vertical RGB" ) \
1250  X(XCB_RENDER_SUB_PIXEL_VERTICAL_BGR , "Vertical BGR" ) \
1251  X(XCB_RENDER_SUB_PIXEL_NONE , "No Geometry" ) \
1252 
1258 #define ZAKERO_XENIUM__OUTPUT_TRANSFORM \
1259  X(XCB_RANDR_TRANSFORM_UNIT , "Unit" ) \
1260  X(XCB_RANDR_TRANSFORM_SCALE_UP , "Scale Up" ) \
1261  X(XCB_RANDR_TRANSFORM_SCALE_DOWN , "Scale Down" ) \
1262  X(XCB_RANDR_TRANSFORM_PROJECTIVE , "Projective" ) \
1263 
1264 // }}}
1265 
1276 #define ZAKERO_XENIUM__ERROR(err_) std::error_code(err_, XeniumErrorCategory)
1277 
1278 // }}}
1279 
1280 namespace zakero
1281 {
1282 // {{{ Anonymous Namespace
1283 
1284 namespace
1285 {
1286  // {{{ Cursor Names (Not used yet)
1287  /*
1288  * \brief Common cursor names
1289  *
1290  * These were found in:
1291  * - gdkcursor-wayland.c
1292  * - /usr/share/icons/whiteglass/cursors
1293  const std::array<const char*, 131> common_cursor_names =
1294  { "X_cursor"
1295  , "alias"
1296  , "all-scroll"
1297  , "arrow"
1298  , "base_arrow_down"
1299  , "base_arrow_up"
1300  , "bd_double_arrow"
1301  , "boat"
1302  , "bottom_left_corner"
1303  , "bottom_right_corner"
1304  , "bottom_side"
1305  , "bottom_tee"
1306  , "cell"
1307  , "center_ptr"
1308  , "circle"
1309  , "closedhand"
1310  , "col-resize"
1311  , "color-picker"
1312  , "context-menu"
1313  , "copy"
1314  , "cross"
1315  , "cross_reverse"
1316  , "crossed_circle"
1317  , "crosshair"
1318  , "default"
1319  , "dnd-copy"
1320  , "dnd-link"
1321  , "dnd-move"
1322  , "dnd-no-drop"
1323  , "dnd-none"
1324  , "dot"
1325  , "dot_box_mask"
1326  , "double_arrow"
1327  , "down-arrow"
1328  , "draft"
1329  , "draft_large"
1330  , "draft_small"
1331  , "draped_box"
1332  , "e-resize"
1333  , "ew-resize"
1334  , "exchange"
1335  , "fd_double_arrow"
1336  , "fleur"
1337  , "forbidden"
1338  , "grab"
1339  , "grabbing"
1340  , "gumby"
1341  , "h_double_arrow"
1342  , "half-busy"
1343  , "hand"
1344  , "hand1"
1345  , "hand2"
1346  , "help"
1347  , "ibeam"
1348  , "left-arrow"
1349  , "left_ptr"
1350  , "left_ptr_help"
1351  , "left_ptr_watch"
1352  , "left_side"
1353  , "left_tee"
1354  , "link"
1355  , "ll_angle"
1356  , "lr_angle"
1357  , "move"
1358  , "n-resize"
1359  , "ne-resize"
1360  , "nesw-resize"
1361  , "no-drop"
1362  , "not-allowed"
1363  , "ns-resize"
1364  , "nw-resize"
1365  , "nwse-resize"
1366  , "openhand"
1367  , "pencil"
1368  , "pirate"
1369  , "plus"
1370  , "pointer"
1371  , "pointing_hand"
1372  , "progress"
1373  , "question_arrow"
1374  , "right-arrow"
1375  , "right_ptr"
1376  , "right_side"
1377  , "right_tee"
1378  , "row-resize"
1379  , "s-resize"
1380  , "sailboat"
1381  , "sb_down_arrow"
1382  , "sb_h_double_arrow"
1383  , "sb_left_arrow"
1384  , "sb_right_arrow"
1385  , "sb_up_arrow"
1386  , "sb_v_double_arrow"
1387  , "se-resize"
1388  , "shuttle"
1389  , "size-bdiag"
1390  , "size-fdiag"
1391  , "size-hor"
1392  , "size-ver"
1393  , "size_all"
1394  , "size_bdiag"
1395  , "size_fdiag"
1396  , "size_hor"
1397  , "size_ver"
1398  , "sizing"
1399  , "split_h"
1400  , "split_v"
1401  , "sw-resize"
1402  , "target"
1403  , "tcross"
1404  , "text"
1405  , "top_left_arrow"
1406  , "top_left_corner"
1407  , "top_right_corner"
1408  , "top_side"
1409  , "top_tee"
1410  , "trek"
1411  , "ul_angle"
1412  , "up-arrow"
1413  , "ur_angle"
1414  , "v_double_arrow"
1415  , "vertical-text"
1416  , "w-resize"
1417  , "wait"
1418  , "watch"
1419  , "wayland-cursor"
1420  , "whats_this"
1421  , "x-cursor"
1422  , "xterm"
1423  , "zoom-in"
1424  , "zoom-out"
1425  };
1426  */
1427  // }}}
1428 
1432  constexpr uint32_t Size_Max = (uint32_t)std::numeric_limits<int32_t>::max();
1433 
1434  //constexpr uint8_t XCB_KEY_REPEAT = XCB_KEY_PRESS | 0x80;
1435 
1439  constexpr uint32_t XCB_XKB_INDICATOR_STATE_CAPSLOCK = 0x00000001;
1440 
1444  constexpr uint32_t XCB_XKB_INDICATOR_STATE_NUMLOCK = 0x00000002;
1445 
1449  constexpr std::array<uint32_t, 8> Pointer_Button_Event_Code =
1450  { BTN_LEFT // 0x110 272
1451  , BTN_MIDDLE // 0x112 274
1452  , BTN_RIGHT // 0x111 273
1453  , BTN_SIDE // 0x113 275
1454  , BTN_EXTRA // 0x114 276
1455  , BTN_FORWARD // 0x115 277
1456  , BTN_BACK // 0x116 278
1457  , BTN_TASK // 0x116 279
1458  };
1459 
1463  const uint32_t Default_Value_Mask = 0
1464  //| XCB_CW_BACK_PIXMAP // Not Used
1465  | XCB_CW_BACK_PIXEL // Not Used
1466  //| XCB_CW_BORDER_PIXMAP // Not Used
1467  //| XCB_CW_BORDER_PIXEL // Not Used
1468  | XCB_CW_BIT_GRAVITY
1469  | XCB_CW_WIN_GRAVITY
1470  | XCB_CW_BACKING_STORE
1471  //| XCB_CW_BACKING_PLANES // Not Used
1472  //| XCB_CW_BACKING_PIXEL // Not Used
1473  //| XCB_CW_OVERRIDE_REDIRECT // Future?
1474  | XCB_CW_SAVE_UNDER
1475  | XCB_CW_EVENT_MASK
1476  //| XCB_CW_DONT_PROPAGATE // Future?
1477  | XCB_CW_COLORMAP
1478  //| XCB_CW_CURSOR // Future?
1479  ;
1480 
1484  xcb_create_window_value_list_t Default_Value_List =
1485  { .background_pixmap = XCB_BACK_PIXMAP_NONE
1486  , .background_pixel = 0
1487  , .border_pixmap = XCB_BACK_PIXMAP_NONE
1488  , .border_pixel = 0
1489  , .bit_gravity = XCB_GRAVITY_CENTER
1490  , .win_gravity = XCB_GRAVITY_NORTH_EAST
1491  , .backing_store = XCB_BACKING_STORE_NOT_USEFUL
1492  , .backing_planes = 0
1493  , .backing_pixel = 0
1494  , .override_redirect = 0
1495  , .save_under = 0
1496  , .event_mask = 0
1497  | XCB_EVENT_MASK_KEY_PRESS
1498  | XCB_EVENT_MASK_KEY_RELEASE
1499  | XCB_EVENT_MASK_BUTTON_PRESS
1500  | XCB_EVENT_MASK_BUTTON_RELEASE
1501  | XCB_EVENT_MASK_ENTER_WINDOW
1502  | XCB_EVENT_MASK_LEAVE_WINDOW
1503  | XCB_EVENT_MASK_POINTER_MOTION
1512  | XCB_EVENT_MASK_EXPOSURE
1513  //| XCB_EVENT_MASK_VISIBILITY_CHANGE
1514  | XCB_EVENT_MASK_STRUCTURE_NOTIFY
1518  | XCB_EVENT_MASK_FOCUS_CHANGE
1519  | XCB_EVENT_MASK_PROPERTY_CHANGE
1522  , .do_not_propogate_mask = XCB_EVENT_MASK_NO_EVENT
1523  , .colormap = XCB_COPY_FROM_PARENT
1524  , .cursor = 0
1525  };
1526 
1527 
1537  class XeniumErrorCategory_
1538  : public std::error_category
1539  {
1540  public:
1547  constexpr XeniumErrorCategory_() noexcept
1548  {
1549  }
1550 
1556  const char* name() const noexcept override
1557  {
1558  return "zakero.Xenium";
1559  }
1560 
1567  std::string message(int condition
1568  ) const noexcept override
1569  {
1570  switch(condition)
1571  {
1572 #define X(name_, val_, mesg_) \
1573  case val_: return mesg_;
1574  ZAKERO_XENIUM__ERROR_DATA
1575 #undef X
1576  }
1577 
1578  return "Unknown error condition";
1579  }
1580  } XeniumErrorCategory;
1581 
1582 
1587  Xenium::Lambda Lambda_DoNothing = []() noexcept {};
1588  Xenium::LambdaKey LambdaKey_DoNothing = [](const Xenium::Key&, const Xenium::KeyModifier&) noexcept {};
1589  Xenium::LambdaAxis LambdaAxis_DoNothing = [](const Xenium::PointerAxis&, const Xenium::KeyModifier&) noexcept {};
1590  Xenium::LambdaButtonMm LambdaButtonMm_DoNothing = [](const Xenium::PointerButton&, const Xenium::PointMm&, const Xenium::KeyModifier&) noexcept {};
1591  Xenium::LambdaButtonPercent LambdaButtonPercent_DoNothing = [](const Xenium::PointerButton&, const Xenium::PointPercent&, const Xenium::KeyModifier&) noexcept {};
1592  Xenium::LambdaButtonPixel LambdaButtonPixel_DoNothing = [](const Xenium::PointerButton&, const Xenium::PointPixel&, const Xenium::KeyModifier&) noexcept {};
1593  Xenium::LambdaPointMm LambdaPointMm_DoNothing = [](const Xenium::PointMm&, const Xenium::KeyModifier&) noexcept {};
1594  Xenium::LambdaPointPercent LambdaPointPercent_DoNothing = [](const Xenium::PointPercent&, const Xenium::KeyModifier&) noexcept {};
1595  Xenium::LambdaPointPixel LambdaPointPixel_DoNothing = [](const Xenium::PointPixel&, const Xenium::KeyModifier&) noexcept {};
1596  Xenium::LambdaBool LambdaBool_DoNothing = [](const bool) noexcept {};
1597  Xenium::LambdaOutputId LambdaOutputId_DoNothing = [](const Xenium::OutputId) noexcept {};
1598  Xenium::LambdaWindowDecorations LambdaWindowDecorations_DoNothing = [](const Xenium::WindowDecorations) noexcept {};
1599  Xenium::LambdaWindowMode LambdaWindowMode_DoNothing = [](const Xenium::WindowMode) noexcept {};
1600  Xenium::LambdaSizeMm LambdaSizeMm_DoNothing = [](const Xenium::SizeMm&) noexcept {};
1601  Xenium::LambdaSizePercent LambdaSizePercent_DoNothing = [](const Xenium::SizePercent&) noexcept {};
1602  Xenium::LambdaSizePixel LambdaSizePixel_DoNothing = [](const Xenium::SizePixel&) noexcept {};
1616  std::error_code convertConnectionError(int xcb_error_code
1617  ) noexcept
1618  {
1619  switch(xcb_error_code)
1620  {
1621  case 0:
1622  return ZAKERO_XENIUM__ERROR(Xenium::Error_None);
1623 
1624  case XCB_CONN_ERROR:
1625  return ZAKERO_XENIUM__ERROR(Xenium::Error_Connection_Failed);
1626 
1627  case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
1628  return ZAKERO_XENIUM__ERROR(Xenium::Error_Extension_Not_Supported);
1629 
1630  case XCB_CONN_CLOSED_MEM_INSUFFICIENT:
1631  return ZAKERO_XENIUM__ERROR(Xenium::Error_Not_Enough_Memory);
1632 
1633  case XCB_CONN_CLOSED_REQ_LEN_EXCEED:
1634  return ZAKERO_XENIUM__ERROR(Xenium::Error_Request_Too_Long);
1635 
1636  case XCB_CONN_CLOSED_PARSE_ERR:
1637  return ZAKERO_XENIUM__ERROR(Xenium::Error_Invalid_Display_Name);
1638 
1639  case XCB_CONN_CLOSED_INVALID_SCREEN:
1640  return ZAKERO_XENIUM__ERROR(Xenium::Error_Invalid_Screen);
1641 
1642  default:
1643  return ZAKERO_XENIUM__ERROR(Xenium::Error_Unknown);
1644  }
1645  }
1646 
1647 
1660  template<class Type
1661  >
1662  std::error_code validateMinMax(const Type& min
1663  , const Type& max
1664  ) noexcept
1665  {
1666  if((min.width < 0)
1667  || (min.height < 0)
1668  || (max.width < 0)
1669  || (max.height < 0)
1670  )
1671  {
1672  return ZAKERO_XENIUM__ERROR(Xenium::Error_Window_Size_Too_Small);
1673  }
1674 
1675  if((max.width > 0)
1676  && (min.width > max.width)
1677  )
1678  {
1679  return ZAKERO_XENIUM__ERROR(Xenium::Error_Minimum_Size_Greater_Than_Maximum_Size);
1680  }
1681 
1682  if((max.height > 0)
1683  && (min.height > max.height)
1684  )
1685  {
1686  return ZAKERO_XENIUM__ERROR(Xenium::Error_Minimum_Size_Greater_Than_Maximum_Size);
1687  }
1688 
1689  return ZAKERO_XENIUM__ERROR(Xenium::Error_None);
1690  }
1691 }
1692 
1693 // }}}
1694 // {{{ Documentation
1695 
1821 /* Disabled because Doxygen does not support "enum classes"
1822  *
1823  * \var Xenium::Released
1824  * \brief The key was released
1825  *
1826  * \var Xenium::Pressed
1827  * \brief The key was pressed
1828  *
1829  * \var Xenium::Repeat
1830  * \brief The key is being held down
1831  */
1832 
2080 /* Disabled because Doxygen does not support "enum classes"
2081  *
2082  * \var Xenium::Unknown
2083  * \brief Unknown
2084  *
2085  * \var Xenium::Continuous
2086  * \brief Continuous
2087  *
2088  * \var Xenium::Finger
2089  * \brief Finger
2090  *
2091  * \var Xenium::Wheel
2092  * \brief Wheel
2093  *
2094  * \var Xenium::Wheel_Tilt
2095  * \brief Wheel Tilt
2096  */
2097 
2103 /* Disabled because Doxygen does not support "enum classes"
2104  *
2105  * \var Xenium::Unknown
2106  * \brief Unknown
2107  *
2108  * \var Xenium::Horizontal
2109  * \brief Horizontal
2110  *
2111  * \var Xenium::Vertical
2112  * \brief Vertical
2113  */
2114 
2141 /* Disabled because Doxygen does not support "enum classes"
2142  *
2143  * \var Xenium::Released
2144  * \brief Released
2145  *
2146  * \var Xenium::Pressed
2147  * \brief Pressed
2148  */
2149 
2206 /* Disabled because Doxygen does not support "enum classes"
2207  *
2208  * \var Xenium::Client_Side
2209  * \brief The user app must draw the decorations.
2210  *
2211  * \var Xenium::Server_Side
2212  * \brief The X11 server will draw the decorations.
2213  */
2214 
2220 /* Disabled because Doxygen does not support "enum classes"
2221  *
2222  * \var Xenium::Normal
2223  * \brief A normal window.
2224  *
2225  * \var Xenium::Fullscreen
2226  * \brief A window that uses the entire screen, no borders.
2227  *
2228  * \var Xenium::Maximized
2229  * \brief A window that uses as much of the screen as possible.
2230  */
2231 
2232 
2339 // }}}
2340 // {{{ Constructor / Destructor
2341 
2347 Xenium::Xenium() noexcept
2348  : event_loop()
2349  , event_loop_is_running(false)
2350 {
2351 }
2352 
2353 
2364 {
2365  if(event_loop_is_running || event_loop.joinable())
2366  {
2367  event_loop.request_stop();
2368  event_loop.join();
2369  }
2370 
2371  disconnect();
2372 }
2373 
2374 // }}}
2375 // {{{ Connection
2376 
2397 {
2398  std::error_code error;
2399 
2400  return Xenium::connect("", error);
2401 }
2402 
2403 
2424 Xenium* Xenium::connect(const std::string& display
2425  ) noexcept
2426 {
2427  std::error_code error;
2428 
2429  return Xenium::connect(display, error);
2430 }
2431 
2432 
2457 Xenium* Xenium::connect(std::error_code& error
2458  ) noexcept
2459 {
2460  return Xenium::connect("", error);
2461 }
2462 
2463 
2489 Xenium* Xenium::connect(const std::string& display
2490  , std::error_code& error
2491  ) noexcept
2492 {
2493  const char* display_name = nullptr;
2494 
2495  if(display.empty() == false)
2496  {
2497  display_name = display.c_str();
2498  }
2499 
2500  // --- Connect To X11 Server --- //
2501  int screen_number = 0;
2502  xcb_connection_t* connection = xcb_connect(display_name, &screen_number);
2503  int xcb_error = xcb_connection_has_error(connection);
2504  if(xcb_error)
2505  {
2506  error = convertConnectionError(xcb_error);
2507 
2508  ZAKERO_XENIUM__DEBUG_ERROR(error);
2509 
2510  return nullptr;
2511  }
2512 
2513  // --- Xenium --- //
2514  Xenium* xenium = new Xenium();
2515 
2516  error = xenium->init(connection, screen_number);
2517  if(error)
2518  {
2519  delete xenium;
2520 
2521  ZAKERO_XENIUM__DEBUG_ERROR(error);
2522 
2523  return nullptr;
2524  }
2525 
2526  xenium->eventLoopStart();
2527 
2528  error = xenium->atomInit();
2529  if(error)
2530  {
2531  delete xenium;
2532 
2533  ZAKERO_XENIUM__DEBUG_ERROR(error);
2534 
2535  return nullptr;
2536  }
2537 
2538  error = ZAKERO_XENIUM__ERROR(Error_None);
2539 
2540  return xenium;
2541 }
2542 
2543 
2551 void Xenium::disconnect() noexcept
2552 {
2553  this->setup = nullptr;
2554 
2555  if(connection)
2556  {
2557  xcb_disconnect(this->connection);
2558  connection = nullptr;
2559  }
2560 
2561  return;
2562 }
2563 
2564 // }}}
2565 // {{{ Initialization
2566 
2572 std::error_code Xenium::init(xcb_connection_t* connection
2573  , int screen_number
2574  ) noexcept
2575 {
2576  this->connection = connection;
2577 
2578  // --- X11 Server Setup Information --- //
2579  this->setup = xcb_get_setup(this->connection);
2580  ZAKERO_XENIUM__DEBUG_VAR(to_string(*setup));
2581 
2582  // --- Find the current screen --- //
2583  xcb_screen_iterator_t screen_iterator = xcb_setup_roots_iterator(this->setup);
2584 
2585  for(int i = 0; i < screen_number; i++)
2586  {
2587  xcb_screen_next(&screen_iterator);
2588  }
2589 
2590  this->screen = screen_iterator.data;
2591  ZAKERO_XENIUM__DEBUG_VAR(to_string(*screen));
2592 
2593  // --- XXB Extension --- //
2594  std::error_code error;
2595 
2596  error = xkbInit();
2597  if(error)
2598  {
2599  ZAKERO_XENIUM__DEBUG_ERROR(error);
2600 
2601  return error;
2602  }
2603 
2604  // --- Initialize The Internals --- //
2605  error = randrInit();
2606  if(error)
2607  {
2608  ZAKERO_XENIUM__DEBUG_ERROR(error);
2609 
2610  return error;
2611  }
2612 
2613  error = outputInit();
2614  if(error)
2615  {
2616  ZAKERO_XENIUM__DEBUG_ERROR(error);
2617 
2618  return error;
2619  }
2620 
2621  return ZAKERO_XENIUM__ERROR(Error_None);
2622 }
2623 
2624 // }}}
2625 // {{{ Cursor
2626 
2627 // }}}
2628 // {{{ Event Loop
2629 
2630 //#define ZAKERO_XENIUM__ENABLE_THREAD_SCHEDULER
2631 
2638 void Xenium::eventLoopStart() noexcept
2639 {
2640  event_loop = std::jthread(&Xenium::eventLoop, this);
2641 
2642  while(event_loop_is_running.load() == false)
2643  {
2644  // Wait for the thread to start
2645  std::this_thread::sleep_for(std::chrono::nanoseconds(42));
2646  }
2647 
2648  //#ifdef ZAKERO_XENIUM__ENABLE_THREAD_SCHEDULER
2649  //int policy = SCHED_FIFO;
2650  //int priority_min = sched_get_priority_min(policy);
2651  //int priority_max = sched_get_priority_max(policy);
2652 
2653  //sched_param sched =
2654  //{ .sched_priority = (priority_min + priority_max) / 2
2655  //};
2656 
2657  //pthread_setschedparam(event_loop.native_handle(), policy, &sched);
2658  //#endif
2659 }
2660 
2661 
2669 void Xenium::eventLoop(std::stop_token thread_token
2670  , Xenium* xenium
2671  ) noexcept
2672 {
2673  const int Randr_Notify_Event = xenium->randr_event_base + XCB_RANDR_NOTIFY;
2674 
2675  xcb_generic_event_t* event = nullptr;
2676 
2677  xenium->event_loop_is_running.store(true);
2678 
2679  // Handle events and render window contents
2680  while(thread_token.stop_requested() == false)
2681  {
2682  // Process all events
2683  while(true)
2684  {
2685  event = xcb_poll_for_event(xenium->connection);
2686 
2687  if(event == nullptr)
2688  {
2689  /*
2690  * No more events from the server, check if
2691  * there any remaining key events to process.
2692  */
2693  xenium->keyDataArrayProcess();
2694 
2695  break;
2696  }
2697 
2698  switch(event->response_type & 0x7f)
2699  {
2700  case XCB_CLIENT_MESSAGE:
2701  xenium->xcbEvent(
2702  (xcb_client_message_event_t*)event
2703  );
2704  break;
2705 
2706  // --- XCB_EVENT_MASK_BUTTON_PRESS ------------ //
2707  // --- XCB_EVENT_MASK_BUTTON_RELEASE ---------- //
2708  case XCB_BUTTON_PRESS:
2709  [[fallthrough]];
2710  case XCB_BUTTON_RELEASE:
2711  xenium->xcbEvent(
2712  (xcb_button_press_event_t*)event
2713  );
2714  break;
2715 
2716  // --- XCB_EVENT_MASK_ENTER_WINDOW ------------ //
2717  // --- XCB_EVENT_MASK_LEAVE_WINDOW ------------ //
2718  case XCB_ENTER_NOTIFY: [[fallthrough]];
2719  case XCB_LEAVE_NOTIFY:
2720  xenium->xcbEvent(
2721  (xcb_enter_notify_event_t*)event
2722  );
2723  break;
2724 
2725  // --- XCB_EVENT_MASK_EXPOSURE ---------------- //
2726  case XCB_EXPOSE:
2727  xenium->xcbEvent(
2728  (xcb_expose_event_t*)event
2729  );
2730  break;
2731 
2732  // --- XCB_EVENT_MASK_FOCUS_CHANGE ------------ //
2733  case XCB_FOCUS_IN: [[fallthrough]];
2734  case XCB_FOCUS_OUT:
2735  xenium->xcbEvent(
2736  (xcb_focus_in_event_t*)event
2737  );
2738  break;
2739 
2740  // --- XCB_EVENT_MASK_KEY_PRESS --------------- //
2741  // --- XCB_EVENT_MASK_KEY_RELEASE ------------- //
2742  case XCB_KEY_PRESS: [[fallthrough]];
2743  case XCB_KEY_RELEASE:
2744  xenium->xcbEvent(
2745  (xcb_key_press_event_t*)event
2746  );
2747  break;
2748 
2749  // --- XCB_EVENT_MASK_POINTER_MOTION ---------- //
2750  case XCB_MOTION_NOTIFY:
2751  xenium->xcbEvent(
2752  (xcb_motion_notify_event_t*)event
2753  );
2754  break;
2755 
2756  // --- XCB_EVENT_MASK_PROPERTY_CHANGE --------- //
2757  case XCB_PROPERTY_NOTIFY:
2758  xenium->xcbEvent(
2759  (xcb_property_notify_event_t*)event
2760  );
2761  break;
2762 
2763  // --- XCB_EVENT_MASK_STRUCTURE_NOTIFY -------- //
2764  case XCB_CONFIGURE_NOTIFY:
2765  xenium->xcbEvent(
2766  (xcb_configure_notify_event_t*)event
2767  );
2768  break;
2769 
2770  case XCB_GRAVITY_NOTIFY:
2771  xenium->xcbEvent(
2772  (xcb_gravity_notify_event_t*)event
2773  );
2774  break;
2775 
2776  case XCB_MAP_NOTIFY:
2777  xenium->xcbEvent(
2778  (xcb_map_notify_event_t*)event
2779  );
2780  break;
2781 
2782  case XCB_REPARENT_NOTIFY:
2783  xenium->xcbEvent(
2784  (xcb_reparent_notify_event_t*)event
2785  );
2786  break;
2787 
2788  case XCB_UNMAP_NOTIFY:
2789  xenium->xcbEvent(
2790  (xcb_unmap_notify_event_t*)event
2791  );
2792  break;
2793 
2794  // --- Other Event Types ---------------------- //
2795  default:
2796  if(event->response_type == Randr_Notify_Event)
2797  {
2798  ZAKERO_XENIUM__DEBUG << "RandR Event: " << to_string(*event) << '\n';
2799  xenium->randrEvent(
2800  (xcb_randr_notify_event_t*)event
2801  );
2802  }
2803  else
2804  {
2805  ZAKERO_XENIUM__DEBUG << "Unknown Event: " << to_string(*event) << '\n';
2806  }
2807 
2808  // --- ------------------------------------ --- //
2809  // --- Unused event masks at this time --- //
2810  // --- ------------------------------------ --- //
2811  // --- XCB_EVENT_MASK_BUTTON_1_MOTION --------- //
2812  // --- XCB_EVENT_MASK_BUTTON_2_MOTION --------- //
2813  // --- XCB_EVENT_MASK_BUTTON_3_MOTION --------- //
2814  // --- XCB_EVENT_MASK_BUTTON_4_MOTION --------- //
2815  // --- XCB_EVENT_MASK_BUTTON_5_MOTION --------- //
2816  // --- XCB_EVENT_MASK_BUTTON_MOTION ----------- //
2817  // --- XCB_EVENT_MASK_COLOR_MAP_CHANGE -------- //
2818  // --- XCB_EVENT_MASK_KEYMAP_STATE ------------ //
2819  // --- XCB_EVENT_MASK_OWNER_GRAB_BUTTON ------- //
2820  // --- XCB_EVENT_MASK_POINTER_MOTION_HINT ----- //
2821  // --- XCB_EVENT_MASK_RESIZE_REDIRECT --------- //
2822  // --- XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY ----- //
2823  // --- XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT --- //
2824  // --- XCB_EVENT_MASK_VISIBILITY_CHANGE ------- //
2825  }
2826 
2827  free(event);
2828  }
2829 
2830  /*
2831  * All events are done, check if there are any windows to
2832  * create or destory.
2833  */
2834  xenium->xenium_window_mutex.lock();
2835  {
2836  for(Xenium::WindowCreateData* window_data : xenium->window_to_create)
2837  {
2838  xenium->xcbWindowCreate(window_data);
2839  window_data->barrier.set_value();
2840  }
2841 
2842  xenium->window_to_create.clear();
2843 
2844  for(Xenium::WindowDestroyData* window_data : xenium->window_to_destroy)
2845  {
2846  xenium->xcbWindowDestroy(window_data);
2847  window_data->barrier.set_value();
2848  }
2849 
2850  xenium->window_to_destroy.clear();
2851  }
2852  xenium->xenium_window_mutex.unlock();
2853 
2854  std::this_thread::yield();
2855  }
2856 
2857  xenium->event_loop_is_running.store(false);
2858 }
2859 
2860 // }}}
2861 // {{{ Keyboard
2862 
2871 int32_t Xenium::keyRepeatDelay() const noexcept
2872 {
2873  return xkb_controls.repeat_delay_ms;
2874 }
2875 
2884 int32_t Xenium::keyRepeatRate() const noexcept
2885 {
2886  return 1000 / xkb_controls.repeat_interval_ms;
2887 }
2888 
2889 // }}}
2890 // {{{ Output
2891 
2904 Xenium::Output Xenium::output(const Xenium::OutputId output_id
2905  ) const noexcept
2906 {
2907  std::lock_guard<std::mutex> lock(output_mutex);
2908 
2909  if(output_map.contains(output_id) == false)
2910  {
2911  ZAKERO_XENIUM__DEBUG
2912  << "Invalid output_id: "
2913  << std::to_string(output_id)
2914  ;
2915 
2916  return {};
2917  }
2918 
2919  return output_map.at(output_id);
2920 }
2921 
2922 
2932 Xenium::VectorOutputId Xenium::outputVector() const noexcept
2933 {
2934  Xenium::VectorOutputId vector(output_map.size());
2935  vector.clear();
2936 
2937  std::lock_guard<std::mutex> lock(output_mutex);
2938 
2939  for(const auto& iter : output_map)
2940  {
2941  vector.push_back(iter.first);
2942  }
2943 
2944  return vector;
2945 }
2946 
2947 
2957 std::string Xenium::outputSubpixelName(int32_t subpixel_format
2958  ) noexcept
2959 {
2960  switch(subpixel_format)
2961  {
2962 #define X(value_, name_) \
2963  case value_: return name_;
2964  ZAKERO_XENIUM__OUTPUT_SUBPIXEL
2965 #undef X
2966  default: return "";
2967  }
2968 }
2969 
2970 
2980 std::string Xenium::outputTransformName(int32_t transform
2981  ) noexcept
2982 {
2983  switch(transform)
2984  {
2985 #define X(value_, name_) \
2986  case value_: return name_;
2987  ZAKERO_XENIUM__OUTPUT_TRANSFORM
2988 #undef X
2989  default: return "";
2990  }
2991 }
2992 
2993 
3002 Xenium::PointMm Xenium::outputConvertToMm(const Xenium::OutputId output_id
3003  , const Xenium::PointPixel& point
3004  ) const noexcept
3005 {
3006  std::lock_guard<std::mutex> lock(output_mutex);
3007 
3008  if(output_map.contains(output_id) == false)
3009  {
3010  return { point.time, 0, 0 };
3011  }
3012 
3013  const Xenium::Output& output = output_map.at(output_id);
3014 
3015  auto p = convertPixelToMm(output, point.x, point.y);
3016 
3017  return { point.time, p.first, p.second };
3018 }
3019 
3020 
3029 Xenium::PointPercent Xenium::outputConvertToPercent(const Xenium::OutputId output_id
3030  , const Xenium::PointPixel& point
3031  ) const noexcept
3032 {
3033  std::lock_guard<std::mutex> lock(output_mutex);
3034 
3035  if(output_map.contains(output_id) == false)
3036  {
3037  return { point.time, 0, 0 };
3038  }
3039 
3040  const Xenium::Output& output = output_map.at(output_id);
3041 
3042  auto p = convertPixelToPercent(output, point.x, point.y);
3043 
3044  return { point.time, p.first, p.second };
3045 }
3046 
3047 
3056 Xenium::PointPixel Xenium::outputConvertToPixel(const Xenium::OutputId output_id
3057  , const Xenium::PointMm& point
3058  ) const noexcept
3059 {
3060  std::lock_guard<std::mutex> lock(output_mutex);
3061 
3062  if(output_map.contains(output_id) == false)
3063  {
3064  return { point.time, 0, 0 };
3065  }
3066 
3067  const Xenium::Output& output = output_map.at(output_id);
3068 
3069  auto p = convertMmToPixel(output, point.x, point.y);
3070 
3071  return { point.time, p.first, p.second };
3072 }
3073 
3074 
3083 Xenium::PointPixel Xenium::outputConvertToPixel(const Xenium::OutputId output_id
3084  , const Xenium::PointPercent& point
3085  ) const noexcept
3086 {
3087  std::lock_guard<std::mutex> lock(output_mutex);
3088 
3089  if(output_map.contains(output_id) == false)
3090  {
3091  return { point.time, 0, 0 };
3092  }
3093 
3094  const Xenium::Output& output = output_map.at(output_id);
3095 
3096  auto p = convertPercentToPixel(output, point.x, point.y);
3097 
3098  return { point.time, p.first, p.second };
3099 }
3100 
3101 
3110 Xenium::SizeMm Xenium::outputConvertToMm(const Xenium::OutputId output_id
3111  , const Xenium::SizePixel& size
3112  ) const noexcept
3113 {
3114  std::lock_guard<std::mutex> lock(output_mutex);
3115 
3116  if(output_map.contains(output_id) == false)
3117  {
3118  return { 0, 0 };
3119  }
3120 
3121  const Xenium::Output& output = output_map.at(output_id);
3122 
3123  auto p = convertPixelToMm(output, size.width, size.height);
3124 
3125  return { p.first, p.second };
3126 }
3127 
3128 
3137 Xenium::SizePercent Xenium::outputConvertToPercent(const Xenium::OutputId output_id
3138  , const Xenium::SizePixel& size
3139  ) const noexcept
3140 {
3141  std::lock_guard<std::mutex> lock(output_mutex);
3142 
3143  if(output_map.contains(output_id) == false)
3144  {
3145  return { 0, 0 };
3146  }
3147 
3148  const Xenium::Output& output = output_map.at(output_id);
3149 
3150  auto p = convertPixelToPercent(output, size.width, size.height);
3151 
3152  return { p.first, p.second };
3153 }
3154 
3155 
3163 Xenium::SizePixel Xenium::outputConvertToPixel(const Xenium::OutputId output_id
3164  , const Xenium::SizeMm& size
3165  ) const noexcept
3166 {
3167  std::lock_guard<std::mutex> lock(output_mutex);
3168 
3169  if(output_map.contains(output_id) == false)
3170  {
3171  return { 0, 0 };
3172  }
3173 
3174  const Xenium::Output& output = output_map.at(output_id);
3175 
3176  auto p = convertMmToPixel(output, size.width, size.height);
3177 
3178  return { p.first, p.second };
3179 }
3180 
3181 
3189 Xenium::SizePixel Xenium::outputConvertToPixel(const Xenium::OutputId output_id
3190  , const Xenium::SizePercent& size
3191  ) const noexcept
3192 {
3193  std::lock_guard<std::mutex> lock(output_mutex);
3194 
3195  if(output_map.contains(output_id) == false)
3196  {
3197  return { 0, 0 };
3198  }
3199 
3200  const Xenium::Output& output = output_map.at(output_id);
3201 
3202  auto p = convertPercentToPixel(output, size.width, size.height);
3203 
3204  return { p.first, p.second };
3205 }
3206 
3207 
3215 void Xenium::outputOnAdd(LambdaOutputId lambda
3216  ) noexcept
3217 {
3218  if(lambda == nullptr)
3219  {
3220  output_on_add = LambdaOutputId_DoNothing;
3221  }
3222  else
3223  {
3224  output_on_add = lambda;
3225  }
3226 }
3227 
3228 
3236 void Xenium::outputOnChange(LambdaOutputId lambda
3237  ) noexcept
3238 {
3239  if(lambda == nullptr)
3240  {
3241  output_on_change = LambdaOutputId_DoNothing;
3242  }
3243  else
3244  {
3245  output_on_change = lambda;
3246  }
3247 }
3248 
3249 
3257 void Xenium::outputOnRemove(LambdaOutputId lambda
3258  ) noexcept
3259 {
3260  if(lambda == nullptr)
3261  {
3262  output_on_remove = LambdaOutputId_DoNothing;
3263  }
3264  else
3265  {
3266  output_on_remove = lambda;
3267  }
3268 }
3269 
3270 
3293 const Xenium::Output& Xenium::output(const int16_t x
3294  , const int16_t y
3295  , OutputId& output_id
3296  ) noexcept
3297 {
3298  for(const auto& iter : output_map)
3299  {
3300  const Output& output = iter.second;
3301 
3302  if(x >= output.x
3303  && x < (output.x + output.width)
3304  && y >= output.y
3305  && y < (output.y + output.height)
3306  )
3307  {
3308  output_id = iter.first;
3309 
3310  return output;
3311  }
3312  }
3313 
3314  uint64_t distance = std::numeric_limits<uint64_t>::max();
3315 
3316  for(const auto& iter : output_map)
3317  {
3318  const Output& output = iter.second;
3319 
3320  int64_t output_x = (std::abs(output.x) + output.width) / 2;
3321  int64_t output_y = (std::abs(output.y) + output.height) / 2;
3322 
3323  uint64_t dist = std::abs(x - output_x) + std::abs(y - output_y);
3324 
3325  if(dist < distance)
3326  {
3327  output_id = iter.first;
3328  }
3329  }
3330 
3331  return output_map[output_id];
3332 }
3333 
3334 
3343 std::error_code Xenium::outputInit() noexcept
3344 {
3345  xcb_generic_error_t* xcb_generic_error = nullptr;
3346 
3347  xcb_randr_get_screen_resources_current_reply_t* screen_resources =
3348  xcb_randr_get_screen_resources_current_reply(this->connection
3349  , xcb_randr_get_screen_resources_current(this->connection
3350  , this->screen->root
3351  )
3352  , &xcb_generic_error
3353  );
3354 
3355  if(screen_resources == nullptr)
3356  {
3357  ZAKERO_XENIUM__DEBUG_ERROR(
3358  ZAKERO_XENIUM__ERROR(Error_RandR_Screen_Resources_Not_Found)
3359  );
3360 
3361  return ZAKERO_XENIUM__ERROR(Error_RandR_Screen_Resources_Not_Found);
3362  }
3363 
3364  xcb_randr_output_t* output_list =
3365  xcb_randr_get_screen_resources_current_outputs(screen_resources
3366  );
3367 
3368  int output_list_size =
3369  xcb_randr_get_screen_resources_current_outputs_length(screen_resources
3370  );
3371 
3372 # if ZAKERO_XENIUM__DEBUG_ENABLED
3373  if(output_list_size == 0)
3374  {
3375  ZAKERO_XENIUM__DEBUG
3376  << "No outputs where found in the Screen Resources"
3377  ;
3378  }
3379 # endif
3380 
3381  for(int i = 0; i < output_list_size; i++)
3382  {
3383  xcb_randr_get_output_info_reply_t* output_info =
3384  xcb_randr_get_output_info_reply(this->connection
3385  , xcb_randr_get_output_info(this->connection
3386  , output_list[i]
3387  , screen_resources->config_timestamp
3388  )
3389  , &xcb_generic_error
3390  );
3391 
3392  if(output_info == nullptr
3393  || output_info->connection != XCB_RANDR_CONNECTION_CONNECTED
3394  || output_info->crtc == XCB_NONE
3395  )
3396  {
3397  // Output Info is not usable
3398  free(output_info);
3399  continue;
3400  }
3401 
3402  xcb_randr_get_crtc_info_reply_t* crtc_info =
3403  xcb_randr_get_crtc_info_reply(this->connection
3404  , xcb_randr_get_crtc_info(this->connection
3405  , output_info->crtc
3406  , screen_resources->config_timestamp
3407  )
3408  , &xcb_generic_error
3409  );
3410 
3411  if(crtc_info == nullptr)
3412  {
3413  free(crtc_info);
3414  free(output_info);
3415  continue;
3416  }
3417 
3418  // Some Virtual Machines do not fully populate the output
3419  // devices for X11. Calculate the `mm` size if it is missing.
3420  if(output_info->mm_width == 0 || output_info->mm_height == 0)
3421  {
3422  ZAKERO_XENIUM__DEBUG_ERROR(
3423  ZAKERO_XENIUM__ERROR(Error_RandR_Output_Info_Is_Incomplete)
3424  );
3425 
3426  const float pixels_per_mm_horizontal = (float)this->screen->width_in_pixels / this->screen->width_in_millimeters;
3427  const float pixels_per_mm_vertical = (float)this->screen->height_in_pixels / this->screen->height_in_millimeters;
3428 
3429  output_info->mm_width = crtc_info->width / pixels_per_mm_horizontal;
3430  output_info->mm_height = crtc_info->height / pixels_per_mm_vertical;
3431  }
3432 
3433  outputAdd(crtc_info, output_info);
3434 
3435  free(crtc_info);
3436  free(output_info);
3437  }
3438 
3439  free(screen_resources);
3440 
3441  return ZAKERO_XENIUM__ERROR(Error_None);
3442 }
3443 
3444 
3454 std::error_code Xenium::outputAdd(xcb_randr_crtc_t randr_crtc
3455  , xcb_randr_output_t randr_output
3456  ) noexcept
3457 {
3458  if(randr_crtc == XCB_NONE)
3459  {
3460  ZAKERO_XENIUM__DEBUG_ERROR(
3461  ZAKERO_XENIUM__ERROR(Error_RandR_Invalid_CRTC_Id)
3462  );
3463 
3464  return ZAKERO_XENIUM__ERROR(Error_RandR_Invalid_CRTC_Id);
3465  }
3466 
3467  if(randr_output == XCB_NONE)
3468  {
3469  ZAKERO_XENIUM__DEBUG_ERROR(
3470  ZAKERO_XENIUM__ERROR(Error_RandR_Invalid_Output_Id)
3471  );
3472 
3473  return ZAKERO_XENIUM__ERROR(Error_RandR_Invalid_Output_Id);
3474  }
3475 
3476  xcb_generic_error_t* xcb_generic_error = nullptr;
3477 
3478  xcb_randr_get_screen_resources_current_reply_t* screen_resources =
3479  xcb_randr_get_screen_resources_current_reply(this->connection
3480  , xcb_randr_get_screen_resources_current(this->connection
3481  , this->screen->root
3482  )
3483  , &xcb_generic_error
3484  );
3485 
3486  if(screen_resources == nullptr)
3487  {
3488  ZAKERO_XENIUM__DEBUG_ERROR(
3489  ZAKERO_XENIUM__ERROR(Error_RandR_Screen_Resources_Not_Found)
3490  );
3491 
3492  return ZAKERO_XENIUM__ERROR(Error_RandR_Screen_Resources_Not_Found);
3493  }
3494 
3495  xcb_randr_get_output_info_reply_t* output_info =
3496  xcb_randr_get_output_info_reply(this->connection
3497  , xcb_randr_get_output_info(this->connection
3498  , randr_output
3499  , screen_resources->config_timestamp
3500  )
3501  , &xcb_generic_error
3502  );
3503 
3504  if(output_info == nullptr)
3505  {
3506  free(screen_resources);
3507 
3508  ZAKERO_XENIUM__DEBUG_ERROR(
3509  ZAKERO_XENIUM__ERROR(Error_RandR_Output_Info_Not_Found)
3510  );
3511 
3512  return ZAKERO_XENIUM__ERROR(Error_RandR_Output_Info_Not_Found);
3513  }
3514 
3515  xcb_randr_get_crtc_info_reply_t* crtc_info =
3516  xcb_randr_get_crtc_info_reply(this->connection
3517  , xcb_randr_get_crtc_info(this->connection
3518  , randr_crtc
3519  , screen_resources->config_timestamp
3520  )
3521  , &xcb_generic_error
3522  );
3523 
3524  if(crtc_info == nullptr)
3525  {
3526  free(output_info);
3527  free(screen_resources);
3528 
3529  ZAKERO_XENIUM__DEBUG_ERROR(
3530  ZAKERO_XENIUM__ERROR(Error_RandR_CRTC_Info_Not_Found)
3531  );
3532 
3533  return ZAKERO_XENIUM__ERROR(Error_RandR_CRTC_Info_Not_Found);
3534  }
3535 
3536  // Some Virtual Machines do not fully populate the output
3537  // devices for X11. Calculate the `mm` size if it is missing.
3538  if(output_info->mm_width == 0 || output_info->mm_height == 0)
3539  {
3540  ZAKERO_XENIUM__DEBUG_ERROR(
3541  ZAKERO_XENIUM__ERROR(Error_RandR_Output_Info_Is_Incomplete)
3542  );
3543 
3544  const float pixels_per_mm_horizontal = (float)this->screen->width_in_pixels / this->screen->width_in_millimeters;
3545  const float pixels_per_mm_vertical = (float)this->screen->height_in_pixels / this->screen->height_in_millimeters;
3546 
3547  output_info->mm_width = crtc_info->width / pixels_per_mm_horizontal;
3548  output_info->mm_height = crtc_info->height / pixels_per_mm_vertical;
3549  }
3550 
3551  outputAdd(crtc_info, output_info);
3552 
3553  free(crtc_info);
3554  free(output_info);
3555  free(screen_resources);
3556 
3557  return ZAKERO_XENIUM__ERROR(Error_None);
3558 }
3559 
3560 
3567 void Xenium::outputAdd(const xcb_randr_get_crtc_info_reply_t* crtc_info
3568  , const xcb_randr_get_output_info_reply_t* output_info
3569  ) noexcept
3570 {
3571  const std::string output_name(
3572  (const char*)xcb_randr_get_output_info_name(output_info),
3573  xcb_randr_get_output_info_name_length(output_info)
3574  );
3575 
3576  OutputId output_id = output_info->crtc;
3577 
3578  this->output_map[output_id] =
3579  { .name = output_name
3580  //, .make = ""
3581  //, .model = ""
3582  , .x = crtc_info->x
3583  , .y = crtc_info->y
3584  , .width = crtc_info->width
3585  , .height = crtc_info->height
3586  , .physical_width_mm = output_info->mm_width
3587  , .physical_height_mm = output_info->mm_height
3588  , .subpixel = output_info->subpixel_order
3589  //, .refresh_mHz = 0 // ????
3590  //, .scale_factor = 0 // ????
3591  , .transform = crtc_info->rotation
3592  , .pixels_per_mm_horizontal = (float)crtc_info->width / output_info->mm_width
3593  , .pixels_per_mm_vertical = (float)crtc_info->height / output_info->mm_height
3594  };
3595 
3596  ZAKERO_XENIUM__DEBUG_VAR(output_id)
3597  ZAKERO_XENIUM__DEBUG_VAR(this->output_map[output_id].name)
3598 }
3599 
3600 
3601 // }}}
3602 // {{{ Utility
3603 
3612 std::pair<float, float> Xenium::convertPixelToMm(const Xenium::Output& output
3613  , int32_t xw
3614  , int32_t yh
3615  ) const noexcept
3616 {
3617  const float ratio_h = output.pixels_per_mm_horizontal;
3618  const float ratio_v = output.pixels_per_mm_vertical;
3619 
3620  return
3621  { xw / ratio_h
3622  , yh / ratio_v
3623  };
3624 }
3625 
3626 
3635 std::pair<float, float> Xenium::convertPixelToPercent(const Xenium::Output& output
3636  , int32_t xw
3637  , int32_t yh
3638  ) const noexcept
3639 {
3640  return
3641  { float(xw) / output.width
3642  , float(yh) / output.height
3643  };
3644 }
3645 
3646 
3655 std::pair<int32_t, int32_t> Xenium::convertMmToPixel(const Xenium::Output& output
3656  , float xw
3657  , float yh
3658  ) const noexcept
3659 {
3660  const float ratio_h = output.pixels_per_mm_horizontal;
3661  const float ratio_v = output.pixels_per_mm_vertical;
3662 
3663  return
3664  { int32_t(xw * ratio_h)
3665  , int32_t(yh * ratio_v)
3666  };
3667 }
3668 
3669 
3678 std::pair<int32_t, int32_t> Xenium::convertPercentToPixel(const Xenium::Output& output
3679  , float xw
3680  , float yh
3681  ) const noexcept
3682 {
3683  return
3684  { int32_t(xw * output.width)
3685  , int32_t(yh * output.height)
3686  };
3687 }
3688 
3689 // }}}
3690 // {{{ Window
3691 
3708  , std::error_code& error
3709  ) noexcept
3710 {
3711  return windowCreate(size, Default_Value_Mask, Default_Value_List, error);
3712 }
3713 
3714 
3734  , const uint32_t value_mask
3735  , xcb_create_window_value_list_t& value_list
3736  , std::error_code& error
3737  ) noexcept
3738 {
3739  Xenium::WindowCreateData data =
3740  { .barrier = {}
3741  , .error = {}
3742  , .window_id = 0
3743  , .output_id = 0
3744  , .atom_close_request = 0
3745  , .gc = 0
3746  , .size_unit = Xenium::SizeUnit::Millimeter
3747  , .size_mm = size_mm
3748  , .size_percent = {}
3749  , .size_pixel = {}
3750  , .value_mask = value_mask
3751  , .value_list = value_list
3752  };
3753 
3754  std::future<void> barrier = data.barrier.get_future();
3755 
3756  windowCreateAddToQueue(&data);
3757 
3758  barrier.wait();
3759 
3760  if(data.error)
3761  {
3762  error = data.error;
3763 
3764  ZAKERO_XENIUM__DEBUG_ERROR(error);
3765 
3766  return nullptr;
3767  }
3768 
3769  Xenium::Window* window = new Xenium::Window(this, (void*)&data);
3770 
3771  windowReadyWait(data.window_id);
3772 
3773  return window;
3774 }
3775 
3776 
3794  , std::error_code& error
3795  ) noexcept
3796 {
3797  return windowCreate(size, Default_Value_Mask, Default_Value_List, error);
3798 }
3799 
3800 
3819  , const uint32_t value_mask
3820  , xcb_create_window_value_list_t& value_list
3821  , std::error_code& error
3822  ) noexcept
3823 {
3824  Xenium::WindowCreateData data =
3825  { .barrier = {}
3826  , .error = {}
3827  , .window_id = 0
3828  , .output_id = 0
3829  , .atom_close_request = 0
3830  , .gc = 0
3831  , .size_unit = Xenium::SizeUnit::Percent
3832  , .size_mm = {}
3833  , .size_percent = size_percent
3834  , .size_pixel = {}
3835  , .value_mask = value_mask
3836  , .value_list = value_list
3837  };
3838 
3839  std::future<void> barrier = data.barrier.get_future();
3840 
3841  windowCreateAddToQueue(&data);
3842 
3843  barrier.wait();
3844 
3845  if(data.error)
3846  {
3847  error = data.error;
3848 
3849  ZAKERO_XENIUM__DEBUG_ERROR(error);
3850 
3851  return nullptr;
3852  }
3853 
3854  Xenium::Window* window = new Xenium::Window(this, (void*)&data);
3855 
3856  windowReadyWait(data.window_id);
3857 
3858  return window;
3859 }
3860 
3861 
3878  , std::error_code& error
3879  ) noexcept
3880 {
3881  return windowCreate(size, Default_Value_Mask, Default_Value_List, error);
3882 }
3883 
3884 
3903  , const uint32_t value_mask
3904  , xcb_create_window_value_list_t& value_list
3905  , std::error_code& error
3906  ) noexcept
3907 {
3908  Xenium::WindowCreateData data =
3909  { .barrier = {}
3910  , .error = {}
3911  , .window_id = 0
3912  , .output_id = 0
3913  , .atom_close_request = 0
3914  , .gc = 0
3915  , .size_unit = Xenium::SizeUnit::Pixel
3916  , .size_mm = {}
3917  , .size_percent = {}
3918  , .size_pixel = size_pixel
3919  , .value_mask = value_mask
3920  , .value_list = value_list
3921  };
3922 
3923  std::future<void> barrier = data.barrier.get_future();
3924 
3925  windowCreateAddToQueue(&data);
3926 
3927  barrier.wait();
3928 
3929  if(data.error)
3930  {
3931  error = data.error;
3932 
3933  ZAKERO_XENIUM__DEBUG_ERROR(error);
3934 
3935  return nullptr;
3936  }
3937 
3938  Xenium::Window* window = new Xenium::Window(this, (void*)&data);
3939 
3940  windowReadyWait(data.window_id);
3941 
3942  return window;
3943 }
3944 
3945 
3955 std::error_code Xenium::windowBorder(const WindowId window_id
3956  , const bool enable
3957  ) noexcept
3958 {
3959  MotifWmHints hints_data =
3960  { .flags = 2
3961  , .functions = 0
3962  , .decorations = enable
3963  , .input_mode = 0
3964  , .status = 0
3965  };
3966 
3967  xcb_void_cookie_t void_cookie =
3968  xcb_change_property_checked(this->connection
3969  , XCB_PROP_MODE_REPLACE // mode
3970  , window_id // window
3971  , atom_motif_wm_hints // property
3972  , atom_motif_wm_hints // type
3973  , 32 // format : pointer to 32-bit data
3974  , 5 // data_len
3975  , &hints_data // data
3976  );
3977 
3978  xcb_generic_error_t generic_error;
3979 
3980  if(requestCheckHasError(void_cookie, generic_error))
3981  {
3982  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
3983 
3984  return ZAKERO_XENIUM__ERROR(Error_Unknown);
3985  }
3986 
3987  return ZAKERO_XENIUM__ERROR(Error_None);
3988 }
3989 
3990 
3994 void Xenium::windowCreateAddToQueue(Xenium::WindowCreateData* window_data
3995  ) noexcept
3996 {
3997  xenium_window_mutex.lock();
3998 
3999  window_to_create.push_back(window_data);
4000 
4001  xenium_window_mutex.unlock();
4002 }
4003 
4004 
4008 void Xenium::windowDestroyAddToQueue(Xenium::WindowDestroyData* window_data
4009  ) noexcept
4010 {
4011  xenium_window_mutex.lock();
4012 
4013  window_to_destroy.push_back(window_data);
4014 
4015  xenium_window_mutex.unlock();
4016 }
4017 
4018 
4028 std::error_code Xenium::windowLocationSet(const WindowId window_id
4029  , const Xenium::PointPixel& point
4030  ) noexcept
4031 {
4032  xcb_configure_window_value_list_t value_list =
4033  { .x = point.x
4034  , .y = point.y
4035  , .width = 0
4036  , .height = 0
4037  , .border_width = 0
4038  , .sibling = 0
4039  , .stack_mode = 0
4040  };
4041 
4042  xcb_void_cookie_t void_cookie =
4043  xcb_configure_window_aux_checked(this->connection
4044  , window_id
4045  , 0
4046  | XCB_CONFIG_WINDOW_X
4047  | XCB_CONFIG_WINDOW_Y
4048  , &value_list
4049  );
4050 
4051  xcb_generic_error_t generic_error;
4052  if(requestCheckHasError(void_cookie, generic_error))
4053  {
4054  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
4055 
4056  return ZAKERO_XENIUM__ERROR(Error_Unknown);
4057  }
4058 
4059  return ZAKERO_XENIUM__ERROR(Error_None);
4060 }
4061 
4062 
4071 std::error_code Xenium::windowMinimize(const Xenium::WindowId window_id
4072  ) noexcept
4073 {
4074  xcb_client_message_event_t event =
4075  { .response_type = XCB_CLIENT_MESSAGE
4076  , .format = 32
4077  , .sequence = 0
4078  , .window = window_id
4079  , .type = atom_wm_change_state
4080  , .data = { }
4081  };
4082  event.data.data32[0] = XCB_ICCCM_WM_STATE_ICONIC;
4083 
4084  xcb_send_event(this->connection
4085  , 0 // do not propagate
4086  , this->screen->root
4087  , XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
4088  , (const char*)&event
4089  );
4090 
4091  xcb_flush(this->connection);
4092 
4093  return ZAKERO_XENIUM__ERROR(Error_None);
4094 }
4095 
4096 
4106 std::error_code Xenium::windowModeSet(const Xenium::WindowId window_id
4107  , const Xenium::WindowMode current_mode
4108  , const Xenium::WindowMode new_mode
4109  ) noexcept
4110 {
4111  xcb_client_message_event_t event =
4112  { .response_type = XCB_CLIENT_MESSAGE
4113  , .format = 32
4114  , .sequence = 0
4115  , .window = window_id
4116  , .type = atom_net_wm_state
4117  , .data = { }
4118  };
4119 
4120  if(current_mode == Xenium::WindowMode::Normal)
4121  {
4122  // Do nothing
4123  }
4124  else
4125  {
4126  event.data.data32[0] = _NET_WM_STATE_REMOVE;
4127 
4128  if(current_mode == Xenium::WindowMode::Fullscreen)
4129  {
4130  event.data.data32[1] = atom_net_wm_state_fullscreen;
4131  event.data.data32[2] = 0;
4132  }
4133  else // if(current_mode == Xenium::WindowMode::Maximized)
4134  {
4135  event.data.data32[1] = atom_net_wm_state_maximized_horz;
4136  event.data.data32[2] = atom_net_wm_state_maximized_vert;
4137  }
4138 
4139  xcb_send_event(this->connection
4140  , 0 // do not propagate
4141  , this->screen->root
4142  , XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
4143  , (const char*)&event
4144  );
4145  }
4146 
4147  if(new_mode == Xenium::WindowMode::Normal)
4148  {
4149  // Do nothing
4150  }
4151  else
4152  {
4153  event.data.data32[0] = _NET_WM_STATE_ADD;
4154 
4155  if(new_mode == Xenium::WindowMode::Fullscreen)
4156  {
4157  event.data.data32[1] = atom_net_wm_state_fullscreen;
4158  event.data.data32[2] = 0;
4159  }
4160  else // if(current_mode == Xenium::WindowMode::Maximized)
4161  {
4162  event.data.data32[1] = atom_net_wm_state_maximized_horz;
4163  event.data.data32[2] = atom_net_wm_state_maximized_vert;
4164  }
4165 
4166  xcb_send_event(this->connection
4167  , 0 // do not propagate
4168  , this->screen->root
4169  , XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
4170  , (const char*)&event
4171  );
4172  }
4173 
4174  xcb_flush(this->connection);
4175 
4176  return ZAKERO_XENIUM__ERROR(Error_None);
4177 }
4178 
4179 
4191 bool Xenium::windowPropertySet(WindowId window_id
4192  , const xcb_atom_t property
4193  , const xcb_atom_t value
4194  , xcb_generic_error_t& generic_error
4195  ) noexcept
4196 {
4197  xcb_void_cookie_t void_cookie =
4198  xcb_change_property_checked(this->connection
4199  , XCB_PROP_MODE_REPLACE // mode
4200  , window_id // window
4201  , property // property
4202  , XCB_ATOM_ATOM // type
4203  , 32 // format : 32-bit pointer
4204  , 1 // data_len
4205  , &value // data
4206  );
4207 
4208  if(requestCheckHasError(void_cookie, generic_error))
4209  {
4210  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
4211  return false;
4212  }
4213 
4214  return true;
4215 }
4216 
4217 
4229 bool Xenium::windowPropertySet(WindowId window_id
4230  , const xcb_atom_t property
4231  , const std::string& value
4232  , xcb_generic_error_t& generic_error
4233  ) noexcept
4234 {
4235  xcb_void_cookie_t void_cookie =
4236  xcb_change_property_checked(this->connection
4237  , XCB_PROP_MODE_REPLACE // mode
4238  , window_id // window
4239  , property // property
4240  , XCB_ATOM_STRING // type
4241  , 8 // format : 32-bit pointer
4242  , value.length() // data_len
4243  , value.data() // data
4244  );
4245  if(requestCheckHasError(void_cookie, generic_error))
4246  {
4247  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
4248  return false;
4249  }
4250 
4251  return true;
4252 }
4253 
4254 
4265 void Xenium::windowReadySet(const WindowId window_id
4266  ) noexcept
4267 {
4268  window_ready_map[window_id] = true;
4269 }
4270 
4271 
4281 void Xenium::windowReadyWait(const WindowId window_id
4282  ) noexcept
4283 {
4284  xcb_map_window(this->connection, window_id);
4285  xcb_flush(this->connection);
4286 
4287  while(window_ready_map[window_id] == false)
4288  {
4289  usleep(42);
4290  }
4291 }
4292 
4293 
4300 void Xenium::windowResizeTo(const Output& output
4301  , Xenium::WindowSizeData& window_size
4302  , const xcb_configure_notify_event_t* event
4303  ) noexcept
4304 {
4305  bool update_size = false;
4306 
4307  if(window_size.unit == SizeUnit::Millimeter)
4308  {
4309  auto pixel = convertMmToPixel(output
4310  , window_size.mm.width
4311  , window_size.mm.height
4312  );
4313 
4314  if(pixel.first != window_size.pixel.width
4315  || pixel.second != window_size.pixel.height
4316  )
4317  {
4318  update_size = true;
4319  }
4320 
4321  window_size.pixel = {pixel.first, pixel.second};
4322 
4323  auto percent = convertPixelToPercent(output
4324  , window_size.pixel.width
4325  , window_size.pixel.height
4326  );
4327  window_size.percent = {percent.first, percent.second};
4328  }
4329  else if(window_size.unit == SizeUnit::Percent)
4330  {
4331  auto pixel = convertPercentToPixel(output
4332  , window_size.percent.width
4333  , window_size.percent.height
4334  );
4335 
4336  if(pixel.first != window_size.pixel.width
4337  || pixel.second != window_size.pixel.height
4338  )
4339  {
4340  update_size = true;
4341  window_size.pixel = {pixel.first, pixel.second};
4342  }
4343 
4344  auto mm = convertPixelToMm(output
4345  , window_size.pixel.width
4346  , window_size.pixel.height
4347  );
4348 
4349  window_size.mm = {mm.first, mm.second};
4350  }
4351  else
4352  {
4353  if(event->width != window_size.pixel.width
4354  || event->height != window_size.pixel.height
4355  )
4356  {
4357  update_size = true;
4358  }
4359 
4360  window_size.pixel = {event->width, event->height};
4361 
4362  auto mm = convertPixelToMm(output
4363  , window_size.pixel.width
4364  , window_size.pixel.height
4365  );
4366  window_size.mm = {mm.first, mm.second};
4367 
4368  auto percent = convertPixelToPercent(output
4369  , window_size.pixel.width
4370  , window_size.pixel.height
4371  );
4372  window_size.percent = {percent.first, percent.second};
4373  }
4374 
4375  windowSizeSetMinMax(output, event->window, window_size);
4376 
4377  if(update_size)
4378  {
4379  window_size.pixel_lambda(window_size.pixel);
4380  window_size.percent_lambda(window_size.percent);
4381  window_size.mm_lambda(window_size.mm);
4382 
4383  windowSizeSet(event->window, window_size.pixel);
4384  }
4385 }
4386 
4387 
4395 std::error_code Xenium::windowSizeSet(const WindowId window_id
4396  , const Xenium::SizePixel& size
4397  ) noexcept
4398 {
4399  xcb_configure_window_value_list_t value_list =
4400  { .x = 0
4401  , .y = 0
4402  , .width = (uint32_t)size.width
4403  , .height = (uint32_t)size.height
4404  , .border_width = 0
4405  , .sibling = 0
4406  , .stack_mode = 0
4407  };
4408 
4409  xcb_void_cookie_t void_cookie =
4410  xcb_configure_window_aux_checked(this->connection
4411  , window_id
4412  , 0
4413  | XCB_CONFIG_WINDOW_WIDTH
4414  | XCB_CONFIG_WINDOW_HEIGHT
4415  , &value_list
4416  );
4417 
4418  xcb_generic_error_t generic_error;
4419  if(requestCheckHasError(void_cookie, generic_error))
4420  {
4421  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
4422 
4423  return ZAKERO_XENIUM__ERROR(Error_Unknown);
4424  }
4425 
4426  return ZAKERO_XENIUM__ERROR(Error_None);
4427 }
4428 
4429 
4438 std::error_code Xenium::windowSizeSetMinMax(const WindowId window_id
4439  , const int32_t min_width
4440  , const int32_t min_height
4441  , const int32_t max_width
4442  , const int32_t max_height
4443  ) noexcept
4444 {
4445  xcb_get_property_cookie_t property_cookie =
4446  xcb_get_property(this->connection
4447  , 0 // delete
4448  , window_id // window
4449  , XCB_ATOM_WM_NORMAL_HINTS // property
4450  , XCB_ATOM_WM_SIZE_HINTS // type
4451  , 0 // offset to data (32-bit)
4452  , 18 // number of 32-bit values
4453  );
4454 
4455  xcb_generic_error_t* error_ptr = nullptr;
4456  xcb_get_property_reply_t* property_reply =
4457  xcb_get_property_reply(this->connection
4458  , property_cookie
4459  , &error_ptr
4460  );
4461  if(error_ptr)
4462  {
4463  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(*error_ptr) << '\n';
4464 
4465  return ZAKERO_XENIUM__ERROR(Error_Unknown);
4466  }
4467 
4468  xcb_size_hints_t* size_hints =
4469  (xcb_size_hints_t*)xcb_get_property_value(property_reply);
4470 
4471  if(min_width == 0 && min_height == 0)
4472  {
4473  size_hints->flags &= (~XCB_ICCCM_SIZE_HINT_P_MIN_SIZE);
4474  }
4475  else
4476  {
4477  size_hints->flags |= XCB_ICCCM_SIZE_HINT_P_MIN_SIZE;
4478  }
4479 
4480  size_hints->min_width = min_width;
4481  size_hints->min_height = min_height;
4482 
4483  if(max_width == 0 && max_height == 0)
4484  {
4485  size_hints->flags &= (~XCB_ICCCM_SIZE_HINT_P_MAX_SIZE);
4486  }
4487  else
4488  {
4489  size_hints->flags |= XCB_ICCCM_SIZE_HINT_P_MAX_SIZE;
4490  }
4491 
4492  size_hints->max_width = max_width;
4493  size_hints->max_height = max_height;
4494 
4495  xcb_void_cookie_t cookie =
4496  xcb_change_property_checked(this->connection
4497  , XCB_PROP_MODE_REPLACE
4498  , window_id
4499  , XCB_ATOM_WM_NORMAL_HINTS
4500  , XCB_ATOM_WM_SIZE_HINTS
4501  , 32 // 32-bit values
4502  , 18 // 18 values
4503  , size_hints
4504  );
4505 
4506  xcb_generic_error_t generic_error;
4507 
4508  if(requestCheckHasError(cookie, generic_error))
4509  {
4510  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
4511 
4512  return ZAKERO_XENIUM__ERROR(Error_Unknown);
4513  }
4514 
4515  return ZAKERO_XENIUM__ERROR(Error_None);
4516 }
4517 
4518 
4537 std::error_code Xenium::windowSizeSetMinMax(const Xenium::Output& output
4538  , const Xenium::WindowId window_id
4539  , Xenium::WindowSizeData& window_size
4540  ) noexcept
4541 {
4542  if(window_size.unit == SizeUnit::Millimeter)
4543  {
4544  // --- Window Minimum Size --- //
4545  auto pixel = convertMmToPixel(output
4546  , window_size.mm_minimum.width
4547  , window_size.mm_minimum.height
4548  );
4549  window_size.pixel_minimum = {pixel.first, pixel.second};
4550 
4551  auto percent = convertPixelToPercent(output
4552  , window_size.pixel_minimum.width
4553  , window_size.pixel_minimum.height
4554  );
4555  window_size.percent_minimum = {percent.first, percent.second};
4556 
4557  // --- Window Maximum Size --- //
4558  pixel = convertMmToPixel(output
4559  , window_size.mm_maximum.width
4560  , window_size.mm_maximum.height
4561  );
4562  window_size.pixel_maximum = {pixel.first, pixel.second};
4563 
4564  percent = convertPixelToPercent(output
4565  , window_size.pixel_maximum.width
4566  , window_size.pixel_maximum.height
4567  );
4568  window_size.percent_maximum = {percent.first, percent.second};
4569  }
4570  else if(window_size.unit == SizeUnit::Percent)
4571  {
4572  // --- Window Minimum Size --- //
4573  auto pixel = convertPercentToPixel(output
4574  , window_size.percent_minimum.width
4575  , window_size.percent_minimum.height
4576  );
4577  window_size.pixel_minimum = {pixel.first, pixel.second};
4578 
4579  auto mm = convertPixelToMm(output
4580  , window_size.pixel_minimum.width
4581  , window_size.pixel_minimum.height
4582  );
4583  window_size.mm_minimum = {mm.first, mm.second};
4584 
4585  // --- Window Maximum Size --- //
4586  pixel = convertPercentToPixel(output
4587  , window_size.percent_maximum.width
4588  , window_size.percent_maximum.height
4589  );
4590  window_size.pixel_maximum = {pixel.first, pixel.second};
4591 
4592  mm = convertPixelToMm(output
4593  , window_size.pixel_maximum.width
4594  , window_size.pixel_maximum.height
4595  );
4596  window_size.mm_maximum = {mm.first, mm.second};
4597  }
4598  else
4599  {
4600  // --- Window Minimum Size --- //
4601  auto mm = convertPixelToMm(output
4602  , window_size.pixel_minimum.width
4603  , window_size.pixel_minimum.height
4604  );
4605  window_size.mm_minimum = {mm.first, mm.second};
4606 
4607  auto percent = convertPixelToPercent(output
4608  , window_size.pixel_minimum.width
4609  , window_size.pixel_minimum.height
4610  );
4611  window_size.percent_minimum = {percent.first, percent.second};
4612 
4613  // --- Window Maximum Size --- //
4614  mm = convertPixelToMm(output
4615  , window_size.pixel_maximum.width
4616  , window_size.pixel_maximum.height
4617  );
4618  window_size.mm_maximum = {mm.first, mm.second};
4619 
4620  percent = convertPixelToPercent(output
4621  , window_size.pixel_maximum.width
4622  , window_size.pixel_maximum.height
4623  );
4624  window_size.percent_maximum = {percent.first, percent.second};
4625  }
4626 
4627  // --- Set Window Min/Max Size --- //
4628  std::error_code error;
4629 
4630  error = windowSizeSetMinMax(window_id
4631  , window_size.pixel_minimum.width, window_size.pixel_minimum.height
4632  , window_size.pixel_maximum.width, window_size.pixel_maximum.height
4633  );
4634 
4635 # if ZAKERO_XENIUM__DEBUG_ENABLED
4636  if(error)
4637  {
4638  ZAKERO_XENIUM__DEBUG_ERROR(error);
4639  }
4640 # endif
4641 
4642  return error;
4643 }
4644 
4645 // }}}
4646 // {{{ XCB
4647 
4651 void Xenium::xcbEvent(const xcb_button_press_event_t* event
4652  ) noexcept
4653 {
4654 //std::cout << "Button Press: " << to_string(*event) << '\n';
4655 
4656  const WindowId window_id = event->event;
4657 
4658  uint32_t button_code = event->detail;
4659 
4660  if(button_code <= 3 || button_code >= 8)
4661  {
4662  if(button_code > 9)
4663  {
4664  button_code = 0;
4665  }
4666  else if(button_code >= 8)
4667  {
4668  button_code -= 5;
4669  }
4670  else
4671  {
4672  button_code--;
4673  }
4674 
4675  PointerButton button =
4676  { .code = Pointer_Button_Event_Code[button_code]
4677  , .state = event->response_type == XCB_BUTTON_PRESS
4678  ? Xenium::PointerButtonState::Pressed
4679  : Xenium::PointerButtonState::Released
4680  };
4681 
4682  OutputId output_id = window_output_map[window_id];
4683  Output output = output_map.at(output_id);
4684 
4685  PointPixel point_pixel = {0, event->event_x, event->event_y};
4686 
4687  auto mm = convertPixelToMm(output
4688  , event->event_x
4689  , event->event_y
4690  );
4691  PointMm point_mm = {0, mm.first, mm.second};
4692 
4693  Xenium::WindowSizeData& window_size = window_size_map[window_id];
4694  PointPercent point_percent =
4695  { event->time
4696  , (float)event->event_x / (float)window_size.pixel.width
4697  , (float)event->event_y / (float)window_size.pixel.height
4698  };
4699 
4700  window_on_button_map[window_id].lambda_mm(button, point_mm, key_modifier);
4701  window_on_button_map[window_id].lambda_percent(button, point_percent, key_modifier);
4702  window_on_button_map[window_id].lambda_pixel(button, point_pixel, key_modifier);
4703  }
4704  else if(event->response_type == XCB_BUTTON_PRESS)
4705  {
4706  if(button_code == 4 || button_code == 5)
4707  {
4708  PointerAxis pointer_axis =
4709  { .time = event->time
4710  , .steps = button_code == 4 ? -1 : 1
4711  , .distance = button_code == 4 ? -15.0f : 15.0f
4712  , .source = PointerAxisSource::Wheel
4713  , .type = PointerAxisType::Vertical
4714  };
4715 
4716  window_on_axis_map[window_id](pointer_axis, key_modifier);
4717  }
4718  else if(button_code == 6 || button_code == 7)
4719  {
4720  PointerAxis pointer_axis =
4721  { .time = event->time
4722  , .steps = button_code == 6 ? -1 : 1
4723  , .distance = button_code == 6 ? -15.0f : 15.0f
4724  , .source = PointerAxisSource::Wheel
4725  , .type = PointerAxisType::Horizontal
4726  };
4727 
4728  window_on_axis_map[window_id](pointer_axis, key_modifier);
4729  }
4730  }
4731 }
4732 
4733 
4737 void Xenium::xcbEvent(const xcb_client_message_event_t* event
4738  ) noexcept
4739 {
4740 //std::cout << "Client Message: " << to_string(*event) << '\n';
4741  if(window_delete_map.contains(event->window))
4742  {
4743  WindowDeleteData& window_delete = window_delete_map[event->window];
4744  if(event->data.data32[0] == window_delete.atom_close_request)
4745  {
4746  window_delete.close_request_lambda();
4747  }
4748  }
4749 }
4750 
4751 
4775 void Xenium::xcbEvent(const xcb_configure_notify_event_t* event
4776  ) noexcept
4777 {
4778 //std::cout << "Configure Notify: " << to_string(*event) << '\n';
4779  if((event->response_type & 0x80) == 0)
4780  {
4781  // Only care about events with the "synthetic bit" set
4782  return;
4783  }
4784 
4785  const WindowId window_id = event->window;
4786 
4787  auto iter = window_size_map.find(window_id);
4788 
4789  if(iter == std::end(window_size_map))
4790  {
4791  return;
4792  }
4793 
4794  Xenium::WindowSizeData& window_size = iter->second;
4795 
4796  OutputId output_id;
4797  const Output& output = this->output(event->x, event->y, output_id);
4798 
4799  if(window_output_map[window_id] != output_id)
4800  {
4801  window_output_map[window_id] = output_id;
4802 
4803  windowResizeTo(output, window_size, event);
4804 
4805  return;
4806  }
4807 
4808  if(window_size.pixel.width == event->width
4809  && window_size.pixel.height == event->height
4810  )
4811  {
4812  return;
4813  }
4814 
4815  window_size.pixel = {event->width, event->height};
4816 
4817  auto mm = convertPixelToMm(output
4818  , window_size.pixel.width
4819  , window_size.pixel.height
4820  );
4821  window_size.mm = {mm.first, mm.second};
4822 
4823  auto percent = convertPixelToPercent(output
4824  , window_size.pixel.width
4825  , window_size.pixel.height
4826  );
4827  window_size.percent = {percent.first, percent.second};
4828 
4829  window_size.pixel_lambda(window_size.pixel);
4830  window_size.percent_lambda(window_size.percent);
4831  window_size.mm_lambda(window_size.mm);
4832 }
4833 
4834 
4842 void Xenium::xcbEvent(const xcb_enter_notify_event_t* event
4843  ) noexcept
4844 {
4845 //std::cout << "Enter Notify: " << to_string(*event) << '\n';
4846 
4847  const WindowId window_id = event->event;
4848 
4849  if(event->response_type == XCB_LEAVE_NOTIFY)
4850  {
4851  window_on_leave_map[window_id]();
4852  window_keyboard[window_id].on_leave();
4853  return;
4854  }
4855 
4856  xkbIndicatorStateUpdate();
4857 
4858  OutputId output_id = window_output_map[window_id];
4859  Output output = output_map.at(output_id);
4860 
4861  PointPixel point_pixel = {0, event->event_x, event->event_y};
4862 
4863  auto mm = convertPixelToMm(output
4864  , event->event_x
4865  , event->event_y
4866  );
4867  PointMm point_mm = {0, mm.first, mm.second};
4868 
4869  Xenium::WindowSizeData& window_size = window_size_map[window_id];
4870  PointPercent point_percent =
4871  { 0
4872  , (float)event->event_x / (float)window_size.pixel.width
4873  , (float)event->event_y / (float)window_size.pixel.height
4874  };
4875 
4876  window_on_enter_map[window_id].lambda_mm(point_mm, key_modifier);
4877  window_on_enter_map[window_id].lambda_percent(point_percent, key_modifier);
4878  window_on_enter_map[window_id].lambda_pixel(point_pixel, key_modifier);
4879 
4880  window_keyboard[window_id].on_enter();
4881 }
4882 
4883 
4887 void Xenium::xcbEvent(const xcb_expose_event_t* event
4888  ) noexcept
4889 {
4890 //std::cout << "Expose: " << to_string(*event) << '\n';
4891  windowReadySet(event->window);
4892 }
4893 
4894 
4898 void Xenium::xcbEvent(const xcb_focus_in_event_t* event
4899  ) noexcept
4900 {
4901 //std::cout << "Pocus: " << to_string(*event) << '\n';
4902 
4903  const WindowId window_id = event->event;
4904 
4905  if(window_focus_map.contains(window_id) == false)
4906  {
4907  return;
4908  }
4909 
4910  if(event->response_type == XCB_FOCUS_IN)
4911  {
4912  xkbControlsUpdate();
4913  xkbIndicatorStateUpdate();
4914  window_focus_map[window_id](true);
4915  }
4916  else
4917  {
4918  keyDataArrayClear();
4919  window_focus_map[window_id](false);
4920  }
4921 }
4922 
4923 
4927 void Xenium::xcbEvent(const xcb_gravity_notify_event_t* //event ///< The XCB Event
4928  ) noexcept
4929 {
4930 //std::cout << "Gravity Notify: " << to_string(*event) << '\n';
4931 }
4932 
4933 
4961 void Xenium::xcbEvent(const xcb_key_press_event_t* event
4962  ) noexcept
4963 {
4964 //std::cout << "Key Press: " << to_string(*event) << '\n';
4965 
4966  uint32_t key_code = (uint32_t)event->detail - 8;
4967 
4968  constexpr uint16_t CapsLock = 0b0000'0010'0000'0000;
4969  constexpr uint16_t NumLock = 0b0000'0001'0000'0000;
4970  constexpr uint16_t Alt_Left = 0b0000'0000'1000'0000;
4971  constexpr uint16_t Alt_Right = 0b0000'0000'0100'0000;
4972  constexpr uint16_t Control_Left = 0b0000'0000'0010'0000;
4973  constexpr uint16_t Control_Right = 0b0000'0000'0001'0000;
4974  constexpr uint16_t Meta_Left = 0b0000'0000'0000'1000;
4975  constexpr uint16_t Meta_Right = 0b0000'0000'0000'0100;
4976  constexpr uint16_t Shift_Left = 0b0000'0000'0000'0010;
4977  constexpr uint16_t Shift_Right = 0b0000'0000'0000'0001;
4978 
4979  if(event->response_type == XCB_KEY_PRESS)
4980  {
4981  switch(key_code)
4982  {
4983  case KEY_CAPSLOCK:
4984  xkbIndicatorStateUpdate();
4985  xkb_modifier_pressed |= CapsLock;
4986  break;
4987 
4988  case KEY_NUMLOCK:
4989  xkbIndicatorStateUpdate();
4990  xkb_modifier_pressed |= NumLock;
4991  break;
4992 
4993  case KEY_LEFTALT:
4994  xkb_modifier_pressed |= Alt_Left;
4995  break;
4996 
4997  case KEY_RIGHTALT:
4998  xkb_modifier_pressed |= Alt_Right;
4999  break;
5000 
5001  case KEY_LEFTCTRL:
5002  xkb_modifier_pressed |= Control_Left;
5003  break;
5004 
5005  case KEY_RIGHTCTRL:
5006  xkb_modifier_pressed |= Control_Right;
5007  break;
5008 
5009  case KEY_LEFTMETA:
5010  xkb_modifier_pressed |= Meta_Left;
5011  break;
5012 
5013  case KEY_RIGHTMETA:
5014  xkb_modifier_pressed |= Meta_Right;
5015  break;
5016 
5017  case KEY_LEFTSHIFT:
5018  xkb_modifier_pressed |= Shift_Left;
5019  break;
5020 
5021  case KEY_RIGHTSHIFT:
5022  xkb_modifier_pressed |= Shift_Right;
5023  break;
5024  }
5025  }
5026  else
5027  {
5028  switch(key_code)
5029  {
5030  case KEY_CAPSLOCK:
5031  xkbIndicatorStateUpdate();
5032  xkb_modifier_pressed &= (~CapsLock);
5033  break;
5034 
5035  case KEY_NUMLOCK:
5036  xkbIndicatorStateUpdate();
5037  xkb_modifier_pressed &= (~NumLock);
5038  break;
5039 
5040  case KEY_LEFTALT:
5041  xkb_modifier_pressed &= (~Alt_Left);
5042  break;
5043 
5044  case KEY_RIGHTALT:
5045  xkb_modifier_pressed &= (~Alt_Right);
5046  break;
5047 
5048  case KEY_LEFTCTRL:
5049  xkb_modifier_pressed &= (~Control_Left);
5050  break;
5051 
5052  case KEY_RIGHTCTRL:
5053  xkb_modifier_pressed &= (~Control_Right);
5054  break;
5055 
5056  case KEY_LEFTMETA:
5057  xkb_modifier_pressed &= (~Meta_Left);
5058  break;
5059 
5060  case KEY_RIGHTMETA:
5061  xkb_modifier_pressed &= (~Meta_Right);
5062  break;
5063 
5064  case KEY_LEFTSHIFT:
5065  xkb_modifier_pressed &= (~Shift_Left);
5066  break;
5067 
5068  case KEY_RIGHTSHIFT:
5069  xkb_modifier_pressed &= (~Shift_Right);
5070  break;
5071  }
5072  }
5073 
5074  key_modifier.pressed = 0;
5075 
5076  if(xkb_modifier_pressed & CapsLock)
5077  {
5078  key_modifier.pressed |= KeyModifier_CapsLock;
5079  }
5080 
5081  if(xkb_modifier_pressed & NumLock)
5082  {
5083  key_modifier.pressed |= KeyModifier_NumLock;
5084  }
5085 
5086  if(xkb_modifier_pressed & (Alt_Left | Alt_Right))
5087  {
5088  key_modifier.pressed |= KeyModifier_Alt;
5089  }
5090 
5091  if(xkb_modifier_pressed & (Control_Left | Control_Right))
5092  {
5093  key_modifier.pressed |= KeyModifier_Control;
5094  }
5095 
5096  if(xkb_modifier_pressed & (Meta_Left | Meta_Right))
5097  {
5098  key_modifier.pressed |= KeyModifier_Meta;
5099  }
5100 
5101  if(xkb_modifier_pressed & (Shift_Left | Shift_Right))
5102  {
5103  key_modifier.pressed |= KeyModifier_Shift;
5104  }
5105 
5106  const WindowId window_id = event->event;
5107 
5108  key_data_array[key_code].window_id = window_id;
5109 
5110  Key& key = key_data_array[key_code].key;
5111  key.code = key_code;
5112 
5113  if(event->response_type == XCB_KEY_PRESS)
5114  {
5115  key_data_array[key_code].modifier = key_modifier;
5116 
5117  if(key.time == event->time)
5118  {
5119  key.state = KeyState::Repeat;
5120  }
5121  else
5122  {
5123  key.time = event->time;
5124  key.state = KeyState::Pressed;
5125 
5126  key_data_array[key_code].repeat_time =
5127  event->time + xkb_controls.repeat_delay_ms
5128  ;
5129 
5130  window_on_key_map[window_id](key, key_modifier);
5131  }
5132  }
5133  /* If another window is active and a key is pressed then the cursor is
5134  * moved into a Xenium window while the key remains pressed, it is
5135  * possible for the first key event to be recieved is the
5136  * XCB_KEY_RELEASE. This event must be ignored because there was no
5137  * precedding XCB_KEY_PRESS event. To identify the bad XCB_KEY_RELEASE
5138  * event from the good event, the key.time value will be `0` if the the
5139  * pressed event is missing. A non-`0` key.time value means there was
5140  * a preceding XCB_KEY_PRESS event.
5141  */
5142  else if(key.time != 0) // && (event->response_type == XCB_KEY_RELEASE)
5143  {
5144  key.time = event->time;
5145  key.state = KeyState::Released;
5146 
5147  key_data_array[key_code].modifier = key_modifier;
5148  }
5149 }
5150 
5151 
5155 void Xenium::xcbEvent(const xcb_map_notify_event_t* //event ///< The XCB Event
5156  ) noexcept
5157 {
5158 //std::cout << "Map Notify: " << to_string(*event) << '\n';
5159 }
5160 
5161 
5165 void Xenium::xcbEvent(const xcb_motion_notify_event_t* event
5166  ) noexcept
5167 {
5168 //std::cout << "Motion Notify: " << to_string(*event) << '\n';
5169 
5170  const WindowId window_id = event->event;
5171 
5172  OutputId output_id = window_output_map[window_id];
5173  Output output = output_map.at(output_id);
5174 
5175  PointPixel point_pixel = {0, event->event_x, event->event_y};
5176 
5177  auto mm = convertPixelToMm(output
5178  , event->event_x
5179  , event->event_y
5180  );
5181  PointMm point_mm = {0, mm.first, mm.second};
5182 
5183  Xenium::WindowSizeData& window_size = window_size_map[window_id];
5184  PointPercent point_percent =
5185  { 0
5186  , (float)event->event_x / (float)window_size.pixel.width
5187  , (float)event->event_y / (float)window_size.pixel.height
5188  };
5189 
5190  window_on_motion_map[window_id].lambda_mm(point_mm, key_modifier);
5191  window_on_motion_map[window_id].lambda_percent(point_percent, key_modifier);
5192  window_on_motion_map[window_id].lambda_pixel(point_pixel, key_modifier);
5193 }
5194 
5195 
5199 void Xenium::xcbEvent(const xcb_property_notify_event_t* event
5200  ) noexcept
5201 {
5202 //std::cout << "Property Notify: " << to_string(*event) << '\n';
5203 
5204  xcb_generic_error_t generic_error;
5205 
5206  if(event->atom == atom_net_frame_extents)
5207  {
5208  std::vector<int32_t> atom_data = Xenium::atomValueData(event->window
5209  , atom_net_frame_extents
5210  , XCB_ATOM_CARDINAL
5211  , 4
5212  , generic_error
5213  );
5214 
5215  struct
5216  {
5217  int32_t left;
5218  int32_t right;
5219  int32_t top;
5220  int32_t bottom;
5221  }
5222  frame_extents =
5223  { .left = atom_data[0]
5224  , .right = atom_data[1]
5225  , .top = atom_data[2]
5226  , .bottom = atom_data[3]
5227  };
5228 
5229  WindowDecorationsData& window = window_decorations_map[event->window];
5230 
5231  WindowDecorations window_decorations = window.window_decorations;
5232 
5233  if(frame_extents.left == 0
5234  && frame_extents.right == 0
5235  && frame_extents.top == 0
5236  && frame_extents.bottom == 0
5237  )
5238  {
5239  window.window_decorations = WindowDecorations::Client_Side;
5240  }
5241  else
5242  {
5243  window.window_decorations = WindowDecorations::Server_Side;
5244  }
5245 
5246  if(window.window_decorations != window_decorations)
5247  {
5248  window.lambda(window.window_decorations);
5249  }
5250 
5251  return;
5252  }
5253 
5254  if(event->atom == atom_net_wm_state)
5255  {
5256  std::vector<xcb_atom_t> value =
5257  atomValueAtom(event->window, event->atom, generic_error);
5258 
5259  WindowMode new_window_mode = WindowMode::Normal;
5260 
5261  if(zakero::vectorContains(value, atom_net_wm_state_maximized_horz)
5262  && zakero::vectorContains(value, atom_net_wm_state_maximized_vert)
5263  )
5264  {
5265  new_window_mode = WindowMode::Maximized;
5266  }
5267  else if(zakero::vectorContains(value, atom_net_wm_state_fullscreen))
5268  {
5269  new_window_mode = WindowMode::Fullscreen;
5270  }
5271 
5272  if(window_mode_map.contains(event->window))
5273  {
5274  WindowModeData& data = window_mode_map[event->window];
5275  LambdaWindowMode lambda = LambdaWindowMode_DoNothing;
5276  if(data.window_mode != new_window_mode)
5277  {
5278  data.window_mode = new_window_mode;
5279  lambda = data.lambda;
5280  }
5281 
5282  lambda(new_window_mode);
5283  }
5284 
5285  return;
5286  }
5287 }
5288 
5292 void Xenium::xcbEvent(const xcb_reparent_notify_event_t* //event ///< The XCB Event
5293  ) noexcept
5294 {
5295 //std::cout << "Reparent Notify: " << to_string(*event) << '\n';
5296 }
5297 
5298 
5302 void Xenium::xcbEvent(const xcb_unmap_notify_event_t* //event ///< The XCB Event
5303  ) noexcept
5304 {
5305 //std::cout << "Unmap Notify: " << to_string(*event) << '\n';
5306 }
5307 
5308 
5316 void Xenium::xcbWindowCreate(Xenium::WindowCreateData* window_data
5317  ) noexcept
5318 {
5319  window_data->error = xcbWindowCreateValidate(window_data);
5320  if(window_data->error)
5321  {
5322  ZAKERO_XENIUM__DEBUG_ERROR(window_data->error);
5323 
5324  return;
5325  }
5326 
5327  window_data->error = xcbWindowCreateClient(window_data);
5328  if(window_data->error)
5329  {
5330  ZAKERO_XENIUM__DEBUG_ERROR(window_data->error);
5331 
5332  Xenium::WindowDestroyData data =
5333  { .barrier = {}
5334  , .window_id = window_data->window_id
5335  , .gc = window_data->gc
5336  };
5337 
5338  xcbWindowDestroy(&data);
5339 
5340  return;
5341  }
5342 
5343  window_data->error = xcbWindowCreateInit(window_data);
5344  if(window_data->error)
5345  {
5346  ZAKERO_XENIUM__DEBUG_ERROR(window_data->error);
5347 
5348  Xenium::WindowDestroyData data =
5349  { .barrier = {}
5350  , .window_id = window_data->window_id
5351  , .gc = window_data->gc
5352  };
5353 
5354  xcbWindowDestroy(&data);
5355 
5356  return;
5357  }
5358 
5359  return;
5360 }
5361 
5362 
5370 std::error_code Xenium::xcbWindowCreateValidate(Xenium::WindowCreateData* window_data
5371  ) noexcept
5372 {
5373  window_data->output_id = output_map.begin()->first;
5374  Xenium::Output& output = output_map.begin()->second;
5375 
5376  Xenium::SizePixel size_pixel;
5377 
5378  if(window_data->size_unit == Xenium::SizeUnit::Millimeter)
5379  {
5380  auto pixel = convertMmToPixel(output
5381  , window_data->size_mm.width
5382  , window_data->size_mm.height
5383  );
5384  size_pixel = {pixel.first, pixel.second};
5385  }
5386  else if(window_data->size_unit == Xenium::SizeUnit::Percent)
5387  {
5388  auto pixel = convertPercentToPixel(output
5389  , window_data->size_percent.width
5390  , window_data->size_percent.height
5391  );
5392  size_pixel = {pixel.first, pixel.second};
5393  }
5394  else // if(window_data->size_unit == Xenium::SizeUnit::Pixel)
5395  {
5396  size_pixel = window_data->size_pixel;
5397  }
5398 
5399  if((size_pixel.width < Window_Size_Minimum)
5400  || (size_pixel.width < Window_Size_Minimum)
5401  )
5402  {
5403  ZAKERO_XENIUM__DEBUG_ERROR(
5404  ZAKERO_XENIUM__ERROR(Error_Window_Size_Too_Small)
5405  );
5406 
5407  return ZAKERO_XENIUM__ERROR(Error_Window_Size_Too_Small);
5408  }
5409 
5410  if(window_data->size_unit == Xenium::SizeUnit::Millimeter)
5411  {
5412  window_data->size_pixel = size_pixel;
5413 
5414  auto percent = convertPixelToPercent(output
5415  , window_data->size_pixel.width
5416  , window_data->size_pixel.height
5417  );
5418  window_data->size_percent = {percent.first, percent.second};
5419  }
5420  else if(window_data->size_unit == Xenium::SizeUnit::Percent)
5421  {
5422  window_data->size_pixel = size_pixel;
5423 
5424  auto mm = convertPixelToMm(output
5425  , window_data->size_pixel.width
5426  , window_data->size_pixel.height
5427  );
5428  window_data->size_mm = {mm.first, mm.second};
5429  }
5430  else // if(window_data->size_unit == Xenium::SizeUnit::Pixel)
5431  {
5432  auto mm = convertPixelToMm(output
5433  , window_data->size_pixel.width
5434  , window_data->size_pixel.height
5435  );
5436  window_data->size_mm = {mm.first, mm.second};
5437 
5438  auto percent = convertPixelToPercent(output
5439  , window_data->size_pixel.width
5440  , window_data->size_pixel.height
5441  );
5442  window_data->size_percent = {percent.first, percent.second};
5443  }
5444 
5445  return ZAKERO_XENIUM__ERROR(Error_None);
5446 }
5447 
5448 
5456 std::error_code Xenium::xcbWindowCreateClient(Xenium::WindowCreateData* data
5457  ) noexcept
5458 {
5459  data->window_id = xcb_generate_id(this->connection);
5460 
5461  xcb_void_cookie_t cookie =
5462  xcb_create_window_aux_checked(this->connection
5463  , this->screen->root_depth // depth
5464  , data->window_id // requested id
5465  , this->screen->root // parent window id
5466  , 0, 0 // location x,y
5467  , data->size_pixel.width // width
5468  , data->size_pixel.height // height
5469  , 0 // border width
5470  , XCB_WINDOW_CLASS_INPUT_OUTPUT // "class"
5471  , this->screen->root_visual // visual
5472  , data->value_mask
5473  , &data->value_list
5474  );
5475 
5476  xcb_generic_error_t generic_error;
5477 
5478  if(requestCheckHasError(cookie, generic_error))
5479  {
5480  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
5481 
5482  data->window_id = 0;
5483 
5484  return ZAKERO_XENIUM__ERROR(Error_Unknown);
5485  }
5486 
5487  data->atom_close_request = atomCreateDeleteWindow(data->window_id
5488  , generic_error
5489  );
5490 
5491  if(data->atom_close_request == XCB_ATOM_NONE)
5492  {
5493  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
5494 
5495  return ZAKERO_XENIUM__ERROR(Error_Unknown);
5496  }
5497 
5498  xcb_size_hints_t size_hints =
5499  { .flags = 0
5500  , .x = 0
5501  , .y = 0
5502  , .width = 0
5503  , .height = 0
5504  , .min_width = 0
5505  , .min_height = 0
5506  , .max_width = 0
5507  , .max_height = 0
5508  , .width_inc = 0
5509  , .height_inc = 0
5510  , .min_aspect_num = 0
5511  , .min_aspect_den = 0
5512  , .max_aspect_num = 0
5513  , .max_aspect_den = 0
5514  , .base_width = 0
5515  , .base_height = 0
5516  , .win_gravity = 0
5517  };
5518 
5519  xcb_change_property_checked(this->connection
5520  , XCB_PROP_MODE_REPLACE
5521  , data->window_id
5522  , XCB_ATOM_WM_NORMAL_HINTS
5523  , XCB_ATOM_WM_SIZE_HINTS
5524  , 32 // 32-bit values
5525  , 18 // 18 values
5526  , &size_hints
5527  );
5528 
5529  if(requestCheckHasError(cookie, generic_error))
5530  {
5531  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
5532 
5533  return ZAKERO_XENIUM__ERROR(Error_Unknown);
5534  }
5535 
5536  data->gc = xcb_generate_id(this->connection);
5537  cookie = xcb_create_gc_checked(this->connection
5538  , data->gc
5539  , data->window_id
5540  , 0
5541  , nullptr
5542  );
5543 
5544  if(requestCheckHasError(cookie, generic_error))
5545  {
5546  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
5547 
5548  data->gc = 0;
5549 
5550  return ZAKERO_XENIUM__ERROR(Error_Unknown);
5551  }
5552 
5553  return ZAKERO_XENIUM__ERROR(Error_None);
5554 }
5555 
5556 
5560 void Xenium::xcbWindowDestroy(Xenium::WindowDestroyData* window_data
5561  ) noexcept
5562 {
5563  if(window_data->window_id == 0)
5564  {
5565  return;
5566  }
5567 
5568  if(window_data->gc != 0)
5569  {
5570  xcb_free_gc(this->connection, window_data->gc);
5571 
5572  window_data->gc = 0;
5573  }
5574 
5575  xcb_destroy_window(this->connection, window_data->window_id);
5576 
5577  window_decorations_map.erase(window_data->window_id);
5578  window_delete_map.erase(window_data->window_id);
5579  window_focus_map.erase(window_data->window_id);
5580  window_keyboard.erase(window_data->window_id);
5581  window_map.erase(window_data->window_id);
5582  window_mode_map.erase(window_data->window_id);
5583  window_on_axis_map.erase(window_data->window_id);
5584  window_on_button_map.erase(window_data->window_id);
5585  window_on_enter_map.erase(window_data->window_id);
5586  window_on_key_map.erase(window_data->window_id);
5587  window_on_leave_map.erase(window_data->window_id);
5588  window_on_motion_map.erase(window_data->window_id);
5589  window_output_map.erase(window_data->window_id);
5590  window_ready_map.erase(window_data->window_id);
5591  window_size_map.erase(window_data->window_id);
5592 
5593  window_data->window_id = 0;
5594 }
5595 
5596 
5604 std::error_code Xenium::xcbWindowCreateInit(Xenium::WindowCreateData* data
5605  ) noexcept
5606 {
5607  window_size_map[data->window_id] =
5608  { .mm = data->size_mm
5609  , .mm_minimum = {0, 0}
5610  , .mm_maximum = {0, 0}
5611  , .mm_lambda = LambdaSizeMm_DoNothing
5612  , .percent = data->size_percent
5613  , .percent_minimum = {0, 0}
5614  , .percent_maximum = {0, 0}
5615  , .percent_lambda = LambdaSizePercent_DoNothing
5616  , .pixel = data->size_pixel
5617  , .pixel_minimum = {0, 0}
5618  , .pixel_maximum = {0, 0}
5619  , .pixel_lambda = LambdaSizePixel_DoNothing
5620  , .unit = data->size_unit
5621  };
5622 
5623  window_mode_map[data->window_id] =
5624  { .window_mode = WindowMode::Normal
5625  , .lambda = LambdaWindowMode_DoNothing
5626  };
5627 
5628  window_output_map[data->window_id] = data->output_id;
5629 
5630  window_keyboard[data->window_id] =
5631  { .on_enter = Lambda_DoNothing
5632  , .on_leave = Lambda_DoNothing
5633  };
5634 
5635  window_on_key_map[data->window_id] = LambdaKey_DoNothing;
5636  window_on_leave_map[data->window_id] = Lambda_DoNothing;
5637  window_on_axis_map[data->window_id] = LambdaAxis_DoNothing;
5638 
5639  window_on_motion_map[data->window_id] =
5640  { .lambda_mm = LambdaPointMm_DoNothing
5641  , .lambda_percent = LambdaPointPercent_DoNothing
5642  , .lambda_pixel = LambdaPointPixel_DoNothing
5643  };
5644 
5645  window_on_button_map[data->window_id] =
5646  { .lambda_mm = LambdaButtonMm_DoNothing
5647  , .lambda_percent = LambdaButtonPercent_DoNothing
5648  , .lambda_pixel = LambdaButtonPixel_DoNothing
5649  };
5650 
5651  window_on_enter_map[data->window_id] =
5652  { .lambda_mm = LambdaPointMm_DoNothing
5653  , .lambda_percent = LambdaPointPercent_DoNothing
5654  , .lambda_pixel = LambdaPointPixel_DoNothing
5655  };
5656 
5657  window_decorations_map[data->window_id] =
5658  { .window_decorations = WindowDecorations::Server_Side
5659  , .lambda = LambdaWindowDecorations_DoNothing
5660  };
5661 
5662  window_focus_map[data->window_id] = LambdaBool_DoNothing;
5663 
5664  window_delete_map[data->window_id] =
5665  { .close_request_lambda = Lambda_DoNothing
5666  , .atom_close_request = data->atom_close_request
5667  };
5668 
5669  window_ready_map[data->window_id] = false;
5670 
5671  return ZAKERO_XENIUM__ERROR(Error_None);
5672 }
5673 
5674 // }}}
5675 // {{{ XCB : Atom
5676 
5685 std::error_code Xenium::atomInit() noexcept
5686 {
5687  auto cookie_motif_wm_hints = internAtomRequest("_MOTIF_WM_HINTS");
5688  auto cookie_net_frame_extents = internAtomRequest("_NET_FRAME_EXTENTS");
5689  auto cookie_net_wm_state = internAtomRequest("_NET_WM_STATE");
5690  auto cookie_net_wm_state_fullscreen = internAtomRequest("_NET_WM_STATE_FULLSCREEN");
5691  auto cookie_net_wm_state_hidden = internAtomRequest("_NET_WM_STATE_HIDDEN");
5692  auto cookie_net_wm_state_maximized_horz = internAtomRequest("_NET_WM_STATE_MAXIMIZED_HORZ");
5693  auto cookie_net_wm_state_maximized_vert = internAtomRequest("_NET_WM_STATE_MAXIMIZED_VERT");
5694  auto cookie_wm_change_state = internAtomRequest("WM_CHANGE_STATE");
5695  auto cookie_wm_delete_window = internAtomRequest("WM_DELETE_WINDOW");
5696  auto cookie_wm_protocols = internAtomRequest("WM_PROTOCOLS");
5697 
5698  xcb_generic_error_t generic_error;
5699 
5700  atom_motif_wm_hints = internAtomReply(cookie_motif_wm_hints , generic_error);
5701  atom_net_frame_extents = internAtomReply(cookie_net_frame_extents , generic_error);
5702  atom_net_wm_state = internAtomReply(cookie_net_wm_state , generic_error);
5703  atom_net_wm_state_fullscreen = internAtomReply(cookie_net_wm_state_fullscreen , generic_error);
5704  atom_net_wm_state_hidden = internAtomReply(cookie_net_wm_state_hidden , generic_error);
5705  atom_net_wm_state_maximized_horz = internAtomReply(cookie_net_wm_state_maximized_horz , generic_error);
5706  atom_net_wm_state_maximized_vert = internAtomReply(cookie_net_wm_state_maximized_vert , generic_error);
5707  atom_wm_change_state = internAtomReply(cookie_wm_change_state , generic_error);
5708  atom_wm_delete_window = internAtomReply(cookie_wm_delete_window , generic_error);
5709  atom_wm_protocols = internAtomReply(cookie_wm_protocols , generic_error);
5710 
5711  if(atom_wm_delete_window == XCB_ATOM_NONE)
5712  {
5713  ZAKERO_XENIUM__DEBUG_ERROR(
5714  ZAKERO_XENIUM__ERROR(Error_Xcb_WM_Delete_Window_Not_Available)
5715  );
5716 
5717  return ZAKERO_XENIUM__ERROR(Error_Xcb_WM_Delete_Window_Not_Available);
5718  }
5719 
5720  if(atom_wm_protocols == XCB_ATOM_NONE)
5721  {
5722  ZAKERO_XENIUM__DEBUG_ERROR(
5723  ZAKERO_XENIUM__ERROR(Error_Xcb_WM_Protocols_Not_Available)
5724  );
5725 
5726  return ZAKERO_XENIUM__ERROR(Error_Xcb_WM_Protocols_Not_Available);
5727  }
5728 
5729  if(atom_net_wm_state == XCB_ATOM_NONE)
5730  {
5731  ZAKERO_XENIUM__DEBUG_ERROR(
5732  ZAKERO_XENIUM__ERROR(Error_Xcb_NETWM_State_Not_Available)
5733  );
5734 
5735  return ZAKERO_XENIUM__ERROR(Error_Xcb_NETWM_State_Not_Available);
5736  }
5737 
5738  if(atom_net_wm_state_fullscreen == XCB_ATOM_NONE)
5739  {
5740  ZAKERO_XENIUM__DEBUG_ERROR(
5741  ZAKERO_XENIUM__ERROR(Error_Xcb_Fullscreen_Not_Available)
5742  );
5743 
5744  return ZAKERO_XENIUM__ERROR(Error_Xcb_Fullscreen_Not_Available);
5745  }
5746 
5747  if(atom_net_wm_state_hidden == XCB_ATOM_NONE)
5748  {
5749  ZAKERO_XENIUM__DEBUG_ERROR(
5750  ZAKERO_XENIUM__ERROR(Error_Xcb_Hidden_Not_Available)
5751  );
5752 
5753  return ZAKERO_XENIUM__ERROR(Error_Xcb_Hidden_Not_Available);
5754  }
5755 
5756  if((atom_net_wm_state_maximized_horz == XCB_ATOM_NONE)
5757  || (atom_net_wm_state_maximized_vert == XCB_ATOM_NONE)
5758  )
5759  {
5760  ZAKERO_XENIUM__DEBUG_ERROR(
5761  ZAKERO_XENIUM__ERROR(Error_Xcb_Maximized_Window_Not_Available)
5762  );
5763 
5764  return ZAKERO_XENIUM__ERROR(Error_Xcb_Maximized_Window_Not_Available);
5765  }
5766 
5767  return ZAKERO_XENIUM__ERROR(Error_None);
5768 }
5769 
5770 
5784 xcb_atom_t Xenium::atomCreateDeleteWindow(const WindowId window_id
5785  , xcb_generic_error_t& generic_error
5786  ) noexcept
5787 {
5788  bool property_was_set = windowPropertySet(window_id
5789  , atom_wm_protocols
5790  , atom_wm_delete_window
5791  , generic_error
5792  );
5793 
5794  if(property_was_set == false)
5795  {
5796  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
5797  return XCB_ATOM_NONE;
5798  }
5799 
5800  return atom_wm_delete_window;
5801 }
5802 
5803 
5815 std::string Xenium::atomName(const xcb_atom_t atom
5816  ) noexcept
5817 {
5818  if(atom == XCB_ATOM_NONE)
5819  {
5820  return "";
5821  }
5822 
5823  xcb_get_atom_name_cookie_t cookie =
5824  xcb_get_atom_name(this->connection, atom);
5825 
5826  xcb_generic_error_t* error = nullptr;
5827 
5828  xcb_get_atom_name_reply_t* reply =
5829  xcb_get_atom_name_reply(this->connection
5830  , cookie
5831  , &error
5832  );
5833 
5834  if(reply == nullptr)
5835  {
5836  return "";
5837  }
5838 
5839  char* name = xcb_get_atom_name_name(reply);
5840 
5841  std::string atom_name(name);
5842 
5843  free(reply);
5844 
5845  return atom_name;
5846 }
5847 
5848 
5861 std::vector<xcb_atom_t> Xenium::atomValueAtom(const WindowId window_id
5862  , const xcb_atom_t property_atom
5863  , xcb_generic_error_t& generic_error
5864  ) noexcept
5865 {
5866  xcb_get_property_cookie_t property_cookie =
5867  xcb_get_property(this->connection
5868  , 0 // Don't Delete
5869  , window_id
5870  , property_atom
5871  , XCB_ATOM_ATOM
5872  , 0 // Data Offset
5873  , 2 // Number of 32-bit values
5874  );
5875 
5876  xcb_generic_error_t* error = nullptr;
5877 
5878  xcb_get_property_reply_t* property =
5879  xcb_get_property_reply(this->connection
5880  , property_cookie
5881  , &error
5882  );
5883 
5884  if(property == nullptr)
5885  {
5886  generic_error = *error;
5887 
5888  return {};
5889  }
5890 
5891  int length = xcb_get_property_value_length(property) / 4;
5892 
5893  std::vector<xcb_atom_t> retval(length);
5894 
5895  xcb_atom_t* value = (xcb_atom_t*)xcb_get_property_value(property);
5896 
5897  for(int i = 0; i < length; i++)
5898  {
5899  retval[i] = value[i];
5900  }
5901 
5902  free(property);
5903 
5904  return retval;
5905 }
5906 
5907 
5925 std::vector<int32_t> Xenium::atomValueData(const WindowId window_id
5926  , const xcb_atom_t property_atom
5927  , const xcb_atom_t type
5928  , const size_t count
5929  , xcb_generic_error_t& generic_error
5930  ) noexcept
5931 {
5932  xcb_get_property_cookie_t property_cookie =
5933  xcb_get_property(this->connection
5934  , 0 // Don't Delete
5935  , window_id
5936  , property_atom
5937  , type
5938  , 0 // Data Offset
5939  , count // Number of 32-bit values
5940  );
5941 
5942  xcb_generic_error_t* error = nullptr;
5943 
5944  xcb_get_property_reply_t* property =
5945  xcb_get_property_reply(this->connection
5946  , property_cookie
5947  , &error
5948  );
5949 
5950  if(property == nullptr)
5951  {
5952  generic_error = *error;
5953 
5954  return {};
5955  }
5956 
5957  int length = xcb_get_property_value_length(property) / 4;
5958 
5959  std::vector<int32_t> vector(length);
5960 
5961  int32_t* value = (int32_t*)xcb_get_property_value(property);
5962 
5963  for(int i = 0; i < length; i++)
5964  {
5965  vector[i] = value[i];
5966  }
5967 
5968  free(property);
5969 
5970  return vector;
5971 }
5972 
5973 
5992 xcb_atom_t Xenium::internAtom(const std::string& atom_name
5993  , const bool create_if_needed
5994  , xcb_generic_error_t& generic_error
5995  ) noexcept
5996 {
5997  xcb_generic_error_t* error = nullptr;
5998 
5999  xcb_intern_atom_reply_t* atom_reply =
6000  xcb_intern_atom_reply(this->connection
6001  , xcb_intern_atom(this->connection
6002  // 0: Create if needed
6003  // 1: result XCB_ATOM_NONE if not exist
6004  , create_if_needed ? 0 : 1
6005  , atom_name.length()
6006  , atom_name.c_str()
6007  )
6008  , &error
6009  );
6010 
6011  xcb_atom_t atom;
6012 
6013  if(error != nullptr)
6014  {
6015  atom = XCB_ATOM_NONE;
6016  generic_error = *error;
6017  free(error);
6018 
6019  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
6020  }
6021  else if(atom_reply->atom == XCB_ATOM_NONE
6022  && atom_name != "XCB_ATOM_NONE"
6023  )
6024  {
6025  ZAKERO_XENIUM__DEBUG << "Error: Failed to get \""
6026  << atom_name
6027  << "\" atom.\n"
6028  ;
6029 
6030  atom = XCB_ATOM_NONE;
6031  }
6032  else
6033  {
6034  atom = atom_reply->atom;
6035  }
6036 
6037  free(atom_reply);
6038 
6039  return atom;
6040 }
6041 
6042 
6057 xcb_intern_atom_cookie_t Xenium::internAtomRequest(const std::string& atom_name
6058  , const bool create_if_needed
6059  ) noexcept
6060 {
6061  xcb_intern_atom_cookie_t cookie =
6062  xcb_intern_atom(this->connection
6063  // 0: Create if needed
6064  // 1: result XCB_ATOM_NONE if not exist
6065  , !create_if_needed
6066  , atom_name.length()
6067  , atom_name.c_str()
6068  );
6069 
6070  return cookie;
6071 }
6072 
6073 
6085 xcb_atom_t Xenium::internAtomReply(const xcb_intern_atom_cookie_t intern_atom_cookie
6086  , xcb_generic_error_t& generic_error
6087  ) noexcept
6088 {
6089  xcb_generic_error_t* error = nullptr;
6090 
6091  xcb_intern_atom_reply_t* atom_reply =
6092  xcb_intern_atom_reply(this->connection
6093  , intern_atom_cookie
6094  , &error
6095  );
6096 
6097  xcb_atom_t atom;
6098 
6099  if(error != nullptr)
6100  {
6101  atom = XCB_ATOM_NONE;
6102  generic_error = *error;
6103  free(error);
6104 
6105  ZAKERO_XENIUM__DEBUG << "Error: " << to_string(generic_error) << '\n';
6106  }
6107  else if(atom_reply->atom == XCB_ATOM_NONE)
6108  {
6109  ZAKERO_XENIUM__DEBUG << "Error: Failed to get atom.\n";
6110 
6111  atom = XCB_ATOM_NONE;
6112  }
6113  else
6114  {
6115  atom = atom_reply->atom;
6116  }
6117 
6118  free(atom_reply);
6119 
6120  return atom;
6121 }
6122 
6123 // }}}
6124 // {{{ XCB : RandR
6125 
6134 std::error_code Xenium::randrInit() noexcept
6135 {
6136  const xcb_query_extension_reply_t* randr =
6137  xcb_get_extension_data(this->connection, &xcb_randr_id);
6138 
6139  if(randr->present == 0)
6140  {
6141  ZAKERO_XENIUM__DEBUG_ERROR(
6142  ZAKERO_XENIUM__ERROR(Error_RandR_Not_Available)
6143  );
6144 
6145  return ZAKERO_XENIUM__ERROR(Error_RandR_Not_Available);
6146  }
6147 
6148  randr_error_base = randr->first_error;
6149  randr_event_base = randr->first_event;
6150 
6151  xcb_generic_error_t* xcb_generic_error = nullptr;
6152 
6153  xcb_randr_query_version_reply_t* randr_query_version =
6154  xcb_randr_query_version_reply(this->connection
6155  , xcb_randr_query_version(this->connection
6156  , std::numeric_limits<uint32_t>::max()
6157  , std::numeric_limits<uint32_t>::max()
6158  )
6159  , &xcb_generic_error
6160  );
6161 
6162  randr_query_version_major = randr_query_version->major_version;
6163  randr_query_version_minor = randr_query_version->minor_version;
6164 
6165  ZAKERO_FREE(randr_query_version);
6166 
6167  if(randr_query_version_major < 1
6168  || (randr_query_version_major == 1
6169  && randr_query_version_minor < 1
6170  )
6171  )
6172  {
6173  ZAKERO_XENIUM__DEBUG_ERROR(
6174  ZAKERO_XENIUM__ERROR(Error_RandR_Version_Too_Old)
6175  );
6176 
6177  return ZAKERO_XENIUM__ERROR(Error_RandR_Version_Too_Old);
6178  }
6179 
6180  xcb_randr_select_input(this->connection
6181  , this->screen->root
6182  , 0
6183  | XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE
6184  | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE
6185  /* Might be of future use
6186  | XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE
6187  | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY
6188  | XCB_RANDR_NOTIFY_MASK_PROVIDER_CHANGE
6189  | XCB_RANDR_NOTIFY_MASK_PROVIDER_PROPERTY
6190  | XCB_RANDR_NOTIFY_MASK_RESOURCE_CHANGE
6191  */
6192  );
6193 
6194  return ZAKERO_XENIUM__ERROR(Error_None);
6195 }
6196 
6197 
6201 void Xenium::randrEvent(const xcb_randr_crtc_change_t* event
6202  ) noexcept
6203 {
6204 //std::cout << "RandR CRTC Chang:" << *event << '\n';
6205  OutputId output_id = event->crtc;
6206 
6207  if(this->screen->root != event->window
6208  || output_map.contains(output_id) == false
6209  )
6210  {
6211  return;
6212  }
6213 
6214  Output& output = output_map[output_id];
6215 
6216  if(output.x == event->x
6217  && output.y == event->y
6218  && output.width == event->width
6219  && output.height == event->height
6220  && output.transform == event->rotation
6221  )
6222  {
6223  return;
6224  }
6225 
6226  output.x = event->x;
6227  output.y = event->y;
6228  output.width = event->width;
6229  output.height = event->height;
6230  output.transform = event->rotation;
6231 
6232  output.pixels_per_mm_horizontal = (float)event->width / output.physical_width_mm;
6233  output.pixels_per_mm_vertical = (float)event->height / output.physical_height_mm;
6234 
6235  output_on_change(output_id);
6236 }
6237 
6238 
6242 void Xenium::randrEvent(const xcb_randr_output_change_t* event
6243  ) noexcept
6244 {
6245 //std::cout << "RandR Output Chg:" << *event << '\n';
6246 
6247  if(this->screen->root != event->window)
6248  {
6249  return;
6250  }
6251 
6252  OutputId output_id = event->crtc;
6253 
6254  if(event->connection == XCB_RANDR_CONNECTION_DISCONNECTED)
6255  {
6256  if(output_map.contains(output_id))
6257  {
6258  output_on_remove(output_id);
6259  }
6260 
6261  std::lock_guard<std::mutex> lock(output_mutex);
6262 
6263  output_map.erase(output_id);
6264  }
6265  else if((event->connection == XCB_RANDR_CONNECTION_CONNECTED)
6266  || (output_map.contains(output_id) == false)
6267  )
6268  {
6269  std::error_code error;
6270  {
6271  std::lock_guard<std::mutex> lock(output_mutex);
6272 
6273  error = outputAdd(event->crtc, event->output);
6274  }
6275 
6276  if(!error)
6277  {
6278  output_on_add(output_id);
6279  }
6280 # if ZAKERO_XENIUM__DEBUG_ENABLED
6281  else
6282  {
6283  ZAKERO_XENIUM__DEBUG_ERROR(error);
6284  }
6285 # endif
6286 
6287  }
6288  else
6289  {
6290  Output& output = output_map[output_id];
6291 
6292  if(output.subpixel != event->subpixel_order
6293  || output.transform != event->rotation
6294  )
6295  {
6296  output.subpixel = event->subpixel_order;
6297  output.transform = event->rotation;
6298 
6299  output_on_change(output_id);
6300  }
6301  }
6302 }
6303 
6304 
6311 void Xenium::randrEvent(const xcb_randr_notify_event_t* event
6312  ) noexcept
6313 {
6314  switch(event->subCode)
6315  {
6316  case XCB_RANDR_NOTIFY_CRTC_CHANGE:
6317  randrEvent(&event->u.cc);
6318  break;
6319  case XCB_RANDR_NOTIFY_OUTPUT_CHANGE:
6320  randrEvent(&event->u.oc);
6321  break;
6322  case XCB_RANDR_NOTIFY_OUTPUT_PROPERTY:
6323  // Not Used
6324  break;
6325  case XCB_RANDR_NOTIFY_PROVIDER_CHANGE:
6326  // Not Used
6327  break;
6328  case XCB_RANDR_NOTIFY_PROVIDER_PROPERTY:
6329  // Not Used
6330  break;
6331  case XCB_RANDR_NOTIFY_RESOURCE_CHANGE:
6332  // Not Used
6333  break;
6334  case XCB_RANDR_NOTIFY_LEASE:
6335  // Not Used
6336  break;
6337  default:
6338  fprintf(stderr, "Unhandled Sub-Event %d\n", event->subCode);
6339  }
6340 }
6341 
6342 
6346 void Xenium::randrEvent(const xcb_randr_screen_change_notify_event_t* //event ///< The event
6347  ) noexcept
6348 {
6349 //std::cout << "RandR ScrnChange:" << to_string(*event) << '\n';
6350 }
6351 
6352 // }}}
6353 // {{{ XCB : XKB
6354 
6362 std::error_code Xenium::xkbInit() noexcept
6363 {
6364  xcb_xkb_use_extension_reply_t* extension_reply =
6365  xcb_xkb_use_extension_reply(this->connection
6366  , xcb_xkb_use_extension(this->connection, 1, 0)
6367  , nullptr
6368  );
6369 
6370  if(extension_reply == nullptr)
6371  {
6372  ZAKERO_XENIUM__DEBUG_ERROR(
6373  ZAKERO_XENIUM__ERROR(Error_Xcb_Xkb_Not_Available)
6374  );
6375 
6376  return ZAKERO_XENIUM__ERROR(Error_Xcb_Xkb_Not_Available);
6377  }
6378 
6379  free(extension_reply);
6380 
6381  return ZAKERO_XENIUM__ERROR(Error_None);
6382 }
6383 
6384 
6392 void Xenium::keyDataArrayClear() noexcept
6393 {
6394  const auto time_now = ZAKERO_STEADY_TIME_NOW(milliseconds);
6395 
6396  for(auto& key_data : key_data_array)
6397  {
6398  Key& key = key_data.key;
6399 
6400  if(key.time == 0)
6401  {
6402  continue;
6403  }
6404 
6405  key.state = KeyState::Released;
6406  key.time = time_now;
6407 
6408  const KeyModifier& modifier = key_data.modifier;
6409  const WindowId& window_id = key_data.window_id;
6410 
6411  window_on_key_map[window_id](key, modifier);
6412 
6413  key.time = 0;
6414  }
6415 }
6416 
6417 
6426 void Xenium::keyDataArrayProcess() noexcept
6427 {
6428  for(auto& key_data : key_data_array)
6429  {
6430  Key& key = key_data.key;
6431 
6432  if(key.time == 0)
6433  {
6434  continue;
6435  }
6436 
6437  if(key.state == KeyState::Pressed)
6438  {
6439  key.state = KeyState::Repeat;
6440 
6441  continue;
6442  }
6443 
6444  const WindowId& window_id = key_data.window_id;
6445 
6446  if(key.state == KeyState::Released)
6447  {
6448  window_on_key_map[window_id](key, key_data.modifier);
6449  key.time = 0;
6450 
6451  continue;
6452  }
6453 
6454  auto& repeat_time = key_data.repeat_time;
6455 
6456  const auto time_now = ZAKERO_STEADY_TIME_NOW(milliseconds);
6457 
6458  if(key.state == KeyState::Repeat
6459  && repeat_time < time_now
6460  )
6461  {
6462  window_on_key_map[window_id](key, key_modifier);
6463  key.time = repeat_time;
6464  repeat_time += xkb_controls.repeat_interval_ms;
6465 
6466  continue;
6467  }
6468  }
6469 }
6470 
6471 
6475 void Xenium::xkbControlsUpdate() noexcept
6476 {
6477  xcb_xkb_get_controls_reply_t* controls_reply =
6478  xcb_xkb_get_controls_reply(this->connection
6479  , xcb_xkb_get_controls(this->connection
6480  , XCB_XKB_ID_USE_CORE_KBD
6481  )
6482  , nullptr
6483  );
6484 
6485  if(controls_reply == nullptr)
6486  {
6487  return;
6488  }
6489 
6490  xkb_controls.repeat_delay_ms = controls_reply->repeatDelay;
6491  xkb_controls.repeat_interval_ms = controls_reply->repeatInterval;
6492 
6493  free(controls_reply);
6494 }
6495 
6496 
6500 void Xenium::xkbIndicatorStateUpdate() noexcept
6501 {
6502  xcb_xkb_get_indicator_state_reply_t* reply =
6503  xcb_xkb_get_indicator_state_reply(this->connection
6504  , xcb_xkb_get_indicator_state(this->connection
6505  , XCB_XKB_ID_USE_CORE_KBD
6506  )
6507  , nullptr
6508  );
6509 
6510  if(reply == nullptr)
6511  {
6512  return;
6513  }
6514 
6515  const uint32_t state = reply->state;
6516  const uint32_t caps_lock = KeyModifier_CapsLock;
6517  const uint32_t num_lock = KeyModifier_NumLock;
6518 
6519  key_modifier.locked = 0
6520  | (bool(state & XCB_XKB_INDICATOR_STATE_CAPSLOCK) * caps_lock)
6521  | (bool(state & XCB_XKB_INDICATOR_STATE_NUMLOCK) * num_lock)
6522  ;
6523 
6524  free(reply);
6525 
6526  return;
6527 }
6528 
6529 // }}}
6530 // {{{ XCB : Utility
6531 
6543 bool Xenium::requestCheckHasError(const xcb_void_cookie_t& void_cookie
6544  , xcb_generic_error_t& generic_error
6545  ) noexcept
6546 {
6547  xcb_generic_error_t* error = xcb_request_check(this->connection, void_cookie);
6548 
6549  if(error != nullptr)
6550  {
6551  std::cout << "requestCheck Error: " << to_string(*error) << '\n';
6552 
6553  generic_error = *error;
6554 
6555  free(error);
6556 
6557  return true;
6558  }
6559 
6560  return false;
6561 }
6562 
6563 // }}}
6564 // {{{ Class Window : Documentation
6565 
6642 // }}}
6643 // {{{ Class Window : Constructor / Destructor
6644 
6665  , void* data
6666  )
6667  : xenium(xenium)
6668  , frame_buffer(nullptr)
6669  , frame_buffer_size{0, 0}
6670  , window_id(((WindowCreateData*)data)->window_id)
6671  , gc(((WindowCreateData*)data)->gc)
6672  , frame_buffer_length(0)
6673  , frame_time(0)
6674 {
6675 }
6676 
6677 
6682 {
6683  Xenium::WindowDestroyData data =
6684  { .barrier = {}
6685  , .window_id = this->window_id
6686  , .gc = this->gc
6687  };
6688 
6689  std::future<void> barrier = data.barrier.get_future();
6690 
6691  xenium->windowDestroyAddToQueue(&data);
6692 
6693  barrier.wait();
6694 
6695  ZAKERO_FREE(frame_buffer);
6696 
6697  window_id = 0;
6698  xenium = nullptr;
6699 }
6700 
6701 // }}}
6702 // {{{ Class Window : Configuration
6703 
6720 void Xenium::Window::classSet(const std::string& class_name
6721  ) noexcept
6722 {
6723  bool success;
6724  xcb_generic_error_t generic_error;
6725 
6726  success = xenium->windowPropertySet(window_id
6727  , XCB_ATOM_WM_CLASS
6728  , class_name
6729  , generic_error
6730  );
6731 
6732  if(success == false)
6733  {
6734  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
6735  }
6736 }
6737 
6738 
6745 void Xenium::Window::titleSet(const std::string& title
6746  ) noexcept
6747 {
6748  bool success;
6749  xcb_generic_error_t generic_error;
6750 
6751  success = xenium->windowPropertySet(window_id
6752  , XCB_ATOM_WM_NAME
6753  , title
6754  , generic_error
6755  );
6756 
6757  if(success == false)
6758  {
6759  ZAKERO_XENIUM__DEBUG_VAR(to_string(generic_error));
6760  }
6761 }
6762 
6763 // }}}
6764 // {{{ Class Window : Events
6765 
6778  ) noexcept
6779 {
6780  WindowDeleteData& window_delete = xenium->window_delete_map[window_id];
6781 
6782  if(lambda == nullptr)
6783  {
6784  window_delete.close_request_lambda = Lambda_DoNothing;
6785  }
6786  else
6787  {
6788  window_delete.close_request_lambda = lambda;
6789  }
6790 }
6791 
6792 
6807  ) noexcept
6808 {
6809  if(lambda == nullptr)
6810  {
6811  xenium->window_focus_map[window_id] = LambdaBool_DoNothing;
6812  }
6813  else
6814  {
6815  xenium->window_focus_map[window_id] = lambda;
6816  }
6817 }
6818 
6819 // }}}
6820 // {{{ Class Window : Decorations
6821 
6838  ) noexcept
6839 {
6840  std::error_code error;
6841 
6842  if(decorations == WindowDecorations::Client_Side)
6843  {
6844  // The application will render the borders
6845  error = xenium->windowBorder(window_id, false);
6846  }
6847  else
6848  {
6849  // The X11 will render the borders
6850  error = xenium->windowBorder(window_id, true);
6851  }
6852 
6853 # if ZAKERO_XENIUM__DEBUG_ENABLED
6854  if(error)
6855  {
6856  ZAKERO_XENIUM__DEBUG_ERROR(error);
6857  }
6858 # endif
6859 
6860  return error;
6861 }
6862 
6863 
6872  ) noexcept
6873 {
6874  WindowDecorationsData& window_decorations = xenium->window_decorations_map[window_id];
6875 
6876  if(lambda)
6877  {
6878  window_decorations.lambda = lambda;
6879  }
6880  else
6881  {
6882  window_decorations.lambda = LambdaWindowDecorations_DoNothing;
6883  }
6884 }
6885 
6886 // }}}
6887 // {{{ Class Window : Size
6888 
6906 std::error_code Xenium::Window::sizeSet(const Xenium::SizeMm& size
6907  ) noexcept
6908 {
6909  std::lock_guard lock(xenium->output_mutex);
6910 
6911  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
6912 
6913  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
6914  window_size.unit = SizeUnit::Millimeter;
6915 
6916  auto pixel = xenium->convertMmToPixel(output
6917  , size.width
6918  , size.height
6919  );
6920 
6921  if(pixel.first < Window_Size_Minimum
6922  || pixel.second < Window_Size_Minimum
6923  )
6924  {
6925  return ZAKERO_XENIUM__ERROR(Error_Window_Size_Too_Small);
6926  }
6927 
6928  if(pixel.first == window_size.pixel.width
6929  && pixel.second == window_size.pixel.height
6930  )
6931  {
6932  return ZAKERO_XENIUM__ERROR(Error_None);
6933  }
6934 
6935  SizePixel size_pixel = {pixel.first, pixel.second};
6936 
6937  std::error_code error = xenium->windowSizeSet(window_id, size_pixel);
6938 
6939 # if ZAKERO_XENIUM__DEBUG_ENABLED
6940  if(error)
6941  {
6942  ZAKERO_XENIUM__DEBUG_ERROR(error);
6943  }
6944 # endif
6945 
6946  return error;
6947 }
6948 
6949 
6967 std::error_code Xenium::Window::sizeSet(const Xenium::SizePercent& size
6968  ) noexcept
6969 {
6970  std::lock_guard lock(xenium->output_mutex);
6971 
6972  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
6973 
6974  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
6975  window_size.unit = SizeUnit::Percent;
6976 
6977  auto pixel = xenium->convertPercentToPixel(output
6978  , size.width
6979  , size.height
6980  );
6981 
6982  if(pixel.first < Window_Size_Minimum
6983  || pixel.second < Window_Size_Minimum
6984  )
6985  {
6986  return ZAKERO_XENIUM__ERROR(Error_Window_Size_Too_Small);
6987  }
6988 
6989  if(pixel.first == window_size.pixel.width
6990  && pixel.second == window_size.pixel.height
6991  )
6992  {
6993  return ZAKERO_XENIUM__ERROR(Error_None);
6994  }
6995 
6996  SizePixel size_pixel = {pixel.first, pixel.second};
6997 
6998  std::error_code error = xenium->windowSizeSet(window_id, size_pixel);
6999 
7000 # if ZAKERO_XENIUM__DEBUG_ENABLED
7001  if(error)
7002  {
7003  ZAKERO_XENIUM__DEBUG_ERROR(error);
7004  }
7005 # endif
7006 
7007  return error;
7008 }
7009 
7010 
7027 std::error_code Xenium::Window::sizeSet(const Xenium::SizePixel& size
7028  ) noexcept
7029 {
7030  if(size.width < Window_Size_Minimum
7031  || size.height < Window_Size_Minimum
7032  )
7033  {
7034  return ZAKERO_XENIUM__ERROR(Error_Window_Size_Too_Small);
7035  }
7036 
7037  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7038  window_size.unit = Xenium::SizeUnit::Pixel;
7039 
7040  if(window_size.pixel.width == size.width
7041  && window_size.pixel.height == size.height
7042  )
7043  {
7044  return ZAKERO_XENIUM__ERROR(Error_None);
7045  }
7046 
7047  std::error_code error = xenium->windowSizeSet(window_id, size);
7048 
7049 # if ZAKERO_XENIUM__DEBUG_ENABLED
7050  if(error)
7051  {
7052  ZAKERO_XENIUM__DEBUG_ERROR(error);
7053  }
7054 # endif
7055 
7056  return error;
7057 }
7058 
7059 
7071 std::error_code Xenium::Window::sizeSetMinMax(const Xenium::SizeMm& size_min
7072  , const Xenium::SizeMm& size_max
7073  ) noexcept
7074 {
7075  std::error_code error;
7076 
7077  error = validateMinMax<Xenium::SizeMm>(size_min, size_max);
7078  if(error)
7079  {
7080  ZAKERO_XENIUM__DEBUG_ERROR(error);
7081 
7082  return error;
7083  }
7084 
7085  std::lock_guard lock(xenium->output_mutex);
7086 
7087  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7088 
7089  window_size.unit = Xenium::SizeUnit::Millimeter;
7090  window_size.mm_minimum = size_min;
7091  window_size.mm_maximum = size_max;
7092 
7093  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7094 
7095  error = xenium->windowSizeSetMinMax(output, window_id, window_size);
7096 
7097 # if ZAKERO_XENIUM__DEBUG_ENABLED
7098  if(error)
7099  {
7100  ZAKERO_XENIUM__DEBUG_ERROR(error);
7101  }
7102 # endif
7103 
7104  return error;
7105 }
7106 
7107 
7119 std::error_code Xenium::Window::sizeSetMinMax(const Xenium::SizePercent& size_min
7120  , const Xenium::SizePercent& size_max
7121  ) noexcept
7122 {
7123  std::error_code error;
7124 
7125  error = validateMinMax<Xenium::SizePercent>(size_min, size_max);
7126  if(error)
7127  {
7128  ZAKERO_XENIUM__DEBUG_ERROR(error);
7129 
7130  return error;
7131  }
7132 
7133  std::lock_guard lock(xenium->output_mutex);
7134 
7135  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7136 
7137  window_size.unit = Xenium::SizeUnit::Percent;
7138  window_size.percent_minimum = size_min;
7139  window_size.percent_maximum = size_max;
7140 
7141  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7142 
7143  error = xenium->windowSizeSetMinMax(output, window_id, window_size);
7144 
7145 # if ZAKERO_XENIUM__DEBUG_ENABLED
7146  if(error)
7147  {
7148  ZAKERO_XENIUM__DEBUG_ERROR(error);
7149  }
7150 # endif
7151 
7152  return error;
7153 }
7154 
7155 
7167 std::error_code Xenium::Window::sizeSetMinMax(const Xenium::SizePixel& size_min
7168  , const Xenium::SizePixel& size_max
7169  ) noexcept
7170 {
7171  std::error_code error;
7172 
7173  error = validateMinMax<Xenium::SizePixel>(size_min, size_max);
7174  if(error)
7175  {
7176  ZAKERO_XENIUM__DEBUG_ERROR(error);
7177 
7178  return error;
7179  }
7180 
7181  std::lock_guard lock(xenium->output_mutex);
7182 
7183  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7184 
7185  window_size.unit = Xenium::SizeUnit::Pixel;
7186  window_size.pixel_minimum = size_min;
7187  window_size.pixel_maximum = size_max;
7188 
7189  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7190 
7191  error = xenium->windowSizeSetMinMax(output, window_id, window_size);
7192 
7193 # if ZAKERO_XENIUM__DEBUG_ENABLED
7194  if(error)
7195  {
7196  ZAKERO_XENIUM__DEBUG_ERROR(error);
7197  }
7198 # endif
7199 
7200  return error;
7201 }
7202 
7203 
7218  ) noexcept
7219 {
7220  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7221 
7222  if(lambda)
7223  {
7224  window_size.mm_lambda = lambda;
7225  }
7226  else
7227  {
7228  window_size.mm_lambda = LambdaSizeMm_DoNothing;
7229  }
7230 }
7231 
7232 
7247  ) noexcept
7248 {
7249  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7250 
7251  if(lambda)
7252  {
7253  window_size.percent_lambda = lambda;
7254  }
7255  else
7256  {
7257  window_size.percent_lambda = LambdaSizePercent_DoNothing;
7258  }
7259 }
7260 
7261 
7276  ) noexcept
7277 {
7278  Xenium::WindowSizeData& window_size = xenium->window_size_map[window_id];
7279 
7280  if(lambda)
7281  {
7282  window_size.pixel_lambda = lambda;
7283  }
7284  else
7285  {
7286  window_size.pixel_lambda = LambdaSizePixel_DoNothing;
7287  }
7288 }
7289 
7290 // }}}
7291 // {{{ Class Window : Conversion
7292 
7301  ) const noexcept
7302 {
7303  std::lock_guard lock(xenium->output_mutex);
7304 
7305  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7306 
7307  auto value = xenium->convertPixelToMm(output, point.x, point.y);
7308 
7309  return {0, value.first, value.second};
7310 }
7311 
7312 
7321  ) const noexcept
7322 {
7323  std::lock_guard lock(xenium->output_mutex);
7324 
7325  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7326 
7327  auto value = xenium->convertPixelToPercent(output, point.x, point.y);
7328 
7329  return {0, value.first, value.second};
7330 }
7331 
7332 
7341  ) const noexcept
7342 {
7343  std::lock_guard lock(xenium->output_mutex);
7344 
7345  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7346 
7347  auto value = xenium->convertMmToPixel(output, point.x, point.y);
7348 
7349  return {0, value.first, value.second};
7350 }
7351 
7352 
7361  ) const noexcept
7362 {
7363  std::lock_guard lock(xenium->output_mutex);
7364 
7365  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7366 
7367  auto value = xenium->convertPercentToPixel(output, point.x, point.y);
7368 
7369  return {0, value.first, value.second};
7370 
7371 }
7372 
7373 
7382  ) const noexcept
7383 {
7384  std::lock_guard lock(xenium->output_mutex);
7385 
7386  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7387 
7388  auto value = xenium->convertPixelToMm(output, size.width, size.height);
7389 
7390  return {value.first, value.second};
7391 }
7392 
7393 
7402  ) const noexcept
7403 {
7404  std::lock_guard lock(xenium->output_mutex);
7405 
7406  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7407 
7408  auto value = xenium->convertPixelToPercent(output, size.width, size.height);
7409 
7410  return {value.first, value.second};
7411 }
7412 
7413 
7422  ) const noexcept
7423 {
7424  std::lock_guard lock(xenium->output_mutex);
7425 
7426  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7427 
7428  auto value = xenium->convertMmToPixel(output, size.width, size.height);
7429 
7430  return {value.first, value.second};
7431 }
7432 
7433 
7442  ) const noexcept
7443 {
7444  std::lock_guard lock(xenium->output_mutex);
7445 
7446  Output& output = xenium->output_map[xenium->window_output_map[window_id]];
7447 
7448  auto value = xenium->convertPercentToPixel(output, size.width, size.height);
7449 
7450  return {value.first, value.second};
7451 }
7452 
7453 // }}}
7454 // {{{ Class Window : Window Mode
7455 
7464  ) const noexcept
7465 {
7466  return xenium->window_mode_map[window_id].window_mode;
7467 }
7468 
7469 
7479  ) const noexcept
7480 {
7481  return (window_mode == this->windowMode());
7482 }
7483 
7484 
7495 std::error_code Xenium::Window::windowModeSet(const Xenium::WindowMode window_mode
7496  ) noexcept
7497 {
7498  Xenium::WindowModeData& data = xenium->window_mode_map[window_id];
7499 
7500  if(data.window_mode == window_mode)
7501  {
7502  return ZAKERO_XENIUM__ERROR(Error_None);
7503  }
7504 
7505  std::error_code error = xenium->windowModeSet(window_id
7506  , data.window_mode // current window mode
7507  , window_mode // new window mode
7508  );
7509 
7510  if(error)
7511  {
7512  ZAKERO_XENIUM__DEBUG_ERROR(error);
7513 
7514  return error;
7515  }
7516 
7517  data.window_mode = window_mode;
7518 
7519  return ZAKERO_XENIUM__ERROR(Error_None);
7520 }
7521 
7522 
7530  ) noexcept
7531 {
7532  Xenium::WindowModeData& data = xenium->window_mode_map[window_id];
7533 
7534  if(lambda == nullptr)
7535  {
7536  data.lambda = LambdaWindowMode_DoNothing;
7537  }
7538  else
7539  {
7540  data.lambda = lambda;
7541  }
7542 }
7543 
7544 
7554 std::error_code Xenium::Window::minimize() noexcept
7555 {
7556  std::error_code error = xenium->windowMinimize(window_id);
7557 
7558 # if ZAKERO_XENIUM__DEBUG_ENABLED
7559  if(error)
7560  {
7561  ZAKERO_XENIUM__DEBUG_ERROR(error);
7562  }
7563 # endif
7564 
7565  return error;
7566 }
7567 
7568 // }}}
7569 // {{{ Class Window : Cursor
7570 
7571 // }}}
7572 // {{{ Class Window : Keyboard
7573 
7580  ) noexcept
7581 {
7582  if(lambda == nullptr)
7583  {
7584  xenium->window_keyboard[window_id].on_enter = Lambda_DoNothing;
7585  }
7586  else
7587  {
7588  xenium->window_keyboard[window_id].on_enter = lambda;
7589  }
7590 }
7591 
7592 
7599  ) noexcept
7600 {
7601  if(lambda == nullptr)
7602  {
7603  xenium->window_keyboard[window_id].on_leave = Lambda_DoNothing;
7604  }
7605  else
7606  {
7607  xenium->window_keyboard[window_id].on_leave = lambda;
7608  }
7609 }
7610 
7611 
7620  ) noexcept
7621 {
7622  if(lambda == nullptr)
7623  {
7624  xenium->window_on_key_map[window_id] = LambdaKey_DoNothing;
7625  }
7626  else
7627  {
7628  xenium->window_on_key_map[window_id] = lambda;
7629  }
7630 }
7631 
7632 // }}}
7633 // {{{ Class Window : Pointer
7634 
7642  ) noexcept
7643 {
7644  if(lambda == nullptr)
7645  {
7646  xenium->window_on_axis_map[window_id] = LambdaAxis_DoNothing;
7647  }
7648  else
7649  {
7650  xenium->window_on_axis_map[window_id] = lambda;
7651  }
7652 }
7653 
7654 
7663  ) noexcept
7664 {
7665  WindowOnButtonData& on_button = xenium->window_on_button_map[window_id];
7666 
7667  if(lambda == nullptr)
7668  {
7669  on_button.lambda_mm = LambdaButtonMm_DoNothing;
7670  }
7671  else
7672  {
7673  on_button.lambda_mm = lambda;
7674  }
7675 }
7676 
7677 
7686  ) noexcept
7687 {
7688  WindowOnButtonData& on_button = xenium->window_on_button_map[window_id];
7689 
7690  if(lambda == nullptr)
7691  {
7692  on_button.lambda_percent = LambdaButtonPercent_DoNothing;
7693  }
7694  else
7695  {
7696  on_button.lambda_percent = lambda;
7697  }
7698 
7699 }
7700 
7701 
7710  ) noexcept
7711 {
7712  WindowOnButtonData& on_button = xenium->window_on_button_map[window_id];
7713 
7714  if(lambda == nullptr)
7715  {
7716  on_button.lambda_pixel = LambdaButtonPixel_DoNothing;
7717  }
7718  else
7719  {
7720  on_button.lambda_pixel = lambda;
7721  }
7722 }
7723 
7724 
7736  ) noexcept
7737 {
7738  WindowOnEnterData& on_enter = xenium->window_on_enter_map[window_id];
7739 
7740  if(lambda == nullptr)
7741  {
7742  on_enter.lambda_mm = LambdaPointMm_DoNothing;
7743  }
7744  else
7745  {
7746  on_enter.lambda_mm = lambda;
7747  }
7748 }
7749 
7750 
7762  ) noexcept
7763 {
7764  WindowOnEnterData& on_enter = xenium->window_on_enter_map[window_id];
7765 
7766  if(lambda == nullptr)
7767  {
7768  on_enter.lambda_percent = LambdaPointPercent_DoNothing;
7769  }
7770  else
7771  {
7772  on_enter.lambda_percent = lambda;
7773  }
7774 }
7775 
7776 
7788  ) noexcept
7789 {
7790  WindowOnEnterData& on_enter = xenium->window_on_enter_map[window_id];
7791 
7792  if(lambda == nullptr)
7793  {
7794  on_enter.lambda_pixel = LambdaPointPixel_DoNothing;
7795  }
7796  else
7797  {
7798  on_enter.lambda_pixel = lambda;
7799  }
7800 }
7801 
7802 
7811  ) noexcept
7812 {
7813  if(lambda == nullptr)
7814  {
7815  xenium->window_on_leave_map[window_id] = Lambda_DoNothing;
7816  }
7817  else
7818  {
7819  xenium->window_on_leave_map[window_id] = lambda;
7820  }
7821 }
7822 
7823 
7832  ) noexcept
7833 {
7834  WindowOnMotionData& on_motion = xenium->window_on_motion_map[window_id];
7835 
7836  if(lambda == nullptr)
7837  {
7838  on_motion.lambda_mm = LambdaPointMm_DoNothing;
7839  }
7840  else
7841  {
7842  on_motion.lambda_mm = lambda;
7843  }
7844 }
7845 
7846 
7855  ) noexcept
7856 {
7857  WindowOnMotionData& on_motion = xenium->window_on_motion_map[window_id];
7858 
7859  if(lambda == nullptr)
7860  {
7861  on_motion.lambda_percent = LambdaPointPercent_DoNothing;
7862  }
7863  else
7864  {
7865  on_motion.lambda_percent = lambda;
7866  }
7867 }
7868 
7869 
7878  ) noexcept
7879 {
7880  WindowOnMotionData& on_motion = xenium->window_on_motion_map[window_id];
7881 
7882  if(lambda == nullptr)
7883  {
7884  on_motion.lambda_pixel = LambdaPointPixel_DoNothing;
7885  }
7886  else
7887  {
7888  on_motion.lambda_pixel = lambda;
7889  }
7890 }
7891 
7892 
7893 /*
7894  * \brief Respond to "Pointer Axis Source" events.
7895  *
7896  * The provided \p lambda will be called when the "Pointer Axis Source" events
7897  * occur.
7898 void Xenium::Window::pointerOnAxisSource(Xenium::Lambda lambda ///< The lambda
7899  ) noexcept
7900 {
7901  ZAKERO_UNUSED(lambda);
7902 }
7903  */
7904 
7905 
7906 /*
7907  * \brief Respond to "Pointer Axis Stop" events.
7908  *
7909  * The provided \p lambda will be called when the "Pointer Axis Stop" events
7910  * occur.
7911 void Xenium::Window::pointerOnAxisStop(Xenium::Lambda lambda ///< The lambda
7912  ) noexcept
7913 {
7914  ZAKERO_UNUSED(lambda);
7915 }
7916  */
7917 
7918 
7919 /*
7920  * \brief Respond to "Pointer Axis Discrete" events.
7921  *
7922  * The provided \p lambda will be called when the "Pointer Axis Discrete"
7923  * events occur.
7924 void Xenium::Window::pointerOnAxisDiscrete(Xenium::Lambda lambda ///< The lambda
7925  ) noexcept
7926 {
7927  ZAKERO_UNUSED(lambda);
7928 }
7929  */
7930 
7931 // }}}
7932 // {{{ Class Window : Rendering
7933 
7970 std::error_code Xenium::Window::imageNext(uint8_t*& image
7971  , Xenium::SizePixel& size
7972  ) noexcept
7973 {
7974  if(frame_buffer)
7975  {
7976  free(frame_buffer);
7977  }
7978 
7979  frame_buffer_size = xenium->window_size_map[window_id].pixel;
7980  frame_buffer_length = frame_buffer_size.width * frame_buffer_size.height * 4;
7981  frame_buffer = (uint8_t*)malloc(sizeof(uint8_t) * frame_buffer_length);
7982 
7983  image = frame_buffer;
7984  size = frame_buffer_size;
7985 
7986  return ZAKERO_XENIUM__ERROR(Error_None);
7987 }
7988 
7989 
7997 {
7998  /*
7999  Xenium::SizePixel size = xenium->window_size_map[window_id].pixel;
8000  if(size.width != frame_buffer_size.width
8001  || size.height != frame_buffer_size.height
8002  )
8003  {
8004  return;
8005  }
8006  */
8007 
8008  xcb_put_image(xenium->connection
8009  , XCB_IMAGE_FORMAT_Z_PIXMAP
8010  , window_id
8011  , gc
8012  , frame_buffer_size.width
8013  , frame_buffer_size.height
8014  , 0
8015  , 0
8016  , 0
8017  , xenium->screen->root_depth
8018  , frame_buffer_length
8019  , frame_buffer
8020  );
8021 
8022  frame_time = ZAKERO_STEADY_TIME_NOW(milliseconds);
8023 }
8024 
8025 
8037 uint32_t Xenium::Window::time() const noexcept
8038 {
8039  return frame_time;
8040 }
8041 
8042 
8051 uint8_t Xenium::Window::bytesPerPixel() const noexcept
8052 {
8053  return 4; // ARGB8888
8054 }
8055 
8056 // }}}
8057 // {{{ Convenience
8058 
8064 std::string to_string(const xcb_generic_error_t& generic_error
8065  ) noexcept
8066 {
8067  return std::string()
8068  + "{ \"response_type\": " + std::to_string(generic_error.response_type)
8069  + ", \"error_code\": " + std::to_string(generic_error.error_code)
8070  + ", \"sequence\": " + std::to_string(generic_error.sequence)
8071  + ", \"resource_id\": " + std::to_string(generic_error.resource_id)
8072  + ", \"minor_code\": " + std::to_string(generic_error.minor_code)
8073  + ", \"major_code\": " + std::to_string(generic_error.major_code)
8074  + ", \"full_sequence\": " + std::to_string(generic_error.full_sequence)
8075  + " }";
8076 }
8077 
8078 
8084 std::string to_string(const xcb_randr_screen_change_notify_event_t& event
8085  ) noexcept
8086 {
8087  return std::string()
8088  + "{ \"response_type\": " + std::to_string(event.response_type)
8089  + ", \"rotation\": " + std::to_string(event.rotation)
8090  + ", \"sequence\": " + std::to_string(event.sequence)
8091  + ", \"timestamp\": " + std::to_string(event.timestamp)
8092  + ", \"config_timestamp\": " + std::to_string(event.config_timestamp)
8093  + ", \"root\": " + std::to_string(event.root)
8094  + ", \"request_window\": " + std::to_string(event.request_window)
8095  + ", \"sizeID\": " + std::to_string(event.sizeID)
8096  + ", \"subpixel_order\": " + std::to_string(event.subpixel_order)
8097  + ", \"width\": " + std::to_string(event.width)
8098  + ", \"height\": " + std::to_string(event.height)
8099  + ", \"mwidth\": " + std::to_string(event.mwidth)
8100  + ", \"mheight\": " + std::to_string(event.mheight)
8101  + " }";
8102 }
8103 
8109 std::string to_string(const xcb_button_press_event_t& event
8110  ) noexcept
8111 {
8112  return std::string()
8113  + "{ \"response_type\": " + std::to_string(event.response_type)
8114  + ", \"detail\": " + std::to_string(event.detail)
8115  + ", \"sequence\": " + std::to_string(event.sequence)
8116  + ", \"time\": " + std::to_string(event.time)
8117  + ", \"root\": " + std::to_string(event.root)
8118  + ", \"event\": " + std::to_string(event.event)
8119  + ", \"child\": " + std::to_string(event.child)
8120  + ", \"root_x\": " + std::to_string(event.root_x)
8121  + ", \"root_y\": " + std::to_string(event.root_y)
8122  + ", \"event_x\": " + std::to_string(event.event_x)
8123  + ", \"event_y\": " + std::to_string(event.event_y)
8124  + ", \"state\": " + std::to_string(event.state)
8125  + ", \"same_screen\": " + std::to_string(event.same_screen)
8126  + ", \"pad0\": " + std::to_string(event.pad0)
8127  + " }";
8128 }
8129 
8130 
8136 std::string to_string(const xcb_client_message_event_t& event
8137  ) noexcept
8138 {
8139  return std::string()
8140  + "{ \"response_type\": " + std::to_string(event.response_type)
8141  + ", \"format\": " + std::to_string(event.format)
8142  + ", \"sequence\": " + std::to_string(event.sequence)
8143  + ", \"window\": " + std::to_string(event.window)
8144  + ", \"type\": " + std::to_string(event.type)
8145  + ", \"data\": [ 0x" + std::to_string(event.data.data8[ 0])
8146  + ", 0x" + std::to_string(event.data.data8[ 1])
8147  + ", 0x" + std::to_string(event.data.data8[ 2])
8148  + ", 0x" + std::to_string(event.data.data8[ 3])
8149  + ", 0x" + std::to_string(event.data.data8[ 4])
8150  + ", 0x" + std::to_string(event.data.data8[ 5])
8151  + ", 0x" + std::to_string(event.data.data8[ 6])
8152  + ", 0x" + std::to_string(event.data.data8[ 7])
8153  + ", 0x" + std::to_string(event.data.data8[ 8])
8154  + ", 0x" + std::to_string(event.data.data8[ 9])
8155  + ", 0x" + std::to_string(event.data.data8[10])
8156  + ", 0x" + std::to_string(event.data.data8[11])
8157  + ", 0x" + std::to_string(event.data.data8[12])
8158  + ", 0x" + std::to_string(event.data.data8[13])
8159  + ", 0x" + std::to_string(event.data.data8[14])
8160  + ", 0x" + std::to_string(event.data.data8[15])
8161  + ", 0x" + std::to_string(event.data.data8[16])
8162  + ", 0x" + std::to_string(event.data.data8[17])
8163  + ", 0x" + std::to_string(event.data.data8[18])
8164  + ", 0x" + std::to_string(event.data.data8[19])
8165  + " ]"
8166  + " }";
8167 }
8168 
8169 
8175 std::string to_string(const xcb_configure_notify_event_t& event
8176  ) noexcept
8177 {
8178  return std::string()
8179  + "{ \"response_type\": " + std::to_string(event.response_type)
8180  + ", \"pad0\": " + std::to_string(event.pad0)
8181  + ", \"sequence\": " + std::to_string(event.sequence)
8182  + ", \"event\": " + std::to_string(event.event)
8183  + ", \"window\": " + std::to_string(event.window)
8184  + ", \"above_sibling\": " + std::to_string(event.above_sibling)
8185  + ", \"x\": " + std::to_string(event.x)
8186  + ", \"y\": " + std::to_string(event.y)
8187  + ", \"width\": " + std::to_string(event.width)
8188  + ", \"height\": " + std::to_string(event.height)
8189  + ", \"border_width\": " + std::to_string(event.border_width)
8190  + ", \"override_redirect\": " + std::to_string(event.override_redirect)
8191  + ", \"pad1\": " + std::to_string(event.pad1)
8192  + " }";
8193 }
8194 
8195 
8201 std::string to_string(const xcb_enter_notify_event_t& event
8202  ) noexcept
8203 {
8204  return std::string()
8205  + "{ \"response_type\": " + std::to_string(event.response_type)
8206  + ", \"detail\": " + std::to_string(event.detail)
8207  + ", \"sequence\": " + std::to_string(event.sequence)
8208  + ", \"time\": " + std::to_string(event.time)
8209  + ", \"root\": " + std::to_string(event.root)
8210  + ", \"event\": " + std::to_string(event.event)
8211  + ", \"root_x\": " + std::to_string(event.root_x)
8212  + ", \"root_y\": " + std::to_string(event.root_y)
8213  + ", \"event_x\": " + std::to_string(event.event_x)
8214  + ", \"event_y\": " + std::to_string(event.event_y)
8215  + ", \"state\": " + std::to_string(event.state)
8216  + ", \"mode\": " + std::to_string(event.mode)
8217  + ", \"same_screen_focus\": " + std::to_string(event.same_screen_focus)
8218  + " }";
8219 }
8220 
8221 
8227 std::string to_string(const xcb_expose_event_t& event
8228  ) noexcept
8229 {
8230  return std::string()
8231  + "{ \"response_type\": " + std::to_string(event.response_type)
8232  + ", \"pad0\": " + std::to_string(event.pad0)
8233  + ", \"sequence\": " + std::to_string(event.sequence)
8234  + ", \"window\": " + std::to_string(event.window)
8235  + ", \"x\": " + std::to_string(event.x)
8236  + ", \"y\": " + std::to_string(event.y)
8237  + ", \"width\": " + std::to_string(event.width)
8238  + ", \"height\": " + std::to_string(event.height)
8239  + ", \"count\": " + std::to_string(event.count)
8240  + ", \"pad1\": [ 0x" + std::to_string(event.pad1[0])
8241  + ", 0x" + std::to_string(event.pad1[1])
8242  + " ]"
8243  + " }";
8244 }
8245 
8246 
8252 std::string to_string(const xcb_focus_in_event_t& event
8253  ) noexcept
8254 {
8255  return std::string()
8256  + "{ \"response_type\": " + std::to_string(event.response_type)
8257  + ", \"detail\": " + std::to_string(event.detail)
8258  + ", \"sequence\": " + std::to_string(event.sequence)
8259  + ", \"event\": " + std::to_string(event.event)
8260  + ", \"mode\": " + std::to_string(event.mode)
8261  + ", \"pad0\": [ 0x" + std::to_string(event.pad0[0])
8262  + ", 0x" + std::to_string(event.pad0[1])
8263  + ", 0x" + std::to_string(event.pad0[2])
8264  + " ]"
8265  + " }";
8266 }
8267 
8268 
8274 std::string to_string(const xcb_generic_event_t& event
8275  ) noexcept
8276 {
8277  return std::string()
8278  + "{ \"response_type\": " + std::to_string(event.response_type)
8279  + ", \"pad0\": " + std::to_string(event.pad0)
8280  + ", \"sequence\": " + std::to_string(event.sequence)
8281  + ", \"pad\": [ 0x" + std::to_string(event.pad[0])
8282  + ", 0x" + std::to_string(event.pad[1])
8283  + ", 0x" + std::to_string(event.pad[2])
8284  + ", 0x" + std::to_string(event.pad[3])
8285  + ", 0x" + std::to_string(event.pad[4])
8286  + ", 0x" + std::to_string(event.pad[5])
8287  + ", 0x" + std::to_string(event.pad[6])
8288  + " ]"
8289  + ", \"full_sequence\": " + std::to_string(event.full_sequence)
8290  + " }";
8291 }
8292 
8293 
8299 std::string to_string(const xcb_gravity_notify_event_t& event
8300  ) noexcept
8301 {
8302  return std::string()
8303  + "{ \"response_type\": " + std::to_string(event.response_type)
8304  + ", \"pad0\": " + std::to_string(event.pad0)
8305  + ", \"sequence\": " + std::to_string(event.sequence)
8306  + ", \"event\": " + std::to_string(event.event)
8307  + ", \"window\": " + std::to_string(event.window)
8308  + ", \"x\": " + std::to_string(event.x)
8309  + ", \"y\": " + std::to_string(event.y)
8310  + " }";
8311 }
8312 
8313 
8319 std::string to_string(const xcb_key_press_event_t& event
8320  ) noexcept
8321 {
8322  return std::string()
8323  + "{ \"response_type\": " + std::to_string(event.response_type)
8324  + ", \"detail\": " + std::to_string(event.detail)
8325  + ", \"sequence\": " + std::to_string(event.sequence)
8326  + ", \"time\": " + std::to_string(event.time)
8327  + ", \"root\": " + std::to_string(event.root)
8328  + ", \"event\": " + std::to_string(event.event)
8329  + ", \"child\": " + std::to_string(event.child)
8330  + ", \"root_x\": " + std::to_string(event.root_x)
8331  + ", \"root_y\": " + std::to_string(event.root_y)
8332  + ", \"event_x\": " + std::to_string(event.event_x)
8333  + ", \"event_y\": " + std::to_string(event.event_y)
8334  + ", \"state\": " + std::to_string(event.state)
8335  + ", \"same_screen\": " + std::to_string(event.same_screen)
8336  + ", \"pad0\": " + std::to_string(event.pad0)
8337  + " }";
8338 }
8339 
8340 
8346 std::string to_string(const xcb_map_notify_event_t& event
8347  ) noexcept
8348 {
8349  return std::string()
8350  + "{ \"response_type\": " + std::to_string(event.response_type)
8351  + ", \"pad0\": " + std::to_string(event.pad0)
8352  + ", \"sequence\": " + std::to_string(event.sequence)
8353  + ", \"event\": " + std::to_string(event.event)
8354  + ", \"window\": " + std::to_string(event.window)
8355  + ", \"override_redirect\": " + std::to_string(event.override_redirect)
8356  + ", \"pad1\": [ 0x" + std::to_string(event.pad1[0])
8357  + ", 0x" + std::to_string(event.pad1[1])
8358  + ", 0x" + std::to_string(event.pad1[2])
8359  + " ]"
8360  + " }";
8361 }
8362 
8363 
8369 std::string to_string(const xcb_motion_notify_event_t& event
8370  ) noexcept
8371 {
8372  return std::string()
8373  + "{ \"response_type\": " + std::to_string(event.response_type)
8374  + ", \"detail\": " + std::to_string(event.detail)
8375  + ", \"sequence\": " + std::to_string(event.sequence)
8376  + ", \"time\": " + std::to_string(event.time)
8377  + ", \"root\": " + std::to_string(event.root)
8378  + ", \"event\": " + std::to_string(event.event)
8379  + ", \"child\": " + std::to_string(event.child)
8380  + ", \"root_x\": " + std::to_string(event.root_x)
8381  + ", \"root_y\": " + std::to_string(event.root_y)
8382  + ", \"event_x\": " + std::to_string(event.event_x)
8383  + ", \"event_y\": " + std::to_string(event.event_y)
8384  + ", \"state\": " + std::to_string(event.state)
8385  + ", \"same_screen\": " + std::to_string(event.same_screen)
8386  + ", \"pad0\": " + std::to_string(event.pad0)
8387  + " }";
8388 }
8389 
8390 
8396 std::string to_string(const xcb_property_notify_event_t& event
8397  ) noexcept
8398 {
8399  return std::string()
8400  + "{ \"response_type\": " + std::to_string(event.response_type)
8401  + ", \"pad0\": " + std::to_string(event.pad0)
8402  + ", \"sequence\": " + std::to_string(event.sequence)
8403  + ", \"window\": " + std::to_string(event.window)
8404  + ", \"atom\": " + std::to_string(event.atom)
8405  + ", \"time\": " + std::to_string(event.time)
8406  + ", \"state\": " + std::to_string(event.state)
8407  + ", \"pad1\": [ 0x" + std::to_string(event.pad1[0])
8408  + ", 0x" + std::to_string(event.pad1[1])
8409  + ", 0x" + std::to_string(event.pad1[2])
8410  + " ]"
8411  + " }";
8412 }
8413 
8414 
8420 std::string to_string(const xcb_reparent_notify_event_t& event
8421  ) noexcept
8422 {
8423  return std::string()
8424  + "{ \"response_type\": " + std::to_string(event.response_type)
8425  + ", \"pad0\": " + std::to_string(event.pad0)
8426  + ", \"sequence\": " + std::to_string(event.sequence)
8427  + ", \"event\": " + std::to_string(event.event)
8428  + ", \"window\": " + std::to_string(event.window)
8429  + ", \"parent\": " + std::to_string(event.parent)
8430  + ", \"x\": " + std::to_string(event.x)
8431  + ", \"y\": " + std::to_string(event.y)
8432  + ", \"override_redirect\": " + std::to_string(event.override_redirect)
8433  + ", \"pad1\": [ 0x" + std::to_string(event.pad1[0])
8434  + ", 0x" + std::to_string(event.pad1[1])
8435  + ", 0x" + std::to_string(event.pad1[2])
8436  + " ]"
8437  + " }";
8438 }
8439 
8440 
8446 std::string to_string(const xcb_unmap_notify_event_t& event
8447  ) noexcept
8448 {
8449  return std::string()
8450  + "{ \"response_type\": " + std::to_string(event.response_type)
8451  + ", \"pad0\": " + std::to_string(event.pad0)
8452  + ", \"sequence\": " + std::to_string(event.sequence)
8453  + ", \"event\": " + std::to_string(event.event)
8454  + ", \"window\": " + std::to_string(event.window)
8455  + ", \"from_configure\": " + std::to_string(event.from_configure)
8456  + ", \"pad1\": [ 0x" + std::to_string(event.pad1[0])
8457  + ", 0x" + std::to_string(event.pad1[1])
8458  + ", 0x" + std::to_string(event.pad1[2])
8459  + " ]"
8460  + " }";
8461 }
8462 
8463 
8469 std::string to_string(const xcb_format_t& format
8470  ) noexcept
8471 {
8472  return std::string()
8473  + "{ \"depth\": " + std::to_string(format.depth)
8474  + ", \"bits_per_pixel\": " + std::to_string(format.bits_per_pixel)
8475  + ", \"scanline_pad\": " + std::to_string(format.scanline_pad)
8476  + ", \"pad0\": [ 0x" + std::to_string(format.pad0[0])
8477  + ", 0x" + std::to_string(format.pad0[1])
8478  + ", 0x" + std::to_string(format.pad0[2])
8479  + ", 0x" + std::to_string(format.pad0[3])
8480  + ", 0x" + std::to_string(format.pad0[4])
8481  + " ]"
8482  + " }";
8483 }
8484 
8485 
8491 std::string to_string(const xcb_screen_t& screen
8492  ) noexcept
8493 {
8494  return std::string()
8495  + "{ \"root\": " + std::to_string(screen.root)
8496  + ", \"default_colormap\": " + std::to_string(screen.default_colormap)
8497  + ", \"white_pixel\": " + std::to_string(screen.white_pixel)
8498  + ", \"black_pixel\": " + std::to_string(screen.black_pixel)
8499  + ", \"current_input_masks\": " + std::to_string(screen.current_input_masks)
8500  + ", \"width_in_pixels\": " + std::to_string(screen.width_in_pixels)
8501  + ", \"height_in_pixels\": " + std::to_string(screen.height_in_pixels)
8502  + ", \"width_in_millimeters\": " + std::to_string(screen.width_in_millimeters)
8503  + ", \"height_in_millimeters\": " + std::to_string(screen.height_in_millimeters)
8504  + ", \"min_installed_maps\": " + std::to_string(screen.min_installed_maps)
8505  + ", \"max_installed_maps\": " + std::to_string(screen.max_installed_maps)
8506  + ", \"root_visual\": " + std::to_string(screen.root_visual)
8507  + ", \"backing_stores\": " + std::to_string(screen.backing_stores)
8508  + ", \"save_unders\": " + std::to_string(screen.save_unders)
8509  + ", \"root_depth\": " + std::to_string(screen.root_depth)
8510  + ", \"allowed_depths_len\": " + std::to_string(screen.allowed_depths_len)
8511  + " }";
8512 }
8513 
8514 
8520 std::string to_string(const std::vector<xcb_atom_t>& vector
8521  ) noexcept
8522 {
8523  std::string string = "[ ";
8524  std::string delim = "";
8525 
8526  for(const auto& atom : vector)
8527  {
8528  string += delim + ' ' + std::to_string(atom);
8529 
8530  delim = ",";
8531  }
8532 
8533  string += " ]";
8534 
8535  return string;
8536 }
8537 
8538 
8544 std::string to_string(const std::vector<int32_t>& vector
8545  ) noexcept
8546 {
8547  std::string string = "[ ";
8548  std::string delim = "";
8549 
8550  for(const int32_t& value : vector)
8551  {
8552  string += delim + ' ' + std::to_string(value);
8553 
8554  delim = ",";
8555  }
8556 
8557  string += " ]";
8558 
8559  return string;
8560 }
8561 
8562 
8568 std::string to_string(const xcb_setup_t& setup
8569  ) noexcept
8570 {
8571  return std::string()
8572  + "{ \"status\": " + std::to_string(setup.status)
8573  + ", \"pad0\": " + std::to_string(setup.pad0)
8574  + ", \"protocol_major_version\": " + std::to_string(setup.protocol_major_version)
8575  + ", \"protocol_minor_version\": " + std::to_string(setup.protocol_minor_version)
8576  + ", \"length\": " + std::to_string(setup.length)
8577  + ", \"release_number\": " + std::to_string(setup.release_number)
8578  + ", \"resource_id_base\": " + std::to_string(setup.resource_id_base)
8579  + ", \"resource_id_mask\": " + std::to_string(setup.resource_id_mask)
8580  + ", \"motion_buffer_size\": " + std::to_string(setup.motion_buffer_size)
8581  + ", \"vendor_len\": " + std::to_string(setup.vendor_len)
8582  + ", \"maximum_request_length\": " + std::to_string(setup.maximum_request_length)
8583  + ", \"roots_len\": " + std::to_string(setup.roots_len)
8584  + ", \"pixmap_formats_len\": " + std::to_string(setup.pixmap_formats_len)
8585  + ", \"image_byte_order\": " + std::to_string(setup.image_byte_order)
8586  + ", \"bitmap_format_bit_order\": " + std::to_string(setup.bitmap_format_bit_order)
8587  + ", \"bitmap_format_scanline_unit\": " + std::to_string(setup.bitmap_format_scanline_unit)
8588  + ", \"bitmap_format_scanline_pad\": " + std::to_string(setup.bitmap_format_scanline_pad)
8589  + ", \"min_keycode\": " + std::to_string(setup.min_keycode)
8590  + ", \"max_keycode\": " + std::to_string(setup.max_keycode)
8591  + ", \"pad1\": [ 0x" + std::to_string(setup.pad1[0])
8592  + ", 0x" + std::to_string(setup.pad1[1])
8593  + ", 0x" + std::to_string(setup.pad1[2])
8594  + ", 0x" + std::to_string(setup.pad1[3])
8595  + " ]"
8596  + " }";
8597 }
8598 
8599 
8607 std::string to_string(const Xenium::Key& key
8608  ) noexcept
8609 {
8610  return std::string()
8611  + "{ \"time\": " + std::to_string(key.time)
8612  + ", \"code\": " + std::to_string(key.code)
8613  + ", \"state\": \"" + zakero::to_string(key.state) + "\""
8614  + " }"
8615  ;
8616 }
8617 
8618 
8627 std::string to_string(const Xenium::KeyModifier& key_modifier
8628  ) noexcept
8629 {
8630  auto mod_to_str = [](std::string& s, uint32_t m)
8631  {
8632  s += "[";
8633  std::string delim = "";
8634 
8636  {
8637  s += delim + "\"Shift\"";
8638  delim = ",";
8639  }
8640 
8642  {
8643  s += delim + "\"CapsLock\"";
8644  delim = ",";
8645  }
8646 
8648  {
8649  s += delim + "\"Control\"";
8650  delim = ",";
8651  }
8652 
8653  if(m & Xenium::KeyModifier_Alt)
8654  {
8655  s += delim + "\"Alt\"";
8656  delim = ",";
8657  }
8658 
8659  if(m & Xenium::KeyModifier_Meta)
8660  {
8661  s += delim + "\"Meta\"";
8662  }
8663 
8665  {
8666  s += delim + "\"NumLock\"";
8667  }
8668 
8669  s += "]";
8670  };
8671 
8672  std::string str = "{ \"pressed\": ";
8673  mod_to_str(str, key_modifier.pressed);
8674 
8675  str += ", \"latched\": ";
8676  mod_to_str(str, key_modifier.latched);
8677 
8678  str += ", \"locked\": ";
8679  mod_to_str(str, key_modifier.locked);
8680 
8681  str += " }";
8682 
8683  return str;
8684 }
8685 
8686 
8694 std::string to_string(const Xenium::KeyState key_state
8695  ) noexcept
8696 {
8697  switch(key_state)
8698  {
8699  case Xenium::KeyState::Pressed: return "Pressed";
8700  case Xenium::KeyState::Released: return "Released";
8701  case Xenium::KeyState::Repeat: return "Repeat";
8702  default: return "";
8703  }
8704 }
8705 
8706 
8714 std::string to_string(const Xenium::Output& output
8715  ) noexcept
8716 {
8717  return std::string()
8718  + "{ \"name\": \"" + output.name + "\""
8719  + ", \"x\": " + std::to_string(output.x)
8720  + ", \"y\": " + std::to_string(output.y)
8721  + ", \"width\": " + std::to_string(output.width)
8722  + ", \"height\": " + std::to_string(output.height)
8723  + ", \"physical_width_mm\": " + std::to_string(output.physical_width_mm)
8724  + ", \"physical_height_mm:\" " + std::to_string(output.physical_height_mm)
8725  + ", \"subpixel\": " + std::to_string(output.subpixel)
8726  + ", \"transform\": " + zakero::Xenium::outputTransformName(output.transform)
8727  + ", \"pixels_per_mm_horizontal\": " + std::to_string(output.pixels_per_mm_horizontal)
8728  + ", \"pixels_per_mm_vertical\": " + std::to_string(output.pixels_per_mm_vertical)
8729  //+ ", \"make\": \"" + output.make + "\""
8730  //+ ", \"model\": \"" + output.model + "\""
8731  //+ ", \"refresh_mHz\": " + std::to_string(output.refresh_mHz)
8732  //+ ", \"scale_factor\": " + std::to_string(output.scale_factor)
8733  + " }";
8734 }
8735 
8736 
8744 std::string to_string(const Xenium::PointMm point
8745  ) noexcept
8746 {
8747  return std::string()
8748  + "{ \"time\": " + std::to_string(point.time)
8749  + ", \"x\": " + std::to_string(point.x)
8750  + ", \"y\": " + std::to_string(point.y)
8751  + " }"
8752  ;
8753 }
8754 
8755 
8763 std::string to_string(const Xenium::PointPercent point
8764  ) noexcept
8765 {
8766  return std::string()
8767  + "{ \"time\": " + std::to_string(point.time)
8768  + ", \"x\": " + std::to_string(point.x)
8769  + ", \"y\": " + std::to_string(point.y)
8770  + " }"
8771  ;
8772 }
8773 
8774 
8782 std::string to_string(const Xenium::PointPixel point
8783  ) noexcept
8784 {
8785  return std::string()
8786  + "{ \"time\": " + std::to_string(point.time)
8787  + ", \"x\": " + std::to_string(point.x)
8788  + ", \"y\": " + std::to_string(point.y)
8789  + " }"
8790  ;
8791 }
8792 
8793 
8801 std::string to_string(const Xenium::PointerAxis& pointer_axis
8802  ) noexcept
8803 {
8804  return std::string()
8805  + "{ \"time\": " + std::to_string(pointer_axis.time)
8806  + ", \"steps\": " + std::to_string(pointer_axis.steps)
8807  + ", \"distance\": " + std::to_string(pointer_axis.distance)
8808  + ", \"source\": \"" + zakero::to_string(pointer_axis.source) + "\""
8809  + ", \"type\": \"" + zakero::to_string(pointer_axis.type) + "\""
8810  + " }"
8811  ;
8812 }
8813 
8814 
8822 std::string to_string(const Xenium::PointerAxisSource source
8823  ) noexcept
8824 {
8825  switch(source)
8826  {
8827  case Xenium::PointerAxisSource::Continuous: return "Continuous";
8828  case Xenium::PointerAxisSource::Finger: return "Finger";
8829  case Xenium::PointerAxisSource::Wheel: return "Wheel";
8830  case Xenium::PointerAxisSource::Wheel_Tilt: return "Wheel Tilt";
8831  case Xenium::PointerAxisSource::Unknown: [[fallthrough]];
8832  default: return "";
8833  }
8834 }
8835 
8836 
8844 std::string to_string(const Xenium::PointerAxisType type
8845  ) noexcept
8846 {
8847  switch(type)
8848  {
8849  case Xenium::PointerAxisType::Horizontal: return "Horizontal";
8850  case Xenium::PointerAxisType::Vertical: return "Vertical";
8851  case Xenium::PointerAxisType::Unknown: [[fallthrough]];
8852  default: return "";
8853  }
8854 }
8855 
8856 
8864 std::string to_string(const Xenium::PointerButton& button
8865  ) noexcept
8866 {
8867  std::string str = std::string()
8868  + "{ \"code\": " + std::to_string(button.code)
8869  + ", \"state\": " + zakero::to_string(button.state)
8870  + " }"
8871  ;
8872 
8873  return str;
8874 }
8875 
8876 
8884 std::string to_string(const Xenium::PointerButtonState& button_state
8885  ) noexcept
8886 {
8887  switch(button_state)
8888  {
8889  case Xenium::PointerButtonState::Pressed: return "Pressed";
8890  case Xenium::PointerButtonState::Released: return "Released";
8891  default: return "";
8892  }
8893 }
8894 
8895 
8903 std::string to_string(const Xenium::SizeMm& size
8904  ) noexcept
8905 {
8906  return std::string()
8907  + "{ \"width\": " + std::to_string(size.width)
8908  + ", \"height\": " + std::to_string(size.height)
8909  + " }";
8910 }
8911 
8912 
8920 std::string to_string(const Xenium::SizePercent& size
8921  ) noexcept
8922 {
8923  return std::string()
8924  + "{ \"width\": " + std::to_string(size.width)
8925  + ", \"height\": " + std::to_string(size.height)
8926  + " }";
8927 }
8928 
8929 
8937 std::string to_string(const Xenium::SizePixel& size
8938  ) noexcept
8939 {
8940  return std::string()
8941  + "{ \"width\": " + std::to_string(size.width)
8942  + ", \"height\": " + std::to_string(size.height)
8943  + " }";
8944 }
8945 
8946 
8954 std::string to_string(const Xenium::WindowDecorations window_decorations
8955  ) noexcept
8956 {
8957  switch(window_decorations)
8958  {
8959  case Xenium::WindowDecorations::Client_Side: return "Client Side";
8960  case Xenium::WindowDecorations::Server_Side: return "Server Side";
8961  default: return "";
8962  }
8963 }
8964 
8965 
8973 std::string to_string(const Xenium::WindowMode window_mode
8974  ) noexcept
8975 {
8976  switch(window_mode)
8977  {
8978  case Xenium::WindowMode::Fullscreen: return "Fullscreen";
8979  case Xenium::WindowMode::Maximized: return "Maximized";
8980  case Xenium::WindowMode::Normal: return "Normal";
8981  default: return "";
8982  }
8983 }
8984 
8985 
9000  , Xenium::PointMm& rhs
9001  ) noexcept
9002 {
9003  return zakero::equalish(lhs.x, rhs.x, 0.001)
9004  && zakero::equalish(lhs.y, rhs.y, 0.001)
9005  ;
9006 }
9007 
9008 
9023  , Xenium::PointPercent& rhs
9024  ) noexcept
9025 {
9026  return zakero::equalish(lhs.x, rhs.x, 0.00001)
9027  && zakero::equalish(lhs.y, rhs.y, 0.00001)
9028  ;
9029 }
9030 
9031 
9043  , Xenium::PointPixel& rhs
9044  ) noexcept
9045 {
9046  return (lhs.x == rhs.x) && (lhs.y == rhs.y);
9047 }
9048 
9049 
9062  , Xenium::SizeMm& rhs
9063  ) noexcept
9064 {
9065  return zakero::equalish(lhs.width, rhs.width, 0.001)
9066  && zakero::equalish(lhs.height, rhs.height, 0.001)
9067  ;
9068 }
9069 
9070 
9083  , Xenium::SizePercent& rhs
9084  ) noexcept
9085 {
9086  return zakero::equalish(lhs.width, rhs.width, 0.00001)
9087  && zakero::equalish(lhs.height, rhs.height, 0.00001)
9088  ;
9089 }
9090 
9091 
9101  , Xenium::SizePixel& rhs
9102  ) noexcept
9103 {
9104  return (lhs.width == rhs.width) && (lhs.height == rhs.height);
9105 }
9106 
9107 // }}}
9108 
9109 }
9110 
9111 #endif // ZAKERO_XENIUM_IMPLEMENTATION
9112 
9113 // }}}
9114 
9115 #endif // zakero_Xenium_h
Zakero Base.
std::string to_string(const bool value) noexcept
Convert a bool into a string.
Definition: Zakero_Base.h:561
#define ZAKERO_STEADY_TIME_NOW(unit_)
Get the current time.
Definition: Zakero_Base.h:231
#define ZAKERO_FREE(ptr_)
Free memory.
Definition: Zakero_Base.h:144
bool equalish(const float a, const float b, const float delta) noexcept
Compare two floats.
Definition: Zakero_Base.h:375
bool operator==(const zakero::messagepack::Object &lhs, const zakero::messagepack::Object &rhs) noexcept
Compare two Objects for equality.
Definition: Zakero_MessagePack.h:8964
#define _NET_WM_STATE_REMOVE
A NET-WM Macro (that may not be defined).
Definition: Zakero_Xenium.h:1072
#define _NET_WM_STATE_ADD
A NET-WM Macro (that may not be defined).
Definition: Zakero_Xenium.h:1079
A Window.
Definition: Zakero_Xenium.h:507
void keyboardOnKey(Xenium::LambdaKey) noexcept
Respond to "Keyboard Key" events.
Definition: Zakero_Xenium.h:7619
void decorationsOnChange(Xenium::LambdaWindowDecorations) noexcept
Respond to "Decoration Change" events.
Definition: Zakero_Xenium.h:6871
void pointerOnEnter(Xenium::LambdaPointMm) noexcept
Respond to "Pointer Enter" events.
Definition: Zakero_Xenium.h:7735
void titleSet(const std::string &) noexcept
Change the window title.
Definition: Zakero_Xenium.h:6745
void pointerOnAxis(Xenium::LambdaAxis) noexcept
Respond to "Pointer Axis" events.
Definition: Zakero_Xenium.h:7641
std::error_code sizeSetMinMax(const Xenium::SizeMm &, const Xenium::SizeMm &) noexcept
Set the minimum window size.
Definition: Zakero_Xenium.h:7071
void sizeOnChange(Xenium::LambdaSizeMm) noexcept
Respond to "Resize" events.
Definition: Zakero_Xenium.h:7217
std::error_code sizeSet(const Xenium::SizeMm &) noexcept
Set the window size.
Definition: Zakero_Xenium.h:6906
void onCloseRequest(Xenium::Lambda) noexcept
Respond to "Close Request" events.
Definition: Zakero_Xenium.h:6777
virtual ~Window()
Destroy a Window.
Definition: Zakero_Xenium.h:6681
Xenium::PointPercent convertToPercent(const Xenium::PointPixel &) const noexcept
Unit conversion.
Definition: Zakero_Xenium.h:7320
void pointerOnButton(Xenium::LambdaButtonMm) noexcept
Respond to "Pointer Button" events.
Definition: Zakero_Xenium.h:7662
uint8_t bytesPerPixel() const noexcept
Get the number of bytes per pixel.
Definition: Zakero_Xenium.h:8051
void keyboardOnLeave(Xenium::Lambda) noexcept
Respond to "Keyboard Leave" events.
Definition: Zakero_Xenium.h:7598
bool windowModeIs(const Xenium::WindowMode) const noexcept
Check the WindowMode.
Definition: Zakero_Xenium.h:7478
std::error_code minimize() noexcept
Minimize the window.
Definition: Zakero_Xenium.h:7554
std::error_code decorationsSet(const Xenium::WindowDecorations) noexcept
Use the Desktop Environment borders.
Definition: Zakero_Xenium.h:6837
Window(Xenium *, void *)
Construct a Window.
Definition: Zakero_Xenium.h:6664
Xenium::PointPixel convertToPixel(const Xenium::PointMm &) const noexcept
Unit conversion.
Definition: Zakero_Xenium.h:7340
void pointerOnMotion(Xenium::LambdaPointMm) noexcept
Respond to "Pointer Motion" events.
Definition: Zakero_Xenium.h:7831
uint32_t time() const noexcept
When the last frame was rendered.
Definition: Zakero_Xenium.h:8037
void keyboardOnEnter(Xenium::Lambda) noexcept
Respond to "Keyboard Enter" events.
Definition: Zakero_Xenium.h:7579
void imagePresent() noexcept
Render the image.
Definition: Zakero_Xenium.h:7996
Xenium::WindowMode windowMode() const noexcept
Get the current WindowMode.
Definition: Zakero_Xenium.h:7463
std::error_code imageNext(uint8_t *&, Xenium::SizePixel &) noexcept
Get an image buffer.
Definition: Zakero_Xenium.h:7970
Xenium::PointMm convertToMm(const Xenium::PointPixel &) const noexcept
Unit conversion.
Definition: Zakero_Xenium.h:7300
std::error_code windowModeSet(const Xenium::WindowMode) noexcept
Change the window mode.
Definition: Zakero_Xenium.h:7495
void pointerOnLeave(Xenium::Lambda) noexcept
Respond to "Pointer Leave" events.
Definition: Zakero_Xenium.h:7810
void onFocusChange(Xenium::LambdaBool) noexcept
Respond to "Active" change events.
Definition: Zakero_Xenium.h:6806
void classSet(const std::string &) noexcept
Change the window class.
Definition: Zakero_Xenium.h:6720
void windowModeOnChange(Xenium::LambdaWindowMode) noexcept
Respond to "Window Mode" events.
Definition: Zakero_Xenium.h:7529
A wrapper class for X11/XCB.
Definition: Zakero_Xenium.h:264
static constexpr uint32_t KeyModifier_Shift
Key Modifier flag.
Definition: Zakero_Xenium.h:290
uint32_t locked
A collection of locked modifiers.
Definition: Zakero_Xenium.h:301
uint32_t physical_height_mm
The height of the device in millimeters.
Definition: Zakero_Xenium.h:430
Xenium::Output output(const Xenium::OutputId) const noexcept
Get a copy of the Output information.
Definition: Zakero_Xenium.h:2904
uint32_t latched
A collection of latched modifiers.
Definition: Zakero_Xenium.h:300
int32_t x
The X position within the global compositor.
Definition: Zakero_Xenium.h:425
int32_t steps
The number of rotation steps.
Definition: Zakero_Xenium.h:355
int32_t keyRepeatRate() const noexcept
The key repeat rate.
Definition: Zakero_Xenium.h:2884
void outputOnRemove(Xenium::LambdaOutputId) noexcept
Notification of removing an Output device.
Definition: Zakero_Xenium.h:3257
Xenium::PointMm outputConvertToMm(const Xenium::OutputId, const Xenium::PointPixel &) const noexcept
Convert Pixel to Millimeter.
Definition: Zakero_Xenium.h:3002
std::function< void(Xenium::WindowMode)> LambdaWindowMode
A Lambda that has a parameter: WindowMode.
Definition: Zakero_Xenium.h:500
static std::string outputSubpixelName(int32_t) noexcept
Get a human readable string.
Definition: Zakero_Xenium.h:2957
Xenium::PointerAxisType type
The type of Axis.
Definition: Zakero_Xenium.h:358
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:489
int32_t y
The Y position within the global compositor.
Definition: Zakero_Xenium.h:426
std::function< void(const Xenium::SizePercent &)> LambdaSizePercent
A Lambda that has a parameter: SizePercent.
Definition: Zakero_Xenium.h:497
static constexpr uint32_t KeyModifier_Control
Key Modifier flag.
Definition: Zakero_Xenium.h:292
float distance
The distance traveled.
Definition: Zakero_Xenium.h:356
PointerButtonState
Mouse button state.
Definition: Zakero_Xenium.h:365
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:490
WindowMode
All the available window modes.
Definition: Zakero_Xenium.h:479
uint32_t physical_width_mm
The width of the device in millimeters.
Definition: Zakero_Xenium.h:429
PointerAxisSource
Where the axis information came from.
Definition: Zakero_Xenium.h:339
std::function< void(const Xenium::PointPercent &, const Xenium::KeyModifier &)> LambdaPointPercent
A Lambda that has parameters: PointPercent and KeyModifier.
Definition: Zakero_Xenium.h:494
float pixels_per_mm_horizontal
A pre-calculated value.
Definition: Zakero_Xenium.h:433
std::function< void(const Xenium::SizeMm &)> LambdaSizeMm
A Lambda that has a parameter: SizeMm.
Definition: Zakero_Xenium.h:496
int32_t height
The height of the device in hardware units.
Definition: Zakero_Xenium.h:428
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:491
Xenium::VectorOutputId outputVector() const noexcept
Get a list of the Output Id's.
Definition: Zakero_Xenium.h:2932
std::string name
The name of the output.
Definition: Zakero_Xenium.h:424
std::function< void(const Xenium::Key &, const Xenium::KeyModifier &)> LambdaKey
A Lambda that has parameters: Key and KeyModifier.
Definition: Zakero_Xenium.h:492
std::function< void()> Lambda
A Lambda that has no parameters.
Definition: Zakero_Xenium.h:486
Xenium::PointerButtonState state
The button state.
Definition: Zakero_Xenium.h:372
std::function< void(bool)> LambdaBool
A Lambda that has a parameter: bool.
Definition: Zakero_Xenium.h:488
uint32_t WindowId
A type for better readablity.
Definition: Zakero_Xenium.h:502
Xenium::PointPixel outputConvertToPixel(const Xenium::OutputId, const Xenium::PointMm &) const noexcept
Convert Millimeter to Pixel.
Definition: Zakero_Xenium.h:3056
int32_t transform
Transform that maps framebuffer to output.
Definition: Zakero_Xenium.h:432
Xenium::Window * windowCreate(const Xenium::SizeMm &, std::error_code &) noexcept
Create a window.
Definition: Zakero_Xenium.h:3707
KeyState
Keyboard key state
Definition: Zakero_Xenium.h:278
static constexpr uint32_t KeyModifier_Meta
Key Modifier flag.
Definition: Zakero_Xenium.h:295
WindowDecorations
Who is responsible for rendering the decorations.
Definition: Zakero_Xenium.h:474
void outputOnChange(Xenium::LambdaOutputId) noexcept
Notification that an Output device has changed.
Definition: Zakero_Xenium.h:3236
static Xenium * connect() noexcept
Establish a connection with the X11 server.
Definition: Zakero_Xenium.h:2396
virtual ~Xenium() noexcept
Destructor.
Definition: Zakero_Xenium.h:2363
std::function< void(Xenium::WindowDecorations)> LambdaWindowDecorations
A Lambda that has a parameter: WindowDecorations.
Definition: Zakero_Xenium.h:499
int32_t subpixel
The device's subpixel orientation.
Definition: Zakero_Xenium.h:431
uint32_t pressed
A collection of pressed modifiers.
Definition: Zakero_Xenium.h:299
int32_t keyRepeatDelay() const noexcept
The key repeat delay.
Definition: Zakero_Xenium.h:2871
int32_t width
The width of the device in hardware units.
Definition: Zakero_Xenium.h:427
uint32_t code
The key code of the event.
Definition: Zakero_Xenium.h:286
std::function< void(const Xenium::PointPixel &, const Xenium::KeyModifier &)> LambdaPointPixel
A Lambda that has parameters: PointPixel and KeyModifier.
Definition: Zakero_Xenium.h:495
static constexpr uint32_t KeyModifier_Alt
Key Modifier flag.
Definition: Zakero_Xenium.h:293
std::function< void(const Xenium::SizePixel &)> LambdaSizePixel
A Lambda that has a parameter: SizePixel.
Definition: Zakero_Xenium.h:498
Xenium::KeyState state
The state of the key.
Definition: Zakero_Xenium.h:287
Xenium::PointPercent outputConvertToPercent(const Xenium::OutputId, const Xenium::PointPixel &) const noexcept
Convert Pixel to a Percentage.
Definition: Zakero_Xenium.h:3029
void outputOnAdd(Xenium::LambdaOutputId) noexcept
Notification of adding an Output device.
Definition: Zakero_Xenium.h:3215
uint32_t time
When the event occurred.
Definition: Zakero_Xenium.h:354
std::function< void(const Xenium::PointerAxis &, const Xenium::KeyModifier &)> LambdaAxis
A Lambda that has parameters: PointerAxis and KeyModifier.
Definition: Zakero_Xenium.h:487
static constexpr uint32_t KeyModifier_CapsLock
Key Modifier flag.
Definition: Zakero_Xenium.h:291
std::function< void(const Xenium::PointMm &, const Xenium::KeyModifier &)> LambdaPointMm
A Lambda that has parameters: PointMm and KeyModifier.
Definition: Zakero_Xenium.h:493
uint32_t code
The event code.
Definition: Zakero_Xenium.h:371
uint32_t time
When the key event happened.
Definition: Zakero_Xenium.h:285
static constexpr uint32_t KeyModifier_NumLock
Key Modifier flag.
Definition: Zakero_Xenium.h:294
Xenium::PointerAxisSource source
The source of the event.
Definition: Zakero_Xenium.h:357
uint32_t group
The keyboard layout.
Definition: Zakero_Xenium.h:302
float pixels_per_mm_vertical
A pre-calculated value.
Definition: Zakero_Xenium.h:434
PointerAxisType
The direction of the axis movement.
Definition: Zakero_Xenium.h:347
static std::string outputTransformName(int32_t) noexcept
Get a human readable string.
Definition: Zakero_Xenium.h:2980
Key event information.
Definition: Zakero_Xenium.h:284
A collection modifier flags.
Definition: Zakero_Xenium.h:298
Information about a output device.
Definition: Zakero_Xenium.h:423
Information about an Axis event.
Definition: Zakero_Xenium.h:353
Information about a pointer button event.
Definition: Zakero_Xenium.h:370
A location that uses millimeters.
Definition: Zakero_Xenium.h:309
float y
Where in the Y-Axis the point is.
Definition: Zakero_Xenium.h:312
float x
Where in the X-Axis the point is.
Definition: Zakero_Xenium.h:311
uint32_t time
Where in time the point is (if > 0).
Definition: Zakero_Xenium.h:310
friend bool operator==(Xenium::PointMm &, Xenium::PointMm &) noexcept
Compare two Point objects.
Definition: Zakero_Xenium.h:8999
A location that uses percentages.
Definition: Zakero_Xenium.h:318
float x
Where in the X-Axis the point is.
Definition: Zakero_Xenium.h:320
uint32_t time
Where in time the point is (if > 0).
Definition: Zakero_Xenium.h:319
float y
Where in the Y-Axis the point is.
Definition: Zakero_Xenium.h:321
friend bool operator==(Xenium::PointPercent &, Xenium::PointPercent &) noexcept
Compare two Point objects.
Definition: Zakero_Xenium.h:9022
A location that uses pixels.
Definition: Zakero_Xenium.h:327
friend bool operator==(Xenium::PointPixel &, Xenium::PointPixel &) noexcept
Compare two Point objects.
Definition: Zakero_Xenium.h:9042
uint32_t time
Where in time the point is (if > 0).
Definition: Zakero_Xenium.h:328
int32_t y
Where in the Y-Axis the point is.
Definition: Zakero_Xenium.h:330
int32_t x
Where in the X-Axis the point is.
Definition: Zakero_Xenium.h:329
Size measured in millimeters.
Definition: Zakero_Xenium.h:379
friend bool operator==(Xenium::SizeMm &, Xenium::SizeMm &) noexcept
Compare two Point objects.
Definition: Zakero_Xenium.h:9061
Size measured as a percentage of the Output (Monitor) resolution.
Definition: Zakero_Xenium.h:387
friend bool operator==(Xenium::SizePercent &, Xenium::SizePercent &) noexcept
Compare two Point objects.
Definition: Zakero_Xenium.h:9082
Size measured in pixels.
Definition: Zakero_Xenium.h:395
friend bool operator==(Xenium::SizePixel &, Xenium::SizePixel &) noexcept
Compare two Size objects.
Definition: Zakero_Xenium.h:9100