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