mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-29 22:05:58 +01:00
Added creating / destroying outputs on a multi-backend + headless backend
See `hyprctl output`.
This commit is contained in:
parent
a71f44baa5
commit
5a750b485a
9 changed files with 129 additions and 1 deletions
|
@ -217,6 +217,21 @@ int setcursorRequest(int argc, char** argv) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int outputRequest(int argc, char** argv) {
|
||||||
|
if (argc < 4) {
|
||||||
|
std::cout << "Usage: hyprctl output <mode> <name>\n\
|
||||||
|
creates / destroys a fake output\n\
|
||||||
|
with create, name is the backend name to use (available: auto, x11, wayland, headless)\n\
|
||||||
|
with destroy, name is the output name to destroy";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rq = "output " + std::string(argv[2]) + " " + std::string(argv[3]);
|
||||||
|
|
||||||
|
request(rq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void batchRequest(std::string arg) {
|
void batchRequest(std::string arg) {
|
||||||
std::string rq = "[[BATCH]]" + arg.substr(arg.find_first_of(" ") + 1);
|
std::string rq = "[[BATCH]]" + arg.substr(arg.find_first_of(" ") + 1);
|
||||||
|
|
||||||
|
@ -290,6 +305,7 @@ int main(int argc, char** argv) {
|
||||||
else if (fullRequest.contains("/reload")) request(fullRequest);
|
else if (fullRequest.contains("/reload")) request(fullRequest);
|
||||||
else if (fullRequest.contains("/getoption")) request(fullRequest);
|
else if (fullRequest.contains("/getoption")) request(fullRequest);
|
||||||
else if (fullRequest.contains("/cursorpos")) request(fullRequest);
|
else if (fullRequest.contains("/cursorpos")) request(fullRequest);
|
||||||
|
else if (fullRequest.contains("/output")) exitStatus = outputRequest(argc, argv);
|
||||||
else if (fullRequest.contains("/setcursor")) exitStatus = setcursorRequest(argc, argv);
|
else if (fullRequest.contains("/setcursor")) exitStatus = setcursorRequest(argc, argv);
|
||||||
else if (fullRequest.contains("/dispatch")) exitStatus = dispatchRequest(argc, argv);
|
else if (fullRequest.contains("/dispatch")) exitStatus = dispatchRequest(argc, argv);
|
||||||
else if (fullRequest.contains("/keyword")) exitStatus = keywordRequest(argc, argv);
|
else if (fullRequest.contains("/keyword")) exitStatus = keywordRequest(argc, argv);
|
||||||
|
|
|
@ -180,6 +180,15 @@ CCompositor::CCompositor() {
|
||||||
m_sWLRIMEMgr = wlr_input_method_manager_v2_create(m_sWLDisplay);
|
m_sWLRIMEMgr = wlr_input_method_manager_v2_create(m_sWLDisplay);
|
||||||
|
|
||||||
m_sWLRActivation = wlr_xdg_activation_v1_create(m_sWLDisplay);
|
m_sWLRActivation = wlr_xdg_activation_v1_create(m_sWLDisplay);
|
||||||
|
|
||||||
|
m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLDisplay);
|
||||||
|
|
||||||
|
if (!m_sWLRHeadlessBackend) {
|
||||||
|
Debug::log(CRIT, "Couldn't create the headless backend");
|
||||||
|
throw std::runtime_error("wlr_headless_backend_create() failed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_multi_backend_add(m_sWLRBackend, m_sWLRHeadlessBackend);
|
||||||
}
|
}
|
||||||
|
|
||||||
CCompositor::~CCompositor() {
|
CCompositor::~CCompositor() {
|
||||||
|
|
|
@ -71,6 +71,7 @@ public:
|
||||||
wlr_text_input_manager_v3* m_sWLRTextInputMgr;
|
wlr_text_input_manager_v3* m_sWLRTextInputMgr;
|
||||||
wlr_xdg_activation_v1* m_sWLRActivation;
|
wlr_xdg_activation_v1* m_sWLRActivation;
|
||||||
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
|
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
|
||||||
|
wlr_backend* m_sWLRHeadlessBackend;
|
||||||
// ------------------------------------------------- //
|
// ------------------------------------------------- //
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -743,6 +743,86 @@ R"#(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void createOutputIter(wlr_backend* backend, void* data) {
|
||||||
|
const auto DATA = (std::pair<std::string, bool>*)data;
|
||||||
|
|
||||||
|
if (DATA->second)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (DATA->first.empty() || DATA->first == "auto") {
|
||||||
|
if (wlr_backend_is_wl(backend)) {
|
||||||
|
wlr_wl_output_create(backend);
|
||||||
|
DATA->second = true;
|
||||||
|
} else if (wlr_backend_is_x11(backend)) {
|
||||||
|
wlr_x11_output_create(backend);
|
||||||
|
DATA->second = true;
|
||||||
|
} else if (wlr_backend_is_headless(backend)) {
|
||||||
|
wlr_headless_add_output(backend, 1920, 1080);
|
||||||
|
DATA->second = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (wlr_backend_is_wl(backend) && DATA->first == "wayland") {
|
||||||
|
wlr_wl_output_create(backend);
|
||||||
|
DATA->second = true;
|
||||||
|
} else if (wlr_backend_is_x11(backend) && DATA->first == "x11") {
|
||||||
|
wlr_x11_output_create(backend);
|
||||||
|
DATA->second = true;
|
||||||
|
} else if (wlr_backend_is_headless(backend) && DATA->first == "headless") {
|
||||||
|
wlr_headless_add_output(backend, 1920, 1080);
|
||||||
|
DATA->second = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string dispatchOutput(std::string request) {
|
||||||
|
std::string curitem = "";
|
||||||
|
|
||||||
|
auto nextItem = [&]() {
|
||||||
|
auto idx = request.find_first_of(' ');
|
||||||
|
|
||||||
|
if (idx != std::string::npos) {
|
||||||
|
curitem = request.substr(0, idx);
|
||||||
|
request = request.substr(idx + 1);
|
||||||
|
} else {
|
||||||
|
curitem = request;
|
||||||
|
request = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
curitem = removeBeginEndSpacesTabs(curitem);
|
||||||
|
};
|
||||||
|
|
||||||
|
nextItem();
|
||||||
|
nextItem();
|
||||||
|
|
||||||
|
const auto MODE = curitem;
|
||||||
|
|
||||||
|
nextItem();
|
||||||
|
|
||||||
|
const auto NAME = curitem;
|
||||||
|
|
||||||
|
if (MODE == "create" || MODE == "add") {
|
||||||
|
std::pair<std::string, bool> result = { NAME, false };
|
||||||
|
|
||||||
|
wlr_multi_for_each_backend(g_pCompositor->m_sWLRBackend, createOutputIter, &result);
|
||||||
|
|
||||||
|
if (!result.second)
|
||||||
|
return "no backend replied to the request";
|
||||||
|
|
||||||
|
} else if (MODE == "destroy" || MODE == "remove") {
|
||||||
|
const auto PMONITOR = g_pCompositor->getMonitorFromName(NAME);
|
||||||
|
|
||||||
|
if (!PMONITOR)
|
||||||
|
return "output not found";
|
||||||
|
|
||||||
|
if (!PMONITOR->createdByUser)
|
||||||
|
return "cannot remove a real display. Use the monitor keyword.";
|
||||||
|
|
||||||
|
wlr_output_destroy(PMONITOR->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
|
|
||||||
std::string getReply(std::string request) {
|
std::string getReply(std::string request) {
|
||||||
auto format = HyprCtl::FORMAT_NORMAL;
|
auto format = HyprCtl::FORMAT_NORMAL;
|
||||||
|
|
||||||
|
@ -786,6 +866,8 @@ std::string getReply(std::string request) {
|
||||||
return splashRequest();
|
return splashRequest();
|
||||||
else if (request == "cursorpos")
|
else if (request == "cursorpos")
|
||||||
return cursorPosRequest(format);
|
return cursorPosRequest(format);
|
||||||
|
else if (request.find("output") == 0)
|
||||||
|
return dispatchOutput(request);
|
||||||
else if (request.find("dispatch") == 0)
|
else if (request.find("dispatch") == 0)
|
||||||
return dispatchRequest(request);
|
return dispatchRequest(request);
|
||||||
else if (request.find("keyword") == 0)
|
else if (request.find("keyword") == 0)
|
||||||
|
|
|
@ -8,6 +8,9 @@ void CMonitor::onConnect(bool noRule) {
|
||||||
|
|
||||||
szName = output->name;
|
szName = output->name;
|
||||||
|
|
||||||
|
if (!wlr_backend_is_drm(output->backend))
|
||||||
|
createdByUser = true; // should be true. WL, X11 and Headless backends should be addable / removable
|
||||||
|
|
||||||
// get monitor rule that matches
|
// get monitor rule that matches
|
||||||
SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(output->name, output->description ? output->description : "");
|
SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(output->name, output->description ? output->description : "");
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ public:
|
||||||
bool dpmsStatus = true;
|
bool dpmsStatus = true;
|
||||||
bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
|
bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
|
||||||
bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
|
bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
|
||||||
|
bool createdByUser = false;
|
||||||
|
|
||||||
// mirroring
|
// mirroring
|
||||||
CMonitor* pMirrorOf = nullptr;
|
CMonitor* pMirrorOf = nullptr;
|
||||||
|
|
|
@ -154,4 +154,8 @@ inline void wlr_xwayland_surface_close(wlr_xwayland_surface*) { }
|
||||||
|
|
||||||
inline void wlr_xwayland_surface_set_fullscreen(wlr_xwayland_surface*, bool) { }
|
inline void wlr_xwayland_surface_set_fullscreen(wlr_xwayland_surface*, bool) { }
|
||||||
|
|
||||||
inline void wlr_xwayland_surface_set_minimized(wlr_xwayland_surface *, bool) {}
|
inline void wlr_xwayland_surface_set_minimized(wlr_xwayland_surface *, bool) { }
|
||||||
|
|
||||||
|
inline bool wlr_backend_is_x11(void*) { return false; }
|
||||||
|
|
||||||
|
inline void wlr_x11_output_create(void*) { }
|
|
@ -99,10 +99,15 @@ extern "C" {
|
||||||
#include <wlr/types/wlr_text_input_v3.h>
|
#include <wlr/types/wlr_text_input_v3.h>
|
||||||
#include <wlr/types/wlr_touch.h>
|
#include <wlr/types/wlr_touch.h>
|
||||||
#include <wlr/types/wlr_switch.h>
|
#include <wlr/types/wlr_switch.h>
|
||||||
|
#include <wlr/config.h>
|
||||||
|
#include <wlr/backend/headless.h>
|
||||||
|
#include <wlr/backend/multi.h>
|
||||||
|
#include <wlr/backend/wayland.h>
|
||||||
|
|
||||||
#include <drm_fourcc.h>
|
#include <drm_fourcc.h>
|
||||||
|
|
||||||
#ifndef NO_XWAYLAND
|
#ifndef NO_XWAYLAND
|
||||||
|
#include <wlr/backend/x11.h>
|
||||||
#include <wlr/xwayland.h>
|
#include <wlr/xwayland.h>
|
||||||
#include <X11/Xproto.h>
|
#include <X11/Xproto.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1347,6 +1347,13 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
|
||||||
pMonitor->vecSize = (Vector2D(x, y) / pMonitor->scale).floor();
|
pMonitor->vecSize = (Vector2D(x, y) / pMonitor->scale).floor();
|
||||||
pMonitor->vecTransformedSize = Vector2D(x,y);
|
pMonitor->vecTransformedSize = Vector2D(x,y);
|
||||||
|
|
||||||
|
if (pMonitor->createdByUser) {
|
||||||
|
wlr_box transformedBox = { 0, 0, (int)pMonitor->vecTransformedSize.x, (int)pMonitor->vecTransformedSize.y };
|
||||||
|
wlr_box_transform(&transformedBox, &transformedBox, wlr_output_transform_invert(pMonitor->output->transform), (int)pMonitor->vecTransformedSize.x, (int)pMonitor->vecTransformedSize.y);
|
||||||
|
|
||||||
|
pMonitor->vecPixelSize = Vector2D(transformedBox.width, transformedBox.height);
|
||||||
|
}
|
||||||
|
|
||||||
if (pMonitorRule->offset == Vector2D(-1, -1) && pMonitor->vecPosition == Vector2D(-1, -1)) {
|
if (pMonitorRule->offset == Vector2D(-1, -1) && pMonitor->vecPosition == Vector2D(-1, -1)) {
|
||||||
// let's find manually a sensible position for it, to the right.
|
// let's find manually a sensible position for it, to the right.
|
||||||
Vector2D finalPos;
|
Vector2D finalPos;
|
||||||
|
|
Loading…
Reference in a new issue