kranewm

An ICCCM & EWMH compliant X11 reparenting, dynamic window manager, written in C++
git clone git://git.deurzen.net/kranewm
Log | Files | Refs | LICENSE

xconnection.cc (112561B)


      1 #include "../common.hh"
      2 #include "../util.hh"
      3 #include "xconnection.hh"
      4 
      5 #include <algorithm>
      6 #include <cstring>
      7 #include <iomanip>
      8 #include <iterator>
      9 #include <numeric>
     10 #include <sstream>
     11 #include <deque>
     12 
     13 extern "C" {
     14 #include <X11/XF86keysym.h>
     15 #include <X11/Xatom.h>
     16 #include <X11/Xproto.h>
     17 #include <X11/Xutil.h>
     18 #include <X11/extensions/XRes.h>
     19 #include <X11/extensions/Xinerama.h>
     20 #include <X11/keysym.h>
     21 #include <X11/keysymdef.h>
     22 #include <fcntl.h>
     23 #include <proc/readproc.h>
     24 #include <signal.h>
     25 #include <sys/socket.h>
     26 #include <unistd.h>
     27 #include <xcb/xcb.h>
     28 #include <xcb/xproto.h>
     29 #include <xkbcommon/xkbcommon-keysyms.h>
     30 }
     31 
     32 
     33 #pragma GCC diagnostic push
     34 #pragma GCC diagnostic ignored "-Wold-style-cast"
     35 
     36 XConnection::XConnection(const std::string_view wm_name)
     37     : mp_dpy(XOpenDisplay(NULL)),
     38       m_root(XDefaultRootWindow(mp_dpy)),
     39       m_check_window(XCreateWindow(
     40           mp_dpy,
     41           m_root,
     42           -1, -1,
     43           1, 1,
     44           CopyFromParent, 0, InputOnly,
     45           CopyFromParent, 0, NULL
     46       )),
     47       m_dpy_fd(XConnectionNumber(mp_dpy)),
     48       m_sock_fd(-1),
     49       m_client_fd(-1),
     50       m_max_fd(-1),
     51       m_wm_name(wm_name),
     52       m_interned_atoms({}),
     53       m_atom_names({}),
     54       m_keys({}),
     55       m_keycodes({}),
     56       m_netwm_atoms({})
     57 {
     58     static const std::unordered_map<NetWMID, const char*> NETWM_ATOM_NAMES({
     59         { NetWMID::NetSupported,                "_NET_SUPPORTED"                    },
     60         { NetWMID::NetClientList,               "_NET_CLIENT_LIST"                  },
     61         { NetWMID::NetNumberOfDesktops,         "_NET_NUMBER_OF_DESKTOPS"           },
     62         { NetWMID::NetCurrentDesktop,           "_NET_CURRENT_DESKTOP"              },
     63         { NetWMID::NetDesktopNames,             "_NET_DESKTOP_NAMES"                },
     64         { NetWMID::NetDesktopGeometry,          "_NET_DESKTOP_GEOMETRY"             },
     65         { NetWMID::NetDesktopViewport,          "_NET_DESKTOP_VIEWPORT"             },
     66         { NetWMID::NetWorkarea,                 "_NET_WORKAREA"                     },
     67         { NetWMID::NetActiveWindow,             "_NET_ACTIVE_WINDOW"                },
     68         { NetWMID::NetWMName,                   "_NET_WM_NAME"                      },
     69         { NetWMID::NetWMDesktop,                "_NET_WM_DESKTOP"                   },
     70         { NetWMID::NetWMStrut,                  "_NET_WM_STRUT"                     },
     71         { NetWMID::NetWMStrutPartial,           "_NET_WM_STRUT_PARTIAL"             },
     72         { NetWMID::NetWMFrameExtents,           "_NET_WM_FRAME_EXTENTS"             },
     73         { NetWMID::NetSupportingWMCheck,        "_NET_SUPPORTING_WM_CHECK"          },
     74         { NetWMID::NetWMState,                  "_NET_WM_STATE"                     },
     75         { NetWMID::NetWMWindowType,             "_NET_WM_WINDOW_TYPE"               },
     76         // root messages
     77         { NetWMID::NetWMCloseWindow,            "_NET_CLOSE_WINDOW"                 },
     78         { NetWMID::NetWMMoveResize,             "_NET_WM_MOVERESIZE"                },
     79         { NetWMID::NetRequestFrameExtents,      "_NET_REQUEST_FRAME_EXTENTS"        },
     80         { NetWMID::NetMoveResizeWindow,         "_NET_MOVERESIZE_WINDOW"            },
     81         // window states
     82         { NetWMID::NetWMStateFullscreen,        "_NET_WM_STATE_FULLSCREEN"          },
     83         { NetWMID::NetWMStateAbove,             "_NET_WM_STATE_ABOVE"               },
     84         { NetWMID::NetWMStateBelow,             "_NET_WM_STATE_BELOW"               },
     85         { NetWMID::NetWMStateDemandsAttention,  "_NET_WM_STATE_DEMANDS_ATTENTION"   },
     86         { NetWMID::NetWMStateHidden,            "_NET_WM_STATE_HIDDEN"              },
     87         // window types
     88         { NetWMID::NetWMWindowTypeDesktop,      "_NET_WM_WINDOW_TYPE_DESKTOP"       },
     89         { NetWMID::NetWMWindowTypeDock,         "_NET_WM_WINDOW_TYPE_DOCK"          },
     90         { NetWMID::NetWMWindowTypeToolbar,      "_NET_WM_WINDOW_TYPE_TOOLBAR"       },
     91         { NetWMID::NetWMWindowTypeMenu,         "_NET_WM_WINDOW_TYPE_MENU"          },
     92         { NetWMID::NetWMWindowTypeUtility,      "_NET_WM_WINDOW_TYPE_UTILITY"       },
     93         { NetWMID::NetWMWindowTypeSplash,       "_NET_WM_WINDOW_TYPE_SPLASH"        },
     94         { NetWMID::NetWMWindowTypeDialog,       "_NET_WM_WINDOW_TYPE_DIALOG"        },
     95         { NetWMID::NetWMWindowTypeDropdownMenu, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU" },
     96         { NetWMID::NetWMWindowTypePopupMenu,    "_NET_WM_WINDOW_TYPE_POPUP_MENU"    },
     97         { NetWMID::NetWMWindowTypeTooltip,      "_NET_WM_WINDOW_TYPE_TOOLTIP"       },
     98         { NetWMID::NetWMWindowTypeNotification, "_NET_WM_WINDOW_TYPE_NOTIFICATION"  },
     99         { NetWMID::NetWMWindowTypeNormal,       "_NET_WM_WINDOW_TYPE_NORMAL"        },
    100     });
    101 
    102     for (auto&& [id,name] : NETWM_ATOM_NAMES)
    103         m_netwm_atoms[id] = get_atom(name);
    104 
    105     for (std::size_t i = 0; i < LASTEvent; ++i)
    106         m_event_dispatcher[i] = &XConnection::on_unimplemented;
    107 
    108     m_event_dispatcher[ButtonPress] = &XConnection::on_button_press;
    109     m_event_dispatcher[ButtonRelease] = &XConnection::on_button_release;
    110     m_event_dispatcher[CirculateRequest] = &XConnection::on_circulate_request;
    111     m_event_dispatcher[ClientMessage] = &XConnection::on_client_message;
    112     m_event_dispatcher[ConfigureNotify] = &XConnection::on_configure_notify;
    113     m_event_dispatcher[ConfigureRequest] = &XConnection::on_configure_request;
    114     m_event_dispatcher[DestroyNotify] = &XConnection::on_destroy_notify;
    115     m_event_dispatcher[Expose] = &XConnection::on_expose;
    116     m_event_dispatcher[FocusIn] = &XConnection::on_focus_in;
    117     m_event_dispatcher[EnterNotify] = &XConnection::on_enter_notify;
    118     m_event_dispatcher[KeyPress] = &XConnection::on_key_press;
    119     m_event_dispatcher[MapNotify] = &XConnection::on_map_notify;
    120     m_event_dispatcher[MapRequest] = &XConnection::on_map_request;
    121     m_event_dispatcher[MappingNotify] = &XConnection::on_mapping_notify;
    122     m_event_dispatcher[MotionNotify] = &XConnection::on_motion_notify;
    123     m_event_dispatcher[PropertyNotify] = &XConnection::on_property_notify;
    124     m_event_dispatcher[UnmapNotify] = &XConnection::on_unmap_notify;
    125 }
    126 
    127 XConnection::~XConnection()
    128 {}
    129 
    130 
    131 void
    132 XConnection::init_wm_ipc()
    133 {
    134     std::string socket_name = m_wm_name.data() + std::string("_SOCKET");
    135     std::string wm_socket;
    136 
    137     if (const char* env_wm_socket = std::getenv(socket_name.c_str()))
    138         wm_socket = std::string(env_wm_socket);
    139     else {
    140         static const std::string delim = "_";
    141         std::string display_string = XDisplayString(mp_dpy);
    142         int default_screen = XDefaultScreen(mp_dpy);
    143 
    144         std::string host_name = "localhost";
    145         std::string display_nr;
    146         std::string screen_nr = std::to_string(default_screen);
    147 
    148         std::string::size_type display_nr_pos = display_string.find_last_of(':');
    149 
    150         if (display_nr_pos != std::string::npos) {
    151             if (display_nr_pos != 0)
    152                 host_name = display_string.substr(0, display_nr_pos);
    153 
    154             display_string = display_string.substr(display_nr_pos + 1);
    155         }
    156 
    157         std::string::size_type screen_nr_pos = display_string.find_last_of('.');
    158 
    159         if (screen_nr_pos != std::string::npos
    160             && display_nr_pos != std::string::npos
    161             && display_nr_pos < screen_nr_pos)
    162         {
    163             display_nr = display_string.substr(display_nr_pos + 1, screen_nr_pos - (display_nr_pos + 1));
    164         } else
    165             display_nr = display_string.substr(0, screen_nr_pos);
    166 
    167         wm_socket = "/tmp/"
    168             + std::string(m_wm_name)
    169             + delim + host_name
    170             + delim + display_nr
    171             + delim + screen_nr;
    172     }
    173 
    174     memset(m_sock_path, 0, 256);
    175     memset(&m_sock_addr, 0, sizeof(m_sock_addr));
    176 
    177     strncpy(m_sock_path,
    178         wm_socket.c_str(),
    179         wm_socket.length() < 256
    180             ? wm_socket.length()
    181             : 255
    182     );
    183 
    184     m_sock_addr.sun_family = AF_UNIX;
    185 
    186     if (snprintf(m_sock_addr.sun_path, sizeof(m_sock_addr.sun_path), "%s", m_sock_path) < 0)
    187         Util::die("unable to write IPC socket path");
    188 
    189     m_sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    190 
    191     if (m_sock_fd == -1)
    192         Util::die("unable to set up IPC socket");
    193 
    194     unlink(m_sock_path);
    195 
    196     if (bind(m_sock_fd, (struct sockaddr*)&m_sock_addr, sizeof(m_sock_addr)) == -1)
    197         Util::die("unable to bind name to IPC socket");
    198 
    199     if (listen(m_sock_fd, SOMAXCONN) == -1)
    200         Util::die("unable to listen to IPC socket");
    201 
    202     fcntl(m_sock_fd, F_SETFD, FD_CLOEXEC | fcntl(m_sock_fd, F_GETFD));
    203 }
    204 
    205 
    206 bool
    207 XConnection::flush()
    208 {
    209     XFlush(mp_dpy);
    210     return true;
    211 }
    212 
    213 winsys::Event
    214 XConnection::step()
    215 {
    216     next_event(m_current_event);
    217 
    218     if (m_current_event.type >= 0 && m_current_event.type <= 256)
    219         return (this->*(m_event_dispatcher[m_current_event.type]))();
    220 
    221     return std::monostate{};
    222 }
    223 
    224 bool
    225 XConnection::check_progress()
    226 {
    227     XFlush(mp_dpy);
    228 
    229     FD_ZERO(&m_descr);
    230     FD_SET(m_sock_fd, &m_descr);
    231     FD_SET(m_dpy_fd, &m_descr);
    232     m_max_fd = std::max(m_sock_fd, m_dpy_fd);
    233 
    234     return select(m_max_fd + 1, &m_descr, NULL, NULL, NULL) > 0;
    235 }
    236 
    237 void
    238 XConnection::process_events(std::function<void(winsys::Event)> callback)
    239 {
    240     if (FD_ISSET(m_dpy_fd, &m_descr))
    241         while (XPending(mp_dpy))
    242             callback(step());
    243 }
    244 
    245 void
    246 XConnection::process_messages(std::function<void(winsys::Message)> callback)
    247 {
    248     enum MessageType {
    249         Command,
    250         Config,
    251         Window,
    252         Workspace,
    253         Query
    254     };
    255 
    256     static const std::unordered_map<std::string_view, MessageType> message_types{
    257         { "command",   Command },
    258         { "cmd",       Command },
    259         { "config",    Config },
    260         { "conf",      Config },
    261         { "cfg",       Config },
    262         { "client",    Window },
    263         { "cli",       Window },
    264         { "workspace", Workspace },
    265         { "ws",        Workspace },
    266         { "query",     Query },
    267         { "qr",        Query },
    268     };
    269 
    270     static int n = 0;
    271     static char msg[BUFSIZ] = {};
    272 
    273     if (FD_ISSET(m_sock_fd, &m_descr)) {
    274         m_client_fd = accept(m_sock_fd, NULL, 0);
    275 
    276         if (m_client_fd > 0 && (n = recv(m_client_fd, msg, sizeof(msg) - 1, 0)) > 0) {
    277             msg[n] = '\0';
    278             FILE* rply = fdopen(m_client_fd, "w");
    279 
    280             if (rply != NULL) {
    281                 winsys::Message message = std::monostate{};
    282 
    283                 std::deque<std::string> words{};
    284                 std::istringstream word_stream(msg);
    285                 std::string word;
    286 
    287                 while (word_stream >> std::quoted(word))
    288                     words.push_back(word);
    289 
    290                 if (!words.empty()) {
    291                     std::string_view area = words.front();
    292 
    293                     if (message_types.count(area) > 0) {
    294                         words.pop_front();
    295 
    296                         switch (message_types.at(area)) {
    297                         case Command:   message = winsys::CommandMessage{words};   break;
    298                         case Config:    message = winsys::ConfigMessage{words};    break;
    299                         case Window:    message = winsys::WindowMessage{words};    break;
    300                         case Workspace: message = winsys::WorkspaceMessage{words}; break;
    301                         case Query:     message = winsys::QueryMessage{words};     break;
    302                         }
    303                     }
    304                 }
    305 
    306                 callback(message);
    307             } else
    308                 close(m_client_fd);
    309         }
    310     }
    311 }
    312 
    313 std::vector<winsys::Screen>
    314 XConnection::connected_outputs()
    315 {
    316     int n_screen_info;
    317     XineramaScreenInfo* screen_info = XineramaQueryScreens(mp_dpy, &n_screen_info);
    318     std::vector<XineramaScreenInfo*> screen_info_vector;
    319 
    320     if (!screen_info)
    321         return {};
    322 
    323     screen_info_vector.reserve(n_screen_info);
    324     std::transform(
    325         &screen_info[0],
    326         &screen_info[n_screen_info],
    327         std::back_inserter(screen_info_vector),
    328         [](XineramaScreenInfo& screen_info) -> XineramaScreenInfo* {
    329             return &screen_info;
    330         }
    331     );
    332 
    333     std::stable_sort(
    334         screen_info_vector.begin(),
    335         screen_info_vector.end(),
    336         [](XineramaScreenInfo* screen_info1, XineramaScreenInfo* screen_info2) -> bool {
    337             if (screen_info1->x_org < screen_info2->x_org)
    338                 return true;
    339 
    340             if (screen_info1->x_org == screen_info2->x_org)
    341                 return screen_info1->y_org < screen_info2->y_org;
    342 
    343             return false;
    344         }
    345     );
    346 
    347     std::vector<winsys::Screen> screens;
    348     screens.reserve(n_screen_info);
    349 
    350     std::transform(
    351         screen_info_vector.begin(),
    352         std::remove_if(
    353             screen_info_vector.begin(),
    354             screen_info_vector.end(),
    355             [](XineramaScreenInfo* screen_info) -> bool {
    356                 return !(screen_info->width > 0 && screen_info->height > 0);
    357             }
    358         ),
    359         std::back_inserter(screens),
    360         [i=0](XineramaScreenInfo* screen_info) mutable -> winsys::Screen {
    361             winsys::Region region = winsys::Region {
    362                 winsys::Pos {
    363                     screen_info->x_org,
    364                     screen_info->y_org
    365                 },
    366                 winsys::Dim {
    367                     screen_info->width,
    368                     screen_info->height
    369                 }
    370             };
    371 
    372             return winsys::Screen(i++, region);
    373         }
    374     );
    375 
    376     XFree(screen_info);
    377 
    378     return screens;
    379 }
    380 
    381 std::vector<winsys::Window>
    382 XConnection::top_level_windows()
    383 {
    384     Window _w;
    385     Window* children;
    386     unsigned nchildren;
    387 
    388     XQueryTree(mp_dpy, m_root, &_w, &_w, &children, &nchildren);
    389 
    390     std::vector<winsys::Window> windows;
    391     for (std::size_t i = 0; i < nchildren; ++i)
    392         if (m_root != children[i])
    393             windows.push_back(children[i]);
    394 
    395     if (children)
    396         XFree(children);
    397 
    398     return windows;
    399 }
    400 
    401 winsys::Pos
    402 XConnection::get_pointer_position()
    403 {
    404     winsys::Window _r, _c;
    405     int rx, ry, _wx, _wy;
    406     unsigned _m;
    407 
    408     XQueryPointer(
    409         mp_dpy,
    410         m_root,
    411         &_r, &_c,
    412         &rx, &ry,
    413         &_wx, &_wy,
    414         &_m
    415     );
    416 
    417     return winsys::Pos { rx, ry };
    418 }
    419 
    420 void
    421 XConnection::warp_pointer_center_of_window_or_root(std::optional<winsys::Window> window, winsys::Screen& screen)
    422 {
    423     winsys::Pos pos;
    424 
    425     if (window) {
    426         XWindowAttributes wa;
    427         XGetWindowAttributes(mp_dpy, *window, &wa);
    428 
    429         pos = winsys::Pos::from_center_of_dim(
    430             winsys::Dim {
    431                 wa.width,
    432                 wa.height
    433             }
    434         );
    435     } else
    436         pos = winsys::Pos::from_center_of_dim(
    437             screen.placeable_region().dim
    438         );
    439 
    440     XWarpPointer(
    441         mp_dpy,
    442         None,
    443         window.value_or(m_root),
    444         0, 0, 0, 0,
    445         pos.x, pos.y
    446     );
    447 }
    448 
    449 void
    450 XConnection::warp_pointer(winsys::Pos pos)
    451 {
    452     XWarpPointer(
    453         mp_dpy,
    454         None,
    455         m_root,
    456         0, 0, 0, 0,
    457         pos.x, pos.y
    458     );
    459 }
    460 
    461 void
    462 XConnection::warp_pointer_rpos(winsys::Window window, winsys::Pos pos)
    463 {
    464     XWarpPointer(
    465         mp_dpy,
    466         None,
    467         window,
    468         0, 0, 0, 0,
    469         pos.x, pos.y
    470     );
    471 }
    472 
    473 void
    474 XConnection::confine_pointer(winsys::Window window)
    475 {
    476     if (!m_confined_to) {
    477         int status = XGrabPointer(
    478             mp_dpy,
    479             m_root,
    480             False,
    481             PointerMotionMask | ButtonReleaseMask,
    482             GrabModeAsync, GrabModeAsync,
    483             m_root,
    484             None,
    485             CurrentTime
    486         );
    487 
    488         if (status == 0) {
    489             XGrabKeyboard(
    490                 mp_dpy,
    491                 m_root,
    492                 False,
    493                 GrabModeAsync, GrabModeAsync,
    494                 CurrentTime
    495             );
    496 
    497             m_confined_to = window;
    498         }
    499     }
    500 }
    501 
    502 bool
    503 XConnection::release_pointer()
    504 {
    505     if (m_confined_to) {
    506         XUngrabPointer(mp_dpy, CurrentTime);
    507         XUngrabKeyboard(mp_dpy, CurrentTime);
    508 
    509         m_confined_to = std::nullopt;
    510         return true;
    511     }
    512 
    513     return false;
    514 }
    515 
    516 void
    517 XConnection::call_external_command(std::string& command)
    518 {
    519     if (!fork()) {
    520         if (mp_dpy)
    521             close(m_dpy_fd);
    522 
    523         setsid();
    524         execl("/bin/sh", "/bin/sh", "-c", ("exec " + command).c_str(), NULL);
    525         exit(EXIT_SUCCESS);
    526     }
    527 }
    528 
    529 void
    530 XConnection::cleanup()
    531 {
    532     XUngrabKey(mp_dpy, AnyKey, AnyModifier, m_root);
    533     XDestroyWindow(mp_dpy, m_check_window);
    534 
    535     unset_card_property(m_root, "_NET_ACTIVE_WINDOW");
    536     unset_window_property(m_root, "_NET_SUPPORTING_WM_CHECK");
    537     unset_string_property(m_root, "_NET_WM_NAME");
    538     unset_string_property(m_root, "WN_CLASS");
    539     unset_string_property(m_root, "WN_CLASS");
    540     unset_atomlist_property(m_root, "_NET_SUPPORTED");
    541     unset_card_property(m_root, "_NET_WM_PID");
    542     unset_windowlist_property(m_root, "_NET_CLIENT_LIST");
    543 
    544     XCloseDisplay(mp_dpy);
    545 }
    546 
    547 
    548 // window manipulation
    549 winsys::Window
    550 XConnection::create_frame(winsys::Region region)
    551 {
    552     long mask = CWBackPixel | CWBorderPixel;
    553     XSetWindowAttributes wa;
    554 
    555     XVisualInfo vinfo;
    556     if (XMatchVisualInfo(mp_dpy, DefaultScreen(mp_dpy), 32, TrueColor, &vinfo)) {
    557         wa.colormap = XCreateColormap(mp_dpy, m_root, vinfo.visual, AllocNone);
    558         mask |= CWColormap;
    559     }
    560 
    561     wa.background_pixel = 0;
    562     wa.border_pixel = 0;
    563 
    564     winsys::Window window = XCreateWindow(
    565         mp_dpy, m_root,
    566         region.pos.x, region.pos.y,
    567         region.dim.w, region.dim.h,
    568         0,
    569         vinfo.depth,
    570         InputOutput,
    571         vinfo.visual,
    572         mask,
    573         &wa
    574     );
    575 
    576     flush();
    577 
    578     return window;
    579 }
    580 
    581 void
    582 XConnection::init_window(winsys::Window window)
    583 {
    584     static const long window_event_mask
    585         = PropertyChangeMask | StructureNotifyMask | FocusChangeMask;
    586 
    587     XSetWindowAttributes wa;
    588     wa.event_mask = window_event_mask;
    589 
    590     XChangeWindowAttributes(mp_dpy, window, CWEventMask, &wa);
    591 }
    592 
    593 void
    594 XConnection::init_frame(winsys::Window window, bool focus_follows_mouse)
    595 {
    596     static const long frame_event_mask
    597         = StructureNotifyMask | SubstructureNotifyMask | SubstructureRedirectMask
    598         | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
    599 
    600     XSetWindowAttributes wa;
    601     wa.event_mask = frame_event_mask;
    602 
    603     if (focus_follows_mouse)
    604         wa.event_mask |= EnterWindowMask;
    605 
    606     XChangeWindowAttributes(mp_dpy, window, CWEventMask, &wa);
    607 }
    608 
    609 void
    610 XConnection::init_unmanaged(winsys::Window window)
    611 {
    612     static const long unmanaged_event_mask = StructureNotifyMask;
    613 
    614     XSetWindowAttributes wa;
    615     wa.event_mask = unmanaged_event_mask;
    616 
    617     XChangeWindowAttributes(mp_dpy, window, CWEventMask, &wa);
    618 }
    619 
    620 void
    621 XConnection::init_move(winsys::Window)
    622 {
    623     static winsys::Window handle = create_handle();
    624     confine_pointer(handle);
    625 }
    626 
    627 void
    628 XConnection::init_resize(winsys::Window)
    629 {
    630     static winsys::Window handle = create_handle();
    631     confine_pointer(handle);
    632 }
    633 
    634 void
    635 XConnection::cleanup_window(winsys::Window window)
    636 {
    637     XDeleteProperty(mp_dpy, window, get_atom("_NET_WM_STATE"));
    638     XDeleteProperty(mp_dpy, window, get_atom("_NET_WM_DESKTOP"));
    639 }
    640 
    641 void
    642 XConnection::map_window(winsys::Window window)
    643 {
    644     XMapWindow(mp_dpy, window);
    645 }
    646 
    647 void
    648 XConnection::unmap_window(winsys::Window window)
    649 {
    650     XUnmapWindow(mp_dpy, window);
    651 }
    652 
    653 void
    654 XConnection::reparent_window(winsys::Window window, winsys::Window parent, winsys::Pos pos)
    655 {
    656     disable_substructure_events();
    657     XReparentWindow(mp_dpy, window, parent, pos.x, pos.y);
    658     enable_substructure_events();
    659 }
    660 
    661 void
    662 XConnection::unparent_window(winsys::Window window, winsys::Pos pos)
    663 {
    664     disable_substructure_events();
    665     XReparentWindow(mp_dpy, window, m_root, pos.x, pos.y);
    666     enable_substructure_events();
    667 }
    668 
    669 void
    670 XConnection::destroy_window(winsys::Window window)
    671 {
    672     XDestroyWindow(mp_dpy, window);
    673 }
    674 
    675 bool
    676 XConnection::close_window(winsys::Window window)
    677 {
    678     Atom* protocols;
    679     int n = 0;
    680 
    681     Atom delete_atom = get_atom("WM_DELETE_WINDOW");
    682     bool found = false;
    683 
    684     if (XGetWMProtocols(mp_dpy, window, &protocols, &n)) {
    685         while (!found && n--)
    686             found = delete_atom == protocols[n];
    687 
    688         XFree(protocols);
    689     }
    690 
    691     if (found) {
    692         XEvent event;
    693         event.type = ClientMessage;
    694         event.xclient.window = window;
    695         event.xclient.message_type = get_atom("WM_PROTOCOLS");
    696         event.xclient.format = 32;
    697         event.xclient.data.l[0] = get_atom("WM_DELETE_WINDOW");
    698         event.xclient.data.l[1] = CurrentTime;
    699         XSendEvent(mp_dpy, window, False, NoEventMask, &event);
    700 
    701         return true;
    702     }
    703 
    704     return false;
    705 }
    706 
    707 bool
    708 XConnection::kill_window(winsys::Window window)
    709 {
    710     if (!close_window(window)) {
    711         XGrabServer(mp_dpy);
    712         XSetErrorHandler(s_passthrough_error_handler);
    713         XSetCloseDownMode(mp_dpy, DestroyAll);
    714         XKillClient(mp_dpy, window);
    715         XSync(mp_dpy, False);
    716         XSetErrorHandler(s_default_error_handler);
    717         XUngrabServer(mp_dpy);
    718     }
    719 
    720     return true;
    721 }
    722 
    723 void
    724 XConnection::place_window(winsys::Window window, winsys::Region& region)
    725 {
    726     disable_substructure_events();
    727     XMoveResizeWindow(mp_dpy, window, region.pos.x, region.pos.y, region.dim.w, region.dim.h);
    728     enable_substructure_events();
    729 }
    730 
    731 void
    732 XConnection::move_window(winsys::Window window, winsys::Pos pos)
    733 {
    734     disable_substructure_events();
    735     XMoveWindow(mp_dpy, window, pos.x, pos.y);
    736     enable_substructure_events();
    737 }
    738 
    739 void
    740 XConnection::resize_window(winsys::Window window, winsys::Dim dim)
    741 {
    742     disable_substructure_events();
    743     XResizeWindow(mp_dpy, window, dim.w, dim.h);
    744     enable_substructure_events();
    745 }
    746 
    747 void
    748 XConnection::focus_window(winsys::Window window)
    749 {
    750     if (window == None)
    751         window = m_root;
    752 
    753     XSetInputFocus(mp_dpy, window, RevertToNone, CurrentTime);
    754 }
    755 
    756 void
    757 XConnection::stack_window_above(winsys::Window window, std::optional<winsys::Window> sibling)
    758 {
    759     long mask = CWStackMode;
    760 
    761     XWindowChanges wc;
    762     wc.stack_mode = Above;
    763 
    764     if (sibling) {
    765         wc.sibling = *sibling;
    766         mask |= CWSibling;
    767     }
    768 
    769     XConfigureWindow(mp_dpy, window, mask, &wc);
    770 }
    771 
    772 void
    773 XConnection::stack_window_below(winsys::Window window, std::optional<winsys::Window> sibling)
    774 {
    775     long mask = CWStackMode;
    776 
    777     XWindowChanges wc;
    778     wc.stack_mode = Below;
    779 
    780     if (sibling != std::nullopt) {
    781         wc.sibling = *sibling;
    782         mask |= CWSibling;
    783     }
    784 
    785     XConfigureWindow(mp_dpy, window, mask, &wc);
    786 }
    787 
    788 void
    789 XConnection::insert_window_in_save_set(winsys::Window window)
    790 {
    791     XChangeSaveSet(mp_dpy, window, SetModeInsert);
    792 }
    793 
    794 void
    795 XConnection::grab_bindings(std::vector<winsys::KeyInput>& key_inputs, std::vector<winsys::MouseInput>& mouse_inputs)
    796 {
    797     static const long mouse_event_mask =
    798         ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
    799 
    800     auto modifier_to_x11 = [](winsys::Modifier modifier) -> std::size_t {
    801         switch (modifier) {
    802         case winsys::Modifier::Ctrl:       return ControlMask;
    803         case winsys::Modifier::Shift:      return ShiftMask;
    804         case winsys::Modifier::Alt:        return Mod1Mask;
    805         case winsys::Modifier::Super:      return Mod4Mask;
    806         case winsys::Modifier::NumLock:    return Mod2Mask;
    807         case winsys::Modifier::ScrollLock: return Mod5Mask;
    808         default: return 0;
    809         }
    810     };
    811 
    812     std::vector<std::size_t> modifiers_to_ignore{
    813         0,
    814         Mod2Mask,
    815         Mod5Mask
    816     };
    817 
    818     for (auto& modifier : modifiers_to_ignore) {
    819         for (auto& key_input : key_inputs)
    820             XGrabKey(mp_dpy,
    821                 get_keycode(key_input.key),
    822                 std::accumulate(
    823                     key_input.modifiers.begin(),
    824                     key_input.modifiers.end(),
    825                     0,
    826                     [modifier_to_x11](std::size_t const& lhs, winsys::Modifier const& rhs) {
    827                         return lhs | modifier_to_x11(rhs);
    828                     }
    829                 ) | modifier,
    830                 m_root,
    831                 True,
    832                 GrabModeAsync,
    833                 GrabModeAsync
    834             );
    835 
    836         for (auto& mouse_input : mouse_inputs)
    837             XGrabButton(mp_dpy,
    838                 get_buttoncode(mouse_input.button),
    839                 std::accumulate(
    840                     mouse_input.modifiers.begin(),
    841                     mouse_input.modifiers.end(),
    842                     0,
    843                     [modifier_to_x11](std::size_t const& lhs, winsys::Modifier const& rhs) {
    844                         return lhs | modifier_to_x11(rhs);
    845                     }
    846                 ) | modifier,
    847                 m_root,
    848                 False,
    849                 mouse_event_mask,
    850                 GrabModeAsync,
    851                 GrabModeAsync,
    852                 None,
    853                 None
    854             );
    855     }
    856 
    857     XSetWindowAttributes wa;
    858     wa.event_mask = PropertyChangeMask | SubstructureRedirectMask | StructureNotifyMask
    859         | ButtonPressMask | PointerMotionMask | FocusChangeMask;
    860 
    861     XChangeWindowAttributes(mp_dpy, m_root, CWEventMask, &wa);
    862 
    863     flush();
    864 }
    865 
    866 void
    867 XConnection::regrab_buttons(winsys::Window window)
    868 {
    869     static const long regrab_event_mask
    870         = ButtonPressMask | ButtonReleaseMask;
    871 
    872     XGrabButton(mp_dpy,
    873         AnyButton,
    874         AnyModifier,
    875         window,
    876         True,
    877         regrab_event_mask,
    878         GrabModeAsync,
    879         GrabModeAsync,
    880         None,
    881         None
    882     );
    883 }
    884 
    885 void
    886 XConnection::ungrab_buttons(winsys::Window window)
    887 {
    888     XUngrabButton(mp_dpy, AnyButton, AnyModifier, window);
    889 }
    890 
    891 void
    892 XConnection::unfocus()
    893 {
    894     XSetInputFocus(mp_dpy, m_root, m_check_window, CurrentTime);
    895 }
    896 
    897 void
    898 XConnection::set_window_border_width(winsys::Window window, unsigned width)
    899 {
    900     XSetWindowBorderWidth(mp_dpy, window, width);
    901 }
    902 
    903 void
    904 XConnection::set_window_border_color(winsys::Window window, unsigned color)
    905 {
    906     XSetWindowBorder(mp_dpy, window, color);
    907 }
    908 
    909 void
    910 XConnection::set_window_background_color(winsys::Window window, unsigned color)
    911 {
    912     XSetWindowBackground(mp_dpy, window, color);
    913     XClearWindow(mp_dpy, window);
    914 }
    915 
    916 void
    917 XConnection::set_window_notify_enter(winsys::Window window, bool notify_enter)
    918 {
    919     static const long frame_event_mask
    920         = StructureNotifyMask | SubstructureNotifyMask | SubstructureRedirectMask
    921         | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
    922 
    923     XSetWindowAttributes wa;
    924     wa.event_mask = frame_event_mask;
    925 
    926     if (notify_enter)
    927         wa.event_mask |= EnterWindowMask;
    928 
    929     XChangeWindowAttributes(mp_dpy, window, CWEventMask, &wa);
    930 }
    931 
    932 void
    933 XConnection::update_window_offset(winsys::Window window, winsys::Window frame)
    934 {
    935     XWindowAttributes fa;
    936     XWindowAttributes wa;
    937 
    938     XGetWindowAttributes(mp_dpy, frame, &fa);
    939     XGetWindowAttributes(mp_dpy, window, &wa);
    940 
    941     XEvent event;
    942     event.type = ConfigureNotify;
    943     event.xconfigure.send_event = True;
    944     event.xconfigure.display = mp_dpy;
    945     event.xconfigure.event = window;
    946     event.xconfigure.window = window;
    947     event.xconfigure.x = fa.x + wa.x;
    948     event.xconfigure.y = fa.y + wa.x;
    949     event.xconfigure.width = wa.width;
    950     event.xconfigure.height = wa.height;
    951     event.xconfigure.border_width = 0;
    952     event.xconfigure.above = None;
    953     event.xconfigure.override_redirect = True;
    954 
    955     XSendEvent(mp_dpy, window, False, StructureNotifyMask, &event);
    956 }
    957 
    958 winsys::Window
    959 XConnection::get_focused_window()
    960 {
    961     winsys::Window window;
    962     int _i;
    963 
    964     XGetInputFocus(mp_dpy, &window, &_i);
    965     return window;
    966 }
    967 
    968 std::optional<winsys::Region>
    969 XConnection::get_window_geometry(winsys::Window window)
    970 {
    971     static XWindowAttributes wa;
    972     if (!XGetWindowAttributes(mp_dpy, window, &wa))
    973         return std::nullopt;
    974 
    975     return winsys::Region {
    976         winsys::Pos {
    977             wa.x,
    978             wa.y
    979         },
    980         winsys::Dim {
    981             wa.width,
    982             wa.height
    983         }
    984     };
    985 }
    986 
    987 std::optional<winsys::Pid>
    988 XConnection::get_window_pid(winsys::Window window)
    989 {
    990     XResClientIdSpec spec{
    991         window,
    992         XRES_CLIENT_ID_PID_MASK
    993     };
    994 
    995     XResClientIdSpec client_specs[1] = { spec };
    996 
    997     long n_values = 0;
    998     XResClientIdValue* client_values = nullptr;
    999 
   1000     std::optional<winsys::Pid> pid = std::nullopt;
   1001 
   1002     if (!XResQueryClientIds(mp_dpy, 1, client_specs, &n_values, &client_values))
   1003         for (long i = 0; i < n_values; ++i)
   1004             if ((client_values[i].spec.mask & XRES_CLIENT_ID_PID_MASK) != 0) {
   1005                 CARD32* client_value
   1006                     = reinterpret_cast<CARD32*>(client_values[i].value);
   1007 
   1008                 if (client_value) {
   1009                     CARD32 value = *client_value;
   1010 
   1011                     if (value > 0) {
   1012                         pid = static_cast<winsys::Pid>(value);
   1013                         goto yield;
   1014                     }
   1015                 }
   1016             }
   1017 yield:
   1018     XFree(client_values);
   1019     return pid;
   1020 }
   1021 
   1022 std::optional<winsys::Pid>
   1023 XConnection::get_ppid(std::optional<winsys::Pid> pid)
   1024 {
   1025     if (!pid)
   1026         return std::nullopt;
   1027 
   1028     proc_t proc;
   1029     memset(&proc, 0, sizeof(proc));
   1030     PROCTAB* ptab = openproc(PROC_FILLSTATUS | PROC_PID, &pid);
   1031 
   1032     std::optional<winsys::Pid> ppid = std::nullopt;
   1033 
   1034     if (readproc(ptab, &proc) != 0 && proc.ppid > 0) {
   1035         ppid = proc.ppid;
   1036         goto yield;
   1037     }
   1038 
   1039 yield:
   1040     closeproc(ptab);
   1041     return ppid;
   1042 }
   1043 
   1044 bool
   1045 XConnection::must_manage_window(winsys::Window window)
   1046 {
   1047     static const std::vector<winsys::WindowType> ignore_types{
   1048         winsys::WindowType::Desktop,
   1049         winsys::WindowType::Dock,
   1050         winsys::WindowType::Toolbar,
   1051         winsys::WindowType::Menu,
   1052         winsys::WindowType::Splash,
   1053         winsys::WindowType::DropdownMenu,
   1054         winsys::WindowType::PopupMenu,
   1055         winsys::WindowType::Tooltip,
   1056         winsys::WindowType::Notification,
   1057         winsys::WindowType::Combo,
   1058         winsys::WindowType::Dnd,
   1059     };
   1060 
   1061     XWindowAttributes wa;
   1062     return XGetWindowAttributes(mp_dpy, window, &wa)
   1063         && wa.c_class != InputOnly
   1064         && !wa.override_redirect
   1065         && !window_is_any_of_types(window, ignore_types);
   1066 }
   1067 
   1068 bool
   1069 XConnection::must_free_window(winsys::Window window)
   1070 {
   1071     static const std::vector<winsys::WindowState> free_states{
   1072         winsys::WindowState::Modal
   1073     };
   1074 
   1075     static const std::vector<winsys::WindowType> free_types{
   1076         winsys::WindowType::Dialog,
   1077         winsys::WindowType::Utility,
   1078     };
   1079 
   1080     std::optional<Index> desktop = get_window_desktop(window);
   1081 
   1082     if ((desktop && *desktop == 0xFFFFFFFF)
   1083         || window_is_any_of_states(window, free_states)
   1084         || window_is_any_of_types(window, free_types))
   1085     {
   1086         return true;
   1087     }
   1088 
   1089     XWindowAttributes wa;
   1090     XGetWindowAttributes(mp_dpy, window, &wa);
   1091 
   1092     std::optional<winsys::SizeHints> sh
   1093         = get_icccm_window_size_hints(window, std::nullopt);
   1094 
   1095     if (sh) {
   1096         if (sh->min_width && sh->min_height && sh->max_width && sh->max_height)
   1097             return *sh->max_width > 0 && *sh->max_height > 0
   1098                 && *sh->max_width == *sh->min_width && *sh->max_height == *sh->min_height;
   1099     }
   1100 
   1101     return false;
   1102 }
   1103 
   1104 bool
   1105 XConnection::window_is_mappable(winsys::Window window)
   1106 {
   1107     XWindowAttributes wa;
   1108     XGetWindowAttributes(mp_dpy, window, &wa);
   1109 
   1110     return wa.c_class != InputOnly;
   1111 }
   1112 
   1113 // ICCCM
   1114 void
   1115 XConnection::set_icccm_window_state(winsys::Window window, winsys::IcccmWindowState state)
   1116 {
   1117     long window_state;
   1118 
   1119     switch (state) {
   1120     case winsys::IcccmWindowState::Withdrawn: window_state = WithdrawnState; break;
   1121     case winsys::IcccmWindowState::Normal:    window_state = NormalState;    break;
   1122     case winsys::IcccmWindowState::Iconic:    window_state = IconicState;    break;
   1123     default: return;
   1124     }
   1125 
   1126     long data[] = { window_state, None };
   1127     XChangeProperty(mp_dpy, window, get_atom("WM_STATE"), get_atom("WM_STATE"), 32,
   1128         PropModeReplace, reinterpret_cast<const unsigned char*>(data), 2);
   1129 }
   1130 
   1131 void
   1132 XConnection::set_icccm_window_hints(winsys::Window window, winsys::Hints hints)
   1133 {
   1134     bool success = false;
   1135     XWMHints hints_;
   1136 
   1137     XWMHints* x_hints = XGetWMHints(mp_dpy, window);
   1138     if (x_hints) {
   1139         std::memcpy(&hints_, x_hints, sizeof(XWMHints));
   1140         XFree(x_hints);
   1141         success = true;
   1142     }
   1143 
   1144     if (!success)
   1145         return;
   1146 
   1147     if (hints.urgent)
   1148         hints_.flags |= XUrgencyHint;
   1149     else
   1150         hints_.flags &= ~XUrgencyHint;
   1151 
   1152     XSetWMHints(mp_dpy, window, &hints_);
   1153 }
   1154 
   1155 std::string
   1156 XConnection::get_icccm_window_name(winsys::Window window)
   1157 {
   1158     std::string name;
   1159     char name_raw[512];
   1160 
   1161     if (!get_text_property(window, get_atom("_NET_WM_NAME"), name_raw, sizeof name_raw))
   1162         get_text_property(window, XA_WM_NAME, name_raw, sizeof name_raw);
   1163 
   1164     if (name_raw[0] == '\0')
   1165         strcpy(name_raw, "N/a");
   1166 
   1167     name.assign(name_raw);
   1168     return name;
   1169 }
   1170 
   1171 std::string
   1172 XConnection::get_icccm_window_class(winsys::Window window)
   1173 {
   1174     std::string class_ = "N/a";
   1175 
   1176     XClassHint* hint = XAllocClassHint();
   1177     XGetClassHint(mp_dpy, window, hint);
   1178 
   1179     if (hint->res_class) {
   1180         class_.assign(hint->res_class);
   1181         XFree(hint);
   1182     }
   1183 
   1184     return class_;
   1185 }
   1186 
   1187 std::string
   1188 XConnection::get_icccm_window_instance(winsys::Window window)
   1189 {
   1190     std::string instance = "N/a";
   1191 
   1192     XClassHint* hint = XAllocClassHint();
   1193     XGetClassHint(mp_dpy, window, hint);
   1194 
   1195     if (hint->res_name) {
   1196         instance.assign(hint->res_name);
   1197         XFree(hint);
   1198     }
   1199 
   1200     return instance;
   1201 }
   1202 
   1203 std::optional<winsys::Window>
   1204 XConnection::get_icccm_window_transient_for(winsys::Window window)
   1205 {
   1206     winsys::Window transient = None;
   1207     XGetTransientForHint(mp_dpy, window, &transient);
   1208 
   1209     return transient == None ? std::nullopt : std::optional(transient);
   1210 }
   1211 
   1212 std::optional<winsys::Window>
   1213 XConnection::get_icccm_window_client_leader(winsys::Window window)
   1214 {
   1215     winsys::Window leader = get_window_property(window, "WM_CLIENT_LEADER");
   1216 
   1217     if (!property_status_ok() || leader == None)
   1218         return std::nullopt;
   1219 
   1220     return leader;
   1221 }
   1222 
   1223 std::optional<winsys::Hints>
   1224 XConnection::get_icccm_window_hints(winsys::Window window)
   1225 {
   1226     bool success = false;
   1227     XWMHints hints;
   1228 
   1229     XWMHints* x_hints = XGetWMHints(mp_dpy, window);
   1230     if (x_hints) {
   1231         std::memcpy(&hints, x_hints, sizeof(XWMHints));
   1232         XFree(x_hints);
   1233         success = true;
   1234     }
   1235 
   1236     if (!success)
   1237         return std::nullopt;
   1238 
   1239     std::optional<winsys::IcccmWindowState> initial_state;
   1240 
   1241     switch (hints.initial_state) {
   1242     case NormalState: initial_state = winsys::IcccmWindowState::Normal; break;
   1243     case IconicState: initial_state = winsys::IcccmWindowState::Iconic; break;
   1244     default: return std::nullopt;
   1245     }
   1246 
   1247     std::optional<winsys::Window> group;
   1248 
   1249     if (hints.window_group)
   1250         group = hints.window_group;
   1251 
   1252     return winsys::Hints {
   1253         (hints.flags & XUrgencyHint) != 0,
   1254         hints.input,
   1255         initial_state,
   1256         group
   1257     };
   1258 }
   1259 
   1260 std::optional<winsys::SizeHints>
   1261 XConnection::get_icccm_window_size_hints(winsys::Window window, std::optional<winsys::Dim> min_window_dim)
   1262 {
   1263     XSizeHints sh;
   1264     if (XGetNormalHints(mp_dpy, window, &sh) == 0)
   1265         return std::nullopt;
   1266 
   1267     std::optional<winsys::Pos> pos = std::nullopt;
   1268     std::optional<unsigned> sh_min_width = std::nullopt;
   1269     std::optional<unsigned> sh_min_height = std::nullopt;
   1270     std::optional<unsigned> sh_base_width = std::nullopt;
   1271     std::optional<unsigned> sh_base_height = std::nullopt;
   1272     std::optional<unsigned> max_width = std::nullopt;
   1273     std::optional<unsigned> max_height = std::nullopt;
   1274 
   1275     bool by_user = (sh.flags & USPosition) != 0;
   1276 
   1277     if (sh.x > 0 || sh.y > 0)
   1278         pos = winsys::Pos { sh.x, sh.y };
   1279 
   1280     if ((sh.flags & PMinSize) != 0) {
   1281         if (sh.min_width > 0)
   1282             sh_min_width = sh.min_width;
   1283 
   1284         if (sh.min_height > 0)
   1285             sh_min_height = sh.min_height;
   1286     }
   1287 
   1288     if ((sh.flags & PBaseSize) != 0) {
   1289         if (sh.base_width > 0)
   1290             sh_base_width = sh.base_width;
   1291 
   1292         if (sh.base_height > 0)
   1293             sh_base_height = sh.base_height;
   1294     }
   1295 
   1296     if ((sh.flags & PMaxSize) != 0) {
   1297         if (sh.max_width > 0)
   1298             max_width = sh.max_width;
   1299 
   1300         if (sh.max_height > 0)
   1301             max_height = sh.max_height;
   1302     }
   1303 
   1304     std::optional<unsigned> min_width = sh_min_width ? sh_min_width : sh_base_width;
   1305     std::optional<unsigned> min_height = sh_min_height ? sh_min_height : sh_base_height;
   1306     std::optional<unsigned> base_width = sh_base_width ? sh_base_width : sh_min_width;
   1307     std::optional<unsigned> base_height = sh_base_height ? sh_base_height : sh_min_height;
   1308 
   1309     if (min_width) {
   1310         if (min_window_dim) {
   1311             if (min_width < min_window_dim->w)
   1312                 min_width = min_window_dim->w;
   1313         } else if (*min_width <= 0)
   1314             min_width = std::nullopt;
   1315     }
   1316 
   1317     if (min_height) {
   1318         if (min_window_dim) {
   1319             if (min_height < min_window_dim->h)
   1320                 min_height = min_window_dim->h;
   1321         } else if (*min_height <= 0)
   1322             min_height = std::nullopt;
   1323     }
   1324 
   1325     std::optional<unsigned> inc_width = std::nullopt;
   1326     std::optional<unsigned> inc_height = std::nullopt;
   1327 
   1328     if ((sh.flags & PResizeInc) != 0) {
   1329         if (sh.width_inc > 0 && sh.width_inc < 0xFFFF)
   1330             inc_width = sh.width_inc;
   1331 
   1332         if (sh.height_inc > 0 && sh.height_inc < 0xFFFF)
   1333             inc_height = sh.height_inc;
   1334     }
   1335 
   1336     std::optional<double> min_ratio = std::nullopt;
   1337     std::optional<winsys::Ratio> min_ratio_vulgar = std::nullopt;
   1338     std::optional<double> max_ratio = std::nullopt;
   1339     std::optional<winsys::Ratio> max_ratio_vulgar = std::nullopt;
   1340 
   1341     if ((sh.flags & PAspect) != 0) {
   1342         if (sh.min_aspect.x > 0 && sh.min_aspect.y > 0)
   1343             min_ratio = static_cast<double>(sh.min_aspect.x)
   1344                 / static_cast<double>(sh.min_aspect.y);
   1345 
   1346         min_ratio_vulgar = winsys::Ratio {
   1347             sh.min_aspect.x,
   1348             sh.min_aspect.y
   1349         };
   1350 
   1351         if (sh.max_aspect.x > 0 && sh.max_aspect.y > 0)
   1352             max_ratio = static_cast<double>(sh.max_aspect.x)
   1353                 / static_cast<double>(sh.max_aspect.y);
   1354 
   1355         max_ratio_vulgar = winsys::Ratio {
   1356             sh.max_aspect.x,
   1357             sh.max_aspect.y
   1358         };
   1359     }
   1360 
   1361     return winsys::SizeHints {
   1362         by_user,
   1363         pos,
   1364         min_width,
   1365         min_height,
   1366         max_width,
   1367         max_height,
   1368         base_width,
   1369         base_height,
   1370         inc_width,
   1371         inc_height,
   1372         min_ratio,
   1373         max_ratio,
   1374         min_ratio_vulgar,
   1375         max_ratio_vulgar,
   1376     };
   1377 }
   1378 
   1379 
   1380 // EWMH
   1381 void
   1382 XConnection::init_for_wm(std::vector<std::string> const& desktop_names)
   1383 {
   1384     if (!mp_dpy || !m_root)
   1385         Util::die("unable to set up window manager");
   1386 
   1387     check_otherwm();
   1388 
   1389     map_window(m_check_window);
   1390     stack_window_below(m_check_window, std::nullopt);
   1391 
   1392     XSetWindowAttributes wa;
   1393     wa.cursor = XCreateFontCursor(mp_dpy, XC_left_ptr);
   1394     XChangeWindowAttributes(mp_dpy, m_root, CWCursor, &wa);
   1395 
   1396     std::vector<std::string> wm_class{ m_wm_name, m_wm_name };
   1397 
   1398     replace_string_property(m_check_window, "_NET_WM_NAME", m_wm_name);
   1399     replace_stringlist_property(m_check_window, "_WM_CLASS", wm_class);
   1400     replace_card_property(m_check_window, "_NET_WM_PID", getpid());
   1401     replace_window_property(m_check_window, "_NET_SUPPORTING_WM_CHECK", m_check_window);
   1402 
   1403     replace_window_property(m_root, "_NET_SUPPORTING_WM_CHECK", m_check_window);
   1404     replace_string_property(m_root, "_NET_WM_NAME", m_wm_name);
   1405     replace_stringlist_property(m_root, "_WM_CLASS", wm_class);
   1406 
   1407     std::vector<Atom> supported_atoms;
   1408     supported_atoms.reserve(NetWMID::NetLast);
   1409 
   1410     for (NetWMID i = NetWMID::NetFirst; i < NetWMID::NetLast; ++i)
   1411         supported_atoms.push_back(get_netwm_atom(i));
   1412 
   1413     replace_atomlist_property(m_root, "_NET_SUPPORTED", supported_atoms);
   1414     replace_card_property(m_root, "_NET_WM_PID", getpid());
   1415     unset_window_property(m_root, "_NET_CLIENT_LIST");
   1416 
   1417     update_desktops(desktop_names);
   1418 }
   1419 
   1420 void
   1421 XConnection::set_current_desktop(Index index)
   1422 {
   1423     replace_card_property(m_root, "_NET_CURRENT_DESKTOP", index);
   1424 }
   1425 
   1426 void
   1427 XConnection::set_root_window_name(std::string const& name)
   1428 {
   1429     replace_string_property(m_root, "WM_NAME", name);
   1430 }
   1431 
   1432 void
   1433 XConnection::set_window_desktop(winsys::Window window, Index index)
   1434 {
   1435     replace_card_property(window, "_NET_WM_DESKTOP", index);
   1436 }
   1437 
   1438 void
   1439 XConnection::set_window_state(winsys::Window window, winsys::WindowState state, bool on)
   1440 {
   1441     Atom atom = get_atom_from_window_state(state);
   1442     if (atom == 0)
   1443         return;
   1444 
   1445     if (on) {
   1446         std::vector<winsys::WindowState> check_state{ state };
   1447         if (window_is_any_of_states(window, check_state))
   1448             return;
   1449 
   1450         append_atomlist_property(window, "_NET_WM_STATE", atom);
   1451     } else {
   1452         std::vector<Atom> atoms
   1453             = get_atomlist_property(window, "_NET_WM_STATE");
   1454 
   1455         if (!property_status_ok())
   1456             return;
   1457 
   1458         atoms.erase(
   1459             std::remove(
   1460                 atoms.begin(),
   1461                 atoms.end(),
   1462                 atom
   1463             ),
   1464             atoms.end()
   1465         );
   1466 
   1467         replace_atomlist_property(window, "_NET_WM_STATE", atoms);
   1468     }
   1469 }
   1470 
   1471 void
   1472 XConnection::set_window_frame_extents(winsys::Window window, winsys::Extents extents)
   1473 {
   1474     std::vector<unsigned long> frame_extents{
   1475         static_cast<unsigned>(extents.left),
   1476         static_cast<unsigned>(extents.right),
   1477         static_cast<unsigned>(extents.top),
   1478         static_cast<unsigned>(extents.bottom)
   1479     };
   1480 
   1481     replace_cardlist_property(window, "_NET_FRAME_EXTENTS", frame_extents);
   1482 }
   1483 
   1484 void
   1485 XConnection::set_desktop_geometry(std::vector<winsys::Region> const& geometries)
   1486 {
   1487     std::vector<unsigned long> values;
   1488     values.reserve(2 * geometries.size());
   1489 
   1490     for (auto& geometry : geometries) {
   1491         values.push_back(geometry.dim.w);
   1492         values.push_back(geometry.dim.h);
   1493     }
   1494 
   1495     replace_cardlist_property(m_root, "_NET_DESKTOP_GEOMETRY", values);
   1496 }
   1497 
   1498 void
   1499 XConnection::set_desktop_viewport(std::vector<winsys::Region> const& viewports)
   1500 {
   1501     std::vector<unsigned long> values;
   1502     values.reserve(2 * viewports.size());
   1503 
   1504     for (auto& viewport : viewports) {
   1505         values.push_back(viewport.pos.x);
   1506         values.push_back(viewport.pos.y);
   1507     }
   1508 
   1509     replace_cardlist_property(m_root, "_NET_DESKTOP_VIEWPORT", values);
   1510 }
   1511 
   1512 void
   1513 XConnection::set_workarea(std::vector<winsys::Region> const& workareas)
   1514 {
   1515     std::vector<unsigned long> values;
   1516     values.reserve(4 * workareas.size());
   1517 
   1518     for (auto& workarea : workareas) {
   1519         values.push_back(workarea.pos.x);
   1520         values.push_back(workarea.pos.y);
   1521         values.push_back(workarea.dim.w);
   1522         values.push_back(workarea.dim.h);
   1523     }
   1524 
   1525     replace_cardlist_property(m_root, "_NET_WORKAREA", values);
   1526 }
   1527 
   1528 void
   1529 XConnection::update_desktops(std::vector<std::string> const& desktop_names)
   1530 {
   1531     replace_card_property(m_root, "_NET_NUMBER_OF_DESKTOPS", desktop_names.size());
   1532     replace_stringlist_property(m_root, "_NET_DESKTOP_NAMES", desktop_names);
   1533 }
   1534 
   1535 void
   1536 XConnection::update_client_list(std::vector<winsys::Window> const& clients)
   1537 {
   1538     replace_windowlist_property(m_root, "_NET_CLIENT_LIST", clients);
   1539 }
   1540 
   1541 void
   1542 XConnection::update_client_list_stacking(std::vector<winsys::Window> const& clients)
   1543 {
   1544     replace_windowlist_property(m_root, "_NET_CLIENT_LIST_STACKING", clients);
   1545 }
   1546 
   1547 std::optional<std::vector<std::optional<winsys::Strut>>>
   1548 XConnection::get_window_strut(winsys::Window window)
   1549 {
   1550     std::optional<std::vector<std::optional<winsys::Strut>>> struts_partial
   1551         = get_window_strut_partial(window);
   1552 
   1553     if (struts_partial)
   1554         return struts_partial;
   1555 
   1556     std::vector<unsigned long> strut_widths = get_cardlist_property(window, "_NET_WM_STRUT");
   1557 
   1558     if (!property_status_ok() || strut_widths.empty())
   1559         return std::nullopt;
   1560 
   1561     std::vector<std::optional<winsys::Strut>> struts;
   1562     struts.reserve(4);
   1563 
   1564     for (std::size_t i = 0; i < strut_widths.size() && i < 4; ++i) {
   1565         std::optional<winsys::Strut> strut = std::nullopt;
   1566 
   1567         if (strut_widths[i] > 0)
   1568             strut = winsys::Strut {
   1569                 window,
   1570                 static_cast<int>(strut_widths[i])
   1571             };
   1572 
   1573         struts.push_back(strut);
   1574     }
   1575 
   1576     return struts;
   1577 }
   1578 
   1579 std::optional<std::vector<std::optional<winsys::Strut>>>
   1580 XConnection::get_window_strut_partial(winsys::Window window)
   1581 {
   1582     std::vector<unsigned long> strut_widths = get_cardlist_property(window, "_NET_WM_STRUT_PARTIAL");
   1583 
   1584     if (!property_status_ok() || strut_widths.empty())
   1585         return std::nullopt;
   1586 
   1587     std::vector<std::optional<winsys::Strut>> struts_partial;
   1588     struts_partial.reserve(4);
   1589 
   1590     for (std::size_t i = 0; i < strut_widths.size() && i < 4; ++i) {
   1591         std::optional<winsys::Strut> strut = std::nullopt;
   1592 
   1593         if (strut_widths[i] > 0)
   1594             strut = winsys::Strut {
   1595                 window,
   1596                 static_cast<int>(strut_widths[i])
   1597             };
   1598 
   1599         struts_partial.push_back(strut);
   1600     }
   1601 
   1602     return struts_partial;
   1603 }
   1604 
   1605 std::optional<Index>
   1606 XConnection::get_window_desktop(winsys::Window window)
   1607 {
   1608     Index index = get_card_property(window, "_NET_WM_DESKTOP");
   1609 
   1610     if (!property_status_ok())
   1611         return std::nullopt;
   1612 
   1613     return index;
   1614 }
   1615 
   1616 std::unordered_set<winsys::WindowType>
   1617 XConnection::get_window_types(winsys::Window window)
   1618 {
   1619     std::vector<Atom> window_type_atoms = get_atomlist_property(window, "_NET_WM_WINDOW_TYPE");
   1620 
   1621     if (!property_status_ok())
   1622         return {};
   1623 
   1624     std::unordered_set<winsys::WindowType> window_types{};
   1625 
   1626     for (Atom atom : window_type_atoms)
   1627         window_types.insert(get_window_type_from_atom(atom));
   1628 
   1629     return window_types;
   1630 }
   1631 
   1632 std::unordered_set<winsys::WindowState>
   1633 XConnection::get_window_states(winsys::Window window)
   1634 {
   1635     std::vector<Atom> window_state_atoms = get_atomlist_property(window, "_NET_WM_STATE");
   1636 
   1637     if (!property_status_ok())
   1638         return {};
   1639 
   1640     std::unordered_set<winsys::WindowState> window_states{};
   1641 
   1642     for (Atom atom : window_state_atoms)
   1643         window_states.insert(get_window_state_from_atom(atom));
   1644 
   1645     return window_states;
   1646 }
   1647 
   1648 bool
   1649 XConnection::window_is_fullscreen(winsys::Window window)
   1650 {
   1651     static const std::vector<winsys::WindowState> fullscreen_state{
   1652         winsys::WindowState::Fullscreen
   1653     };
   1654 
   1655     return window_is_any_of_states(window, fullscreen_state);
   1656 }
   1657 
   1658 bool
   1659 XConnection::window_is_above(winsys::Window window)
   1660 {
   1661     static const std::vector<winsys::WindowState> above_state{
   1662         winsys::WindowState::Above_
   1663     };
   1664 
   1665     return window_is_any_of_states(window, above_state);
   1666 }
   1667 
   1668 bool
   1669 XConnection::window_is_below(winsys::Window window)
   1670 {
   1671     static const std::vector<winsys::WindowState> below_state{
   1672         winsys::WindowState::Below_
   1673     };
   1674 
   1675     return window_is_any_of_states(window, below_state);
   1676 }
   1677 
   1678 bool
   1679 XConnection::window_is_sticky(winsys::Window window)
   1680 {
   1681     static const std::vector<winsys::WindowState> sticky_state{
   1682         winsys::WindowState::Sticky
   1683     };
   1684 
   1685     return window_is_any_of_states(window, sticky_state);
   1686 }
   1687 
   1688 
   1689 // IPC client
   1690 void
   1691 XConnection::init_for_client()
   1692 {
   1693 
   1694 }
   1695 
   1696 
   1697 void
   1698 XConnection::enable_substructure_events()
   1699 {
   1700     if (--m_substructure_level != 0)
   1701         return;
   1702 
   1703     if ((m_prev_root_mask & SubstructureNotifyMask) == 0)
   1704         return;
   1705 
   1706     XSelectInput(mp_dpy, m_root, m_prev_root_mask | SubstructureNotifyMask);
   1707     XFlush(mp_dpy);
   1708 }
   1709 
   1710 void
   1711 XConnection::disable_substructure_events()
   1712 {
   1713     if (++m_substructure_level != 1)
   1714         return;
   1715 
   1716     if ((m_prev_root_mask & SubstructureNotifyMask) == 0)
   1717         return;
   1718 
   1719     XSelectInput(mp_dpy, m_root, m_prev_root_mask & ~SubstructureNotifyMask);
   1720     XFlush(mp_dpy);
   1721 }
   1722 
   1723 void
   1724 XConnection::next_event(XEvent& event)
   1725 {
   1726     XNextEvent(mp_dpy, &event);
   1727 }
   1728 
   1729 bool
   1730 XConnection::typed_event(XEvent& event, int type)
   1731 {
   1732     return XCheckTypedEvent(mp_dpy, type, &event);
   1733 }
   1734 
   1735 void
   1736 XConnection::last_typed_event(XEvent& event, int type)
   1737 {
   1738     while (typed_event(event, type));
   1739 }
   1740 
   1741 void
   1742 XConnection::sync(bool discard)
   1743 {
   1744     XSync(mp_dpy, discard);
   1745 }
   1746 
   1747 int
   1748 XConnection::pending()
   1749 {
   1750     return XPending(mp_dpy);
   1751 }
   1752 
   1753 winsys::Window
   1754 XConnection::create_handle()
   1755 {
   1756     winsys::Window window = XCreateWindow(
   1757         mp_dpy, m_root,
   1758         -2, -2,
   1759         1, 1,
   1760         CopyFromParent, 0,
   1761         InputOnly,
   1762         CopyFromParent, 0, NULL
   1763     );
   1764 
   1765     flush();
   1766 
   1767     return window;
   1768 }
   1769 
   1770 
   1771 Atom
   1772 XConnection::get_atom(std::string const& name)
   1773 {
   1774     if (m_interned_atoms.count(name) > 0)
   1775         return m_interned_atoms.at(name);
   1776 
   1777     Atom atom = XInternAtom(mp_dpy, name.c_str(), False);
   1778     intern_atom(name, atom);
   1779     return atom;
   1780 }
   1781 
   1782 Atom
   1783 XConnection::get_netwm_atom(NetWMID const& id)
   1784 {
   1785     if (m_netwm_atoms.count(id) > 0)
   1786         return m_netwm_atoms.at(id);
   1787 
   1788     return NetWMID::NetLast;
   1789 }
   1790 
   1791 void
   1792 XConnection::intern_atom(std::string const& name, Atom atom)
   1793 {
   1794     m_interned_atoms[name] = atom;
   1795     m_atom_names[atom] = name;
   1796 }
   1797 
   1798 winsys::Key
   1799 XConnection::get_key(const std::size_t keycode)
   1800 {
   1801     if (m_keys.count(keycode) > 0)
   1802         return m_keys.at(keycode);
   1803 
   1804     return winsys::Key::Any;
   1805 }
   1806 
   1807 std::size_t
   1808 XConnection::get_keycode(const winsys::Key key)
   1809 {
   1810     if (m_keycodes.count(key) > 0)
   1811         return m_keycodes.at(key);
   1812 
   1813     unsigned keycode = 0;
   1814 
   1815     switch (key) {
   1816     case winsys::Key::BackSpace:        keycode = XKeysymToKeycode(mp_dpy, XK_BackSpace);            break;
   1817     case winsys::Key::Tab:              keycode = XKeysymToKeycode(mp_dpy, XK_Tab);                  break;
   1818     case winsys::Key::Clear:            keycode = XKeysymToKeycode(mp_dpy, XK_Clear);                break;
   1819     case winsys::Key::Return:           keycode = XKeysymToKeycode(mp_dpy, XK_Return);               break;
   1820     case winsys::Key::Shift:            keycode = XKeysymToKeycode(mp_dpy, XK_Shift_L);              break;
   1821     case winsys::Key::Control:          keycode = XKeysymToKeycode(mp_dpy, XK_Control_L);            break;
   1822     case winsys::Key::Alt:              keycode = XKeysymToKeycode(mp_dpy, XK_Alt_L);                break;
   1823     case winsys::Key::Super:            keycode = XKeysymToKeycode(mp_dpy, XK_Super_L);              break;
   1824     case winsys::Key::Menu:             keycode = XKeysymToKeycode(mp_dpy, XK_Menu);                 break;
   1825     case winsys::Key::Pause:            keycode = XKeysymToKeycode(mp_dpy, XK_Pause);                break;
   1826     case winsys::Key::CapsLock:         keycode = XKeysymToKeycode(mp_dpy, XK_Caps_Lock);            break;
   1827     case winsys::Key::Escape:           keycode = XKeysymToKeycode(mp_dpy, XK_Escape);               break;
   1828     case winsys::Key::Space:            keycode = XKeysymToKeycode(mp_dpy, XK_space);                break;
   1829     case winsys::Key::ExclamationMark:  keycode = XKeysymToKeycode(mp_dpy, XK_exclam);               break;
   1830     case winsys::Key::QuotationMark:    keycode = XKeysymToKeycode(mp_dpy, XK_quotedbl);             break;
   1831     case winsys::Key::QuestionMark:     keycode = XKeysymToKeycode(mp_dpy, XK_question);             break;
   1832     case winsys::Key::NumberSign:       keycode = XKeysymToKeycode(mp_dpy, XK_numbersign);           break;
   1833     case winsys::Key::DollarSign:       keycode = XKeysymToKeycode(mp_dpy, XK_dollar);               break;
   1834     case winsys::Key::PercentSign:      keycode = XKeysymToKeycode(mp_dpy, XK_percent);              break;
   1835     case winsys::Key::AtSign:           keycode = XKeysymToKeycode(mp_dpy, XK_at);                   break;
   1836     case winsys::Key::Ampersand:        keycode = XKeysymToKeycode(mp_dpy, XK_ampersand);            break;
   1837     case winsys::Key::Apostrophe:       keycode = XKeysymToKeycode(mp_dpy, XK_apostrophe);           break;
   1838     case winsys::Key::LeftParenthesis:  keycode = XKeysymToKeycode(mp_dpy, XK_parenleft);            break;
   1839     case winsys::Key::RightParenthesis: keycode = XKeysymToKeycode(mp_dpy, XK_parenright);           break;
   1840     case winsys::Key::LeftBracket:      keycode = XKeysymToKeycode(mp_dpy, XK_bracketleft);          break;
   1841     case winsys::Key::RightBracket:     keycode = XKeysymToKeycode(mp_dpy, XK_bracketright);         break;
   1842     case winsys::Key::LeftBrace:        keycode = XKeysymToKeycode(mp_dpy, XK_braceleft);            break;
   1843     case winsys::Key::RightBrace:       keycode = XKeysymToKeycode(mp_dpy, XK_braceright);           break;
   1844     case winsys::Key::Underscore:       keycode = XKeysymToKeycode(mp_dpy, XK_underscore);           break;
   1845     case winsys::Key::Grave:            keycode = XKeysymToKeycode(mp_dpy, XK_grave);                break;
   1846     case winsys::Key::Bar:              keycode = XKeysymToKeycode(mp_dpy, XK_bar);                  break;
   1847     case winsys::Key::Tilde:            keycode = XKeysymToKeycode(mp_dpy, XK_asciitilde);           break;
   1848     case winsys::Key::QuoteLeft:        keycode = XKeysymToKeycode(mp_dpy, XK_quoteleft);            break;
   1849     case winsys::Key::Asterisk:         keycode = XKeysymToKeycode(mp_dpy, XK_asterisk);             break;
   1850     case winsys::Key::Plus:             keycode = XKeysymToKeycode(mp_dpy, XK_plus);                 break;
   1851     case winsys::Key::Comma:            keycode = XKeysymToKeycode(mp_dpy, XK_comma);                break;
   1852     case winsys::Key::Minus:            keycode = XKeysymToKeycode(mp_dpy, XK_minus);                break;
   1853     case winsys::Key::Period:           keycode = XKeysymToKeycode(mp_dpy, XK_period);               break;
   1854     case winsys::Key::Slash:            keycode = XKeysymToKeycode(mp_dpy, XK_slash);                break;
   1855     case winsys::Key::BackSlash:        keycode = XKeysymToKeycode(mp_dpy, XK_backslash);            break;
   1856     case winsys::Key::Colon:            keycode = XKeysymToKeycode(mp_dpy, XK_colon);                break;
   1857     case winsys::Key::SemiColon:        keycode = XKeysymToKeycode(mp_dpy, XK_semicolon);            break;
   1858     case winsys::Key::Less:             keycode = XKeysymToKeycode(mp_dpy, XK_less);                 break;
   1859     case winsys::Key::Equal:            keycode = XKeysymToKeycode(mp_dpy, XK_equal);                break;
   1860     case winsys::Key::Greater:          keycode = XKeysymToKeycode(mp_dpy, XK_greater);              break;
   1861     case winsys::Key::PageUp:           keycode = XKeysymToKeycode(mp_dpy, XK_Prior);                break;
   1862     case winsys::Key::PageDown:         keycode = XKeysymToKeycode(mp_dpy, XK_Next);                 break;
   1863     case winsys::Key::End:              keycode = XKeysymToKeycode(mp_dpy, XK_End);                  break;
   1864     case winsys::Key::Home:             keycode = XKeysymToKeycode(mp_dpy, XK_Home);                 break;
   1865     case winsys::Key::Left:             keycode = XKeysymToKeycode(mp_dpy, XK_Left);                 break;
   1866     case winsys::Key::Up:               keycode = XKeysymToKeycode(mp_dpy, XK_Up);                   break;
   1867     case winsys::Key::Right:            keycode = XKeysymToKeycode(mp_dpy, XK_Right);                break;
   1868     case winsys::Key::Down:             keycode = XKeysymToKeycode(mp_dpy, XK_Down);                 break;
   1869     case winsys::Key::Select:           keycode = XKeysymToKeycode(mp_dpy, XK_Select);               break;
   1870     case winsys::Key::Print:            keycode = XKeysymToKeycode(mp_dpy, XK_Print);                break;
   1871     case winsys::Key::Execute:          keycode = XKeysymToKeycode(mp_dpy, XK_Execute);              break;
   1872     case winsys::Key::PrintScreen:      keycode = XKeysymToKeycode(mp_dpy, XK_Print);                break;
   1873     case winsys::Key::Insert:           keycode = XKeysymToKeycode(mp_dpy, XK_Insert);               break;
   1874     case winsys::Key::Delete:           keycode = XKeysymToKeycode(mp_dpy, XK_Delete);               break;
   1875     case winsys::Key::Help:             keycode = XKeysymToKeycode(mp_dpy, XK_Help);                 break;
   1876     case winsys::Key::Zero:             keycode = XKeysymToKeycode(mp_dpy, XK_0);                    break;
   1877     case winsys::Key::One:              keycode = XKeysymToKeycode(mp_dpy, XK_1);                    break;
   1878     case winsys::Key::Two:              keycode = XKeysymToKeycode(mp_dpy, XK_2);                    break;
   1879     case winsys::Key::Three:            keycode = XKeysymToKeycode(mp_dpy, XK_3);                    break;
   1880     case winsys::Key::Four:             keycode = XKeysymToKeycode(mp_dpy, XK_4);                    break;
   1881     case winsys::Key::Five:             keycode = XKeysymToKeycode(mp_dpy, XK_5);                    break;
   1882     case winsys::Key::Six:              keycode = XKeysymToKeycode(mp_dpy, XK_6);                    break;
   1883     case winsys::Key::Seven:            keycode = XKeysymToKeycode(mp_dpy, XK_7);                    break;
   1884     case winsys::Key::Eight:            keycode = XKeysymToKeycode(mp_dpy, XK_8);                    break;
   1885     case winsys::Key::Nine:             keycode = XKeysymToKeycode(mp_dpy, XK_9);                    break;
   1886     case winsys::Key::A:                keycode = XKeysymToKeycode(mp_dpy, XK_a);                    break;
   1887     case winsys::Key::B:                keycode = XKeysymToKeycode(mp_dpy, XK_b);                    break;
   1888     case winsys::Key::C:                keycode = XKeysymToKeycode(mp_dpy, XK_c);                    break;
   1889     case winsys::Key::D:                keycode = XKeysymToKeycode(mp_dpy, XK_d);                    break;
   1890     case winsys::Key::E:                keycode = XKeysymToKeycode(mp_dpy, XK_e);                    break;
   1891     case winsys::Key::F:                keycode = XKeysymToKeycode(mp_dpy, XK_f);                    break;
   1892     case winsys::Key::G:                keycode = XKeysymToKeycode(mp_dpy, XK_g);                    break;
   1893     case winsys::Key::H:                keycode = XKeysymToKeycode(mp_dpy, XK_h);                    break;
   1894     case winsys::Key::I:                keycode = XKeysymToKeycode(mp_dpy, XK_i);                    break;
   1895     case winsys::Key::J:                keycode = XKeysymToKeycode(mp_dpy, XK_j);                    break;
   1896     case winsys::Key::K:                keycode = XKeysymToKeycode(mp_dpy, XK_k);                    break;
   1897     case winsys::Key::L:                keycode = XKeysymToKeycode(mp_dpy, XK_l);                    break;
   1898     case winsys::Key::M:                keycode = XKeysymToKeycode(mp_dpy, XK_m);                    break;
   1899     case winsys::Key::N:                keycode = XKeysymToKeycode(mp_dpy, XK_n);                    break;
   1900     case winsys::Key::O:                keycode = XKeysymToKeycode(mp_dpy, XK_o);                    break;
   1901     case winsys::Key::P:                keycode = XKeysymToKeycode(mp_dpy, XK_p);                    break;
   1902     case winsys::Key::Q:                keycode = XKeysymToKeycode(mp_dpy, XK_q);                    break;
   1903     case winsys::Key::R:                keycode = XKeysymToKeycode(mp_dpy, XK_r);                    break;
   1904     case winsys::Key::S:                keycode = XKeysymToKeycode(mp_dpy, XK_s);                    break;
   1905     case winsys::Key::T:                keycode = XKeysymToKeycode(mp_dpy, XK_t);                    break;
   1906     case winsys::Key::U:                keycode = XKeysymToKeycode(mp_dpy, XK_u);                    break;
   1907     case winsys::Key::V:                keycode = XKeysymToKeycode(mp_dpy, XK_v);                    break;
   1908     case winsys::Key::W:                keycode = XKeysymToKeycode(mp_dpy, XK_w);                    break;
   1909     case winsys::Key::X:                keycode = XKeysymToKeycode(mp_dpy, XK_x);                    break;
   1910     case winsys::Key::Y:                keycode = XKeysymToKeycode(mp_dpy, XK_y);                    break;
   1911     case winsys::Key::Z:                keycode = XKeysymToKeycode(mp_dpy, XK_z);                    break;
   1912     case winsys::Key::NumPad0:          keycode = XKeysymToKeycode(mp_dpy, XK_KP_0);                 break;
   1913     case winsys::Key::NumPad1:          keycode = XKeysymToKeycode(mp_dpy, XK_KP_1);                 break;
   1914     case winsys::Key::NumPad2:          keycode = XKeysymToKeycode(mp_dpy, XK_KP_2);                 break;
   1915     case winsys::Key::NumPad3:          keycode = XKeysymToKeycode(mp_dpy, XK_KP_3);                 break;
   1916     case winsys::Key::NumPad4:          keycode = XKeysymToKeycode(mp_dpy, XK_KP_4);                 break;
   1917     case winsys::Key::NumPad5:          keycode = XKeysymToKeycode(mp_dpy, XK_KP_5);                 break;
   1918     case winsys::Key::NumPad6:          keycode = XKeysymToKeycode(mp_dpy, XK_KP_6);                 break;
   1919     case winsys::Key::NumPad7:          keycode = XKeysymToKeycode(mp_dpy, XK_KP_7);                 break;
   1920     case winsys::Key::NumPad8:          keycode = XKeysymToKeycode(mp_dpy, XK_KP_8);                 break;
   1921     case winsys::Key::NumPad9:          keycode = XKeysymToKeycode(mp_dpy, XK_KP_9);                 break;
   1922     case winsys::Key::Multiply:         keycode = XKeysymToKeycode(mp_dpy, XK_KP_Multiply);          break;
   1923     case winsys::Key::Add:              keycode = XKeysymToKeycode(mp_dpy, XK_KP_Add);               break;
   1924     case winsys::Key::Seperator:        keycode = XKeysymToKeycode(mp_dpy, XK_KP_Separator);         break;
   1925     case winsys::Key::Subtract:         keycode = XKeysymToKeycode(mp_dpy, XK_KP_Subtract);          break;
   1926     case winsys::Key::Decimal:          keycode = XKeysymToKeycode(mp_dpy, XK_KP_Decimal);           break;
   1927     case winsys::Key::Divide:           keycode = XKeysymToKeycode(mp_dpy, XK_KP_Divide);            break;
   1928     case winsys::Key::F1:               keycode = XKeysymToKeycode(mp_dpy, XK_F1);                   break;
   1929     case winsys::Key::F2:               keycode = XKeysymToKeycode(mp_dpy, XK_F2);                   break;
   1930     case winsys::Key::F3:               keycode = XKeysymToKeycode(mp_dpy, XK_F3);                   break;
   1931     case winsys::Key::F4:               keycode = XKeysymToKeycode(mp_dpy, XK_F4);                   break;
   1932     case winsys::Key::F5:               keycode = XKeysymToKeycode(mp_dpy, XK_F5);                   break;
   1933     case winsys::Key::F6:               keycode = XKeysymToKeycode(mp_dpy, XK_F6);                   break;
   1934     case winsys::Key::F7:               keycode = XKeysymToKeycode(mp_dpy, XK_F7);                   break;
   1935     case winsys::Key::F8:               keycode = XKeysymToKeycode(mp_dpy, XK_F8);                   break;
   1936     case winsys::Key::F9:               keycode = XKeysymToKeycode(mp_dpy, XK_F9);                   break;
   1937     case winsys::Key::F10:              keycode = XKeysymToKeycode(mp_dpy, XK_F10);                  break;
   1938     case winsys::Key::F11:              keycode = XKeysymToKeycode(mp_dpy, XK_F11);                  break;
   1939     case winsys::Key::F12:              keycode = XKeysymToKeycode(mp_dpy, XK_F12);                  break;
   1940     case winsys::Key::F13:              keycode = XKeysymToKeycode(mp_dpy, XK_F13);                  break;
   1941     case winsys::Key::F14:              keycode = XKeysymToKeycode(mp_dpy, XK_F14);                  break;
   1942     case winsys::Key::F15:              keycode = XKeysymToKeycode(mp_dpy, XK_F15);                  break;
   1943     case winsys::Key::F16:              keycode = XKeysymToKeycode(mp_dpy, XK_F16);                  break;
   1944     case winsys::Key::F17:              keycode = XKeysymToKeycode(mp_dpy, XK_F17);                  break;
   1945     case winsys::Key::F18:              keycode = XKeysymToKeycode(mp_dpy, XK_F18);                  break;
   1946     case winsys::Key::F19:              keycode = XKeysymToKeycode(mp_dpy, XK_F19);                  break;
   1947     case winsys::Key::F20:              keycode = XKeysymToKeycode(mp_dpy, XK_F20);                  break;
   1948     case winsys::Key::F21:              keycode = XKeysymToKeycode(mp_dpy, XK_F21);                  break;
   1949     case winsys::Key::F22:              keycode = XKeysymToKeycode(mp_dpy, XK_F22);                  break;
   1950     case winsys::Key::F23:              keycode = XKeysymToKeycode(mp_dpy, XK_F23);                  break;
   1951     case winsys::Key::F24:              keycode = XKeysymToKeycode(mp_dpy, XK_F24);                  break;
   1952     case winsys::Key::Numlock:          keycode = XKeysymToKeycode(mp_dpy, XK_Num_Lock);             break;
   1953     case winsys::Key::ScrollLock:       keycode = XKeysymToKeycode(mp_dpy, XK_Scroll_Lock);          break;
   1954     case winsys::Key::LeftShift:        keycode = XKeysymToKeycode(mp_dpy, XK_Shift_L);              break;
   1955     case winsys::Key::RightShift:       keycode = XKeysymToKeycode(mp_dpy, XK_Shift_R);              break;
   1956     case winsys::Key::LeftControl:      keycode = XKeysymToKeycode(mp_dpy, XK_Control_L);            break;
   1957     case winsys::Key::RightContol:      keycode = XKeysymToKeycode(mp_dpy, XK_Control_R);            break;
   1958     case winsys::Key::LeftAlt:          keycode = XKeysymToKeycode(mp_dpy, XK_Alt_L);                break;
   1959     case winsys::Key::RightAlt:         keycode = XKeysymToKeycode(mp_dpy, XK_Alt_R);                break;
   1960     case winsys::Key::LeftSuper:        keycode = XKeysymToKeycode(mp_dpy, XK_Super_L);              break;
   1961     case winsys::Key::RightSuper:       keycode = XKeysymToKeycode(mp_dpy, XK_Super_R);              break;
   1962     case winsys::Key::BrowserBack:      keycode = XKeysymToKeycode(mp_dpy, XF86XK_Back);             break;
   1963     case winsys::Key::BrowserForward:   keycode = XKeysymToKeycode(mp_dpy, XF86XK_Forward);          break;
   1964     case winsys::Key::BrowserRefresh:   keycode = XKeysymToKeycode(mp_dpy, XF86XK_Refresh);          break;
   1965     case winsys::Key::BrowserStop:      keycode = XKeysymToKeycode(mp_dpy, XF86XK_Close);            break;
   1966     case winsys::Key::BrowserSearch:    keycode = XKeysymToKeycode(mp_dpy, XF86XK_Search);           break;
   1967     case winsys::Key::BrowserFavorites: keycode = XKeysymToKeycode(mp_dpy, XF86XK_Favorites);        break;
   1968     case winsys::Key::BrowserHome:      keycode = XKeysymToKeycode(mp_dpy, XF86XK_HomePage);         break;
   1969     case winsys::Key::VolumeMute:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_AudioMute);        break;
   1970     case winsys::Key::VolumeDown:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_AudioLowerVolume); break;
   1971     case winsys::Key::VolumeUp:         keycode = XKeysymToKeycode(mp_dpy, XF86XK_AudioRaiseVolume); break;
   1972     case winsys::Key::MicMute:          keycode = XKeysymToKeycode(mp_dpy, XF86XK_AudioMicMute);     break;
   1973     case winsys::Key::NextTrack:        keycode = XKeysymToKeycode(mp_dpy, XF86XK_AudioNext);        break;
   1974     case winsys::Key::PreviousTrack:    keycode = XKeysymToKeycode(mp_dpy, XF86XK_AudioPrev);        break;
   1975     case winsys::Key::StopMedia:        keycode = XKeysymToKeycode(mp_dpy, XF86XK_AudioStop);        break;
   1976     case winsys::Key::PlayPause:        keycode = XKeysymToKeycode(mp_dpy, XF86XK_AudioPlay);        break;
   1977     case winsys::Key::LaunchMail:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_Mail);             break;
   1978     case winsys::Key::SelectMedia:      keycode = XKeysymToKeycode(mp_dpy, XF86XK_AudioMedia);       break;
   1979     case winsys::Key::LaunchAppA:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_LaunchA);          break;
   1980     case winsys::Key::LaunchAppB:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_LaunchB);          break;
   1981     case winsys::Key::LaunchAppC:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_LaunchC);          break;
   1982     case winsys::Key::LaunchAppD:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_LaunchD);          break;
   1983     case winsys::Key::LaunchAppE:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_LaunchE);          break;
   1984     case winsys::Key::LaunchAppF:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_LaunchF);          break;
   1985     case winsys::Key::LaunchApp0:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_Launch0);          break;
   1986     case winsys::Key::LaunchApp1:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_Launch1);          break;
   1987     case winsys::Key::LaunchApp2:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_Launch2);          break;
   1988     case winsys::Key::LaunchApp3:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_Launch3);          break;
   1989     case winsys::Key::LaunchApp4:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_Launch4);          break;
   1990     case winsys::Key::LaunchApp5:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_Launch5);          break;
   1991     case winsys::Key::LaunchApp6:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_Launch6);          break;
   1992     case winsys::Key::LaunchApp7:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_Launch7);          break;
   1993     case winsys::Key::LaunchApp8:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_Launch8);          break;
   1994     case winsys::Key::LaunchApp9:       keycode = XKeysymToKeycode(mp_dpy, XF86XK_Launch9);          break;
   1995     case winsys::Key::BrightnessDown:   keycode = XKeysymToKeycode(mp_dpy, XKB_KEY_XF86MonBrightnessDown); break;
   1996     case winsys::Key::BrightnessUp:     keycode = XKeysymToKeycode(mp_dpy, XKB_KEY_XF86MonBrightnessDown); break;
   1997     case winsys::Key::KeyboardBrightnessDown: keycode = XKeysymToKeycode(mp_dpy, XKB_KEY_XF86KbdBrightnessDown); break;
   1998     case winsys::Key::KeyboardBrightnessUp:   keycode = XKeysymToKeycode(mp_dpy, XKB_KEY_XF86KbdBrightnessDown); break;
   1999     case winsys::Key::Any: return 0;
   2000     }
   2001 
   2002     intern_key(keycode, key);
   2003     return keycode;
   2004 }
   2005 
   2006 void
   2007 XConnection::intern_key(const unsigned keycode, const winsys::Key key)
   2008 {
   2009     m_keys[keycode] = key;
   2010     m_keycodes[key] = keycode;
   2011 }
   2012 
   2013 winsys::Button
   2014 XConnection::get_button(const unsigned buttoncode) const
   2015 {
   2016     switch (buttoncode) {
   2017     case 1: return winsys::Button::Left;
   2018     case 2: return winsys::Button::Middle;
   2019     case 3: return winsys::Button::Right;
   2020     case 4: return winsys::Button::ScrollUp;
   2021     case 5: return winsys::Button::ScrollDown;
   2022     case 6: return winsys::Button::Backward;
   2023     case 7: return winsys::Button::Forward;
   2024     default: return winsys::Button::Left;
   2025     }
   2026 }
   2027 
   2028 unsigned
   2029 XConnection::get_buttoncode(const winsys::Button button) const
   2030 {
   2031     switch (button) {
   2032     case winsys::Button::Left:       return 1;
   2033     case winsys::Button::Middle:     return 2;
   2034     case winsys::Button::Right:      return 3;
   2035     case winsys::Button::ScrollUp:   return 4;
   2036     case winsys::Button::ScrollDown: return 5;
   2037     case winsys::Button::Backward:   return 6;
   2038     case winsys::Button::Forward:    return 7;
   2039     default: return 0;
   2040     }
   2041 }
   2042 
   2043 winsys::WindowState
   2044 XConnection::get_window_state_from_atom(Atom atom)
   2045 {
   2046     if (atom == get_atom("_NET_WM_STATE_MODAL"))             return winsys::WindowState::Modal;
   2047     if (atom == get_atom("_NET_WM_STATE_STICKY"))            return winsys::WindowState::Sticky;
   2048     if (atom == get_atom("_NET_WM_STATE_MAXIMIZEDVERT"))     return winsys::WindowState::MaximizedVert;
   2049     if (atom == get_atom("_NET_WM_STATE_MAXIMIZEDHORZ"))     return winsys::WindowState::MaximizedHorz;
   2050     if (atom == get_atom("_NET_WM_STATE_SHADED"))            return winsys::WindowState::Shaded;
   2051     if (atom == get_atom("_NET_WM_STATE_SKIPTASKBAR"))       return winsys::WindowState::SkipTaskbar;
   2052     if (atom == get_atom("_NET_WM_STATE_SKIPPAGER"))         return winsys::WindowState::SkipPager;
   2053     if (atom == get_atom("_NET_WM_STATE_HIDDEN"))            return winsys::WindowState::Hidden;
   2054     if (atom == get_atom("_NET_WM_STATE_FULLSCREEN"))        return winsys::WindowState::Fullscreen;
   2055     if (atom == get_atom("_NET_WM_STATE_ABOVE"))             return winsys::WindowState::Above_;
   2056     if (atom == get_atom("_NET_WM_STATE_BELOW"))             return winsys::WindowState::Below_;
   2057     if (atom == get_atom("_NET_WM_STATE_DEMANDS_ATTENTION")) return winsys::WindowState::DemandsAttention;
   2058     return winsys::WindowState::Hidden;
   2059 }
   2060 
   2061 winsys::WindowType
   2062 XConnection::get_window_type_from_atom(Atom atom)
   2063 {
   2064     if (atom == get_atom("_NET_WM_WINDOW_TYPE_DESKTOP"))      return winsys::WindowType::Desktop;
   2065     if (atom == get_atom("_NET_WM_WINDOW_TYPE_DOCK"))         return winsys::WindowType::Dock;
   2066     if (atom == get_atom("_NET_WM_WINDOW_TYPE_TOOLBAR"))      return winsys::WindowType::Toolbar;
   2067     if (atom == get_atom("_NET_WM_WINDOW_TYPE_MENU"))         return winsys::WindowType::Menu;
   2068     if (atom == get_atom("_NET_WM_WINDOW_TYPE_UTILITY"))      return winsys::WindowType::Utility;
   2069     if (atom == get_atom("_NET_WM_WINDOW_TYPE_SPLASH"))       return winsys::WindowType::Splash;
   2070     if (atom == get_atom("_NET_WM_WINDOW_TYPE_DIALOG"))       return winsys::WindowType::Dialog;
   2071     if (atom == get_atom("_NET_WM_WINDOW_TYPE_DROPDOWNMENU")) return winsys::WindowType::DropdownMenu;
   2072     if (atom == get_atom("_NET_WM_WINDOW_TYPE_POPUPMENU"))    return winsys::WindowType::PopupMenu;
   2073     if (atom == get_atom("_NET_WM_WINDOW_TYPE_TOOLTIP"))      return winsys::WindowType::Tooltip;
   2074     if (atom == get_atom("_NET_WM_WINDOW_TYPE_NOTIFICATION")) return winsys::WindowType::Notification;
   2075     if (atom == get_atom("_NET_WM_WINDOW_TYPE_COMBO"))        return winsys::WindowType::Combo;
   2076     if (atom == get_atom("_NET_WM_WINDOW_TYPE_DND"))          return winsys::WindowType::Dnd;
   2077     if (atom == get_atom("_NET_WM_WINDOW_TYPE_NORMAL"))       return winsys::WindowType::Normal;
   2078     return winsys::WindowType::Normal;
   2079 }
   2080 
   2081 Atom
   2082 XConnection::get_atom_from_window_state(winsys::WindowState state)
   2083 {
   2084     switch (state) {
   2085     case winsys::WindowState::Modal:            return get_atom("_NET_WM_STATE_MODAL");
   2086     case winsys::WindowState::Sticky:           return get_atom("_NET_WM_STATE_STICKY");
   2087     case winsys::WindowState::MaximizedVert:    return get_atom("_NET_WM_STATE_MAXIMIZEDVERT");
   2088     case winsys::WindowState::MaximizedHorz:    return get_atom("_NET_WM_STATE_MAXIMIZEDHORZ");
   2089     case winsys::WindowState::Shaded:           return get_atom("_NET_WM_STATE_SHADED");
   2090     case winsys::WindowState::SkipTaskbar:      return get_atom("_NET_WM_STATE_SKIPTASKBAR");
   2091     case winsys::WindowState::SkipPager:        return get_atom("_NET_WM_STATE_SKIPPAGER");
   2092     case winsys::WindowState::Hidden:           return get_atom("_NET_WM_STATE_HIDDEN");
   2093     case winsys::WindowState::Fullscreen:       return get_atom("_NET_WM_STATE_FULLSCREEN");
   2094     case winsys::WindowState::Above_:           return get_atom("_NET_WM_STATE_ABOVE");
   2095     case winsys::WindowState::Below_:           return get_atom("_NET_WM_STATE_BELOW");
   2096     case winsys::WindowState::DemandsAttention: return get_atom("_NET_WM_STATE_DEMANDS_ATTENTION");
   2097     default: return 0;
   2098     }
   2099 }
   2100 
   2101 Atom
   2102 XConnection::get_atom_from_window_type(winsys::WindowType type)
   2103 {
   2104     switch (type) {
   2105     case winsys::WindowType::Desktop:      return get_atom("_NET_WM_WINDOW_TYPE_DESKTOP");
   2106     case winsys::WindowType::Dock:         return get_atom("_NET_WM_WINDOW_TYPE_DOCK");
   2107     case winsys::WindowType::Toolbar:      return get_atom("_NET_WM_WINDOW_TYPE_TOOLBAR");
   2108     case winsys::WindowType::Menu:         return get_atom("_NET_WM_WINDOW_TYPE_MENU");
   2109     case winsys::WindowType::Utility:      return get_atom("_NET_WM_WINDOW_TYPE_UTILITY");
   2110     case winsys::WindowType::Splash:       return get_atom("_NET_WM_WINDOW_TYPE_SPLASH");
   2111     case winsys::WindowType::Dialog:       return get_atom("_NET_WM_WINDOW_TYPE_DIALOG");
   2112     case winsys::WindowType::DropdownMenu: return get_atom("_NET_WM_WINDOW_TYPE_DROPDOWNMENU");
   2113     case winsys::WindowType::PopupMenu:    return get_atom("_NET_WM_WINDOW_TYPE_POPUPMENU");
   2114     case winsys::WindowType::Tooltip:      return get_atom("_NET_WM_WINDOW_TYPE_TOOLTIP");
   2115     case winsys::WindowType::Notification: return get_atom("_NET_WM_WINDOW_TYPE_NOTIFICATION");
   2116     case winsys::WindowType::Combo:        return get_atom("_NET_WM_WINDOW_TYPE_COMBO");
   2117     case winsys::WindowType::Dnd:          return get_atom("_NET_WM_WINDOW_TYPE_DND");
   2118     case winsys::WindowType::Normal:       return get_atom("_NET_WM_WINDOW_TYPE_NORMAL");
   2119     default: return get_atom("_NET_WM_WINDOW_TYPE_NORMAL");
   2120     }
   2121 }
   2122 
   2123 bool
   2124 XConnection::property_status_ok()
   2125 {
   2126     bool status_ok = m_property_status == 0;
   2127     m_property_status = 0;
   2128 
   2129     return status_ok;
   2130 }
   2131 
   2132 bool
   2133 XConnection::has_atom_property(winsys::Window window, Atom atom)
   2134 {
   2135     int _i;
   2136     unsigned long _ul;
   2137     unsigned char* ucp = nullptr;
   2138     Atom returned_type = None;
   2139     unsigned long n_items_returned = 0;
   2140 
   2141     return (XGetWindowProperty(mp_dpy, window, atom,
   2142         0L, 32, False, XA_ATOM,
   2143         &returned_type, &_i, &n_items_returned,
   2144         &_ul, &ucp) == Success
   2145         && ucp && XFree(ucp)
   2146         && returned_type == XA_ATOM
   2147         && n_items_returned > 0
   2148     );
   2149 }
   2150 
   2151 Atom
   2152 XConnection::get_atom_property(winsys::Window window, std::string const& name)
   2153 {
   2154     int _i;
   2155     unsigned long n;
   2156     unsigned long _ul;
   2157     unsigned char* ucp = nullptr;
   2158     Atom _a = None;
   2159     Atom atom = None;
   2160 
   2161     m_property_status = XGetWindowProperty(
   2162         mp_dpy,
   2163         window,
   2164         get_atom(name),
   2165         0L, 32, False,
   2166         XA_ATOM,
   2167         &_a, &_i, &n, &_ul,
   2168         &ucp
   2169     );
   2170 
   2171 	if (m_property_status == Success && ucp) {
   2172 		atom = *(Atom*)ucp;
   2173 		XFree(ucp);
   2174 	} else
   2175         m_property_status = !Success;
   2176 
   2177 	return atom;
   2178 }
   2179 
   2180 void
   2181 XConnection::replace_atom_property(winsys::Window window, std::string const& name, Atom atom)
   2182 {
   2183     XChangeProperty(
   2184         mp_dpy,
   2185         window,
   2186         get_atom(name),
   2187         XA_ATOM,
   2188         32,
   2189         PropModeReplace,
   2190         reinterpret_cast<const unsigned char*>(&atom),
   2191         1
   2192     );
   2193 }
   2194 
   2195 void
   2196 XConnection::unset_atom_property(winsys::Window window, std::string const& name)
   2197 {
   2198     XChangeProperty(
   2199         mp_dpy,
   2200         window,
   2201         get_atom(name),
   2202         XA_ATOM,
   2203         32,
   2204         PropModeReplace,
   2205         reinterpret_cast<const unsigned char*>(0),
   2206         0
   2207     );
   2208 }
   2209 
   2210 bool
   2211 XConnection::has_atomlist_property(winsys::Window window, Atom atom)
   2212 {
   2213     int _i;
   2214     unsigned long _ul;
   2215     unsigned char* ucp = nullptr;
   2216     Atom returned_type = None;
   2217     unsigned long n_items_returned = 0;
   2218 
   2219     return (XGetWindowProperty(mp_dpy, window, atom,
   2220         0L, 32, False, XA_ATOM,
   2221         &returned_type, &_i, &n_items_returned,
   2222         &_ul, &ucp) == Success
   2223         && ucp && XFree(ucp)
   2224         && returned_type == XA_ATOM
   2225         && n_items_returned > 0
   2226     );
   2227 }
   2228 
   2229 std::vector<Atom>
   2230 XConnection::get_atomlist_property(winsys::Window window, std::string const& name)
   2231 {
   2232     int _i;
   2233     unsigned long n;
   2234     unsigned long _ul;
   2235     unsigned char* ucp = nullptr;
   2236     Atom _a = None;
   2237     std::vector<Atom> atomlist{};
   2238 
   2239     m_property_status = XGetWindowProperty(
   2240         mp_dpy,
   2241         window,
   2242         get_atom(name),
   2243         0L, 32, False,
   2244         XA_ATOM,
   2245         &_a, &_i, &n, &_ul,
   2246         &ucp
   2247     );
   2248 
   2249 	if (m_property_status == Success && ucp) {
   2250         for (std::size_t i = 0; i < n; ++i)
   2251             atomlist.push_back(((Atom*)ucp)[i]);
   2252 
   2253 		XFree(ucp);
   2254 	} else
   2255         m_property_status = !Success;
   2256 
   2257 	return atomlist;
   2258 }
   2259 
   2260 void
   2261 XConnection::replace_atomlist_property(winsys::Window window, std::string const& name, std::vector<Atom> const& atomlist)
   2262 {
   2263     XChangeProperty(
   2264         mp_dpy,
   2265         window,
   2266         get_atom(name),
   2267         XA_ATOM,
   2268         32,
   2269         PropModeReplace,
   2270         reinterpret_cast<const unsigned char*>(atomlist.data()),
   2271         atomlist.size()
   2272     );
   2273 }
   2274 
   2275 void
   2276 XConnection::append_atomlist_property(winsys::Window window, std::string const& name, Atom atom)
   2277 {
   2278     XChangeProperty(
   2279         mp_dpy,
   2280         window,
   2281         get_atom(name),
   2282         XA_ATOM,
   2283         32,
   2284         PropModeAppend,
   2285         reinterpret_cast<const unsigned char*>(&atom),
   2286         1
   2287     );
   2288 }
   2289 
   2290 void
   2291 XConnection::unset_atomlist_property(winsys::Window window, std::string const& name)
   2292 {
   2293     XChangeProperty(
   2294         mp_dpy,
   2295         window,
   2296         get_atom(name),
   2297         XA_ATOM,
   2298         32,
   2299         PropModeReplace,
   2300         reinterpret_cast<const unsigned char*>(0),
   2301         0
   2302     );
   2303 }
   2304 
   2305 bool
   2306 XConnection::has_window_property(winsys::Window window, Atom atom)
   2307 {
   2308     int _i;
   2309     unsigned long _ul;
   2310     unsigned char* ucp = nullptr;
   2311     Atom returned_type = None;
   2312     unsigned long n_items_returned = 0;
   2313 
   2314     return (XGetWindowProperty(mp_dpy, window, atom,
   2315         0L, 32, False, XA_WINDOW,
   2316         &returned_type, &_i, &n_items_returned,
   2317         &_ul, &ucp) == Success
   2318         && ucp && XFree(ucp)
   2319         && returned_type == XA_WINDOW
   2320         && n_items_returned > 0
   2321     );
   2322 }
   2323 
   2324 winsys::Window
   2325 XConnection::get_window_property(winsys::Window window, std::string const& name)
   2326 {
   2327     int _i;
   2328     unsigned long n;
   2329     unsigned long _ul;
   2330     unsigned char* ucp = nullptr;
   2331     Atom _a = None;
   2332     winsys::Window window_ = None;
   2333 
   2334     m_property_status = XGetWindowProperty(
   2335         mp_dpy,
   2336         window,
   2337         get_atom(name),
   2338         0L, 32, False,
   2339         XA_WINDOW,
   2340         &_a, &_i, &n, &_ul,
   2341         &ucp
   2342     );
   2343 
   2344 	if (m_property_status == Success && ucp) {
   2345 		window_ = *(winsys::Window*)ucp;
   2346 		XFree(ucp);
   2347 	} else
   2348         m_property_status = !Success;
   2349 
   2350 	return window_;
   2351 }
   2352 
   2353 void
   2354 XConnection::replace_window_property(winsys::Window window, std::string const& name, winsys::Window window_)
   2355 {
   2356     XChangeProperty(
   2357         mp_dpy,
   2358         window,
   2359         get_atom(name),
   2360         XA_WINDOW,
   2361         32,
   2362         PropModeReplace,
   2363         reinterpret_cast<const unsigned char*>(&window_),
   2364         1
   2365     );
   2366 }
   2367 
   2368 void
   2369 XConnection::unset_window_property(winsys::Window window, std::string const& name)
   2370 {
   2371     XChangeProperty(
   2372         mp_dpy,
   2373         window,
   2374         get_atom(name),
   2375         XA_WINDOW,
   2376         32,
   2377         PropModeReplace,
   2378         reinterpret_cast<const unsigned char*>(0),
   2379         0
   2380     );
   2381 }
   2382 
   2383 bool
   2384 XConnection::has_windowlist_property(winsys::Window window, Atom atom)
   2385 {
   2386     int _i;
   2387     unsigned long _ul;
   2388     unsigned char* ucp = nullptr;
   2389     Atom returned_type = None;
   2390     unsigned long n_items_returned = 0;
   2391 
   2392     return (XGetWindowProperty(mp_dpy, window, atom,
   2393         0L, 32, False, XA_WINDOW,
   2394         &returned_type, &_i, &n_items_returned,
   2395         &_ul, &ucp) == Success
   2396         && ucp && XFree(ucp)
   2397         && returned_type == XA_ATOM
   2398         && n_items_returned > 0
   2399     );
   2400 }
   2401 
   2402 std::vector<winsys::Window>
   2403 XConnection::get_windowlist_property(winsys::Window window, std::string const& name)
   2404 {
   2405     int _i;
   2406     unsigned long n;
   2407     unsigned long _ul;
   2408     unsigned char* ucp = nullptr;
   2409     Atom _a = None;
   2410     std::vector<winsys::Window> windowlist{};
   2411 
   2412     m_property_status = XGetWindowProperty(
   2413         mp_dpy,
   2414         window,
   2415         get_atom(name),
   2416         0L, 32, False,
   2417         XA_WINDOW,
   2418         &_a, &_i, &n, &_ul,
   2419         &ucp
   2420     );
   2421 
   2422 	if (m_property_status == Success && ucp) {
   2423         for (std::size_t i = 0; i < n; ++i)
   2424             windowlist.push_back(((winsys::Window*)ucp)[i]);
   2425 
   2426 		XFree(ucp);
   2427 	} else
   2428         m_property_status = !Success;
   2429 
   2430 	return windowlist;
   2431 }
   2432 
   2433 void
   2434 XConnection::replace_windowlist_property(winsys::Window window, std::string const& name, std::vector<winsys::Window> const& windowlist)
   2435 {
   2436     XChangeProperty(
   2437         mp_dpy,
   2438         window,
   2439         get_atom(name),
   2440         XA_WINDOW,
   2441         32,
   2442         PropModeReplace,
   2443         reinterpret_cast<const unsigned char*>(windowlist.data()),
   2444         windowlist.size()
   2445     );
   2446 }
   2447 
   2448 void
   2449 XConnection::append_windowlist_property(winsys::Window window, std::string const& name, winsys::Window window_)
   2450 {
   2451     XChangeProperty(
   2452         mp_dpy,
   2453         window,
   2454         get_atom(name),
   2455         XA_WINDOW,
   2456         32,
   2457         PropModeAppend,
   2458         reinterpret_cast<const unsigned char*>(&window_),
   2459         1
   2460     );
   2461 }
   2462 
   2463 void
   2464 XConnection::unset_windowlist_property(winsys::Window window, std::string const& name)
   2465 {
   2466     XChangeProperty(
   2467         mp_dpy,
   2468         window,
   2469         get_atom(name),
   2470         XA_WINDOW,
   2471         32,
   2472         PropModeReplace,
   2473         reinterpret_cast<const unsigned char*>(0),
   2474         0
   2475     );
   2476 }
   2477 
   2478 bool
   2479 XConnection::has_string_property(winsys::Window window, Atom atom)
   2480 {
   2481     int _i;
   2482     unsigned long _ul;
   2483     unsigned char* ucp = nullptr;
   2484     Atom returned_type = None;
   2485     unsigned long n_items_returned = 0;
   2486 
   2487     return (XGetWindowProperty(mp_dpy, window, atom,
   2488         0L, 8, False, get_atom("UTF8_STRING"),
   2489         &returned_type, &_i, &n_items_returned,
   2490         &_ul, &ucp) == Success
   2491         && ucp && XFree(ucp)
   2492         && returned_type == get_atom("UTF8_STRING")
   2493         && n_items_returned > 0
   2494     );
   2495 }
   2496 
   2497 std::string
   2498 XConnection::get_string_property(winsys::Window window, std::string const& name)
   2499 {
   2500     int _i;
   2501     unsigned long n;
   2502     unsigned long _ul;
   2503     unsigned char* ucp = nullptr;
   2504     Atom _a = None;
   2505     std::string str_{};
   2506 
   2507     m_property_status = XGetWindowProperty(
   2508         mp_dpy,
   2509         window,
   2510         get_atom(name),
   2511         0L, 8, False,
   2512         get_atom("UTF8_STRING"),
   2513         &_a, &_i, &n, &_ul,
   2514         &ucp
   2515     );
   2516 
   2517 	if (m_property_status == Success && ucp) {
   2518 		str_ = std::string((char*)ucp);
   2519 		XFree(ucp);
   2520 	} else
   2521         m_property_status = !Success;
   2522 
   2523 	return str_;
   2524 }
   2525 
   2526 void
   2527 XConnection::replace_string_property(winsys::Window window, std::string const& name, std::string const& str_)
   2528 {
   2529     XChangeProperty(
   2530         mp_dpy,
   2531         window,
   2532         get_atom(name),
   2533         get_atom("UTF8_STRING"),
   2534         8,
   2535         PropModeReplace,
   2536         reinterpret_cast<const unsigned char*>(str_.c_str()),
   2537         str_.length()
   2538     );
   2539 }
   2540 
   2541 void
   2542 XConnection::unset_string_property(winsys::Window window, std::string const& name)
   2543 {
   2544     XChangeProperty(
   2545         mp_dpy,
   2546         window,
   2547         get_atom(name),
   2548         get_atom("UTF8_STRING"),
   2549         8,
   2550         PropModeReplace,
   2551         reinterpret_cast<const unsigned char*>(0),
   2552         0
   2553     );
   2554 }
   2555 
   2556 bool
   2557 XConnection::has_stringlist_property(winsys::Window window, Atom atom)
   2558 {
   2559     int _i;
   2560     unsigned long _ul;
   2561     unsigned char* ucp = nullptr;
   2562     Atom returned_type = None;
   2563     unsigned long n_items_returned = 0;
   2564 
   2565     return (XGetWindowProperty(mp_dpy, window, atom,
   2566         0L, 8, False, get_atom("UTF8_STRING"),
   2567         &returned_type, &_i, &n_items_returned,
   2568         &_ul, &ucp) == Success
   2569         && ucp && XFree(ucp)
   2570         && returned_type == get_atom("UTF8_STRING")
   2571         && n_items_returned > 0
   2572     );
   2573 }
   2574 
   2575 std::vector<std::string>
   2576 XConnection::get_stringlist_property(winsys::Window window, std::string const& name)
   2577 {
   2578     int _i;
   2579     unsigned long n;
   2580     unsigned long _ul;
   2581     unsigned char* ucp = nullptr;
   2582     Atom _a = None;
   2583     std::vector<std::string> stringlist{};
   2584 
   2585     m_property_status = XGetWindowProperty(
   2586         mp_dpy,
   2587         window,
   2588         get_atom(name),
   2589         0L, 8, False,
   2590         get_atom("UTF8_STRING"),
   2591         &_a, &_i, &n, &_ul,
   2592         &ucp
   2593     );
   2594 
   2595 	if (m_property_status == Success && ucp) {
   2596         for (std::size_t i = 0; i < n; ++i)
   2597             stringlist.emplace_back(std::string(((char**)ucp)[i]));
   2598 
   2599 		XFree(ucp);
   2600 	} else
   2601         m_property_status = !Success;
   2602 
   2603 	return stringlist;
   2604 }
   2605 
   2606 void
   2607 XConnection::replace_stringlist_property(winsys::Window window, std::string const& name, std::vector<std::string> const& stringlist)
   2608 {
   2609     static char buffer[1024];
   2610 
   2611     std::size_t i = 0;
   2612     for (auto& str_ : stringlist) {
   2613         if (i == 1023)
   2614             goto append;
   2615 
   2616         for (auto& c : str_) {
   2617             if (i == 1023)
   2618                 goto append;
   2619 
   2620             buffer[i++] = c;
   2621         }
   2622 
   2623         buffer[i++] = '\0';
   2624     }
   2625 
   2626 append:
   2627     buffer[1023] = '\0';
   2628 
   2629     XChangeProperty(
   2630         mp_dpy,
   2631         window,
   2632         get_atom(name),
   2633         get_atom("UTF8_STRING"),
   2634         8,
   2635         PropModeAppend,
   2636         reinterpret_cast<const unsigned char*>(buffer),
   2637         i
   2638     );
   2639 }
   2640 
   2641 void
   2642 XConnection::append_stringlist_property(winsys::Window window, std::string const& name, std::string const& str_)
   2643 {
   2644     static char buffer[1024];
   2645 
   2646     std::size_t i = 0;
   2647     for (auto& c : str_) {
   2648         if (i == 1023)
   2649             break;
   2650 
   2651         buffer[i++] = c;
   2652     }
   2653 
   2654     buffer[1023] = '\0';
   2655 
   2656     XChangeProperty(
   2657         mp_dpy,
   2658         window,
   2659         get_atom(name),
   2660         get_atom("UTF8_STRING"),
   2661         8,
   2662         PropModeAppend,
   2663         reinterpret_cast<const unsigned char*>(buffer),
   2664         i
   2665     );
   2666 }
   2667 
   2668 void
   2669 XConnection::unset_stringlist_property(winsys::Window window, std::string const& name)
   2670 {
   2671     XChangeProperty(
   2672         mp_dpy,
   2673         window,
   2674         get_atom(name),
   2675         get_atom("UTF8_STRING"),
   2676         8,
   2677         PropModeReplace,
   2678         reinterpret_cast<const unsigned char*>(0),
   2679         0
   2680     );
   2681 }
   2682 
   2683 bool
   2684 XConnection::has_card_property(winsys::Window window, Atom atom)
   2685 {
   2686     int _i;
   2687     unsigned long _ul;
   2688     unsigned char* ucp = nullptr;
   2689     Atom returned_type = None;
   2690     unsigned long n_items_returned = 0;
   2691 
   2692     return (XGetWindowProperty(mp_dpy, window, atom,
   2693         0L, 32, False, XA_CARDINAL,
   2694         &returned_type, &_i, &n_items_returned,
   2695         &_ul, &ucp) == Success
   2696         && ucp && XFree(ucp)
   2697         && returned_type == XA_CARDINAL
   2698         && n_items_returned > 0
   2699     );
   2700 }
   2701 
   2702 unsigned long
   2703 XConnection::get_card_property(winsys::Window window, std::string const& name)
   2704 {
   2705     int _i;
   2706     unsigned long n;
   2707     unsigned long _ul;
   2708     unsigned char* ucp = nullptr;
   2709     Atom _a = None;
   2710     unsigned long card = None;
   2711 
   2712     m_property_status = XGetWindowProperty(
   2713         mp_dpy,
   2714         window,
   2715         get_atom(name),
   2716         0L, 32, False,
   2717         XA_CARDINAL,
   2718         &_a, &_i, &n, &_ul,
   2719         &ucp
   2720     );
   2721 
   2722 	if (m_property_status == Success && ucp) {
   2723 		card = *(winsys::Window*)ucp;
   2724 		XFree(ucp);
   2725 	} else
   2726         m_property_status = !Success;
   2727 
   2728 	return card;
   2729 }
   2730 
   2731 void
   2732 XConnection::replace_card_property(winsys::Window window, std::string const& name, unsigned long card)
   2733 {
   2734     XChangeProperty(
   2735         mp_dpy,
   2736         window,
   2737         get_atom(name),
   2738         XA_CARDINAL,
   2739         32,
   2740         PropModeReplace,
   2741         reinterpret_cast<const unsigned char*>(&card),
   2742         1
   2743     );
   2744 }
   2745 
   2746 void
   2747 XConnection::unset_card_property(winsys::Window window, std::string const& name)
   2748 {
   2749     XChangeProperty(
   2750         mp_dpy,
   2751         window,
   2752         get_atom(name),
   2753         XA_CARDINAL,
   2754         32,
   2755         PropModeReplace,
   2756         reinterpret_cast<const unsigned char*>(0),
   2757         0
   2758     );
   2759 }
   2760 
   2761 std::vector<unsigned long>
   2762 XConnection::get_cardlist_property(winsys::Window window, std::string const& name)
   2763 {
   2764     int _i;
   2765     unsigned long n;
   2766     unsigned long _ul;
   2767     unsigned char* ucp = nullptr;
   2768     Atom _a = None;
   2769     std::vector<unsigned long> cardlist{};
   2770 
   2771     m_property_status = XGetWindowProperty(
   2772         mp_dpy,
   2773         window,
   2774         get_atom(name),
   2775         0L, 32, False,
   2776         XA_CARDINAL,
   2777         &_a, &_i, &n, &_ul,
   2778         &ucp
   2779     );
   2780 
   2781 	if (m_property_status == Success && ucp) {
   2782         for (std::size_t i = 0; i < n; ++i)
   2783             cardlist.push_back(((unsigned long*)ucp)[i]);
   2784 
   2785 		XFree(ucp);
   2786 	} else
   2787         m_property_status = !Success;
   2788 
   2789 	return cardlist;
   2790 }
   2791 
   2792 void
   2793 XConnection::replace_cardlist_property(winsys::Window window, std::string const& name, std::vector<unsigned long> const& cardlist)
   2794 {
   2795     XChangeProperty(
   2796         mp_dpy,
   2797         window,
   2798         get_atom(name),
   2799         XA_CARDINAL,
   2800         32,
   2801         PropModeReplace,
   2802         reinterpret_cast<const unsigned char*>(cardlist.data()),
   2803         cardlist.size()
   2804     );
   2805 }
   2806 
   2807 void
   2808 XConnection::append_cardlist_property(winsys::Window window, std::string const& name, unsigned long card)
   2809 {
   2810     XChangeProperty(
   2811         mp_dpy,
   2812         window,
   2813         get_atom(name),
   2814         XA_CARDINAL,
   2815         32,
   2816         PropModeAppend,
   2817         reinterpret_cast<const unsigned char*>(&card),
   2818         1
   2819     );
   2820 }
   2821 
   2822 void
   2823 XConnection::unset_cardlist_property(winsys::Window window, std::string const& name)
   2824 {
   2825     XChangeProperty(
   2826         mp_dpy,
   2827         window,
   2828         get_atom(name),
   2829         XA_CARDINAL,
   2830         32,
   2831         PropModeReplace,
   2832         reinterpret_cast<const unsigned char*>(0),
   2833         0
   2834     );
   2835 }
   2836 
   2837 bool
   2838 XConnection::get_text_property(winsys::Window window, Atom atom, char* text, unsigned size)
   2839 {
   2840     char** list = NULL;
   2841     int n;
   2842     XTextProperty name;
   2843 
   2844     if (!text || size == 0)
   2845         return false;
   2846 
   2847     text[0] = '\0';
   2848 
   2849     if (!XGetTextProperty(mp_dpy, window, &name, atom) || !name.nitems)
   2850         return false;
   2851 
   2852     if (name.encoding == XA_STRING)
   2853         std::strncpy(text, (char*)name.value, size - 1);
   2854     else if (XmbTextPropertyToTextList(mp_dpy, &name, &list, &n)
   2855         >= Success && n > 0 && *list
   2856     ) {
   2857         std::strncpy(text, *list, size - 1);
   2858         XFreeStringList(list);
   2859     }
   2860 
   2861     text[size - 1] = '\0';
   2862     XFree(name.value);
   2863     return true;
   2864 }
   2865 
   2866 bool
   2867 XConnection::window_is_any_of_states(winsys::Window window, std::vector<winsys::WindowState> const& free_states)
   2868 {
   2869     std::vector<Atom> window_state_atoms
   2870         = get_atomlist_property(window, "_NET_WM_STATE");
   2871 
   2872     if (!property_status_ok())
   2873         return false;
   2874 
   2875     std::vector<Atom> free_state_atoms;
   2876     free_state_atoms.reserve(free_states.size());
   2877 
   2878     std::transform(
   2879         free_states.begin(),
   2880         free_states.end(),
   2881         std::back_inserter(free_state_atoms),
   2882         [=, this](winsys::WindowState state) -> Atom {
   2883             return get_atom_from_window_state(state);
   2884         }
   2885     );
   2886 
   2887     std::sort(window_state_atoms.begin(), window_state_atoms.end());
   2888     std::sort(free_state_atoms.begin(), free_state_atoms.end());
   2889 
   2890     std::vector<Atom> found_atoms;
   2891     std::set_intersection(
   2892         window_state_atoms.begin(),
   2893         window_state_atoms.end(),
   2894         free_state_atoms.begin(),
   2895         free_state_atoms.end(),
   2896         std::back_inserter(found_atoms)
   2897     );
   2898 
   2899     return found_atoms.size() > 0;
   2900 }
   2901 
   2902 bool
   2903 XConnection::window_is_any_of_types(winsys::Window window, std::vector<winsys::WindowType> const& free_types)
   2904 {
   2905     std::vector<Atom> window_type_atoms
   2906         = get_atomlist_property(window, "_NET_WM_WINDOW_TYPE");
   2907 
   2908     if (!property_status_ok())
   2909         return false;
   2910 
   2911     std::vector<Atom> free_type_atoms;
   2912     free_type_atoms.reserve(free_types.size());
   2913 
   2914     std::transform(
   2915         free_types.begin(),
   2916         free_types.end(),
   2917         std::back_inserter(free_type_atoms),
   2918         [=, this](winsys::WindowType type) -> Atom {
   2919             return get_atom_from_window_type(type);
   2920         }
   2921     );
   2922 
   2923     std::sort(window_type_atoms.begin(), window_type_atoms.end());
   2924     std::sort(free_type_atoms.begin(), free_type_atoms.end());
   2925 
   2926     std::vector<Atom> found_atoms;
   2927     std::set_intersection(window_type_atoms.begin(), window_type_atoms.end(),
   2928         free_type_atoms.begin(), free_type_atoms.end(), std::back_inserter(found_atoms));
   2929 
   2930     return found_atoms.size() > 0;
   2931 }
   2932 
   2933 void
   2934 XConnection::check_otherwm()
   2935 {
   2936     XSetErrorHandler(XConnection::s_otherwm_error_handler);
   2937     XSelectInput(mp_dpy, m_root, SubstructureRedirectMask);
   2938     sync(false);
   2939     XSetErrorHandler(s_default_error_handler);
   2940     sync(false);
   2941 }
   2942 
   2943 
   2944 winsys::Event
   2945 XConnection::on_button_press()
   2946 {
   2947     XButtonEvent event = m_current_event.xbutton;
   2948     winsys::Window window = event.window;
   2949     winsys::Window subwindow = event.subwindow;
   2950 
   2951     bool on_root = false;
   2952     if (window == m_root && subwindow == None)
   2953         on_root = true;
   2954 
   2955     if (window == m_root || window == None)
   2956         window = subwindow;
   2957 
   2958     winsys::Button button;
   2959     switch (event.button) {
   2960     case 1: button = winsys::Button::Left;       break;
   2961     case 2: button = winsys::Button::Middle;     break;
   2962     case 3: button = winsys::Button::Right;      break;
   2963     case 4: button = winsys::Button::ScrollUp;   break;
   2964     case 5: button = winsys::Button::ScrollDown; break;
   2965     case 6: button = winsys::Button::Backward;   break;
   2966     case 7: button = winsys::Button::Forward;    break;
   2967     default: return std::monostate{};
   2968     }
   2969 
   2970     std::size_t x11_modifiers[] = {
   2971         ControlMask,
   2972         ShiftMask,
   2973         Mod1Mask,
   2974         Mod4Mask,
   2975         Mod2Mask,
   2976         Mod5Mask
   2977     };
   2978 
   2979     auto x11_to_modifier = [](std::size_t x11_modifier) -> winsys::Modifier {
   2980         switch (x11_modifier) {
   2981         case ControlMask: return winsys::Modifier::Ctrl;
   2982         case ShiftMask:   return winsys::Modifier::Shift;
   2983         case Mod1Mask:    return winsys::Modifier::Alt;
   2984         case Mod4Mask:    return winsys::Modifier::Super;
   2985         case Mod2Mask:    return winsys::Modifier::NumLock;
   2986         case Mod5Mask:    return winsys::Modifier::ScrollLock;
   2987         default: return static_cast<winsys::Modifier>(0);
   2988         }
   2989     };
   2990 
   2991     std::unordered_set<winsys::Modifier> modifiers{};
   2992     for (auto& x11_modifier : x11_modifiers)
   2993         if ((event.state & x11_modifier) > 0)
   2994             modifiers.insert(x11_to_modifier(x11_modifier));
   2995 
   2996     return winsys::MouseEvent {
   2997         {
   2998             winsys::MouseCapture::MouseCaptureKind::Press,
   2999             winsys::MouseInput {
   3000                 winsys::MouseInput::MouseInputTarget::Global,
   3001                 button,
   3002                 modifiers
   3003             },
   3004             window,
   3005             winsys::Pos {
   3006                 event.x_root,
   3007                 event.y_root
   3008             }
   3009         },
   3010         on_root
   3011     };
   3012 }
   3013 
   3014 winsys::Event
   3015 XConnection::on_button_release()
   3016 {
   3017     XButtonEvent event = m_current_event.xbutton;
   3018     winsys::Window window = event.window;
   3019     winsys::Window subwindow = event.subwindow;
   3020 
   3021     bool on_root = false;
   3022     if (window == m_root && subwindow == None)
   3023         on_root = true;
   3024 
   3025     if (window == m_root || window == None)
   3026         window = subwindow;
   3027 
   3028     winsys::Button button;
   3029     switch (event.button) {
   3030     case 1: button = winsys::Button::Left;       break;
   3031     case 2: button = winsys::Button::Middle;     break;
   3032     case 3: button = winsys::Button::Right;      break;
   3033     case 4: button = winsys::Button::ScrollUp;   break;
   3034     case 5: button = winsys::Button::ScrollDown; break;
   3035     case 6: button = winsys::Button::Backward;   break;
   3036     case 7: button = winsys::Button::Forward;    break;
   3037     default: return std::monostate{};
   3038     }
   3039 
   3040     std::size_t x11_modifiers[] = {
   3041         ControlMask,
   3042         ShiftMask,
   3043         Mod1Mask,
   3044         Mod4Mask,
   3045         Mod2Mask,
   3046         Mod5Mask
   3047     };
   3048 
   3049     auto x11_to_modifier = [](std::size_t x11_modifier) -> winsys::Modifier {
   3050         switch (x11_modifier) {
   3051         case ControlMask: return winsys::Modifier::Ctrl;
   3052         case ShiftMask:   return winsys::Modifier::Shift;
   3053         case Mod1Mask:    return winsys::Modifier::Alt;
   3054         case Mod4Mask:    return winsys::Modifier::Super;
   3055         case Mod2Mask:    return winsys::Modifier::NumLock;
   3056         case Mod5Mask:    return winsys::Modifier::ScrollLock;
   3057         default: return static_cast<winsys::Modifier>(0);
   3058         }
   3059     };
   3060 
   3061     std::unordered_set<winsys::Modifier> modifiers{};
   3062     for (auto& x11_modifier : x11_modifiers)
   3063         if ((event.state & x11_modifier) > 0)
   3064             modifiers.insert(x11_to_modifier(x11_modifier));
   3065 
   3066     return winsys::MouseEvent {
   3067         {
   3068             winsys::MouseCapture::MouseCaptureKind::Release,
   3069             winsys::MouseInput {
   3070                 winsys::MouseInput::MouseInputTarget::Global,
   3071                 button,
   3072                 modifiers
   3073             },
   3074             window,
   3075             winsys::Pos {
   3076                 event.x_root,
   3077                 event.y_root
   3078             }
   3079         },
   3080         on_root
   3081     };
   3082 }
   3083 
   3084 winsys::Event
   3085 XConnection::on_circulate_request()
   3086 {
   3087     XCirculateSubwindows(
   3088         mp_dpy,
   3089         m_current_event.xcirculaterequest.window,
   3090         m_current_event.xcirculaterequest.place
   3091             == PlaceOnTop ?  RaiseLowest : LowerHighest
   3092     );
   3093 
   3094     return std::monostate{};
   3095 }
   3096 
   3097 winsys::Event
   3098 XConnection::on_client_message()
   3099 {
   3100     XClientMessageEvent event = m_current_event.xclient;
   3101     winsys::Window window = event.window;
   3102 
   3103     NetWMID id;
   3104     for (id = NetWMID::NetFirst; id < NetWMID::NetLast; ++id)
   3105         if (event.message_type == get_netwm_atom(id))
   3106             break;
   3107 
   3108     if (id >= NetWMID::NetLast)
   3109         return std::monostate{};
   3110 
   3111     switch (id) {
   3112     case NetWMID::NetWMState:
   3113     {
   3114         for (std::size_t property = 1; property <= 2; ++property) {
   3115             if (event.data.l[property] == 0)
   3116                 continue;
   3117 
   3118             if (event.data.l[0] >= static_cast<int>(NetWMAction::NetNoAction))
   3119                 break;
   3120 
   3121             Atom atom = static_cast<Atom>(event.data.l[property]);
   3122             winsys::WindowState state;
   3123 
   3124             if (atom == get_netwm_atom(NetWMID::NetWMStateFullscreen))
   3125                 state = winsys::WindowState::Fullscreen;
   3126             else if (atom == get_netwm_atom(NetWMID::NetWMStateAbove))
   3127                 state = winsys::WindowState::Above_;
   3128             else if (atom == get_netwm_atom(NetWMID::NetWMStateBelow))
   3129                 state = winsys::WindowState::Below_;
   3130             else if (atom == get_netwm_atom(NetWMID::NetWMStateDemandsAttention))
   3131                 state = winsys::WindowState::DemandsAttention;
   3132             else
   3133                  return std::monostate{};
   3134 
   3135             winsys::Toggle toggle;
   3136             switch (event.data.l[0]) {
   3137             case 0: toggle = winsys::Toggle::Off; break;
   3138             case 1: toggle = winsys::Toggle::On; break;
   3139             case 2: toggle = winsys::Toggle::Reverse; break;
   3140             default: return std::monostate{};
   3141             }
   3142 
   3143             return winsys::StateRequestEvent {
   3144                 window,
   3145                 state,
   3146                 toggle,
   3147                 window == m_root
   3148             };
   3149         }
   3150 
   3151         break;
   3152     }
   3153     case NetWMID::NetMoveResizeWindow:
   3154     {
   3155         std::optional<winsys::Pos> pos{};
   3156         std::optional<winsys::Dim> dim{};
   3157 
   3158         pos = winsys::Pos {
   3159             static_cast<int>(event.data.l[1]),
   3160             static_cast<int>(event.data.l[2])
   3161         };
   3162 
   3163         if (event.data.l[3] > 0 && event.data.l[4] > 0)
   3164             dim = winsys::Dim {
   3165                 static_cast<int>(event.data.l[3]),
   3166                 static_cast<int>(event.data.l[4])
   3167             };
   3168 
   3169         return winsys::PlacementRequestEvent {
   3170             window,
   3171             pos,
   3172             dim,
   3173             window == m_root
   3174         };
   3175     }
   3176     case NetWMID::NetWMMoveResize:
   3177     {
   3178         winsys::Grip grip = static_cast<winsys::Grip>(0);
   3179 
   3180         switch (event.data.l[2]) {
   3181         case 0: grip |= winsys::Grip::Left;   // fallthrough
   3182         case 1: grip |= winsys::Grip::Top;    break;
   3183 
   3184         case 2: grip |= winsys::Grip::Top;    // fallthrough
   3185         case 3: grip |= winsys::Grip::Right;  break;
   3186 
   3187         case 4: grip |= winsys::Grip::Right;  // fallthrough
   3188         case 5: grip |= winsys::Grip::Bottom; break;
   3189 
   3190         case 6: grip |= winsys::Grip::Left;   // fallthrough
   3191         case 7: grip |= winsys::Grip::Bottom; break;
   3192         case 8: break;
   3193         default: return std::monostate{};
   3194         }
   3195 
   3196         return winsys::GripRequestEvent {
   3197             event.window,
   3198             winsys::Pos {
   3199                 static_cast<int>(event.data.l[0]),
   3200                 static_cast<int>(event.data.l[1])
   3201             },
   3202             grip == static_cast<winsys::Grip>(0)
   3203                 ? std::nullopt
   3204                 : std::optional(grip),
   3205             event.window == m_root
   3206         };
   3207     }
   3208     case NetWMID::NetActiveWindow:
   3209     {
   3210         if (event.data.l[0] <= 2)
   3211             return winsys::FocusRequestEvent {
   3212                 window,
   3213                 window == m_root
   3214             };
   3215 
   3216         break;
   3217     }
   3218     case NetWMID::NetWMCloseWindow:
   3219     return winsys::CloseRequestEvent {
   3220         window,
   3221         window == m_root
   3222     };
   3223     case NetWMID::NetRequestFrameExtents:
   3224     return winsys::FrameExtentsRequestEvent {
   3225         window,
   3226         window == m_root
   3227     };
   3228     case NetWMID::NetCurrentDesktop:
   3229     return winsys::WorkspaceRequestEvent {
   3230         std::nullopt,
   3231         static_cast<unsigned>(event.data.l[0]),
   3232         window == m_root
   3233     };
   3234     default: break;
   3235     }
   3236 
   3237     return std::monostate{};
   3238 }
   3239 
   3240 winsys::Event
   3241 XConnection::on_configure_notify()
   3242 {
   3243     XConfigureEvent event = m_current_event.xconfigure;
   3244     winsys::Window window = event.window;
   3245 
   3246     return winsys::ConfigureEvent {
   3247         window,
   3248         winsys::Region {
   3249             winsys::Pos { event.x, event.y },
   3250             winsys::Dim {
   3251                 static_cast<int>(event.width),
   3252                 static_cast<int>(event.height)
   3253             }
   3254         },
   3255         window == m_root
   3256     };
   3257 }
   3258 
   3259 winsys::Event
   3260 XConnection::on_configure_request()
   3261 {
   3262     XConfigureRequestEvent event = m_current_event.xconfigurerequest;
   3263     winsys::Window window = event.window;
   3264     winsys::Window sibling = event.above;
   3265 
   3266     std::optional<winsys::Region> region
   3267         = get_window_geometry(window);
   3268 
   3269     if (!region)
   3270         return std::monostate{};
   3271 
   3272     std::optional<int> x = std::nullopt;
   3273     std::optional<int> y = std::nullopt;
   3274     std::optional<int> w = std::nullopt;
   3275     std::optional<int> h = std::nullopt;
   3276 
   3277     if ((event.value_mask & CWX) != 0)
   3278         x = event.x;
   3279 
   3280     if ((event.value_mask & CWY) != 0)
   3281         y = event.y;
   3282 
   3283     if ((event.value_mask & CWWidth) != 0)
   3284         w = event.width;
   3285 
   3286     if ((event.value_mask & CWHeight) != 0)
   3287         h = event.height;
   3288 
   3289     std::optional<winsys::Pos> pos = std::nullopt;
   3290     std::optional<winsys::Dim> dim = std::nullopt;
   3291 
   3292     if (x || y)
   3293         pos = winsys::Pos {
   3294             x ? *x : region->pos.x,
   3295             y ? *y : region->pos.y
   3296         };
   3297 
   3298     if (w || h)
   3299         dim = winsys::Dim {
   3300             w ? *w : region->dim.w,
   3301             h ? *h : region->dim.h
   3302         };
   3303 
   3304     if (pos || dim)
   3305         return winsys::PlacementRequestEvent {
   3306             window,
   3307             pos,
   3308             dim,
   3309             window == m_root
   3310         };
   3311 
   3312     if ((event.value_mask & CWStackMode) != 0) {
   3313         if (sibling != None)
   3314             switch (event.detail) {
   3315             case Above:
   3316             return winsys::RestackRequestEvent {
   3317                 window,
   3318                 sibling,
   3319                 winsys::StackMode::Above_,
   3320                 window == m_root
   3321             };
   3322             case Below:
   3323             return winsys::RestackRequestEvent {
   3324                 window,
   3325                 sibling,
   3326                 winsys::StackMode::Below_,
   3327                 window == m_root
   3328             };
   3329             default: break;
   3330             }
   3331     }
   3332 
   3333     return std::monostate{};
   3334 }
   3335 
   3336 winsys::Event
   3337 XConnection::on_destroy_notify()
   3338 {
   3339     return winsys::DestroyEvent {
   3340         m_current_event.xdestroywindow.window
   3341     };
   3342 }
   3343 
   3344 winsys::Event
   3345 XConnection::on_expose()
   3346 {
   3347     return winsys::ExposeEvent {
   3348         m_current_event.xexpose.window
   3349     };
   3350 }
   3351 
   3352 winsys::Event
   3353 XConnection::on_focus_in()
   3354 {
   3355     return std::monostate{};
   3356 }
   3357 
   3358 winsys::Event
   3359 XConnection::on_enter_notify()
   3360 {
   3361     XKeyEvent event = m_current_event.xkey;
   3362     winsys::Window window = event.window;
   3363     winsys::Window subwindow = event.subwindow;
   3364 
   3365     if (window == m_root || window == None)
   3366         window = subwindow;
   3367 
   3368     return winsys::EnterEvent {
   3369         window
   3370     };
   3371 }
   3372 
   3373 winsys::Event
   3374 XConnection::on_key_press()
   3375 {
   3376     XKeyEvent event = m_current_event.xkey;
   3377     winsys::Window window = event.window;
   3378     winsys::Window subwindow = event.subwindow;
   3379 
   3380     if (window == m_root || window == None)
   3381         window = subwindow;
   3382 
   3383     winsys::Key key = get_key(event.keycode);
   3384 
   3385     std::size_t x11_modifiers[] = {
   3386         ControlMask,
   3387         ShiftMask,
   3388         Mod1Mask,
   3389         Mod4Mask,
   3390         Mod2Mask,
   3391         Mod5Mask
   3392     };
   3393 
   3394     auto x11_to_modifier = [](std::size_t x11_modifier) -> winsys::Modifier {
   3395         switch (x11_modifier) {
   3396         case ControlMask: return winsys::Modifier::Ctrl;
   3397         case ShiftMask:   return winsys::Modifier::Shift;
   3398         case Mod1Mask:    return winsys::Modifier::Alt;
   3399         case Mod4Mask:    return winsys::Modifier::Super;
   3400         case Mod2Mask:    return winsys::Modifier::NumLock;
   3401         case Mod5Mask:    return winsys::Modifier::ScrollLock;
   3402         default: return static_cast<winsys::Modifier>(0);
   3403         }
   3404     };
   3405 
   3406     std::unordered_set<winsys::Modifier> modifiers{};
   3407     for (auto& x11_modifier : x11_modifiers)
   3408         if ((event.state & x11_modifier) != 0)
   3409             modifiers.insert(x11_to_modifier(x11_modifier));
   3410 
   3411     return winsys::KeyEvent {
   3412         {
   3413             winsys::KeyInput {
   3414                 key,
   3415                 modifiers
   3416             },
   3417             window
   3418         }
   3419     };
   3420 }
   3421 
   3422 winsys::Event
   3423 XConnection::on_map_notify()
   3424 {
   3425     XMapEvent event = m_current_event.xmap;
   3426     winsys::Window window = event.window;
   3427 
   3428     return winsys::MapEvent {
   3429         window,
   3430         !must_manage_window(event.window)
   3431     };
   3432 }
   3433 
   3434 winsys::Event
   3435 XConnection::on_map_request()
   3436 {
   3437     XMapRequestEvent event = m_current_event.xmaprequest;
   3438     winsys::Window window = event.window;
   3439 
   3440     return winsys::MapRequestEvent {
   3441         window,
   3442         !must_manage_window(event.window)
   3443     };
   3444 }
   3445 
   3446 winsys::Event
   3447 XConnection::on_mapping_notify()
   3448 {
   3449     XMappingEvent event = m_current_event.xmapping;
   3450     if (event.request == MappingKeyboard)
   3451         XRefreshKeyboardMapping(&event);
   3452 
   3453     return std::monostate{};
   3454 }
   3455 
   3456 winsys::Event
   3457 XConnection::on_motion_notify()
   3458 {
   3459     last_typed_event(m_current_event, MotionNotify);
   3460 
   3461     XMotionEvent event = m_current_event.xmotion;
   3462     winsys::Window window = event.window;
   3463     winsys::Window subwindow = event.subwindow;
   3464 
   3465     bool on_root = false;
   3466     if (window == m_root && subwindow == None)
   3467         on_root = true;
   3468 
   3469     if (window == m_root || window == None)
   3470         window = subwindow;
   3471 
   3472     std::size_t x11_modifiers[] = {
   3473         ControlMask,
   3474         ShiftMask,
   3475         Mod1Mask,
   3476         Mod4Mask,
   3477         Mod2Mask,
   3478         Mod5Mask
   3479     };
   3480 
   3481     auto x11_to_modifier = [](std::size_t x11_modifier) -> winsys::Modifier {
   3482         switch (x11_modifier) {
   3483         case ControlMask: return winsys::Modifier::Ctrl;
   3484         case ShiftMask:   return winsys::Modifier::Shift;
   3485         case Mod1Mask:    return winsys::Modifier::Alt;
   3486         case Mod4Mask:    return winsys::Modifier::Super;
   3487         case Mod2Mask:    return winsys::Modifier::NumLock;
   3488         case Mod5Mask:    return winsys::Modifier::ScrollLock;
   3489         default: return static_cast<winsys::Modifier>(0);
   3490         }
   3491     };
   3492 
   3493     std::unordered_set<winsys::Modifier> modifiers{};
   3494     for (auto& x11_modifier : x11_modifiers)
   3495         if ((event.state & x11_modifier) != 0)
   3496             modifiers.insert(x11_to_modifier(x11_modifier));
   3497 
   3498     return winsys::MouseEvent {
   3499         {
   3500             winsys::MouseCapture::MouseCaptureKind::Motion,
   3501             winsys::MouseInput {
   3502                 winsys::MouseInput::MouseInputTarget::Global,
   3503                 winsys::Button::Left,
   3504                 modifiers
   3505             },
   3506             window,
   3507             winsys::Pos {
   3508                 event.x_root,
   3509                 event.y_root
   3510             }
   3511         },
   3512         on_root
   3513     };
   3514 }
   3515 
   3516 winsys::Event
   3517 XConnection::on_property_notify()
   3518 {
   3519     XPropertyEvent event = m_current_event.xproperty;
   3520     winsys::Window window = event.window;
   3521 
   3522     if (event.state == PropertyNewValue) {
   3523         switch (event.atom) {
   3524         case XA_WM_NAME:
   3525         return winsys::PropertyEvent {
   3526             window,
   3527             winsys::PropertyKind::Name,
   3528             window == m_root
   3529         };
   3530         case XA_WM_CLASS:
   3531         return winsys::PropertyEvent {
   3532             window,
   3533             winsys::PropertyKind::Class,
   3534             window == m_root
   3535         };
   3536         case XA_WM_NORMAL_HINTS:
   3537         return winsys::PropertyEvent {
   3538             window,
   3539             winsys::PropertyKind::Size,
   3540             window == m_root
   3541         };
   3542         }
   3543 
   3544         if (event.atom == get_atom("_NET_WM_NAME")) {
   3545             return winsys::PropertyEvent {
   3546                 window,
   3547                 winsys::PropertyKind::Name,
   3548                 window == m_root
   3549             };
   3550         }
   3551     }
   3552 
   3553     if (event.atom == get_atom("_NET_WM_STRUT_PARTIAL")
   3554         || event.atom == get_atom("_NET_WM_STRUT"))
   3555     {
   3556         return winsys::PropertyEvent {
   3557             window,
   3558             winsys::PropertyKind::Strut,
   3559             window == m_root
   3560         };
   3561     }
   3562 
   3563     return std::monostate{};
   3564 }
   3565 
   3566 winsys::Event
   3567 XConnection::on_unmap_notify()
   3568 {
   3569     XMapEvent event = m_current_event.xmap;
   3570     winsys::Window window = event.window;
   3571 
   3572     return winsys::UnmapEvent {
   3573         window,
   3574         !must_manage_window(window)
   3575     };
   3576 }
   3577 
   3578 winsys::Event
   3579 XConnection::on_screen_change()
   3580 {
   3581     return winsys::ScreenChangeEvent {};
   3582 }
   3583 
   3584 winsys::Event
   3585 XConnection::on_unimplemented()
   3586 {
   3587     return std::monostate{};
   3588 }
   3589 
   3590 
   3591 int
   3592 XConnection::s_otherwm_error_handler(Display*, XErrorEvent*)
   3593 {
   3594     Util::die("another window manager is already running");
   3595     return -1;
   3596 }
   3597 
   3598 int
   3599 XConnection::s_default_error_handler(Display*, XErrorEvent* error)
   3600 {
   3601     static const std::unordered_map<int, int> permissible_errors({
   3602         { X_GrabButton,        BadAccess   },
   3603         { X_GrabKey,           BadAccess   },
   3604         { X_CopyArea,          BadDrawable },
   3605         { X_PolyFillRectangle, BadDrawable },
   3606         { X_PolySegment,       BadDrawable },
   3607         { X_PolyText8,         BadDrawable },
   3608         { X_ConfigureWindow,   BadMatch    },
   3609         { X_SetInputFocus,     BadMatch    },
   3610     });
   3611 
   3612     if (error->error_code == BadWindow)
   3613         return 0;
   3614 
   3615     for (auto reqerr_pair : permissible_errors)
   3616         if (error->request_code == reqerr_pair.first && error->error_code == reqerr_pair.second)
   3617             return 0;
   3618 
   3619     return -1;
   3620 }
   3621 
   3622 int
   3623 XConnection::s_passthrough_error_handler(Display*, XErrorEvent*)
   3624 {
   3625     return 0;
   3626 }
   3627 
   3628 #pragma GCC diagnostic pop