Commit 9e02e023 authored by Jon Turney's avatar Jon Turney

hw/xwin: xcbify clipboard integration

Convert clipboard integration code from libX11 to xcb

This drops support for COMPOUND_TEXT.  Presumably some ancient
(pre-2000) clients exist which support that, but not UTF8_STRING, but we
don't have an example to test with. (Given the nature of the thing, the
users of those clients probably work in CJK languages)

Supporting COMPOUND_TEXT would also involve writing (or extracting from
Xlib) support for the ISO 2022 encoding.

v2:
Fix the length of text property set by a SelectionRequest

The length of the text property is not neccessarily the same as the
length of the clipboard text before it is d2u converted (specifically,
if that contains any '\r\n' sequences, it will be shorter as they are
now just '\n')
parent f4936de7
......@@ -2081,7 +2081,7 @@ if test "x$XWIN" = xyes; then
AC_DEFINE_UNQUOTED(__VENDORDWEBSUPPORT__, ["$VENDOR_WEB"], [Vendor web address for support])
AC_CHECK_TOOL(WINDRES, windres)
PKG_CHECK_MODULES([XWINMODULES],[x11 xau xdmcp xfixes x11-xcb xcb-aux xcb-composite xcb-image xcb-ewmh xcb-icccm])
PKG_CHECK_MODULES([XWINMODULES],[x11 xau xdmcp x11-xcb xcb-aux xcb-composite xcb-image xcb-ewmh xcb-icccm xcb-xfixes])
if test "x$WINDOWSDRI" = xauto; then
PKG_CHECK_EXISTS([windowsdriproto], [WINDOWSDRI=yes], [WINDOWSDRI=no])
......
......@@ -31,8 +31,9 @@
#ifndef WINCLIPBOARD_INTERNAL_H
#define WINCLIPBOARD_INTERNAL_H
/* X headers */
#include <X11/Xlib.h>
#include <xcb/xproto.h>
#include <X11/Xfuncproto.h> // for _X_ATTRIBUTE_PRINTF
#include <X11/Xdefs.h> // for Bool type
/* Windows headers */
#include <X11/Xwindows.h>
......@@ -67,15 +68,14 @@ void
* winclipboardthread.c
*/
typedef struct
{
Atom atomClipboard;
Atom atomLocalProperty;
Atom atomUTF8String;
Atom atomCompoundText;
Atom atomTargets;
Atom atomIncr;
xcb_atom_t atomClipboard;
xcb_atom_t atomLocalProperty;
xcb_atom_t atomUTF8String;
xcb_atom_t atomCompoundText;
xcb_atom_t atomTargets;
xcb_atom_t atomIncr;
} ClipboardAtoms;
/*
......@@ -89,8 +89,8 @@ winClipboardWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
typedef struct
{
Display *pClipboardDisplay;
Window iClipboardWindow;
xcb_connection_t *pClipboardDisplay;
xcb_window_t iClipboardWindow;
ClipboardAtoms *atoms;
} ClipboardWindowCreationParams;
......@@ -100,17 +100,17 @@ typedef struct
typedef struct
{
Atom *targetList;
xcb_atom_t *targetList;
unsigned char *incr;
unsigned long int incrsize;
} ClipboardConversionData;
int
winClipboardFlushXEvents(HWND hwnd,
Window iWindow, Display * pDisplay, ClipboardConversionData *data, ClipboardAtoms *atom);
xcb_window_t iWindow, xcb_connection_t * pDisplay,
ClipboardConversionData *data, ClipboardAtoms *atoms);
Atom
xcb_atom_t
winClipboardGetLastOwnedSelectionAtom(ClipboardAtoms *atoms);
void
......
......@@ -12,8 +12,10 @@ xwin_clipboard = static_library(
include_directories: inc,
c_args: '-DHAVE_XWIN_CONFIG_H',
dependencies: [
dependency('x11'),
dependency('xfixes'),
dependency('xcb'),
dependency('xcb-aux'),
dependency('xcb-icccm'),
dependency('xcb-xfixes'),
socket_dep,
],
)
......@@ -28,7 +30,6 @@ executable(
srcs_xwinclip,
link_with: xwin_clipboard,
link_args: ['-lgdi32', '-lpthread'],
dependencies: [dependency('x11')],
install: true,
)
......
......@@ -32,15 +32,6 @@
#include <xwin-config.h>
#endif
/*
* Including any server header might define the macro _XSERVER64 on 64 bit machines.
* That macro must _NOT_ be defined for Xlib client code, otherwise bad things happen.
* So let's undef that macro if necessary.
*/
#ifdef _XSERVER64
#undef _XSERVER64
#endif
#include <stdlib.h>
#include "internal.h"
......
......@@ -36,19 +36,9 @@
#define HAS_WINSOCK 1
#endif
/*
* Including any server header might define the macro _XSERVER64 on 64 bit machines.
* That macro must _NOT_ be defined for Xlib client code, otherwise bad things happen.
* So let's undef that macro if necessary.
*/
#ifdef _XSERVER64
#undef _XSERVER64
#endif
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <setjmp.h>
#include <pthread.h>
#include <sys/param.h> // for MAX() macro
......@@ -58,8 +48,11 @@
#include <errno.h>
#endif
#include <X11/Xatom.h>
#include <X11/extensions/Xfixes.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xfixes.h>
#include "winclipboard.h"
#include "internal.h"
......@@ -77,9 +70,6 @@
*/
static HWND g_hwndClipboard = NULL;
static jmp_buf g_jmpEntry;
static XIOErrorHandler g_winClipboardOldIOErrorHandler;
static pthread_t g_winClipboardProcThread;
int xfixes_event_base;
int xfixes_error_base;
......@@ -89,13 +79,23 @@ int xfixes_error_base;
*/
static HWND
winClipboardCreateMessagingWindow(Display *pDisplay, Window iWindow, ClipboardAtoms *atoms);
static int
winClipboardErrorHandler(Display * pDisplay, XErrorEvent * pErr);
winClipboardCreateMessagingWindow(xcb_connection_t *conn, xcb_window_t iWindow, ClipboardAtoms *atoms);
static int
winClipboardIOErrorHandler(Display * pDisplay);
static xcb_atom_t
intern_atom(xcb_connection_t *conn, const char *atomName)
{
xcb_intern_atom_reply_t *atom_reply;
xcb_intern_atom_cookie_t atom_cookie;
xcb_atom_t atom = XCB_ATOM_NONE;
atom_cookie = xcb_intern_atom(conn, 0, strlen(atomName), atomName);
atom_reply = xcb_intern_atom_reply(conn, atom_cookie, NULL);
if (atom_reply) {
atom = atom_reply->atom;
free(atom_reply);
}
return atom;
}
/*
* Create X11 and Win32 messaging windows, and run message processing loop
......@@ -104,13 +104,12 @@ static int
*/
Bool
winClipboardProc(char *szDisplay)
winClipboardProc(char *szDisplay, xcb_auth_info_t *auth_info)
{
ClipboardAtoms atoms;
int iReturn;
HWND hwnd = NULL;
int iConnectionNumber = 0;
#ifdef HAS_DEVWINDOWS
int fdMessageQueue = 0;
#else
......@@ -118,54 +117,27 @@ winClipboardProc(char *szDisplay)
#endif
fd_set fdsRead;
int iMaxDescriptor;
Display *pDisplay = NULL;
Window iWindow = None;
xcb_connection_t *conn;
xcb_window_t iWindow = XCB_NONE;
int iSelectError;
Bool fShutdown = FALSE;
static Bool fErrorHandlerSet = FALSE;
ClipboardConversionData data;
int screen;
winDebug("winClipboardProc - Hello\n");
/* Allow multiple threads to access Xlib */
if (XInitThreads() == 0) {
ErrorF("winClipboardProc - XInitThreads failed.\n");
goto winClipboardProc_Exit;
}
/* See if X supports the current locale */
if (XSupportsLocale() == False) {
ErrorF("winClipboardProc - Warning: Locale not supported by X.\n");
}
g_winClipboardProcThread = pthread_self();
/* Set error handler */
if (!fErrorHandlerSet) {
XSetErrorHandler(winClipboardErrorHandler);
g_winClipboardOldIOErrorHandler =
XSetIOErrorHandler(winClipboardIOErrorHandler);
fErrorHandlerSet = TRUE;
}
/* Set jump point for Error exits */
if (setjmp(g_jmpEntry)) {
ErrorF("winClipboardProc - setjmp returned for IO Error Handler.\n");
goto winClipboardProc_Done;
}
/* Make sure that the display opened */
pDisplay = XOpenDisplay(szDisplay);
if (pDisplay == NULL) {
conn = xcb_connect_to_display_with_auth_info(szDisplay, auth_info, &screen);
if (xcb_connection_has_error(conn)) {
ErrorF("winClipboardProc - Failed opening the display, giving up\n");
goto winClipboardProc_Done;
}
ErrorF("winClipboardProc - XOpenDisplay () returned and "
ErrorF("winClipboardProc - xcb_connect () returned and "
"successfully opened the display.\n");
/* Get our connection number */
iConnectionNumber = ConnectionNumber(pDisplay);
iConnectionNumber = xcb_get_file_descriptor(conn);
#ifdef HAS_DEVWINDOWS
/* Open a file descriptor for the windows message queue */
......@@ -181,56 +153,75 @@ winClipboardProc(char *szDisplay)
iMaxDescriptor = iConnectionNumber + 1;
#endif
if (!XFixesQueryExtension(pDisplay, &xfixes_event_base, &xfixes_error_base))
const xcb_query_extension_reply_t *xfixes_query;
xfixes_query = xcb_get_extension_data(conn, &xcb_xfixes_id);
if (!xfixes_query->present)
ErrorF ("winClipboardProc - XFixes extension not present\n");
xfixes_event_base = xfixes_query->first_event;
xfixes_error_base = xfixes_query->first_error;
/* Must advise server of XFIXES version we require */
xcb_xfixes_query_version_unchecked(conn, 1, 0);
/* Create atoms */
atoms.atomClipboard = XInternAtom(pDisplay, "CLIPBOARD", False);
atoms.atomLocalProperty = XInternAtom (pDisplay, "CYGX_CUT_BUFFER", False);
atoms.atomUTF8String = XInternAtom (pDisplay, "UTF8_STRING", False);
atoms.atomCompoundText = XInternAtom (pDisplay, "COMPOUND_TEXT", False);
atoms.atomTargets = XInternAtom (pDisplay, "TARGETS", False);
atoms.atomIncr = XInternAtom (pDisplay, "INCR", False);
atoms.atomClipboard = intern_atom(conn, "CLIPBOARD");
atoms.atomLocalProperty = intern_atom(conn, "CYGX_CUT_BUFFER");
atoms.atomUTF8String = intern_atom(conn, "UTF8_STRING");
atoms.atomCompoundText = intern_atom(conn, "COMPOUND_TEXT");
atoms.atomTargets = intern_atom(conn, "TARGETS");
atoms.atomIncr = intern_atom(conn, "INCR");
xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screen);
xcb_window_t root_window_id = root_screen->root;
/* Create a messaging window */
iWindow = XCreateSimpleWindow(pDisplay,
DefaultRootWindow(pDisplay),
1, 1,
500, 500,
0,
BlackPixel(pDisplay, 0),
BlackPixel(pDisplay, 0));
if (iWindow == 0) {
iWindow = xcb_generate_id(conn);
xcb_void_cookie_t cookie = xcb_create_window_checked(conn,
XCB_COPY_FROM_PARENT,
iWindow,
root_window_id,
1, 1,
500, 500,
0,
XCB_WINDOW_CLASS_INPUT_ONLY,
XCB_COPY_FROM_PARENT,
0,
NULL);
xcb_generic_error_t *error;
if ((error = xcb_request_check(conn, cookie))) {
ErrorF("winClipboardProc - Could not create an X window.\n");
free(error);
goto winClipboardProc_Done;
}
XStoreName(pDisplay, iWindow, "xwinclip");
xcb_icccm_set_wm_name(conn, iWindow, XCB_ATOM_STRING, 8, strlen("xwinclip"), "xwinclip");
/* Select event types to watch */
if (XSelectInput(pDisplay, iWindow, PropertyChangeMask) == BadWindow)
ErrorF("winClipboardProc - XSelectInput generated BadWindow "
"on messaging window\n");
XFixesSelectSelectionInput (pDisplay,
iWindow,
XA_PRIMARY,
XFixesSetSelectionOwnerNotifyMask |
XFixesSelectionWindowDestroyNotifyMask |
XFixesSelectionClientCloseNotifyMask);
const static uint32_t values[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
cookie = xcb_change_window_attributes_checked(conn, iWindow, XCB_CW_EVENT_MASK, values);
if ((error = xcb_request_check(conn, cookie))) {
ErrorF("winClipboardProc - Could not set event mask on messaging window\n");
free(error);
}
XFixesSelectSelectionInput (pDisplay,
iWindow,
atoms.atomClipboard,
XFixesSetSelectionOwnerNotifyMask |
XFixesSelectionWindowDestroyNotifyMask |
XFixesSelectionClientCloseNotifyMask);
xcb_xfixes_select_selection_input(conn,
iWindow,
XCB_ATOM_PRIMARY,
XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE);
xcb_xfixes_select_selection_input(conn,
iWindow,
atoms.atomClipboard,
XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE);
/* Initialize monitored selection state */
winClipboardInitMonitoredSelections();
/* Create Windows messaging window */
hwnd = winClipboardCreateMessagingWindow(pDisplay, iWindow, &atoms);
hwnd = winClipboardCreateMessagingWindow(conn, iWindow, &atoms);
/* Save copy of HWND */
g_hwndClipboard = hwnd;
......@@ -238,20 +229,18 @@ winClipboardProc(char *szDisplay)
/* Assert ownership of selections if Win32 clipboard is owned */
if (NULL != GetClipboardOwner()) {
/* PRIMARY */
iReturn = XSetSelectionOwner(pDisplay, XA_PRIMARY,
iWindow, CurrentTime);
if (iReturn == BadAtom || iReturn == BadWindow ||
XGetSelectionOwner(pDisplay, XA_PRIMARY) != iWindow) {
cookie = xcb_set_selection_owner_checked(conn, iWindow, XCB_ATOM_PRIMARY, XCB_CURRENT_TIME);
if ((error = xcb_request_check(conn, cookie))) {
ErrorF("winClipboardProc - Could not set PRIMARY owner\n");
free(error);
goto winClipboardProc_Done;
}
/* CLIPBOARD */
iReturn = XSetSelectionOwner(pDisplay, atoms.atomClipboard,
iWindow, CurrentTime);
if (iReturn == BadAtom || iReturn == BadWindow ||
XGetSelectionOwner(pDisplay, atoms.atomClipboard) != iWindow) {
cookie = xcb_set_selection_owner_checked(conn, iWindow, atoms.atomClipboard, XCB_CURRENT_TIME);
if ((error = xcb_request_check(conn, cookie))) {
ErrorF("winClipboardProc - Could not set CLIPBOARD owner\n");
free(error);
goto winClipboardProc_Done;
}
}
......@@ -263,8 +252,7 @@ winClipboardProc(char *szDisplay)
while (1) {
/* Process X events */
winClipboardFlushXEvents(hwnd,
iWindow, pDisplay, &data, &atoms);
winClipboardFlushXEvents(hwnd, iWindow, conn, &data, &atoms);
/* Process Windows messages */
if (!winClipboardFlushWindowsMessageQueue(hwnd)) {
......@@ -274,7 +262,7 @@ winClipboardProc(char *szDisplay)
}
/* We need to ensure that all pending requests are sent */
XFlush(pDisplay);
xcb_flush(conn);
/* Setup the file descriptor set */
/*
......@@ -346,7 +334,6 @@ winClipboardProc(char *szDisplay)
#endif
}
winClipboardProc_Exit:
/* broke out of while loop on a shutdown message */
fShutdown = TRUE;
......@@ -357,12 +344,13 @@ winClipboardProc(char *szDisplay)
}
/* Close our X window */
if (pDisplay && iWindow) {
iReturn = XDestroyWindow(pDisplay, iWindow);
if (iReturn == BadWindow)
ErrorF("winClipboardProc - XDestroyWindow returned BadWindow.\n");
if (!xcb_connection_has_error(conn) && iWindow) {
cookie = xcb_destroy_window_checked(conn, iWindow);
if ((error = xcb_request_check(conn, cookie)))
ErrorF("winClipboardProc - XDestroyWindow failed.\n");
else
ErrorF("winClipboardProc - XDestroyWindow succeeded.\n");
free(error);
}
#ifdef HAS_DEVWINDOWS
......@@ -371,26 +359,15 @@ winClipboardProc(char *szDisplay)
close(fdMessageQueue);
#endif
#if 0
/*
* FIXME: XCloseDisplay hangs if we call it
*
* XCloseDisplay() calls XSync(), so any outstanding errors are reported.
* If we are built into the server, this can deadlock if the server is
* in the process of exiting and waiting for this thread to exit.
* xcb_disconnect() does not sync, so is safe to call even when we are built
* into the server. Unlike XCloseDisplay() there will be no deadlock if the
* server is in the process of exiting and waiting for this thread to exit.
*/
/* Discard any remaining events */
XSync(pDisplay, TRUE);
/* Select event types to watch */
XSelectInput(pDisplay, DefaultRootWindow(pDisplay), None);
/* Close our X display */
if (pDisplay) {
XCloseDisplay(pDisplay);
if (!xcb_connection_has_error(conn)) {
/* Close our X display */
xcb_disconnect(conn);
}
#endif
/* global clipboard variable reset */
g_hwndClipboard = NULL;
......@@ -403,7 +380,7 @@ winClipboardProc(char *szDisplay)
*/
static HWND
winClipboardCreateMessagingWindow(Display *pDisplay, Window iWindow, ClipboardAtoms *atoms)
winClipboardCreateMessagingWindow(xcb_connection_t *conn, xcb_window_t iWindow, ClipboardAtoms *atoms)
{
WNDCLASSEX wc;
ClipboardWindowCreationParams cwcp;
......@@ -425,7 +402,7 @@ winClipboardCreateMessagingWindow(Display *pDisplay, Window iWindow, ClipboardAt
RegisterClassEx(&wc);
/* Information to be passed to WM_CREATE */
cwcp.pClipboardDisplay = pDisplay;
cwcp.pClipboardDisplay = conn;
cwcp.iClipboardWindow = iWindow;
cwcp.atoms = atoms;
......@@ -453,42 +430,6 @@ winClipboardCreateMessagingWindow(Display *pDisplay, Window iWindow, ClipboardAt
return hwnd;
}
/*
* winClipboardErrorHandler - Our application specific error handler
*/
static int
winClipboardErrorHandler(Display * pDisplay, XErrorEvent * pErr)
{
char pszErrorMsg[100];
XGetErrorText(pDisplay, pErr->error_code, pszErrorMsg, sizeof(pszErrorMsg));
ErrorF("winClipboardErrorHandler - ERROR: \n\t%s\n"
"\tSerial: %lu, Request Code: %d, Minor Code: %d\n",
pszErrorMsg, pErr->serial, pErr->request_code, pErr->minor_code);
return 0;
}
/*
* winClipboardIOErrorHandler - Our application specific IO error handler
*/
static int
winClipboardIOErrorHandler(Display * pDisplay)
{
ErrorF("winClipboardIOErrorHandler!\n");
if (pthread_equal(pthread_self(), g_winClipboardProcThread)) {
/* Restart at the main entry point */
longjmp(g_jmpEntry, 2);
}
if (g_winClipboardOldIOErrorHandler)
g_winClipboardOldIOErrorHandler(pDisplay);
return 0;
}
void
winClipboardWindowDestroy(void)
{
......
......@@ -27,7 +27,10 @@
#ifndef WINCLIPBOARD_H
#define WINCLIPBOARD_H
Bool winClipboardProc(char *szDisplay);
#include <X11/Xdefs.h> // for Bool type
#include <xcb/xcb.h>
Bool winClipboardProc(char *szDisplay, xcb_auth_info_t *auth_info);
void winFixClipboardChain(void);
......
......@@ -36,20 +36,12 @@
#include <xwin-config.h>
#endif
/*
* Including any server header might define the macro _XSERVER64 on 64 bit machines.
* That macro must _NOT_ be defined for Xlib client code, otherwise bad things happen.
* So let's undef that macro if necessary.
*/
#ifdef _XSERVER64
#undef _XSERVER64
#endif
#include <sys/types.h>
#include <sys/time.h>
#include <limits.h>
#include <X11/Xatom.h>
#include <xcb/xproto.h>
#include <xcb/xcb_aux.h>
#include "internal.h"
#include "winclipboard.h"
......@@ -65,7 +57,7 @@
*/
static int
winProcessXEventsTimeout(HWND hwnd, Window iWindow, Display * pDisplay,
winProcessXEventsTimeout(HWND hwnd, xcb_window_t iWindow, xcb_connection_t *conn,
ClipboardConversionData *data, ClipboardAtoms *atoms, int iTimeoutSec)
{
int iConnNumber;
......@@ -76,7 +68,7 @@ winProcessXEventsTimeout(HWND hwnd, Window iWindow, Display * pDisplay,
iTimeoutSec);
/* Get our connection number */
iConnNumber = ConnectionNumber(pDisplay);
iConnNumber = xcb_get_file_descriptor(conn);
/* Loop for X events */
while (1) {
......@@ -84,7 +76,7 @@ winProcessXEventsTimeout(HWND hwnd, Window iWindow, Display * pDisplay,
long remainingTime;
/* Process X events */
iReturn = winClipboardFlushXEvents(hwnd, iWindow, pDisplay, data, atoms);
iReturn = winClipboardFlushXEvents(hwnd, iWindow, conn, data, atoms);
winDebug("winProcessXEventsTimeout () - winClipboardFlushXEvents returned %d\n", iReturn);
......@@ -94,7 +86,7 @@ winProcessXEventsTimeout(HWND hwnd, Window iWindow, Display * pDisplay,
}
/* We need to ensure that all pending requests are sent */
XFlush(pDisplay);
xcb_flush(conn);
/* Setup the file descriptor set */
FD_ZERO(&fdsRead);
......@@ -136,8 +128,8 @@ winProcessXEventsTimeout(HWND hwnd, Window iWindow, Display * pDisplay,
LRESULT CALLBACK
winClipboardWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static Display *pDisplay;
static Window iWindow;
static xcb_connection_t *conn;
static xcb_window_t iWindow;
static ClipboardAtoms *atoms;
static Bool fRunning;
......@@ -166,7 +158,7 @@ winClipboardWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
winDebug("winClipboardWindowProc - WM_CREATE\n");
pDisplay = cwcp->pClipboardDisplay;
conn = cwcp->pClipboardDisplay;
iWindow = cwcp->iClipboardWindow;
atoms = cwcp->atoms;
fRunning = TRUE;
......@@ -177,7 +169,8 @@ winClipboardWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_CLIPBOARDUPDATE:
{
int iReturn;
xcb_generic_error_t *error;