static void xf_cliprdr_process_html(clipboardContext* cb, BYTE* data, int size) { char* start_str; char* end_str; int start; int end; start_str = strstr((char*) data, "StartHTML:"); end_str = strstr((char*) data, "EndHTML:"); if (start_str == NULL || end_str == NULL) { DEBUG_X11_CLIPRDR("invalid HTML clipboard format"); return; } start = atoi(start_str + 10); end = atoi(end_str + 8); if (start > size || end > size || start >= end) { DEBUG_X11_CLIPRDR("invalid HTML offset"); return; } cb->data = (BYTE*) malloc(size - start + 1); CopyMemory(cb->data, data + start, end - start); cb->data_length = end - start; crlf2lf(cb->data, &cb->data_length); }
BOOL xf_cliprdr_process_property_notify(xfInfo* xfi, XEvent* xevent) { clipboardContext* cb = (clipboardContext*) xfi->clipboard_context; if (!cb) return TRUE; if (xevent->xproperty.atom != cb->property_atom) return FALSE; /* Not cliprdr-related */ if (xevent->xproperty.window == cb->root_window) { DEBUG_X11_CLIPRDR("root window PropertyNotify"); xf_cliprdr_send_format_list(xfi); } else if (xevent->xproperty.window == xfi->drawable && xevent->xproperty.state == PropertyNewValue && cb->incr_starts && cb->request_index >= 0) { DEBUG_X11_CLIPRDR("cliprdr window PropertyNotify"); xf_cliprdr_get_requested_data(xfi, cb->format_mappings[cb->request_index].target_format); } return TRUE; }
static void xf_cliprdr_send_raw_format_list(xfInfo* xfi) { Atom type; BYTE* format_data; int format, result; unsigned long length, bytes_left; RDP_CB_FORMAT_LIST_EVENT* event; clipboardContext* cb = (clipboardContext*) xfi->clipboard_context; result = XGetWindowProperty(xfi->display, cb->root_window, cb->property_atom, 0, 3600, 0, XA_STRING, &type, &format, &length, &bytes_left, (BYTE**) &format_data); if (result != Success) { DEBUG_WARN("XGetWindowProperty failed"); return; } DEBUG_X11_CLIPRDR("format=%d len=%d bytes_left=%d", format, (int) length, (int) bytes_left); event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_FormatList, NULL, NULL); event->raw_format_data = (BYTE*) malloc(length); CopyMemory(event->raw_format_data, format_data, length); event->raw_format_data_size = length; XFree(format_data); freerdp_channels_send_event(cb->channels, (wMessage*) event); }
void xf_process_cliprdr_event(xfInfo* xfi, wMessage* event) { switch (GetMessageType(event->id)) { case CliprdrChannel_MonitorReady: xf_cliprdr_process_cb_monitor_ready_event(xfi); break; case CliprdrChannel_FormatList: xf_cliprdr_process_cb_format_list_event(xfi, (RDP_CB_FORMAT_LIST_EVENT*) event); break; case CliprdrChannel_DataRequest: xf_cliprdr_process_cb_data_request_event(xfi, (RDP_CB_DATA_REQUEST_EVENT*) event); break; case CliprdrChannel_DataResponse: xf_cliprdr_process_cb_data_response_event(xfi, (RDP_CB_DATA_RESPONSE_EVENT*) event); break; default: DEBUG_X11_CLIPRDR("unknown event type %d", GetMessageType(event->id)); break; } }
void xf_process_cliprdr_event(xfInfo* xfi, RDP_EVENT* event) { switch (event->event_type) { case RDP_EVENT_TYPE_CB_MONITOR_READY: xf_cliprdr_process_cb_monitor_ready_event(xfi); break; case RDP_EVENT_TYPE_CB_FORMAT_LIST: xf_cliprdr_process_cb_format_list_event(xfi, (RDP_CB_FORMAT_LIST_EVENT*) event); break; case RDP_EVENT_TYPE_CB_DATA_REQUEST: xf_cliprdr_process_cb_data_request_event(xfi, (RDP_CB_DATA_REQUEST_EVENT*) event); break; case RDP_EVENT_TYPE_CB_DATA_RESPONSE: xf_cliprdr_process_cb_data_response_event(xfi, (RDP_CB_DATA_RESPONSE_EVENT*) event); break; default: DEBUG_X11_CLIPRDR("unknown event type %d", event->event_type); break; } }
static void xf_cliprdr_get_requested_targets(xfInfo* xfi) { int num; int i, j; Atom atom; int format; BYTE* data = NULL; unsigned long length, bytes_left; RDP_CB_FORMAT_LIST_EVENT* event; clipboardContext* cb = (clipboardContext*) xfi->clipboard_context; XGetWindowProperty(xfi->display, xfi->drawable, cb->property_atom, 0, 200, 0, XA_ATOM, &atom, &format, &length, &bytes_left, &data); DEBUG_X11_CLIPRDR("type=%d format=%d length=%d bytes_left=%d", (int) atom, format, (int) length, (int) bytes_left); if (length > 0) { event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(CliprdrChannel_Class, CliprdrChannel_FormatList, NULL, NULL); event->formats = (UINT32*) malloc(sizeof(UINT32) * cb->num_format_mappings); num = 0; for (i = 0; i < length; i++) { atom = ((Atom*) data)[i]; DEBUG_X11("atom %d", (int) atom); for (j = 0; j < cb->num_format_mappings; j++) { if (cb->format_mappings[j].target_format == atom) { DEBUG_X11("found format %d for atom %d", cb->format_mappings[j].format_id, (int)atom); event->formats[num++] = cb->format_mappings[j].format_id; break; } } } event->num_formats = num; XFree(data); freerdp_channels_send_event(cb->channels, (wMessage*) event); } else { if (data) XFree(data); xf_cliprdr_send_null_format_list(xfi); } }
static void xf_cliprdr_process_dib(clipboardContext* cb, uint8* data, int size) { STREAM* s; uint16 bpp; uint32 offset; uint32 ncolors; /* size should be at least sizeof(BITMAPINFOHEADER) */ if (size < 40) { DEBUG_X11_CLIPRDR("dib size %d too short", size); return; } s = stream_new(0); stream_attach(s, data, size); stream_seek(s, 14); stream_read_uint16(s, bpp); stream_read_uint32(s, ncolors); offset = 14 + 40 + (bpp <= 8 ? (ncolors == 0 ? (1 << bpp) : ncolors) * 4 : 0); stream_detach(s); stream_free(s); DEBUG_X11_CLIPRDR("offset=%d bpp=%d ncolors=%d", offset, bpp, ncolors); s = stream_new(14 + size); stream_write_uint8(s, 'B'); stream_write_uint8(s, 'M'); stream_write_uint32(s, 14 + size); stream_write_uint32(s, 0); stream_write_uint32(s, offset); stream_write(s, data, size); cb->data = stream_get_head(s); cb->data_length = stream_get_length(s); stream_detach(s); stream_free(s); }
static void xf_cliprdr_process_cb_data_request_event(xfInfo* xfi, RDP_CB_DATA_REQUEST_EVENT* event) { int i; clipboardContext* cb = (clipboardContext*) xfi->clipboard_context; DEBUG_X11_CLIPRDR("format %d", event->format); if (xf_cliprdr_is_self_owned(xfi)) { /* CB_FORMAT_RAW */ i = 0; XChangeProperty(xfi->display, xfi->drawable, cb->property_atom, XA_INTEGER, 32, PropModeReplace, (BYTE*) &event->format, 1); } else { i = xf_cliprdr_select_format_by_id(cb, event->format); } if (i < 0) { DEBUG_X11_CLIPRDR("unsupported format requested"); xf_cliprdr_send_null_data_response(xfi); } else { cb->request_index = i; DEBUG_X11_CLIPRDR("target=%d", (int) cb->format_mappings[i].target_format); XConvertSelection(xfi->display, cb->clipboard_atom, cb->format_mappings[i].target_format, cb->property_atom, xfi->drawable, CurrentTime); XFlush(xfi->display); /* After this point, we expect a SelectionNotify event from the clipboard owner. */ } }
static void xf_cliprdr_process_dib(clipboardContext* cb, BYTE* data, int size) { wStream* s; UINT16 bpp; UINT32 offset; UINT32 ncolors; /* size should be at least sizeof(BITMAPINFOHEADER) */ if (size < 40) { DEBUG_X11_CLIPRDR("dib size %d too short", size); return; } s = Stream_New(data, size); Stream_Seek(s, 14); Stream_Read_UINT16(s, bpp); Stream_Read_UINT32(s, ncolors); offset = 14 + 40 + (bpp <= 8 ? (ncolors == 0 ? (1 << bpp) : ncolors) * 4 : 0); Stream_Free(s, FALSE); DEBUG_X11_CLIPRDR("offset=%d bpp=%d ncolors=%d", offset, bpp, ncolors); s = Stream_New(NULL, 14 + size); Stream_Write_UINT8(s, 'B'); Stream_Write_UINT8(s, 'M'); Stream_Write_UINT32(s, 14 + size); Stream_Write_UINT32(s, 0); Stream_Write_UINT32(s, offset); Stream_Write(s, data, size); cb->data = Stream_Buffer(s); cb->data_length = Stream_GetPosition(s); Stream_Free(s, FALSE); }
static uint8* xf_cliprdr_process_requested_dib(uint8* data, int* size) { uint8* outbuf; /* length should be at least BMP header (14) + sizeof(BITMAPINFOHEADER) */ if (*size < 54) { DEBUG_X11_CLIPRDR("bmp length %d too short", *size); return NULL; } *size -= 14; outbuf = (uint8*) xzalloc(*size); memcpy(outbuf, data + 14, *size); return outbuf; }
static BYTE* xf_cliprdr_process_requested_dib(BYTE* data, int* size) { BYTE* outbuf; /* length should be at least BMP header (14) + sizeof(BITMAPINFOHEADER) */ if (*size < 54) { DEBUG_X11_CLIPRDR("bmp length %d too short", *capacity); return NULL; } *size -= 14; outbuf = (BYTE*) malloc(*size); ZeroMemory(outbuf, *size); memcpy(outbuf, data + 14, *size); return outbuf; }
BOOL xf_cliprdr_process_selection_notify(xfInfo* xfi, XEvent* xevent) { clipboardContext* cb = (clipboardContext*) xfi->clipboard_context; if (xevent->xselection.target == cb->targets[1]) { if (xevent->xselection.property == None) { DEBUG_X11_CLIPRDR("owner not support TARGETS. sending all format."); xf_cliprdr_send_supported_format_list(xfi); } else { xf_cliprdr_get_requested_targets(xfi); } return TRUE; } else { return xf_cliprdr_get_requested_data(xfi, xevent->xselection.target); } }
static void xf_cliprdr_process_cb_data_response_event(xfInfo* xfi, RDP_CB_DATA_RESPONSE_EVENT* event) { clipboardContext* cb = (clipboardContext*) xfi->clipboard_context; DEBUG_X11_CLIPRDR("size=%d", event->size); if (!cb->respond) { DEBUG_X11_CLIPRDR("unexpected data"); return; } if (event->size == 0) { cb->respond->xselection.property = None; } else { if (cb->data) { free(cb->data); cb->data = NULL; } switch (cb->data_format) { case CB_FORMAT_RAW: case CB_FORMAT_PNG: case CB_FORMAT_JPEG: case CB_FORMAT_GIF: cb->data = event->data; cb->data_length = event->size; event->data = NULL; event->size = 0; break; case CB_FORMAT_TEXT: xf_cliprdr_process_text(cb, event->data, event->size); break; case CB_FORMAT_UNICODETEXT: xf_cliprdr_process_unicodetext(cb, event->data, event->size); break; case CB_FORMAT_DIB: xf_cliprdr_process_dib(cb, event->data, event->size); break; case CB_FORMAT_HTML: xf_cliprdr_process_html(cb, event->data, event->size); break; default: cb->respond->xselection.property = None; break; } xf_cliprdr_provide_data(xfi, cb->respond); } XSendEvent(xfi->display, cb->respond->xselection.requestor, 0, 0, cb->respond); XFlush(xfi->display); free(cb->respond); cb->respond = NULL; }
static BOOL xf_cliprdr_get_requested_data(xfInfo* xfi, Atom target) { Atom type; int format; BYTE* data = NULL; BOOL has_data = FALSE; unsigned long length, bytes_left, dummy; clipboardContext* cb = (clipboardContext*) xfi->clipboard_context; if ((cb->request_index < 0) || (cb->format_mappings[cb->request_index].target_format != target)) { DEBUG_X11_CLIPRDR("invalid target"); xf_cliprdr_send_null_data_response(xfi); return FALSE; } XGetWindowProperty(xfi->display, xfi->drawable, cb->property_atom, 0, 0, 0, target, &type, &format, &length, &bytes_left, &data); DEBUG_X11_CLIPRDR("type=%d format=%d bytes=%d request_index=%d", (int) type, format, (int) bytes_left, cb->request_index); if (data) { XFree(data); data = NULL; } if (bytes_left <= 0 && !cb->incr_starts) { DEBUG_X11("no data"); } else if (type == cb->incr_atom) { DEBUG_X11("INCR started"); cb->incr_starts = TRUE; if (cb->incr_data) { free(cb->incr_data); cb->incr_data = NULL; } cb->incr_data_length = 0; /* Data will be followed in PropertyNotify event */ has_data = TRUE; } else { if (bytes_left <= 0) { /* INCR finish */ data = cb->incr_data; cb->incr_data = NULL; bytes_left = cb->incr_data_length; cb->incr_data_length = 0; cb->incr_starts = 0; DEBUG_X11("INCR finished"); has_data = TRUE; } else if (XGetWindowProperty(xfi->display, xfi->drawable, cb->property_atom, 0, bytes_left, 0, target, &type, &format, &length, &dummy, &data) == Success) { if (cb->incr_starts) { bytes_left = length * format / 8; DEBUG_X11("%d bytes", (int)bytes_left); cb->incr_data = (BYTE*) realloc(cb->incr_data, cb->incr_data_length + bytes_left); CopyMemory(cb->incr_data + cb->incr_data_length, data, bytes_left); cb->incr_data_length += bytes_left; XFree(data); data = NULL; } has_data = TRUE; } else { DEBUG_X11_CLIPRDR("XGetWindowProperty failed"); } } XDeleteProperty(xfi->display, xfi->drawable, cb->property_atom); xf_cliprdr_process_requested_data(xfi, has_data, data, (int) bytes_left); if (data) XFree(data); return TRUE; }
BOOL xf_cliprdr_process_selection_request(xfInfo* xfi, XEvent* xevent) { int i; int fmt; Atom type; UINT32 format; XEvent* respond; UINT32 alt_format; BYTE* data = NULL; BOOL delay_respond; unsigned long length, bytes_left; clipboardContext* cb = (clipboardContext*) xfi->clipboard_context; DEBUG_X11_CLIPRDR("target=%d", (int) xevent->xselectionrequest.target); if (xevent->xselectionrequest.owner != xfi->drawable) { DEBUG_X11_CLIPRDR("not owner"); return FALSE; } delay_respond = FALSE; respond = (XEvent*) malloc(sizeof(XEvent)); ZeroMemory(respond, sizeof(XEvent)); respond->xselection.property = None; respond->xselection.type = SelectionNotify; respond->xselection.display = xevent->xselectionrequest.display; respond->xselection.requestor = xevent->xselectionrequest.requestor; respond->xselection.selection = xevent->xselectionrequest.selection; respond->xselection.target = xevent->xselectionrequest.target; respond->xselection.time = xevent->xselectionrequest.time; if (xevent->xselectionrequest.target == cb->targets[0]) /* TIMESTAMP */ { /* TODO */ DEBUG_X11_CLIPRDR("target: TIMESTAMP (unimplemented)"); } else if (xevent->xselectionrequest.target == cb->targets[1]) /* TARGETS */ { /* Someone else requests our available formats */ DEBUG_X11_CLIPRDR("target: TARGETS"); respond->xselection.property = xevent->xselectionrequest.property; xf_cliprdr_provide_targets(xfi, respond); } else { DEBUG_X11_CLIPRDR("target: other"); i = xf_cliprdr_select_format_by_atom(cb, xevent->xselectionrequest.target); if (i >= 0 && xevent->xselectionrequest.requestor != xfi->drawable) { format = cb->format_mappings[i].format_id; alt_format = format; if (format == CB_FORMAT_RAW) { if (XGetWindowProperty(xfi->display, xevent->xselectionrequest.requestor, cb->property_atom, 0, 4, 0, XA_INTEGER, &type, &fmt, &length, &bytes_left, &data) != Success) { DEBUG_X11_CLIPRDR("XGetWindowProperty failed"); } if (data) { CopyMemory(&alt_format, data, 4); XFree(data); } } DEBUG_X11_CLIPRDR("provide format 0x%04x alt_format 0x%04x", format, alt_format); if ((cb->data != 0) && (format == cb->data_format) && (alt_format == cb->data_alt_format)) { /* Cached clipboard data available. Send it now */ respond->xselection.property = xevent->xselectionrequest.property; xf_cliprdr_provide_data(xfi, respond); } else if (cb->respond) { DEBUG_X11_CLIPRDR("duplicated request"); } else { /** * Send clipboard data request to the server. * Response will be postponed after receiving the data */ if (cb->data) { free(cb->data); cb->data = NULL; } respond->xselection.property = xevent->xselectionrequest.property; cb->respond = respond; cb->data_format = format; cb->data_alt_format = alt_format; delay_respond = TRUE; xf_cliprdr_send_data_request(xfi, alt_format); } } } if (delay_respond == FALSE) { XSendEvent(xfi->display, xevent->xselectionrequest.requestor, 0, 0, respond); XFlush(xfi->display); free(respond); } return TRUE; }