Untitled

 avatar
unknown
plain_text
a month ago
23 kB
2
Indexable
light@light:~/wayfire/subprojects/wlroots/backend/rdprail$ ls
meson.build  rdprail_backend.c  rdprail_output.h
rdprail.c    rdprail_backend.h  rdprail_surface.c
rdprail.h    rdprail_output.c   rdprail_surface.h
light@light:~/wayfire/subprojects/wlroots/backend/rdprail$ cat meson.build  rdprail.h          rdprail_backend.h  rdprail_output.h rdprail.c    rdprail_backend.c  rdprail_output.c
# subprojects/wlroots/backend/rdprail/meson.build
rdprail_files = files(
    'rdprail.c',
    'rdprail_output.c',
    'rdprail_backend.c'
)

# Check for RDP dependencies
dep_freerdp = dependency('freerdp-server2', required: false)
dep_winpr = dependency('winpr2', required: false)
dep_gbm = dependency('gbm', required: false)

# Optional RDP backend support
if get_option('backends').contains('rdprail') or get_option('backends').contains('auto')
    if dep_freerdp.found() and dep_winpr.found() and dep_gbm.found()
        wlr_files += rdprail_files
        wlr_deps += [dep_freerdp, dep_winpr, dep_gbm]
    else
        if not dep_freerdp.found()
            error('FreerRDP not found. Disable RDP backend or install freerdp-server2.')
        endif
        if not dep_winpr.found()
            error('WinPR not found. Disable RDP backend or install winpr2.')
        endif
        if not dep_gbm.found()
            error('GBM not found. Disable RDP backend or install libgbm.')
        endif
    endif
endif
/* rdprail.h */
#ifndef WLR_RENDER_ALLOCATOR_RDPRAIL_H
#define WLR_RENDER_ALLOCATOR_RDPRAIL_H

#define _GNU_SOURCE  // For memfd_create

#include <stdint.h>
#include <wlr/render/allocator.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/interfaces/wlr_buffer.h>

// Define RECTANGLE_16 structure first
typedef struct {
    uint16_t left;
    uint16_t top;
    uint16_t right;
    uint16_t bottom;
} RECTANGLE_16;

// RDP-specific PDU structures
typedef struct {
    uint32_t poolId;
} GFXREDIR_OPEN_POOL_PDU;

typedef struct {
    uint32_t poolId;
    uint32_t bufferId;
    int width;
    int height;
    int format;
} GFXREDIR_CREATE_BUFFER_PDU;

// Surface command definition
#define GFX_REDIR_SURFACE_COMMAND_UPDATEREGION 1

// Context structure
struct _GfxRedirServerContext {
    int (*OpenPool)(struct _GfxRedirServerContext *ctx,
                   GFXREDIR_OPEN_POOL_PDU *open_pool);
    int (*CreateBuffer)(struct _GfxRedirServerContext *ctx,
                       GFXREDIR_CREATE_BUFFER_PDU *create_buffer);
    int (*SurfaceCommand)(struct _GfxRedirServerContext *ctx,
                         void *surface_data,
                         int command,
                         RECTANGLE_16 *rect,
                         void *buffer_data);
};

typedef struct _GfxRedirServerContext GfxRedirServerContext;

struct wlr_rdp_allocator {
    struct wlr_allocator base;
    GfxRedirServerContext *redir_ctx;
};

struct wlr_rdp_buffer {
    struct wlr_buffer buffer;
    uint32_t pool_id;
    uint32_t buffer_id;
    void *rdp_memory;
    size_t rdp_memory_size;
    int width;
    int height;
    int fd;  // File descriptor for shared memory
    bool data_access_in_progress;
};

// Function declarations
void* rdp_get_shared_memory(uint32_t pool_id, size_t *size, int *fd);
struct wlr_allocator *wlr_rdp_allocator_create(GfxRedirServerContext *redir_ctx);

#endif // WLR_RENDER_ALLOCATOR_RDPRAIL_H#ifndef WLR_BACKEND_RDP_H
#define WLR_BACKEND_RDP_H

#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/render/allocator.h>
#include "rdprail.h"

// Forward declarations
struct wlr_rdp_surface;

// Function declarations
struct wlr_backend *wlr_rdp_backend_create(
    struct wl_display *display,
    struct wlr_session *session,
    void *redir_ctx
);

struct wlr_rdp_backend *get_rdp_backend_from_backend(
    struct wlr_backend *backend
);

struct wlr_rdp_backend {
    struct wlr_backend base;
    GfxRedirServerContext *redir_ctx;
    struct wl_display *display;
    struct wlr_session *session;
    struct wlr_allocator *allocator;
    struct {
        struct wl_signal destroy;
        struct wl_signal new_output;
        struct wl_signal new_input;
    } events;
    struct wl_list outputs;  // wlr_rdp_output::link
    struct wl_list surfaces; // wlr_rdp_surface::link
    bool started;
};

#endif // WLR_BACKEND_RDP_H
/* rdprail_output.h */
#ifndef WLR_RDP_OUTPUT_H
#define WLR_RDP_OUTPUT_H

#include <wayland-server-core.h>
#include <wlr/interfaces/wlr_output.h>
#include <pixman.h>
#include "rdprail_backend.h"

struct wlr_rdp_backend;  // Forward declaration

struct wlr_rdp_output {
    struct wlr_output base;
    struct wlr_rdp_backend *backend;
    struct wl_list link;

    int width, height;
    int refresh_rate;
    bool initialized;

    struct wlr_rdp_output_state {
        struct wlr_buffer *pending_buffer;
        bool damaged;
        pixman_region32_t damage;
    } pending;

    struct {
        struct wl_signal destroy;
    } events;
};

struct wlr_rdp_output *wlr_rdp_output_create(struct wlr_rdp_backend *backend);
void wlr_rdp_output_set_refresh_rate(struct wlr_rdp_output *output, int refresh);

#endif


/* rdprail.c */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/memfd.h>
#include <wlr/render/allocator.h>
#include <wlr/render/drm_format_set.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/interfaces/wlr_buffer.h>
#include <wlr/util/log.h>
#include <drm/drm_fourcc.h>
#include <stdio.h>
#include "rdprail.h"
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <fcntl.h>      // For open() and O_RDWR
#include <gbm.h>        // For gbm_create_device and gbm_device_destroy
#include <wayland-server.h>  // For wl_event_loop functions
#include "backend/rdprail/rdprail.h"  // For RDP backend functions
#include "backend/backend.h"


void wlr_rdp_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer);

// Rest of the existing code remains the same, but add these function implementations if they're not already defined
/*
void wlr_rdp_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {
    // Implement the function body, likely similar to other buffer access end functions in wlroots
    // This might involve releasing locks, syncing memory, or other cleanup
}*/

// Logging function for debugging
static void log_buffer_state(struct wlr_buffer *buffer, const char *context) {
    fprintf(stderr, "Buffer state at %s:\n", context);
    fprintf(stderr, "  Width: %d\n", buffer->width);
    fprintf(stderr, "  Height: %d\n", buffer->height);
    fprintf(stderr, "  Impl: %p\n", (void*)buffer->impl);
    fprintf(stderr, "  Dropped: %s\n", buffer->dropped ? "true" : "false");
    fprintf(stderr, "  Locks: %zu\n", buffer->n_locks);
    fprintf(stderr, "  Accessing data ptr: %s\n",
        buffer->accessing_data_ptr ? "true" : "false");
}

// Placeholder implementation for shared memory retrieval
/*void* rdp_get_shared_memory(uint32_t pool_id, size_t *size, int *fd) {
    // Use syscall for memfd_create as a fallback
    *fd = syscall(SYS_memfd_create, "rdp_buffer", MFD_CLOEXEC);

    // If syscall fails, fall back to temporary file
    if (*fd == -1) {
        char temp_path[] = "/tmp/rdp_buffer_XXXXXX";
        *fd = mkstemp(temp_path);
        if (*fd == -1) {
            wlr_log(WLR_ERROR, "Failed to create shared memory");
            return NULL;
        }
        // Unlink the temporary file so it's not visible in the filesystem
        unlink(temp_path);
    }

    // Set size
    *size = 1024 * 1024;  // 1MB

    // Resize the file descriptor
    if (ftruncate(*fd, *size) == -1) {
        wlr_log(WLR_ERROR, "Failed to resize shared memory");
        close(*fd);
        return NULL;
    }

    // Map the shared memory
    void *memory = mmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
    if (memory == MAP_FAILED) {
        wlr_log(WLR_ERROR, "Failed to map shared memory");
        close(*fd);
        return NULL;
    }

    return memory;
}*/

void* rdp_get_shared_memory(uint32_t pool_id, size_t *size, int *fd) {
    *size = 640 * 480 * 4;  // Calculate exact size needed

    // Create memfd with specific size
    *fd = syscall(SYS_memfd_create, "rdp_buffer", MFD_CLOEXEC);
    if (*fd == -1) {
        char temp_path[] = "/tmp/rdp_buffer_XXXXXX";
        *fd = mkstemp(temp_path);
        if (*fd == -1) {
            return NULL;
        }
        unlink(temp_path);
    }

    // Set the size
    if (ftruncate(*fd, *size) == -1) {
        close(*fd);
        return NULL;
    }

    // Map the memory with proper protection flags
    void *memory = mmap(NULL, *size, PROT_READ | PROT_WRITE,
                       MAP_SHARED, *fd, 0);
    if (memory == MAP_FAILED) {
        close(*fd);
        return NULL;
    }

    // Clear the memory
    memset(memory, 0, *size);
    return memory;
}

// Mock RDP context functions
static int mock_open_pool(struct _GfxRedirServerContext *ctx,
    GFXREDIR_OPEN_POOL_PDU *open_pool) {
    static uint32_t next_pool_id = 1;
    open_pool->poolId = next_pool_id++;
    fprintf(stderr, "RDP Mock: Created pool %u\n", open_pool->poolId);
    return 0;
}

static int mock_create_buffer(struct _GfxRedirServerContext *ctx,
    GFXREDIR_CREATE_BUFFER_PDU *create_buffer) {
    static uint32_t next_buffer_id = 1;
    create_buffer->bufferId = next_buffer_id++;
    fprintf(stderr, "RDP Mock: Created buffer %u in pool %u\n",
            create_buffer->bufferId, create_buffer->poolId);
    return 0;
}

// Buffer method implementations
/*static void wlr_rdp_buffer_destroy(struct wlr_buffer *buffer) {
    struct wlr_rdp_buffer *rdp_buffer =
        wl_container_of(buffer, rdp_buffer, buffer);

    fprintf(stderr, "Destroying RDP buffer\n");

    // Ensure no data access in progress
    if (buffer->accessing_data_ptr) {
        wlr_log(WLR_ERROR, "Destroying buffer while data access in progress");
    }

    // Unmap and close shared memory
    if (rdp_buffer->rdp_memory) {
        munmap(rdp_buffer->rdp_memory, rdp_buffer->rdp_memory_size);
    }

    if (rdp_buffer->fd != -1) {
        close(rdp_buffer->fd);
    }

    // Free the buffer struct
    free(rdp_buffer);
}*/

static void wlr_rdp_buffer_destroy(struct wlr_buffer *buffer) {
    struct wlr_rdp_buffer *rdp_buffer = wl_container_of(buffer, rdp_buffer, buffer);

    // Wait for any ongoing data access to complete
    if (rdp_buffer->data_access_in_progress) {
        wlr_rdp_buffer_end_data_ptr_access(buffer);
    }

    // Clean up memory
    if (rdp_buffer->rdp_memory && rdp_buffer->rdp_memory_size > 0) {
        munmap(rdp_buffer->rdp_memory, rdp_buffer->rdp_memory_size);
        rdp_buffer->rdp_memory = NULL;
    }

    if (rdp_buffer->fd >= 0) {
        close(rdp_buffer->fd);
        rdp_buffer->fd = -1;
    }

    free(rdp_buffer);
}

static bool wlr_rdp_buffer_get_dmabuf(
    struct wlr_buffer *wlr_buffer,
    struct wlr_dmabuf_attributes *attribs
) {
    // No DMA-BUF support for this buffer type
    return false;
}

static bool wlr_rdp_buffer_get_shm(
    struct wlr_buffer *wlr_buffer,
    struct wlr_shm_attributes *attribs
) {
    struct wlr_rdp_buffer *rdp_buffer =
        wl_container_of(wlr_buffer, rdp_buffer, buffer);

    if (!rdp_buffer->rdp_memory) {
        return false;
    }

    attribs->fd = rdp_buffer->fd;
    attribs->format = DRM_FORMAT_ARGB8888;
    attribs->width = rdp_buffer->width;
    attribs->height = rdp_buffer->height;
    attribs->stride = rdp_buffer->width * 4;
    attribs->offset = 0;

    return true;
}

static bool wlr_rdp_buffer_begin_data_ptr_access(
    struct wlr_buffer *wlr_buffer,
    uint32_t flags,
    void **data,
    uint32_t *format,
    size_t *stride
) {
    struct wlr_rdp_buffer *rdp_buffer =
        wl_container_of(wlr_buffer, rdp_buffer, buffer);

    fprintf(stderr, "Beginning data ptr access\n");
    log_buffer_state(wlr_buffer, "Begin data ptr access");

    // Additional safety checks
    if (wlr_buffer->accessing_data_ptr) {
        wlr_log(WLR_ERROR, "Attempting to access buffer data while already in access");
        return false;
    }

    if (!rdp_buffer->rdp_memory) {
        wlr_log(WLR_ERROR, "No memory allocated for RDP buffer");
        return false;
    }

    if (data) {
        *data = rdp_buffer->rdp_memory;
        fprintf(stderr, "Data pointer set to %p\n", *data);
    }

    if (format) {
        *format = DRM_FORMAT_ARGB8888;
        fprintf(stderr, "Format set to %u\n", *format);
    }

    if (stride) {
        *stride = rdp_buffer->width * 4; // 4 bytes per pixel
        fprintf(stderr, "Stride set to %zu\n", *stride);
    }

    // Mark that data access is in progress
    rdp_buffer->data_access_in_progress = true;

    return true;
}

void wlr_rdp_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {
    struct wlr_rdp_buffer *rdp_buffer =
        wl_container_of(wlr_buffer, rdp_buffer, buffer);

    fprintf(stderr, "Ending data ptr access\n");
    log_buffer_state(wlr_buffer, "End data ptr access");

    // Reset data access flag
    rdp_buffer->data_access_in_progress = false;
}

// Define the buffer implementation matching wlr_buffer_impl
static const struct wlr_buffer_impl wlr_rdp_buffer_impl = {
    .destroy = wlr_rdp_buffer_destroy,
    .get_dmabuf = wlr_rdp_buffer_get_dmabuf,
    .get_shm = wlr_rdp_buffer_get_shm,
    .begin_data_ptr_access = wlr_rdp_buffer_begin_data_ptr_access,
    .end_data_ptr_access = wlr_rdp_buffer_end_data_ptr_access,
};

// Allocator creation methods
static struct wlr_buffer *rdp_allocator_create_buffer(
        struct wlr_allocator *alloc, int width, int height,
        const struct wlr_drm_format *format) {
    struct wlr_rdp_allocator *rdp_alloc =
        wl_container_of(alloc, rdp_alloc, base);

    fprintf(stderr, "Creating RDP buffer: %dx%d\n", width, height);

    // Create RDP pool and buffer
    GFXREDIR_OPEN_POOL_PDU open_pool = {0};
    if (rdp_alloc->redir_ctx->OpenPool(rdp_alloc->redir_ctx, &open_pool) != 0) {
        wlr_log(WLR_ERROR, "Failed to open RDP pool");
        return NULL;
    }

    GFXREDIR_CREATE_BUFFER_PDU create_buffer = {
        .poolId = open_pool.poolId,
        .width = width,
        .height = height,
        .format = 1 // Placeholder for GFXREDIR_BUFFER_PIXEL_FORMAT_ARGB_8888
    };

    if (rdp_alloc->redir_ctx->CreateBuffer(rdp_alloc->redir_ctx, &create_buffer) != 0) {
        wlr_log(WLR_ERROR, "Failed to create RDP buffer");
        return NULL;
    }

    // Allocate RDP buffer struct
    struct wlr_rdp_buffer *rdp_buffer = calloc(1, sizeof(struct wlr_rdp_buffer));
    if (!rdp_buffer) {
        wlr_log(WLR_ERROR, "Failed to allocate wlr_rdp_buffer");
        return NULL;
    }

    rdp_buffer->pool_id = open_pool.poolId;
    rdp_buffer->buffer_id = create_buffer.bufferId;
    rdp_buffer->width = width;
    rdp_buffer->height = height;
    rdp_buffer->fd = -1;
    rdp_buffer->data_access_in_progress = false;

    // Acquire shared memory
    rdp_buffer->rdp_memory = rdp_get_shared_memory(
        open_pool.poolId, &rdp_buffer->rdp_memory_size, &rdp_buffer->fd);
    if (!rdp_buffer->rdp_memory) {
        wlr_log(WLR_ERROR, "Failed to get shared memory for RDP buffer");
        free(rdp_buffer);
        return NULL;
    }

    // Initialize the buffer with our implementation
    wlr_buffer_init(
        &rdp_buffer->buffer,
        &wlr_rdp_buffer_impl,
        width, height
    );

    fprintf(stderr, "RDP buffer created successfully\n");
    return &rdp_buffer->buffer;
}

static void rdp_allocator_destroy(struct wlr_allocator *alloc) {
    struct wlr_rdp_allocator *rdp_alloc =
        wl_container_of(alloc, rdp_alloc, base);

    fprintf(stderr, "Destroying RDP allocator\n");
    free(rdp_alloc);
}

static const struct wlr_allocator_interface rdp_allocator_impl = {
    .create_buffer = rdp_allocator_create_buffer,
    .destroy = rdp_allocator_destroy,
};

struct wlr_allocator *wlr_rdp_allocator_create(
        GfxRedirServerContext *redir_ctx) {
    fprintf(stderr, "Creating RDP allocator\n");

    if (!redir_ctx) {
        wlr_log(WLR_ERROR, "Invalid RDP context");
        return NULL;
    }

    struct wlr_rdp_allocator *alloc =
        calloc(1, sizeof(struct wlr_rdp_allocator));
    if (!alloc) {
        wlr_log(WLR_ERROR, "Failed to allocate RDP allocator");
        return NULL;
    }

    // Initialize allocator
    wlr_allocator_init(&alloc->base, &rdp_allocator_impl,
        WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_SHM);

    // Store RDP context
    alloc->redir_ctx = redir_ctx;

    fprintf(stderr, "RDP allocator created successfully\n");
    return &alloc->base;
}
#include <assert.h>
#include <stdlib.h>
#include <wlr/backend.h>
#include <wlr/util/log.h>
#include <wayland-server-core.h>
#include "rdprail.h"
#include "rdprail_backend.h"
#include "rdprail_output.h"



// Function prototypes
static void handle_display_destroy(struct wl_listener *listener, void *data);
static bool rdp_backend_start(struct wlr_backend *wlr_backend);
static void rdp_backend_destroy(struct wlr_backend *wlr_backend);

// Backend implementation structure
static const struct wlr_backend_impl backend_impl = {
    .start = rdp_backend_start,
    .destroy = rdp_backend_destroy,
};

static bool rdp_backend_start(struct wlr_backend *wlr_backend) {
    struct wlr_rdp_backend *backend =
        wl_container_of(wlr_backend, backend, base);

    if (backend->started) {
        return true;
    }

    // Create an initial output
    if (!wlr_rdp_output_create(backend)) {
        wlr_log(WLR_ERROR, "Failed to create RDP output");
        return false;
    }

    backend->started = true;
    return true;
}

static void rdp_backend_destroy(struct wlr_backend *wlr_backend) {
    struct wlr_rdp_backend *backend =
        wl_container_of(wlr_backend, backend, base);

    if (!backend) {
        return;
    }

    // Remove display destroy listener
    wl_list_remove(&backend->listeners.display_destroy.link);

    // Emit destroy signal before cleanup
    wlr_signal_emit_safe(&backend->events.destroy, backend);

    // Destroy allocator if it exists
    if (backend->allocator) {
        wlr_allocator_destroy(backend->allocator);
    }

    free(backend);
}

static void handle_display_destroy(struct wl_listener *listener, void *data) {
    struct wlr_rdp_backend *backend =
        wl_container_of(listener, backend, listeners.display_destroy);

    // Clean up backend resources
    wlr_backend_destroy(&backend->base);
}

struct wlr_backend *wlr_rdp_backend_create(struct wl_display *display,
        struct wlr_session *session, void *redir_ctx) {
    struct wlr_rdp_backend *backend = calloc(1, sizeof(*backend));
    if (!backend) {
        wlr_log(WLR_ERROR, "Failed to allocate RDP backend");
        return NULL;
    }

    // Initialize backend
    backend->base.impl = &backend_impl;

    backend->display = display;
    backend->session = session;
    backend->redir_ctx = (GfxRedirServerContext *)redir_ctx;

    // Initialize lists
    wl_list_init(&backend->outputs);

    // Initialize signals
    wl_signal_init(&backend->events.destroy);
    wl_signal_init(&backend->events.new_output);
    wl_signal_init(&backend->events.new_input);

    // Setup display destruction listener
    backend->listeners.display_destroy.notify = handle_display_destroy;
    wl_display_add_destroy_listener(display, &backend->listeners.display_destroy);

    return &backend->base;
}

bool wlr_backend_is_rdp(struct wlr_backend *backend) {
    return backend && backend->impl == &backend_impl;
}

struct wlr_rdp_backend *get_rdp_backend_from_backend(struct wlr_backend *backend) {
    if (!wlr_backend_is_rdp(backend)) {
        return NULL;
    }

    return wl_container_of(backend, backend, base);
}// rdprail_output.c
#include <assert.h>
#include <stdlib.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/util/log.h>
#include "backend/rdprail/rdprail_output.h"

// Remove the gamma size function as it's not needed

// rdprail_output.c
static bool output_test(struct wlr_output *wlr_output,
        const struct wlr_output_state *state) {
    return true;  // Always allow test state
}

static bool output_commit(struct wlr_output *wlr_output,
        const struct wlr_output_state *state) {
    struct wlr_rdp_output *output = (struct wlr_rdp_output *)wlr_output;

    if (state->committed & WLR_OUTPUT_STATE_BUFFER && state->buffer) {
        void *data;
        uint32_t format;
        size_t stride;

        if (wlr_buffer_begin_data_ptr_access(state->buffer, 0,
            &data, &format, &stride)) {
            // Get damage region
            pixman_box32_t *rects;
            int n_rects;
            rects = pixman_region32_rectangles(&output->pending.damage, &n_rects);

            for (int i = 0; i < n_rects; i++) {
                RECTANGLE_16 rect = {
                    .left = (uint16_t)rects[i].x1,
                    .top = (uint16_t)rects[i].y1,
                    .right = (uint16_t)rects[i].x2,
                    .bottom = (uint16_t)rects[i].y2
                };

                output->backend->redir_ctx->SurfaceCommand(
                    output->backend->redir_ctx,
                    output,
                    GFX_REDIR_SURFACE_COMMAND_UPDATEREGION,
                    &rect,
                    data);
            }

            wlr_buffer_end_data_ptr_access(state->buffer);
        }
    }
    return true;
}

static void output_destroy(struct wlr_output *wlr_output) {
    struct wlr_rdp_output *output = (struct wlr_rdp_output *)wlr_output;
    wl_list_remove(&output->link);
    free(output);
}

static const struct wlr_output_impl output_impl = {
    .destroy = output_destroy,
    .test = output_test,
    .commit = output_commit,
};
struct wlr_rdp_output *wlr_rdp_output_create(struct wlr_rdp_backend *backend) {
    struct wlr_rdp_output *output = calloc(1, sizeof(*output));
    if (!output) {
        return NULL;
    }

    struct wl_event_loop *event_loop =
        wl_display_get_event_loop(backend->display);

    wlr_output_init(&output->base,
        &backend->base,     // backend
        &output_impl,       // impl
        event_loop,         // event_loop
        NULL);             // state

    output->backend = backend;
    output->width = 1920;
    output->height = 1080;
    output->refresh_rate = 60000;

    pixman_region32_init(&output->pending.damage);

    wl_list_insert(&backend->outputs, &output->link);
    wlr_signal_emit_safe(&backend->events.new_output, output);

    return output;
}light@light:~/wayfire/subprojects/wlroots/backend/rdprail$
Leave a Comment