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:
commit
6b6ebaad66
@ -25,7 +25,7 @@ LDVNC = @LDVNC@
|
|||||||
VNCLINK = @VNCLINK@
|
VNCLINK = @VNCLINK@
|
||||||
SOUNDOBJ = @SOUNDOBJ@
|
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
|
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
|
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 \
|
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 \
|
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 \
|
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
|
cat proto.tail >> proto.h
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#include "rdesktop.h"
|
#include "rdesktop.h"
|
||||||
|
|
||||||
#define MAX_CHANNELS 5
|
#define MAX_CHANNELS 6
|
||||||
#define CHANNEL_CHUNK_LENGTH 1600
|
#define CHANNEL_CHUNK_LENGTH 1600
|
||||||
#define CHANNEL_FLAG_FIRST 0x01
|
#define CHANNEL_FLAG_FIRST 0x01
|
||||||
#define CHANNEL_FLAG_LAST 0x02
|
#define CHANNEL_FLAG_LAST 0x02
|
||||||
@ -87,7 +87,7 @@ channel_send(STREAM s, VCHANNEL * channel)
|
|||||||
s_pop_layer(s, channel_hdr);
|
s_pop_layer(s, channel_hdr);
|
||||||
length = s->end - s->p - 8;
|
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);
|
thislength = MIN(length, CHANNEL_CHUNK_LENGTH);
|
||||||
/* Note: In the original clipboard implementation, this number was
|
/* 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, length);
|
||||||
out_uint32_le(s, flags);
|
out_uint32_le(s, flags);
|
||||||
data = s->end = s->p + thislength;
|
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);
|
sec_send_to_channel(s, g_encryption ? SEC_ENCRYPT : 0, channel->mcs_id);
|
||||||
|
|
||||||
/* subsequent segments copied (otherwise would have to generate headers backwards) */
|
/* 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)
|
if (channel->flags & CHANNEL_OPTION_SHOW_PROTOCOL)
|
||||||
flags |= CHANNEL_FLAG_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);
|
s = sec_init(g_encryption ? SEC_ENCRYPT : 0, thislength + 8);
|
||||||
out_uint32_le(s, length);
|
out_uint32_le(s, length);
|
||||||
|
@ -684,6 +684,15 @@ AC_ARG_WITH(debug-clipboard,
|
|||||||
fi
|
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
|
# target-specific stuff
|
||||||
|
12
constants.h
12
constants.h
@ -412,3 +412,15 @@ enum RDP_INPUT_DEVICE
|
|||||||
#define exDiscReasonLicenseErrClientEncryption 0x0108
|
#define exDiscReasonLicenseErrClientEncryption 0x0108
|
||||||
#define exDiscReasonLicenseCantUpgradeLicense 0x0109
|
#define exDiscReasonLicenseCantUpgradeLicense 0x0109
|
||||||
#define exDiscReasonLicenseNoRemoteConnections 0x010a
|
#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
|
||||||
|
32
doc/TODO
32
doc/TODO
@ -52,3 +52,35 @@
|
|||||||
* More fancy homepage.
|
* More fancy homepage.
|
||||||
|
|
||||||
* Enhance documentation. Write a FAQ.
|
* 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.
|
||||||
|
@ -76,6 +76,13 @@ toggled at any time using Ctrl-Alt-Enter.
|
|||||||
Force the server to send screen updates as bitmaps rather than using
|
Force the server to send screen updates as bitmaps rather than using
|
||||||
higher-level drawing operations.
|
higher-level drawing operations.
|
||||||
.TP
|
.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"
|
.BR "-B"
|
||||||
Use the BackingStore of the Xserver instead of the integrated one in
|
Use the BackingStore of the Xserver instead of the integrated one in
|
||||||
rdesktop.
|
rdesktop.
|
||||||
|
266
doc/seamlessrdp-channel.txt
Normal file
266
doc/seamlessrdp-channel.txt
Normal 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.
|
285
ewmhints.c
285
ewmhints.c
@ -22,17 +22,29 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
#include "rdesktop.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;
|
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)
|
Get window property value (32 bit format)
|
||||||
Returns zero on success, -1 on error
|
Returns zero on success, -1 on error
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
get_property_value(char *propname, long max_length,
|
get_property_value(Window wnd, char *propname, long max_length,
|
||||||
unsigned long *nitems_return, unsigned char **prop_return)
|
unsigned long *nitems_return, unsigned char **prop_return, int nowarn)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
Atom property;
|
Atom property;
|
||||||
@ -47,7 +59,7 @@ get_property_value(char *propname, long max_length,
|
|||||||
return (-1);
|
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 */
|
max_length, /* long_length */
|
||||||
False, /* delete */
|
False, /* delete */
|
||||||
AnyPropertyType, /* req_type */
|
AnyPropertyType, /* req_type */
|
||||||
@ -63,7 +75,8 @@ get_property_value(char *propname, long max_length,
|
|||||||
|
|
||||||
if (actual_type_return == None || actual_format_return == 0)
|
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);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +106,9 @@ get_current_desktop(void)
|
|||||||
unsigned char *prop_return;
|
unsigned char *prop_return;
|
||||||
int current_desktop;
|
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);
|
return (-1);
|
||||||
|
|
||||||
if (nitems_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 net_workarea_height_offset = 3;
|
||||||
const uint32 max_prop_length = 32 * 4; /* Max 32 desktops */
|
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);
|
return (-1);
|
||||||
|
|
||||||
if (nitems_return % 4)
|
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
|
#if 0
|
||||||
|
|
||||||
/* FIXME: _NET_MOVERESIZE_WINDOW is for pagers, not for
|
/* FIXME: _NET_MOVERESIZE_WINDOW is for pagers, not for
|
||||||
|
24
proto.h
24
proto.h
@ -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);
|
int mppc_expand(uint8 * data, uint32 clen, uint8 ctype, uint32 * roff, uint32 * rlen);
|
||||||
/* ewmhints.c */
|
/* ewmhints.c */
|
||||||
int get_current_workarea(uint32 * x, uint32 * y, uint32 * width, uint32 * height);
|
int get_current_workarea(uint32 * x, uint32 * y, uint32 * width, uint32 * height);
|
||||||
|
void ewmh_init(void);
|
||||||
/* iso.c */
|
/* iso.c */
|
||||||
STREAM iso_init(int length);
|
STREAM iso_init(int length);
|
||||||
void iso_send(STREAM s);
|
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_desktop_restore(uint32 offset, int x, int y, int cx, int cy);
|
||||||
void ui_begin_update(void);
|
void ui_begin_update(void);
|
||||||
void ui_end_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 */
|
/* lspci.c */
|
||||||
BOOL lspci_init(void);
|
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* */
|
/* *INDENT-OFF* */
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
47
rdesktop.c
47
rdesktop.c
@ -89,6 +89,7 @@ BOOL g_numlock_sync = False;
|
|||||||
BOOL lspci_enabled = False;
|
BOOL lspci_enabled = False;
|
||||||
BOOL g_owncolmap = False;
|
BOOL g_owncolmap = False;
|
||||||
BOOL g_ownbackstore = True; /* We can't rely on external BackingStore */
|
BOOL g_ownbackstore = True; /* We can't rely on external BackingStore */
|
||||||
|
BOOL g_seamless_rdp = False;
|
||||||
uint32 g_embed_wnd;
|
uint32 g_embed_wnd;
|
||||||
uint32 g_rdp5_performanceflags =
|
uint32 g_rdp5_performanceflags =
|
||||||
RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG | RDP5_NO_MENUANIMATIONS;
|
RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG | RDP5_NO_MENUANIMATIONS;
|
||||||
@ -146,6 +147,7 @@ usage(char *program)
|
|||||||
#ifdef HAVE_ICONV
|
#ifdef HAVE_ICONV
|
||||||
fprintf(stderr, " -L: local codepage\n");
|
fprintf(stderr, " -L: local codepage\n");
|
||||||
#endif
|
#endif
|
||||||
|
fprintf(stderr, " -A: enable SeamlessRDP mode\n");
|
||||||
fprintf(stderr, " -B: use BackingStore of X-server (if available)\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 (French TS)\n");
|
||||||
fprintf(stderr, " -E: disable encryption from client to server\n");
|
fprintf(stderr, " -E: disable encryption from client to server\n");
|
||||||
@ -390,6 +392,7 @@ main(int argc, char *argv[])
|
|||||||
int c;
|
int c;
|
||||||
char *locale = NULL;
|
char *locale = NULL;
|
||||||
int username_option = 0;
|
int username_option = 0;
|
||||||
|
BOOL geometry_option = False;
|
||||||
int run_count = 0; /* Session Directory support */
|
int run_count = 0; /* Session Directory support */
|
||||||
BOOL continue_connect = True; /* Session Directory support */
|
BOOL continue_connect = True; /* Session Directory support */
|
||||||
|
|
||||||
@ -416,7 +419,7 @@ main(int argc, char *argv[])
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
while ((c = getopt(argc, argv,
|
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)
|
switch (c)
|
||||||
{
|
{
|
||||||
@ -434,6 +437,10 @@ main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
case 'A':
|
||||||
|
g_seamless_rdp = True;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
STRNCPY(g_username, optarg, sizeof(g_username));
|
STRNCPY(g_username, optarg, sizeof(g_username));
|
||||||
username_option = 1;
|
username_option = 1;
|
||||||
@ -484,6 +491,7 @@ main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'g':
|
case 'g':
|
||||||
|
geometry_option = True;
|
||||||
g_fullscreen = False;
|
g_fullscreen = False;
|
||||||
if (!strcmp(optarg, "workarea"))
|
if (!strcmp(optarg, "workarea"))
|
||||||
{
|
{
|
||||||
@ -732,6 +740,43 @@ main(int argc, char *argv[])
|
|||||||
STRNCPY(server, argv[optind], sizeof(server));
|
STRNCPY(server, argv[optind], sizeof(server));
|
||||||
parse_server_and_port(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)
|
if (!username_option)
|
||||||
{
|
{
|
||||||
pw = getpwuid(getuid());
|
pw = getpwuid(getuid());
|
||||||
|
22
rdesktop.h
22
rdesktop.h
@ -57,6 +57,12 @@
|
|||||||
#define DEBUG_CLIPBOARD(args)
|
#define DEBUG_CLIPBOARD(args)
|
||||||
#endif
|
#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; }
|
#define STRNCPY(dst,src,n) { strncpy(dst,src,n-1); dst[n-1] = 0; }
|
||||||
|
|
||||||
#ifndef MIN
|
#ifndef MIN
|
||||||
@ -67,6 +73,22 @@
|
|||||||
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
|
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
|
||||||
#endif
|
#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
|
/* If configure does not define the endianess, try
|
||||||
to find it out */
|
to find it out */
|
||||||
#if !defined(L_ENDIAN) && !defined(B_ENDIAN)
|
#if !defined(L_ENDIAN) && !defined(B_ENDIAN)
|
||||||
|
438
seamless.c
Normal file
438
seamless.c
Normal 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
19
seamless.h
Normal 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.
|
||||||
|
*/
|
11
secure.c
11
secure.c
@ -533,6 +533,7 @@ sec_parse_x509_key(X509 * cert)
|
|||||||
if (OBJ_obj2nid(cert->cert_info->key->algor->algorithm) == NID_md5WithRSAEncryption)
|
if (OBJ_obj2nid(cert->cert_info->key->algor->algorithm) == NID_md5WithRSAEncryption)
|
||||||
{
|
{
|
||||||
DEBUG_RDP5(("Re-setting algorithm type to RSA in server certificate\n"));
|
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);
|
cert->cert_info->key->algor->algorithm = OBJ_nid2obj(NID_rsaEncryption);
|
||||||
}
|
}
|
||||||
epk = X509_get_pubkey(cert);
|
epk = X509_get_pubkey(cert);
|
||||||
@ -542,7 +543,9 @@ sec_parse_x509_key(X509 * cert)
|
|||||||
return False;
|
return False;
|
||||||
}
|
}
|
||||||
|
|
||||||
server_public_key = (RSA *) epk->pkey.ptr;
|
server_public_key = RSAPublicKey_dup((RSA *) epk->pkey.ptr);
|
||||||
|
|
||||||
|
EVP_PKEY_free(epk);
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
@ -680,6 +683,8 @@ sec_parse_crypt_info(STREAM s, uint32 * rc4_key_size,
|
|||||||
MITM-attacks.
|
MITM-attacks.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
X509_free(cacert);
|
||||||
|
|
||||||
in_uint32_le(s, cert_len);
|
in_uint32_le(s, cert_len);
|
||||||
DEBUG_RDP5(("Certificate length is %d\n", cert_len));
|
DEBUG_RDP5(("Certificate length is %d\n", cert_len));
|
||||||
server_cert = d2i_X509(NULL, &(s->p), 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))
|
if (!sec_parse_x509_key(server_cert))
|
||||||
{
|
{
|
||||||
DEBUG_RDP5(("Didn't parse X509 correctly\n"));
|
DEBUG_RDP5(("Didn't parse X509 correctly\n"));
|
||||||
|
X509_free(server_cert);
|
||||||
return False;
|
return False;
|
||||||
}
|
}
|
||||||
|
X509_free(server_cert);
|
||||||
return True; /* There's some garbage here we don't care about */
|
return True; /* There's some garbage here we don't care about */
|
||||||
}
|
}
|
||||||
return s_check_end(s);
|
return s_check_end(s);
|
||||||
@ -745,6 +752,8 @@ sec_process_crypt_info(STREAM s)
|
|||||||
|
|
||||||
reverse(sec_crypted_random, SEC_MODULUS_SIZE);
|
reverse(sec_crypted_random, SEC_MODULUS_SIZE);
|
||||||
|
|
||||||
|
RSA_free(server_public_key);
|
||||||
|
server_public_key = NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ /* RDP4-style encryption */
|
{ /* RDP4-style encryption */
|
||||||
|
@ -589,6 +589,11 @@ handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, BOOL pres
|
|||||||
/* Inhibit */
|
/* Inhibit */
|
||||||
return True;
|
return True;
|
||||||
break;
|
break;
|
||||||
|
case XK_Overlay1_Enable:
|
||||||
|
/* Toggle SeamlessRDP */
|
||||||
|
if (pressed)
|
||||||
|
ui_seamless_toggle();
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
return False;
|
return False;
|
||||||
|
7
xproto.h
7
xproto.h
@ -2,3 +2,10 @@ void xclip_handle_SelectionNotify(XSelectionEvent * event);
|
|||||||
void xclip_handle_SelectionRequest(XSelectionRequestEvent * xevent);
|
void xclip_handle_SelectionRequest(XSelectionRequestEvent * xevent);
|
||||||
void xclip_handle_SelectionClear(void);
|
void xclip_handle_SelectionClear(void);
|
||||||
void xclip_handle_PropertyNotify(XPropertyEvent * xev);
|
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user