FreeRDP/client/X11/xfreerdp.c

1237 lines
32 KiB
C
Raw Normal View History

2011-08-07 17:52:40 +04:00
/**
* FreeRDP: A Remote Desktop Protocol Client
* X11 Client
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <X11/Xlib.h>
#include <X11/Xutil.h>
2011-09-29 08:33:16 +04:00
#ifdef WITH_XCURSOR
#include <X11/Xcursor/Xcursor.h>
#endif
#ifdef WITH_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif
2011-09-19 02:44:49 +04:00
#include <errno.h>
2011-08-26 06:01:33 +04:00
#include <stdio.h>
#include <stdlib.h>
2011-08-09 04:31:21 +04:00
#include <locale.h>
2011-09-19 02:44:49 +04:00
#include <unistd.h>
#include <string.h>
#include <termios.h>
2011-09-19 02:44:49 +04:00
#include <pthread.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/select.h>
#include <freerdp/constants.h>
2011-10-04 13:40:03 +04:00
#include <freerdp/codec/nsc.h>
#include <freerdp/codec/rfx.h>
#include <freerdp/codec/color.h>
#include <freerdp/codec/bitmap.h>
2011-08-07 17:52:40 +04:00
#include <freerdp/utils/args.h>
#include <freerdp/utils/memory.h>
#include <freerdp/utils/semaphore.h>
2011-09-29 08:33:16 +04:00
#include <freerdp/utils/memory.h>
2011-08-07 17:52:40 +04:00
#include <freerdp/utils/event.h>
2011-09-29 08:37:30 +04:00
#include <freerdp/utils/signal.h>
#include <freerdp/utils/passphrase.h>
2011-08-11 07:07:03 +04:00
#include <freerdp/plugins/cliprdr.h>
#include <freerdp/rail.h>
2011-08-07 17:52:40 +04:00
2011-08-26 02:07:52 +04:00
#include "xf_gdi.h"
#include "xf_rail.h"
2011-09-20 12:27:59 +04:00
#include "xf_tsmf.h"
2011-08-07 17:52:40 +04:00
#include "xf_event.h"
2011-09-23 07:37:17 +04:00
#include "xf_cliprdr.h"
#include "xf_monitor.h"
#include "xf_graphics.h"
2011-08-26 06:01:33 +04:00
#include "xf_keyboard.h"
2011-08-07 17:52:40 +04:00
#include "xfreerdp.h"
static freerdp_sem g_sem;
2011-08-07 17:52:40 +04:00
static int g_thread_count = 0;
static uint8 g_disconnect_reason = 0;
2011-08-07 17:52:40 +04:00
2011-09-20 12:27:59 +04:00
static long xv_port = 0;
static const size_t password_size = 512;
2011-09-20 12:27:59 +04:00
2011-08-07 17:52:40 +04:00
struct thread_data
{
freerdp* instance;
};
int xf_process_client_args(rdpSettings* settings, const char* opt, const char* val, void* user_data);
int xf_process_plugin_args(rdpSettings* settings, const char* name, RDP_PLUGIN_DATA* plugin_data, void* user_data);
void xf_context_new(freerdp* instance, rdpContext* context)
{
context->channels = freerdp_channels_new();
}
void xf_context_free(freerdp* instance, rdpContext* context)
{
}
void xf_sw_begin_paint(rdpContext* context)
2011-08-07 17:52:40 +04:00
{
rdpGdi* gdi = context->gdi;
2011-08-07 17:52:40 +04:00
gdi->primary->hdc->hwnd->invalid->null = 1;
gdi->primary->hdc->hwnd->ninvalid = 0;
2011-08-07 17:52:40 +04:00
}
void xf_sw_end_paint(rdpContext* context)
2011-08-07 17:52:40 +04:00
{
rdpGdi* gdi;
2011-08-07 17:52:40 +04:00
xfInfo* xfi;
sint32 x, y;
uint32 w, h;
2011-08-07 17:52:40 +04:00
xfi = ((xfContext*) context)->xfi;
gdi = context->gdi;
2011-08-07 17:52:40 +04:00
if (xfi->remote_app != true)
{
if (xfi->complex_regions != true)
{
if (gdi->primary->hdc->hwnd->invalid->null)
return;
x = gdi->primary->hdc->hwnd->invalid->x;
y = gdi->primary->hdc->hwnd->invalid->y;
w = gdi->primary->hdc->hwnd->invalid->w;
h = gdi->primary->hdc->hwnd->invalid->h;
XPutImage(xfi->display, xfi->primary, xfi->gc, xfi->image, x, y, x, y, w, h);
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, w, h, x, y);
}
else
{
int i;
int ninvalid;
HGDI_RGN cinvalid;
if (gdi->primary->hdc->hwnd->ninvalid < 1)
return;
ninvalid = gdi->primary->hdc->hwnd->ninvalid;
cinvalid = gdi->primary->hdc->hwnd->cinvalid;
for (i = 0; i < ninvalid; i++)
{
x = cinvalid[i].x;
y = cinvalid[i].y;
w = cinvalid[i].w;
h = cinvalid[i].h;
XPutImage(xfi->display, xfi->primary, xfi->gc, xfi->image, x, y, x, y, w, h);
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, w, h, x, y);
}
XFlush(xfi->display);
}
}
else
{
if (gdi->primary->hdc->hwnd->invalid->null)
return;
x = gdi->primary->hdc->hwnd->invalid->x;
y = gdi->primary->hdc->hwnd->invalid->y;
w = gdi->primary->hdc->hwnd->invalid->w;
h = gdi->primary->hdc->hwnd->invalid->h;
xf_rail_paint(xfi, context->rail, x, y, x + w - 1, y + h - 1);
}
2011-08-07 17:52:40 +04:00
}
void xf_sw_desktop_resize(rdpContext* context)
{
xfInfo* xfi;
rdpSettings* settings;
xfi = ((xfContext*) context)->xfi;
settings = xfi->instance->settings;
if (xfi->fullscreen != true)
{
rdpGdi* gdi = context->gdi;
gdi_resize(gdi, xfi->width, xfi->height);
if (xfi->image)
{
xfi->image->data = NULL;
XDestroyImage(xfi->image);
xfi->image = XCreateImage(xfi->display, xfi->visual, xfi->depth, ZPixmap, 0,
(char*) gdi->primary_buffer, gdi->width, gdi->height, xfi->scanline_pad, 0);
}
}
}
void xf_hw_begin_paint(rdpContext* context)
{
xfInfo* xfi;
xfi = ((xfContext*) context)->xfi;
xfi->hdc->hwnd->invalid->null = 1;
xfi->hdc->hwnd->ninvalid = 0;
}
void xf_hw_end_paint(rdpContext* context)
{
xfInfo* xfi;
sint32 x, y;
uint32 w, h;
xfi = ((xfContext*) context)->xfi;
if (xfi->remote_app)
{
if (xfi->hdc->hwnd->invalid->null)
return;
x = xfi->hdc->hwnd->invalid->x;
y = xfi->hdc->hwnd->invalid->y;
w = xfi->hdc->hwnd->invalid->w;
h = xfi->hdc->hwnd->invalid->h;
xf_rail_paint(xfi, context->rail, x, y, x + w - 1, y + h - 1);
}
}
void xf_hw_desktop_resize(rdpContext* context)
{
xfInfo* xfi;
boolean same;
rdpSettings* settings;
xfi = ((xfContext*) context)->xfi;
settings = xfi->instance->settings;
if (xfi->fullscreen != true)
{
xfi->width = settings->width;
xfi->height = settings->height;
if (xfi->window)
xf_ResizeDesktopWindow(xfi, xfi->window, settings->width, settings->height);
if (xfi->primary)
{
same = (xfi->primary == xfi->drawing) ? true : false;
XFreePixmap(xfi->display, xfi->primary);
xfi->primary = XCreatePixmap(xfi->display, xfi->drawable,
xfi->width, xfi->height, xfi->depth);
if (same)
xfi->drawing = xfi->primary;
}
}
}
2011-08-07 17:52:40 +04:00
boolean xf_get_fds(freerdp* instance, void** rfds, int* rcount, void** wfds, int* wcount)
{
xfInfo* xfi = ((xfContext*) instance->context)->xfi;
2011-08-07 17:52:40 +04:00
rfds[*rcount] = (void*)(long)(xfi->xfds);
(*rcount)++;
return true;
2011-08-07 17:52:40 +04:00
}
boolean xf_check_fds(freerdp* instance, fd_set* set)
{
XEvent xevent;
xfInfo* xfi = ((xfContext*) instance->context)->xfi;
while (XPending(xfi->display))
{
memset(&xevent, 0, sizeof(xevent));
XNextEvent(xfi->display, &xevent);
if (xf_event_process(instance, &xevent) != true)
return false;
}
2011-08-07 17:52:40 +04:00
return true;
2011-08-07 17:52:40 +04:00
}
void xf_create_window(xfInfo* xfi)
{
XEvent xevent;
char* win_title;
2011-10-07 23:50:07 +04:00
int width, height;
2011-10-07 23:50:07 +04:00
width = xfi->width;
height = xfi->height;
xfi->attribs.background_pixel = BlackPixelOfScreen(xfi->screen);
xfi->attribs.border_pixel = WhitePixelOfScreen(xfi->screen);
xfi->attribs.backing_store = xfi->primary ? NotUseful : Always;
xfi->attribs.override_redirect = xfi->fullscreen;
xfi->attribs.colormap = xfi->colormap;
xfi->attribs.bit_gravity = ForgetGravity;
xfi->attribs.win_gravity = StaticGravity;
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
if (xfi->instance->settings->window_title != NULL)
{
win_title = xstrdup(xfi->instance->settings->window_title);
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
}
else if (xfi->instance->settings->port == 3389)
{
win_title = xmalloc(1 + sizeof("FreeRDP: ") + strlen(xfi->instance->settings->hostname));
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
sprintf(win_title, "FreeRDP: %s", xfi->instance->settings->hostname);
}
else
{
win_title = xmalloc(1 + sizeof("FreeRDP: ") + strlen(xfi->instance->settings->hostname) + sizeof(":00000"));
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
sprintf(win_title, "FreeRDP: %s:%i", xfi->instance->settings->hostname, xfi->instance->settings->port);
}
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
xfi->window = xf_CreateDesktopWindow(xfi, win_title, width, height, xfi->decorations);
xfree(win_title);
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
if (xfi->parent_window)
XReparentWindow(xfi->display, xfi->window->handle, xfi->parent_window, 0, 0);
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
if (xfi->fullscreen)
xf_SetWindowFullscreen(xfi, xfi->window, xfi->fullscreen);
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
/* wait for VisibilityNotify */
do
{
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
XMaskEvent(xfi->display, VisibilityChangeMask, &xevent);
}
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
while (xevent.type != VisibilityNotify);
xfi->unobscured = (xevent.xvisibility.state == VisibilityUnobscured);
XSetWMProtocols(xfi->display, xfi->window->handle, &(xfi->WM_DELETE_WINDOW), 1);
xfi->drawable = xfi->window->handle;
}
2011-08-24 08:46:34 +04:00
void xf_toggle_fullscreen(xfInfo* xfi)
{
Pixmap contents = 0;
contents = XCreatePixmap(xfi->display, xfi->window->handle, xfi->width, xfi->height, xfi->depth);
XCopyArea(xfi->display, xfi->primary, contents, xfi->gc, 0, 0, xfi->width, xfi->height, 0, 0);
2011-09-14 06:20:04 +04:00
XDestroyWindow(xfi->display, xfi->window->handle);
xfi->fullscreen = (xfi->fullscreen) ? false : true;
xf_create_window(xfi);
2011-08-24 08:46:34 +04:00
XCopyArea(xfi->display, contents, xfi->primary, xfi->gc, 0, 0, xfi->width, xfi->height, 0, 0);
XFreePixmap(xfi->display, contents);
}
boolean xf_get_pixmap_info(xfInfo* xfi)
{
int i;
int vi_count;
int pf_count;
XVisualInfo* vi;
XVisualInfo* vis;
XVisualInfo template;
XPixmapFormatValues* pf;
XPixmapFormatValues* pfs;
2012-02-27 21:08:38 +04:00
XWindowAttributes window_attributes;
2011-08-24 08:46:34 +04:00
pfs = XListPixmapFormats(xfi->display, &pf_count);
if (pfs == NULL)
{
printf("xf_get_pixmap_info: XListPixmapFormats failed\n");
return 1;
}
for (i = 0; i < pf_count; i++)
{
pf = pfs + i;
if (pf->depth == xfi->depth)
{
xfi->bpp = pf->bits_per_pixel;
xfi->scanline_pad = pf->scanline_pad;
break;
}
}
XFree(pfs);
memset(&template, 0, sizeof(template));
template.class = TrueColor;
template.screen = xfi->screen_number;
2012-02-27 21:08:38 +04:00
if (XGetWindowAttributes(xfi->display, RootWindowOfScreen(xfi->screen), &window_attributes) == 0)
{
printf("xf_get_pixmap_info: XGetWindowAttributes failed\n");
return false;
}
2011-08-24 08:46:34 +04:00
vis = XGetVisualInfo(xfi->display, VisualClassMask | VisualScreenMask, &template, &vi_count);
if (vis == NULL)
{
printf("xf_get_pixmap_info: XGetVisualInfo failed\n");
return false;
2011-08-24 08:46:34 +04:00
}
vi = NULL;
2011-08-24 08:46:34 +04:00
for (i = 0; i < vi_count; i++)
{
vi = vis + i;
2012-02-27 21:08:38 +04:00
if (vi->visual == window_attributes.visual)
2011-08-24 08:46:34 +04:00
{
xfi->visual = vi->visual;
break;
}
}
if (vi)
{
/*
* Detect if the server visual has an inverted colormap
* (BGR vs RGB, or red being the least significant byte)
*/
if (vi->red_mask & 0xFF)
{
xfi->clrconv->invert = true;
}
}
2011-08-24 08:46:34 +04:00
XFree(vis);
if ((xfi->visual == NULL) || (xfi->scanline_pad == 0))
{
return false;
2011-08-24 08:46:34 +04:00
}
return true;
2011-08-24 08:46:34 +04:00
}
static int (*_def_error_handler)(Display*, XErrorEvent*);
int xf_error_handler(Display* d, XErrorEvent* ev)
{
char buf[256];
int do_abort = true;
XGetErrorText(d, ev->error_code, buf, sizeof(buf));
printf("%s", buf);
if (do_abort)
abort();
_def_error_handler(d, ev);
return false;
}
int _xf_error_handler(Display* d, XErrorEvent* ev)
{
/*
* ungrab the keyboard, in case a debugger is running in
* another window. This make xf_error_handler() a potential
* debugger breakpoint.
*/
XUngrabKeyboard(d, CurrentTime);
return xf_error_handler(d, ev);
}
/**
* Callback given to freerdp_connect() to process the pre-connect operations.
* It will parse the command line parameters given to xfreerdp (using freerdp_parse_args())
* and fill the rdp_freerdp structure (instance) with the appropriate options to use for the connection.
*
* @param instance - pointer to the rdp_freerdp structure that contains the connection's parameters, and will
* be filled with the appropriate informations.
*
* @return true if successful. false otherwise.
* Can exit with error code XF_EXIT_PARSE_ARGUMENTS if there is an error in the parameters.
*/
2011-08-07 17:52:40 +04:00
boolean xf_pre_connect(freerdp* instance)
{
xfInfo* xfi;
boolean bitmap_cache;
2011-08-07 17:52:40 +04:00
rdpSettings* settings;
int arg_parse_result;
2011-08-07 17:52:40 +04:00
xfi = (xfInfo*) xzalloc(sizeof(xfInfo));
((xfContext*) instance->context)->xfi = xfi;
xfi->_context = instance->context;
xfi->context = (xfContext*) instance->context;
xfi->context->settings = instance->settings;
xfi->instance = instance;
arg_parse_result = freerdp_parse_args(instance->settings, instance->context->argc,instance->context->argv,
xf_process_plugin_args, instance->context->channels, xf_process_client_args, xfi);
if (arg_parse_result < 0)
{
if (arg_parse_result == FREERDP_ARGS_PARSE_FAILURE)
printf("failed to parse arguments.\n");
exit(XF_EXIT_PARSE_ARGUMENTS);
}
2011-08-07 17:52:40 +04:00
settings = instance->settings;
bitmap_cache = settings->bitmap_cache;
2011-08-07 17:52:40 +04:00
settings->os_major_type = OSMAJORTYPE_UNIX;
settings->os_minor_type = OSMINORTYPE_NATIVE_XSERVER;
settings->order_support[NEG_DSTBLT_INDEX] = true;
settings->order_support[NEG_PATBLT_INDEX] = true;
settings->order_support[NEG_SCRBLT_INDEX] = true;
settings->order_support[NEG_OPAQUE_RECT_INDEX] = true;
settings->order_support[NEG_DRAWNINEGRID_INDEX] = false;
settings->order_support[NEG_MULTIDSTBLT_INDEX] = false;
settings->order_support[NEG_MULTIPATBLT_INDEX] = false;
settings->order_support[NEG_MULTISCRBLT_INDEX] = false;
settings->order_support[NEG_MULTIOPAQUERECT_INDEX] = true;
settings->order_support[NEG_MULTI_DRAWNINEGRID_INDEX] = false;
settings->order_support[NEG_LINETO_INDEX] = true;
settings->order_support[NEG_POLYLINE_INDEX] = true;
settings->order_support[NEG_MEMBLT_INDEX] = bitmap_cache;
2012-02-13 03:12:28 +04:00
2012-02-13 04:41:39 +04:00
settings->order_support[NEG_MEM3BLT_INDEX] = (settings->sw_gdi) ? true : false;
2012-02-13 03:12:28 +04:00
settings->order_support[NEG_MEMBLT_V2_INDEX] = bitmap_cache;
settings->order_support[NEG_MEM3BLT_V2_INDEX] = false;
settings->order_support[NEG_SAVEBITMAP_INDEX] = false;
settings->order_support[NEG_GLYPH_INDEX_INDEX] = true;
settings->order_support[NEG_FAST_INDEX_INDEX] = true;
settings->order_support[NEG_FAST_GLYPH_INDEX] = true;
2012-02-13 02:14:59 +04:00
settings->order_support[NEG_POLYGON_SC_INDEX] = (settings->sw_gdi) ? false : true;
settings->order_support[NEG_POLYGON_CB_INDEX] = (settings->sw_gdi) ? false : true;
settings->order_support[NEG_ELLIPSE_SC_INDEX] = false;
settings->order_support[NEG_ELLIPSE_CB_INDEX] = false;
2011-08-07 17:52:40 +04:00
freerdp_channels_pre_connect(xfi->_context->channels, instance);
2011-08-07 17:52:40 +04:00
xfi->display = XOpenDisplay(NULL);
if (xfi->display == NULL)
{
printf("xf_pre_connect: failed to open display: %s\n", XDisplayName(NULL));
printf("Please check that the $DISPLAY environment variable is properly set.\n");
return false;
}
if (xfi->debug)
{
printf("Enabling X11 debug mode.\n");
XSynchronize(xfi->display, true);
_def_error_handler = XSetErrorHandler(_xf_error_handler);
}
xfi->_NET_WM_ICON = XInternAtom(xfi->display, "_NET_WM_ICON", False);
xfi->_MOTIF_WM_HINTS = XInternAtom(xfi->display, "_MOTIF_WM_HINTS", False);
xfi->_NET_CURRENT_DESKTOP = XInternAtom(xfi->display, "_NET_CURRENT_DESKTOP", False);
xfi->_NET_WORKAREA = XInternAtom(xfi->display, "_NET_WORKAREA", False);
xfi->_NET_WM_STATE = XInternAtom(xfi->display, "_NET_WM_STATE", False);
xfi->_NET_WM_STATE_FULLSCREEN = XInternAtom(xfi->display, "_NET_WM_STATE_FULLSCREEN", False);
xfi->_NET_WM_WINDOW_TYPE = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE", False);
xfi->_NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
xfi->_NET_WM_WINDOW_TYPE_DIALOG = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
xfi->_NET_WM_WINDOW_TYPE_POPUP= XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_POPUP", False);
xfi->_NET_WM_WINDOW_TYPE_UTILITY = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
xfi->_NET_WM_WINDOW_TYPE_DROPDOWN_MENU = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False);
xfi->_NET_WM_STATE_SKIP_TASKBAR = XInternAtom(xfi->display, "_NET_WM_STATE_SKIP_TASKBAR", False);
xfi->_NET_WM_STATE_SKIP_PAGER = XInternAtom(xfi->display, "_NET_WM_STATE_SKIP_PAGER", False);
xfi->_NET_WM_MOVERESIZE = XInternAtom(xfi->display, "_NET_WM_MOVERESIZE", False);
xfi->_NET_MOVERESIZE_WINDOW = XInternAtom(xfi->display, "_NET_MOVERESIZE_WINDOW", False);
xfi->WM_PROTOCOLS = XInternAtom(xfi->display, "WM_PROTOCOLS", False);
xfi->WM_DELETE_WINDOW = XInternAtom(xfi->display, "WM_DELETE_WINDOW", False);
xf_kbd_init(xfi);
xfi->clrconv = freerdp_clrconv_new(CLRCONV_ALPHA);
instance->context->cache = cache_new(instance->settings);
xfi->xfds = ConnectionNumber(xfi->display);
xfi->screen_number = DefaultScreen(xfi->display);
xfi->screen = ScreenOfDisplay(xfi->display, xfi->screen_number);
xfi->depth = DefaultDepthOfScreen(xfi->screen);
xfi->big_endian = (ImageByteOrder(xfi->display) == MSBFirst);
xfi->mouse_motion = settings->mouse_motion;
xfi->complex_regions = true;
2011-10-07 23:50:07 +04:00
xfi->decorations = settings->decorations;
2011-08-19 19:12:30 +04:00
xfi->fullscreen = settings->fullscreen;
xfi->grab_keyboard = settings->grab_keyboard;
xfi->fullscreen_toggle = true;
xfi->sw_gdi = settings->sw_gdi;
xfi->parent_window = (Window) settings->parent_window_xid;
xf_detect_monitors(xfi, settings);
return true;
}
void cpuid(unsigned info, unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx)
{
#ifdef __GNUC__
#if defined(__i386__) || defined(__x86_64__)
*eax = info;
__asm volatile
("mov %%ebx, %%edi;" /* 32bit PIC: don't clobber ebx */
"cpuid;"
"mov %%ebx, %%esi;"
"mov %%edi, %%ebx;"
:"+a" (*eax), "=S" (*ebx), "=c" (*ecx), "=d" (*edx)
: :"edi");
#endif
#endif
}
uint32 xf_detect_cpu()
{
unsigned int eax, ebx, ecx, edx = 0;
uint32 cpu_opt = 0;
cpuid(1, &eax, &ebx, &ecx, &edx);
if (edx & (1<<26))
{
DEBUG("SSE2 detected");
cpu_opt |= CPU_SSE2;
}
return cpu_opt;
}
/**
* Callback given to freerdp_connect() to perform post-connection operations.
* It will be called only if the connection was initialized properly, and will continue the initialization based on the
* newly created connection.
*/
2011-08-07 17:52:40 +04:00
boolean xf_post_connect(freerdp* instance)
{
xfInfo* xfi;
XGCValues gcv;
rdpCache* cache;
rdpChannels* channels;
RFX_CONTEXT* rfx_context = NULL;
2011-08-07 17:52:40 +04:00
xfi = ((xfContext*) instance->context)->xfi;
cache = instance->context->cache;
channels = xfi->_context->channels;
2011-08-07 17:52:40 +04:00
if (xf_get_pixmap_info(xfi) != true)
return false;
2011-10-21 02:18:45 +04:00
xf_register_graphics(instance->context->graphics);
if (xfi->sw_gdi)
{
rdpGdi* gdi;
uint32 flags;
flags = CLRCONV_ALPHA;
if (xfi->bpp > 16)
flags |= CLRBUF_32BPP;
else
flags |= CLRBUF_16BPP;
gdi_init(instance, flags, NULL);
gdi = instance->context->gdi;
xfi->primary_buffer = gdi->primary_buffer;
rfx_context = gdi->rfx_context;
}
else
{
xfi->srcBpp = instance->settings->color_depth;
xf_gdi_register_update_callbacks(instance->update);
xfi->hdc = gdi_CreateDC(xfi->clrconv, xfi->bpp);
if (instance->settings->rfx_codec)
{
rfx_context = (void*) rfx_context_new();
xfi->rfx_context = rfx_context;
}
if (instance->settings->ns_codec)
xfi->nsc_context = (void*) nsc_context_new();
}
2011-08-26 02:07:52 +04:00
if (rfx_context)
{
#ifdef WITH_SSE2
/* detect only if needed */
rfx_context_set_cpu_opt(rfx_context, xf_detect_cpu());
#endif
}
2011-10-07 23:50:07 +04:00
xfi->width = instance->settings->width;
xfi->height = instance->settings->height;
xf_create_window(xfi);
memset(&gcv, 0, sizeof(gcv));
xfi->modifier_map = XGetModifierMapping(xfi->display);
xfi->gc = XCreateGC(xfi->display, xfi->drawable, GCGraphicsExposures, &gcv);
xfi->primary = XCreatePixmap(xfi->display, xfi->drawable, xfi->width, xfi->height, xfi->depth);
xfi->drawing = xfi->primary;
xfi->bitmap_mono = XCreatePixmap(xfi->display, xfi->drawable, 8, 8, 1);
xfi->gc_mono = XCreateGC(xfi->display, xfi->bitmap_mono, GCGraphicsExposures, &gcv);
XSetForeground(xfi->display, xfi->gc, BlackPixelOfScreen(xfi->screen));
XFillRectangle(xfi->display, xfi->primary, xfi->gc, 0, 0, xfi->width, xfi->height);
xfi->image = XCreateImage(xfi->display, xfi->visual, xfi->depth, ZPixmap, 0,
(char*) xfi->primary_buffer, xfi->width, xfi->height, xfi->scanline_pad, 0);
xfi->bmp_codec_none = (uint8*) xmalloc(64 * 64 * 4);
if (xfi->sw_gdi)
{
instance->update->BeginPaint = xf_sw_begin_paint;
instance->update->EndPaint = xf_sw_end_paint;
instance->update->DesktopResize = xf_sw_desktop_resize;
}
else
{
instance->update->BeginPaint = xf_hw_begin_paint;
instance->update->EndPaint = xf_hw_end_paint;
instance->update->DesktopResize = xf_hw_desktop_resize;
}
2011-08-07 17:52:40 +04:00
pointer_cache_register_callbacks(instance->update);
2011-09-29 08:33:16 +04:00
if (xfi->sw_gdi != true)
{
2011-11-24 22:01:34 +04:00
glyph_cache_register_callbacks(instance->update);
brush_cache_register_callbacks(instance->update);
bitmap_cache_register_callbacks(instance->update);
offscreen_cache_register_callbacks(instance->update);
palette_cache_register_callbacks(instance->update);
}
instance->context->rail = rail_new(instance->settings);
rail_register_update_callbacks(instance->context->rail, instance->update);
xf_rail_register_callbacks(xfi, instance->context->rail);
freerdp_channels_post_connect(channels, instance);
2011-08-07 17:52:40 +04:00
2011-09-20 12:27:59 +04:00
xf_tsmf_init(xfi, xv_port);
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
xf_cliprdr_init(xfi, channels);
2011-09-20 12:27:59 +04:00
return true;
2011-08-07 17:52:40 +04:00
}
/** Callback set in the rdp_freerdp structure, and used to get the user's password,
* if required to establish the connection.
* This function is actually called in credssp_ntlmssp_client_init()
* @see rdp_server_accept_nego() and rdp_check_fds()
* @param instance - pointer to the rdp_freerdp structure that contains the connection settings
* @param username - unused
* @param password - on return: pointer to a character string that will be filled by the password entered by the user.
* Note that this character string will be allocated inside the function, and needs to be deallocated by the caller
* using xfree(), even in case this function fails.
* @param domain - unused
* @return true if a password was successfully entered. See freerdp_passphrase_read() for more details.
*/
boolean xf_authenticate(freerdp* instance, char** username, char** password, char** domain)
{
// FIXME: seems this callback may be called when 'username' is not known.
// But it doesn't do anything to fix it...
*password = xmalloc(password_size * sizeof(char));
if (freerdp_passphrase_read("Password: ", *password, password_size) == NULL)
return false;
return true;
}
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
* when the connection requires it.
* This function will actually be called by tls_verify_certificate().
* @see rdp_client_connect() and tls_connect()
* @param instance - pointer to the rdp_freerdp structure that contains the connection settings
* @param subject
* @param issuer
* @param fingerprint
* @return true if the certificate is trusted. false otherwise.
*/
boolean xf_verify_certificate(freerdp* instance, char* subject, char* issuer, char* fingerprint)
{
char answer;
printf("Certificate details:\n");
printf("\tSubject: %s\n", subject);
printf("\tIssuer: %s\n", issuer);
printf("\tThumbprint: %s\n", fingerprint);
printf("The above X.509 certificate could not be verified, possibly because you do not have "
"the CA certificate in your certificate store, or the certificate has expired. "
"Please look at the documentation on how to create local certificate store for a private CA.\n");
while (1)
{
printf("Do you trust the above certificate? (Y/N) ");
answer = fgetc(stdin);
if (answer == 'y' || answer == 'Y')
{
return true;
}
else if (answer == 'n' || answer == 'N')
{
break;
}
printf("\n");
}
return false;
}
/** Used to parse xfreerdp-specific commandline parameters.
* This function is provided as a parameter to freerdp_parse_args(), that will call it
* each time a parameter is not recognized by the library.
* @see xf_pre_connect(), where freerdp_parse_args() is called.
*
* @param settings
* @param opt
* @param val
* @param user_data
* @return the number of parameters that where taken into account.
* 0 means no options recognized.
* freerdp_parse_args() will use this number to move forward in the parameters parsing.
*/
int xf_process_client_args(rdpSettings* settings, const char* opt, const char* val, void* user_data)
2011-08-26 06:01:33 +04:00
{
2011-09-20 12:27:59 +04:00
int argc = 0;
xfInfo* xfi = (xfInfo*) user_data;
2011-09-20 12:27:59 +04:00
2011-08-26 06:01:33 +04:00
if (strcmp("--kbd-list", opt) == 0)
{
int i;
RDP_KEYBOARD_LAYOUT* layouts;
2011-08-26 06:01:33 +04:00
2012-02-19 07:04:28 +04:00
layouts = freerdp_keyboard_get_layouts(RDP_KEYBOARD_LAYOUT_TYPE_STANDARD);
2011-08-26 06:01:33 +04:00
printf("\nKeyboard Layouts\n");
for (i = 0; layouts[i].code; i++)
{
2011-08-26 06:01:33 +04:00
printf("0x%08X\t%s\n", layouts[i].code, layouts[i].name);
xfree(layouts[i].name);
}
xfree(layouts);
2011-08-26 06:01:33 +04:00
2012-02-19 07:04:28 +04:00
layouts = freerdp_keyboard_get_layouts(RDP_KEYBOARD_LAYOUT_TYPE_VARIANT);
2011-08-26 06:01:33 +04:00
printf("\nKeyboard Layout Variants\n");
for (i = 0; layouts[i].code; i++)
{
2011-08-26 06:01:33 +04:00
printf("0x%08X\t%s\n", layouts[i].code, layouts[i].name);
xfree(layouts[i].name);
}
xfree(layouts);
2011-08-26 06:01:33 +04:00
2012-02-19 07:04:28 +04:00
layouts = freerdp_keyboard_get_layouts(RDP_KEYBOARD_LAYOUT_TYPE_IME);
2011-08-26 06:01:33 +04:00
printf("\nKeyboard Input Method Editors (IMEs)\n");
for (i = 0; layouts[i].code; i++)
{
2011-08-26 06:01:33 +04:00
printf("0x%08X\t%s\n", layouts[i].code, layouts[i].name);
xfree(layouts[i].name);
}
xfree(layouts);
2011-08-26 06:01:33 +04:00
exit(0);
}
2011-09-20 12:27:59 +04:00
else if (strcmp("--xv-port", opt) == 0)
{
xv_port = atoi(val);
argc = 2;
}
else if (strcmp("--dbg-x11", opt) == 0)
{
xfi->debug = true;
argc = 1;
}
2011-08-26 06:01:33 +04:00
2011-09-20 12:27:59 +04:00
return argc;
2011-08-26 06:01:33 +04:00
}
/** Used to load plugins based on the commandline parameters.
* This function is provided as a parameter to freerdp_parse_args(), that will call it
* each time a plugin name is found on the command line.
* This function just calls freerdp_channels_load_plugin() for the given plugin, and always returns 1.
* @see xf_pre_connect(), where freerdp_parse_args() is called.
*
* @param settings
* @param name
* @param plugin_data
* @param user_data
* @return 1
*/
2011-08-18 01:28:26 +04:00
int xf_process_plugin_args(rdpSettings* settings, const char* name, RDP_PLUGIN_DATA* plugin_data, void* user_data)
2011-08-07 17:52:40 +04:00
{
rdpChannels* channels = (rdpChannels*) user_data;
2011-08-07 17:52:40 +04:00
printf("loading plugin %s\n", name);
freerdp_channels_load_plugin(channels, settings, name, plugin_data);
// FIXME we should check the return code for freerdp_channels_load_plugin()
// and report any error to the caller. freerdp_parse_args() actually expect to get
// an error code when there is a loading error.
// Is it as easy as "return freerdp_channels_load_plugin()" ?
2011-08-07 17:52:40 +04:00
return 1;
}
int xf_receive_channel_data(freerdp* instance, int channelId, uint8* data, int size, int flags, int total_size)
2011-08-07 17:52:40 +04:00
{
return freerdp_channels_data(instance, channelId, data, size, flags, total_size);
2011-08-07 17:52:40 +04:00
}
void xf_process_channel_event(rdpChannels* chanman, freerdp* instance)
2011-08-07 17:52:40 +04:00
{
xfInfo* xfi;
2011-08-18 01:28:26 +04:00
RDP_EVENT* event;
2011-08-07 17:52:40 +04:00
xfi = ((xfContext*) instance->context)->xfi;
event = freerdp_channels_pop_event(chanman);
2011-08-07 17:52:40 +04:00
if (event)
{
switch (event->event_class)
{
case RDP_EVENT_CLASS_RAIL:
xf_process_rail_event(xfi, chanman, event);
break;
2011-09-20 12:27:59 +04:00
case RDP_EVENT_CLASS_TSMF:
xf_process_tsmf_event(xfi, event);
break;
2011-09-20 12:27:59 +04:00
case RDP_EVENT_CLASS_CLIPRDR:
2011-09-23 07:37:17 +04:00
xf_process_cliprdr_event(xfi, event);
2011-08-07 17:52:40 +04:00
break;
2011-09-20 12:27:59 +04:00
2011-08-07 17:52:40 +04:00
default:
break;
}
2011-09-20 12:27:59 +04:00
2011-08-07 17:52:40 +04:00
freerdp_event_free(event);
}
}
void xf_window_free(xfInfo* xfi)
{
rdpContext* context = xfi->instance->context;
XFreeModifiermap(xfi->modifier_map);
xfi->modifier_map = 0;
XFreeGC(xfi->display, xfi->gc);
xfi->gc = 0;
2012-02-02 06:11:46 +04:00
XFreeGC(xfi->display, xfi->gc_mono);
xfi->gc_mono = 0;
xfreerdp: a quick workaround for some issues with TS Remote App. Currently in Remote App mode we have no option to interact with the remote desktop host before the first RAIL window is created. In many situations this interaction possibility is absolutely required. One example is that screen which gets displayed if another user is logged on. It requires clicking a button in pre-RAIL mode so that the currently logged on user gets notified to confirm or deny the connection. Another example is the option to log on graphically (e.g. for hosts that don't support NLA) without predefined credentials. Also if the administrator sets the "User must change password at next logon" option there is currently no way to do this in TS Remote App mode. This change basically lets xfreerdp create the main window in Remote App mode like in a normal session and xfi->remote_app is not set to true initially. As soon as the rail exec result event or the first rail window creation request is received (whatever comes first) the main window gets destroyed and xfi->remote_app is set to true. The second change is to disconnect immediately if the rail exec result event reports an error, e.g. if the specified app cannot be found or if it is not in the list of allowed applications. This fixes FreeRDP github issue #143 and issue #308. I'm aware that this is not the most elegant solution but it is definitely an improvement and probably good enough for 1.0. A nicer solution would be hiding the main window and only displaying it if no rail exec result or rail window creation event is received after a certain timeout ...
2012-01-02 13:27:04 +04:00
if (xfi->window != NULL)
{
xf_DestroyWindow(xfi, xfi->window);
xfi->window = NULL;
}
if (xfi->primary)
{
XFreePixmap(xfi->display, xfi->primary);
xfi->primary = 0;
}
2011-09-07 09:51:29 +04:00
if (xfi->image)
{
xfi->image->data = NULL;
XDestroyImage(xfi->image);
xfi->image = NULL;
}
if (context != NULL)
2011-09-07 09:51:29 +04:00
{
cache_free(context->cache);
context->cache = NULL;
rail_free(context->rail);
context->rail = NULL;
2011-09-07 09:51:29 +04:00
}
2011-09-07 10:19:11 +04:00
2011-10-19 00:50:40 +04:00
if (xfi->rfx_context)
{
rfx_context_free(xfi->rfx_context);
xfi->rfx_context = NULL;
}
freerdp_clrconv_free(xfi->clrconv);
if (xfi->hdc)
gdi_DeleteDC(xfi->hdc);
2011-09-20 12:27:59 +04:00
xf_tsmf_uninit(xfi);
2011-09-23 07:37:17 +04:00
xf_cliprdr_uninit(xfi);
}
void xf_free(xfInfo* xfi)
{
xf_window_free(xfi);
xfree(xfi->bmp_codec_none);
XCloseDisplay(xfi->display);
2012-02-02 06:11:46 +04:00
2011-08-13 07:46:00 +04:00
xfree(xfi);
}
/** Main loop for the rdp connection.
* It will be run from the thread's entry point (thread_func()).
* It initiates the connection, and will continue to run until the session ends,
* processing events as they are received.
* @param instance - pointer to the rdp_freerdp structure that contains the session's settings
* @return A code from the enum XF_EXIT_CODE (0 if successfull)
*/
2011-08-13 07:46:00 +04:00
int xfreerdp_run(freerdp* instance)
2011-08-07 17:52:40 +04:00
{
int i;
int fds;
xfInfo* xfi;
2011-08-07 17:52:40 +04:00
int max_fds;
int rcount;
int wcount;
2011-12-12 03:05:32 +04:00
int ret = 0;
2011-08-07 17:52:40 +04:00
void* rfds[32];
void* wfds[32];
fd_set rfds_set;
fd_set wfds_set;
2011-12-12 03:05:32 +04:00
int select_status;
rdpChannels* channels;
2011-12-12 03:05:32 +04:00
struct timeval timeout;
2011-08-07 17:52:40 +04:00
memset(rfds, 0, sizeof(rfds));
memset(wfds, 0, sizeof(wfds));
2011-12-12 03:05:32 +04:00
memset(&timeout, 0, sizeof(struct timeval));
2011-08-07 17:52:40 +04:00
if (!freerdp_connect(instance))
return XF_EXIT_CONN_FAILED;
2011-08-13 07:46:00 +04:00
xfi = ((xfContext*) instance->context)->xfi;
channels = instance->context->channels;
2011-08-07 17:52:40 +04:00
while (!xfi->disconnect && !freerdp_shall_disconnect(instance))
2011-08-07 17:52:40 +04:00
{
rcount = 0;
wcount = 0;
if (freerdp_get_fds(instance, rfds, &rcount, wfds, &wcount) != true)
2011-08-07 17:52:40 +04:00
{
printf("Failed to get FreeRDP file descriptor\n");
ret = XF_EXIT_CONN_FAILED;
2011-08-07 17:52:40 +04:00
break;
}
if (freerdp_channels_get_fds(channels, instance, rfds, &rcount, wfds, &wcount) != true)
2011-08-07 17:52:40 +04:00
{
printf("Failed to get channel manager file descriptor\n");
ret = XF_EXIT_CONN_FAILED;
2011-08-07 17:52:40 +04:00
break;
}
if (xf_get_fds(instance, rfds, &rcount, wfds, &wcount) != true)
2011-08-07 17:52:40 +04:00
{
printf("Failed to get xfreerdp file descriptor\n");
ret = XF_EXIT_CONN_FAILED;
2011-08-07 17:52:40 +04:00
break;
}
max_fds = 0;
FD_ZERO(&rfds_set);
FD_ZERO(&wfds_set);
2011-08-07 17:52:40 +04:00
for (i = 0; i < rcount; i++)
{
fds = (int)(long)(rfds[i]);
if (fds > max_fds)
max_fds = fds;
FD_SET(fds, &rfds_set);
}
if (max_fds == 0)
break;
2011-12-12 03:05:32 +04:00
timeout.tv_sec = 5;
select_status = select(max_fds + 1, &rfds_set, &wfds_set, NULL, &timeout);
if (select_status == 0)
{
//freerdp_send_keep_alive(instance);
continue;
}
else if (select_status == -1)
2011-08-07 17:52:40 +04:00
{
/* these are not really errors */
if (!((errno == EAGAIN) ||
(errno == EWOULDBLOCK) ||
(errno == EINPROGRESS) ||
(errno == EINTR))) /* signal occurred */
{
printf("xfreerdp_run: select failed\n");
2011-08-07 17:52:40 +04:00
break;
}
}
if (freerdp_check_fds(instance) != true)
2011-08-07 17:52:40 +04:00
{
printf("Failed to check FreeRDP file descriptor\n");
break;
}
if (xf_check_fds(instance, &rfds_set) != true)
2011-08-07 17:52:40 +04:00
{
printf("Failed to check xfreerdp file descriptor\n");
2011-08-07 17:52:40 +04:00
break;
}
if (freerdp_channels_check_fds(channels, instance) != true)
2011-08-07 17:52:40 +04:00
{
printf("Failed to check channel manager file descriptor\n");
break;
}
xf_process_channel_event(channels, instance);
2011-08-07 17:52:40 +04:00
}
if (!ret)
ret = freerdp_error_info(instance);
freerdp_channels_close(channels, instance);
freerdp_channels_free(channels);
freerdp_disconnect(instance);
2011-09-07 09:51:29 +04:00
gdi_free(instance);
xf_free(xfi);
2011-08-07 17:52:40 +04:00
freerdp_context_free(instance);
freerdp_free(instance);
return ret;
2011-08-07 17:52:40 +04:00
}
/** Entry point for the thread that will deal with the session.
* It just calls xfreerdp_run() using the given instance as parameter.
* @param - pointer to a thread_data structure that contains the initialized connection.
*/
2011-08-07 17:52:40 +04:00
void* thread_func(void* param)
{
struct thread_data* data;
data = (struct thread_data*) param;
g_disconnect_reason = xfreerdp_run(data->instance);
2011-08-07 17:52:40 +04:00
xfree(data);
pthread_detach(pthread_self());
g_thread_count--;
if (g_thread_count < 1)
2011-08-13 07:46:00 +04:00
freerdp_sem_signal(g_sem);
2011-08-07 17:52:40 +04:00
2012-02-02 06:11:46 +04:00
pthread_exit(NULL);
2011-08-07 17:52:40 +04:00
}
static uint8 exit_code_from_disconnect_reason(uint32 reason)
{
if (reason == 0 ||
(reason >= XF_EXIT_PARSE_ARGUMENTS && reason <= XF_EXIT_CONN_FAILED))
return reason;
/* Licence error set */
else if (reason >= 0x100 && reason <= 0x10A)
reason -= 0x100 + XF_EXIT_LICENSE_INTERNAL;
/* RDP protocol error set */
else if (reason >= 0x10c9 && reason <= 0x1193)
reason = XF_EXIT_RDP;
/* There's no need to test protocol-independent codes: they match */
else if (!(reason <= 0xB))
reason = XF_EXIT_UNKNOWN;
return reason;
}
2011-08-07 17:52:40 +04:00
int main(int argc, char* argv[])
{
pthread_t thread;
freerdp* instance;
struct thread_data* data;
2011-08-07 17:52:40 +04:00
2011-09-28 09:27:14 +04:00
freerdp_handle_signals();
2011-08-09 04:31:21 +04:00
setlocale(LC_ALL, "");
freerdp_channels_global_init();
2011-08-07 17:52:40 +04:00
g_sem = freerdp_sem_new(1);
instance = freerdp_new();
instance->PreConnect = xf_pre_connect;
instance->PostConnect = xf_post_connect;
instance->Authenticate = xf_authenticate;
instance->VerifyCertificate = xf_verify_certificate;
2011-08-07 17:52:40 +04:00
instance->ReceiveChannelData = xf_receive_channel_data;
instance->context_size = sizeof(xfContext);
instance->ContextNew = (pContextNew) xf_context_new;
instance->ContextFree = (pContextFree) xf_context_free;
freerdp_context_new(instance);
2011-08-07 17:52:40 +04:00
instance->context->argc = argc;
instance->context->argv = argv;
instance->settings->sw_gdi = false;
2011-08-07 17:52:40 +04:00
data = (struct thread_data*) xzalloc(sizeof(struct thread_data));
data->instance = instance;
g_thread_count++;
pthread_create(&thread, 0, thread_func, data);
while (g_thread_count > 0)
{
freerdp_sem_wait(g_sem);
}
freerdp_channels_global_uninit();
2011-08-07 17:52:40 +04:00
return exit_code_from_disconnect_reason(g_disconnect_reason);
2011-08-07 17:52:40 +04:00
}