Hyprland/src/Compositor.cpp

2325 lines
84 KiB
C++
Raw Normal View History

2022-03-16 21:37:21 +01:00
#include "Compositor.hpp"
2022-07-10 15:41:26 +02:00
#include "helpers/Splashes.hpp"
#include <random>
2022-07-31 00:27:32 +02:00
#include "debug/HyprCtl.hpp"
2023-02-19 14:45:56 +01:00
#include "debug/CrashReporter.hpp"
#ifdef USES_SYSTEMD
#include <systemd/sd-daemon.h> // for sd_notify
#endif
2023-03-01 15:06:52 +01:00
#include <ranges>
2022-03-16 21:37:21 +01:00
2022-07-13 18:18:23 +02:00
int handleCritSignal(int signo, void* data) {
Debug::log(LOG, "Hyprland received signal %d", signo);
if (signo == SIGTERM || signo == SIGINT || signo == SIGKILL) {
g_pCompositor->cleanup();
}
return 0; // everything went fine
}
2023-03-01 16:08:44 +01:00
void handleUnrecoverableSignal(int sig) {
2023-03-01 22:55:30 +01:00
// remove our handlers
signal(SIGABRT, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
if (g_pHookSystem->m_bCurrentEventPlugin) {
longjmp(g_pHookSystem->m_jbHookFaultJumpBuf, 1);
return;
}
2023-03-01 16:08:44 +01:00
CrashReporter::createAndSaveCrash(sig);
2023-03-01 22:55:30 +01:00
2023-02-20 12:02:44 +01:00
abort();
2023-02-19 14:45:56 +01:00
}
2022-03-16 21:37:21 +01:00
CCompositor::CCompositor() {
m_iHyprlandPID = getpid();
m_szInstanceSignature = GIT_COMMIT_HASH + std::string("_") + std::to_string(time(NULL));
2022-03-18 23:52:36 +01:00
2022-06-29 12:58:49 +02:00
setenv("HYPRLAND_INSTANCE_SIGNATURE", m_szInstanceSignature.c_str(), true);
2022-12-21 16:41:02 +01:00
if (!std::filesystem::exists("/tmp/hypr")) {
std::filesystem::create_directory("/tmp/hypr");
std::filesystem::permissions("/tmp/hypr", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
2022-06-29 12:58:49 +02:00
const auto INSTANCEPATH = "/tmp/hypr/" + m_szInstanceSignature;
2022-12-21 16:41:02 +01:00
std::filesystem::create_directory(INSTANCEPATH);
std::filesystem::permissions(INSTANCEPATH, std::filesystem::perms::group_all, std::filesystem::perm_options::replace);
std::filesystem::permissions(INSTANCEPATH, std::filesystem::perms::owner_all, std::filesystem::perm_options::add);
2022-06-29 12:58:49 +02:00
2022-06-03 17:48:07 +02:00
Debug::init(m_szInstanceSignature);
Debug::log(LOG, "Instance Signature: %s", m_szInstanceSignature.c_str());
Debug::log(LOG, "Hyprland PID: %i", m_iHyprlandPID);
2022-06-25 20:49:06 +02:00
Debug::log(LOG, "===== SYSTEM INFO: =====");
logSystemInfo();
Debug::log(LOG, "========================");
2022-06-25 21:16:52 +02:00
Debug::log(NONE, "\n\n"); // pad
2023-01-07 12:33:36 +01:00
Debug::log(INFO, "If you are crashing, or encounter any bugs, please consult https://wiki.hyprland.org/Crashes-and-Bugs/\n\n");
2022-06-25 21:16:52 +02:00
2022-07-10 15:41:26 +02:00
setRandomSplash();
Debug::log(LOG, "\nCurrent splash: %s\n\n", m_szCurrentSplash.c_str());
}
CCompositor::~CCompositor() {
cleanup();
}
2022-07-10 15:41:26 +02:00
void CCompositor::setRandomSplash() {
std::random_device dev;
std::mt19937 engine(dev());
std::uniform_int_distribution<> distribution(0, SPLASHES.size() - 1);
m_szCurrentSplash = SPLASHES[distribution(engine)];
}
void CCompositor::initServer() {
2023-03-24 14:00:54 +01:00
2022-03-16 21:37:21 +01:00
m_sWLDisplay = wl_display_create();
2022-07-13 18:18:23 +02:00
m_sWLEventLoop = wl_display_get_event_loop(m_sWLDisplay);
// register crit signal handler
wl_event_loop_add_signal(m_sWLEventLoop, SIGTERM, handleCritSignal, nullptr);
2023-03-01 16:08:44 +01:00
signal(SIGSEGV, handleUnrecoverableSignal);
signal(SIGABRT, handleUnrecoverableSignal);
2022-07-13 18:18:23 +02:00
//wl_event_loop_add_signal(m_sWLEventLoop, SIGINT, handleCritSignal, nullptr);
initManagers(STAGE_PRIORITY);
wlr_log_init(WLR_INFO, NULL);
const auto LOGWLR = getenv("HYPRLAND_LOG_WLR");
if (LOGWLR && std::string(LOGWLR) == "1")
wlr_log_init(WLR_DEBUG, Debug::wlrLog);
2022-11-19 17:28:04 +01:00
m_sWLRBackend = wlr_backend_autocreate(m_sWLDisplay, &m_sWLRSession);
2022-03-16 21:37:21 +01:00
if (!m_sWLRBackend) {
Debug::log(CRIT, "m_sWLRBackend was NULL!");
throw std::runtime_error("wlr_backend_autocreate() failed!");
2022-03-16 21:37:21 +01:00
}
2022-04-04 19:44:25 +02:00
m_iDRMFD = wlr_backend_get_drm_fd(m_sWLRBackend);
if (m_iDRMFD < 0) {
2022-03-24 15:57:46 +01:00
Debug::log(CRIT, "Couldn't query the DRM FD!");
throw std::runtime_error("wlr_backend_get_drm_fd() failed!");
2022-03-24 15:57:46 +01:00
}
2022-04-04 19:44:25 +02:00
m_sWLRRenderer = wlr_gles2_renderer_create_with_drm_fd(m_iDRMFD);
2022-03-16 21:37:21 +01:00
if (!m_sWLRRenderer) {
Debug::log(CRIT, "m_sWLRRenderer was NULL!");
throw std::runtime_error("wlr_gles2_renderer_create_with_drm_fd() failed!");
2022-03-16 21:37:21 +01:00
}
2022-11-05 13:50:47 +01:00
wlr_renderer_init_wl_shm(m_sWLRRenderer, m_sWLDisplay);
if (wlr_renderer_get_dmabuf_texture_formats(m_sWLRRenderer)) {
2022-12-01 22:00:54 +01:00
if (wlr_renderer_get_drm_fd(m_sWLRRenderer) >= 0)
2022-11-05 13:50:47 +01:00
wlr_drm_create(m_sWLDisplay, m_sWLRRenderer);
2022-12-01 22:00:54 +01:00
m_sWLRLinuxDMABuf = wlr_linux_dmabuf_v1_create_with_renderer(m_sWLDisplay, 4, m_sWLRRenderer);
2022-11-05 13:50:47 +01:00
}
2022-03-17 19:03:15 +01:00
2022-03-16 21:37:21 +01:00
m_sWLRAllocator = wlr_allocator_autocreate(m_sWLRBackend, m_sWLRRenderer);
if (!m_sWLRAllocator) {
Debug::log(CRIT, "m_sWLRAllocator was NULL!");
throw std::runtime_error("wlr_allocator_autocreate() failed!");
2022-03-16 21:37:21 +01:00
}
2022-03-24 17:17:08 +01:00
m_sWLREGL = wlr_gles2_renderer_get_egl(m_sWLRRenderer);
if (!m_sWLREGL) {
Debug::log(CRIT, "m_sWLREGL was NULL!");
throw std::runtime_error("wlr_gles2_renderer_get_egl() failed!");
2022-03-24 17:17:08 +01:00
}
2022-03-16 21:37:21 +01:00
m_sWLRCompositor = wlr_compositor_create(m_sWLDisplay, m_sWLRRenderer);
2022-03-17 19:03:15 +01:00
m_sWLRSubCompositor = wlr_subcompositor_create(m_sWLDisplay);
m_sWLRDataDevMgr = wlr_data_device_manager_create(m_sWLDisplay);
2022-03-17 19:03:15 +01:00
2022-03-19 14:09:11 +01:00
wlr_export_dmabuf_manager_v1_create(m_sWLDisplay);
wlr_data_control_manager_v1_create(m_sWLDisplay);
wlr_gamma_control_manager_v1_create(m_sWLDisplay);
wlr_primary_selection_v1_device_manager_create(m_sWLDisplay);
wlr_viewporter_create(m_sWLDisplay);
2022-03-19 14:09:11 +01:00
2022-03-16 21:37:21 +01:00
m_sWLROutputLayout = wlr_output_layout_create();
2022-07-30 22:41:24 +02:00
m_sWLROutputPowerMgr = wlr_output_power_manager_v1_create(m_sWLDisplay);
2022-03-17 19:03:15 +01:00
m_sWLRScene = wlr_scene_create();
wlr_scene_attach_output_layout(m_sWLRScene, m_sWLROutputLayout);
2022-03-16 21:37:21 +01:00
2022-10-20 19:04:21 +02:00
m_sWLRXDGShell = wlr_xdg_shell_create(m_sWLDisplay, 5);
2022-03-16 21:37:21 +01:00
m_sWLRCursor = wlr_cursor_create();
wlr_cursor_attach_output_layout(m_sWLRCursor, m_sWLROutputLayout);
2022-10-17 15:01:04 +02:00
if (const auto XCURSORENV = getenv("XCURSOR_SIZE"); !XCURSORENV || std::string(XCURSORENV).empty())
setenv("XCURSOR_SIZE", "24", true);
const auto XCURSORENV = getenv("XCURSOR_SIZE");
int cursorSize = 24;
try {
cursorSize = std::stoi(XCURSORENV);
} catch (std::exception& e) { Debug::log(ERR, "XCURSOR_SIZE invalid in check #2? (%s)", XCURSORENV); }
m_sWLRXCursorMgr = wlr_xcursor_manager_create(nullptr, cursorSize);
wlr_xcursor_manager_load(m_sWLRXCursorMgr, 1);
2022-03-22 18:29:13 +01:00
m_sSeat.seat = wlr_seat_create(m_sWLDisplay, "seat0");
2022-03-17 19:03:15 +01:00
2022-03-17 20:22:29 +01:00
m_sWLRPresentation = wlr_presentation_create(m_sWLDisplay, m_sWLRBackend);
2022-03-18 20:42:49 +01:00
m_sWLRIdle = wlr_idle_create(m_sWLDisplay);
2022-03-19 13:54:24 +01:00
2022-11-19 17:28:04 +01:00
m_sWLRLayerShell = wlr_layer_shell_v1_create(m_sWLDisplay, 4);
2022-03-19 13:54:24 +01:00
m_sWLRServerDecoMgr = wlr_server_decoration_manager_create(m_sWLDisplay);
m_sWLRXDGDecoMgr = wlr_xdg_decoration_manager_v1_create(m_sWLDisplay);
wlr_server_decoration_manager_set_default_mode(m_sWLRServerDecoMgr, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
2022-03-19 13:54:24 +01:00
wlr_xdg_output_manager_v1_create(m_sWLDisplay, m_sWLROutputLayout);
m_sWLROutputMgr = wlr_output_manager_v1_create(m_sWLDisplay);
2022-03-22 18:29:13 +01:00
m_sWLRInhibitMgr = wlr_input_inhibit_manager_create(m_sWLDisplay);
2022-03-22 18:29:13 +01:00
m_sWLRKbShInhibitMgr = wlr_keyboard_shortcuts_inhibit_v1_create(m_sWLDisplay);
m_sWLREXTWorkspaceMgr = wlr_ext_workspace_manager_v1_create(m_sWLDisplay);
m_sWLRPointerConstraints = wlr_pointer_constraints_v1_create(m_sWLDisplay);
m_sWLRRelPointerMgr = wlr_relative_pointer_manager_v1_create(m_sWLDisplay);
2022-04-27 17:29:33 +02:00
m_sWLRVKeyboardMgr = wlr_virtual_keyboard_manager_v1_create(m_sWLDisplay);
2022-05-12 12:13:02 +02:00
m_sWLRVirtPtrMgr = wlr_virtual_pointer_manager_v1_create(m_sWLDisplay);
2022-05-29 11:24:42 +02:00
m_sWLRToplevelMgr = wlr_foreign_toplevel_manager_v1_create(m_sWLDisplay);
m_sWRLDRMLeaseMgr = wlr_drm_lease_v1_manager_create(m_sWLDisplay, m_sWLRBackend);
2022-08-27 23:24:36 +02:00
if (!m_sWRLDRMLeaseMgr) {
Debug::log(INFO, "Failed to create wlr_drm_lease_v1_manager");
Debug::log(INFO, "VR will not be available");
2022-08-27 23:24:36 +02:00
}
m_sWLRTabletManager = wlr_tablet_v2_create(m_sWLDisplay);
2022-06-29 22:23:51 +02:00
m_sWLRForeignRegistry = wlr_xdg_foreign_registry_create(m_sWLDisplay);
m_sWLRIdleInhibitMgr = wlr_idle_inhibit_v1_create(m_sWLDisplay);
2022-06-29 22:23:51 +02:00
wlr_xdg_foreign_v1_create(m_sWLDisplay, m_sWLRForeignRegistry);
wlr_xdg_foreign_v2_create(m_sWLDisplay, m_sWLRForeignRegistry);
2022-07-07 11:52:12 +02:00
m_sWLRPointerGestures = wlr_pointer_gestures_v1_create(m_sWLDisplay);
2022-03-16 21:37:21 +01:00
2022-08-05 13:03:37 +02:00
m_sWLRTextInputMgr = wlr_text_input_manager_v3_create(m_sWLDisplay);
m_sWLRIMEMgr = wlr_input_method_manager_v2_create(m_sWLDisplay);
2022-10-22 17:43:47 +02:00
m_sWLRActivation = wlr_xdg_activation_v1_create(m_sWLDisplay);
m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLDisplay);
2023-02-03 12:58:55 +01:00
m_sWLRSessionLockMgr = wlr_session_lock_manager_v1_create(m_sWLDisplay);
if (!m_sWLRHeadlessBackend) {
Debug::log(CRIT, "Couldn't create the headless backend");
throw std::runtime_error("wlr_headless_backend_create() failed!");
}
2023-02-16 13:55:46 +01:00
wlr_single_pixel_buffer_manager_v1_create(m_sWLDisplay);
wlr_multi_backend_add(m_sWLRBackend, m_sWLRHeadlessBackend);
2022-07-10 15:41:26 +02:00
initManagers(STAGE_LATE);
Debug::log(LOG, "Disabling stdout logs! Check the log for further logs.");
Debug::disableStdout = true;
2022-07-10 15:41:26 +02:00
}
2022-03-18 23:52:36 +01:00
void CCompositor::initAllSignals() {
2022-03-28 16:10:30 +02:00
addWLSignal(&m_sWLRBackend->events.new_output, &Events::listen_newOutput, m_sWLRBackend, "Backend");
addWLSignal(&m_sWLRXDGShell->events.new_surface, &Events::listen_newXDGSurface, m_sWLRXDGShell, "XDG Shell");
addWLSignal(&m_sWLRCursor->events.motion, &Events::listen_mouseMove, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.motion_absolute, &Events::listen_mouseMoveAbsolute, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.button, &Events::listen_mouseButton, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.axis, &Events::listen_mouseAxis, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.frame, &Events::listen_mouseFrame, m_sWLRCursor, "WLRCursor");
2022-07-07 11:52:12 +02:00
addWLSignal(&m_sWLRCursor->events.swipe_begin, &Events::listen_swipeBegin, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.swipe_update, &Events::listen_swipeUpdate, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.swipe_end, &Events::listen_swipeEnd, m_sWLRCursor, "WLRCursor");
2022-07-16 16:12:31 +02:00
addWLSignal(&m_sWLRCursor->events.pinch_begin, &Events::listen_pinchBegin, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.pinch_update, &Events::listen_pinchUpdate, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.pinch_end, &Events::listen_pinchEnd, m_sWLRCursor, "WLRCursor");
2022-08-07 21:17:03 +02:00
addWLSignal(&m_sWLRCursor->events.touch_down, &Events::listen_touchBegin, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.touch_up, &Events::listen_touchEnd, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.touch_motion, &Events::listen_touchUpdate, m_sWLRCursor, "WLRCursor");
2022-09-22 22:14:02 +02:00
addWLSignal(&m_sWLRCursor->events.touch_frame, &Events::listen_touchFrame, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.hold_begin, &Events::listen_holdBegin, m_sWLRCursor, "WLRCursor");
addWLSignal(&m_sWLRCursor->events.hold_end, &Events::listen_holdEnd, m_sWLRCursor, "WLRCursor");
2022-03-28 16:10:30 +02:00
addWLSignal(&m_sWLRBackend->events.new_input, &Events::listen_newInput, m_sWLRBackend, "Backend");
addWLSignal(&m_sSeat.seat->events.request_set_cursor, &Events::listen_requestMouse, &m_sSeat, "Seat");
addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat");
addWLSignal(&m_sSeat.seat->events.request_start_drag, &Events::listen_requestDrag, &m_sSeat, "Seat");
2022-03-31 17:25:23 +02:00
addWLSignal(&m_sSeat.seat->events.start_drag, &Events::listen_startDrag, &m_sSeat, "Seat");
2022-07-17 18:56:01 +02:00
addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat");
addWLSignal(&m_sSeat.seat->events.request_set_primary_selection, &Events::listen_requestSetPrimarySel, &m_sSeat, "Seat");
2022-03-28 16:10:30 +02:00
addWLSignal(&m_sWLRLayerShell->events.new_surface, &Events::listen_newLayerSurface, m_sWLRLayerShell, "LayerShell");
addWLSignal(&m_sWLROutputLayout->events.change, &Events::listen_change, m_sWLROutputLayout, "OutputLayout");
addWLSignal(&m_sWLROutputMgr->events.apply, &Events::listen_outputMgrApply, m_sWLROutputMgr, "OutputMgr");
addWLSignal(&m_sWLROutputMgr->events.test, &Events::listen_outputMgrTest, m_sWLROutputMgr, "OutputMgr");
addWLSignal(&m_sWLRInhibitMgr->events.activate, &Events::listen_InhibitActivate, m_sWLRInhibitMgr, "InhibitMgr");
addWLSignal(&m_sWLRInhibitMgr->events.deactivate, &Events::listen_InhibitDeactivate, m_sWLRInhibitMgr, "InhibitMgr");
addWLSignal(&m_sWLRPointerConstraints->events.new_constraint, &Events::listen_newConstraint, m_sWLRPointerConstraints, "PointerConstraints");
addWLSignal(&m_sWLRXDGDecoMgr->events.new_toplevel_decoration, &Events::listen_NewXDGDeco, m_sWLRXDGDecoMgr, "XDGDecoMgr");
2022-05-12 12:13:02 +02:00
addWLSignal(&m_sWLRVirtPtrMgr->events.new_virtual_pointer, &Events::listen_newVirtPtr, m_sWLRVirtPtrMgr, "VirtPtrMgr");
2022-08-05 16:21:08 +02:00
addWLSignal(&m_sWLRVKeyboardMgr->events.new_virtual_keyboard, &Events::listen_newVirtualKeyboard, m_sWLRVKeyboardMgr, "VKeyboardMgr");
addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer");
addWLSignal(&m_sWLRIdleInhibitMgr->events.new_inhibitor, &Events::listen_newIdleInhibitor, m_sWLRIdleInhibitMgr, "WLRIdleInhibitMgr");
2022-07-30 22:41:24 +02:00
addWLSignal(&m_sWLROutputPowerMgr->events.set_mode, &Events::listen_powerMgrSetMode, m_sWLROutputPowerMgr, "PowerMgr");
2022-08-05 13:03:37 +02:00
addWLSignal(&m_sWLRIMEMgr->events.input_method, &Events::listen_newIME, m_sWLRIMEMgr, "IMEMgr");
addWLSignal(&m_sWLRTextInputMgr->events.text_input, &Events::listen_newTextInput, m_sWLRTextInputMgr, "TextInputMgr");
2022-10-22 17:43:47 +02:00
addWLSignal(&m_sWLRActivation->events.request_activate, &Events::listen_activateXDG, m_sWLRActivation, "ActivationV1");
2023-02-03 12:58:55 +01:00
addWLSignal(&m_sWLRSessionLockMgr->events.new_lock, &Events::listen_newSessionLock, m_sWLRSessionLockMgr, "SessionLockMgr");
2022-08-05 13:03:37 +02:00
if (m_sWRLDRMLeaseMgr)
addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM");
2022-07-13 18:31:09 +02:00
if (m_sWLRSession)
addWLSignal(&m_sWLRSession->events.active, &Events::listen_sessionActive, m_sWLRSession, "Session");
}
2022-07-13 18:18:23 +02:00
void CCompositor::cleanup() {
2022-08-25 21:35:47 +02:00
if (!m_sWLDisplay || m_bIsShuttingDown)
return;
m_bIsShuttingDown = true;
// unload all remaining plugins while the compositor is
// still in a normal working state.
g_pPluginSystem->unloadAllPlugins();
m_pLastFocus = nullptr;
2022-05-29 00:07:31 +02:00
m_pLastWindow = nullptr;
// end threads
g_pEventManager->m_tThread = std::thread();
2022-06-30 15:44:26 +02:00
m_vWorkspaces.clear();
m_vWindows.clear();
2022-08-25 21:35:47 +02:00
for (auto& m : m_vMonitors) {
2022-08-22 14:17:25 +02:00
g_pHyprOpenGL->destroyMonitorResources(m.get());
2022-08-25 21:35:47 +02:00
wlr_output_enable(m->output, false);
wlr_output_commit(m->output);
}
2022-10-06 19:43:50 +02:00
m_vMonitors.clear();
if (g_pXWaylandManager->m_sWLRXWayland) {
wlr_xwayland_destroy(g_pXWaylandManager->m_sWLRXWayland);
g_pXWaylandManager->m_sWLRXWayland = nullptr;
}
2023-03-09 15:19:41 +01:00
wl_display_destroy_clients(g_pCompositor->m_sWLDisplay);
2022-07-13 18:18:23 +02:00
wl_display_terminate(m_sWLDisplay);
2023-03-09 15:19:41 +01:00
m_sWLDisplay = nullptr;
2022-03-18 23:52:36 +01:00
}
2022-03-17 17:08:54 +01:00
void CCompositor::initManagers(eManagersInitStage stage) {
switch (stage) {
case STAGE_PRIORITY: {
Debug::log(LOG, "Creating the HookSystem!");
g_pHookSystem = std::make_unique<CHookSystemManager>();
2022-03-19 17:48:18 +01:00
Debug::log(LOG, "Creating the KeybindManager!");
g_pKeybindManager = std::make_unique<CKeybindManager>();
2022-04-23 21:47:16 +02:00
Debug::log(LOG, "Creating the AnimationManager!");
g_pAnimationManager = std::make_unique<CAnimationManager>();
2022-07-16 15:57:31 +02:00
Debug::log(LOG, "Creating the ConfigManager!");
g_pConfigManager = std::make_unique<CConfigManager>();
2022-03-17 17:08:54 +01:00
Debug::log(LOG, "Creating the CHyprError!");
g_pHyprError = std::make_unique<CHyprError>();
2023-01-20 20:48:07 +01:00
Debug::log(LOG, "Creating the LayoutManager!");
g_pLayoutManager = std::make_unique<CLayoutManager>();
2022-03-17 17:08:54 +01:00
g_pConfigManager->init();
} break;
case STAGE_LATE: {
Debug::log(LOG, "Creating the ThreadManager!");
g_pThreadManager = std::make_unique<CThreadManager>();
2022-03-17 20:22:29 +01:00
Debug::log(LOG, "Creating the InputManager!");
g_pInputManager = std::make_unique<CInputManager>();
2022-04-04 19:44:25 +02:00
Debug::log(LOG, "Creating the CHyprOpenGLImpl!");
g_pHyprOpenGL = std::make_unique<CHyprOpenGLImpl>();
2022-03-18 20:03:39 +01:00
Debug::log(LOG, "Creating the HyprRenderer!");
g_pHyprRenderer = std::make_unique<CHyprRenderer>();
Debug::log(LOG, "Creating the XWaylandManager!");
g_pXWaylandManager = std::make_unique<CHyprXWaylandManager>();
Debug::log(LOG, "Creating the ProtocolManager!");
g_pProtocolManager = std::make_unique<CProtocolManager>();
2023-02-03 12:58:55 +01:00
Debug::log(LOG, "Creating the SessionLockManager!");
g_pSessionLockManager = std::make_unique<CSessionLockManager>();
2022-05-28 17:32:19 +02:00
Debug::log(LOG, "Creating the EventManager!");
g_pEventManager = std::make_unique<CEventManager>();
g_pEventManager->startThread();
Debug::log(LOG, "Creating the HyprDebugOverlay!");
g_pDebugOverlay = std::make_unique<CHyprDebugOverlay>();
Debug::log(LOG, "Creating the HyprNotificationOverlay!");
g_pHyprNotificationOverlay = std::make_unique<CHyprNotificationOverlay>();
2022-03-17 17:08:54 +01:00
Debug::log(LOG, "Creating the PluginSystem!");
g_pPluginSystem = std::make_unique<CPluginSystem>();
} break;
default: UNREACHABLE();
}
}
2022-08-01 23:31:25 +02:00
void CCompositor::startCompositor() {
2022-03-18 23:52:36 +01:00
initAllSignals();
2022-12-05 18:21:09 +01:00
// get socket, avoid using 0
for (int candidate = 1; candidate <= 32; candidate++) {
const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate));
const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
if (RETVAL >= 0) {
m_szWLDisplaySocket = CANDIDATESTR;
Debug::log(LOG, "wl_display_add_socket for %s succeeded with %i", CANDIDATESTR.c_str(), RETVAL);
2022-12-05 18:21:09 +01:00
break;
} else {
Debug::log(WARN, "wl_display_add_socket for %s returned %i: skipping candidate %i", CANDIDATESTR.c_str(), RETVAL, candidate);
2022-12-05 18:21:09 +01:00
}
}
2022-03-16 21:37:21 +01:00
if (m_szWLDisplaySocket.empty()) {
Debug::log(WARN, "All candidates failed, trying wl_display_add_socket_auto");
const auto SOCKETSTR = wl_display_add_socket_auto(m_sWLDisplay);
if (SOCKETSTR)
m_szWLDisplaySocket = SOCKETSTR;
}
2022-12-05 18:21:09 +01:00
if (m_szWLDisplaySocket.empty()) {
2022-03-16 21:37:21 +01:00
Debug::log(CRIT, "m_szWLDisplaySocket NULL!");
2022-03-17 19:03:15 +01:00
wlr_backend_destroy(m_sWLRBackend);
throw std::runtime_error("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
2022-03-16 21:37:21 +01:00
}
2022-12-05 18:21:09 +01:00
setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
2022-03-16 21:37:21 +01:00
signal(SIGPIPE, SIG_IGN);
if (m_sWLRSession /* Session-less Hyprland usually means a nest, don't update the env in that case */ && fork() == 0)
2023-03-04 16:20:23 +01:00
execl(
"/bin/sh", "/bin/sh", "-c",
"systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP && hash dbus-update-activation-environment 2>/dev/null && "
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE",
nullptr);
2022-12-07 23:47:32 +01:00
Debug::log(LOG, "Running on WAYLAND_DISPLAY: %s", m_szWLDisplaySocket.c_str());
2022-03-16 21:37:21 +01:00
if (!wlr_backend_start(m_sWLRBackend)) {
Debug::log(CRIT, "Backend did not start!");
2022-03-17 19:03:15 +01:00
wlr_backend_destroy(m_sWLRBackend);
wl_display_destroy(m_sWLDisplay);
throw std::runtime_error("The backend could not start!");
2022-03-16 21:37:21 +01:00
}
wlr_xcursor_manager_set_cursor_image(m_sWLRXCursorMgr, "left_ptr", m_sWLRCursor);
#ifdef USES_SYSTEMD
if (sd_booted() > 0)
// tell systemd that we are ready so it can start other bond, following, related units
sd_notify(0, "READY=1");
else
Debug::log(LOG, "systemd integration is baked in but system itself is not booted à la systemd!");
#endif
2022-03-16 21:37:21 +01:00
// This blocks until we are done.
Debug::log(LOG, "Hyprland is ready, running the event loop!");
wl_display_run(m_sWLDisplay);
2022-03-17 20:22:29 +01:00
}
2022-07-27 12:32:00 +02:00
CMonitor* CCompositor::getMonitorFromID(const int& id) {
2022-06-30 15:44:26 +02:00
for (auto& m : m_vMonitors) {
if (m->ID == (uint64_t)id) {
return m.get();
2022-03-17 20:22:29 +01:00
}
}
return nullptr;
}
2022-07-27 12:32:00 +02:00
CMonitor* CCompositor::getMonitorFromName(const std::string& name) {
2022-06-30 15:44:26 +02:00
for (auto& m : m_vMonitors) {
if (m->szName == name) {
return m.get();
}
}
return nullptr;
}
2022-07-27 12:32:00 +02:00
CMonitor* CCompositor::getMonitorFromCursor() {
2022-03-19 20:30:21 +01:00
const auto COORDS = Vector2D(m_sWLRCursor->x, m_sWLRCursor->y);
2022-03-17 20:22:29 +01:00
return getMonitorFromVector(COORDS);
2022-03-18 22:35:51 +01:00
}
2022-07-27 12:32:00 +02:00
CMonitor* CCompositor::getMonitorFromVector(const Vector2D& point) {
const auto OUTPUT = wlr_output_layout_output_at(m_sWLROutputLayout, point.x, point.y);
if (!OUTPUT) {
float bestDistance = 0.f;
CMonitor* pBestMon = nullptr;
2022-06-30 15:44:26 +02:00
for (auto& m : m_vMonitors) {
float dist = vecToRectDistanceSquared(point, m->vecPosition, m->vecPosition + m->vecSize);
if (dist < bestDistance || !pBestMon) {
bestDistance = dist;
pBestMon = m.get();
}
}
if (!pBestMon) { // ?????
Debug::log(WARN, "getMonitorFromVector no close mon???");
2022-06-30 15:44:26 +02:00
return m_vMonitors.front().get();
}
return pBestMon;
}
return getMonitorFromOutput(OUTPUT);
}
2022-03-18 22:35:51 +01:00
void CCompositor::removeWindowFromVectorSafe(CWindow* pWindow) {
if (windowExists(pWindow) && !pWindow->m_bFadingOut) {
2022-06-30 15:44:26 +02:00
// if X11, also check its children
// and delete any needed
if (pWindow->m_bIsX11) {
for (auto& w : m_vWindows) {
if (!w->m_bIsX11)
continue;
if (w->m_pX11Parent == pWindow)
2022-12-07 15:32:24 +01:00
std::erase_if(m_vWindows, [&](std::unique_ptr<CWindow>& el) { return el.get() == w.get(); });
2022-06-30 15:44:26 +02:00
}
}
2022-12-07 15:32:24 +01:00
std::erase_if(m_vWindows, [&](std::unique_ptr<CWindow>& el) { return el.get() == pWindow; });
2022-06-30 15:44:26 +02:00
}
2022-03-18 22:53:27 +01:00
}
bool CCompositor::windowExists(CWindow* pWindow) {
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
if (w.get() == pWindow)
2022-03-18 22:53:27 +01:00
return true;
}
return false;
}
CWindow* CCompositor::vectorToWindow(const Vector2D& pos) {
2022-03-20 15:55:47 +01:00
const auto PMONITOR = getMonitorFromVector(pos);
2022-05-31 14:01:00 +02:00
2022-11-27 23:42:22 +01:00
if (PMONITOR->specialWorkspaceID) {
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (w->m_bIsFloating && w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && !w->isHidden())
return w.get();
}
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
2022-11-27 23:42:22 +01:00
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && !w->m_bIsFloating && !w->isHidden())
2022-06-30 15:44:26 +02:00
return w.get();
2022-05-31 14:01:00 +02:00
}
}
2022-09-10 13:11:02 +02:00
// pinned
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && w->m_bIsFloating && !w->isHidden() && w->m_bPinned)
return w.get();
2022-09-10 13:11:02 +02:00
}
2022-06-30 15:44:26 +02:00
// first loop over floating cuz they're above, m_vWindows should be sorted bottom->top, for tiled it doesn't matter.
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() && !w->m_bPinned)
return w.get();
}
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && !w->m_bIsFloating && PMONITOR->activeWorkspace == w->m_iWorkspaceID && !w->isHidden())
2022-06-30 15:44:26 +02:00
return w.get();
2022-03-18 22:53:27 +01:00
}
return nullptr;
}
2022-03-20 18:31:58 +01:00
CWindow* CCompositor::vectorToWindowTiled(const Vector2D& pos) {
const auto PMONITOR = getMonitorFromVector(pos);
2022-05-31 14:01:00 +02:00
2022-11-27 23:42:22 +01:00
if (PMONITOR->specialWorkspaceID) {
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
2022-11-27 23:42:22 +01:00
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && wlr_box_contains_point(&box, pos.x, pos.y) && !w->m_bIsFloating && !w->isHidden())
2022-06-30 15:44:26 +02:00
return w.get();
2022-05-31 14:01:00 +02:00
}
}
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->m_bIsFloating && !w->isHidden())
2022-06-30 15:44:26 +02:00
return w.get();
2022-03-20 18:31:58 +01:00
}
return nullptr;
}
2022-03-19 17:48:18 +01:00
CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
2023-02-18 23:35:31 +01:00
const auto PMONITOR = getMonitorFromVector(pos);
static auto* const PRESIZEONBORDER = &g_pConfigManager->getConfigValuePtr("general:resize_on_border")->intValue;
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
static auto* const PBORDERGRABEXTEND = &g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area")->intValue;
const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0;
2022-05-31 14:01:00 +02:00
// special workspace
2022-11-27 23:42:22 +01:00
if (PMONITOR->specialWorkspaceID) {
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
const auto BB = w->getWindowInputBox();
2023-02-28 23:32:42 +01:00
wlr_box box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA};
2023-03-01 15:06:52 +01:00
if (w->m_bIsFloating && w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && !w->isHidden() &&
!w->m_bX11ShouldntFocus)
return w.get();
}
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (!w->m_bIsFloating && w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && !w->isHidden() &&
!w->m_bX11ShouldntFocus)
2022-06-30 15:44:26 +02:00
return w.get();
2022-05-31 14:01:00 +02:00
}
}
2022-09-10 13:11:02 +02:00
// pinned windows on top of floating regardless
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
const auto BB = w->getWindowInputBox();
2023-02-28 23:32:42 +01:00
wlr_box box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA};
2023-03-01 15:06:52 +01:00
if (w->m_bIsFloating && w->m_bIsMapped && !w->isHidden() && !w->m_bX11ShouldntFocus && w->m_bPinned) {
2022-09-10 13:11:02 +02:00
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y))
2023-03-01 15:06:52 +01:00
return w.get();
2022-09-10 13:11:02 +02:00
2023-03-01 15:06:52 +01:00
if (!w->m_bIsX11) {
if (w->hasPopupAt(pos))
return w.get();
2022-09-10 13:11:02 +02:00
}
2022-09-25 20:07:48 +02:00
}
2022-09-10 13:11:02 +02:00
}
2022-04-04 16:25:30 +02:00
// first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter.
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
const auto BB = w->getWindowInputBox();
2023-02-28 23:32:42 +01:00
wlr_box box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA};
2023-03-01 15:06:52 +01:00
if (w->m_bIsFloating && w->m_bIsMapped && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() && !w->m_bPinned) {
2022-10-01 19:25:02 +02:00
// OR windows should add focus to parent
2023-03-01 15:06:52 +01:00
if (w->m_bX11ShouldntFocus && w->m_iX11Type != 2)
2022-10-01 19:25:02 +02:00
continue;
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y)) {
2023-03-01 15:06:52 +01:00
if (w->m_bIsX11 && w->m_iX11Type == 2 && !wlr_xwayland_or_surface_wants_focus(w->m_uSurface.xwayland)) {
2022-10-01 19:25:02 +02:00
// Override Redirect
return g_pCompositor->m_pLastWindow; // we kinda trick everything here.
2022-10-01 19:25:02 +02:00
// TODO: this is wrong, we should focus the parent, but idk how to get it considering it's nullptr in most cases.
}
2023-03-01 15:06:52 +01:00
return w.get();
2022-10-01 19:25:02 +02:00
}
2023-03-01 15:06:52 +01:00
if (!w->m_bIsX11) {
if (w->hasPopupAt(pos))
return w.get();
}
2022-09-25 20:07:48 +02:00
}
2022-03-20 11:14:24 +01:00
}
// for windows, we need to check their extensions too, first.
for (auto& w : m_vWindows) {
if (!w->m_bIsX11 && !w->m_bIsFloating && w->m_bIsMapped && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->isHidden() && !w->m_bX11ShouldntFocus) {
2023-02-18 23:35:31 +01:00
if ((w)->hasPopupAt(pos))
return w.get();
}
}
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (!w->m_bIsFloating && w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->isHidden() &&
!w->m_bX11ShouldntFocus)
2022-06-30 15:44:26 +02:00
return w.get();
2022-03-19 20:30:21 +01:00
}
return nullptr;
}
CWindow* CCompositor::windowFromCursor() {
const auto PMONITOR = getMonitorFromCursor();
2022-03-20 11:14:24 +01:00
2022-11-27 23:42:22 +01:00
if (PMONITOR->specialWorkspaceID) {
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (w->m_bIsFloating && w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && w->m_bIsMapped && wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) &&
!w->isHidden())
return w.get();
}
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
2022-11-27 23:42:22 +01:00
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped)
2022-06-30 15:44:26 +02:00
return w.get();
2022-05-31 14:01:00 +02:00
}
}
2022-09-10 13:11:02 +02:00
// pinned
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_bIsFloating && w->m_bPinned)
return w.get();
2022-09-10 13:11:02 +02:00
}
2022-04-04 16:25:30 +02:00
// first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter.
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->m_bPinned)
return w.get();
2022-03-20 11:14:24 +01:00
}
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_iWorkspaceID == PMONITOR->activeWorkspace)
return w.get();
2022-03-19 17:48:18 +01:00
}
return nullptr;
}
2022-03-20 11:14:24 +01:00
CWindow* CCompositor::windowFloatingFromCursor() {
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_bIsFloating && !w->isHidden() && w->m_bPinned)
return w.get();
2022-09-10 13:11:02 +02:00
}
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() &&
!w->m_bPinned)
return w.get();
2022-03-20 11:14:24 +01:00
}
return nullptr;
}
2022-04-02 13:02:16 +02:00
wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, CWindow* pWindow, Vector2D& sl) {
if (!windowValidMapped(pWindow))
return nullptr;
RASSERT(!pWindow->m_bIsX11, "Cannot call vectorWindowToSurface on an X11 window!");
const auto PSURFACE = pWindow->m_uSurface.xdg;
2022-09-25 20:07:48 +02:00
double subx, suby;
2022-04-02 13:02:16 +02:00
2022-06-22 15:45:56 +02:00
// calc for oversized windows... fucking bullshit, again.
wlr_box geom;
2022-06-22 15:45:56 +02:00
wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, &geom);
const auto PFOUND = wlr_xdg_surface_surface_at(PSURFACE, pos.x - pWindow->m_vRealPosition.vec().x + geom.x, pos.y - pWindow->m_vRealPosition.vec().y + geom.y, &subx, &suby);
2022-04-02 13:02:16 +02:00
if (PFOUND) {
sl.x = subx;
sl.y = suby;
return PFOUND;
}
2022-04-23 14:16:02 +02:00
sl.x = pos.x - pWindow->m_vRealPosition.vec().x;
sl.y = pos.y - pWindow->m_vRealPosition.vec().y;
2022-04-02 13:02:16 +02:00
2022-06-22 15:45:56 +02:00
sl.x += geom.x;
sl.y += geom.y;
2022-04-02 13:02:16 +02:00
return PSURFACE->surface;
}
2022-07-27 12:32:00 +02:00
CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) {
2022-06-30 15:44:26 +02:00
for (auto& m : m_vMonitors) {
if (m->output == out) {
return m.get();
2022-03-19 20:56:19 +01:00
}
}
return nullptr;
}
2022-04-02 18:57:09 +02:00
void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
2022-03-18 22:53:27 +01:00
2022-04-18 17:16:01 +02:00
if (g_pCompositor->m_sSeat.exclusiveClient) {
Debug::log(LOG, "Disallowing setting focus to a window due to there being an active input inhibitor layer.");
return;
}
2022-05-14 20:56:21 +02:00
if (!pWindow || !windowValidMapped(pWindow)) {
2022-08-08 20:42:14 +02:00
const auto PLASTWINDOW = m_pLastWindow;
m_pLastWindow = nullptr;
2022-08-08 20:42:14 +02:00
if (windowValidMapped(PLASTWINDOW)) {
updateWindowAnimatedDecorationValues(PLASTWINDOW);
2022-10-28 20:18:10 +02:00
g_pXWaylandManager->activateWindow(PLASTWINDOW, false);
2022-08-08 20:42:14 +02:00
if (PLASTWINDOW->m_phForeignToplevel)
wlr_foreign_toplevel_handle_v1_set_activated(PLASTWINDOW->m_phForeignToplevel, false);
}
2022-05-14 20:56:21 +02:00
wlr_seat_keyboard_notify_clear_focus(m_sSeat.seat);
2022-08-21 18:34:38 +02:00
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","});
2023-02-06 14:16:54 +01:00
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ","});
2022-08-21 18:34:38 +02:00
EMIT_HOOK_EVENT("activeWindow", nullptr);
g_pLayoutManager->getCurrentLayout()->onWindowFocusChange(nullptr);
m_pLastFocus = nullptr;
2022-10-31 13:26:07 +01:00
g_pInputManager->recheckIdleInhibitorStatus();
2022-05-14 14:37:57 +02:00
return;
}
2022-05-14 20:56:21 +02:00
if (pWindow->m_bNoFocus) {
Debug::log(LOG, "Ignoring focus to nofocus window!");
2022-03-18 22:53:27 +01:00
return;
}
if (m_pLastWindow == pWindow && m_sSeat.seat->keyboard_state.focused_surface == pSurface)
2022-04-05 18:29:58 +02:00
return;
2022-09-10 13:11:02 +02:00
if (pWindow->m_bPinned)
pWindow->m_iWorkspaceID = m_pLastMonitor->activeWorkspace;
2023-01-20 20:57:35 +01:00
if (!isWorkspaceVisible(pWindow->m_iWorkspaceID)) {
// This is to fix incorrect feedback on the focus history.
const auto PWORKSPACE = getWorkspaceByID(pWindow->m_iWorkspaceID);
PWORKSPACE->m_pLastFocusedWindow = pWindow;
g_pKeybindManager->changeworkspace("[internal]" + std::to_string(pWindow->m_iWorkspaceID));
2023-01-20 20:57:35 +01:00
// changeworkspace already calls focusWindow
return;
}
2022-04-23 14:16:02 +02:00
const auto PLASTWINDOW = m_pLastWindow;
m_pLastWindow = pWindow;
2022-04-23 14:16:02 +02:00
// we need to make the PLASTWINDOW not equal to m_pLastWindow so that RENDERDATA is correct for an unfocused window
if (windowValidMapped(PLASTWINDOW)) {
2022-07-12 13:40:55 +02:00
updateWindowAnimatedDecorationValues(PLASTWINDOW);
2022-04-23 14:16:02 +02:00
2022-10-28 21:12:17 +02:00
if (!pWindow->m_bIsX11 || pWindow->m_iX11Type == 1)
g_pXWaylandManager->activateWindow(PLASTWINDOW, false);
2022-05-29 11:24:42 +02:00
if (PLASTWINDOW->m_phForeignToplevel)
wlr_foreign_toplevel_handle_v1_set_activated(PLASTWINDOW->m_phForeignToplevel, false);
2022-04-05 18:29:58 +02:00
}
2022-04-23 14:16:02 +02:00
m_pLastWindow = PLASTWINDOW;
2023-03-20 16:00:58 +01:00
const auto PWINDOWSURFACE = pSurface ? pSurface : pWindow->m_pWLSurface.wlr();
2022-03-18 22:53:27 +01:00
2022-04-02 18:57:09 +02:00
focusSurface(PWINDOWSURFACE, pWindow);
2022-04-23 14:16:02 +02:00
g_pXWaylandManager->activateWindow(pWindow, true); // sets the m_pLastWindow
2022-04-02 18:57:09 +02:00
2022-09-25 20:07:48 +02:00
// do pointer focus too
const auto POINTERLOCAL = g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition.goalv();
wlr_seat_pointer_notify_enter(m_sSeat.seat, PWINDOWSURFACE, POINTERLOCAL.x, POINTERLOCAL.y);
2022-04-13 20:19:40 +02:00
2022-07-12 13:40:55 +02:00
updateWindowAnimatedDecorationValues(pWindow);
// Handle urgency hint on the workspace
if (pWindow->m_bIsUrgent) {
pWindow->m_bIsUrgent = false;
if (!hasUrgentWindowOnWorkspace(pWindow->m_iWorkspaceID)) {
const auto PWORKSPACE = getWorkspaceByID(pWindow->m_iWorkspaceID);
if (PWORKSPACE->m_pWlrHandle) {
wlr_ext_workspace_handle_v1_set_urgent(PWORKSPACE->m_pWlrHandle, 0);
}
}
}
// Send an event
2022-07-20 18:39:08 +02:00
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", g_pXWaylandManager->getAppIDClass(pWindow) + "," + pWindow->m_szTitle});
2023-02-06 14:16:54 +01:00
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", getFormat("%x", pWindow)});
2022-05-29 11:24:42 +02:00
EMIT_HOOK_EVENT("activeWindow", pWindow);
g_pLayoutManager->getCurrentLayout()->onWindowFocusChange(pWindow);
2022-05-29 11:24:42 +02:00
if (pWindow->m_phForeignToplevel)
wlr_foreign_toplevel_handle_v1_set_activated(pWindow->m_phForeignToplevel, true);
2022-10-28 20:20:12 +02:00
if (!pWindow->m_bIsX11) {
const auto PCONSTRAINT = wlr_pointer_constraints_v1_constraint_for_surface(m_sWLRPointerConstraints, pWindow->m_uSurface.xdg->surface, m_sSeat.seat);
if (PCONSTRAINT)
g_pInputManager->constrainMouse(m_sSeat.mouse, PCONSTRAINT);
}
2022-10-31 13:26:07 +01:00
g_pInputManager->recheckIdleInhibitorStatus();
2023-01-20 19:15:15 +01:00
// move to front of the window history
const auto HISTORYPIVOT = std::find_if(m_vWindowFocusHistory.begin(), m_vWindowFocusHistory.end(), [&](const auto& other) { return other == pWindow; });
if (HISTORYPIVOT == m_vWindowFocusHistory.end()) {
Debug::log(ERR, "BUG THIS: Window %x has no pivot in history", pWindow);
} else {
std::rotate(m_vWindowFocusHistory.begin(), HISTORYPIVOT, HISTORYPIVOT + 1);
}
2022-03-20 14:36:55 +01:00
}
2022-04-02 13:02:16 +02:00
void CCompositor::focusSurface(wlr_surface* pSurface, CWindow* pWindowOwner) {
2022-04-18 17:16:01 +02:00
2023-03-20 16:00:58 +01:00
if (m_sSeat.seat->keyboard_state.focused_surface == pSurface || (pWindowOwner && m_sSeat.seat->keyboard_state.focused_surface == pWindowOwner->m_pWLSurface.wlr()))
return; // Don't focus when already focused on this.
2022-03-20 14:36:55 +01:00
2023-02-12 20:20:13 +01:00
if (g_pSessionLockManager->isSessionLocked()) {
wlr_seat_keyboard_clear_focus(m_sSeat.seat);
m_pLastFocus = nullptr;
}
2022-07-07 21:47:59 +02:00
// Unfocus last surface if should
2022-10-28 20:18:10 +02:00
if (m_pLastFocus && !pWindowOwner)
2022-07-07 21:47:59 +02:00
g_pXWaylandManager->activateSurface(m_pLastFocus, false);
2022-07-11 12:29:50 +02:00
if (!pSurface) {
wlr_seat_keyboard_clear_focus(m_sSeat.seat);
2022-07-20 18:39:08 +02:00
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","}); // unfocused
2023-02-06 14:16:54 +01:00
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ","});
EMIT_HOOK_EVENT("keyboardFocus", (wlr_surface*)nullptr);
2023-02-12 20:20:13 +01:00
m_pLastFocus = nullptr;
2022-03-27 19:16:33 +02:00
return;
2022-07-11 12:29:50 +02:00
}
2022-09-25 20:07:48 +02:00
2022-03-22 18:29:13 +01:00
const auto KEYBOARD = wlr_seat_get_keyboard(m_sSeat.seat);
2022-03-27 19:16:33 +02:00
if (!KEYBOARD)
return;
2022-03-22 18:29:13 +01:00
wlr_seat_keyboard_notify_enter(m_sSeat.seat, pSurface, KEYBOARD->keycodes, KEYBOARD->num_keycodes, &KEYBOARD->modifiers);
2022-03-18 22:53:27 +01:00
2022-04-02 19:09:27 +02:00
wlr_seat_keyboard_focus_change_event event = {
.seat = m_sSeat.seat,
2022-04-02 19:09:27 +02:00
.old_surface = m_pLastFocus,
.new_surface = pSurface,
};
2022-08-20 20:06:24 +02:00
wl_signal_emit_mutable(&m_sSeat.seat->keyboard_state.events.focus_change, &event);
2022-04-02 19:09:27 +02:00
if (pWindowOwner)
Debug::log(LOG, "Set keyboard focus to surface %x, with window name: %s", pSurface, pWindowOwner->m_szTitle.c_str());
2022-03-31 19:41:55 +02:00
else
Debug::log(LOG, "Set keyboard focus to surface %x", pSurface);
2022-07-08 13:19:57 +02:00
2022-11-02 19:54:41 +01:00
g_pXWaylandManager->activateSurface(pSurface, true);
2022-07-08 13:19:57 +02:00
m_pLastFocus = pSurface;
EMIT_HOOK_EVENT("keyboardFocus", pSurface);
2022-03-18 23:16:15 +01:00
}
bool CCompositor::windowValidMapped(CWindow* pWindow) {
if (!pWindow)
return false;
2022-03-18 23:16:15 +01:00
if (!windowExists(pWindow))
return false;
if (pWindow->m_bIsX11 && !pWindow->m_bMappedX11)
return false;
2022-03-22 20:53:11 +01:00
if (!pWindow->m_bIsMapped)
return false;
if (pWindow->isHidden())
return false;
2022-03-18 23:16:15 +01:00
return true;
2022-03-20 12:11:57 +01:00
}
2022-03-20 14:00:46 +01:00
CWindow* CCompositor::getWindowForPopup(wlr_xdg_popup* popup) {
2022-06-30 15:44:26 +02:00
for (auto& p : m_vXDGPopups) {
if (p->popup == popup)
return p->parentWindow;
2022-03-20 14:00:46 +01:00
}
2022-03-20 14:36:55 +01:00
return nullptr;
}
wlr_surface* CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector<std::unique_ptr<SLayerSurface>>* layerSurfaces, Vector2D* sCoords,
SLayerSurface** ppLayerSurfaceFound) {
2023-03-01 15:06:52 +01:00
for (auto& ls : *layerSurfaces | std::views::reverse) {
if (ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->mapped) || ls->alpha.fl() == 0.f)
2022-03-20 14:36:55 +01:00
continue;
2023-03-01 15:06:52 +01:00
auto SURFACEAT = wlr_layer_surface_v1_surface_at(ls->layerSurface, pos.x - ls->geometry.x, pos.y - ls->geometry.y, &sCoords->x, &sCoords->y);
2023-03-01 15:06:52 +01:00
if (!SURFACEAT && VECINRECT(pos, ls->geometry.x, ls->geometry.y, ls->geometry.x + ls->geometry.width, ls->geometry.y + ls->geometry.height)) {
SURFACEAT = ls->layerSurface->surface;
}
2022-03-20 14:36:55 +01:00
2023-03-01 15:06:52 +01:00
if (ls->layerSurface->current.keyboard_interactive && ls->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
if (!SURFACEAT)
2023-03-01 15:06:52 +01:00
SURFACEAT = ls->layerSurface->surface;
2023-03-01 15:06:52 +01:00
*ppLayerSurfaceFound = ls.get();
return SURFACEAT;
}
if (SURFACEAT) {
if (!pixman_region32_not_empty(&SURFACEAT->input_region))
continue;
2023-03-01 15:06:52 +01:00
*ppLayerSurfaceFound = ls.get();
2022-03-20 14:36:55 +01:00
return SURFACEAT;
}
2022-03-20 14:36:55 +01:00
}
return nullptr;
}
CWindow* CCompositor::getWindowFromSurface(wlr_surface* pSurface) {
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
if (!w->m_bIsMapped || w->m_bFadingOut || !w->m_bMappedX11)
continue;
2023-03-20 16:00:58 +01:00
if (w->m_pWLSurface.wlr() == pSurface)
2022-06-30 15:44:26 +02:00
return w.get();
2022-03-20 14:36:55 +01:00
}
2022-03-20 15:55:47 +01:00
return nullptr;
}
CWindow* CCompositor::getWindowFromHandle(uint32_t handle) {
for (auto& w : m_vWindows) {
2022-12-05 18:57:59 +01:00
if ((uint32_t)(((uint64_t)w.get()) & 0xFFFFFFFF) == handle) {
return w.get();
2022-12-05 18:57:59 +01:00
}
}
return nullptr;
}
CWindow* CCompositor::getWindowFromZWLRHandle(wl_resource* handle) {
for (auto& w : m_vWindows) {
if (!w->m_bIsMapped || w->isHidden() || !w->m_phForeignToplevel)
continue;
wl_resource* current;
wl_list_for_each(current, &w->m_phForeignToplevel->resources, link) {
if (current == handle) {
return w.get();
}
}
}
return nullptr;
}
2022-03-21 19:18:33 +01:00
CWindow* CCompositor::getFullscreenWindowOnWorkspace(const int& ID) {
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == ID && w->m_bIsFullscreen)
return w.get();
2022-03-21 19:18:33 +01:00
}
return nullptr;
}
2022-03-20 15:55:47 +01:00
bool CCompositor::isWorkspaceVisible(const int& w) {
2022-06-30 15:44:26 +02:00
for (auto& m : m_vMonitors) {
if (m->activeWorkspace == w)
2022-03-20 15:55:47 +01:00
return true;
2022-09-25 20:07:48 +02:00
2022-11-27 23:42:22 +01:00
if (m->specialWorkspaceID && isWorkspaceSpecial(w))
2022-05-31 14:01:00 +02:00
return true;
2022-03-20 15:55:47 +01:00
}
return false;
}
CWorkspace* CCompositor::getWorkspaceByID(const int& id) {
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWorkspaces) {
if (w->m_iID == id)
return w.get();
2022-03-20 15:55:47 +01:00
}
return nullptr;
}
void CCompositor::sanityCheckWorkspaces() {
2022-12-29 17:06:30 +01:00
auto it = m_vWorkspaces.begin();
while (it != m_vWorkspaces.end()) {
2022-06-30 16:11:26 +02:00
const auto WINDOWSONWORKSPACE = getWindowsOnWorkspace((*it)->m_iID);
2022-06-30 16:00:44 +02:00
2022-11-27 23:42:22 +01:00
if ((*it)->m_bIsSpecialWorkspace && WINDOWSONWORKSPACE == 0) {
getMonitorFromID((*it)->m_iMonitorID)->specialWorkspaceID = 0;
2022-05-31 14:01:00 +02:00
2022-06-30 15:44:26 +02:00
it = m_vWorkspaces.erase(it);
2022-12-29 17:06:30 +01:00
continue;
}
2022-06-30 16:11:26 +02:00
2022-12-29 17:06:30 +01:00
if ((WINDOWSONWORKSPACE == 0 && !isWorkspaceVisible((*it)->m_iID))) {
it = m_vWorkspaces.erase(it);
continue;
2022-05-31 14:01:00 +02:00
}
2022-12-29 17:06:30 +01:00
++it;
2022-03-20 15:55:47 +01:00
}
}
int CCompositor::getWindowsOnWorkspace(const int& id) {
int no = 0;
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == id && w->m_bIsMapped)
2022-03-20 15:55:47 +01:00
no++;
}
return no;
}
CWindow* CCompositor::getUrgentWindow() {
for (auto& w : m_vWindows) {
if (w->m_bIsMapped && w->m_bIsUrgent)
return w.get();
}
return nullptr;
}
bool CCompositor::hasUrgentWindowOnWorkspace(const int& id) {
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == id && w->m_bIsMapped && w->m_bIsUrgent)
return true;
}
return false;
}
2022-03-20 15:55:47 +01:00
CWindow* CCompositor::getFirstWindowOnWorkspace(const int& id) {
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == id && w->m_bIsMapped && !w->isHidden())
2022-06-30 15:44:26 +02:00
return w.get();
2022-03-20 15:55:47 +01:00
}
2022-03-20 14:00:46 +01:00
return nullptr;
}
2022-03-22 18:29:13 +01:00
bool CCompositor::doesSeatAcceptInput(wlr_surface* surface) {
2023-02-03 12:58:55 +01:00
if (g_pSessionLockManager->isSessionLocked()) {
if (g_pSessionLockManager->isSurfaceSessionLock(surface))
return true;
if (surface && m_sSeat.exclusiveClient == wl_resource_get_client(surface->resource))
return true;
return false;
}
if (m_sSeat.exclusiveClient) {
if (surface && m_sSeat.exclusiveClient == wl_resource_get_client(surface->resource))
return true;
return false;
}
return true;
2022-04-02 13:02:16 +02:00
}
bool CCompositor::isWindowActive(CWindow* pWindow) {
2022-05-29 00:07:31 +02:00
if (!m_pLastWindow && !m_pLastFocus)
return false;
2022-04-02 18:57:09 +02:00
if (!windowValidMapped(pWindow))
return false;
2023-03-20 16:00:58 +01:00
const auto PSURFACE = pWindow->m_pWLSurface.wlr();
2022-04-02 13:02:16 +02:00
2022-04-02 18:57:09 +02:00
return PSURFACE == m_pLastFocus || pWindow == m_pLastWindow;
2022-04-04 16:25:30 +02:00
}
void CCompositor::moveWindowToTop(CWindow* pWindow) {
if (!windowValidMapped(pWindow))
return;
auto moveToTop = [&](CWindow* pw) -> void {
for (auto it = m_vWindows.begin(); it != m_vWindows.end(); ++it) {
if (it->get() == pw) {
std::rotate(it, it + 1, m_vWindows.end());
break;
}
}
2023-01-11 17:57:54 +01:00
if (pw->m_bIsMapped)
g_pHyprRenderer->damageMonitor(getMonitorFromID(pw->m_iMonitorID));
};
moveToTop(pWindow);
pWindow->m_bCreatedOverFullscreen = true;
if (!pWindow->m_bIsX11)
return;
// move all children
std::deque<CWindow*> toMove;
for (auto& w : m_vWindows) {
if (w->m_bIsMapped && w->m_bMappedX11 && !w->isHidden() && w->m_bIsX11 && w->X11TransientFor() == pWindow) {
toMove.emplace_back(w.get());
2022-04-04 16:25:30 +02:00
}
}
for (auto& pw : toMove) {
moveToTop(pw);
moveWindowToTop(pw);
}
2022-04-05 19:28:10 +02:00
}
void CCompositor::cleanupFadingOut(const int& monid) {
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindowsFadingOut) {
2022-04-26 17:51:00 +02:00
if (w->m_iMonitorID != (long unsigned int)monid)
continue;
2022-04-28 15:39:49 +02:00
bool valid = windowExists(w);
2022-06-30 15:44:26 +02:00
2022-04-26 17:51:00 +02:00
if (!valid || !w->m_bFadingOut || w->m_fAlpha.fl() == 0.f) {
if (valid && !w->m_bReadyToDelete)
continue;
2023-03-01 14:15:51 +01:00
std::erase_if(g_pHyprOpenGL->m_mWindowFramebuffers, [&](const auto& other) { return other.first == w; });
2022-12-07 15:32:24 +01:00
w->m_bFadingOut = false;
2022-06-30 15:44:26 +02:00
removeWindowFromVectorSafe(w);
2022-12-07 15:32:24 +01:00
std::erase(m_vWindowsFadingOut, w);
Debug::log(LOG, "Cleanup: destroyed a window");
glFlush(); // to free mem NOW.
2022-04-05 19:28:10 +02:00
return;
}
}
2022-06-30 15:44:26 +02:00
for (auto& ls : m_vSurfacesFadingOut) {
2022-07-25 22:40:34 +02:00
// sometimes somehow fucking happens wtf
bool exists = false;
for (auto& m : m_vMonitors) {
for (auto& lsl : m->m_aLayerSurfaceLayers) {
2022-07-25 22:40:34 +02:00
for (auto& lsp : lsl) {
if (lsp.get() == ls) {
exists = true;
break;
}
}
if (exists)
break;
}
if (exists)
break;
}
if (!exists) {
std::erase(m_vSurfacesFadingOut, ls);
2022-07-25 22:40:34 +02:00
Debug::log(LOG, "Fading out a non-existent LS??");
return;
}
if (ls->monitorID != monid)
continue;
// mark blur for recalc
if (ls->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || ls->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
g_pHyprOpenGL->markBlurDirtyForMonitor(getMonitorFromID(monid));
if (ls->fadingOut && ls->readyToDelete && !ls->alpha.isBeingAnimated()) {
g_pHyprOpenGL->m_mLayerFramebuffers[ls].release();
g_pHyprOpenGL->m_mLayerFramebuffers.erase(ls);
2022-07-23 15:48:08 +02:00
for (auto& m : m_vMonitors) {
for (auto& lsl : m->m_aLayerSurfaceLayers) {
if (!lsl.empty() && std::find_if(lsl.begin(), lsl.end(), [&](std::unique_ptr<SLayerSurface>& other) { return other.get() == ls; }) != lsl.end()) {
std::erase_if(lsl, [&](std::unique_ptr<SLayerSurface>& other) { return other.get() == ls; });
}
2022-07-23 15:48:08 +02:00
}
}
std::erase(m_vSurfacesFadingOut, ls);
2022-07-25 22:00:46 +02:00
2022-05-14 17:28:55 +02:00
Debug::log(LOG, "Cleanup: destroyed a layersurface");
glFlush(); // to free mem NOW.
return;
}
}
2022-04-09 13:26:55 +02:00
}
void CCompositor::addToFadingOutSafe(SLayerSurface* pLS) {
const auto FOUND = std::find_if(m_vSurfacesFadingOut.begin(), m_vSurfacesFadingOut.end(), [&](SLayerSurface* other) { return other == pLS; });
if (FOUND != m_vSurfacesFadingOut.end())
return; // if it's already added, don't add it.
m_vSurfacesFadingOut.emplace_back(pLS);
}
void CCompositor::addToFadingOutSafe(CWindow* pWindow) {
const auto FOUND = std::find_if(m_vWindowsFadingOut.begin(), m_vWindowsFadingOut.end(), [&](CWindow* other) { return other == pWindow; });
if (FOUND != m_vWindowsFadingOut.end())
return; // if it's already added, don't add it.
m_vWindowsFadingOut.emplace_back(pWindow);
}
2022-04-09 13:26:55 +02:00
CWindow* CCompositor::getWindowInDirection(CWindow* pWindow, char dir) {
2023-01-26 15:36:22 +01:00
// 0 -> history, 1 -> shared length
static auto* const PMETHOD = &g_pConfigManager->getConfigValuePtr("binds:focus_preferred_method")->intValue;
2023-01-26 15:36:22 +01:00
const auto WINDOWIDEALBB = pWindow->getWindowIdealBoundingBoxIgnoreReserved();
2022-04-09 13:26:55 +02:00
2023-01-26 15:36:22 +01:00
const auto POSA = Vector2D(WINDOWIDEALBB.x, WINDOWIDEALBB.y);
const auto SIZEA = Vector2D(WINDOWIDEALBB.width, WINDOWIDEALBB.height);
auto leaderValue = -1;
CWindow* leaderWindow = nullptr;
2022-04-09 13:26:55 +02:00
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
if (w.get() == pWindow || !w->m_bIsMapped || w->isHidden() || w->m_bIsFloating || !isWorkspaceVisible(w->m_iWorkspaceID))
2022-04-09 13:26:55 +02:00
continue;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID);
if (PWORKSPACE->m_bHasFullscreenWindow && !w->m_bIsFullscreen && !w->m_bCreatedOverFullscreen)
continue;
2022-06-30 15:44:26 +02:00
const auto BWINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved();
const auto POSB = Vector2D(BWINDOWIDEALBB.x, BWINDOWIDEALBB.y);
const auto SIZEB = Vector2D(BWINDOWIDEALBB.width, BWINDOWIDEALBB.height);
2023-01-26 15:36:22 +01:00
double intersectLength = -1;
2022-04-09 13:26:55 +02:00
switch (dir) {
case 'l':
if (STICKS(POSA.x, POSB.x + SIZEB.x)) {
2023-01-26 15:36:22 +01:00
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
2022-04-09 13:26:55 +02:00
}
break;
case 'r':
if (STICKS(POSA.x + SIZEA.x, POSB.x)) {
2023-01-26 15:36:22 +01:00
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
2022-04-09 13:26:55 +02:00
}
break;
case 't':
case 'u':
if (STICKS(POSA.y, POSB.y + SIZEB.y)) {
2023-01-26 15:36:22 +01:00
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
2022-04-09 13:26:55 +02:00
}
break;
case 'b':
case 'd':
if (STICKS(POSA.y + SIZEA.y, POSB.y)) {
2023-01-26 15:36:22 +01:00
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
2022-04-09 13:26:55 +02:00
}
break;
}
2023-01-26 15:36:22 +01:00
if (*PMETHOD == 0 /* history */) {
if (intersectLength > 0) {
// get idx
int windowIDX = -1;
for (size_t i = 0; i < g_pCompositor->m_vWindowFocusHistory.size(); ++i) {
if (g_pCompositor->m_vWindowFocusHistory[i] == w.get()) {
windowIDX = i;
break;
}
}
windowIDX = g_pCompositor->m_vWindowFocusHistory.size() - windowIDX;
if (windowIDX > leaderValue) {
leaderValue = windowIDX;
leaderWindow = w.get();
}
}
} else /* length */ {
if (intersectLength > leaderValue) {
leaderValue = intersectLength;
leaderWindow = w.get();
}
}
2022-04-09 13:26:55 +02:00
}
2023-01-26 15:36:22 +01:00
if (leaderValue != -1)
return leaderWindow;
2022-04-09 13:26:55 +02:00
return nullptr;
}
2022-04-28 17:55:25 +02:00
void CCompositor::deactivateAllWLRWorkspaces(wlr_ext_workspace_handle_v1* exclude) {
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWorkspaces) {
if (w->m_pWlrHandle && w->m_pWlrHandle != exclude)
w->setActive(false);
}
2022-04-13 20:45:06 +02:00
}
2022-09-29 17:53:31 +02:00
CWindow* CCompositor::getNextWindowOnWorkspace(CWindow* pWindow, bool focusableOnly) {
2022-04-13 20:45:06 +02:00
bool gotToWindow = false;
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
if (w.get() != pWindow && !gotToWindow)
2022-04-13 20:45:06 +02:00
continue;
2022-06-30 15:44:26 +02:00
if (w.get() == pWindow) {
2022-04-13 20:45:06 +02:00
gotToWindow = true;
continue;
}
if (w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_bNoFocus))
2022-06-30 15:44:26 +02:00
return w.get();
2022-04-13 20:45:06 +02:00
}
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
if (w.get() != pWindow && w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_bNoFocus))
2022-06-30 15:44:26 +02:00
return w.get();
2022-04-13 20:45:06 +02:00
}
2022-04-21 16:38:48 +02:00
return nullptr;
}
2022-09-29 17:53:31 +02:00
CWindow* CCompositor::getPrevWindowOnWorkspace(CWindow* pWindow, bool focusableOnly) {
2022-07-09 18:39:41 +02:00
bool gotToWindow = false;
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
if (w.get() != pWindow && !gotToWindow)
2022-07-09 18:39:41 +02:00
continue;
2023-03-01 15:06:52 +01:00
if (w.get() == pWindow) {
2022-07-09 18:39:41 +02:00
gotToWindow = true;
continue;
}
2023-03-01 15:06:52 +01:00
if (w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_bNoFocus))
return w.get();
2022-07-09 18:39:41 +02:00
}
2023-03-01 15:06:52 +01:00
for (auto& w : m_vWindows | std::views::reverse) {
if (w.get() != pWindow && w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_bNoFocus))
return w.get();
2022-07-09 18:39:41 +02:00
}
return nullptr;
}
2022-04-21 16:38:48 +02:00
int CCompositor::getNextAvailableNamedWorkspace() {
2022-04-21 21:35:08 +02:00
int lowest = -1337 + 1;
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWorkspaces) {
if (w->m_iID < -1 && w->m_iID < lowest)
lowest = w->m_iID;
2022-04-21 16:38:48 +02:00
}
2022-04-21 21:35:08 +02:00
return lowest - 1;
2022-04-21 16:38:48 +02:00
}
CWorkspace* CCompositor::getWorkspaceByName(const std::string& name) {
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWorkspaces) {
if (w->m_szName == name)
return w.get();
2022-04-21 16:38:48 +02:00
}
return nullptr;
}
CWorkspace* CCompositor::getWorkspaceByString(const std::string& str) {
if (str.find("name:") == 0) {
return getWorkspaceByName(str.substr(str.find_first_of(':') + 1));
}
try {
std::string name = "";
return getWorkspaceByID(getWorkspaceIDFromString(str, name));
} catch (std::exception& e) { Debug::log(ERR, "Error in getWorkspaceByString, invalid id"); }
2022-04-21 16:38:48 +02:00
2022-04-13 20:45:06 +02:00
return nullptr;
2022-04-23 14:16:02 +02:00
}
bool CCompositor::isPointOnAnyMonitor(const Vector2D& point) {
2022-06-30 15:44:26 +02:00
for (auto& m : m_vMonitors) {
if (VECINRECT(point, m->vecPosition.x, m->vecPosition.y, m->vecSize.x + m->vecPosition.x, m->vecSize.y + m->vecPosition.y))
return true;
}
return false;
2022-04-25 13:40:46 +02:00
}
2022-10-28 20:35:02 +02:00
void checkFocusSurfaceIter(wlr_surface* pSurface, int x, int y, void* data) {
auto pair = (std::pair<wlr_surface*, bool>*)data;
2022-10-28 20:35:02 +02:00
pair->second = pair->second || pSurface == pair->first;
}
2022-04-25 13:40:46 +02:00
CWindow* CCompositor::getConstraintWindow(SMouse* pMouse) {
if (!pMouse->currentConstraint)
return nullptr;
const auto PSURFACE = pMouse->currentConstraint->surface;
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
2023-03-20 16:00:58 +01:00
if (w->isHidden() || !w->m_bMappedX11 || !w->m_bIsMapped || !w->m_pWLSurface.exists())
2022-10-28 20:35:02 +02:00
continue;
2022-04-25 13:40:46 +02:00
2022-10-28 20:35:02 +02:00
if (w->m_bIsX11) {
2023-03-20 16:00:58 +01:00
if (PSURFACE == w->m_pWLSurface.wlr())
2022-10-28 20:35:02 +02:00
return w.get();
} else {
std::pair<wlr_surface*, bool> check = {PSURFACE, false};
wlr_surface_for_each_surface(w->m_uSurface.xdg->surface, checkFocusSurfaceIter, &check);
if (check.second)
return w.get();
2022-04-25 13:40:46 +02:00
}
}
2022-05-05 12:50:25 +02:00
return nullptr;
}
2022-07-27 12:32:00 +02:00
CMonitor* CCompositor::getMonitorInDirection(const char& dir) {
const auto POSA = m_pLastMonitor->vecPosition;
2022-05-05 12:50:25 +02:00
const auto SIZEA = m_pLastMonitor->vecSize;
auto longestIntersect = -1;
CMonitor* longestIntersectMonitor = nullptr;
2022-05-05 12:50:25 +02:00
2022-06-30 15:44:26 +02:00
for (auto& m : m_vMonitors) {
if (m.get() == m_pLastMonitor)
2022-05-05 12:50:25 +02:00
continue;
const auto POSB = m->vecPosition;
2022-06-30 15:44:26 +02:00
const auto SIZEB = m->vecSize;
2022-05-05 12:50:25 +02:00
switch (dir) {
case 'l':
if (STICKS(POSA.x, POSB.x + SIZEB.x)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
2022-05-05 12:50:25 +02:00
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
2022-06-30 15:44:26 +02:00
longestIntersectMonitor = m.get();
2022-05-05 12:50:25 +02:00
}
}
break;
case 'r':
if (STICKS(POSA.x + SIZEA.x, POSB.x)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
2022-05-05 12:50:25 +02:00
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
2022-06-30 15:44:26 +02:00
longestIntersectMonitor = m.get();
2022-05-05 12:50:25 +02:00
}
}
break;
case 't':
case 'u':
if (STICKS(POSA.y, POSB.y + SIZEB.y)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
2022-05-05 12:50:25 +02:00
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
2022-06-30 15:44:26 +02:00
longestIntersectMonitor = m.get();
2022-05-05 12:50:25 +02:00
}
}
break;
case 'b':
case 'd':
if (STICKS(POSA.y + SIZEA.y, POSB.y)) {
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
2022-05-05 12:50:25 +02:00
if (INTERSECTLEN > longestIntersect) {
longestIntersect = INTERSECTLEN;
2022-06-30 15:44:26 +02:00
longestIntersectMonitor = m.get();
2022-05-05 12:50:25 +02:00
}
}
break;
}
}
if (longestIntersect != -1)
return longestIntersectMonitor;
2022-04-25 13:40:46 +02:00
return nullptr;
}
2022-05-26 21:23:13 +02:00
2022-07-12 13:40:55 +02:00
void CCompositor::updateAllWindowsAnimatedDecorationValues() {
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
if (!w->m_bIsMapped)
2022-05-26 21:23:13 +02:00
continue;
2022-07-12 13:40:55 +02:00
updateWindowAnimatedDecorationValues(w.get());
2022-05-26 21:23:13 +02:00
}
}
2022-07-12 13:40:55 +02:00
void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) {
2022-05-26 21:23:13 +02:00
// optimization
static auto* const ACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.active_border")->data.get();
static auto* const INACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.inactive_border")->data.get();
2023-02-19 22:07:32 +01:00
static auto* const GROUPACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.group_border_active")->data.get();
static auto* const GROUPINACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.group_border")->data.get();
static auto* const PINACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:inactive_opacity")->floatValue;
static auto* const PACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:active_opacity")->floatValue;
static auto* const PFULLSCREENALPHA = &g_pConfigManager->getConfigValuePtr("decoration:fullscreen_opacity")->floatValue;
static auto* const PSHADOWCOL = &g_pConfigManager->getConfigValuePtr("decoration:col.shadow")->intValue;
static auto* const PSHADOWCOLINACTIVE = &g_pConfigManager->getConfigValuePtr("decoration:col.shadow_inactive")->intValue;
static auto* const PDIMSTRENGTH = &g_pConfigManager->getConfigValuePtr("decoration:dim_strength")->floatValue;
auto setBorderColor = [&](CGradientValueData grad) -> void {
2022-11-26 18:56:43 +01:00
if (grad == pWindow->m_cRealBorderColor)
return;
pWindow->m_cRealBorderColorPrevious = pWindow->m_cRealBorderColor;
pWindow->m_cRealBorderColor = grad;
pWindow->m_fBorderFadeAnimationProgress.setValueAndWarp(0.f);
pWindow->m_fBorderFadeAnimationProgress = 1.f;
2022-11-26 18:56:43 +01:00
};
2022-07-12 13:40:55 +02:00
// border
2022-05-26 21:23:13 +02:00
const auto RENDERDATA = g_pLayoutManager->getCurrentLayout()->requestRenderHints(pWindow);
if (RENDERDATA.isBorderGradient)
setBorderColor(*RENDERDATA.borderGradient);
2023-02-19 22:07:32 +01:00
else {
if (pWindow == m_pLastWindow) {
const auto* const ACTIVECOLOR = !pWindow->m_sGroupData.pNextWindow ? ACTIVECOL : GROUPACTIVECOL;
setBorderColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying() >= 0 ?
CGradientValueData(CColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying())) :
*ACTIVECOLOR);
} else {
const auto* const INACTIVECOLOR = !pWindow->m_sGroupData.pNextWindow ? INACTIVECOL : GROUPINACTIVECOL;
setBorderColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying() >= 0 ?
CGradientValueData(CColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying())) :
*INACTIVECOLOR);
}
}
2022-07-12 13:40:55 +02:00
// tick angle if it's not running (aka dead)
if (!pWindow->m_fBorderAngleAnimationProgress.isBeingAnimated())
pWindow->m_fBorderAngleAnimationProgress.setValueAndWarp(0.f);
2022-07-12 13:40:55 +02:00
// opacity
2022-12-11 18:15:02 +01:00
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID);
if (pWindow->m_bIsFullscreen && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) {
pWindow->m_fActiveInactiveAlpha = *PFULLSCREENALPHA;
2022-07-12 13:40:55 +02:00
} else {
if (pWindow == m_pLastWindow)
2023-01-26 15:36:22 +01:00
pWindow->m_fActiveInactiveAlpha = pWindow->m_sSpecialRenderData.alphaOverride.toUnderlying() ? pWindow->m_sSpecialRenderData.alpha.toUnderlying() :
pWindow->m_sSpecialRenderData.alpha.toUnderlying() * *PACTIVEALPHA;
2022-07-12 13:40:55 +02:00
else
2023-01-24 20:05:34 +01:00
pWindow->m_fActiveInactiveAlpha = pWindow->m_sSpecialRenderData.alphaInactive.toUnderlying() != -1 ?
(pWindow->m_sSpecialRenderData.alphaInactiveOverride.toUnderlying() ? pWindow->m_sSpecialRenderData.alphaInactive.toUnderlying() :
pWindow->m_sSpecialRenderData.alphaInactive.toUnderlying() * *PINACTIVEALPHA) :
*PINACTIVEALPHA;
2022-07-12 13:40:55 +02:00
}
2022-08-30 12:46:17 +02:00
// dim
if (pWindow == m_pLastWindow) {
pWindow->m_fDimPercent = 0;
} else {
pWindow->m_fDimPercent = *PDIMSTRENGTH;
}
// shadow
2022-07-18 12:39:57 +02:00
if (pWindow->m_iX11Type != 2 && !pWindow->m_bX11DoesntWantBorders) {
if (pWindow == m_pLastWindow) {
pWindow->m_cRealShadowColor = CColor(*PSHADOWCOL);
} else {
pWindow->m_cRealShadowColor = CColor(*PSHADOWCOLINACTIVE != INT_MAX ? *PSHADOWCOLINACTIVE : *PSHADOWCOL);
}
} else {
2022-07-18 12:39:57 +02:00
pWindow->m_cRealShadowColor.setValueAndWarp(CColor(0, 0, 0, 0)); // no shadow
}
2023-02-28 00:34:41 +01:00
for (auto& d : pWindow->m_dWindowDecorations)
d->updateWindow(pWindow);
2022-05-30 17:11:35 +02:00
}
int CCompositor::getNextAvailableMonitorID() {
int64_t topID = -1;
2022-08-03 16:19:00 +02:00
for (auto& m : m_vRealMonitors) {
2022-06-30 15:44:26 +02:00
if ((int64_t)m->ID > topID)
topID = m->ID;
2022-05-30 17:11:35 +02:00
}
return topID + 1;
}
2022-05-30 20:05:38 +02:00
void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB) {
const auto PWORKSPACEA = g_pCompositor->getWorkspaceByID(pMonitorA->activeWorkspace);
const auto PWORKSPACEB = g_pCompositor->getWorkspaceByID(pMonitorB->activeWorkspace);
PWORKSPACEA->m_iMonitorID = pMonitorB->ID;
PWORKSPACEA->moveToMonitor(pMonitorB->ID);
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == PWORKSPACEA->m_iID) {
2023-03-20 02:50:46 +01:00
if (w->m_bPinned) {
w->m_iWorkspaceID = PWORKSPACEB->m_iID;
continue;
}
w->m_iMonitorID = pMonitorB->ID;
2022-08-27 19:11:21 +02:00
// additionally, move floating and fs windows manually
if (w->m_bIsFloating)
w->m_vRealPosition = w->m_vRealPosition.vec() - pMonitorA->vecPosition + pMonitorB->vecPosition;
2022-08-27 19:11:21 +02:00
if (w->m_bIsFullscreen) {
w->m_vRealPosition = pMonitorB->vecPosition;
w->m_vRealSize = pMonitorB->vecSize;
}
w->updateToplevel();
}
}
PWORKSPACEB->m_iMonitorID = pMonitorA->ID;
PWORKSPACEB->moveToMonitor(pMonitorA->ID);
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == PWORKSPACEB->m_iID) {
2023-03-20 02:50:46 +01:00
if (w->m_bPinned) {
w->m_iWorkspaceID = PWORKSPACEA->m_iID;
continue;
}
w->m_iMonitorID = pMonitorA->ID;
2022-08-27 19:11:21 +02:00
// additionally, move floating and fs windows manually
if (w->m_bIsFloating)
w->m_vRealPosition = w->m_vRealPosition.vec() - pMonitorB->vecPosition + pMonitorA->vecPosition;
2022-08-27 19:11:21 +02:00
if (w->m_bIsFullscreen) {
w->m_vRealPosition = pMonitorA->vecPosition;
w->m_vRealSize = pMonitorA->vecSize;
}
w->updateToplevel();
}
}
pMonitorA->activeWorkspace = PWORKSPACEB->m_iID;
pMonitorB->activeWorkspace = PWORKSPACEA->m_iID;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitorA->ID);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitorB->ID);
g_pInputManager->refocus();
2022-08-26 16:05:02 +02:00
// event
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", PWORKSPACEA->m_szName + "," + pMonitorB->szName});
EMIT_HOOK_EVENT("moveWorkspace", (std::vector<void*>{PWORKSPACEA, pMonitorB}));
2022-08-26 16:05:02 +02:00
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", PWORKSPACEB->m_szName + "," + pMonitorA->szName});
EMIT_HOOK_EVENT("moveWorkspace", (std::vector<void*>{PWORKSPACEB, pMonitorA}));
}
CMonitor* CCompositor::getMonitorFromString(const std::string& name) {
2022-11-10 13:22:19 +01:00
if (name[0] == '+' || name[0] == '-') {
// relative
if (m_vMonitors.size() == 1)
return m_vMonitors.begin()->get();
2022-11-10 13:22:19 +01:00
const auto OFFSET = name[0] == '-' ? name : name.substr(1);
if (!isNumber(OFFSET)) {
Debug::log(ERR, "Error in getMonitorFromString: Not a number in relative.");
return nullptr;
}
2022-12-08 13:09:48 +01:00
int offsetLeft = std::stoi(OFFSET);
offsetLeft = offsetLeft < 0 ? -((-offsetLeft) % m_vMonitors.size()) : offsetLeft % m_vMonitors.size();
2022-11-10 13:22:19 +01:00
int currentPlace = 0;
for (int i = 0; i < (int)m_vMonitors.size(); i++) {
if (m_vMonitors[i].get() == m_pLastMonitor) {
currentPlace = i;
break;
}
}
currentPlace += offsetLeft;
if (currentPlace < 0) {
currentPlace = m_vMonitors.size() + currentPlace;
2022-11-10 13:22:19 +01:00
} else {
currentPlace = currentPlace % m_vMonitors.size();
}
2023-01-23 21:56:43 +01:00
if (currentPlace != std::clamp(currentPlace, 0, (int)m_vMonitors.size() - 1)) {
2022-11-10 13:22:19 +01:00
Debug::log(WARN, "Error in getMonitorFromString: Vaxry's code sucks.");
currentPlace = std::clamp(currentPlace, 0, (int)m_vMonitors.size() - 1);
2022-11-10 13:22:19 +01:00
}
return m_vMonitors[currentPlace].get();
} else if (isNumber(name)) {
// change by ID
int monID = -1;
try {
monID = std::stoi(name);
} catch (std::exception& e) {
// shouldn't happen but jic
Debug::log(ERR, "Error in getMonitorFromString: invalid num");
return nullptr;
}
2023-03-02 13:04:41 +01:00
if (monID > -1 && monID < (int)m_vMonitors.size()) {
return getMonitorFromID(monID);
} else {
Debug::log(ERR, "Error in getMonitorFromString: invalid arg 1");
return nullptr;
}
2023-03-02 13:04:41 +01:00
} else if (name.find("desc:") == 0) {
const auto DESCRIPTION = name.substr(5);
for (auto& m : m_vMonitors) {
2023-03-16 15:03:40 +01:00
if (!m->output)
continue;
2023-03-02 13:04:41 +01:00
if (m->output->description && std::string(m->output->description).find(DESCRIPTION) == 0) {
return m.get();
}
}
return nullptr;
} else {
2022-09-10 19:57:57 +02:00
if (name == "current")
return g_pCompositor->m_pLastMonitor;
if (isDirection(name)) {
2023-03-02 13:04:41 +01:00
const auto PMONITOR = getMonitorInDirection(name[0]);
return PMONITOR;
} else {
2023-03-02 13:04:41 +01:00
for (auto& m : m_vMonitors) {
if (m->szName == name) {
return m.get();
}
}
}
}
return nullptr;
}
2022-07-27 12:32:00 +02:00
void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMonitor) {
2022-05-30 20:05:38 +02:00
// We trust the workspace and monitor to be correct.
if (pWorkspace->m_iMonitorID == pMonitor->ID)
return;
Debug::log(LOG, "moveWorkspaceToMonitor: Moving %d to monitor %d", pWorkspace->m_iID, pMonitor->ID);
const auto POLDMON = getMonitorFromID(pWorkspace->m_iMonitorID);
const bool SWITCHINGISACTIVE = POLDMON->activeWorkspace == pWorkspace->m_iID;
// fix old mon
int nextWorkspaceOnMonitorID = -1;
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWorkspaces) {
if (w->m_iMonitorID == POLDMON->ID && w->m_iID != pWorkspace->m_iID) {
nextWorkspaceOnMonitorID = w->m_iID;
2022-05-30 20:05:38 +02:00
break;
}
}
if (nextWorkspaceOnMonitorID == -1) {
nextWorkspaceOnMonitorID = 1;
while (getWorkspaceByID(nextWorkspaceOnMonitorID) || [&]() -> bool {
const auto B = g_pConfigManager->getBoundMonitorForWS(std::to_string(nextWorkspaceOnMonitorID));
return B && B != POLDMON;
}())
2022-05-30 20:05:38 +02:00
nextWorkspaceOnMonitorID++;
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with new %d", nextWorkspaceOnMonitorID);
}
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with existing %d", nextWorkspaceOnMonitorID);
g_pKeybindManager->focusMonitor(std::to_string(POLDMON->ID));
g_pKeybindManager->changeworkspace(std::to_string(nextWorkspaceOnMonitorID));
// move the workspace
pWorkspace->m_iMonitorID = pMonitor->ID;
pWorkspace->moveToMonitor(pMonitor->ID);
2022-05-30 20:05:38 +02:00
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == pWorkspace->m_iID) {
2023-03-20 02:50:46 +01:00
if (w->m_bPinned) {
w->m_iWorkspaceID = nextWorkspaceOnMonitorID;
continue;
}
2022-06-30 15:44:26 +02:00
w->m_iMonitorID = pMonitor->ID;
2022-08-27 19:11:21 +02:00
// additionally, move floating and fs windows manually
if (w->m_bIsMapped && !w->isHidden()) {
2022-08-27 19:11:21 +02:00
if (w->m_bIsFloating)
w->m_vRealPosition = w->m_vRealPosition.vec() - POLDMON->vecPosition + pMonitor->vecPosition;
if (w->m_bIsFullscreen) {
w->m_vRealPosition = pMonitor->vecPosition;
w->m_vRealSize = pMonitor->vecSize;
2022-08-27 19:11:21 +02:00
}
}
w->updateToplevel();
}
2022-05-30 20:05:38 +02:00
}
if (SWITCHINGISACTIVE) { // if it was active, preserve its' status. If it wasn't, don't.
Debug::log(LOG, "moveWorkspaceToMonitor: SWITCHINGISACTIVE, active %d -> %d", pMonitor->activeWorkspace, pWorkspace->m_iID);
if (const auto PWORKSPACE = getWorkspaceByID(pMonitor->activeWorkspace); PWORKSPACE)
getWorkspaceByID(pMonitor->activeWorkspace)->startAnim(false, false);
2022-05-30 20:05:38 +02:00
pMonitor->activeWorkspace = pWorkspace->m_iID;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->ID);
pWorkspace->startAnim(true, true, true);
wlr_cursor_warp(m_sWLRCursor, m_sSeat.mouse->mouse, pMonitor->vecPosition.x + pMonitor->vecTransformedSize.x / 2,
pMonitor->vecPosition.y + pMonitor->vecTransformedSize.y / 2);
2022-05-30 20:05:38 +02:00
}
// finalize
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(POLDMON->ID);
g_pInputManager->refocus();
2022-08-26 16:05:02 +02:00
// event
g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", pWorkspace->m_szName + "," + pMonitor->szName});
EMIT_HOOK_EVENT("moveWorkspace", (std::vector<void*>{pWorkspace, pMonitor}));
}
bool CCompositor::workspaceIDOutOfBounds(const int& id) {
int lowestID = 99999;
int highestID = -99999;
2022-06-30 15:44:26 +02:00
for (auto& w : m_vWorkspaces) {
2022-11-27 23:42:22 +01:00
if (w->m_bIsSpecialWorkspace)
2022-09-17 16:05:12 +02:00
continue;
2022-06-30 15:44:26 +02:00
if (w->m_iID < lowestID)
lowestID = w->m_iID;
2022-09-25 20:07:48 +02:00
2022-06-30 15:44:26 +02:00
if (w->m_iID > highestID)
highestID = w->m_iID;
}
return std::clamp(id, lowestID, highestID) != id;
2022-06-26 12:12:29 +02:00
}
void CCompositor::setWindowFullscreen(CWindow* pWindow, bool on, eFullscreenMode mode) {
if (!windowValidMapped(pWindow))
return;
2022-09-21 15:09:26 +02:00
if (pWindow->m_bPinned) {
Debug::log(LOG, "Pinned windows cannot be fullscreen'd");
return;
}
2022-09-29 11:20:12 +02:00
const auto PMONITOR = getMonitorFromID(pWindow->m_iMonitorID);
const auto PWORKSPACE = getWorkspaceByID(pWindow->m_iWorkspaceID);
if (PWORKSPACE->m_bHasFullscreenWindow && on) {
Debug::log(LOG, "Rejecting fullscreen ON on a fullscreen workspace");
return;
}
2022-06-26 12:12:29 +02:00
g_pLayoutManager->getCurrentLayout()->fullscreenRequestForWindow(pWindow, mode, on);
g_pXWaylandManager->setWindowFullscreen(pWindow, pWindow->m_bIsFullscreen && mode == FULLSCREEN_FULL);
2022-09-25 20:07:48 +02:00
pWindow->updateDynamicRules();
g_pCompositor->updateWindowAnimatedDecorationValues(pWindow);
2022-06-26 12:12:29 +02:00
// make all windows on the same workspace under the fullscreen window
2022-06-30 15:44:26 +02:00
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == pWindow->m_iWorkspaceID) {
2022-06-30 15:44:26 +02:00
w->m_bCreatedOverFullscreen = false;
2022-09-20 16:33:53 +02:00
if (w.get() != pWindow && !w->m_bFadingOut && !w->m_bPinned)
w->m_fAlpha = pWindow->m_bIsFullscreen ? 0.f : 1.f;
}
2022-06-30 15:44:26 +02:00
}
2022-09-19 11:23:13 +02:00
for (auto& ls : PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
if (!ls->fadingOut)
ls->alpha = pWindow->m_bIsFullscreen && mode == FULLSCREEN_FULL ? 0.f : 1.f;
2022-09-19 11:23:13 +02:00
}
2022-09-25 20:07:48 +02:00
g_pXWaylandManager->setWindowSize(pWindow, pWindow->m_vRealSize.goalv(), true);
forceReportSizesToWindowsOnWorkspace(pWindow->m_iWorkspaceID);
2022-10-31 13:26:07 +01:00
g_pInputManager->recheckIdleInhibitorStatus();
2022-11-05 13:50:47 +01:00
// DMAbuf stuff for direct scanout
g_pHyprRenderer->setWindowScanoutMode(pWindow);
2023-02-14 18:08:42 +01:00
g_pConfigManager->ensureVRR(PMONITOR);
2022-06-30 15:44:26 +02:00
}
CWindow* CCompositor::getX11Parent(CWindow* pWindow) {
if (!pWindow->m_bIsX11)
return nullptr;
for (auto& w : m_vWindows) {
if (!w->m_bIsX11)
continue;
if (w->m_uSurface.xwayland == pWindow->m_uSurface.xwayland->parent)
return w.get();
}
2022-09-25 20:07:48 +02:00
2022-06-30 15:44:26 +02:00
return nullptr;
2022-07-07 11:52:12 +02:00
}
void CCompositor::updateWorkspaceWindowDecos(const int& id) {
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID != id)
continue;
w->updateWindowDecos();
}
}
2022-07-13 18:18:23 +02:00
2022-07-27 12:32:00 +02:00
void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor) {
2022-07-13 18:31:09 +02:00
if ((m_sWLRSession && !m_sWLRSession->active) || !m_bSessionActive)
2022-07-13 18:18:23 +02:00
return;
if (!pMonitor->m_bEnabled)
return;
if (pMonitor->renderingActive)
pMonitor->pendingFrame = true;
2022-07-13 18:18:23 +02:00
wlr_output_schedule_frame(pMonitor->output);
2022-07-26 17:30:30 +02:00
}
CWindow* CCompositor::getWindowByRegex(const std::string& regexp) {
eFocusWindowMode mode = MODE_CLASS_REGEX;
std::regex regexCheck(regexp);
std::string matchCheck;
2022-07-26 17:30:30 +02:00
if (regexp.find("title:") == 0) {
mode = MODE_TITLE_REGEX;
2022-07-26 17:30:30 +02:00
regexCheck = std::regex(regexp.substr(6));
} else if (regexp.find("address:") == 0) {
mode = MODE_ADDRESS;
2022-07-26 17:30:30 +02:00
matchCheck = regexp.substr(8);
} else if (regexp.find("pid:") == 0) {
mode = MODE_PID;
2022-07-26 17:30:30 +02:00
matchCheck = regexp.substr(4);
}
for (auto& w : g_pCompositor->m_vWindows) {
2023-02-28 13:34:59 +01:00
if (!w->m_bIsMapped || (w->isHidden() && !w->m_sGroupData.pNextWindow))
2022-07-26 17:30:30 +02:00
continue;
switch (mode) {
case MODE_CLASS_REGEX: {
const auto windowClass = g_pXWaylandManager->getAppIDClass(w.get());
if (!std::regex_search(g_pXWaylandManager->getAppIDClass(w.get()), regexCheck))
continue;
break;
}
case MODE_TITLE_REGEX: {
const auto windowTitle = g_pXWaylandManager->getTitle(w.get());
if (!std::regex_search(windowTitle, regexCheck))
continue;
break;
}
case MODE_ADDRESS: {
std::string addr = getFormat("0x%x", w.get());
if (matchCheck != addr)
continue;
break;
}
case MODE_PID: {
std::string pid = getFormat("%d", w->getPID());
if (matchCheck != pid)
continue;
break;
}
default: break;
2022-07-26 17:30:30 +02:00
}
return w.get();
}
return nullptr;
2022-07-31 00:27:32 +02:00
}
2023-01-28 13:26:33 +01:00
void CCompositor::warpCursorTo(const Vector2D& pos, bool force) {
2022-08-01 18:50:16 +02:00
// warpCursorTo should only be used for warps that
// should be disabled with no_cursor_warps
static auto* const PNOWARPS = &g_pConfigManager->getConfigValuePtr("general:no_cursor_warps")->intValue;
2022-08-01 18:50:16 +02:00
2023-01-28 13:26:33 +01:00
if (*PNOWARPS && !force)
2022-08-01 18:50:16 +02:00
return;
2023-01-28 18:54:14 +01:00
if (!m_sSeat.mouse)
return;
2022-08-01 18:50:16 +02:00
wlr_cursor_warp(m_sWLRCursor, m_sSeat.mouse->mouse, pos.x, pos.y);
const auto PMONITORNEW = getMonitorFromVector(pos);
if (PMONITORNEW != m_pLastMonitor)
2022-11-19 17:41:36 +01:00
setActiveMonitor(PMONITORNEW);
2022-08-01 18:50:16 +02:00
}
2022-08-05 17:07:01 +02:00
SLayerSurface* CCompositor::getLayerSurfaceFromWlr(wlr_layer_surface_v1* pLS) {
for (auto& m : m_vMonitors) {
for (auto& lsl : m->m_aLayerSurfaceLayers) {
2022-08-05 17:07:01 +02:00
for (auto& ls : lsl) {
if (ls->layerSurface == pLS)
return ls.get();
}
}
}
return nullptr;
}
void CCompositor::closeWindow(CWindow* pWindow) {
if (pWindow && windowValidMapped(pWindow)) {
g_pXWaylandManager->sendCloseWindow(pWindow);
}
2022-08-10 17:46:01 +02:00
}
SLayerSurface* CCompositor::getLayerSurfaceFromSurface(wlr_surface* pSurface) {
2023-04-02 14:30:45 +02:00
std::pair<wlr_surface*, bool> result = {pSurface, false};
2022-08-10 17:46:01 +02:00
for (auto& m : m_vMonitors) {
for (auto& lsl : m->m_aLayerSurfaceLayers) {
2022-08-10 17:46:01 +02:00
for (auto& ls : lsl) {
if (ls->layerSurface && ls->layerSurface->surface == pSurface)
return ls.get();
2023-04-02 14:30:45 +02:00
static auto iter = [](wlr_surface* surf, int x, int y, void* data) -> void {
if (surf == ((std::pair<wlr_surface*, bool>*)data)->first) {
*(bool*)data = true;
return;
}
};
2023-04-02 14:30:45 +02:00
if (!ls->layerSurface || !ls->mapped)
continue;
wlr_surface_for_each_surface(ls->layerSurface->surface, iter, &result);
if (result.second)
return ls.get();
2022-08-10 17:46:01 +02:00
}
}
}
return nullptr;
}
// returns a delta
Vector2D CCompositor::parseWindowVectorArgsRelative(const std::string& args, const Vector2D& relativeTo) {
if (!args.contains(' '))
return relativeTo;
std::string x = args.substr(0, args.find_first_of(' '));
std::string y = args.substr(args.find_first_of(' ') + 1);
if (x == "exact") {
std::string newX = y.substr(0, y.find_first_of(' '));
std::string newY = y.substr(y.find_first_of(' ') + 1);
if (!isNumber(newX) || !isNumber(newY)) {
Debug::log(ERR, "parseWindowVectorArgsRelative: exact args not numbers");
return relativeTo;
}
const int X = std::stoi(newX);
const int Y = std::stoi(newY);
if (X < 0 || Y < 0) {
Debug::log(ERR, "parseWindowVectorArgsRelative: exact args cannot be < 0");
return relativeTo;
}
return Vector2D(X, Y);
}
if (!isNumber(x) || !isNumber(y)) {
Debug::log(ERR, "parseWindowVectorArgsRelative: args not numbers");
return relativeTo;
}
const int X = std::stoi(x);
const int Y = std::stoi(y);
return Vector2D(X + relativeTo.x, Y + relativeTo.y);
}
void CCompositor::forceReportSizesToWindowsOnWorkspace(const int& wid) {
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == wid && w->m_bIsMapped && !w->isHidden()) {
g_pXWaylandManager->setWindowSize(w.get(), w->m_vRealSize.vec(), true);
}
}
2022-09-03 15:35:53 +02:00
}
bool CCompositor::cursorOnReservedArea() {
const auto PMONITOR = getMonitorFromCursor();
const auto XY1 = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft;
const auto XY2 = PMONITOR->vecPosition + PMONITOR->vecSize - PMONITOR->vecReservedBottomRight;
const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal();
return !VECINRECT(CURSORPOS, XY1.x, XY1.y, XY2.x, XY2.y);
}
2022-10-24 19:36:31 +02:00
CWorkspace* CCompositor::createNewWorkspace(const int& id, const int& monid, const std::string& name) {
const auto NAME = name == "" ? std::to_string(id) : name;
auto monID = monid;
// check if bound
if (const auto PMONITOR = g_pConfigManager->getBoundMonitorForWS(NAME); PMONITOR) {
monID = PMONITOR->ID;
}
2022-11-27 23:42:22 +01:00
const bool SPECIAL = id >= SPECIAL_WORKSPACE_START && id <= -2;
const auto PWORKSPACE = m_vWorkspaces.emplace_back(std::make_unique<CWorkspace>(monID, NAME, SPECIAL)).get();
2022-10-24 19:36:31 +02:00
// We are required to set the name here immediately
2022-11-27 23:42:22 +01:00
if (!SPECIAL)
2022-10-24 19:36:31 +02:00
wlr_ext_workspace_handle_v1_set_name(PWORKSPACE->m_pWlrHandle, NAME.c_str());
PWORKSPACE->m_iID = id;
PWORKSPACE->m_iMonitorID = monID;
2022-10-24 19:36:31 +02:00
return PWORKSPACE;
}
2022-11-19 17:41:36 +01:00
void CCompositor::renameWorkspace(const int& id, const std::string& name) {
const auto PWORKSPACE = getWorkspaceByID(id);
if (!PWORKSPACE)
return;
if (isWorkspaceSpecial(id))
return;
2023-01-28 18:52:32 +01:00
Debug::log(LOG, "renameWorkspace: Renaming workspace %d to '%s'", id, name.c_str());
wlr_ext_workspace_handle_v1_set_name(PWORKSPACE->m_pWlrHandle, name.c_str());
PWORKSPACE->m_szName = name;
}
2022-11-19 17:41:36 +01:00
void CCompositor::setActiveMonitor(CMonitor* pMonitor) {
if (m_pLastMonitor == pMonitor)
return;
2022-11-23 00:17:10 +01:00
if (!pMonitor) {
m_pLastMonitor = nullptr;
return;
}
2022-11-19 17:41:36 +01:00
const auto PWORKSPACE = getWorkspaceByID(pMonitor->activeWorkspace);
g_pEventManager->postEvent(SHyprIPCEvent{"focusedmon", pMonitor->szName + "," + PWORKSPACE->m_szName});
EMIT_HOOK_EVENT("focusedMon", pMonitor);
2022-11-19 17:41:36 +01:00
m_pLastMonitor = pMonitor;
}
2022-11-27 23:42:22 +01:00
bool CCompositor::isWorkspaceSpecial(const int& id) {
return id >= SPECIAL_WORKSPACE_START && id <= -2;
}
int CCompositor::getNewSpecialID() {
int highest = -100;
for (auto& ws : m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace && ws->m_iID > highest) {
highest = ws->m_iID;
}
}
return highest + 1;
}
2023-03-20 16:02:47 +01:00
void CCompositor::performUserChecks() {
2023-03-20 23:26:49 +01:00
static constexpr auto BAD_PORTALS = {"kde", "gnome"};
2023-03-20 16:02:47 +01:00
static auto* const PSUPPRESSPORTAL = &g_pConfigManager->getConfigValuePtr("misc:suppress_portal_warnings")->intValue;
if (!*PSUPPRESSPORTAL) {
if (std::ranges::any_of(BAD_PORTALS, [&](const std::string& portal) { return std::filesystem::exists("/usr/share/xdg-desktop-portal/portals/" + portal + ".portal"); })) {
// bad portal detected
g_pHyprNotificationOverlay->addNotification("You have one or more incompatible xdg-desktop-portal impls installed. Please remove incompatible ones to avoid issues.",
CColor(0), 15000, ICON_ERROR);
}
2023-03-20 23:26:49 +01:00
if (std::filesystem::exists("/usr/share/xdg-desktop-portal/portals/hyprland.portal") && std::filesystem::exists("/usr/share/xdg-desktop-portal/portals/wlr.portal")) {
g_pHyprNotificationOverlay->addNotification("You have xdg-desktop-portal-hyprland and -wlr installed simultaneously. Please uninstall one to avoid issues.", CColor(0),
15000, ICON_ERROR);
}
2023-03-20 23:26:49 +01:00
}
2023-03-20 16:02:47 +01:00
}