1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Objects defined in wayland protocols

use std::future::Future;

use hashbrown::{HashMap, HashSet};
use runa_core::{
    client::traits::{Client, EventDispatcher, EventHandler, EventHandlerAction, Store},
    error::Error,
    objects::wayland_object,
};
use runa_io::traits::WriteMessage;
use runa_wayland_protocols::wayland::{wl_buffer::v1 as wl_buffer, wl_output::v4 as wl_output};

use crate::{
    shell::{
        buffers::{AttachableBuffer, BufferEvent, BufferLike, HasBuffer},
        output::Output as ShellOutput,
        HasShell,
    },
    utils::WeakPtr,
};

pub mod compositor;
pub mod input;
pub mod shm;
pub mod xdg_shell;

/// Implementation of the `wl_buffer` interface.
#[derive(Debug)]
pub struct Buffer<B> {
    pub(crate) buffer:    AttachableBuffer<B>,
    _event_handler_abort: crate::utils::AutoAbort,
}

impl<B: BufferLike> Buffer<B> {
    pub(crate) fn new<Ctx: Client, B2: Into<B>, E: EventDispatcher<Ctx>>(
        buffer: B2,
        event_dispatcher: &mut E,
    ) -> Self {
        struct BufferEventHandler {
            object_id: u32,
        }
        impl<Ctx: Client> EventHandler<Ctx> for BufferEventHandler {
            type Message = BufferEvent;

            type Future<'ctx> = impl Future<
                    Output = Result<EventHandlerAction, Box<dyn std::error::Error + Send + Sync>>,
                > + 'ctx;

            fn handle_event<'ctx>(
                &'ctx mut self,
                _objects: &'ctx mut Ctx::ObjectStore,
                connection: &'ctx mut Ctx::Connection,
                _server_context: &'ctx Ctx::ServerContext,
                _message: &'ctx mut Self::Message,
            ) -> Self::Future<'ctx> {
                async move {
                    connection
                        .send(self.object_id, wl_buffer::events::Release {})
                        .await?;
                    Ok(EventHandlerAction::Keep)
                }
            }
        }
        let buffer = buffer.into();
        let object_id = buffer.object_id();
        let rx = buffer.subscribe();
        let (rx, abort_handle) = futures_util::stream::abortable(rx);
        let ret = Self {
            buffer:               AttachableBuffer::new(buffer),
            _event_handler_abort: abort_handle.into(),
        };
        event_dispatcher.add_event_handler(rx, BufferEventHandler { object_id });
        ret
    }
}

#[wayland_object]
impl<Ctx, B: 'static> wl_buffer::RequestDispatch<Ctx> for Buffer<B>
where
    Ctx: Client,
    Ctx::ServerContext: HasBuffer<Buffer = B>,
{
    type Error = Error;

    type DestroyFut<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Ctx: 'a;

    fn destroy(ctx: &mut Ctx, object_id: u32) -> Self::DestroyFut<'_> {
        ctx.objects_mut().remove(object_id);
        futures_util::future::ok(())
    }
}

#[doc(hidden)]
#[derive(Debug, Default)]
pub struct OutputState {
    /// A map of shell outputs to their set of object ids
    pub(crate) all_outputs: HashMap<WeakPtr<ShellOutput>, HashSet<u32>>,
}

/// Implementation of the `wl_output` interface.
#[derive(Debug)]
pub struct Output {
    /// The corresponding shell output object.
    pub(crate) output:              WeakPtr<ShellOutput>,
    pub(crate) event_handler_abort: Option<crate::utils::AutoAbort>,
}

#[wayland_object(state = "OutputState")]
impl<Ctx> wl_output::RequestDispatch<Ctx> for Output
where
    Ctx::ServerContext: HasShell,
    Ctx: Client,
{
    type Error = Error;

    type ReleaseFut<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Ctx: 'a;

    fn release(ctx: &mut Ctx, object_id: u32) -> Self::ReleaseFut<'_> {
        async move {
            use runa_core::objects::AnyObject;
            let objects = ctx.objects_mut();
            let object = objects.remove(object_id).unwrap();
            let object = object.cast::<Self>().unwrap();

            // Remove ourself from all_outputs
            let Some(state) = objects.get_state_mut::<Self>() else {
                // No bound output objects left anymore
                return Ok(());
            };

            use hashbrown::hash_map::Entry;
            let Entry::Occupied(mut all_outputs_entry) = state.all_outputs.entry(object.output.clone()) else {
                panic!("Output object not found in all_outputs");
            };

            let ids = all_outputs_entry.get_mut();
            ids.remove(&object_id);
            if ids.is_empty() {
                all_outputs_entry.remove();
            }

            Ok(())
        }
    }
}