Compare commits

10 Commits
master ... dev

Author SHA1 Message Date
2cc5bb045b refactor code 2025-07-10 09:57:29 +02:00
9cc2408dc8 add support to protocol: output-power-manager + output-manager 2025-07-09 20:32:33 +02:00
194b7894cd feat: add support to two new wayland protocols
- Idle notify
- Idle inhibit
2025-07-07 16:37:23 +02:00
dd8242abc6 update README.md 2025-07-05 20:40:57 +02:00
f62a21d3b2 fix: compilation errors 2025-07-05 20:37:04 +02:00
0a77ed8d91 switch to meson build + refactor code to remove compile warning 2025-07-05 20:33:49 +02:00
13543e1382 add support for the following protocols:
- wlr_xdg_output_v1
 - wlr_screencopy_v1
 - wlr_export_dmabuf_v1
 - wlr_data_control_v1
 - wlr_single_pixel_buffer_v1

minor feature: add option -v to increase the log level
2025-07-05 19:07:57 +02:00
6be0e9b5c4 fix: init foreign toplevel only when the surface is first mapped 2025-07-03 15:51:38 +02:00
ea98928849 fix: xdg surface handle pending configure + dont update usable area when new layer surface firt create 2025-07-03 14:58:20 +02:00
c1393a1366 fix: update to wlroot 0.18 2025-07-02 22:54:16 +02:00
21 changed files with 1085 additions and 213 deletions

4
.gitignore vendored
View File

@ -139,7 +139,5 @@ m4/lt~obsolete.m4
# can automatically generate from config.status script
# (which is called by configure script))
# Makefile
xdg-shell-protocol.*
diyac
build
.vscode
wlr-layer-shell-unstable-v1-protocol*

View File

@ -1,53 +0,0 @@
WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols)
WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner)
LIBS=\
$(shell pkg-config --cflags --libs wlroots) \
$(shell pkg-config --cflags --libs wayland-server) \
$(shell pkg-config --cflags --libs xkbcommon)
OBJS=\
diyac.c \
cursor.c \
output.c \
seat.c \
node.c \
view.c \
foreign.c \
xdg.c \
xdg-shell-protocol.c \
layer.c \
session.c \
wlr-layer-shell-unstable-v1-protocol.c
# wayland-scanner is a tool which generates C headers and rigging for Wayland
# protocols, which are specified in XML. wlroots requires you to rig these up
# to your build system yourself and provide them in the include path.
xdg-shell-protocol.h:
$(WAYLAND_SCANNER) server-header \
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
xdg-shell-protocol.c: xdg-shell-protocol.h
$(WAYLAND_SCANNER) private-code \
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
wlr-layer-shell-unstable-v1-protocol.c: wlr-layer-shell-unstable-v1-protocol.h
$(WAYLAND_SCANNER) private-code \
protocol/wlr-layer-shell-unstable-v1.xml $@
wlr-layer-shell-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocol/wlr-layer-shell-unstable-v1.xml $@
diyac: $(OBJS)
echo "Object is $(OBJS)"
$(CC) $(CFLAGS) \
-g -Werror -I. \
-DWLR_USE_UNSTABLE \
-o $@ $(OBJS) \
$(LIBS)
clean:
rm -f diyac xdg-shell-protocol.* wlr-layer-shell-unstable-v1-protocol.*
.DEFAULT_GOAL=diyac
.PHONY: clean

View File

@ -1,2 +1,3 @@
# diyac
Simple Wayland compositor used mainly by diya-session manager and diya shell

View File

@ -5,6 +5,7 @@
#include "view.h"
#include "seat.h"
#include "node.h"
#include "idle.h"
void diyac_cursor_focus(struct diyac_server *server)
{
@ -13,7 +14,7 @@ void diyac_cursor_focus(struct diyac_server *server)
struct diyac_node_descriptor *desc = diyac_node_at(server,
server->seat.cursor->x, server->seat.cursor->y, &surface, &sx, &sy);
struct diyac_layer_surface *layer;
struct diyac_view *root = NULL;
// struct diyac_view *root = NULL;
if (!desc)
{
return;
@ -55,6 +56,7 @@ void diyac_reset_cursor_mode(struct diyac_server *server)
static void process_cursor_move(struct diyac_server *server, uint32_t time)
{
(void) time;
struct diyac_view *toplevel = server->grabbed_view;
/* Move the grabbed toplevel to the new position. */
if(!toplevel->output)
@ -84,6 +86,7 @@ static void process_cursor_move(struct diyac_server *server, uint32_t time)
static void process_cursor_resize(struct diyac_server *server, uint32_t time)
{
(void) time;
/*
* Resizing the grabbed toplevel can be a little bit complicated, because we
* could be resizing from any corner or edge. This not only resizes the
@ -214,6 +217,7 @@ static void server_cursor_motion(struct wl_listener *listener, void *data)
struct diyac_seat *seat =
wl_container_of(listener, seat, cursor_motion);
struct wlr_pointer_motion_event *event = data;
diya_idle_manager_notify_activity(seat->wlr_seat);
/* The cursor doesn't move unless we tell it to. The cursor automatically
* handles constraining the motion to the output layout, as well as any
* special configuration applied for the specific input device which
@ -235,6 +239,7 @@ static void server_cursor_motion_absolute(
* emits these events. */
struct diyac_seat *seat =
wl_container_of(listener, seat, cursor_motion_absolute);
diya_idle_manager_notify_activity(seat->wlr_seat);
struct wlr_pointer_motion_absolute_event *event = data;
wlr_cursor_warp_absolute(seat->cursor, &event->pointer->base, event->x,
event->y);
@ -248,11 +253,12 @@ static void server_cursor_button(struct wl_listener *listener, void *data)
struct diyac_seat *seat =
wl_container_of(listener, seat, cursor_button);
struct wlr_pointer_button_event *event = data;
diya_idle_manager_notify_activity(seat->wlr_seat);
/* Notify the client with pointer focus that a button press has occurred */
wlr_seat_pointer_notify_button(seat->wlr_seat,
event->time_msec, event->button, event->state);
if (event->state == WLR_BUTTON_RELEASED)
if (event->state == WL_POINTER_BUTTON_STATE_RELEASED)
{
/* If you released any buttons, we exit interactive move/resize mode. */
diyac_reset_cursor_mode(seat->server);
@ -269,15 +275,17 @@ static void server_cursor_axis(struct wl_listener *listener, void *data)
* for example when you move the scroll wheel. */
struct diyac_seat *seat =
wl_container_of(listener, seat, cursor_axis);
diya_idle_manager_notify_activity(seat->wlr_seat);
struct wlr_pointer_axis_event *event = data;
/* Notify the client with pointer focus of the axis event. */
wlr_seat_pointer_notify_axis(seat->wlr_seat,
event->time_msec, event->orientation, event->delta,
event->delta_discrete, event->source);
event->delta_discrete, event->source, event->delta);
}
static void server_cursor_frame(struct wl_listener *listener, void *data)
{
(void) data;
/* This event is forwarded by the cursor when a pointer emits an frame
* event. Frame events are sent after regular pointer events to group
* multiple events together. For instance, two axis events may happen at the

55
diyac.c
View File

@ -8,20 +8,31 @@
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include "output.h"
#include "xdg.h"
#include "cursor.h"
#include "seat.h"
#include "layer.h"
#include "session.h"
#include "idle.h"
#define PROC_MON_TO 100
void help()
{
printf("diyac - a simple Wayland compositor\n");
printf("Usage: diyac [-x] [startup command]\n");
printf("Options:\n");
printf(" -x exit with the session\n");
printf(" -v increase log level\n");
printf(" -h show this help message\n");
#ifdef __COMPOSITOR_VERSION__
printf("Version: %s\n", __COMPOSITOR_VERSION__);
#endif
}
/**
@ -54,15 +65,20 @@ static int session_monitor(void *data)
int main(int argc, char *argv[])
{
wlr_log_init(WLR_INFO, NULL);
char *startup_cmd = NULL;
int exit_with_session = 0;
int log_level = WLR_ERROR;
int c;
while ((c = getopt(argc, argv, "xh")) != -1)
while ((c = getopt(argc, argv, "xvh")) != -1)
{
switch (c)
{
case 'v':
if (log_level < WLR_DEBUG)
{
log_level++;
}
break;
case 'x':
exit_with_session = 1;
break;
@ -86,6 +102,7 @@ int main(int argc, char *argv[])
// the last argument is the startup command
startup_cmd = argv[optind];
}
wlr_log_init(log_level, NULL);
struct diyac_server server = {0};
/* The Wayland display is managed by libwayland. It handles accepting
* clients from the Unix socket, manging Wayland globals, and so on. */
@ -95,7 +112,7 @@ int main(int argc, char *argv[])
* output hardware. The autocreate option will choose the most suitable
* backend based on the current environment, such as opening an X11 window
* if an X11 server is running. */
server.backend = wlr_backend_autocreate(server.wl_display, NULL);
server.backend = wlr_backend_autocreate(server.wl_event_loop, NULL);
if (server.backend == NULL)
{
wlr_log(WLR_ERROR, "failed to create wlr_backend");
@ -149,15 +166,6 @@ int main(int argc, char *argv[])
*/
wlr_primary_selection_v1_device_manager_create(server.wl_display);
/* Creates an output layout, which a wlroots utility for working with an
* arrangement of screens in a physical layout. */
server.output_layout = wlr_output_layout_create();
/* Configure a listener to be notified when new outputs are available on the
* backend. */
wl_list_init(&server.outputs);
server.new_output.notify = diyac_server_new_output;
wl_signal_add(&server.backend->events.new_output, &server.new_output);
/* Create a scene graph. This is a wlroots abstraction that handles all
* rendering and damage tracking. All the compositor author needs to do
@ -166,8 +174,14 @@ int main(int argc, char *argv[])
* necessary.
*/
server.scene = wlr_scene_create();
server.scene_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout);
diyac_output_init(&server);
wlr_export_dmabuf_manager_v1_create(server.wl_display);
wlr_data_control_manager_v1_create(server.wl_display);
wlr_screencopy_manager_v1_create(server.wl_display);
wlr_single_pixel_buffer_manager_v1_create(server.wl_display);
wlr_fractional_scale_manager_v1_create(server.wl_display, 1);
diya_init_idle_manager(server.wl_display);
/* Set up xdg-shell version 6 The xdg-shell is a Wayland protocol which is
* used for application windows. For more detail on shells, refer to
* https://drewdevault.com/2018/07/29/Wayland-shells.html.
@ -180,9 +194,9 @@ int main(int argc, char *argv[])
server.xdg_popup_tree = wlr_scene_tree_create(&server.scene->tree);
server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 6);
server.new_xdg_surface.notify = diyac_new_xdg_surface;
wl_signal_add(&server.xdg_shell->events.new_surface,
&server.new_xdg_surface);
server.new_xdg_toplevel.notify = diyac_new_xdg_toplevel;
wl_signal_add(&server.xdg_shell->events.new_toplevel,
&server.new_xdg_toplevel);
server.layer_shell = wlr_layer_shell_v1_create(server.wl_display, 4);
server.new_layer_surface.notify = diyac_new_layer_surface;
@ -263,6 +277,13 @@ int main(int argc, char *argv[])
{
wl_event_source_remove(server.proc_mon);
}
wl_list_remove(&server.new_xdg_toplevel.link);
wl_list_remove(&server.new_layer_surface.link);
wl_list_remove(&server.new_output.link);
wl_list_remove(&server.output_power_manager_set_mode.link);
wl_list_remove(&server.output_manager_apply.link);
wl_list_remove(&server.output_manager_test.link);
wl_list_remove(&server.output_layout_change.link);
wl_display_destroy_clients(server.wl_display);
// wlr_scene_node_destroy(&server.scene->tree.node);
wlr_xcursor_manager_destroy(server.seat.cursor_mgr);

17
diyac.h
View File

@ -18,6 +18,8 @@
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
#include <wlr/types/wlr_session_lock_v1.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_output_management_v1.h>
#define LAYER_TREE_SZ 4
@ -131,7 +133,9 @@ struct diyac_server
struct wlr_xdg_shell *xdg_shell;
struct wlr_layer_shell_v1 *layer_shell;
struct wl_listener new_xdg_surface;
struct wl_listener new_xdg_toplevel;
struct wl_listener new_layer_surface;
struct wl_list views;
struct diyac_view *active_view;
@ -154,6 +158,14 @@ struct diyac_server
struct wl_list outputs;
struct wl_listener new_output;
struct wlr_output_power_manager_v1 *output_power_manager;
struct wl_listener output_power_manager_set_mode;
struct wlr_output_manager_v1 *output_manager;
struct wl_listener output_manager_test;
struct wl_listener output_manager_apply;
struct wl_listener output_layout_change;
struct diyac_session_lock *lock;
struct wl_event_source *proc_mon;
@ -195,6 +207,8 @@ struct diyac_output
struct wl_listener request_state;
struct wl_listener destroy;
struct wlr_box usable_area;
struct wlr_output_state pending_state;
struct wlr_scene_output* scene_output;
// layer output
struct diyac_output_scenes scenes;
@ -244,7 +258,6 @@ struct diyac_view
struct wl_listener request_minimize;
struct wl_listener request_maximize;
struct wl_listener request_fullscreen;
struct wl_listener set_app_id;
struct wl_listener new_popup;
struct wl_listener set_title;

View File

@ -29,6 +29,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
static void handle_request_activate(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_view *view = wl_container_of(listener, view, toplevel.activate);
// struct wlr_foreign_toplevel_handle_v1_activated_event *event = data;
/* In a multi-seat world we would select seat based on event->seat here. */
@ -39,6 +40,7 @@ static void handle_request_activate(struct wl_listener *listener, void *data)
static void
handle_request_close(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_view *view = wl_container_of(listener, view, toplevel.close);
wlr_xdg_toplevel_send_close(view->xdg_toplevel);
}
@ -46,6 +48,7 @@ handle_request_close(struct wl_listener *listener, void *data)
static void
handle_destroy(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_view *view = wl_container_of(listener, view, toplevel.destroy);
struct foreign_toplevel *toplevel = &view->toplevel;
wl_list_remove(&toplevel->maximize.link);

77
idle.c Normal file
View File

@ -0,0 +1,77 @@
#include "diyac.h"
#include "idle.h"
#include <stdlib.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
struct diya_idle_inhibitor
{
struct wlr_idle_inhibitor_v1 *wlr_inhibitor;
struct wl_listener on_inhibitor_destroy;
};
struct diya_idle_mngr
{
struct wlr_idle_inhibit_manager_v1 *wlr_inhibitor_mngr;
struct wl_listener on_new_inhibitor;
struct wlr_idle_notifier_v1 *wlr_idle_notifier;
// struct wl_listener on_display_destroy;
};
static struct diya_idle_mngr g_idle_mngr;
static void handle_idle_inhibitor_destroy(struct wl_listener *listener, void *data)
{
(void) data;
struct diya_idle_inhibitor *inhibitor = wl_container_of(listener, inhibitor, on_inhibitor_destroy);
if (g_idle_mngr.wlr_idle_notifier) {
bool still_inhibited = wl_list_length(&g_idle_mngr.wlr_inhibitor_mngr->inhibitors) > 1;
wlr_idle_notifier_v1_set_inhibited(g_idle_mngr.wlr_idle_notifier, still_inhibited);
}
wl_list_remove(&inhibitor->on_inhibitor_destroy.link);
free(inhibitor);
}
/*
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
(void) data;
(void) listener;
wl_list_remove(&g_idle_mngr.on_display_destroy.link);
g_idle_mngr.wlr_idle_notifier = NULL;
}
*/
static void handle_idle_inhibitor_new(struct wl_listener *listener, void *data)
{
(void) listener;
struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data;
struct diya_idle_inhibitor *inhibitor = malloc(sizeof(struct diya_idle_inhibitor));
inhibitor->wlr_inhibitor = wlr_inhibitor;
inhibitor->on_inhibitor_destroy.notify = handle_idle_inhibitor_destroy;
wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->on_inhibitor_destroy);
wlr_idle_notifier_v1_set_inhibited(g_idle_mngr.wlr_idle_notifier, true);
}
void diya_init_idle_manager(struct wl_display *display)
{
g_idle_mngr.wlr_idle_notifier = wlr_idle_notifier_v1_create(display);
g_idle_mngr.wlr_inhibitor_mngr = wlr_idle_inhibit_v1_create(display);
g_idle_mngr.on_new_inhibitor.notify = handle_idle_inhibitor_new;
wl_signal_add(&g_idle_mngr.wlr_inhibitor_mngr->events.new_inhibitor,&g_idle_mngr.on_new_inhibitor);
//g_idle_mngr.on_display_destroy.notify = handle_display_destroy;
//wl_display_add_destroy_listener(display, &g_idle_mngr.on_display_destroy);
}
void diya_idle_manager_notify_activity(struct wlr_seat *seat)
{
if (g_idle_mngr.wlr_idle_notifier) {
wlr_idle_notifier_v1_notify_activity(g_idle_mngr.wlr_idle_notifier, seat);
}
}

9
idle.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef DIYAC_IDLE_H
#define DIYAC_IDLE_H
struct wl_display;
struct wlr_seat;
void diya_init_idle_manager(struct wl_display *display);
void diya_idle_manager_notify_activity(struct wlr_seat *seat);
#endif

View File

@ -53,6 +53,7 @@ static void process_keyboard_interactivity(struct diyac_layer_surface *layer)
}
static void layer_surface_commit(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_layer_surface *layer =
wl_container_of(listener, layer, surface_commit);
struct wlr_layer_surface_v1 *layer_surface =
@ -94,6 +95,7 @@ static void layer_surface_commit(struct wl_listener *listener, void *data)
}
static void layer_surface_unmap(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_layer_surface *layer = wl_container_of(listener, layer, unmap);
struct wlr_layer_surface_v1 *layer_surface =
layer->scene_layer_surface->layer_surface;
@ -109,6 +111,7 @@ static void layer_surface_unmap(struct wl_listener *listener, void *data)
static void layer_surface_map(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_layer_surface *layer = wl_container_of(listener, layer, map);
struct wlr_output *wlr_output =
layer->scene_layer_surface->layer_surface->output;
@ -127,6 +130,7 @@ static void layer_surface_map(struct wl_listener *listener, void *data)
}
static void layer_surface_node_destroy(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_layer_surface *layer =
wl_container_of(listener, layer, node_destroy);
@ -142,6 +146,7 @@ static void layer_surface_node_destroy(struct wl_listener *listener, void *data)
}
static void layer_surface_output_destroy(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_layer_surface *layer =
wl_container_of(listener, layer, output_destroy);
layer->scene_layer_surface->layer_surface->output = NULL;
@ -150,6 +155,7 @@ static void layer_surface_output_destroy(struct wl_listener *listener, void *dat
static void
popup_handle_destroy(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_popup *popup = wl_container_of(listener, popup, destroy);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->new_popup.link);
@ -165,6 +171,7 @@ popup_handle_destroy(struct wl_listener *listener, void *data)
static void popup_handle_commit(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_popup *popup =
wl_container_of(listener, popup, commit);
struct wlr_box popup_box ;
@ -352,7 +359,7 @@ void diyac_new_layer_surface(struct wl_listener *listener, void *data)
*/
struct wlr_layer_surface_v1_state old_state = layer_surface->current;
layer_surface->current = layer_surface->pending;
diyac_output_update_usable_area(output);
// diyac_output_update_usable_area(output);
layer_surface->current = old_state;
}

75
meson.build Normal file
View File

@ -0,0 +1,75 @@
project('diyac',
['c'],
version: '0.1.2',
license: 'MIT',
meson_version: '>=0.58.0',
default_options: ['c_std=gnu11', 'warning_level=3'])
lib_so_version = '0'
add_project_arguments(
[
'-Wno-pedantic',
'-Werror=implicit-function-declaration',
'-Werror=return-type',
'-DWLR_USE_UNSTABLE',
'-D__COMPOSITOR_VERSION__="@0@"'.format(meson.project_version())
],
language: 'c')
wlroots = dependency('wlroots')
wayland_server = dependency('wayland-server', version: '>=1.10.0')
xkbcommon = dependency('xkbcommon')
# wayland_scanner is required, but we can find it without pkg-config
wayland_scanner = find_program('wayland-scanner')
wayland_protocols = dependency('wayland-protocols', version: '>=1.16')
wl_protocol_dir = wayland_protocols.get_variable('pkgdatadir')
wayland_targets=[]
wl_protocols = [
wl_protocol_dir / 'stable/xdg-shell/xdg-shell',
'protocol/wlr-layer-shell-unstable-v1',
'protocol/wlr-output-power-management-unstable-v1',
]
foreach proto : wl_protocols
xml = ''.join([proto,'.xml'])
header = ''.join([proto.split('/').get(-1),'-protocol.h'])
cfile = ''.join([proto.split('/').get(-1),'-protocol.c'])
wayland_targets += custom_target(header,output:header,input:xml,
command: [ wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@' ] )
wayland_targets += custom_target(cfile,output:cfile,input:xml,
command: [ wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@' ] )
endforeach
incdir = include_directories([
])
src = [
'diyac.c',
'cursor.c',
'output.c',
'seat.c',
'node.c',
'view.c',
'foreign.c',
'xdg.c',
'layer.c',
'session.c',
'idle.c',
wayland_targets
]
buil_dep = [wlroots, wayland_server, xkbcommon]
executable(
'diyac',
src,
dependencies: buil_dep,
include_directories : incdir)

1
node.c
View File

@ -5,6 +5,7 @@
static void destroy_notify(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_node_descriptor *node_descriptor =
wl_container_of(listener, node_descriptor, destroy);
wl_list_remove(&node_descriptor->destroy.link);

650
output.c
View File

@ -3,14 +3,66 @@
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <wlr/backend/wayland.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h>
#include <assert.h>
#include "output.h"
#include "layer.h"
#include "node.h"
#include "view.h"
#include "session.h"
/*
* While an output layout change is in process, this counter is
* non-zero and causes change-events from the wlr_output_layout
* to be ignored (to prevent, for example, moving views in a
* transitory layout state). Once the counter reaches zero,
* do_output_layout_change() must be called explicitly.
*/
static int g_pending_output_layout_change = 0;
static void output_state_init(struct diyac_output *output)
{
wlr_output_state_init(&output->pending_state);
/*
* As there is no direct way to convert an existing output
* configuration to an output_state we first convert it
* to a temporary output-management config and then apply
* it to an empty wlr_output_state.
*/
struct wlr_output_configuration_v1 *backup_config =
wlr_output_configuration_v1_create();
struct wlr_output_configuration_head_v1 *backup_head =
wlr_output_configuration_head_v1_create(
backup_config, output->wlr_output);
wlr_output_head_v1_state_apply(&backup_head->state, &output->pending_state);
wlr_output_configuration_v1_destroy(backup_config);
}
static bool output_state_commit(struct diyac_output *output)
{
bool committed =
wlr_output_commit_state(output->wlr_output, &output->pending_state);
if (committed)
{
wlr_output_state_finish(&output->pending_state);
wlr_output_state_init(&output->pending_state);
}
else
{
wlr_log(WLR_ERROR, "Failed to commit frame");
}
return committed;
}
static void output_frame(struct wl_listener *listener, void *data)
{
(void)data;
/* This function is called every time an output is ready to display a frame,
* generally at the output's refresh rate (e.g. 60Hz). */
struct diyac_output *output = wl_container_of(listener, output, frame);
@ -27,18 +79,124 @@ static void output_frame(struct wl_listener *listener, void *data)
wlr_scene_output_send_frame_done(scene_output, &now);
}
static struct wlr_output_configuration_v1 *create_output_config(struct diyac_server *server)
{
struct wlr_output_configuration_v1 *config =
wlr_output_configuration_v1_create();
if (!config)
{
wlr_log(WLR_ERROR, "wlr_output_configuration_v1_create()");
return NULL;
}
struct diyac_output *output;
wl_list_for_each(output, &server->outputs, link)
{
struct wlr_output_configuration_head_v1 *head =
wlr_output_configuration_head_v1_create(config,
output->wlr_output);
if (!head)
{
wlr_log(WLR_ERROR,
"wlr_output_configuration_head_v1_create()");
wlr_output_configuration_v1_destroy(config);
return NULL;
}
struct wlr_box box;
wlr_output_layout_get_box(server->output_layout,
output->wlr_output, &box);
if (!wlr_box_empty(&box))
{
head->state.x = box.x;
head->state.y = box.y;
}
else
{
wlr_log(WLR_ERROR, "failed to get output layout box");
}
}
return config;
}
/* returns true if usable area changed */
static bool update_usable_area(struct diyac_output *output)
{
struct wlr_box old = output->usable_area;
diyac_layers_arrange(output);
return !wlr_box_equal(&old, &output->usable_area);
}
static void do_output_layout_change(struct diyac_server *server)
{
if (!g_pending_output_layout_change)
{
struct wlr_output_configuration_v1 *config =
create_output_config(server);
if (config)
{
wlr_output_manager_v1_set_configuration(
server->output_manager, config);
}
else
{
wlr_log(WLR_ERROR,
"wlr_output_manager_v1_set_configuration()");
}
struct diyac_output *output;
wl_list_for_each(output, &server->outputs, link)
{
diyac_output_update_usable_area(output);
}
wlr_cursor_move(server->seat.cursor, NULL, 0, 0);
}
}
static void output_request_state(struct wl_listener *listener, void *data)
{
/* This function is called when the backend requests a new state for
* the output. For example, Wayland and X11 backends request a new mode
* when the output window is resized. */
/* This ensures nested backends can be resized */
struct diyac_output *output = wl_container_of(listener, output, request_state);
const struct wlr_output_event_request_state *event = data;
wlr_output_commit_state(output->wlr_output, event->state);
/*
* If wlroots ever requests other state changes here we could
* restore more of ddc9047a67cd53b2948f71fde1bbe9118000dd3f.
*/
if (event->state->committed == WLR_OUTPUT_STATE_MODE)
{
/* Only the mode has changed */
switch (event->state->mode_type)
{
case WLR_OUTPUT_STATE_MODE_FIXED:
wlr_output_state_set_mode(&output->pending_state,
event->state->mode);
break;
case WLR_OUTPUT_STATE_MODE_CUSTOM:
wlr_output_state_set_custom_mode(&output->pending_state,
event->state->custom_mode.width,
event->state->custom_mode.height,
event->state->custom_mode.refresh);
break;
}
wlr_output_schedule_frame(output->wlr_output);
return;
}
/*
* Fallback path for everything that we didn't handle above.
* The commit will cause a black frame injection so this
* path causes flickering during resize of nested outputs.
*/
if (!wlr_output_commit_state(output->wlr_output, event->state))
{
wlr_log(WLR_ERROR, "Backend requested a new state that could not be applied");
}
}
static void output_destroy(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_output *output = wl_container_of(listener, output, destroy);
if (output->lock_handle)
{
@ -51,7 +209,6 @@ static void output_destroy(struct wl_listener *listener, void *data)
wlr_scene_node_destroy(&output->scenes.overlay->node);
wlr_scene_node_destroy(&output->scenes.session->node);
wl_list_remove(&output->frame.link);
wl_list_remove(&output->request_state.link);
wl_list_remove(&output->destroy.link);
@ -59,8 +216,10 @@ static void output_destroy(struct wl_listener *listener, void *data)
struct diyac_server *server = output->server;
struct diyac_view *view;
wl_list_for_each(view, &server->views, link) {
if (view->output == output) {
wl_list_for_each(view, &server->views, link)
{
if (view->output == output)
{
/**
* TODO: testing this case
*/
@ -72,10 +231,119 @@ static void output_destroy(struct wl_listener *listener, void *data)
}
}
}
wlr_output_state_finish(&output->pending_state);
free(output);
}
void diyac_server_new_output(struct wl_listener *listener, void *data)
static bool output_test_auto(struct wlr_output *wlr_output, struct wlr_output_state *state,
bool is_client_request)
{
wlr_log(WLR_DEBUG, "testing modes for %s", wlr_output->name);
/*
* When a client requests a specific mode, test only that mode. Here
* we interpret a custom_mode of all zeroes as "none/any"; this is
* seen e.g. with kanshi configs containing no "mode" field. In
* theory, (state->committed & WLR_OUTPUT_STATE_MODE) should be zero
* in this case, but this is not seen in practice.
*
* If the wlr_output_state did not come from a client request, then
* ignore the mode/custom_mode fields which are not meaningful.
*/
if (is_client_request && (state->mode || state->custom_mode.width || state->custom_mode.height || state->custom_mode.refresh))
{
if (state->mode)
{
wlr_log(WLR_DEBUG, "testing requested mode %dx%d@%d",
state->mode->width, state->mode->height,
state->mode->refresh);
}
else
{
wlr_log(WLR_DEBUG, "testing custom mode %dx%d@%d",
state->custom_mode.width,
state->custom_mode.height,
state->custom_mode.refresh);
}
return wlr_output_test_state(wlr_output, state);
}
struct wlr_output_mode *preferred_mode =
wlr_output_preferred_mode(wlr_output);
if (preferred_mode)
{
wlr_log(WLR_DEBUG, "testing preferred mode %dx%d@%d",
preferred_mode->width, preferred_mode->height,
preferred_mode->refresh);
wlr_output_state_set_mode(state, preferred_mode);
if (wlr_output_test_state(wlr_output, state))
{
return true;
}
}
/*
* Sometimes the preferred mode is not available due to hardware
* constraints (e.g. GPU or cable bandwidth limitations). In these
* cases it's better to fallback to lower modes than to end up with
* a black screen. See sway@4cdc4ac6
*/
struct wlr_output_mode *mode;
wl_list_for_each(mode, &wlr_output->modes, link)
{
if (mode == preferred_mode)
{
continue;
}
wlr_log(WLR_DEBUG, "testing fallback mode %dx%d@%d",
mode->width, mode->height, mode->refresh);
wlr_output_state_set_mode(state, mode);
if (wlr_output_test_state(wlr_output, state))
{
return true;
}
}
/* Reset mode if none worked (we may still try to commit) */
wlr_output_state_set_mode(state, NULL);
return false;
}
static void add_output_to_layout(struct diyac_server *server, struct diyac_output *output)
{
struct wlr_output *wlr_output = output->wlr_output;
struct wlr_output_layout_output *layout_output =
wlr_output_layout_add_auto(server->output_layout, wlr_output);
if (!layout_output)
{
wlr_log(WLR_ERROR, "unable to add output to layout");
return;
}
if (!output->scene_output)
{
output->scene_output =
wlr_scene_output_create(server->scene, wlr_output);
if (!output->scene_output)
{
wlr_log(WLR_ERROR, "unable to create scene output");
return;
}
/*
* Note: wlr_scene_output_layout_add_output() is not
* safe to call twice, so we call it only when initially
* creating the scene_output.
*/
wlr_scene_output_layout_add_output(server->scene_layout,
layout_output, output->scene_output);
}
/* Create lock surface if needed */
if (server->lock)
{
diyac_session_lock_output(output);
}
}
static void diyac_server_new_output(struct wl_listener *listener, void *data)
{
/* This event is raised by the backend when a new output (aka a display or
* monitor) becomes available. */
@ -87,26 +355,6 @@ void diyac_server_new_output(struct wl_listener *listener, void *data)
* and our renderer. Must be done once, before commiting the output */
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
/* The output may be disabled, switch it on. */
struct wlr_output_state state;
wlr_output_state_init(&state);
wlr_output_state_set_enabled(&state, true);
/* Some backends don't have modes. DRM+KMS does, and we need to set a mode
* before we can use the output. The mode is a tuple of (width, height,
* refresh rate), and each monitor supports only a specific set of modes. We
* just pick the monitor's preferred mode, a more sophisticated compositor
* would let the user configure it. */
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
if (mode != NULL)
{
wlr_output_state_set_mode(&state, mode);
}
/* Atomically applies the new output state. */
wlr_output_commit_state(wlr_output, &state);
wlr_output_state_finish(&state);
/* Allocates and configures our state for this output */
struct diyac_output *output = calloc(1, sizeof(*output));
output->wlr_output = wlr_output;
@ -116,6 +364,9 @@ void diyac_server_new_output(struct wl_listener *listener, void *data)
output->usable_area.y = 0;
output->usable_area.width = wlr_output->width;
output->usable_area.height = wlr_output->height;
output_state_init(output);
/* Sets up a listener for the frame event. */
output->frame.notify = output_frame;
wl_signal_add(&wlr_output->events.frame, &output->frame);
@ -154,10 +405,6 @@ void diyac_server_new_output(struct wl_listener *listener, void *data)
output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY] = output->scenes.overlay;
output->lock_handle = NULL;
if(server->lock)
{
diyac_session_lock_output(output);
}
/*
* Set the z-positions to achieve the following order (from top to
* bottom):
@ -176,28 +423,25 @@ void diyac_server_new_output(struct wl_listener *listener, void *data)
wlr_scene_node_raise_to_top(&output->scenes.popup->node);
wlr_scene_node_raise_to_top(&output->scenes.session->node);
/* Adds this to the output layout. The add_auto function arranges outputs
* from left-to-right in the order they appear. A more sophisticated
* compositor would let the user configure the arrangement of outputs in the
* layout.
*
* The output layout utility automatically adds a wl_output global to the
* display, which Wayland clients can see to find out information about the
* output (such as DPI, scale factor, manufacturer, etc).
*/
struct wlr_output_layout_output *l_output = wlr_output_layout_add_auto(server->output_layout,
wlr_output);
struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output);
wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output);
}
wlr_output_state_set_enabled(&output->pending_state, true);
/* returns true if usable area changed */
static bool update_usable_area(struct diyac_output *output)
if (!output_test_auto(wlr_output, &output->pending_state,
/* is_client_request */ false))
{
struct wlr_box old = output->usable_area;
diyac_layers_arrange(output);
return !wlr_box_equal(&old, &output->usable_area);
wlr_log(WLR_INFO, "mode test failed for output %s",
wlr_output->name);
/*
* Continue anyway. For some reason, the test fails when
* running nested, yet the following commit succeeds.
*/
}
output_state_commit(output);
wlr_output_effective_resolution(wlr_output,
&output->usable_area.width, &output->usable_area.height);
g_pending_output_layout_change++;
add_output_to_layout(server, output);
g_pending_output_layout_change--;
do_output_layout_change(server);
}
void diyac_output_update_usable_area(struct diyac_output *output)
@ -254,3 +498,305 @@ void diyac_output_full_area(struct diyac_output *output,struct wlr_box* area)
box.y -= oy;
memcpy(area, &box, sizeof(box));
}
static void diyac_output_power_manager_set_mode(struct wl_listener *listener, void *data)
{
struct diyac_server *server = wl_container_of(listener, server,
output_power_manager_set_mode);
struct wlr_output_power_v1_set_mode_event *event = data;
struct diyac_output *output = event->output->data;
assert(output);
switch (event->mode)
{
case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
if (!event->output->enabled)
{
return;
}
wlr_output_state_set_enabled(&output->pending_state, false);
output_state_commit(output);
break;
case ZWLR_OUTPUT_POWER_V1_MODE_ON:
if (event->output->enabled)
{
return;
}
wlr_output_state_set_enabled(&output->pending_state, true);
output_state_commit(output);
/*
* Re-set the cursor image so that the cursor
* isn't invisible on the newly enabled output.
*/
// cursor_update_image(&server->seat);
break;
}
}
static void handle_output_layout_change(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_server *server =
wl_container_of(listener, server, output_layout_change);
do_output_layout_change(server);
}
static bool verify_output_config_v1(const struct wlr_output_configuration_v1 *config)
{
const char *err_msg = NULL;
struct wlr_output_configuration_head_v1 *head;
wl_list_for_each(head, &config->heads, link)
{
if (!head->state.enabled)
{
continue;
}
/* Handle custom modes */
if (!head->state.mode)
{
int32_t refresh = head->state.custom_mode.refresh;
if (wlr_output_is_wl(head->state.output) && refresh != 0)
{
/* Wayland backend does not support refresh rates */
err_msg = "Wayland backend refresh rates unsupported";
goto custom_mode_failed;
}
}
if (wlr_output_is_wl(head->state.output) && !head->state.adaptive_sync_enabled)
{
err_msg = "Wayland backend requires adaptive sync";
goto custom_mode_failed;
}
/*
* Ensure the new output state can be applied on
* its own and inform the client when it can not.
*
* Applying the changes may still fail later when
* getting mixed with wlr_output->pending which
* may contain further unrelated changes.
*/
struct wlr_output_state output_state;
wlr_output_state_init(&output_state);
wlr_output_head_v1_state_apply(&head->state, &output_state);
if (!output_test_auto(head->state.output, &output_state,
/* is_client_request */ true))
{
wlr_output_state_finish(&output_state);
return false;
}
wlr_output_state_finish(&output_state);
}
return true;
custom_mode_failed:
assert(err_msg);
wlr_log(WLR_INFO, "%s (%s: %dx%d@%d)",
err_msg,
head->state.output->name,
head->state.custom_mode.width,
head->state.custom_mode.height,
head->state.custom_mode.refresh);
return false;
}
void diyac_output_enable_adaptive_sync(struct diyac_output *output, bool enabled)
{
wlr_output_state_set_adaptive_sync_enabled(&output->pending_state, enabled);
if (!wlr_output_test_state(output->wlr_output, &output->pending_state))
{
wlr_output_state_set_adaptive_sync_enabled(&output->pending_state, false);
wlr_log(WLR_DEBUG,
"failed to enable adaptive sync for output %s",
output->wlr_output->name);
}
else
{
wlr_log(WLR_INFO, "adaptive sync %sabled for output %s",
enabled ? "en" : "dis", output->wlr_output->name);
}
}
static bool output_config_apply(struct diyac_server *server, struct wlr_output_configuration_v1 *config)
{
bool success = true;
g_pending_output_layout_change++;
struct wlr_output_configuration_head_v1 *head;
wl_list_for_each(head, &config->heads, link)
{
struct wlr_output *o = head->state.output;
struct diyac_output *output = o->data;
struct wlr_output_state *os = &output->pending_state;
bool output_enabled = head->state.enabled;
wlr_output_state_set_enabled(os, output_enabled);
if (output_enabled)
{
/* Output specific actions only */
if (head->state.mode)
{
wlr_output_state_set_mode(os, head->state.mode);
}
else
{
wlr_output_state_set_custom_mode(os,
head->state.custom_mode.width,
head->state.custom_mode.height,
head->state.custom_mode.refresh);
}
/*
* Try to ensure a valid mode. Ignore failures
* here and just check the commit below.
*/
(void)output_test_auto(o, os,
/* is_client_request */ true);
wlr_output_state_set_scale(os, head->state.scale);
wlr_output_state_set_transform(os, head->state.transform);
diyac_output_enable_adaptive_sync(output,
head->state.adaptive_sync_enabled);
}
if (!output_state_commit(output))
{
/*
* FIXME: This is only part of the story, we should revert
* all previously committed outputs as well here.
*
* See https://github.com/labwc/labwc/pull/1528
*/
wlr_log(WLR_INFO, "Output config commit failed: %s", o->name);
success = false;
break;
}
/*
* Add or remove output from layout only if the commit went
* through. Note that at startup, the output may have already
* been enabled but not yet been added to the layout.
*/
bool was_in_layout =
!!wlr_output_layout_get(server->output_layout, o);
if (output_enabled)
{
if (!was_in_layout)
{
add_output_to_layout(server, output);
}
struct wlr_box pos = {0};
wlr_output_layout_get_box(server->output_layout, o, &pos);
if (pos.x != head->state.x || pos.y != head->state.y)
{
/*
* This overrides the automatic layout
*
* wlr_output_layout_add() in fact means _move()
*/
wlr_output_layout_add(server->output_layout, o,
head->state.x, head->state.y);
}
}
else if (was_in_layout)
{
/*
* At time of writing, wlr_output_layout_remove()
* indirectly destroys the wlr_scene_output, but
* this behavior may change in future. To remove
* doubt and avoid either a leak or double-free,
* explicitly destroy the wlr_scene_output before
* calling wlr_output_layout_remove().
*/
wlr_scene_output_destroy(output->scene_output);
wlr_output_layout_remove(server->output_layout, o);
output->scene_output = NULL;
}
}
g_pending_output_layout_change--;
do_output_layout_change(server);
return success;
}
static void handle_output_manager_apply(struct wl_listener *listener, void *data)
{
struct diyac_server *server =
wl_container_of(listener, server, output_manager_apply);
struct wlr_output_configuration_v1 *config = data;
bool config_is_good = verify_output_config_v1(config);
if (config_is_good && output_config_apply(server, config))
{
wlr_output_configuration_v1_send_succeeded(config);
}
else
{
wlr_output_configuration_v1_send_failed(config);
}
wlr_output_configuration_v1_destroy(config);
/*
struct output *output;
wl_list_for_each(output, &server->outputs, link)
{
wlr_xcursor_manager_load(server->seat.xcursor_manager,
output->wlr_output->scale);
}
cursor_update_focus(server);
cursor_update_image(&server->seat);
*/
}
static void handle_output_manager_test(struct wl_listener *listener, void *data)
{
(void) listener;
struct wlr_output_configuration_v1 *config = data;
if (verify_output_config_v1(config))
{
wlr_output_configuration_v1_send_succeeded(config);
}
else
{
wlr_output_configuration_v1_send_failed(config);
}
wlr_output_configuration_v1_destroy(config);
}
void diyac_output_init(struct diyac_server *server)
{
/* Creates an output layout, which a wlroots utility for working with an
* arrangement of screens in a physical layout. */
server->output_layout = wlr_output_layout_create(server->wl_display);
/* Configure a listener to be notified when new outputs are available on the
* backend. */
wl_list_init(&server->outputs);
server->new_output.notify = diyac_server_new_output;
wl_signal_add(&server->backend->events.new_output, &server->new_output);
server->scene_layout = wlr_scene_attach_output_layout(server->scene, server->output_layout);
wlr_xdg_output_manager_v1_create(server->wl_display, server->output_layout);
server->output_layout_change.notify = handle_output_layout_change;
wl_signal_add(&server->output_layout->events.change,
&server->output_layout_change);
server->output_manager = wlr_output_manager_v1_create(server->wl_display);
server->output_manager_apply.notify = handle_output_manager_apply;
wl_signal_add(&server->output_manager->events.apply,
&server->output_manager_apply);
server->output_manager_test.notify = handle_output_manager_test;
wl_signal_add(&server->output_manager->events.test,
&server->output_manager_test);
server->output_power_manager =
wlr_output_power_manager_v1_create(server->wl_display);
server->output_power_manager_set_mode.notify =
diyac_output_power_manager_set_mode;
wl_signal_add(&server->output_power_manager->events.set_mode,
&server->output_power_manager_set_mode);
}

View File

@ -2,9 +2,10 @@
#define DIYAC_OUTPUT_H
#include "diyac.h"
void diyac_server_new_output(struct wl_listener *listener, void *data);
void diyac_output_update_usable_area(struct diyac_output *output);
struct diyac_output *diyac_output_from_cursor(struct diyac_server *server);
void diyac_output_usable_area(struct diyac_output * output,struct wlr_box* box);
void diyac_output_full_area(struct diyac_output *output,struct wlr_box* area);
void diyac_output_init(struct diyac_server * server);
void diyac_output_enable_adaptive_sync(struct diyac_output *output, bool enabled);
#endif

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_output_power_management_unstable_v1">
<copyright>
Copyright © 2019 Purism SPC
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="Control power management modes of outputs">
This protocol allows clients to control power management modes
of outputs that are currently part of the compositor space. The
intent is to allow special clients like desktop shells to power
down outputs when the system is idle.
To modify outputs not currently part of the compositor space see
wlr-output-management.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<interface name="zwlr_output_power_manager_v1" version="1">
<description summary="manager to create per-output power management">
This interface is a manager that allows creating per-output power
management mode controls.
</description>
<request name="get_output_power">
<description summary="get a power management for an output">
Create an output power management mode control that can be used to
adjust the power management mode for a given output.
</description>
<arg name="id" type="new_id" interface="zwlr_output_power_v1"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the manager">
All objects created by the manager will still remain valid, until their
appropriate destroy request has been called.
</description>
</request>
</interface>
<interface name="zwlr_output_power_v1" version="1">
<description summary="adjust power management mode for an output">
This object offers requests to set the power management mode of
an output.
</description>
<enum name="mode">
<entry name="off" value="0"
summary="Output is turned off."/>
<entry name="on" value="1"
summary="Output is turned on, no power saving"/>
</enum>
<enum name="error">
<entry name="invalid_mode" value="1" summary="nonexistent power save mode"/>
</enum>
<request name="set_mode">
<description summary="Set an outputs power save mode">
Set an output's power save mode to the given mode. The mode change
is effective immediately. If the output does not support the given
mode a failed event is sent.
</description>
<arg name="mode" type="uint" enum="mode" summary="the power save mode to set"/>
</request>
<event name="mode">
<description summary="Report a power management mode change">
Report the power management mode change of an output.
The mode event is sent after an output changed its power
management mode. The reason can be a client using set_mode or the
compositor deciding to change an output's mode.
This event is also sent immediately when the object is created
so the client is informed about the current power management mode.
</description>
<arg name="mode" type="uint" enum="mode"
summary="the output's new power management mode"/>
</event>
<event name="failed">
<description summary="object no longer valid">
This event indicates that the output power management mode control
is no longer valid. This can happen for a number of reasons,
including:
- The output doesn't support power management
- Another client already has exclusive power management mode control
for this output
- The output disappeared
Upon receiving this event, the client should destroy this object.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy this power management">
Destroys the output power management mode control object.
</description>
</request>
</interface>
</protocol>

8
seat.c
View File

@ -7,9 +7,11 @@
#include "seat.h"
#include "view.h"
#include "output.h"
#include "idle.h"
static void configure_keyboard(struct diyac_seat* seat, struct diyac_input* input, bool force)
{
(void) seat;
struct wlr_input_device *device = input->device;
assert(device->type == WLR_INPUT_DEVICE_KEYBOARD);
struct diyac_keyboard *keyboard = (struct diyac_keyboard *)input;
@ -41,6 +43,7 @@ static void configure_keyboard(struct diyac_seat* seat, struct diyac_input* inpu
static void keyboard_handle_modifiers(
struct wl_listener *listener, void *data)
{
(void) data;
/* This event is raised when a modifier key, such as shift or alt, is
* pressed. We simply communicate this to the client. */
struct diyac_keyboard *keyboard =
@ -95,7 +98,7 @@ static void keyboard_handle_key(
wl_container_of(listener, keyboard, key);
struct wlr_keyboard_key_event *event = data;
struct wlr_seat *seat = keyboard->input.seat->wlr_seat;
diya_idle_manager_notify_activity(seat);
/* Translate libinput keycode -> xkbcommon */
// wlr_log(WLR_INFO, "receive keycode %d", event->keycode);
uint32_t keycode = event->keycode + 8;
@ -113,7 +116,7 @@ static void keyboard_handle_key(
* process it as a compositor keybinding. */
for (int i = 0; i < nsyms; i++)
{
handled = handle_keybinding(keyboard->input.seat->server, syms[i]);
handled |= handle_keybinding(keyboard->input.seat->server, syms[i]);
}
}
@ -128,6 +131,7 @@ static void keyboard_handle_key(
static void input_handle_destroy(struct wl_listener *listener, void *data)
{
(void) data;
/* This event is raised by the keyboard base wlr_input_device to signal
* the destruction of the wlr_keyboard. It will no longer receive events
* and should be destroyed.

View File

@ -39,6 +39,7 @@ static void session_lock_update_geometry(struct diyac_output *output, bool align
static void handle_surface_map(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_output_lock_handle *handle = wl_container_of(listener, handle, surface_map);
if (!g_server->lock->focused)
{
@ -49,6 +50,7 @@ static void handle_surface_map(struct wl_listener *listener, void *data)
static void handle_surface_destroy(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_output_lock_handle *handle = wl_container_of(listener, handle, surface_destroy);
if (g_server->lock->focused == handle->surface->surface)
@ -123,6 +125,7 @@ static void session_lock_destroy(struct diyac_session_lock *lock)
static void handle_unlock(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_session_lock *lock = wl_container_of(listener, lock, unlock);
wlr_log(WLR_INFO, "handle_unlock: Lock session is unlocked");
session_lock_destroy(lock);
@ -131,6 +134,7 @@ static void handle_unlock(struct wl_listener *listener, void *data)
static void handle_session_lock_destroy(struct wl_listener *listener, void *data)
{
(void) data;
struct diyac_session_lock *lock = wl_container_of(listener, lock, destroy);
lock->abandoned = true;
wlr_log(WLR_INFO, "handle_session_lock_destroy: Lock session is destroyed without unlocking, session abandoned");
@ -141,6 +145,7 @@ static void handle_session_lock_destroy(struct wl_listener *listener, void *data
static void handle_new_session_lock(struct wl_listener *listener, void *data)
{
(void) listener;
struct wlr_session_lock_v1 *lock = data;
if (g_server->lock)
{
@ -200,6 +205,8 @@ static void handle_commit(struct wl_listener *listener, void *data)
static void handle_lock_manager_destroy(struct wl_listener *listener, void *data)
{
(void) data;
(void) listener;
if (g_server->lock)
{
session_lock_destroy(g_server->lock);
@ -245,9 +252,9 @@ void diyac_session_lock_output(struct diyac_output *output)
handle->background = wlr_scene_rect_create(handle->tree, 0, 0, black);
if (!handle->background)
{
wlr_scene_node_destroy(&handle->tree->node);
free(handle);
wlr_log(WLR_ERROR, "diyac_session_lock_output:Unable to create lock background");
wlr_scene_node_destroy(&handle->tree->node);
exit(EXIT_FAILURE);
}
handle->surface = NULL;

25
view.c
View File

@ -26,10 +26,9 @@ static int handle_configure_timeout(void *data)
return 0; /* ignored per wl_event_loop docs */
}
static void diyac_view_configure(struct diyac_view *view, struct wlr_box geo)
void diya_view_set_pending_configure_serial(struct diyac_view* view, uint32_t serial)
{
view->pending_size = geo;
view->configuration_serial = wlr_xdg_toplevel_set_size(view->xdg_toplevel, geo.width, geo.height);
view->configuration_serial = serial;
if (!view->configuration_timeout)
{
view->configuration_timeout = wl_event_loop_add_timer(view->server->wl_event_loop, handle_configure_timeout, view);
@ -37,10 +36,24 @@ static void diyac_view_configure(struct diyac_view *view, struct wlr_box geo)
wl_event_source_timer_update(view->configuration_timeout, CONFIGURE_TIMEOUT_MS);
}
static void diyac_view_configure(struct diyac_view *view, struct wlr_box geo)
{
view->pending_size = geo;
uint32_t serial = wlr_xdg_toplevel_set_size(view->xdg_toplevel, geo.width, geo.height);
if(serial > 0)
{
diya_view_set_pending_configure_serial(view, serial);
}
}
static void diyac_view_set_activated(struct diyac_view *view, bool activated)
{
struct diyac_server *server = view->server;
wlr_xdg_toplevel_set_activated(view->xdg_toplevel, activated);
uint32_t serial = wlr_xdg_toplevel_set_activated(view->xdg_toplevel, activated);
if(serial > 0)
{
diya_view_set_pending_configure_serial(view, serial);
}
if (view->toplevel.handle)
{
wlr_foreign_toplevel_handle_v1_set_activated(view->toplevel.handle, activated);
@ -170,8 +183,8 @@ void diyac_focus_topmost_view(struct diyac_server *server, bool raise)
struct diyac_view *diyac_topmost_focusable_view(struct diyac_server *server)
{
struct wlr_surface *prev =
server->seat.wlr_seat->keyboard_state.focused_surface;
//struct wlr_surface *prev =
// server->seat.wlr_seat->keyboard_state.focused_surface;
struct diyac_view *view;
struct wl_list *node_list;
struct wlr_scene_node *node;

1
view.h
View File

@ -22,4 +22,5 @@ void diyac_view_update_title(struct diyac_view * view);
void diyac_view_update_app_id(struct diyac_view * view);
void diyac_view_sync_geo(struct diyac_view *view);
void diya_view_unfocus(struct diyac_view* view);
void diya_view_set_pending_configure_serial(struct diyac_view* view, uint32_t serial);
#endif

86
xdg.c
View File

@ -55,26 +55,44 @@ static void begin_interactive(struct diyac_view *toplevel,
}
static void xdg_toplevel_commit(struct wl_listener *listener, void *data)
{
struct diyac_view *view = wl_container_of(listener, view, commit);
uint32_t serial = view->configuration_serial;
if (serial > 0 && serial == view->xdg_surface->current.configure_serial)
(void)data;
struct diyac_view *toplevel = wl_container_of(listener, toplevel, commit);
uint32_t serial = toplevel->configuration_serial;
struct wlr_xdg_surface *xdg_surface = toplevel->xdg_surface;
if (xdg_surface->initial_commit)
{
wl_event_source_remove(view->configuration_timeout);
view->configuration_serial = 0;
view->configuration_timeout = NULL;
uint32_t serial =
wlr_xdg_surface_schedule_configure(xdg_surface);
uint32_t wm_caps = WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE | WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN | WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE;
wlr_xdg_toplevel_set_wm_capabilities(toplevel->xdg_toplevel, wm_caps);
if(serial > 0)
{
diya_view_set_pending_configure_serial(toplevel, serial);
}
return;
}
if (serial > 0 && serial == toplevel->xdg_surface->current.configure_serial)
{
wl_event_source_remove(toplevel->configuration_timeout);
toplevel->configuration_serial = 0;
toplevel->configuration_timeout = NULL;
// TODO move view
}
diyac_view_sync_geo(view);
diyac_view_sync_geo(toplevel);
}
static void xdg_toplevel_map(struct wl_listener *listener, void *data)
{
(void)data;
wlr_log(WLR_INFO, "xdg_toplevel_map: %p", listener);
/* Called when the surface is mapped, or ready to display on-screen. */
struct diyac_view *toplevel = wl_container_of(listener, toplevel, map);
if (toplevel->mapped)
{
return;
}
diyac_init_foreign_toplevel(toplevel);
/*
wlr_xdg_toplevel_set_wm_capabilities(toplevel->xdg_toplevel,
WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE |
@ -84,9 +102,6 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data)
wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &toplevel->pending_size);
wlr_scene_node_set_enabled(&toplevel->scene_tree->node, true);
toplevel->mapped = true;
wl_list_insert(&toplevel->server->views, &toplevel->link);
toplevel->commit.notify = xdg_toplevel_commit;
wl_signal_add(&toplevel->xdg_surface->surface->events.commit, &toplevel->commit);
diyac_view_update_app_id(toplevel);
diyac_view_update_title(toplevel);
@ -106,6 +121,7 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data)
static void xdg_toplevel_unmap(struct wl_listener *listener, void *data)
{
(void)data;
/* Called when the surface is unmapped, and should no longer be shown. */
struct diyac_view *toplevel = wl_container_of(listener, toplevel, unmap);
toplevel->mapped = false;
@ -144,6 +160,7 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data)
static void xdg_toplevel_request_move(
struct wl_listener *listener, void *data)
{
(void)data;
/* This event is raised when a client would like to begin an interactive
* move, typically because the user clicked on their client-side
* decorations. Note that a more sophisticated compositor should check the
@ -170,6 +187,7 @@ static void xdg_toplevel_request_resize(
static void xdg_toplevel_request_maximize(
struct wl_listener *listener, void *data)
{
(void)data;
/* This event is raised when a client would like to maximize itself,
* typically because the user clicked on the maximize button on
* client-side decorations. diyac doesn't support maximization, but
@ -183,6 +201,7 @@ static void xdg_toplevel_request_maximize(
static void xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_view *toplevel =
wl_container_of(listener, toplevel, request_fullscreen);
diyac_view_set_fullscreen(toplevel, toplevel->xdg_toplevel->requested.fullscreen);
@ -190,6 +209,7 @@ static void xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *
static void xdg_toplevel_request_minimize(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_view *toplevel =
wl_container_of(listener, toplevel, request_minimize);
diyac_view_set_mimimize(toplevel, toplevel->xdg_toplevel->requested.minimized);
@ -197,6 +217,7 @@ static void xdg_toplevel_request_minimize(struct wl_listener *listener, void *da
static void xdg_toplevel_destroy(struct wl_listener *listener, void *data)
{
(void)data;
/* Called when the xdg_toplevel is destroyed. */
struct diyac_view *toplevel = wl_container_of(listener, toplevel, destroy);
if (toplevel->toplevel.handle)
@ -229,6 +250,7 @@ static void xdg_toplevel_destroy(struct wl_listener *listener, void *data)
static void handle_xdg_popup_destroy(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_popup *popup = wl_container_of(listener, popup, destroy);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->new_popup.link);
@ -248,9 +270,9 @@ static void popup_unconstrain(struct diyac_popup *popup)
{
return;
}
struct diyac_server *server = view->server;
struct wlr_output_layout *output_layout = server->output_layout;
struct wlr_output *wlr_output = view->output->wlr_output;
// struct diyac_server *server = view->server;
//struct wlr_output_layout *output_layout = server->output_layout;
//struct wlr_output *wlr_output = view->output->wlr_output;
struct wlr_box usable = {
.x = 0,
.y = 0,
@ -273,6 +295,7 @@ static void popup_unconstrain(struct diyac_popup *popup)
static void handle_xdg_popup_commit(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_popup *popup = wl_container_of(listener, popup, commit);
struct wlr_box popup_box;
wlr_xdg_surface_get_geometry(popup->wlr_popup->base, &popup_box);
@ -280,7 +303,7 @@ static void handle_xdg_popup_commit(struct wl_listener *listener, void *data)
if (!wlr_box_empty(&popup_box))
// if (popup->wlr_popup->base->initial_commit)
{
struct diyac_view *view = popup->parent;
// struct diyac_view *view = popup->parent;
// wlr_output_commit(view->output->wlr_output);
/* Prevent getting called over and over again */
wl_list_remove(&popup->commit.link);
@ -352,12 +375,14 @@ static void xdg_popup_create(struct diyac_view *view, struct wlr_xdg_popup *wlr_
static void xdg_set_appid_notify(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_view *view = wl_container_of(listener, view, set_app_id);
diyac_view_update_app_id(view);
}
static void xdg_set_title_notify(struct wl_listener *listener, void *data)
{
(void)data;
struct diyac_view *view = wl_container_of(listener, view, set_title);
diyac_view_update_title(view);
}
@ -370,31 +395,15 @@ static void xdg_new_popup_notify(struct wl_listener *listener, void *data)
xdg_popup_create(view, wlr_popup);
}
void diyac_new_xdg_surface(struct wl_listener *listener, void *data)
void diyac_new_xdg_toplevel(struct wl_listener *listener, void *data)
{
/* This event is raised when wlr_xdg_shell receives a new xdg surface from a
* client, either a toplevel (application window) or popup. */
struct diyac_server *server =
wl_container_of(listener, server, new_xdg_surface);
struct wlr_xdg_surface *xdg_surface = data;
wl_container_of(listener, server, new_xdg_toplevel);
struct wlr_xdg_toplevel *xdg_toplevel = data;
struct wlr_xdg_surface *xdg_surface = xdg_toplevel->base;
/* We must add xdg popups to the scene graph so they get rendered. The
* wlroots scene graph provides a helper for this, but to use it we must
* provide the proper parent scene node of the xdg popup. To enable this,
* we always set the user data field of xdg_surfaces to the corresponding
* scene node. */
if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP)
{
/*struct wlr_xdg_surface *parent =
wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent);
assert(parent != NULL);
struct wlr_scene_tree *parent_tree = parent->data;
xdg_surface->data = wlr_scene_xdg_surface_create(
parent_tree, xdg_surface);
return;*/
wlr_log(WLR_INFO, "diyac_new_xdg_surface: Creating new dialog using view popup");
return;
}
assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
wlr_log(WLR_INFO, "diyac_new_xdg_surface: Creating new application windows");
wlr_xdg_surface_ping(xdg_surface);
@ -421,7 +430,6 @@ void diyac_new_xdg_surface(struct wl_listener *listener, void *data)
wlr_scene_node_set_enabled(&toplevel->scene_tree->node, false);
diyac_node_descriptor_create(&toplevel->scene_tree->node,
DIYAC_NODE_VIEW, toplevel);
diyac_init_foreign_toplevel(toplevel);
/* Listen to the various events it can emit */
toplevel->map.notify = xdg_toplevel_map;
@ -432,7 +440,6 @@ void diyac_new_xdg_surface(struct wl_listener *listener, void *data)
wl_signal_add(&xdg_surface->events.destroy, &toplevel->destroy);
/* cotd */
struct wlr_xdg_toplevel *xdg_toplevel = xdg_surface->toplevel;
toplevel->request_move.notify = xdg_toplevel_request_move;
wl_signal_add(&xdg_toplevel->events.request_move, &toplevel->request_move);
toplevel->request_resize.notify = xdg_toplevel_request_resize;
@ -453,4 +460,9 @@ void diyac_new_xdg_surface(struct wl_listener *listener, void *data)
toplevel->set_title.notify = xdg_set_title_notify;
wl_signal_add(&xdg_toplevel->events.set_title, &toplevel->set_title);
toplevel->commit.notify = xdg_toplevel_commit;
wl_signal_add(&toplevel->xdg_surface->surface->events.commit, &toplevel->commit);
wl_list_insert(&toplevel->server->views, &toplevel->link);
}

2
xdg.h
View File

@ -2,5 +2,5 @@
#define DIYAC_XDG_H
#include "diyac.h"
void diyac_new_xdg_surface(struct wl_listener *listener, void *data);
void diyac_new_xdg_toplevel(struct wl_listener *listener, void *data);
#endif