Skip to content

Commit

Permalink
feat: impl treeland output management
Browse files Browse the repository at this point in the history
Log: primary output setting
  • Loading branch information
wineee committed Jan 3, 2024
1 parent 0a4a7c6 commit ce94284
Show file tree
Hide file tree
Showing 14 changed files with 300 additions and 10 deletions.
3 changes: 2 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
packages = with pkgs; [
# For submodule build
libinput
wayland
];

inputsFrom = [
Expand All @@ -51,7 +52,7 @@
export NIX_CFLAGS_COMPILE=$(echo $NIX_CFLAGS_COMPILE | sed 's/-DQT_NO_DEBUG//')
#export QT_LOGGING_RULES="*.debug=true;qt.*.debug=false"
#export WAYLAND_DEBUG=1
export QT_PLUGIN_PATH=${makeQtpluginPath (with pkgs.qt6; [ qtbase qtdeclarative qtquick3d qtwayland qt5compat ])}
export QT_PLUGIN_PATH=${makeQtpluginPath (with pkgs.qt6; [ qtbase qtdeclarative qtquick3d qtimageformats qtwayland qt5compat ])}
export QML2_IMPORT_PATH=${makeQmlpluginPath (with pkgs.qt6; [ qtdeclarative qtquick3d qt5compat ]
++ [ dde-nixos.packages.${system}.qt6.dtkdeclarative ] )}
export QML_IMPORT_PATH=$QML2_IMPORT_PATH
Expand Down
2 changes: 2 additions & 0 deletions nix/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
, wrapQtAppsHook
, qtbase
, qtquick3d
, qtimageformats
, qtwayland
, dtkdeclarative
, dtksystemsettings
Expand Down Expand Up @@ -61,6 +62,7 @@ stdenv.mkDerivation rec {
buildInputs = [
qtbase
qtquick3d
qtimageformats
qtwayland
dtkdeclarative
dtksystemsettings
Expand Down
2 changes: 2 additions & 0 deletions src/treeland/data/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ ws_generate_local(server ${CMAKE_CURRENT_SOURCE_DIR}/treeland-shortcut-manager-v
ws_generate_local(server ${CMAKE_CURRENT_SOURCE_DIR}/treeland-socket-manager-v1.xml socket-server-protocol)
ws_generate_local(server ${CMAKE_CURRENT_SOURCE_DIR}/treeland-foreign-toplevel-manager-v1.xml foreign-toplevel-manager-server-protocol)
ws_generate_local(server ${CMAKE_CURRENT_SOURCE_DIR}/treeland-personalization-manager-v1.xml personalization-server-protocol)
ws_generate_local(server ${CMAKE_CURRENT_SOURCE_DIR}/treeland-output-management.xml output-management-protocol)

ws_generate(server wayland-protocols stable/xdg-shell/xdg-shell.xml xdg-shell-protocol)
ws_generate(server wayland-protocols staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml ext-foreign-toplevel-list-server-protocol)
Expand All @@ -55,6 +56,7 @@ target_sources(treeland-data PRIVATE
personalization-server-protocol.c
foreign-toplevel-manager-server-protocol.c
ext-foreign-toplevel-list-server-protocol.c
output-management-protocol.c
)

target_link_libraries(treeland-data
Expand Down
30 changes: 30 additions & 0 deletions src/treeland/data/treeland-output-management.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="treeland_output_manager_v1">
<copyright><![CDATA[
SPDX-FileCopyrightText: 2023 rewine <[email protected]>.
SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
]]></copyright>

<interface name="treeland_output_manager_v1" version="1">
<description summary="expose which is the primary display">
Protocol for telling which is the primary display among the selection of enabled outputs.
</description>

<request name="set_primary_output" since="1">
<description summary="Select which primary output to use" />
<arg name="output" type="string" allow-null="false"/>
</request>

<event name="primary_output">
<description summary="Provide the current primary output's name">
Specifies which output is the primary one identified by their name.
</description>
<arg name="output_name" type="string" summary="the name of the output"/>
</event>

<request name="destroy" type="destructor" since="1">
<description summary="Destroy the primary output notifier."/>
</request>
</interface>

</protocol>
2 changes: 2 additions & 0 deletions src/treeland/protocols/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ FILES
ext_foreign_toplevel_list_impl.h
foreigntoplevelhandlev1.h
foreign_toplevel_manager_impl.h
output_manager_impl.h
personalization_manager_impl.h
personalizationmanager.h
shortcut_manager_impl.h
Expand All @@ -23,6 +24,7 @@ target_sources(treeland-protocols PRIVATE
ext_foreign_toplevel_list_impl.cpp
foreigntoplevelhandlev1.cpp
foreigntoplevelmanagerv1.cpp
output_manager_impl.cpp
personalization_manager_impl.cpp
personalizationwindowcontext.cpp
personalizationwallpapercontext.cpp
Expand Down
80 changes: 80 additions & 0 deletions src/treeland/protocols/output_manager_impl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (C) 2023 rewine <[email protected]>.
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "output_manager_impl.h"
#include "utils.h"

#include <wayland-server-core.h>
#include <cassert>

void output_manager_handle_set_primary_output([[maybe_unused]]struct wl_client *client,
struct wl_resource *resource,
const char *output) {
auto *manager = output_manager_from_resource(resource);
wl_signal_emit(&manager->events.set_primary_output, &output);
}

static const struct treeland_output_manager_v1_interface output_manager_impl {
.set_primary_output = output_manager_handle_set_primary_output,
.destroy = resource_handle_destroy,
};

struct treeland_output_manager_v1 *treeland_output_manager_v1_create(struct wl_display *display)
{
auto *manager = static_cast<treeland_output_manager_v1*>(calloc(1, sizeof(treeland_output_manager_v1)));
if (manager == nullptr) {
return nullptr;
}
manager->global = wl_global_create(
display, &treeland_output_manager_v1_interface,
TREELAND_OUTPUT_MANAGER_V1_VERSION, manager, output_manager_bind);

wl_list_init(&manager->resources);
wl_signal_init(&manager->events.set_primary_output);

manager->display_destroy.notify = output_manager_handle_display_destroy;
wl_display_add_destroy_listener(display,
&manager->display_destroy);
return manager;
}

void treeland_output_manager_v1_set_primary_output(treeland_output_manager_v1 *manager, const char *name)
{
manager->primary_output_name = name;
wl_resource *resource;
wl_list_for_each(resource, &manager->resources, link) {
treeland_output_manager_v1_send_primary_output(resource, name);
}
}

struct treeland_output_manager_v1 *output_manager_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &treeland_output_manager_v1_interface, &output_manager_impl));
struct treeland_output_manager_v1 *manager = static_cast<treeland_output_manager_v1*>(wl_resource_get_user_data(resource));
assert(manager != NULL);
return manager;
}

void output_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) {
struct treeland_output_manager_v1 *manager = static_cast<treeland_output_manager_v1*>(data);

struct wl_resource *resource = wl_resource_create(client, &treeland_output_manager_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &output_manager_impl, manager, NULL);
wl_list_insert(&manager->resources, wl_resource_get_link(resource));

treeland_output_manager_v1_send_primary_output(resource, manager->primary_output_name);
}

void output_manager_handle_display_destroy(struct wl_listener *listener, [[maybe_unused]] void *data)
{
struct treeland_output_manager_v1 *manager =
wl_container_of(listener, manager, display_destroy);

wl_global_destroy(manager->global);
wl_list_remove(&manager->display_destroy.link);
free(manager);
}
39 changes: 39 additions & 0 deletions src/treeland/protocols/output_manager_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (C) 2023 rewine <[email protected]>.
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#pragma once

#include "output-management-protocol.h"

#define TREELAND_OUTPUT_MANAGER_V1_VERSION 1

struct treeland_output_manager_v1 {
struct wl_global *global;
struct wl_list resources;

struct {
struct wl_signal set_primary_output;
} events;

const char *primary_output_name { nullptr };

struct wl_listener display_destroy;
};

struct treeland_output_manager_v1 *treeland_output_manager_v1_create(
struct wl_display *display);

void treeland_output_manager_v1_set_primary_output(
struct treeland_output_manager_v1 *manager,
const char *name);

struct treeland_output_manager_v1 *output_manager_from_resource(
struct wl_resource *resource);

void output_manager_bind(struct wl_client *client,
void *data,
uint32_t version,
uint32_t id);

void output_manager_handle_display_destroy(struct wl_listener *listener,
void *data);
4 changes: 3 additions & 1 deletion src/treeland/protocols/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

#pragma once

#include <wayland-server.h>
#include <wayland-server-core.h>

static void resource_handle_destroy(struct wl_client *,
struct wl_resource *resource)
{
wl_list_remove(&resource->link);
wl_resource_destroy(resource);
}

Expand All @@ -20,4 +22,4 @@ static void context_destroy(T *context)

wl_list_remove(&context->link);
free(context);
}
}
2 changes: 2 additions & 0 deletions src/treeland/quick/protocols/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ qt_add_qml_module(treeland-quick-protocols
STATIC
SOURCES
extforeigntoplevellist.cpp
outputmanagement.cpp
foreigntoplevelmanagerv1.cpp
shortcutmanager.cpp
socketmanager.cpp
Expand All @@ -19,6 +20,7 @@ target_sources(treeland-quick-protocols PUBLIC
FILE_SET HEADERS
FILES
extforeigntoplevellist.h
outputmanagement.h
foreigntoplevelmanagerv1.h
shortcutmanager.h
socketmanager.h
Expand Down
83 changes: 83 additions & 0 deletions src/treeland/quick/protocols/outputmanagement.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (C) 2023 rewine <[email protected]>.
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "outputmanagement.h"
#include "output_manager_impl.h"

#include <qwdisplay.h>
#include <wserver.h>
#include <woutput.h>

#include <QQmlInfo>
#include <QDebug>

extern "C" {
#include <wayland-server-core.h>
}

TreelandOutputManager::TreelandOutputManager(QObject *parent)
: Waylib::Server::WQuickWaylandServerInterface(parent)
{

}

void TreelandOutputManager::on_set_primary_output(void *data)
{
const char *name = *(static_cast<const char **>(data));
Q_EMIT requestSetPrimaryOutput(name);
}

const char *TreelandOutputManager::primaryOutput()
{
return m_handle->primary_output_name;
}

bool TreelandOutputManager::setPrimaryOutput(const char *name)
{
if (name == nullptr) {
if (m_outputs.empty()) { // allow null when output list is empty
treeland_output_manager_v1_set_primary_output(m_handle, nullptr);
Q_EMIT primaryOutputChanged();
return true;
} else {
return false;
}
}
if (m_handle->primary_output_name != nullptr && strcmp(m_handle->primary_output_name, name) == 0)
return true;
for (auto *output : m_outputs)
if (strcmp(output->nativeHandle()->name, name) == 0) {
treeland_output_manager_v1_set_primary_output(m_handle, output->nativeHandle()->name);
Q_EMIT primaryOutputChanged();
return true;
}
qmlWarning(this) << QString("Try to set unknown output(%1) as primary output!").arg(name);
return false;
}

void TreelandOutputManager::newOutput(WAYLIB_SERVER_NAMESPACE::WOutput *output)
{
m_outputs.append(output);
if (m_handle->primary_output_name == nullptr)
setPrimaryOutput(output->nativeHandle()->name);
}

void TreelandOutputManager::removeOutput(WAYLIB_SERVER_NAMESPACE::WOutput *output)
{
m_outputs.removeOne(output);

if (m_handle->primary_output_name == output->nativeHandle()->name) {
if (m_outputs.isEmpty()) {
setPrimaryOutput(nullptr);
} else {
setPrimaryOutput(m_outputs.first()->nativeHandle()->name);
}
}
}

void TreelandOutputManager::create()
{
m_handle = treeland_output_manager_v1_create(server()->handle()->handle());

m_sc.connect(&m_handle->events.set_primary_output, this, &TreelandOutputManager::on_set_primary_output);
}
45 changes: 45 additions & 0 deletions src/treeland/quick/protocols/outputmanagement.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (C) 2023 rewine <[email protected]>.
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#pragma once

#include <qwsignalconnector.h>
#include <wquickwaylandserver.h>

#include <QList>
#include <QQmlEngine>

struct treeland_output_manager_v1;

WAYLIB_SERVER_BEGIN_NAMESPACE
class WOutput;
WAYLIB_SERVER_END_NAMESPACE

class TreelandOutputManager : public Waylib::Server::WQuickWaylandServerInterface {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(const char *primaryOutput READ primaryOutput WRITE setPrimaryOutput NOTIFY primaryOutputChanged)

public:
explicit TreelandOutputManager(QObject *parent = nullptr);

const char *primaryOutput();
bool setPrimaryOutput(const char *name);
Q_INVOKABLE void newOutput(WAYLIB_SERVER_NAMESPACE::WOutput *output);
Q_INVOKABLE void removeOutput(WAYLIB_SERVER_NAMESPACE::WOutput *output);

public Q_SLOTS:
void on_set_primary_output(void *data);

protected:
void create() override;

Q_SIGNALS:
void requestSetPrimaryOutput(const char *);
void primaryOutputChanged();

private:
treeland_output_manager_v1 *m_handle { nullptr };
QList<WAYLIB_SERVER_NAMESPACE::WOutput*> m_outputs;
QW_NAMESPACE::QWSignalConnector m_sc;
};
3 changes: 0 additions & 3 deletions src/treeland/quick/protocols/socketmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
#include <wserver.h>

#include <QDebug>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <QTimer>

#include "socket_manager_impl.h"
Expand Down
Loading

0 comments on commit ce94284

Please sign in to comment.