/*
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
*
* This 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 software 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 software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
/*
* selection.c - functions to deal with the selection and cut buffer.
*/
#include <vncviewer.h>
static void GetInitialSelectionTimeCallback(Widget w, XtPointer clientData,
Atom* selection, Atom* type,
XtPointer value,
unsigned long* length,
int* format);
static void GetSelectionCallback(Widget w, XtPointer clientData,
Atom* selection, Atom* type, XtPointer value,
unsigned long* length, int* format);
static void GetSelectionTimeCallback(Widget w, XtPointer clientData,
Atom* selection, Atom* type,
XtPointer value, unsigned long* length,
int* format);
static void SendCutBuffer();
static void CutBufferChange(Widget w, XtPointer ptr, XEvent *ev,
Boolean *cont);
static Boolean ConvertSelection(Widget w, Atom* selection, Atom* target,
Atom* type, XtPointer* value,
unsigned long* length, int* format);
static void LoseSelection(Widget w, Atom *selection);
static Bool iAmSelectionOwner = False;
static Time prevSelectionTime = 0L;
static Time cutBufferTime = 0L;
#define TIME_LATER(a, b) ((a) != 0 && ((b) == 0 || (INT32)((a) - (b)) > 0))
/*
* InitialiseSelection() must be called after realizing widgets (because
* otherwise XtGetSelectionValue() fails). We register events on the root
* window to appear as if on the toplevel window, and catch cut buffer
* PropertyNotify events. Then we try to get the timestamp of any existing
* selection by calling XtGetSelectionValue() with target "TIMESTAMP". In the
* normal case we won't send this selection to the VNC server, but we need to
* know its timestamp so that we can tell when a new selection has been made.
* GetInitialSelectionTimeCallback() will be invoked when the timestamp is
* available.
*/
void
InitialiseSelection()
{
#if XtSpecificationRelease >= 6
XtRegisterDrawable(dpy, DefaultRootWindow(dpy), toplevel);
#else
_XtRegisterWindow(DefaultRootWindow(dpy), toplevel);
#endif
XSelectInput(dpy, DefaultRootWindow(dpy), PropertyChangeMask);
XtAddRawEventHandler(toplevel, PropertyChangeMask, False, CutBufferChange,
NULL);
XtGetSelectionValue(toplevel, XA_PRIMARY,
XInternAtom(dpy, "TIMESTAMP", False),
GetInitialSelectionTimeCallback, NULL, CurrentTime);
}
/*
* GetInitialSelectionTimeCallback() is invoked when the selection owner has
* told us the timestamp of the initial selection. We just set
* prevSelectionTime to the value returned, or the special value 0 (the same as
* CurrentTime) if there is no selection.
*/
static void
GetInitialSelectionTimeCallback(Widget w, XtPointer clientData,
Atom* selection, Atom* type, XtPointer value,
unsigned long* length, int* format)
{
if (value && *format == 32 && *length == 1)
prevSelectionTime = *(CARD32 *)value;
else
prevSelectionTime = 0L;
if (value)
XtFree(value);
}
/*
* SelectionToVNC() is an action which is usually invoked when the mouse enters
* the viewer window. With the "always" argument we always transfer the
* current selection to the VNC server. With no argument or the "new" argument
* we transfer the selection only if it is "new" i.e. its timestamp is later
* than the previously transferred selection.
*
* In the former case we call XtGetSelectionValue() with target "STRING" to get
* the selection. GetSelectionCallback() will be invoked when the selection is
* available.
*
* In the latter case we call XtGetSelectionValue() with target "TIMESTAMP".
* GetSelectionTimeCallback() will be invoked when the timestamp is available.
*/
void
SelectionToVNC(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
Bool always = False;
if (*num_params != 0) {
if (strcmp(params[0],"always") == 0) {
always = True;
} else if (strcmp(params[0],"new") == 0) {
always = False;
} else {
fprintf(stderr,"Invalid params: SelectionToVNC(always|new)\n");
return;
}
}
if (always) {
XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, GetSelectionCallback, NULL,
TimeFromEvent(event));
} else {
XtGetSelectionValue(w, XA_PRIMARY, XInternAtom(dpy, "TIMESTAMP", False),
GetSelectionTimeCallback, NULL, TimeFromEvent(event));
}
}
/*
* GetSelectionCallback() is invoked when the selection has been retrieved from
* the selection owner - we simply send it to the VNC server. If there is
* no PRIMARY selection available, we try sending CUT_BUFFER0 instead.
*/
static void
GetSelectionCallback(Widget w, XtPointer clientData, Atom* selection,
Atom* type, XtPointer value, unsigned long* length,
int* format)
{
int len = *length;
char *str = (char *)value;
if (str)
SendClientCutText(str, len);
else
SendCutBuffer();
}
/*
* GetSelectionTimeCallback() is invoked when the selection owner has told us
* the timestamp of the selection. If the timestamp is later than that of the
* previous selection then we call XtGetSelectionValue() with target "STRING"
* to get the selection. GetSelectionCallback() will be invoked when the
* selection is available. If there is no selection we see if the time
* CUT_BUFFER0 was last changed is later than that of the previous selection,
* and if so, send it.
*/
static void
GetSelectionTimeCallback(Widget w, XtPointer clientData, Atom* selection,
Atom* type, XtPointer value, unsigned long* length,
int* format)
{
if (value && *format == 32 && *length == 1) {
Time t = *(CARD32 *)value;
if (TIME_LATER(t, prevSelectionTime)) {
prevSelectionTime = t;
XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, GetSelectionCallback, NULL,
CurrentTime);
}
} else {
if (TIME_LATER(cutBufferTime, prevSelectionTime)) {
prevSelectionTime = cutBufferTime;
SendCutBuffer();
}
}
if (value)
XtFree(value);
}
/*
* SendCutBuffer gets the CUT_BUFFER0 property from the root window and sends
* it to the VNC server.
*/
static void
SendCutBuffer()
{
char *str;
int len;
str = XFetchBytes(dpy, &len);
if (!str) return;
SendClientCutText(str, len);
XFree(str);
}
/*
* CutBufferChange - check PropertyNotify events on the root window to see if
* the cut buffer has changed. If so, record its timestamp.
*/
static void
CutBufferChange(Widget w, XtPointer ptr, XEvent *ev, Boolean *cont)
{
if (ev->type != PropertyNotify || ev->xproperty.atom != XA_CUT_BUFFER0)
return;
cutBufferTime = ev->xproperty.time;
}
/*
* SelectionFromVNC() is an action which is usually invoked when the mouse
* leaves the viewer window. With the "always" argument we always set the
* PRIMARY selection and CUT_BUFFER0 to the current value of the VNC server
* "cut text". With no argument or the "new" argument we set the selection
* only if it is "new" i.e. there has been new "cut text" since the last time
* it was called.
*/
void
SelectionFromVNC(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
Bool always = False;
Time t = TimeFromEvent(event);
if (*num_params != 0) {
if (strcmp(params[0],"always") == 0) {
always = True;
} else if (strcmp(params[0],"new") == 0) {
always = False;
} else {
fprintf(stderr,"Invalid params: SelectionFromVNC(always|new)\n");
return;
}
}
if (t == CurrentTime) {
fprintf(stderr,"Error in translations: SelectionFromVNC() must act on "
"event with time field\n");
return;
}
if (!serverCutText || (!always && !newServerCutText))
return;
newServerCutText = False;
XStoreBytes(dpy, serverCutText, strlen(serverCutText));
if (XtOwnSelection(desktop, XA_PRIMARY, t, ConvertSelection, LoseSelection,
NULL)) {
iAmSelectionOwner = True;
}
}
/*
* ConvertSelection is called when another X client requests the selection.
* Simply send the "server cut text" for a STRING target, or do a standard
* conversion for anything else.
*/
static Boolean
ConvertSelection(Widget w, Atom* selection, Atom* target, Atom* type,
XtPointer* value, unsigned long* length, int* format)
{
if (*target == XA_STRING && serverCutText != NULL) {
*type = XA_STRING;
*length = strlen(serverCutText);
*value = (XtPointer)XtMalloc(*length);
memcpy((char*)*value, serverCutText, *length);
*format = 8;
return True;
}
if (XmuConvertStandardSelection(w, CurrentTime, selection, target, type,
(XPointer*)value, length, format)) {
if (*target == XInternAtom(dpy, "TARGETS", False)) {
/* add STRING to list of standard targets */
Atom* targetP;
Atom* std_targets = (Atom*)*value;
unsigned long std_length = *length;
*length = std_length + 1;
*value = (XtPointer)XtMalloc(sizeof(Atom)*(*length));
targetP = *(Atom**)value;
*targetP++ = XA_STRING;
memmove((char*)targetP, (char*)std_targets, sizeof(Atom)*std_length);
XtFree((char*)std_targets);
*type = XA_ATOM;
*format = 32;
return True;
}
return True;
}
return False;
}
/*
* LoseSelection is called when we've lost ownership of the selection.
*/
static void
LoseSelection(Widget w, Atom *selection)
{
iAmSelectionOwner = False;
}
syntax highlighted by Code2HTML, v. 0.9.1