2 * @file sipe-appshare.c
6 * Copyright (C) 2014-2018 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #ifdef HAVE_APPSHARE_SERVER
33 #include <glib/gstdio.h>
35 #include <sys/socket.h>
38 #include <freerdp/server/shadow.h>
39 #endif // HAVE_APPSHARE_SERVER
42 #include "sipe-appshare-client.h"
43 #include "sipe-backend.h"
44 #include "sipe-buddy.h"
45 #include "sipe-chat.h"
46 #include "sipe-common.h"
47 #include "sipe-conf.h"
48 #include "sipe-core.h"
49 #include "sipe-core-private.h"
50 #include "sipe-appshare.h"
51 #include "sipe-media.h"
53 #include "sipe-schedule.h"
54 #include "sipe-user.h"
55 #include "sipe-utils.h"
58 struct sipe_appshare
{
59 struct sipe_media_stream
*stream
;
62 guint rdp_channel_readable_watch_id
;
63 guint rdp_channel_writable_watch_id
;
65 struct sipe_user_ask_ctx
*ask_ctx
;
67 gchar rdp_channel_buffer
[0x800];
68 gchar
*rdp_channel_buffer_pos
;
69 gsize rdp_channel_buffer_len
;
71 struct sipe_rdp_client client
;
73 #ifdef HAVE_APPSHARE_SERVER
74 rdpShadowServer
*server
;
75 #endif // HAVE_APPSHARE_SERVER
79 sipe_appshare_free(struct sipe_appshare
*appshare
)
81 if (appshare
->rdp_channel_readable_watch_id
!= 0) {
82 g_source_destroy(g_main_context_find_source_by_id(NULL
,
83 appshare
->rdp_channel_readable_watch_id
));
86 if (appshare
->rdp_channel_writable_watch_id
!= 0) {
87 g_source_destroy(g_main_context_find_source_by_id(NULL
,
88 appshare
->rdp_channel_writable_watch_id
));
91 if (appshare
->channel
) {
94 g_io_channel_shutdown(appshare
->channel
, TRUE
, &error
);
96 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
100 g_io_channel_unref(appshare
->channel
);
103 if (appshare
->socket
) {
104 g_object_unref(appshare
->socket
);
107 #ifdef HAVE_APPSHARE_SERVER
108 if (appshare
->server
) {
109 if (appshare
->server
->ipcSocket
) {
110 g_unlink(appshare
->server
->ipcSocket
);
113 shadow_server_stop(appshare
->server
);
114 shadow_server_uninit(appshare
->server
);
115 shadow_server_free(appshare
->server
);
117 #endif // HAVE_APPSHARE_SERVER
119 if (appshare
->ask_ctx
) {
120 sipe_user_close_ask(appshare
->ask_ctx
);
123 g_free(appshare
->client
.cmdline
);
124 if (appshare
->client
.free_cb
) {
125 appshare
->client
.free_cb(&appshare
->client
);
132 rdp_channel_readable_cb(GIOChannel
*channel
,
133 GIOCondition condition
,
136 struct sipe_appshare
*appshare
= data
;
137 GError
*error
= NULL
;
141 if (condition
& G_IO_HUP
) {
142 struct sipe_media_call
*call
= appshare
->stream
->call
;
144 SIPE_DEBUG_INFO_NOFORMAT("Received HUP from RDP client.");
145 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
149 buffer
= g_malloc(2048);
150 while (sipe_media_stream_is_writable(appshare
->stream
)) {
153 status
= g_io_channel_read_chars(channel
,
155 &bytes_read
, &error
);
157 struct sipe_media_call
*call
= appshare
->stream
->call
;
159 SIPE_DEBUG_ERROR("Error reading from RDP channel: %s",
162 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
167 if (status
== G_IO_STATUS_EOF
) {
168 struct sipe_media_call
*call
= appshare
->stream
->call
;
170 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
175 if (bytes_read
== 0) {
179 sipe_media_stream_write(appshare
->stream
, (guint8
*)buffer
,
181 SIPE_DEBUG_INFO("Written: %" G_GSIZE_FORMAT
"\n", bytes_read
);
189 socket_connect_cb(SIPE_UNUSED_PARAMETER GIOChannel
*channel
,
190 SIPE_UNUSED_PARAMETER GIOCondition condition
,
193 struct sipe_appshare
*appshare
= data
;
194 GError
*error
= NULL
;
195 GSocket
*data_socket
;
198 SIPE_DEBUG_INFO_NOFORMAT("RDP client has connected.");
200 data_socket
= g_socket_accept(appshare
->socket
, NULL
, &error
);
202 struct sipe_media_call
*call
= appshare
->stream
->call
;
204 SIPE_DEBUG_ERROR("Error accepting RDP client connection: %s",
207 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
211 g_io_channel_shutdown(appshare
->channel
, TRUE
, &error
);
213 struct sipe_media_call
*call
= appshare
->stream
->call
;
215 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
218 g_object_unref(data_socket
);
219 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
222 g_io_channel_unref(appshare
->channel
);
224 g_object_unref(appshare
->socket
);
225 appshare
->socket
= data_socket
;
227 fd
= g_socket_get_fd(appshare
->socket
);
229 struct sipe_media_call
*call
= appshare
->stream
->call
;
231 SIPE_DEBUG_ERROR_NOFORMAT("Invalid file descriptor for RDP client connection socket");
232 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
235 appshare
->channel
= g_io_channel_unix_new(fd
);
237 // No encoding for binary data
238 g_io_channel_set_encoding(appshare
->channel
, NULL
, &error
);
240 struct sipe_media_call
*call
= appshare
->stream
->call
;
242 SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s",
245 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
249 appshare
->rdp_channel_readable_watch_id
=
250 g_io_add_watch(appshare
->channel
, G_IO_IN
| G_IO_HUP
,
251 rdp_channel_readable_cb
, appshare
);
257 launch_rdp_client(struct sipe_appshare
*appshare
)
259 struct sipe_rdp_client
*client
= &appshare
->client
;
260 struct sipe_media_call
*call
= appshare
->stream
->call
;
261 GSocketAddress
*address
;
262 GError
*error
= NULL
;
265 address
= client
->get_listen_address_cb(client
);
267 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
271 appshare
->socket
= g_socket_new(g_socket_address_get_family(address
),
272 G_SOCKET_TYPE_STREAM
,
273 G_SOCKET_PROTOCOL_DEFAULT
,
276 SIPE_DEBUG_ERROR("Can't create RDP client listen socket: %s",
279 g_object_unref(address
);
280 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
284 g_socket_set_blocking(appshare
->socket
, FALSE
);
286 g_socket_bind(appshare
->socket
, address
, TRUE
, &error
);
287 g_object_unref(address
);
289 SIPE_DEBUG_ERROR("Can't bind to RDP client socket: %s",
292 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
296 g_socket_listen(appshare
->socket
, &error
);
298 SIPE_DEBUG_ERROR("Can't listen on RDP client socket: %s",
301 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
305 fd
= g_socket_get_fd(appshare
->socket
);
307 SIPE_DEBUG_ERROR_NOFORMAT("Invalid file descriptor for RDP client listen socket");
308 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
311 appshare
->channel
= g_io_channel_unix_new(fd
);
313 appshare
->rdp_channel_readable_watch_id
=
314 g_io_add_watch(appshare
->channel
, G_IO_IN
,
315 socket_connect_cb
, appshare
);
317 address
= g_socket_get_local_address(appshare
->socket
, &error
);
319 SIPE_DEBUG_ERROR("Couldn't get appshare socket address: %s",
322 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
326 if (!client
->launch_cb(client
, address
, appshare
->stream
)) {
327 SIPE_DEBUG_ERROR_NOFORMAT("Failed to launch RDP client.");
328 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
331 SIPE_DEBUG_INFO_NOFORMAT("RDP client launched.");
333 g_object_unref(address
);
337 rdp_client_channel_write(struct sipe_appshare
*appshare
)
340 GError
*error
= NULL
;
342 g_io_channel_write_chars(appshare
->channel
,
343 appshare
->rdp_channel_buffer_pos
,
344 appshare
->rdp_channel_buffer_len
,
345 &bytes_written
, &error
);
347 SIPE_DEBUG_ERROR("Couldn't write data to RDP client: %s",
353 g_io_channel_flush(appshare
->channel
, &error
);
355 if (g_error_matches(error
, G_IO_CHANNEL_ERROR
,
356 G_IO_CHANNEL_ERROR_PIPE
)) {
357 /* Ignore broken pipe here and wait for the call to be
358 * hung up upon G_IO_HUP in client_channel_cb(). */
363 SIPE_DEBUG_ERROR("Couldn't flush RDP channel: %s",
369 appshare
->rdp_channel_buffer_pos
+= bytes_written
;
370 appshare
->rdp_channel_buffer_len
-= bytes_written
;
372 return bytes_written
;
376 delayed_hangup_cb(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
379 struct sipe_media_call
*call
= data
;
381 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
385 rdp_channel_writable_cb(SIPE_UNUSED_PARAMETER GIOChannel
*channel
,
386 SIPE_UNUSED_PARAMETER GIOCondition condition
,
389 struct sipe_appshare
*appshare
= data
;
390 struct sipe_media_call
*call
= appshare
->stream
->call
;
392 if (rdp_client_channel_write(appshare
) < 0) {
393 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
397 if (appshare
->rdp_channel_buffer_len
== 0) {
398 // Writing done, disconnect writable watch.
399 appshare
->rdp_channel_writable_watch_id
= 0;
407 read_cb(struct sipe_media_stream
*stream
)
409 struct sipe_appshare
*appshare
= sipe_media_stream_get_data(stream
);
411 gssize bytes_written
= 0;
413 if (appshare
->rdp_channel_writable_watch_id
!= 0) {
414 // Data still in the buffer. Let the client read it first.
418 while (bytes_read
== (gint
)bytes_written
) {
419 bytes_read
= sipe_backend_media_stream_read(stream
,
420 (guint8
*)appshare
->rdp_channel_buffer
,
421 sizeof (appshare
->rdp_channel_buffer
));
422 if (bytes_read
== 0) {
426 appshare
->rdp_channel_buffer_pos
= appshare
->rdp_channel_buffer
;
427 appshare
->rdp_channel_buffer_len
= bytes_read
;
429 bytes_written
= rdp_client_channel_write(appshare
);
431 if (bytes_written
< 0) {
432 /* Don't deallocate stream while in its read callback.
433 * Schedule call hangup to be executed after we're back
434 * in the message loop. */
435 sipe_schedule_seconds(sipe_media_get_sipe_core_private(stream
->call
),
436 "appshare delayed hangup",
437 stream
->call
->backend_private
,
445 if (bytes_read
!= (gint
)bytes_written
) {
446 /* Schedule writing of the buffer's remainder to when
447 * RDP channel becomes writable again. */
448 appshare
->rdp_channel_writable_watch_id
=
449 g_io_add_watch(appshare
->channel
, G_IO_OUT
,
450 rdp_channel_writable_cb
,
456 writable_cb(struct sipe_media_stream
*stream
)
458 struct sipe_appshare
*appshare
= sipe_media_stream_get_data(stream
);
460 if (!appshare
->socket
) {
461 launch_rdp_client(appshare
);
466 accept_cb(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
469 struct sipe_appshare
*appshare
= data
;
470 appshare
->ask_ctx
= NULL
;
472 sipe_backend_media_accept(appshare
->stream
->call
->backend_private
, TRUE
);
476 decline_cb(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
479 struct sipe_appshare
*appshare
= data
;
480 appshare
->ask_ctx
= NULL
;
482 sipe_backend_media_hangup(appshare
->stream
->call
->backend_private
, TRUE
);
485 static struct sipe_user_ask_ctx
*
486 ask_accept_applicationsharing(struct sipe_core_private
*sipe_private
,
488 SipeUserAskCb accept_cb
,
489 SipeUserAskCb decline_cb
,
492 struct sipe_user_ask_ctx
*ctx
;
493 gchar
*alias
= sipe_buddy_get_alias(sipe_private
, from
);
494 gchar
*ask_msg
= g_strdup_printf(_("%s wants to start presenting"),
495 alias
? alias
: from
);
497 ctx
= sipe_user_ask(sipe_private
, ask_msg
,
498 _("Accept"), accept_cb
,
499 _("Decline"), decline_cb
,
508 static struct sipe_appshare
*
509 initialize_appshare(struct sipe_media_stream
*stream
)
511 struct sipe_appshare
*appshare
;
512 struct sipe_media_call
*call
;
513 struct sipe_core_private
*sipe_private
;
514 const gchar
*cmdline
;
517 sipe_private
= sipe_media_get_sipe_core_private(call
);
519 appshare
= g_new0(struct sipe_appshare
, 1);
520 appshare
->stream
= stream
;
522 sipe_media_stream_set_data(stream
, appshare
,
523 (GDestroyNotify
)sipe_appshare_free
);
525 cmdline
= sipe_backend_setting(SIPE_CORE_PUBLIC
,
526 SIPE_SETTING_RDP_CLIENT
);
527 if (is_empty(cmdline
))
529 appshare
->client
.cmdline
= g_strdup(cmdline
);
531 if (strstr(cmdline
, "xfreerdp")) {
532 sipe_appshare_xfreerdp_init(&appshare
->client
);
533 } else if (strstr(cmdline
, "remmina")) {
534 sipe_appshare_remmina_init(&appshare
->client
);
536 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
537 _("Application sharing error"),
538 _("Unknown remote desktop client configured."));
539 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
543 sipe_media_stream_add_extra_attribute(stream
,
544 "x-applicationsharing-session-id", "1");
545 sipe_media_stream_add_extra_attribute(stream
,
546 "x-applicationsharing-role", "viewer");
547 sipe_media_stream_add_extra_attribute(stream
,
548 "x-applicationsharing-media-type", "rdp");
550 stream
->read_cb
= read_cb
;
551 stream
->writable_cb
= writable_cb
;
557 process_incoming_invite_appshare(struct sipe_core_private
*sipe_private
,
560 struct sipe_media_call
*call
;
561 struct sipe_media_stream
*stream
;
562 struct sipe_appshare
*appshare
;
563 struct sdpmsg
*sdpmsg
;
566 sdpmsg
= sdpmsg_parse_msg(msg
->body
);
568 /* Skype for Business compatibility - ignore desktop video. */
571 struct sdpmedia
*media
= i
->data
;
576 label
= sipe_utils_nameval_find(media
->attributes
, "label");
578 if (sipe_strequal(media
->name
, "video") &&
579 sipe_strequal(label
, "applicationsharing-video")) {
580 sdpmsg
->media
= g_slist_remove(sdpmsg
->media
, media
);
581 sdpmedia_free(media
);
585 call
= process_incoming_invite_call_parsed_sdp(sipe_private
,
592 stream
= sipe_core_media_get_stream_by_id(call
, "applicationsharing");
594 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
598 appshare
= initialize_appshare(stream
);
603 from
= parse_from(sipmsg_find_header(msg
, "From"));
604 appshare
->ask_ctx
= ask_accept_applicationsharing(sipe_private
, from
,
613 connect_conference(struct sipe_core_private
*sipe_private
,
614 struct sipe_chat_session
*chat_session
)
616 struct sipe_media_call
*call
;
617 struct sipe_media_stream
*stream
;
620 chat_session
->appshare_ask_ctx
= NULL
;
622 uri
= sipe_conf_build_uri(chat_session
->id
, "applicationsharing");
624 call
= sipe_media_call_new(sipe_private
, uri
, NULL
,
626 SIPE_MEDIA_CALL_NO_UI
);
630 stream
= sipe_media_stream_add(call
, "applicationsharing",
631 SIPE_MEDIA_APPLICATION
,
632 SIPE_ICE_RFC_5245
, TRUE
, 0);
634 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
635 _("Application sharing error"),
636 _("Couldn't connect application sharing"));
637 sipe_backend_media_hangup(call
->backend_private
, FALSE
);
640 sipe_media_stream_add_extra_attribute(stream
, "connection", "new");
641 sipe_media_stream_add_extra_attribute(stream
, "setup", "active");
643 initialize_appshare(stream
);
647 sipe_core_appshare_connect_conference(struct sipe_core_public
*sipe_public
,
648 struct sipe_chat_session
*chat_session
,
649 gboolean user_must_accept
)
651 if (user_must_accept
) {
654 if (chat_session
->appshare_ask_ctx
) {
655 // Accept dialog already opened.
659 if (chat_session
->title
) {
660 from
= chat_session
->title
;
661 } else if (chat_session
->organizer
) {
662 from
= chat_session
->organizer
;
664 from
= chat_session
->id
;
667 chat_session
->appshare_ask_ctx
=
668 ask_accept_applicationsharing(SIPE_CORE_PRIVATE
,
670 (SipeUserAskCb
)connect_conference
,
674 connect_conference(SIPE_CORE_PRIVATE
, chat_session
);
679 sipe_appshare_get_role(struct sipe_media_call
*call
)
681 struct sipe_media_stream
*stream
;
683 g_return_val_if_fail(call
, SIPE_APPSHARE_ROLE_NONE
);
685 stream
= sipe_core_media_get_stream_by_id(call
, "applicationsharing");
688 struct sipe_appshare
*appshare
;
690 appshare
= sipe_media_stream_get_data(stream
);
692 #ifdef HAVE_APPSHARE_SERVER
693 return appshare
->server
? SIPE_APPSHARE_ROLE_PRESENTER
:
694 SIPE_APPSHARE_ROLE_VIEWER
;
696 return SIPE_APPSHARE_ROLE_VIEWER
;
697 #endif // HAVE_APPSHARE_SERVER
701 return SIPE_APPSHARE_ROLE_NONE
;
704 #ifdef HAVE_APPSHARE_SERVER
706 set_shared_display_area(rdpShadowServer
*server
, guint monitor_id
)
708 if (monitor_id
== 0) {
709 MONITOR_DEF monitors
[16];
713 UINT16 maxHeight
= 0;
715 monitor_count
= shadow_enum_monitors(monitors
, 16);
716 for (i
= 0; i
!= monitor_count
; ++i
) {
717 MONITOR_DEF
*m
= &monitors
[i
];
718 if (m
->right
> maxWidth
) {
721 if (m
->bottom
> maxHeight
) {
722 maxHeight
= m
->bottom
;
726 server
->subRect
.top
= 0;
727 server
->subRect
.left
= 0;
728 server
->subRect
.right
= maxWidth
;
729 server
->subRect
.bottom
= maxHeight
;
730 server
->shareSubRect
= TRUE
;
732 // Index 0 is reserved for "whole desktop" choice.
733 server
->selectedMonitor
= monitor_id
- 1;
738 candidate_pairs_established_cb(struct sipe_media_stream
*stream
)
740 struct sipe_appshare
*appshare
;
741 GSocketAddress
*address
;
742 GError
*error
= NULL
;
743 struct sockaddr_un native
;
744 rdpShadowServer
* server
;
745 const gchar
*server_error
= NULL
;
747 g_return_if_fail(sipe_strequal(stream
->id
, "applicationsharing"));
749 appshare
= sipe_media_stream_get_data(stream
);
751 server
= shadow_server_new();
753 server_error
= _("Could not create RDP server.");
755 server
->ipcSocket
= g_strdup_printf("%s/sipe-appshare-%u-%p",
756 g_get_user_runtime_dir(),
758 server
->authentication
= FALSE
;
759 set_shared_display_area(server
, appshare
->monitor_id
);
761 /* Experimentally determined cap on multifrag max request size
762 * Lync client would accept. Higher values result in a black
763 * screen being displayed on the remote end.
765 * See related https://github.com/FreeRDP/FreeRDP/pull/3669. */
766 server
->settings
->MultifragMaxRequestSize
= 0x3EFFFF;
768 if(shadow_server_init(server
) < 0) {
769 server_error
= _("Could not initialize RDP server.");
770 } else if(shadow_server_start(server
) < 0) {
771 server_error
= _("Could not start RDP server.");
775 struct sipe_core_private
*sipe_private
;
777 sipe_private
= sipe_media_get_sipe_core_private(stream
->call
);
778 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
779 _("Application sharing error"),
781 sipe_backend_media_hangup(stream
->call
->backend_private
, TRUE
);
783 shadow_server_uninit(server
);
784 shadow_server_free(server
);
789 appshare
->server
= server
;
790 appshare
->socket
= g_socket_new(G_SOCKET_FAMILY_UNIX
,
791 G_SOCKET_TYPE_STREAM
,
792 G_SOCKET_PROTOCOL_DEFAULT
,
795 SIPE_DEBUG_ERROR("Can't create RDP server socket: %s",
798 sipe_backend_media_hangup(stream
->call
->backend_private
, TRUE
);
802 g_socket_set_blocking(appshare
->socket
, FALSE
);
804 native
.sun_family
= AF_LOCAL
;
805 strncpy(native
.sun_path
, server
->ipcSocket
, sizeof (native
.sun_path
) - 1);
806 native
.sun_path
[sizeof (native
.sun_path
) - 1] = '\0';
807 address
= g_socket_address_new_from_native(&native
, sizeof native
);
809 g_socket_connect(appshare
->socket
, address
, NULL
, &error
);
811 SIPE_DEBUG_ERROR("Can't connect to RDP server: %s", error
->message
);
813 sipe_backend_media_hangup(stream
->call
->backend_private
, TRUE
);
817 appshare
->channel
= g_io_channel_unix_new(g_socket_get_fd(appshare
->socket
));
819 // No encoding for binary data
820 g_io_channel_set_encoding(appshare
->channel
, NULL
, &error
);
822 SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s",
825 sipe_backend_media_hangup(stream
->call
->backend_private
, TRUE
);
829 appshare
->rdp_channel_readable_watch_id
=
830 g_io_add_watch(appshare
->channel
, G_IO_IN
| G_IO_HUP
,
831 rdp_channel_readable_cb
, appshare
);
833 // Appshare structure initialized; don't call this again.
834 stream
->candidate_pairs_established_cb
= NULL
;
838 monitor_selected_cb(struct sipe_core_private
*sipe_private
, gchar
*with
,
841 struct sipe_media_call
*call
;
842 struct sipe_media_stream
*stream
;
843 struct sipe_appshare
*appshare
;
845 if (monitor_id
== SIPE_CHOICE_CANCELLED
) {
850 call
= sipe_media_call_new(sipe_private
, with
, NULL
, SIPE_ICE_RFC_5245
,
851 SIPE_MEDIA_CALL_INITIATOR
|
852 SIPE_MEDIA_CALL_NO_UI
);
855 stream
= sipe_media_stream_add(call
, "applicationsharing",
856 SIPE_MEDIA_APPLICATION
,
857 SIPE_ICE_RFC_5245
, TRUE
, 0);
859 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
860 _("Application sharing error"),
861 _("Couldn't initialize application sharing"));
862 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
866 stream
->candidate_pairs_established_cb
= candidate_pairs_established_cb
;
867 stream
->read_cb
= read_cb
;
869 sipe_media_stream_add_extra_attribute(stream
,
872 sipe_media_stream_add_extra_attribute(stream
,
873 "x-applicationsharing-session-id",
875 sipe_media_stream_add_extra_attribute(stream
,
876 "x-applicationsharing-role",
878 sipe_media_stream_add_extra_attribute(stream
,
879 "x-applicationsharing-media-type",
882 appshare
= g_new0(struct sipe_appshare
, 1);
883 appshare
->stream
= stream
;
884 appshare
->monitor_id
= monitor_id
;
886 sipe_media_stream_set_data(stream
, appshare
,
887 (GDestroyNotify
)sipe_appshare_free
);
891 present_monitor_choice(struct sipe_core_public
*sipe_public
, const gchar
*who
)
893 MONITOR_DEF monitors
[16];
896 shadow_subsystem_set_entry_builtin("X11");
897 monitor_count
= shadow_enum_monitors(monitors
, 16);
899 if (monitor_count
== 1) {
900 // Don't show choice dialog, share whole desktop right away.
901 monitor_selected_cb(SIPE_CORE_PRIVATE
, g_strdup(who
), 0);
903 GSList
*choices
= NULL
;
906 choices
= g_slist_append(choices
, g_strdup(_("Whole desktop")));
908 for (i
= 0; i
!= monitor_count
; ++i
) {
909 MONITOR_DEF
*mon
= &monitors
[i
];
910 gchar
*str
= g_strdup_printf("%dx%d @ [%d, %d]",
911 mon
->right
- mon
->left
,
912 mon
->bottom
- mon
->top
,
916 choices
= g_slist_append(choices
, str
);
919 sipe_user_ask_choice(SIPE_CORE_PRIVATE
, _("Monitor to share"),
921 (SipeUserAskChoiceCb
)monitor_selected_cb
,
924 g_slist_free_full(choices
, g_free
);
929 sipe_core_appshare_share_desktop(struct sipe_core_public
*sipe_public
,
932 present_monitor_choice(sipe_public
, with
);
934 #endif // HAVE_APPSHARE_SERVER