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.h | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 src/x/connection.h (limited to 'src/x/connection.h') diff --git a/src/x/connection.h b/src/x/connection.h new file mode 100644 index 0000000..83c6b18 --- /dev/null +++ b/src/x/connection.h @@ -0,0 +1,204 @@ +// 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. + +// The main connection to the X server. + +#ifndef GLPLANET_SRC_X_CONNECTION_H_ +#define GLPLANET_SRC_X_CONNECTION_H_ + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/undo_xlib_dot_h_namespace_pollution.h" +// + +#include "src/x/event.h" +#include "src/x/rpc.h" +#include "src/x/screen.h" +#include "src/x/types.h" +#include "third_party/abseil/absl/base/thread_annotations.h" +#include "third_party/abseil/absl/strings/string_view.h" +#include "third_party/abseil/absl/synchronization/mutex.h" +#include "third_party/abseil/absl/time/time.h" +#include "third_party/abseil/absl/types/optional.h" + +namespace x { + +enum class WindowClass : uint16_t { + kCopyFromParent = XCB_WINDOW_CLASS_COPY_FROM_PARENT, + kInputOutput = XCB_WINDOW_CLASS_INPUT_OUTPUT, + kInputOnly = XCB_WINDOW_CLASS_INPUT_ONLY, +}; + +enum EventMask : uint32_t { + kNoEvent = XCB_EVENT_MASK_NO_EVENT, + kKeyPress = XCB_EVENT_MASK_KEY_PRESS, + kKeyRelease = XCB_EVENT_MASK_KEY_RELEASE, + kButtonPress = XCB_EVENT_MASK_BUTTON_PRESS, + kButtonRelease = XCB_EVENT_MASK_BUTTON_RELEASE, + kEnterWindow = XCB_EVENT_MASK_ENTER_WINDOW, + kLeaveWindow = XCB_EVENT_MASK_LEAVE_WINDOW, + kPointerMotion = XCB_EVENT_MASK_POINTER_MOTION, + kPointerMotionHint = XCB_EVENT_MASK_POINTER_MOTION_HINT, + kButton1Motion = XCB_EVENT_MASK_BUTTON_1_MOTION, + kButton2Motion = XCB_EVENT_MASK_BUTTON_2_MOTION, + kButton3Motion = XCB_EVENT_MASK_BUTTON_3_MOTION, + kButton4Motion = XCB_EVENT_MASK_BUTTON_4_MOTION, + kButton5Motion = XCB_EVENT_MASK_BUTTON_5_MOTION, + kButtonMotion = XCB_EVENT_MASK_BUTTON_MOTION, + kKeymapState = XCB_EVENT_MASK_KEYMAP_STATE, + kExposure = XCB_EVENT_MASK_EXPOSURE, + kVisibilityChange = XCB_EVENT_MASK_VISIBILITY_CHANGE, + kStructureNotify = XCB_EVENT_MASK_STRUCTURE_NOTIFY, + kResizeRedirect = XCB_EVENT_MASK_RESIZE_REDIRECT, + kSubstructureNotify = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, + kSubstructureRedirect = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + kFocusChange = XCB_EVENT_MASK_FOCUS_CHANGE, + kPropertyChange = XCB_EVENT_MASK_PROPERTY_CHANGE, + kColorMapChange = XCB_EVENT_MASK_COLOR_MAP_CHANGE, + kOwnerGrabButton = XCB_EVENT_MASK_OWNER_GRAB_BUTTON, +}; + +// A connection to the X server. +// +// This class is thread-safe. +class Connection final { + public: + struct CreateWindowOptions { + uint8_t depth; + Id window; + Id parent; + int16_t x; + int16_t y; + uint16_t width; + uint16_t height; + uint16_t border_width; + WindowClass window_class; + Id visual_id; + + // TODO(bbarenblat@gmail.com): Support additional attributes. + absl::optional background_pixel = absl::nullopt; + absl::optional event_mask = absl::nullopt; + }; + + explicit Connection(const char* display_name); + + Connection(Connection&&) noexcept = default; + Connection& operator=(Connection&&) noexcept = default; + + ~Connection() noexcept { XCloseDisplay(xlib_); } + + Id GenerateId() noexcept { return xcb_generate_id(xcb_); } + + Screen DefaultScreen() const noexcept { + return Screen(xcb_aux_get_screen(xcb_, xlib::DefaultScreen(xlib_))); + } + + VoidCompletion CreateWindow(const CreateWindowOptions& options) noexcept; + + VoidCompletion DestroyWindow(Id window) noexcept { + return VoidCompletion(xcb_, xcb_destroy_window_checked(xcb_, window)); + } + + VoidCompletion MapWindow(Id window) noexcept { + return VoidCompletion(xcb_, xcb_map_window_checked(xcb_, window)); + } + + // Per the X specification, name should be encoded as ISO 8859-1. + InternAtomCompletion InternOrGetAtomByName(absl::string_view name) noexcept { + return InternAtom(name, /*only_if_exists=*/false); + } + + // Per the X specification, name should be encoded as ISO 8859-1. + InternAtomCompletion GetAtomByName(absl::string_view name) noexcept { + return InternAtom(name, /*only_if_exists=*/true); + } + + VoidCompletion SendEvent(const Event&, bool propagate, Id destination_window, + const std::vector&); + + // Escape hatches: raw handles to the X server as Xlib and XCB objects. Use + // these to interface with other libraries. + // + // Be careful with the Xlib handle--XCB, not Xlib, owns the event loop. + Display* AsXlibDisplay() noexcept { return xlib_; } + xcb_connection_t* AsXcbConnection() noexcept { return xcb_; } + + private: + InternAtomCompletion InternAtom(absl::string_view name, + bool only_if_exists) noexcept { + return InternAtomCompletion( + xcb_, xcb_intern_atom(xcb_, only_if_exists, name.size(), name.data())); + } + + Display* xlib_; + xcb_connection_t* xcb_; +}; + +// A class that monitors for X events. You probably only want one of these for +// each connection; otherwise, events will be delivered nondeterministically to +// the monitors. +// +// This class is thread-safe. +class EventMonitor final { + public: + // Starts monitoring for X events on the specified connection. + explicit EventMonitor(Connection&); + + EventMonitor(EventMonitor&&) noexcept = default; + EventMonitor& operator=(EventMonitor&&) noexcept = default; + + ~EventMonitor(); + + absl::optional GetEventIfReady() noexcept ABSL_LOCKS_EXCLUDED(mu_) { + return WaitForEventWithTimeout(absl::ZeroDuration()); + } + + Event WaitForEvent() noexcept ABSL_LOCKS_EXCLUDED(mu_) { + return *WaitForEventWithTimeout(absl::InfiniteDuration()); + } + + absl::optional WaitForEventWithTimeout(absl::Duration) noexcept + ABSL_LOCKS_EXCLUDED(mu_); + + private: + static constexpr absl::string_view kDoneAtomName = + "_GLPLANET_SRC_X_CONNECTION_EVENTMONITOR_DONE"; + + void WatcherThreadMain() noexcept ABSL_LOCKS_EXCLUDED(mu_); + + bool EventsPresent() const noexcept { + mu_.AssertReaderHeld(); + return !pending_events_.empty(); + }; + + Connection& x_; + Id communication_window_; + Id close_connection_atom_; + + absl::Mutex mu_; + std::deque pending_events_ ABSL_GUARDED_BY(mu_); + + absl::optional watcher_thread_; +}; + +} // namespace x + +#endif // GLPLANET_SRC_X_CONNECTION_H_ -- cgit v1.2.3