1 /*
2 * Copyright (C) 2018, Matthias Clasen
3 *
4 * This file is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation, version 3.0 of the
7 * License.
8 *
9 * This file is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * SPDX-License-Identifier: LGPL-3.0-only
18 */
19
20 #include "config.h"
21
22 #include "session-private.h"
23 #include "portal-private.h"
24
25 /**
26 * XdpSession
27 *
28 * A representation of long-lived screencast portal interactions.
29 *
30 * The XdpSession object is used to represent portal interactions with the
31 * screencast or remote desktop portals that extend over multiple portal calls.
32 *
33 * To find out what kind of session an XdpSession object represents and whether
34 * it is still active, you can use [method@Session.get_session_type] and
35 * [method@Session.get_session_state].
36 *
37 * All sessions start in an initial state. They can be made active by calling
38 * [method@Session.start], and ended by calling [method@Session.close].
39 */
40 enum {
41 CLOSED,
42 LAST_SIGNAL
43 };
44
45 static guint signals[LAST_SIGNAL];
46
G_DEFINE_TYPE(XdpSession,xdp_session,G_TYPE_OBJECT)47 G_DEFINE_TYPE (XdpSession, xdp_session, G_TYPE_OBJECT)
48
49 static void
50 xdp_session_finalize (GObject *object)
51 {
52 XdpSession *session = XDP_SESSION (object);
53
54 if (session->signal_id)
55 g_dbus_connection_signal_unsubscribe (session->portal->bus, session->signal_id);
56
57 g_clear_object (&session->portal);
58 g_free (session->restore_token);
59 g_free (session->id);
60 g_clear_pointer (&session->streams, g_variant_unref);
61
62 G_OBJECT_CLASS (xdp_session_parent_class)->finalize (object);
63 }
64
65 static void
xdp_session_class_init(XdpSessionClass * klass)66 xdp_session_class_init (XdpSessionClass *klass)
67 {
68 GObjectClass *object_class = G_OBJECT_CLASS (klass);
69
70 object_class->finalize = xdp_session_finalize;
71
72 /**
73 * XdpSession::closed:
74 *
75 * The ::closed signal is emitted when a session is closed externally.
76 */
77 signals[CLOSED] =
78 g_signal_new ("closed",
79 G_TYPE_FROM_CLASS (object_class),
80 G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
81 0,
82 NULL, NULL,
83 NULL,
84 G_TYPE_NONE, 0);
85 }
86
87 static void
xdp_session_init(XdpSession * session)88 xdp_session_init (XdpSession *session)
89 {
90 }
91
92 static void
session_closed(GDBusConnection * bus,const char * sender_name,const char * object_path,const char * interface_name,const char * signal_name,GVariant * parameters,gpointer data)93 session_closed (GDBusConnection *bus,
94 const char *sender_name,
95 const char *object_path,
96 const char *interface_name,
97 const char *signal_name,
98 GVariant *parameters,
99 gpointer data)
100 {
101 XdpSession *session = data;
102
103 _xdp_session_set_session_state (session, XDP_SESSION_CLOSED);
104 }
105
106 XdpSession *
_xdp_session_new(XdpPortal * portal,const char * id,XdpSessionType type)107 _xdp_session_new (XdpPortal *portal,
108 const char *id,
109 XdpSessionType type)
110 {
111 XdpSession *session;
112
113 session = g_object_new (XDP_TYPE_SESSION, NULL);
114 session->portal = g_object_ref (portal);
115 session->id = g_strdup (id);
116 session->type = type;
117 session->state = XDP_SESSION_INITIAL;
118
119 session->signal_id = g_dbus_connection_signal_subscribe (portal->bus,
120 PORTAL_BUS_NAME,
121 SESSION_INTERFACE,
122 "Closed",
123 id,
124 NULL,
125 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
126 session_closed,
127 session,
128 NULL);
129 return session;
130 }
131
132 /**
133 * xdp_session_get_session_type:
134 * @session: an [class@Session]
135 *
136 * Obtains information about the type of session that is represented
137 * by @session.
138 *
139 * Returns: the type of @session
140 */
141 XdpSessionType
xdp_session_get_session_type(XdpSession * session)142 xdp_session_get_session_type (XdpSession *session)
143 {
144 g_return_val_if_fail (XDP_IS_SESSION (session), XDP_SESSION_SCREENCAST);
145
146 return session->type;
147 }
148
149 /**
150 * xdp_session_get_session_state:
151 * @session: an [class@Session]
152 *
153 * Obtains information about the state of the session that is represented
154 * by @session.
155 *
156 * Returns: the state of @session
157 */
158 XdpSessionState
xdp_session_get_session_state(XdpSession * session)159 xdp_session_get_session_state (XdpSession *session)
160 {
161 g_return_val_if_fail (XDP_IS_SESSION (session), XDP_SESSION_CLOSED);
162
163 return session->state;
164 }
165
166 void
_xdp_session_set_session_state(XdpSession * session,XdpSessionState state)167 _xdp_session_set_session_state (XdpSession *session,
168 XdpSessionState state)
169 {
170 session->state = state;
171
172 if (state == XDP_SESSION_INITIAL && session->state != XDP_SESSION_INITIAL)
173 {
174 g_warning ("Can't move a session back to initial state");
175 return;
176 }
177 if (session->state == XDP_SESSION_CLOSED && state != XDP_SESSION_CLOSED)
178 {
179 g_warning ("Can't move a session back from closed state");
180 return;
181 }
182
183 if (state == XDP_SESSION_CLOSED)
184 g_signal_emit (session, signals[CLOSED], 0);
185 }
186
187 /**
188 * xdp_session_get_devices:
189 * @session: a [class@Session]
190 *
191 * Obtains the devices that the user selected.
192 *
193 * Unless the session is active, this function returns `XDP_DEVICE_NONE`.
194 *
195 * Returns: the selected devices
196 */
197 XdpDeviceType
xdp_session_get_devices(XdpSession * session)198 xdp_session_get_devices (XdpSession *session)
199 {
200 g_return_val_if_fail (XDP_IS_SESSION (session), XDP_DEVICE_NONE);
201
202 if (session->state != XDP_SESSION_ACTIVE)
203 return XDP_DEVICE_NONE;
204
205 return session->devices;
206 }
207
208 void
_xdp_session_set_devices(XdpSession * session,XdpDeviceType devices)209 _xdp_session_set_devices (XdpSession *session,
210 XdpDeviceType devices)
211 {
212 session->devices = devices;
213 }
214
215 /**
216 * xdp_session_get_streams:
217 * @session: a [class@Session]
218 *
219 * Obtains the streams that the user selected. The information in the
220 * returned [struct@GLib.Variant] has the format `a(ua{sv})`. Each item in the array
221 * is describing a stream. The first member is the pipewire node ID, the
222 * second is a dictionary of stream properties, including:
223 *
224 * - position, `(ii)`: a tuple consisting of the position `(x, y)` in the compositor
225 * coordinate space. Note that the position may not be equivalent to a
226 * position in a pixel coordinate space. Only available for monitor streams.
227 * - size, `(ii)`: a tuple consisting of (width, height). The size represents the size
228 * of the stream as it is displayed in the compositor coordinate space.
229 * Note that this size may not be equivalent to a size in a pixel coordinate
230 * space. The size may differ from the size of the stream.
231 *
232 * Unless the session is active, this function returns `NULL`.
233 *
234 * Returns: the selected streams
235 */
236 GVariant *
xdp_session_get_streams(XdpSession * session)237 xdp_session_get_streams (XdpSession *session)
238 {
239 g_return_val_if_fail (XDP_IS_SESSION (session), NULL);
240
241 if (session->state != XDP_SESSION_ACTIVE)
242 return NULL;
243
244 return session->streams;
245 }
246
247 void
_xdp_session_set_streams(XdpSession * session,GVariant * streams)248 _xdp_session_set_streams (XdpSession *session,
249 GVariant *streams)
250 {
251 if (session->streams)
252 g_variant_unref (session->streams);
253 session->streams = streams;
254 if (session->streams)
255 g_variant_ref (session->streams);
256 }
257