From 80775707eae174644a5e71a48443ad10c7d89c6b Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Sat, 3 Dec 2022 21:27:49 +0000 Subject: [PATCH] Implement hyprland-share-picker --- .gitignore | 4 + README.md | 2 + .../xdg-desktop-portal-hyprland.service.in | 11 + .../systemd/xdg-desktop-portal-wlr.service.in | 11 - hyprland-share-picker/CMakeLists.txt | 61 +++++ hyprland-share-picker/Makefile | 4 + hyprland-share-picker/main.cpp | 176 +++++++++++++ hyprland-share-picker/mainpicker.cpp | 19 ++ hyprland-share-picker/mainpicker.h | 25 ++ hyprland-share-picker/mainpicker.ui | 160 ++++++++++++ wlr.portal => hyprland.portal | 4 +- meson.build | 30 +-- meson_options.txt | 1 - ...op.impl.portal.desktop.hyprland.service.in | 4 + ...desktop.impl.portal.desktop.wlr.service.in | 4 - src/screencast/wlr_screencast.c | 231 +++--------------- xdg-desktop-portal-wlr.5.scd | 99 -------- 17 files changed, 510 insertions(+), 336 deletions(-) create mode 100644 contrib/systemd/xdg-desktop-portal-hyprland.service.in delete mode 100644 contrib/systemd/xdg-desktop-portal-wlr.service.in create mode 100644 hyprland-share-picker/CMakeLists.txt create mode 100644 hyprland-share-picker/Makefile create mode 100644 hyprland-share-picker/main.cpp create mode 100644 hyprland-share-picker/mainpicker.cpp create mode 100644 hyprland-share-picker/mainpicker.h create mode 100644 hyprland-share-picker/mainpicker.ui rename wlr.portal => hyprland.portal (50%) create mode 100644 org.freedesktop.impl.portal.desktop.hyprland.service.in delete mode 100644 org.freedesktop.impl.portal.desktop.wlr.service.in delete mode 100644 xdg-desktop-portal-wlr.5.scd diff --git a/.gitignore b/.gitignore index fc62187..b89059a 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,7 @@ dkms.conf # build folder build/ build-*/ + +.vscode/ + +hyprland-share-picker/build/ \ No newline at end of file diff --git a/README.md b/README.md index 28abebb..3f50b6b 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Although `-wlr` **does** work with Hyprland, `-hyprland` offers more features. ```sh meson build ninja -C build +cd hyprland-share-picker && make all && cd .. ``` ## Installing @@ -21,6 +22,7 @@ ninja -C build ```sh ninja -C build install +sudo cp ./hyprland-share-picker/build/hyprland-share-picker /usr/bin ``` ## License diff --git a/contrib/systemd/xdg-desktop-portal-hyprland.service.in b/contrib/systemd/xdg-desktop-portal-hyprland.service.in new file mode 100644 index 0000000..ecaec96 --- /dev/null +++ b/contrib/systemd/xdg-desktop-portal-hyprland.service.in @@ -0,0 +1,11 @@ +[Unit] +Description=Portal service (Hyprland implementation) +PartOf=graphical-session.target +After=graphical-session.target +ConditionEnvironment=WAYLAND_DISPLAY + +[Service] +Type=dbus +BusName=org.freedesktop.impl.portal.desktop.hyprland +ExecStart=@libexecdir@/xdg-desktop-portal-hyprland +Restart=on-failure diff --git a/contrib/systemd/xdg-desktop-portal-wlr.service.in b/contrib/systemd/xdg-desktop-portal-wlr.service.in deleted file mode 100644 index 29a03f4..0000000 --- a/contrib/systemd/xdg-desktop-portal-wlr.service.in +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=Portal service (wlroots implementation) -PartOf=graphical-session.target -After=graphical-session.target -ConditionEnvironment=WAYLAND_DISPLAY - -[Service] -Type=dbus -BusName=org.freedesktop.impl.portal.desktop.wlr -ExecStart=@libexecdir@/xdg-desktop-portal-wlr -Restart=on-failure diff --git a/hyprland-share-picker/CMakeLists.txt b/hyprland-share-picker/CMakeLists.txt new file mode 100644 index 0000000..8f5a4d8 --- /dev/null +++ b/hyprland-share-picker/CMakeLists.txt @@ -0,0 +1,61 @@ +cmake_minimum_required(VERSION 3.5) + +project(hyprland-share-picker VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) + +set(PROJECT_SOURCES + main.cpp + mainpicker.cpp + mainpicker.h + mainpicker.ui +) + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_executable(hyprland-share-picker + MANUAL_FINALIZATION + ${PROJECT_SOURCES} + ) +# Define target properties for Android with Qt 6 as: +# set_property(TARGET hyprland-share-picker APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR +# ${CMAKE_CURRENT_SOURCE_DIR}/android) +# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation +else() + if(ANDROID) + add_library(hyprland-share-picker SHARED + ${PROJECT_SOURCES} + ) +# Define properties for Android with Qt 5 after find_package() calls as: +# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") + else() + add_executable(hyprland-share-picker + ${PROJECT_SOURCES} + ) + endif() +endif() + +target_link_libraries(hyprland-share-picker PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) + +set_target_properties(hyprland-share-picker PROPERTIES + MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + WIN32_EXECUTABLE TRUE +) + +install(TARGETS hyprland-share-picker + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +if(QT_VERSION_MAJOR EQUAL 6) + qt_finalize_executable(hyprland-share-picker) +endif() diff --git a/hyprland-share-picker/Makefile b/hyprland-share-picker/Makefile new file mode 100644 index 0000000..5dc3661 --- /dev/null +++ b/hyprland-share-picker/Makefile @@ -0,0 +1,4 @@ + +all: + mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -H./ -B./build -G Ninja + cmake --build ./build --config Release --target all -j$(shell nproc) \ No newline at end of file diff --git a/hyprland-share-picker/main.cpp b/hyprland-share-picker/main.cpp new file mode 100644 index 0000000..6225170 --- /dev/null +++ b/hyprland-share-picker/main.cpp @@ -0,0 +1,176 @@ +#include "mainpicker.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +std::string execAndGet(const char* cmd) { + std::array buffer; + std::string result; + std::unique_ptr pipe(popen(cmd, "r"), pclose); + if (!pipe) { + throw std::runtime_error("popen() failed!"); + } + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } + return result; +} + +QApplication* pickerPtr = nullptr; + +int main(int argc, char *argv[]) { + qputenv("QT_LOGGING_RULES", "qml=false"); + QApplication picker(argc, argv); + pickerPtr = &picker; + MainPicker w; + + // get the tabwidget + const auto TABWIDGET = (QTabWidget*)w.children()[1]->children()[0]; + + const auto TAB1 = (QWidget*)TABWIDGET->children()[0]; + + const auto SCREENS_SCROLL_AREA_CONTENTS = (QWidget*)TAB1->findChild("screens")->findChild("scrollArea")->findChild("scrollAreaWidgetContents"); + + // add all screens + const auto SCREENS = picker.screens(); + + constexpr int BUTTON_WIDTH = 441; + constexpr int BUTTON_HEIGHT = 41; + constexpr int BUTTON_PAD = 4; + + for (int i = 0; i < SCREENS.size(); ++i) { + const auto GEOMETRY = SCREENS[i]->geometry(); + + QString text = QString::fromStdString(std::string("Screen " + std::to_string(i) + " at " + std::to_string(GEOMETRY.x()) + ", " + + std::to_string(GEOMETRY.y()) + " (" + std::to_string(GEOMETRY.width()) + "x" + + std::to_string(GEOMETRY.height()) + ") (") + SCREENS[i]->name().toStdString() + ")"); + QPushButton* button = new QPushButton(text, (QWidget*)SCREENS_SCROLL_AREA_CONTENTS); + button->move(9, 5 + (BUTTON_HEIGHT + BUTTON_PAD) * i); + button->resize(BUTTON_WIDTH, BUTTON_HEIGHT); + QObject::connect(button, &QPushButton::clicked, [=] () { + std::string ID = button->text().toStdString(); + ID = ID.substr(ID.find_last_of('(') + 1); + ID = ID.substr(0, ID.find_last_of(')')); + + std::cout << "screen:" << ID << "\n"; + pickerPtr->quit(); + }); + } + + SCREENS_SCROLL_AREA_CONTENTS->resize(SCREENS_SCROLL_AREA_CONTENTS->size().width(), 5 + (BUTTON_HEIGHT + BUTTON_PAD) * SCREENS.size()); + + // windows + const auto WINDOWS_SCROLL_AREA_CONTENTS = (QWidget*)TAB1->findChild("windows")->findChild("scrollArea_2")->findChild("scrollAreaWidgetContents_2"); + + // get all windows from hyprctl + std::string windowsList = execAndGet("hyprctl clients"); + + // loop over them + int windowIterator = 0; + while (windowsList.find("Window ") != std::string::npos) { + auto windowPropLen = windowsList.find("Window ", windowsList.find("\n\n") + 2); + if (windowPropLen == std::string::npos) + windowPropLen = windowsList.length(); + const std::string windowProp = windowsList.substr(0, windowPropLen); + windowsList = windowsList.substr(windowPropLen); + + // get window name + auto windowName = windowProp.substr(windowProp.find(" -> ") + 4); + windowName = windowName.substr(0, windowName.find_first_of('\n') - 1); + + auto windowHandle = windowProp.substr(7, windowProp.find(" -> ") - 7); + + QString text = QString::fromStdString("Window " + windowHandle + ": " + windowName); + + QPushButton* button = new QPushButton(text, (QWidget*)WINDOWS_SCROLL_AREA_CONTENTS); + button->move(9, 5 + (BUTTON_HEIGHT + BUTTON_PAD) * windowIterator); + button->resize(BUTTON_WIDTH, BUTTON_HEIGHT); + QObject::connect(button, &QPushButton::clicked, [=] () { + std::string HANDLE = button->text().toStdString(); + HANDLE = HANDLE.substr(7, HANDLE.find_first_of(':') - 7); + + std::cout << "window:" << HANDLE << "\n"; + pickerPtr->quit(); + }); + + windowIterator++; + } + + WINDOWS_SCROLL_AREA_CONTENTS->resize(WINDOWS_SCROLL_AREA_CONTENTS->size().width(), 5 + (BUTTON_HEIGHT + BUTTON_PAD) * windowIterator); + + // lastly, region + const auto REGION_OBJECT = (QWidget*)TAB1->findChild("region"); + + QString text = "Select region..."; + + QPushButton* button = new QPushButton(text, (QWidget*)REGION_OBJECT); + button->move(79, 80); + button->resize(321, 41); + QObject::connect(button, &QPushButton::clicked, [=] () { + auto REGION = execAndGet("slurp -f \"%o %x %y %w %h\""); + REGION = REGION.substr(0, REGION.length() - 1); + + // now, get the screen + QScreen* pScreen = nullptr; + if (REGION.find_first_of(' ') == std::string::npos) { + std::cout << "error1\n"; + pickerPtr->quit(); + return 1; + } + const auto SCREEN_NAME = REGION.substr(0, REGION.find_first_of(' ')); + + for (auto& screen : SCREENS) { + if (screen->name().toStdString() == SCREEN_NAME) { + pScreen = screen; + break; + } + } + + if (!pScreen) { + std::cout << "error2\n"; + pickerPtr->quit(); + return 1; + } + + // get all the coords + try { + REGION = REGION.substr(REGION.find_first_of(' ') + 1); + const auto X = std::stoi(REGION.substr(0, REGION.find_first_of(' '))); + REGION = REGION.substr(REGION.find_first_of(' ') + 1); + const auto Y = std::stoi(REGION.substr(0, REGION.find_first_of(' '))); + REGION = REGION.substr(REGION.find_first_of(' ') + 1); + const auto W = std::stoi(REGION.substr(0, REGION.find_first_of(' '))); + REGION = REGION.substr(REGION.find_first_of(' ') + 1); + const auto H = std::stoi(REGION); + + std::cout << "region:" << SCREEN_NAME << "@" << X - pScreen->geometry().x() << "," << Y - pScreen->geometry().y() << "," << W << "," << H << "\n"; + pickerPtr->quit(); + return 0; + } catch (...) { + std::cout << "error3\n"; + pickerPtr->quit(); + return 1; + } + + std::cout << "error4\n"; + pickerPtr->quit(); + return 1; + }); + + w.show(); + return picker.exec(); +} diff --git a/hyprland-share-picker/mainpicker.cpp b/hyprland-share-picker/mainpicker.cpp new file mode 100644 index 0000000..a09bb9e --- /dev/null +++ b/hyprland-share-picker/mainpicker.cpp @@ -0,0 +1,19 @@ +#include "mainpicker.h" +#include "./ui_mainpicker.h" +#include + +MainPicker::MainPicker(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainPicker) +{ + ui->setupUi(this); +} + +MainPicker::~MainPicker() +{ + delete ui; +} + +void MainPicker::onMonitorButtonClicked(QObject* target, QEvent* event) { + qDebug() << "click"; +} diff --git a/hyprland-share-picker/mainpicker.h b/hyprland-share-picker/mainpicker.h new file mode 100644 index 0000000..1713646 --- /dev/null +++ b/hyprland-share-picker/mainpicker.h @@ -0,0 +1,25 @@ +#ifndef MAINPICKER_H +#define MAINPICKER_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class MainPicker; } +QT_END_NAMESPACE + +class MainPicker : public QMainWindow +{ + Q_OBJECT + +public: + MainPicker(QWidget *parent = nullptr); + ~MainPicker(); + + void onMonitorButtonClicked(QObject* target, QEvent* event); + +private: + Ui::MainPicker *ui; +}; +#endif // MAINPICKER_H diff --git a/hyprland-share-picker/mainpicker.ui b/hyprland-share-picker/mainpicker.ui new file mode 100644 index 0000000..c40a385 --- /dev/null +++ b/hyprland-share-picker/mainpicker.ui @@ -0,0 +1,160 @@ + + + MainPicker + + + + 0 + 0 + 500 + 290 + + + + + 500 + 290 + + + + + 500 + 290 + + + + MainPicker + + + + + 500 + 290 + + + + + 500 + 290 + + + + + + 0 + 0 + 500 + 290 + + + + + 500 + 290 + + + + + 500 + 290 + + + + + + + QTabWidget::North + + + 0 + + + + Screen + + + + + 9 + 9 + 461 + 241 + + + + false + + + + + 0 + 0 + 459 + 239 + + + + + 459 + 239 + + + + Qt::WheelFocus + + + + + + + Window + + + + + 9 + 9 + 461 + 241 + + + + false + + + + + 0 + 0 + 459 + 239 + + + + + 459 + 239 + + + + Qt::WheelFocus + + + + + + + Region + + + + + + + scrollArea + scrollArea_2 + tabWidget + + + + diff --git a/wlr.portal b/hyprland.portal similarity index 50% rename from wlr.portal rename to hyprland.portal index aa63335..7b4f95c 100644 --- a/wlr.portal +++ b/hyprland.portal @@ -1,4 +1,4 @@ [portal] -DBusName=org.freedesktop.impl.portal.desktop.wlr +DBusName=org.freedesktop.impl.portal.desktop.hyprland Interfaces=org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.ScreenCast; -UseIn=wlroots;sway;Wayfire;river;phosh;Hyprland; +UseIn=Hyprland; diff --git a/meson.build b/meson.build index c1db66b..d1ee6ed 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project( - 'xdg-desktop-portal-wlr', + 'xdg-desktop-portal-hyprland', 'c', version: '0.6.0', license: 'MIT', @@ -86,7 +86,7 @@ xdpw_files = files([ ]) executable( - 'xdg-desktop-portal-wlr', + 'xdg-desktop-portal-hyprland', [xdpw_files, wl_proto_files], dependencies: [ wayland_client, @@ -111,7 +111,7 @@ conf_data.set('systemd_service', '') systemd = dependency('systemd', required: get_option('systemd')) if systemd.found() - systemd_service_file = 'xdg-desktop-portal-wlr.service' + systemd_service_file = 'xdg-desktop-portal-hyprland.service' user_unit_dir = systemd.get_variable(pkgconfig: 'systemduserunitdir', pkgconfig_define: ['prefix', get_option('prefix')]) conf_data.set('systemd_service', 'SystemdService=' + systemd_service_file) @@ -126,33 +126,13 @@ endif configure_file( configuration: conf_data, - input: 'org.freedesktop.impl.portal.desktop.wlr.service.in', + input: 'org.freedesktop.impl.portal.desktop.hyprland.service.in', output: '@BASENAME@', install_dir: join_paths(get_option('datadir'), 'dbus-1', 'services'), ) install_data( - 'wlr.portal', + 'hyprland.portal', install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal', 'portals'), ) -scdoc = dependency('scdoc', required: get_option('man-pages'), version: '>= 1.9.7', native: true) -if scdoc.found() - man_pages = ['xdg-desktop-portal-wlr.5.scd'] - foreach src : man_pages - topic = src.split('.')[0] - section = src.split('.')[1] - output = topic + '.' + section - - custom_target( - output, - input: files(src), - output: output, - command: [ - 'sh', '-c', '@0@ < @INPUT@ > @1@'.format(scdoc.get_variable(pkgconfig: 'scdoc'), output) - ], - install: true, - install_dir: join_paths(get_option('mandir'), 'man' + section), - ) - endforeach -endif diff --git a/meson_options.txt b/meson_options.txt index 77fa135..d7ae3ab 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,3 +1,2 @@ option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library') option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit') -option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') diff --git a/org.freedesktop.impl.portal.desktop.hyprland.service.in b/org.freedesktop.impl.portal.desktop.hyprland.service.in new file mode 100644 index 0000000..cb1f305 --- /dev/null +++ b/org.freedesktop.impl.portal.desktop.hyprland.service.in @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.freedesktop.impl.portal.desktop.hyprland +Exec=@libexecdir@/xdg-desktop-portal-hyprland +@systemd_service@ diff --git a/org.freedesktop.impl.portal.desktop.wlr.service.in b/org.freedesktop.impl.portal.desktop.wlr.service.in deleted file mode 100644 index 2397e77..0000000 --- a/org.freedesktop.impl.portal.desktop.wlr.service.in +++ /dev/null @@ -1,4 +0,0 @@ -[D-BUS Service] -Name=org.freedesktop.impl.portal.desktop.wlr -Exec=@libexecdir@/xdg-desktop-portal-wlr -@systemd_service@ diff --git a/src/screencast/wlr_screencast.c b/src/screencast/wlr_screencast.c index 8d64553..cbf347d 100644 --- a/src/screencast/wlr_screencast.c +++ b/src/screencast/wlr_screencast.c @@ -333,202 +333,45 @@ static void wlr_init_xdg_outputs(struct xdpw_screencast_context *ctx) { } } -static pid_t spawn_chooser(char *cmd, int chooser_in[2], int chooser_out[2]) { - logprint(TRACE, - "exec chooser called: cmd %s, pipe chooser_in (%d,%d), pipe chooser_out (%d,%d)", - cmd, chooser_in[0], chooser_in[1], chooser_out[0], chooser_out[1]); - pid_t pid = fork(); - - if (pid < 0) { - perror("fork"); - return pid; - } else if (pid == 0) { - close(chooser_in[1]); - close(chooser_out[0]); - - dup2(chooser_in[0], STDIN_FILENO); - dup2(chooser_out[1], STDOUT_FILENO); - close(chooser_in[0]); - close(chooser_out[1]); - - execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); - - perror("execl"); - _exit(127); - } - - close(chooser_in[0]); - close(chooser_out[1]); - - return pid; -} - -static bool wait_chooser(pid_t pid) { - int status; - if (waitpid(pid ,&status, 0) != -1 && WIFEXITED(status)) { - return WEXITSTATUS(status) != 127; - } - return false; -} - -static bool wlr_output_chooser(struct xdpw_output_chooser *chooser, - struct wl_list *output_list, struct xdpw_wlr_output **output) { - logprint(DEBUG, "wlroots: output chooser called"); - struct xdpw_wlr_output *out; - size_t name_size = 0; - char *name = NULL; - *output = NULL; - - int chooser_in[2]; //p -> c - int chooser_out[2]; //c -> p - - if (pipe(chooser_in) == -1) { - perror("pipe chooser_in"); - logprint(ERROR, "Failed to open pipe chooser_in"); - goto error_chooser_in; - } - if (pipe(chooser_out) == -1) { - perror("pipe chooser_out"); - logprint(ERROR, "Failed to open pipe chooser_out"); - goto error_chooser_out; - } - - pid_t pid = spawn_chooser(chooser->cmd, chooser_in, chooser_out); - if (pid < 0) { - logprint(ERROR, "Failed to fork chooser"); - goto error_fork; - } - - switch (chooser->type) { - case XDPW_CHOOSER_DMENU:; - FILE *f = fdopen(chooser_in[1], "w"); - if (f == NULL) { - perror("fdopen pipe chooser_in"); - logprint(ERROR, "Failed to create stream writing to pipe chooser_in"); - goto error_fork; - } - wl_list_for_each(out, output_list, link) { - fprintf(f, "%s\n", out->name); - } - fclose(f); - break; - default: - close(chooser_in[1]); - } - - if (!wait_chooser(pid)) { - close(chooser_out[0]); - return false; - } - - FILE *f = fdopen(chooser_out[0], "r"); - if (f == NULL) { - perror("fdopen pipe chooser_out"); - logprint(ERROR, "Failed to create stream reading from pipe chooser_out"); - close(chooser_out[0]); - goto end; - } - - ssize_t nread = getline(&name, &name_size, f); - fclose(f); - if (nread < 0) { - perror("getline failed"); - goto end; - } - - //Strip newline - char *p = strchr(name, '\n'); - if (p != NULL) { - *p = '\0'; - } - - logprint(TRACE, "wlroots: output chooser %s selects output %s", chooser->cmd, name); - wl_list_for_each(out, output_list, link) { - if (strcmp(out->name, name) == 0) { - *output = out; - break; - } - } - free(name); - -end: - return true; - -error_fork: - close(chooser_out[0]); - close(chooser_out[1]); -error_chooser_out: - close(chooser_in[0]); - close(chooser_in[1]); -error_chooser_in: - *output = NULL; - return false; -} - -static struct xdpw_wlr_output *wlr_output_chooser_default(struct wl_list *output_list) { - logprint(DEBUG, "wlroots: output chooser called"); - struct xdpw_output_chooser default_chooser[] = { - {XDPW_CHOOSER_SIMPLE, "slurp -f %o -or"}, - {XDPW_CHOOSER_DMENU, "wofi -d -n --prompt='Select the monitor to share:'"}, - {XDPW_CHOOSER_DMENU, "bemenu --prompt='Select the monitor to share:'"}, - }; - - size_t N = sizeof(default_chooser)/sizeof(default_chooser[0]); - struct xdpw_wlr_output *output = NULL; - bool ret; - for (size_t i = 0; iname); - } else { - logprint(DEBUG, "wlroots: output chooser canceled"); - } - return output; - } - return xdpw_wlr_output_first(output_list); -} - struct xdpw_wlr_output *xdpw_wlr_output_chooser(struct xdpw_screencast_context *ctx) { - switch (ctx->state->config->screencast_conf.chooser_type) { - case XDPW_CHOOSER_DEFAULT: - return wlr_output_chooser_default(&ctx->output_list); - case XDPW_CHOOSER_NONE: - if (ctx->state->config->screencast_conf.output_name) { - return xdpw_wlr_output_find_by_name(&ctx->output_list, ctx->state->config->screencast_conf.output_name); - } else { - return xdpw_wlr_output_first(&ctx->output_list); - } - case XDPW_CHOOSER_DMENU: - case XDPW_CHOOSER_SIMPLE:; - struct xdpw_wlr_output *output = NULL; - if (!ctx->state->config->screencast_conf.chooser_cmd) { - logprint(ERROR, "wlroots: no output chooser given"); - goto end; - } - struct xdpw_output_chooser chooser = { - ctx->state->config->screencast_conf.chooser_type, - ctx->state->config->screencast_conf.chooser_cmd - }; - logprint(DEBUG, "wlroots: output chooser %s (%d)", chooser.cmd, chooser.type); - bool ret = wlr_output_chooser(&chooser, &ctx->output_list, &output); - if (!ret) { - logprint(ERROR, "wlroots: output chooser %s failed", chooser.cmd); - goto end; - } - if (output) { - logprint(DEBUG, "wlroots: output chooser selects %s", output->name); - } else { - logprint(DEBUG, "wlroots: output chooser canceled"); - } - return output; + char result[1024]; + FILE *fp; + char buf[1024]; + + fp = popen("/usr/bin/hyprland-share-picker", "r"); + if (fp == NULL) { + printf("Failed to run command\n"); + exit(1); + } + + while (fgets(buf, sizeof(buf), fp) != NULL) { + strcat(result, buf); + } + + pclose(fp); + + // great, let's parse it. + + // TODO: window & region + if (strncmp(result, "screen:", 7) == 0) { + // find + char* display_name = malloc(strlen(result) - 7); + strncpy(display_name, result + 7, strlen(result) - 8); + display_name[strlen(result) - 8] = 0; + + struct xdpw_wlr_output* out; + wl_list_for_each(out, &ctx->output_list, link) { + if (strcmp(out->name, display_name) == 0) { + break; + } + } + + free(display_name); + + return out; + } else { + return NULL; } -end: - return NULL; } struct xdpw_wlr_output *xdpw_wlr_output_first(struct wl_list *output_list) { diff --git a/xdg-desktop-portal-wlr.5.scd b/xdg-desktop-portal-wlr.5.scd deleted file mode 100644 index 6cec968..0000000 --- a/xdg-desktop-portal-wlr.5.scd +++ /dev/null @@ -1,99 +0,0 @@ -xdg-desktop-portal-wlr(5) - -# NAME - -xdg-desktop-portal-wlr - an xdg-desktop-portal backend for wlroots - -# DESCRIPTION - -xdg-desktop-portal-wlr (or xdpw for short) allows applications to request -screenshots and screencasts via xdg-desktop-portal in wlroots-based Wayland -compositors. - -xdpw will try to load the configuration file from these locations: - -- $XDG_CONFIG_HOME/xdg-desktop-portal-wlr/$XDG_CURRENT_DESKTOP -- $XDG_CONFIG_HOME/xdg-desktop-portal-wlr/config -- /etc/xdg/xdg-desktop-portal-wlr/$XDG_CURRENT_DESKTOP -- /etc/xdg/xdg-desktop-portal-wlr/config - -_$XDG_CONFIG_HOME_ defaults to _~/.config_. -_$XDG_CURRENT_DESKTOP_ can be a colon separated list. Each element of that list will be tried. - -The configuration files use the INI file format. Example: - -``` -[screencast] -output_name=HDMI-A-1 -max_fps=30 -exec_before=disable_notifications.sh -exec_after=enable_notifications.sh -chooser_type=simple -chooser_cmd=slurp -f %o -or -``` - -# SCREENCAST OPTIONS - -These options need to be placed under the **[screencast]** section. - -**output_name** = _name_ - Select which output will be screencast. - - This option is used with **chooser_type** = none. The list of available outputs - can be obtained via **wayland-info**(1) (under the _zxdg_output_manager_v1_ - section). - -**max_fps** = _limit_ - Limit the number of frames per second to the provided rate. - - This is useful to reduce CPU usage when capturing frames at the output's - refresh rate is unnecessary. - -**exec_before** = _command_ - Execute _command_ before starting a screencast. The command will be executed within sh. - -**exec_after** = _command_ - Execute _command_ after ending all screencasts. The command will be executed within sh. - -**chooser_cmd** = _command_ - Run this command to select an output. - - For more details see **OUTPUT CHOOSER**. - -**chooser_type** = _type_ - Specifies the input send to the chooser. - - The supported types are: - - default: xdpw will try to use the first chooser found in the list of hardcoded choosers - (slurp, wofi, bemenu) and will fallback to an arbitrary output if none of those were found. - - none: xdpw will allow screencast either on the output given by **output_name**, or if empty - an arbitrary output without further interaction. - - simple, dmenu: xdpw will launch the chooser given by **chooser_cmd**. For more details - see **OUTPUT CHOOSER**. - -**force_mod_linear** = _bool_ - Force buffers with implicit modifiers to be linear (experimental) - - Setting this option to 1 will force xdpw to allocate dma-bufs with implicit modifier as linear. - This option shouldn't be required on single gpu setups, but can increase compatibility - especially on setups with multiple gpus. - - This option is experimental and can be removed or replaced in future versions. - -## OUTPUT CHOOSER - -The chooser can be any program or script with the following behaviour: -- It returns any error code except 127. The error code 127 is internally used to signal - that no command could be found and all output from it will be ignored. -- It returns the name of a valid output on stdout as given by **wayland-info**(1). - Everything else will be handled as declined by the user. -- To signal that the user has declined screencast, the chooser should exit without - anything on stdout. - -Supported types of choosers via the **chooser_type** option: -- simple: the chooser is just called without anything further on stdin. -- dmenu: the chooser receives a newline separated list (dmenu style) of outputs on stdin. - -# SEE ALSO - -**pipewire**(1)