FreeRDP/client/X11/xfreerdp.c

1346 lines
34 KiB
C
Raw Normal View History

2011-08-07 17:52:40 +04:00
/**
2012-10-09 07:02:04 +04:00
* FreeRDP: A Remote Desktop Protocol Implementation
2011-08-07 17:52:40 +04:00
* X11 Client
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2012 HP Development Company, LLC
2011-08-07 17:52:40 +04:00
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#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>
2011-09-19 02:44:49 +04:00
#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/event.h>
2011-09-29 08:37:30 +04:00
#include <freerdp/utils/signal.h>
#include <freerdp/utils/passphrase.h>
#include <freerdp/client/cliprdr.h>
#include <freerdp/client/channels.h>
#include <freerdp/rail.h>
#include <winpr/synch.h>
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 HANDLE 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;
}
}
else
{
XSetFunction(xfi->display, xfi->gc, GXcopy);
XSetFillStyle(xfi->display, xfi->gc, FillSolid);
XSetForeground(xfi->display, xfi->gc, 0);
XFillRectangle(xfi->display, xfi->drawable, xfi->gc,
0, 0, xfi->width, xfi->height);
}
}
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_process_x_events(freerdp* instance)
2011-08-07 17:52:40 +04:00
{
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;
2012-06-14 19:16:29 +04:00
memset(&xevent, 0x00, sizeof(xevent));
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;
Multiple RAIL fixes/improvements 1. Linked Window Manager Maximize/Minimize and Restore operations to those from the Server Rail Window so that they are in sync 2. Enable things like "CTRL-ALT-DELETE" and "WindowsKey-L" to show the full desktop window again since the desktop is not actively monitored since this was still trying to draw to the rail window without updating the size of the window to accomodate the full workspace area. 3. Changed local window coordinates to be based on the visibileOffsetX/Y- while moving server window based on WindowOffsetX/Y. I have seen various issues regarding this when trying to use a maximized window where this is a disconnect between local window coordinates and remote window coordinates. This change clears these things up. 4. Commented the XShapeCombineRectangles calls - this can cause issues where the entire window is not visible and it does not currently play well with the changes from #3. The gain here is greater than the loss. 5. Draw the initial workspace correctly when running across multiple monitors. The correct size was always used, but the window was only starting on the current monitor and thus could draw the window off of the viewable area. Known Issues: Although the changes for #2 worked well in the stable branch that I developed from - the desktop window shown once the rail windows are destroyed does not respond to input unless I minimize/restore the window. Once the window starts responding to input - you can hit cancel to close the desktop window and return to your rail windows again(or launch task manager, etc.). This is still a big step in the right direction as xfreerdp is now correctly acting when the rail server stops Actively Monitoring the desktop. XShapeCombineRectangles needs to be revisited, most windows applications will give you a rectangular window anyways.
2012-08-04 02:35:17 +04:00
xfi->attribs.bit_gravity = NorthWestGravity;
xfi->attribs.win_gravity = NorthWestGravity;
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->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
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*);
2012-09-03 00:34:06 +04:00
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)
fprintf(stderr, "%s:%d: failed to parse arguments.\n", __FILE__, __LINE__);
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
2012-09-03 00:34:06 +04:00
if (settings->authentication_only)
{
/* Check --authonly has a username and password. */
2012-09-03 00:34:06 +04:00
if (settings->username == NULL )
{
fprintf(stderr, "--authonly, but no -u username. Please provide one.\n");
exit(1);
}
2012-09-03 00:34:06 +04:00
if (settings->password == NULL )
{
fprintf(stderr, "--authonly, but no -p password. Please provide one.\n");
exit(1);
}
fprintf(stderr, "%s:%d: Authenication only. Don't connect to X.\n", __FILE__, __LINE__);
2012-09-03 00:34:06 +04:00
/* Avoid XWindows initialization and configuration below. */
return true;
}
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);
Multiple RAIL fixes/improvements 1. Linked Window Manager Maximize/Minimize and Restore operations to those from the Server Rail Window so that they are in sync 2. Enable things like "CTRL-ALT-DELETE" and "WindowsKey-L" to show the full desktop window again since the desktop is not actively monitored since this was still trying to draw to the rail window without updating the size of the window to accomodate the full workspace area. 3. Changed local window coordinates to be based on the visibileOffsetX/Y- while moving server window based on WindowOffsetX/Y. I have seen various issues regarding this when trying to use a maximized window where this is a disconnect between local window coordinates and remote window coordinates. This change clears these things up. 4. Commented the XShapeCombineRectangles calls - this can cause issues where the entire window is not visible and it does not currently play well with the changes from #3. The gain here is greater than the loss. 5. Draw the initial workspace correctly when running across multiple monitors. The correct size was always used, but the window was only starting on the current monitor and thus could draw the window off of the viewable area. Known Issues: Although the changes for #2 worked well in the stable branch that I developed from - the desktop window shown once the rail windows are destroyed does not respond to input unless I minimize/restore the window. Once the window starts responding to input - you can hit cancel to close the desktop window and return to your rail windows again(or launch task manager, etc.). This is still a big step in the right direction as xfreerdp is now correctly acting when the rail server stops Actively Monitoring the desktop. XShapeCombineRectangles needs to be revisited, most windows applications will give you a rectangular window anyways.
2012-08-04 02:35:17 +04:00
xfi->WM_STATE = XInternAtom(xfi->display, "WM_STATE", 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__)
__asm volatile
(
/* The EBX (or RBX register on x86_64) is used for the PIC base address
and must not be corrupted by our inline assembly. */
#if defined(__i386__)
"mov %%ebx, %%esi;"
"cpuid;"
"xchg %%ebx, %%esi;"
#else
"mov %%rbx, %%rsi;"
"cpuid;"
"xchg %%rbx, %%rsi;"
#endif
: "=a" (*eax), "=S" (*ebx), "=c" (*ecx), "=d" (*edx)
: "0" (info)
);
#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)
{
#ifdef WITH_SSE2
2012-03-06 18:42:57 +04:00
uint32 cpu;
#endif
2011-08-07 17:52:40 +04:00
xfInfo* xfi;
XGCValues gcv;
rdpCache* cache;
rdpChannels* channels;
RFX_CONTEXT* rfx_context = NULL;
2012-03-06 18:42:57 +04:00
NSC_CONTEXT* nsc_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)
2012-03-06 18:42:57 +04:00
{
nsc_context = (void*) nsc_context_new();
xfi->nsc_context = nsc_context;
}
}
2011-08-26 02:07:52 +04:00
#ifdef WITH_SSE2
2012-03-06 18:42:57 +04:00
/* detect only if needed */
cpu = xf_detect_cpu();
if (rfx_context)
rfx_context_set_cpu_opt(rfx_context, cpu);
if (nsc_context)
nsc_context_set_cpu_opt(nsc_context, 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);
2012-09-03 00:34:06 +04:00
XSetFunction(xfi->display, xfi->gc, GXcopy);
XSetFillStyle(xfi->display, xfi->gc, FillSolid);
XSetForeground(xfi->display, xfi->gc, BlackPixelOfScreen(xfi->screen));
XFillRectangle(xfi->display, xfi->primary, xfi->gc, 0, 0, xfi->width, xfi->height);
2012-09-03 00:34:06 +04:00
XFlush(xfi->display);
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, instance->settings->from_stdin) == 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
{
void* entry = NULL;
rdpChannels* channels = (rdpChannels*) user_data;
2011-08-07 17:52:40 +04:00
entry = freerdp_channels_find_static_virtual_channel_entry(name);
if (entry)
{
if (freerdp_channels_client_load(channels, settings, entry, plugin_data) == 0)
{
printf("loading channel %s (static)\n", name);
return 1;
}
}
printf("loading channel %s (plugin)\n", name);
freerdp_channels_load_plugin(channels, settings, name, plugin_data);
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;
if (xfi->gc != NULL)
{
XFreeGC(xfi->display, xfi->gc);
xfi->gc = 0;
}
if (xfi->gc_mono != NULL)
{
XFreeGC(xfi->display, xfi->gc_mono);
xfi->gc_mono = 0;
}
2012-02-02 06:11:46 +04:00
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
2012-03-16 19:42:45 +04:00
if (xfi->bitmap_mono)
{
XFreePixmap(xfi->display, xfi->bitmap_mono);
xfi->bitmap_mono = 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;
}
if (xfi->nsc_context)
{
nsc_context_free(xfi->nsc_context);
xfi->nsc_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
boolean status = freerdp_connect(instance);
/* Connection succeeded. --authonly ? */
if (instance->settings->authentication_only) {
freerdp_disconnect(instance);
fprintf(stderr, "%s:%d: Authentication only, exit status %d\n", __FILE__, __LINE__, !status);
exit(!status);
}
if (!status)
{
xf_free(((xfContext*) instance->context)->xfi);
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_process_x_events(instance) != true)
2011-08-07 17:52:40 +04:00
{
printf("Closed from X\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
}
FILE *fin = fopen("/tmp/tsmf.tid", "rt");
if(fin)
{
int thid = 0;
fscanf(fin, "%d", &thid);
fclose(fin);
pthread_kill((pthread_t) thid, SIGUSR1);
FILE *fin1 = fopen("/tmp/tsmf.tid", "rt");
int timeout = 5;
while (fin1)
{
fclose(fin1);
sleep(1);
timeout--;
if (timeout <= 0)
{
unlink("/tmp/tsmf.tid");
pthread_kill((pthread_t) thid, SIGKILL);
break;
}
fin1 = fopen("/tmp/tsmf.tid", "rt");
}
}
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
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 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);
g_thread_count--;
if (g_thread_count < 1)
ReleaseSemaphore(g_sem, 1, NULL);
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;
/* License 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 = CreateSemaphore(NULL, 0, 1, NULL);
2011-08-07 17:52:40 +04:00
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)
{
WaitForSingleObject(g_sem, INFINITE);
2011-08-07 17:52:40 +04:00
}
pthread_join(thread, NULL);
pthread_detach(thread);
freerdp_context_free(instance);
freerdp_free(instance);
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
}