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