Added SeamlessRDP support: Merged seamlessrdp-branch

git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/trunk/rdesktop@1198 423420c4-83ab-492f-b58f-81f9feb106b5
This commit is contained in:
Peter Åstrand 2006-03-27 08:17:34 +00:00
commit 6b6ebaad66
17 changed files with 2152 additions and 71 deletions

View File

@ -25,7 +25,7 @@ LDVNC = @LDVNC@
VNCLINK = @VNCLINK@
SOUNDOBJ = @SOUNDOBJ@
RDPOBJ = tcp.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o
RDPOBJ = tcp.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o seamless.o
X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.o
VNCOBJ = vnc/rdp2vnc.o vnc/vnc.o vnc/xkeymap.o vnc/x11stubs.o
@ -82,7 +82,7 @@ proto:
bitmap.c cache.c channels.c cliprdr.c disk.c mppc.c ewmhints.c \
iso.c licence.c mcs.c orders.c parallel.c printer.c printercache.c \
pstcache.c rdesktop.c rdp5.c rdp.c rdpdr.c rdpsnd.c rdpsnd_oss.c \
secure.c serial.c tcp.c xclip.c xkeymap.c xwin.c lspci.c >> proto.h
secure.c serial.c tcp.c xclip.c xkeymap.c xwin.c lspci.c seamless.c >> proto.h
cat proto.tail >> proto.h
.PHONY: clean

View File

@ -21,7 +21,7 @@
#include "rdesktop.h"
#define MAX_CHANNELS 5
#define MAX_CHANNELS 6
#define CHANNEL_CHUNK_LENGTH 1600
#define CHANNEL_FLAG_FIRST 0x01
#define CHANNEL_FLAG_LAST 0x02
@ -87,7 +87,7 @@ channel_send(STREAM s, VCHANNEL * channel)
s_pop_layer(s, channel_hdr);
length = s->end - s->p - 8;
DEBUG_CLIPBOARD(("channel_send, length = %d\n", length));
DEBUG_CHANNEL(("channel_send, length = %d\n", length));
thislength = MIN(length, CHANNEL_CHUNK_LENGTH);
/* Note: In the original clipboard implementation, this number was
@ -102,7 +102,7 @@ channel_send(STREAM s, VCHANNEL * channel)
out_uint32_le(s, length);
out_uint32_le(s, flags);
data = s->end = s->p + thislength;
DEBUG_CLIPBOARD(("Sending %d bytes with FLAG_FIRST\n", thislength));
DEBUG_CHANNEL(("Sending %d bytes with FLAG_FIRST\n", thislength));
sec_send_to_channel(s, g_encryption ? SEC_ENCRYPT : 0, channel->mcs_id);
/* subsequent segments copied (otherwise would have to generate headers backwards) */
@ -114,7 +114,7 @@ channel_send(STREAM s, VCHANNEL * channel)
if (channel->flags & CHANNEL_OPTION_SHOW_PROTOCOL)
flags |= CHANNEL_FLAG_SHOW_PROTOCOL;
DEBUG_CLIPBOARD(("Sending %d bytes with flags %d\n", thislength, flags));
DEBUG_CHANNEL(("Sending %d bytes with flags %d\n", thislength, flags));
s = sec_init(g_encryption ? SEC_ENCRYPT : 0, thislength + 8);
out_uint32_le(s, length);

View File

@ -684,6 +684,15 @@ AC_ARG_WITH(debug-clipboard,
fi
])
AC_ARG_WITH(debug-channel,
[ --with-debug-channel enable debugging of virtual channel code],
[
if test $withval != "no";
then
AC_DEFINE(WITH_DEBUG_CHANNEL,1)
fi
])
#
# target-specific stuff

View File

@ -412,3 +412,15 @@ enum RDP_INPUT_DEVICE
#define exDiscReasonLicenseErrClientEncryption 0x0108
#define exDiscReasonLicenseCantUpgradeLicense 0x0109
#define exDiscReasonLicenseNoRemoteConnections 0x010a
/* SeamlessRDP constants */
#define SEAMLESSRDP_NOTYETMAPPED -1
#define SEAMLESSRDP_NORMAL 0
#define SEAMLESSRDP_MINIMIZED 1
#define SEAMLESSRDP_MAXIMIZED 2
#define SEAMLESSRDP_POSITION_TIMER 200000
#define SEAMLESSRDP_CREATE_MODAL 0x0001
#define SEAMLESSRDP_HELLO_RECONNECT 0x0001
#define SEAMLESSRDP_HELLO_HIDDEN 0x0002

View File

@ -52,3 +52,35 @@
* More fancy homepage.
* Enhance documentation. Write a FAQ.
* SeamlessRDP mode
* Add a client to server message for starting additional
applications.
* Detect the "Windows Security" dialog.
* Support cmd.exe.
* Support for Input Contexts.
* Enhanced support for WM_DELETE_WINDOW: Instead of terminating
rdesktop, close the window on the server side.
* Clipboard support needs to be adapted - it uses g_wnd now.
* Systray support.
* Support for transferring the application taskbar icon.
* Better support for non-EWMH window managers.
* Support for non-rectangular windows.
* The focus handling of menus is a bit crude.
* Support for sending focus information from client to server.
* Implement something similiar to explhook.dll - support for running
explorer.exe in non-shell mode.

View File

@ -76,6 +76,13 @@ toggled at any time using Ctrl-Alt-Enter.
Force the server to send screen updates as bitmaps rather than using
higher-level drawing operations.
.TP
.BR "-A"
Enable SeamlessRDP. In this mode, rdesktop creates a X11 window for
each window on the server side. This mode requires the SeamlessRDP
server side component. When using this option, you should specify a
startup shell which launches the desired application through
SeamlessRDP. Example: rdesktop -A -s 'seamlessrdpshell notepad'.
.TP
.BR "-B"
Use the BackingStore of the Xserver instead of the integrated one in
rdesktop.

266
doc/seamlessrdp-channel.txt Normal file
View File

@ -0,0 +1,266 @@
TODO
----
* Command for executing new programs.
* Commands for changing z order and focus.
* Command for transferring icon.
* Think about protocol version management
* Try to assure that messages aren't repeated or are sent for hidden windows.
Overview
========
The protocol is a line based protocol following this simple syntax:
OPERATION,SERIAL[,ARG1[,ARG2,[...]]]
Each operation will have an increasing serial number. The initial value is
implementation defined.
The goal is to have a generic protocol that can be used for other display
systems (e.g. VNC) as well.
One line may not exceed 1024 bytes, including newline.
The protocol has no concept of hidden or unmapped windows. A window does not
exist unless it is visible. Note that a minimized window is an exception to
this rule.
The protocol has no way of indicating failure, meaning that all commands are
expected to succeed. If a command fails, the receiving end must send a
corresponding command back, indicating the actual state.
Data types
==========
Window ID are written in hex, like 0x4321.
Window positions can be negative. This happens when a window is moved
outside the desktop.
All integers fit inside 32 bits.
Strings are sent in UTF-8 and do not contain any characters less than 0x20 or
the character , (comma).
Server to Client Operations
===========================
CREATE
------
Allocate structures for a new window.
Syntax:
CREATE,<SERIAL>,<ID>,<PARENT>,<FLAGS>
Indicates that a window has appeared on the server. If PARENT is non-zero then
the new window is a child of that window (it's transient for it). The special
value 0xFFFFFFFF for PARENT means that the window is a popup window without a
parent. It's commonly used for splash screens, tool tips and context menus.
Note that very little information is included in this message. Things like
title and state will come in subsequent messages. This message should only
be used to allocate data structures for the new window.
DESTROY
-------
Remove a window.
Syntax:
DESTROY,<SERIAL>,<ID>,<FLAGS>
Remove the window and deallocate all associated structures.
POSITION
--------
Move and/or resize a window.
Syntax:
POSITION,<SERIAL>,<ID>,<X>,<Y>,<WIDTH>,<HEIGHT>,<FLAGS>
If the window isn't visible yet (because a
STATE hasn't been set or because it's minimized), you must store the position
and size. A new POSITION is not guaranteed to be sent when the window changes
state.
TITLE
-----
Sets a window title.
Syntax:
TITLE,<SERIAL>,<ID>,<TITLE>,<FLAGS>
The text is guaranteed to be stripped of control characters (< 0x20).
Note that this has the same requirement as POSITION, that the title needs to
be stored for newly created windows until a STATE is sent. It is however not
guaranteed that a TITLE will be sent before the first STATE.
ZCHANGE
-------
The window moved in z order.
Syntax:
ZCHANGE,<SERIAL>,<ID>,<BEHIND>,<FLAGS>
The window with the id ID is behind the window with the id BEHIND. If
BEHIND is 0, then this window should be brought to the front.
STATE
-----
Changes the window's state and/or title.
Syntax:
STATE,<SERIAL>,<ID>,<STATE>,<FLAGS>
State can have one of three values:
0 : "Normal" window.
1 : Minimized.
2 : Maximized.
The initial STATE for a window will always be preceeded by one CREATE and one
POSITION. Optionally, a TITLE may also be sent before the first STATE.
DEBUG
-----
Debug output from the server component.
Syntax:
DEBUG,<SERIAL>,<STRING>
Used for debugging.
SYNCBEGIN
---------
Indicates that a synchronisation has begun.
Syntax:
SYNCBEGIN,<SERIAL>,<FLAGS>
Sent when the server starts a synchronisation. The client should flush all
information at this point.
SYNCEND
-------
Indicates that a synchronisation is complete.
Syntac:
SYNCEND,<SERIAL>,<FLAGS>
Sent when the last message that is part of the synchronisation has been sent.
This may be followed by duplicate messages and/or messages referring invalid
windows that were queued up during the synchronisation.
HELLO
-----
Initial message sent by server.
Syntax:
HELLO,<SERIAL>,<FLAGS>
The server starts each connection by sending this message. Normally the client
will react by sending a SYNC back to the server.
Flags:
0x0001 : This is a reconnect to an existing session.
0x0002 : The desktop is currently hidden (see HIDE).
ACK
---
Acknowledgement of a request to manipulate a window.
Syntax:
ACK,<SERIAL>,<ACKSERIAL>
Whenever one of the commands POSITION, ZCHANGE, STATE or FOCUS is executed on
the server, the server will send an ACK back to the client. The purpose of this
is to inform the client of when the operation was actually performed, allowing
high latencies in the channel.
HIDE
----
The desktop has become hidden on the server.
Syntax:
HIDE,<SERIAL>,<FLAGS>
This message is sent when the desktop on the server is obscured by something
that cannot be tracked. The client should remove all windows and display the
entire desktop, allowing the user to handle whatever is blocking the view.
Note that updates to windows will still be sent as the windows still exist on
the server, they are merely not visible.
UNHIDE
------
The desktop has been restored on the server.
Syntax:
UNHIDE,<SERIAL>,<FLAGS>
This message is sent some time after a HIDE, indicating that the windowed view
has been restored. If the client has dropped all information about windows then
it can send a SYNC to re-enumerate them.
Client to Server Operations
===========================
SYNC
----
Request a synchronisation of window information.
Syntax:
SYNC,<SERIAL>,<FLAGS>
For each window, the server will send CREATE, POSITION and STATE, in that
order, just as if the window was created. Note that a TITLE may also,
optionally, be sent before the STATE.
POSITION
--------
Identical to the command sent from server to client.
TITLE
-----
Identical to the command sent from server to client.
ZCHANGE
-------
Identical to the command sent from server to client.
STATE
-----
Identical to the command sent from server to client.
FOCUS
-----
Sets which window has input focus.
Syntax:
FOCUS,<SERIAL>,<ID>,<FLAGS>
Changes which window that will recieve the keyboard events. Note that this
might cause the window to change z order.

View File

@ -22,17 +22,29 @@
*/
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include "rdesktop.h"
#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
#define _NET_WM_STATE_ADD 1 /* add/set property */
#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
extern Display *g_display;
static Atom g_net_wm_state_maximized_vert_atom, g_net_wm_state_maximized_horz_atom,
g_net_wm_state_hidden_atom, g_net_wm_name_atom, g_utf8_string_atom,
g_net_wm_state_skip_taskbar_atom, g_net_wm_state_skip_pager_atom, g_net_wm_state_modal_atom;
Atom g_net_wm_state_atom, g_net_wm_desktop_atom;
/*
Get window property value (32 bit format)
Returns zero on success, -1 on error
*/
static int
get_property_value(char *propname, long max_length,
unsigned long *nitems_return, unsigned char **prop_return)
get_property_value(Window wnd, char *propname, long max_length,
unsigned long *nitems_return, unsigned char **prop_return, int nowarn)
{
int result;
Atom property;
@ -47,7 +59,7 @@ get_property_value(char *propname, long max_length,
return (-1);
}
result = XGetWindowProperty(g_display, DefaultRootWindow(g_display), property, 0, /* long_offset */
result = XGetWindowProperty(g_display, wnd, property, 0, /* long_offset */
max_length, /* long_length */
False, /* delete */
AnyPropertyType, /* req_type */
@ -63,7 +75,8 @@ get_property_value(char *propname, long max_length,
if (actual_type_return == None || actual_format_return == 0)
{
fprintf(stderr, "Root window is missing property %s\n", propname);
if (!nowarn)
fprintf(stderr, "Window is missing property %s\n", propname);
return (-1);
}
@ -93,7 +106,9 @@ get_current_desktop(void)
unsigned char *prop_return;
int current_desktop;
if (get_property_value("_NET_CURRENT_DESKTOP", 1, &nitems_return, &prop_return) < 0)
if (get_property_value
(DefaultRootWindow(g_display), "_NET_CURRENT_DESKTOP", 1, &nitems_return,
&prop_return, 0) < 0)
return (-1);
if (nitems_return != 1)
@ -125,7 +140,9 @@ get_current_workarea(uint32 * x, uint32 * y, uint32 * width, uint32 * height)
const uint32 net_workarea_height_offset = 3;
const uint32 max_prop_length = 32 * 4; /* Max 32 desktops */
if (get_property_value("_NET_WORKAREA", max_prop_length, &nitems_return, &prop_return) < 0)
if (get_property_value
(DefaultRootWindow(g_display), "_NET_WORKAREA", max_prop_length, &nitems_return,
&prop_return, 0) < 0)
return (-1);
if (nitems_return % 4)
@ -153,6 +170,262 @@ get_current_workarea(uint32 * x, uint32 * y, uint32 * width, uint32 * height)
}
void
ewmh_init()
{
/* FIXME: Use XInternAtoms */
g_net_wm_state_maximized_vert_atom =
XInternAtom(g_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
g_net_wm_state_maximized_horz_atom =
XInternAtom(g_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
g_net_wm_state_hidden_atom = XInternAtom(g_display, "_NET_WM_STATE_HIDDEN", False);
g_net_wm_state_skip_taskbar_atom =
XInternAtom(g_display, "_NET_WM_STATE_SKIP_TASKBAR", False);
g_net_wm_state_skip_pager_atom = XInternAtom(g_display, "_NET_WM_STATE_SKIP_PAGER", False);
g_net_wm_state_modal_atom = XInternAtom(g_display, "_NET_WM_STATE_MODAL", False);
g_net_wm_state_atom = XInternAtom(g_display, "_NET_WM_STATE", False);
g_net_wm_desktop_atom = XInternAtom(g_display, "_NET_WM_DESKTOP", False);
g_net_wm_name_atom = XInternAtom(g_display, "_NET_WM_NAME", False);
g_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False);
}
/*
Get the window state: normal/minimized/maximized.
*/
#ifndef MAKE_PROTO
int
ewmh_get_window_state(Window w)
{
unsigned long nitems_return;
unsigned char *prop_return;
uint32 *return_words;
unsigned long item;
BOOL maximized_vert, maximized_horz, hidden;
maximized_vert = maximized_horz = hidden = False;
if (get_property_value(w, "_NET_WM_STATE", 64, &nitems_return, &prop_return, 0) < 0)
return SEAMLESSRDP_NORMAL;
return_words = (uint32 *) prop_return;
for (item = 0; item < nitems_return; item++)
{
if (return_words[item] == g_net_wm_state_maximized_vert_atom)
maximized_vert = True;
if (return_words[item] == g_net_wm_state_maximized_horz_atom)
maximized_horz = True;
if (return_words[item] == g_net_wm_state_hidden_atom)
hidden = True;
}
XFree(prop_return);
if (maximized_vert && maximized_horz)
return SEAMLESSRDP_MAXIMIZED;
else if (hidden)
return SEAMLESSRDP_MINIMIZED;
else
return SEAMLESSRDP_NORMAL;
}
static int
ewmh_modify_state(Window wnd, int add, Atom atom1, Atom atom2)
{
Status status;
XEvent xevent;
int result;
unsigned long nitems;
unsigned char *props;
uint32 state;
/* The spec states that the window manager must respect any
_NET_WM_STATE attributes on a withdrawn window. In order words, we
modify the attributes directly for withdrawn windows and ask the WM
to do it for active windows. */
result = get_property_value(wnd, "WM_STATE", 64, &nitems, &props, 1);
if ((result >= 0) && nitems)
{
state = *(uint32 *) props;
XFree(props);
}
if ((result < 0) || !nitems || (state == WithdrawnState))
{
if (add)
{
Atom atoms[2];
atoms[0] = atom1;
nitems = 1;
if (atom2)
{
atoms[1] = atom2;
nitems = 2;
}
XChangeProperty(g_display, wnd, g_net_wm_state_atom, XA_ATOM,
32, PropModeAppend, (unsigned char *) atoms, nitems);
}
else
{
Atom *atoms;
int i;
if (get_property_value(wnd, "_NET_WM_STATE", 64, &nitems, &props, 1) < 0)
return 0;
atoms = (Atom *) props;
for (i = 0; i < nitems; i++)
{
if ((atoms[i] == atom1) || (atom2 && (atoms[i] == atom2)))
{
if (i != (nitems - 1))
memmove(&atoms[i], &atoms[i + 1],
sizeof(Atom) * (nitems - i - 1));
nitems--;
i--;
}
}
XChangeProperty(g_display, wnd, g_net_wm_state_atom, XA_ATOM,
32, PropModeReplace, (unsigned char *) atoms, nitems);
XFree(props);
}
return 0;
}
xevent.type = ClientMessage;
xevent.xclient.window = wnd;
xevent.xclient.message_type = g_net_wm_state_atom;
xevent.xclient.format = 32;
if (add)
xevent.xclient.data.l[0] = _NET_WM_STATE_ADD;
else
xevent.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
xevent.xclient.data.l[1] = atom1;
xevent.xclient.data.l[2] = atom2;
xevent.xclient.data.l[3] = 0;
xevent.xclient.data.l[4] = 0;
status = XSendEvent(g_display, DefaultRootWindow(g_display), False,
SubstructureNotifyMask | SubstructureRedirectMask, &xevent);
if (!status)
return -1;
return 0;
}
/*
Set the window state: normal/minimized/maximized.
Returns -1 on failure.
*/
int
ewmh_change_state(Window wnd, int state)
{
/*
* Deal with the max atoms
*/
if (state == SEAMLESSRDP_MAXIMIZED)
{
if (ewmh_modify_state
(wnd, 1, g_net_wm_state_maximized_vert_atom,
g_net_wm_state_maximized_horz_atom) < 0)
return -1;
}
else
{
if (ewmh_modify_state
(wnd, 0, g_net_wm_state_maximized_vert_atom,
g_net_wm_state_maximized_horz_atom) < 0)
return -1;
}
return 0;
}
int
ewmh_get_window_desktop(Window wnd)
{
unsigned long nitems_return;
unsigned char *prop_return;
int desktop;
if (get_property_value(wnd, "_NET_WM_DESKTOP", 1, &nitems_return, &prop_return, 0) < 0)
return (-1);
if (nitems_return != 1)
{
fprintf(stderr, "_NET_WM_DESKTOP has bad length\n");
return (-1);
}
desktop = *prop_return;
XFree(prop_return);
return desktop;
}
int
ewmh_move_to_desktop(Window wnd, unsigned int desktop)
{
Status status;
XEvent xevent;
xevent.type = ClientMessage;
xevent.xclient.window = wnd;
xevent.xclient.message_type = g_net_wm_desktop_atom;
xevent.xclient.format = 32;
xevent.xclient.data.l[0] = desktop;
xevent.xclient.data.l[1] = 0;
xevent.xclient.data.l[2] = 0;
xevent.xclient.data.l[3] = 0;
xevent.xclient.data.l[4] = 0;
status = XSendEvent(g_display, DefaultRootWindow(g_display), False,
SubstructureNotifyMask | SubstructureRedirectMask, &xevent);
if (!status)
return -1;
return 0;
}
void
ewmh_set_wm_name(Window wnd, const char *title)
{
int len;
len = strlen(title);
XChangeProperty(g_display, wnd, g_net_wm_name_atom, g_utf8_string_atom,
8, PropModeReplace, (unsigned char *) title, len);
}
int
ewmh_set_window_popup(Window wnd)
{
if (ewmh_modify_state
(wnd, 1, g_net_wm_state_skip_taskbar_atom, g_net_wm_state_skip_pager_atom) < 0)
return -1;
return 0;
}
int
ewmh_set_window_modal(Window wnd)
{
if (ewmh_modify_state(wnd, 1, g_net_wm_state_modal_atom, 0) < 0)
return -1;
return 0;
}
#endif /* MAKE_PROTO */
#if 0
/* FIXME: _NET_MOVERESIZE_WINDOW is for pagers, not for

24
proto.h
View File

@ -67,6 +67,7 @@ NTSTATUS disk_query_directory(NTHANDLE handle, uint32 info_class, char *pattern,
int mppc_expand(uint8 * data, uint32 clen, uint8 ctype, uint32 * roff, uint32 * rlen);
/* ewmhints.c */
int get_current_workarea(uint32 * x, uint32 * y, uint32 * width, uint32 * height);
void ewmh_init(void);
/* iso.c */
STREAM iso_init(int length);
void iso_send(STREAM s);
@ -271,8 +272,31 @@ void ui_desktop_save(uint32 offset, int x, int y, int cx, int cy);
void ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy);
void ui_begin_update(void);
void ui_end_update(void);
void ui_seamless_begin(BOOL hidden);
void ui_seamless_hide_desktop(void);
void ui_seamless_unhide_desktop(void);
void ui_seamless_toggle(void);
void ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long parent,
unsigned long flags);
void ui_seamless_destroy_window(unsigned long id, unsigned long flags);
void ui_seamless_move_window(unsigned long id, int x, int y, int width, int height,
unsigned long flags);
void ui_seamless_restack_window(unsigned long id, unsigned long behind, unsigned long flags);
void ui_seamless_settitle(unsigned long id, const char *title, unsigned long flags);
void ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags);
void ui_seamless_syncbegin(unsigned long flags);
void ui_seamless_ack(unsigned int serial);
/* lspci.c */
BOOL lspci_init(void);
/* seamless.c */
BOOL seamless_init(void);
unsigned int seamless_send_sync(void);
unsigned int seamless_send_state(unsigned long id, unsigned int state, unsigned long flags);
unsigned int seamless_send_position(unsigned long id, int x, int y, int width, int height,
unsigned long flags);
void seamless_select_timeout(struct timeval *tv);
unsigned int seamless_send_zchange(unsigned long id, unsigned long below, unsigned long flags);
unsigned int seamless_send_focus(unsigned long id, unsigned long flags);
/* *INDENT-OFF* */
#ifdef __cplusplus

View File

@ -89,6 +89,7 @@ BOOL g_numlock_sync = False;
BOOL lspci_enabled = False;
BOOL g_owncolmap = False;
BOOL g_ownbackstore = True; /* We can't rely on external BackingStore */
BOOL g_seamless_rdp = False;
uint32 g_embed_wnd;
uint32 g_rdp5_performanceflags =
RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG | RDP5_NO_MENUANIMATIONS;
@ -146,6 +147,7 @@ usage(char *program)
#ifdef HAVE_ICONV
fprintf(stderr, " -L: local codepage\n");
#endif
fprintf(stderr, " -A: enable SeamlessRDP mode\n");
fprintf(stderr, " -B: use BackingStore of X-server (if available)\n");
fprintf(stderr, " -e: disable encryption (French TS)\n");
fprintf(stderr, " -E: disable encryption from client to server\n");
@ -390,6 +392,7 @@ main(int argc, char *argv[])
int c;
char *locale = NULL;
int username_option = 0;
BOOL geometry_option = False;
int run_count = 0; /* Session Directory support */
BOOL continue_connect = True; /* Session Directory support */
@ -416,7 +419,7 @@ main(int argc, char *argv[])
#endif
while ((c = getopt(argc, argv,
VNCOPT "u:L:d:s:c:p:n:k:g:fbBeEmzCDKS:T:NX:a:x:Pr:045h?")) != -1)
VNCOPT "Au:L:d:s:c:p:n:k:g:fbBeEmzCDKS:T:NX:a:x:Pr:045h?")) != -1)
{
switch (c)
{
@ -434,6 +437,10 @@ main(int argc, char *argv[])
break;
#endif
case 'A':
g_seamless_rdp = True;
break;
case 'u':
STRNCPY(g_username, optarg, sizeof(g_username));
username_option = 1;
@ -484,6 +491,7 @@ main(int argc, char *argv[])
break;
case 'g':
geometry_option = True;
g_fullscreen = False;
if (!strcmp(optarg, "workarea"))
{
@ -732,6 +740,43 @@ main(int argc, char *argv[])
STRNCPY(server, argv[optind], sizeof(server));
parse_server_and_port(server);
if (g_seamless_rdp)
{
if (g_win_button_size)
{
error("You cannot use -S and -A at the same time\n");
return 1;
}
g_rdp5_performanceflags &= ~RDP5_NO_FULLWINDOWDRAG;
if (geometry_option)
{
error("You cannot use -g and -A at the same time\n");
return 1;
}
if (g_fullscreen)
{
error("You cannot use -f and -A at the same time\n");
return 1;
}
if (g_hide_decorations)
{
error("You cannot use -D and -A at the same time\n");
return 1;
}
if (g_embed_wnd)
{
error("You cannot use -X and -A at the same time\n");
return 1;
}
if (!g_use_rdp5)
{
error("You cannot use -4 and -A at the same time\n");
return 1;
}
g_width = -100;
g_grab_keyboard = False;
}
if (!username_option)
{
pw = getpwuid(getuid());

View File

@ -57,6 +57,12 @@
#define DEBUG_CLIPBOARD(args)
#endif
#ifdef WITH_DEBUG_CHANNEL
#define DEBUG_CHANNEL(args) printf args;
#else
#define DEBUG_CHANNEL(args)
#endif
#define STRNCPY(dst,src,n) { strncpy(dst,src,n-1); dst[n-1] = 0; }
#ifndef MIN
@ -67,6 +73,22 @@
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
#endif
/* timeval macros */
#ifndef timerisset
#define timerisset(tvp)\
((tvp)->tv_sec || (tvp)->tv_usec)
#endif
#ifndef timercmp
#define timercmp(tvp, uvp, cmp)\
((tvp)->tv_sec cmp (uvp)->tv_sec ||\
(tvp)->tv_sec == (uvp)->tv_sec &&\
(tvp)->tv_usec cmp (uvp)->tv_usec)
#endif
#ifndef timerclear
#define timerclear(tvp)\
((tvp)->tv_sec = (tvp)->tv_usec = 0)
#endif
/* If configure does not define the endianess, try
to find it out */
#if !defined(L_ENDIAN) && !defined(B_ENDIAN)

438
seamless.c Normal file
View File

@ -0,0 +1,438 @@
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Seamless Windows support
Copyright (C) Peter Astrand <astrand@cendio.se> 2005-2006
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "rdesktop.h"
#include <stdarg.h>
#include <assert.h>
/* #define WITH_DEBUG_SEAMLESS */
#ifdef WITH_DEBUG_SEAMLESS
#define DEBUG_SEAMLESS(args) printf args;
#else
#define DEBUG_SEAMLESS(args)
#endif
extern BOOL g_seamless_rdp;
static VCHANNEL *seamless_channel;
static unsigned int seamless_serial;
static char *
seamless_get_token(char **s)
{
char *comma, *head;
head = *s;
if (!head)
return NULL;
comma = strchr(head, ',');
if (comma)
{
*comma = '\0';
*s = comma + 1;
}
else
{
*s = NULL;
}
return head;
}
static BOOL
seamless_process_line(const char *line, void *data)
{
char *p, *l;
char *tok1, *tok2, *tok3, *tok4, *tok5, *tok6, *tok7, *tok8;
unsigned long id, flags;
char *endptr;
l = xstrdup(line);
p = l;
DEBUG_SEAMLESS(("seamlessrdp got:%s\n", p));
tok1 = seamless_get_token(&p);
tok2 = seamless_get_token(&p);
tok3 = seamless_get_token(&p);
tok4 = seamless_get_token(&p);
tok5 = seamless_get_token(&p);
tok6 = seamless_get_token(&p);
tok7 = seamless_get_token(&p);
tok8 = seamless_get_token(&p);
if (!strcmp("CREATE", tok1))
{
unsigned long group, parent;
if (!tok6)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
group = strtoul(tok4, &endptr, 0);
if (*endptr)
return False;
parent = strtoul(tok5, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok6, &endptr, 0);
if (*endptr)
return False;
ui_seamless_create_window(id, group, parent, flags);
}
else if (!strcmp("DESTROY", tok1))
{
if (!tok4)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok4, &endptr, 0);
if (*endptr)
return False;
ui_seamless_destroy_window(id, flags);
}
else if (!strcmp("SETICON", tok1))
{
unimpl("SeamlessRDP SETICON1\n");
}
else if (!strcmp("POSITION", tok1))
{
int x, y, width, height;
if (!tok8)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
x = strtol(tok4, &endptr, 0);
if (*endptr)
return False;
y = strtol(tok5, &endptr, 0);
if (*endptr)
return False;
width = strtol(tok6, &endptr, 0);
if (*endptr)
return False;
height = strtol(tok7, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok8, &endptr, 0);
if (*endptr)
return False;
ui_seamless_move_window(id, x, y, width, height, flags);
}
else if (!strcmp("ZCHANGE", tok1))
{
unsigned long behind;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
behind = strtoul(tok4, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok5, &endptr, 0);
if (*endptr)
return False;
ui_seamless_restack_window(id, behind, flags);
}
else if (!strcmp("TITLE", tok1))
{
if (!tok5)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok5, &endptr, 0);
if (*endptr)
return False;
ui_seamless_settitle(id, tok4, flags);
}
else if (!strcmp("STATE", tok1))
{
unsigned int state;
if (!tok5)
return False;
id = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
state = strtoul(tok4, &endptr, 0);
if (*endptr)
return False;
flags = strtoul(tok5, &endptr, 0);
if (*endptr)
return False;
ui_seamless_setstate(id, state, flags);
}
else if (!strcmp("DEBUG", tok1))
{
printf("SeamlessRDP:%s\n", line);
}
else if (!strcmp("SYNCBEGIN", tok1))
{
if (!tok3)
return False;
flags = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
ui_seamless_syncbegin(flags);
}
else if (!strcmp("SYNCEND", tok1))
{
if (!tok3)
return False;
flags = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
/* do nothing, currently */
}
else if (!strcmp("HELLO", tok1))
{
if (!tok3)
return False;
flags = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
ui_seamless_begin(!!(flags & SEAMLESSRDP_HELLO_HIDDEN));
}
else if (!strcmp("ACK", tok1))
{
unsigned int serial;
serial = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
ui_seamless_ack(serial);
}
else if (!strcmp("HIDE", tok1))
{
if (!tok3)
return False;
flags = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
ui_seamless_hide_desktop();
}
else if (!strcmp("UNHIDE", tok1))
{
if (!tok3)
return False;
flags = strtoul(tok3, &endptr, 0);
if (*endptr)
return False;
ui_seamless_unhide_desktop();
}
xfree(l);
return True;
}
static BOOL
seamless_line_handler(const char *line, void *data)
{
if (!seamless_process_line(line, data))
{
warning("SeamlessRDP: Invalid request:%s\n", line);
}
return True;
}
static void
seamless_process(STREAM s)
{
unsigned int pkglen;
static char *rest = NULL;
char *buf;
pkglen = s->end - s->p;
/* str_handle_lines requires null terminated strings */
buf = xmalloc(pkglen + 1);
STRNCPY(buf, (char *) s->p, pkglen + 1);
#if 0
printf("seamless recv:\n");
hexdump(s->p, pkglen);
#endif
str_handle_lines(buf, &rest, seamless_line_handler, NULL);
xfree(buf);
}
BOOL
seamless_init(void)
{
if (!g_seamless_rdp)
return False;
seamless_serial = 0;
seamless_channel =
channel_register("seamrdp", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
seamless_process);
return (seamless_channel != NULL);
}
static unsigned int
seamless_send(const char *command, const char *format, ...)
{
STREAM s;
size_t len;
va_list argp;
char buf[1025];
len = snprintf(buf, sizeof(buf) - 1, "%s,%u,", command, seamless_serial);
assert(len < (sizeof(buf) - 1));
va_start(argp, format);
len += vsnprintf(buf + len, sizeof(buf) - len - 1, format, argp);
va_end(argp);
assert(len < (sizeof(buf) - 1));
buf[len] = '\n';
buf[len + 1] = '\0';
len++;
s = channel_init(seamless_channel, len);
out_uint8p(s, buf, len) s_mark_end(s);
DEBUG_SEAMLESS(("SeamlessRDP sending:%s", buf));
#if 0
printf("seamless send:\n");
hexdump(s->channel_hdr + 8, s->end - s->channel_hdr - 8);
#endif
channel_send(s, seamless_channel);
return seamless_serial++;
}
unsigned int
seamless_send_sync()
{
if (!g_seamless_rdp)
return (unsigned int) -1;
return seamless_send("SYNC", "");
}
unsigned int
seamless_send_state(unsigned long id, unsigned int state, unsigned long flags)
{
if (!g_seamless_rdp)
return (unsigned int) -1;
return seamless_send("STATE", "0x%08lx,0x%x,0x%lx", id, state, flags);
}
unsigned int
seamless_send_position(unsigned long id, int x, int y, int width, int height, unsigned long flags)
{
return seamless_send("POSITION", "0x%08lx,%d,%d,%d,%d,0x%lx", id, x, y, width, height,
flags);
}
/* Update select timeout */
void
seamless_select_timeout(struct timeval *tv)
{
struct timeval ourtimeout = { 0, SEAMLESSRDP_POSITION_TIMER };
if (g_seamless_rdp)
{
if (timercmp(&ourtimeout, tv, <))
{
tv->tv_sec = ourtimeout.tv_sec;
tv->tv_usec = ourtimeout.tv_usec;
}
}
}
unsigned int
seamless_send_zchange(unsigned long id, unsigned long below, unsigned long flags)
{
if (!g_seamless_rdp)
return (unsigned int) -1;
return seamless_send("ZCHANGE", "0x%08lx,0x%08lx,0x%lx", id, below, flags);
}
unsigned int
seamless_send_focus(unsigned long id, unsigned long flags)
{
if (!g_seamless_rdp)
return (unsigned int) -1;
return seamless_send("FOCUS", "0x%08lx,0x%lx", id, flags);
}

19
seamless.h Normal file
View File

@ -0,0 +1,19 @@
/*
rdesktop: A Remote Desktop Protocol client.
Seamless Windows support
Copyright (C) Peter Astrand <astrand@cendio.se> 2005-2006
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

View File

@ -533,6 +533,7 @@ sec_parse_x509_key(X509 * cert)
if (OBJ_obj2nid(cert->cert_info->key->algor->algorithm) == NID_md5WithRSAEncryption)
{
DEBUG_RDP5(("Re-setting algorithm type to RSA in server certificate\n"));
ASN1_OBJECT_free(cert->cert_info->key->algor->algorithm);
cert->cert_info->key->algor->algorithm = OBJ_nid2obj(NID_rsaEncryption);
}
epk = X509_get_pubkey(cert);
@ -542,7 +543,9 @@ sec_parse_x509_key(X509 * cert)
return False;
}
server_public_key = (RSA *) epk->pkey.ptr;
server_public_key = RSAPublicKey_dup((RSA *) epk->pkey.ptr);
EVP_PKEY_free(epk);
return True;
}
@ -680,6 +683,8 @@ sec_parse_crypt_info(STREAM s, uint32 * rc4_key_size,
MITM-attacks.
*/
X509_free(cacert);
in_uint32_le(s, cert_len);
DEBUG_RDP5(("Certificate length is %d\n", cert_len));
server_cert = d2i_X509(NULL, &(s->p), cert_len);
@ -698,8 +703,10 @@ sec_parse_crypt_info(STREAM s, uint32 * rc4_key_size,
if (!sec_parse_x509_key(server_cert))
{
DEBUG_RDP5(("Didn't parse X509 correctly\n"));
X509_free(server_cert);
return False;
}
X509_free(server_cert);
return True; /* There's some garbage here we don't care about */
}
return s_check_end(s);
@ -745,6 +752,8 @@ sec_process_crypt_info(STREAM s)
reverse(sec_crypted_random, SEC_MODULUS_SIZE);
RSA_free(server_public_key);
server_public_key = NULL;
}
else
{ /* RDP4-style encryption */

View File

@ -589,6 +589,11 @@ handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, BOOL pres
/* Inhibit */
return True;
break;
case XK_Overlay1_Enable:
/* Toggle SeamlessRDP */
if (pressed)
ui_seamless_toggle();
break;
}
return False;

View File

@ -2,3 +2,10 @@ void xclip_handle_SelectionNotify(XSelectionEvent * event);
void xclip_handle_SelectionRequest(XSelectionRequestEvent * xevent);
void xclip_handle_SelectionClear(void);
void xclip_handle_PropertyNotify(XPropertyEvent * xev);
int ewmh_get_window_state(Window w);
int ewmh_change_state(Window wnd, int state);
int ewmh_move_to_desktop(Window wnd, unsigned int desktop);
int ewmh_get_window_desktop(Window wnd);
void ewmh_set_wm_name(Window wnd, const char *title);
int ewmh_set_window_popup(Window wnd);
int ewmh_set_window_modal(Window wnd);

1027
xwin.c

File diff suppressed because it is too large Load Diff