From d0e18bdb7924c71cdca8dd983711171d87ef28be Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 17 Jan 2022 23:12:32 -0500 Subject: glplanet, an OpenGL-based planetary renderer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glplanet draws Earth like it currently appears from space, putting nighttime areas in shadow and daytime areas in light. It’s modeled after Xplanet (http://xplanet.sourceforge.net/), but whereas Xplanet is entirely a CPU-resident program, glplanet draws using OpenGL. It’s thus much less resource-intensive, particularly when using high-resolution textures. --- src/x/connection.cc | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 src/x/connection.cc (limited to 'src/x/connection.cc') diff --git a/src/x/connection.cc b/src/x/connection.cc new file mode 100644 index 0000000..f376a2a --- /dev/null +++ b/src/x/connection.cc @@ -0,0 +1,174 @@ +// Copyright 2021, 2022 Benjamin Barenblat +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#include "src/x/connection.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/undo_xlib_dot_h_namespace_pollution.h" +// + +#include "src/util.h" +#include "src/x/event.h" +#include "src/x/util.h" +#include "third_party/abseil/absl/cleanup/cleanup.h" +#include "third_party/abseil/absl/container/btree_map.h" +#include "third_party/abseil/absl/strings/string_view.h" +#include "third_party/abseil/absl/synchronization/mutex.h" +#include "third_party/abseil/absl/types/optional.h" + +namespace x { + +namespace { + +std::pair> MarshalValueList( + const absl::btree_map>& + values) noexcept { + uint32_t value_mask = 0; + std::vector value_list; + for (auto& [k, v] : values) { + if (v.has_value()) { + value_mask |= k; + value_list.push_back(*v); + } + } + return {value_mask, value_list}; +} + +} // namespace + +Connection::Connection(const char* display_name) { + if (display_name == nullptr) { + throw std::invalid_argument("X: null display name"); + } + + xlib_ = XOpenDisplay(display_name); + if (xlib_ == nullptr) { + throw std::runtime_error("X: could not connect to X server"); + } + XSetEventQueueOwner(xlib_, XCBOwnsEventQueue); + + // We make a lot of calls into XCB, so cache the XCB handle. + xcb_ = XGetXCBConnection(xlib_); +} + +VoidCompletion Connection::CreateWindow( + const CreateWindowOptions& options) noexcept { + const auto& [value_mask, value_list] = + MarshalValueList({{XCB_CW_BACK_PIXEL, options.background_pixel}, + {XCB_CW_EVENT_MASK, options.event_mask}}); + return VoidCompletion( + xcb_, xcb_create_window_checked( + xcb_, options.depth, options.window, options.parent, options.x, + options.y, options.width, options.height, options.border_width, + FromEnum(options.window_class), options.visual_id, value_mask, + value_list.data())); +} + +VoidCompletion Connection::SendEvent(const Event& event, bool propagate, + Id destination_window, + const std::vector& event_mask) { + uint32_t serialized_event_mask = 0; + for (EventMask m : event_mask) { + serialized_event_mask |= FromEnum(m); + } + std::array serialized_event = SerializeEvent(event); + return VoidCompletion( + xcb_, + xcb_send_event_checked(xcb_, propagate, destination_window, + serialized_event_mask, serialized_event.data())); +} + +EventMonitor::EventMonitor(Connection& x) + : x_(x), communication_window_(x_.GenerateId()) { + // Create a window for us to communicate with the watcher thread. + VoidCompletion create_window_completion = + x_.CreateWindow({.depth = 0, // as required for an InputOnly window + .window = communication_window_, + .parent = x_.DefaultScreen().root(), + .x = 0, + .y = 0, + .width = 1, + .height = 1, + .border_width = 0, + .window_class = WindowClass::kInputOnly, + .visual_id = XCB_COPY_FROM_PARENT}); + InternAtomCompletion intern_atom_completion = + x_.InternOrGetAtomByName(kDoneAtomName); + std::move(create_window_completion).Check(); + close_connection_atom_ = std::move(intern_atom_completion).Get(); + watcher_thread_.emplace(&EventMonitor::WatcherThreadMain, this); +} + +EventMonitor::~EventMonitor() { + // Send a client message through the connection so the waiter thread knows to + // exit. + xcb_client_message_event_t event = {.response_type = XCB_CLIENT_MESSAGE, + .format = 32, + .sequence = 0, + .window = communication_window_, + .type = close_connection_atom_, + .data = {}}; + x_.SendEvent(ClientMessageEvent(event), /*propagate=*/false, + communication_window_, {EventMask::kNoEvent}) + .Check(); + watcher_thread_->join(); + x_.DestroyWindow(communication_window_); +} + +absl::optional EventMonitor::WaitForEventWithTimeout( + absl::Duration timeout) noexcept { + bool events_present = mu_.LockWhenWithTimeout( + absl::Condition(this, &EventMonitor::EventsPresent), timeout); + absl::Cleanup unlock = [&]() noexcept { mu_.Unlock(); }; + if (events_present) { + Event event = std::move(pending_events_.front()); + pending_events_.pop_front(); + return event; + } else { + return absl::nullopt; + } +} + +void EventMonitor::WatcherThreadMain() noexcept { + while (true) { + auto generic_event = + std::unique_ptr( + xcb_wait_for_event(x_.AsXcbConnection())); + if (generic_event == nullptr) { + // The connection dropped or something. + return; + } + + Event event = FromXcbGenericEvent(*generic_event); + if (const auto* client_message = absl::get_if(&event); + client_message != nullptr && + client_message->type() == close_connection_atom_) { + // Our destructor is running. + return; + } + absl::MutexLock lock(&mu_); + pending_events_.push_back(event); + } +} + +} // namespace x -- cgit v1.2.3