From 4e3d04d350aea8585667e3be18be2d71e4b2b84e Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 21 Aug 2012 18:03:51 +0900 Subject: [PATCH] upload tizen2.0 source --- INSTALL | 229 +++++++ Makefile.am | 3 + NEWS | 55 ++ acinclude.m4 | 0 configure.ac | 45 +- doc/Makefile.am | 4 +- doc/tutorial/index.html | 4 +- doc/xkb_internals | 49 ++ doc/xkb_issues | 38 ++ packaging/libxcb.spec | 96 +-- src/Makefile.am | 59 +- src/c-client.xsl | 1551 ----------------------------------------------- src/c_client.py | 1527 ++++++++++++++++++++++++++++++++++++++++++---- src/xcb.h | 44 +- src/xcb_auth.c | 48 +- src/xcb_conn.c | 114 +++- src/xcb_in.c | 269 ++++---- src/xcb_out.c | 120 ++-- src/xcb_util.c | 206 ++++--- src/xcb_windefs.h | 45 ++ src/xcbext.h | 16 +- src/xcbint.h | 7 +- xcb-xkb.pc.in | 11 + 23 files changed, 2483 insertions(+), 2057 deletions(-) create mode 100644 INSTALL mode change 100755 => 100644 NEWS mode change 100755 => 100644 acinclude.m4 mode change 100755 => 100644 configure.ac mode change 100755 => 100644 doc/tutorial/index.html create mode 100644 doc/xkb_internals create mode 100644 doc/xkb_issues delete mode 100755 src/c-client.xsl mode change 100755 => 100644 src/c_client.py mode change 100755 => 100644 src/xcb_auth.c mode change 100755 => 100644 src/xcb_conn.c mode change 100755 => 100644 src/xcb_in.c mode change 100755 => 100644 src/xcb_out.c mode change 100755 => 100644 src/xcb_util.c create mode 100644 src/xcb_windefs.h mode change 100755 => 100644 src/xcbint.h create mode 100644 xcb-xkb.pc.in diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..54caf7c --- /dev/null +++ b/INSTALL @@ -0,0 +1,229 @@ +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am index ff9dea1..1f88b72 100644 --- a/Makefile.am +++ b/Makefile.am @@ -58,6 +58,9 @@ endif if BUILD_XINPUT pkgconfig_DATA += xcb-xinput.pc endif +if BUILD_XKB +pkgconfig_DATA += xcb-xkb.pc +endif if BUILD_XPRINT pkgconfig_DATA += xcb-xprint.pc endif diff --git a/NEWS b/NEWS old mode 100755 new mode 100644 index 7969813..66d6253 --- a/NEWS +++ b/NEWS @@ -1,3 +1,58 @@ +Release 1.8.1 (2012-03-09) +========================== +- Fix a busy loop on BSD and Mac OS +- Bump xcb-proto requirement +- Fallback to TCP if no protocol is specified and the UNIX connection fails +- Update use of error_connection under WIN32 to _xcb_conn_ret_error() +- Fix build of xcb_auth.c with XDMCP on WIN32 +- Revert "Fix include order with Xdmcp on WIN32" +- darwin: Use read(2) rather than recv(2) +- Add xkb_internals and xkb_issues to EXTRA_DIST. + +Release 1.8 (2012-01-11) +======================== +- xcb_disconnect: call shutdown() to force a disconnect +- Use special path to sockets when running under Solaris Trusted Extensions +- Remove unused DECnet code +- Add #include to xcb_conn.c +- Make launchd code in xcb_util.c match surrounding code indent levels +- If protocol is "unix", use a Unix domain socket, not TCP +- Added more error states and removed global error_connection +- Handle XGE events with the "send event" flag +- added xcb_sumof() with restriction to uint8_t +- xkb: updated configure.ac/Makefile.am +- xkb: added pkg config file +- special case 'intermixed variable and fixed size fields': fixed reply side, needs testing +- added accessors for special cases +- Add support for building with Python 3 +- Insert, not append explicit xcbgen dir python path +- xcb_request_check: Sync even if an event was read for this sequence. +- _xcb_conn_wait: Shut down the connection on unexpected poll() events. +- xcb_send_request: Send all requests using a common internal send_request. +- xcb_request_check: Hold the I/O lock while deciding to sync. +- xcb_discard_reply: Simplify by re-using poll_for_reply helper. +- xcb_in: Use 64-bit sequence numbers internally everywhere. +- Enable AM_SILENT_RULES on automake 1.11 or newer. +- Factor reader_list management out of wait_for_reply. +- Dequeue readers that can't receive any new responses. +- Delete the old c-client.xsl. +- Keep ALIGNOF definition out of the public namespace. +- darwin: Don't use poll() when expected to run on darwin10 and prior +- Add Win32 +- Allow disconnecting connections that are in error state. +- Make xcb_take_socket keep flushing until idle +- Support pre-IPv6 systems (without getaddrinfo) +- Drop AI_ADDRCONFIG when resolving TCP addresses +- xcb_auth: Fix memory leak in _xcb_get_auth_info. +- Don't emit out-of-module sizeof definitions +- Clean up a couple of warnings in xprint +- Prevent reply waiters from being blocked. +- Prevent theoretical double free and leak on get_peer_sock_name. +- Introduce a variant of xcb_poll_for_event for examining event queue. +- xcb_take_socket: Document sequence wrap requirements +- Compute alignment correctly +- Fix a dead-lock due to xcb_poll_for_reply + Release 1.7 (2010-08-13) ======================== - Always wake up readers after writing diff --git a/acinclude.m4 b/acinclude.m4 old mode 100755 new mode 100644 diff --git a/configure.ac b/configure.ac old mode 100755 new mode 100644 index 4178989..e94e32c --- a/configure.ac +++ b/configure.ac @@ -3,18 +3,20 @@ AC_PREREQ(2.57) AC_INIT([libxcb], - 1.7, + 1.8.1, [xcb@lists.freedesktop.org]) AC_CONFIG_SRCDIR([xcb.pc.in]) AM_INIT_AUTOMAKE([foreign dist-bzip2]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -AM_PATH_PYTHON([2.5]) +AM_PATH_PYTHON([2.6]) PKG_CHECK_MODULES(CHECK, [check >= 0.9.4], [HAVE_CHECK=yes], [HAVE_CHECK=no]) AM_CONDITIONAL(HAVE_CHECK, test x$HAVE_CHECK = xyes) AC_CONFIG_HEADERS([src/config.h]) +AC_LIBTOOL_WIN32_DLL AC_PROG_LIBTOOL AC_PROG_CC @@ -32,7 +34,7 @@ fi AC_SUBST(HTML_CHECK_RESULT) # Checks for pkg-config packages -PKG_CHECK_MODULES(XCBPROTO, xcb-proto >= 1.6) +PKG_CHECK_MODULES(XCBPROTO, xcb-proto >= 1.7) NEEDED="pthread-stubs xau >= 0.99.2" PKG_CHECK_MODULES(NEEDED, $NEEDED) @@ -72,12 +74,21 @@ AC_HEADER_STDC AC_SEARCH_LIBS(getaddrinfo, socket) AC_SEARCH_LIBS(connect, socket) +have_win32="no" +lt_enable_auto_import="" case $host_os in +mingw*) + have_win32="yes" + lt_enable_auto_import="-Wl,--enable-auto-import" + ;; linux*) AC_DEFINE([HAVE_ABSTRACT_SOCKETS], 1, [Define if your platform supports abstract sockets]) ;; esac +AC_SUBST(lt_enable_auto_import) +AM_CONDITIONAL([XCB_HAVE_WIN32], [test "x${have_win32}" = "xyes"]) + dnl define buffer queue size AC_ARG_WITH([queue-size], AC_HELP_STRING([--with-queue-size=SIZE], @@ -95,6 +106,10 @@ AC_CHECK_MEMBER([struct sockaddr_un.sun_len], #include ]) +dnl check for support for Solaris Trusted Extensions +AC_CHECK_HEADERS([tsol/label.h]) +AC_CHECK_FUNCS([is_system_labeled]) + xcbincludedir='${includedir}/xcb' AC_SUBST(xcbincludedir) @@ -120,9 +135,26 @@ AC_PREREQ([2.59c], [], [AC_SUBST([htmldir], [m4_ifset([AC_PACKAGE_TARNAME], XCB_CHECK_DOXYGEN() +AC_CHECK_FUNC(getaddrinfo, [AC_DEFINE(HAVE_GETADDRINFO, 1, [getaddrinfo() function is available])], ) + case $host_os in - # darwin has poll() but can't be used to poll character devices (atleast through SnowLeopard) - darwin*) ;; + # darwin through Snow Leopard has poll() but can't be used to poll character devices. + darwin@<:@789@:>@*|darwin10*) ;; + darwin*) + _ac_xorg_macosx_version_min="" + if echo $CPPFLAGS $CFLAGS | grep -q mmacosx-version-min ; then + _ac_xorg_macosx_version_min=`echo $CPPFLAGS $CFLAGS | sed 's/^.*-mmacosx-version-min=\(@<:@^ @:>@*\).*$/\1/'` + else + _ac_xorg_macosx_version_min=$MACOSX_DEPLOYMENT_TARGET + fi + case $_ac_xorg_macosx_version_min in + 10.@<:@0123456@:>@|10.@<:@0123456@:>@.*) ;; + *) + AC_CHECK_FUNC(poll, [AC_DEFINE(USE_POLL, 1, [poll() function is available])], ) + ;; + esac + unset _ac_xorg_macosx_version_min + ;; *) AC_CHECK_FUNC(poll, [AC_DEFINE(USE_POLL, 1, [poll() function is available])], ) ;; @@ -146,6 +178,7 @@ XCB_EXTENSION(XFixes, "yes") XCB_EXTENSION(XFree86-DRI, "yes") XCB_EXTENSION(Xinerama, "yes") XCB_EXTENSION(XInput, "no") +XCB_EXTENSION(XKB, "no") XCB_EXTENSION(Xprint, "yes") XCB_EXTENSION(SELinux, "no") XCB_EXTENSION(XTest, "yes") @@ -189,6 +222,7 @@ xcb-xf86dri.pc xcb-xfixes.pc xcb-xinerama.pc xcb-xinput.pc +xcb-xkb.pc xcb-xprint.pc xcb-xselinux.pc xcb-xtest.pc @@ -232,6 +266,7 @@ echo " Xfixes..............: ${BUILD_XFIXES}" echo " Xfree86-dri.........: ${BUILD_XFREE86_DRI}" echo " xinerama............: ${BUILD_XINERAMA}" echo " xinput..............: ${BUILD_XINPUT}" +echo " xkb.................: ${BUILD_XKB}" echo " xprint..............: ${BUILD_XPRINT}" echo " xtest...............: ${BUILD_XTEST}" echo " xv..................: ${BUILD_XV}" diff --git a/doc/Makefile.am b/doc/Makefile.am index 1925519..dbe2e0b 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -2,7 +2,9 @@ EXTRA_DIST = \ tutorial/index.html \ tutorial/xcb.css \ -xcb.doxygen.in +xcb.doxygen.in \ +xkb_internals \ +xkb_issues docdirs = $(srcdir)/tutorial diff --git a/doc/tutorial/index.html b/doc/tutorial/index.html old mode 100755 new mode 100644 index aa3ae17..adec0ac --- a/doc/tutorial/index.html +++ b/doc/tutorial/index.html @@ -1453,7 +1453,7 @@ typedef enum {

If the window has already been created, we can use the - xcb_configure_window() function to set + xcb_change_window_attributes() function to set the events that the window will receive. The subsection Configuring a window shows its prototype. As an example, here is a piece of code that @@ -1466,7 +1466,7 @@ const static uint32_t values[] = { XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTT /* The connection c and the window win are supposed to be defined */ -xcb_configure_window (c, win, XCB_CW_EVENT_MASK, values); +xcb_change_window_attributes (c, win, XCB_CW_EVENT_MASK, values);

diff --git a/doc/xkb_internals b/doc/xkb_internals new file mode 100644 index 0000000..93c6d01 --- /dev/null +++ b/doc/xkb_internals @@ -0,0 +1,49 @@ + +XKB introduces several uncommon data structures: + - switch allows conditional inclusion of fields + - several complex objects intermix variable and fixed size fields + - lists with a variable number of variable size objects + +To handle these objects, a number of new functions is generated: + - _serialize() turns a structured object into a byte stream, + (re)ordering or including fields according to the protocol + - _unserialize() rewrites data from a buffer into a structured object + - _unpack() expands a buffer representing a switch object into + a special structured type, all flags needed to resolve the switch + expression have to given as parameters + - _sizeof() calculates the size of a serialized object, often by calling + _unserialize()/_unpack() internally + +The new structured data type for switch is special as it contains fixed +and variable size fields. Variable size fields can be accessed via pointers. + +If switch appears in a request, an additional set of request helpers is +generated with the suffix _aux or _aux_(un)checked. While the 'common' +request functions require that switch has been serialized before, the _aux +variants take the structured data type. They are especially designed to +replace certain functions in xcb-util/aux. + +Accessors for switch members need two parameters, where the first is usually +a pointer to the respective request or reply structure, while the second +is a pointer to the unpacked switch data structure. + +Functions from the serialize family that take a double pointer can allocate +memory on their own, which is useful if the size of a buffer has to be +calculated depending on the data within. These functions call malloc() when +the double pointer is given as the address of a pointer that has been +initialized to 0. It is the responsibility of the user to free any allocated +memory. + +Intermixed variable and fixed size fields are an important special case in XKB. +The current implementation resolves the issue by reordering the fields before +sending them on the wire as well as before returning a reply. That means that +these objects look like 'common' XCB data types and they can be accessed as such +(i.e. fixed size fields directly via the structured type and variable size fields +via accessors/iterators). + +In case a list with variable size elements needs to be accessed, it is necessary +to use iterators. The iterator functions take care of determining the actual +object size for each element automatically. + +A small and preliminary set of auxiliary functions is available in xkb_util.c +in the check_xkb module. diff --git a/doc/xkb_issues b/doc/xkb_issues new file mode 100644 index 0000000..80efcc1 --- /dev/null +++ b/doc/xkb_issues @@ -0,0 +1,38 @@ + +There are a number of problematic special cases in XKB. The issues +mentioned here are at most partly resolved. + +1. The are several XxxDoodad structures defined in xkb.xml. They are used + in a few lists, but in a rather special way: + The struct "CommonDoodad" is supposed to be a rather generic data type, + combining the most basic Doodad fields that are common in all these structures. + All Doodads are encapsulated in a union type simply called "Doodad". + Now this union is used in subsequent list definitions, aiming at a kind of + 'polymorphism': From inspection of the protocol and Xlib, the Doodads are to + be discriminated based on their type field. + However the special meaning of the type field is not encoded in the protocol. + Furthermore the TextDoodad and the LogoDoodad are variable size types due to + some fields of type CountedString16, thereby turning the union into a + possibly variable size type as well. + However, for lists with variable size elements, special sizeof functions are + required. These cannot be autogenerated as it cannot be referred which + Doodad type to use for the union. + Therefore, the Doodad type structures are unsupported at the moment. + +2. There are still some bugs in xkb.xml: Either certain fields are missing + that are required by the protocol, or Xlib simply has another understanding + of the protocol. + +3. The interface for accessors should be reviewed. + +4. Currently some bitcases carry 'name' attributes. These could be avoided if + the data within would consist of a singe struct field only. + +5. switch could get a 'fixed_size' attribute, so when rewriting valueparam to switch, + an uint32_t * pointer could be used instead of void *. + +6. The automatic inclusion of padding requires some complicated coding in the + generator. This is errorprone and could be avoided if all padding is explicitly + given in the protocol definition. For variable size fields that require padding, + the pad tag could get a 'fieldref' attribute. That way padding could be handled + a lot easier in the autogenerator. \ No newline at end of file diff --git a/packaging/libxcb.spec b/packaging/libxcb.spec index b7eb978..f76dafd 100644 --- a/packaging/libxcb.spec +++ b/packaging/libxcb.spec @@ -1,82 +1,86 @@ -Name: libxcb -Summary: A C binding to the X11 protocol -Version: 1.7 -Release: 0 -Group: System/Libraries -License: MIT -URL: http://xcb.freedesktop.org/ -Source0: http://xcb.freedesktop.org/dist/%{name}-%{version}.tar.gz -Requires(post): /sbin/ldconfig -Requires(postun): /sbin/ldconfig -BuildRequires: pkgconfig(xorg-macros) -BuildRequires: pkgconfig(pthread-stubs) -BuildRequires: pkgconfig(xcb-proto) -BuildRequires: pkgconfig(xau) +Name: libxcb +Summary: A C binding to the X11 protocol +Version: 1.8.1 +Release: 1 +Group: System/Libraries +License: MIT +URL: http://xcb.freedesktop.org/ +Source0: http://xcb.freedesktop.org/dist/%{name}-%{version}.tar.gz + +BuildRequires: xorg-x11-xutils-dev +BuildRequires: libpthread-stubs +BuildRequires: libXau-devel BuildRequires: pkgconfig(xproto) +BuildRequires: xcb-proto >= 1.6 BuildRequires: libxslt BuildRequires: python BuildRequires: python-xml +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig %description Description: %{summary} - +The X protocol C-language Binding (XCB) is a replacement for Xlib featuring a +small footprint, latency hiding, direct access to the protocol, improved +threading support, and extensibility. %package devel -Summary: Development files for %{name} -Group: Development/Libraries -Requires: %{name} = %{version}-%{release} -Requires: libpthread-stubs -BuildRequires: pkgconfig(xau) +Summary: Development files for %{name} +Group: Development/Libraries -%description devel -Description: %{summary} +BuildRequires: libXau-devel -%package doc -Summary: Documentation for %{name} -Group: Documentation -Requires: %{name} = %{version}-%{release} +Requires: %{name} = %{version}-%{release} +Requires: libpthread-stubs -%description doc -Description: %{summary} +%description devel +The %{name}-devel package contains libraries and header files for +developing applications that use %{name}. +%package doc +Summary: Documentation for %{name} +Group: Documentation +BuildArch: noarch + +%description doc +The %{name}-doc package contains documentation for the %{name} library. %prep %setup -q - %build +./autogen.sh %reconfigure \ --disable-build-docs \ - CFLAGS="-D_F_ENABLE_XI2_SENDEVENT_" \ - LDFLAGS="-Wl,--hash-style=both -Wl,--as-needed" - + LDFLAGS="${LDFLAGS} -Wl,--hash-style=both -Wl,--as-needed" +# Call make instruction with smp support make %{?jobs:-j%jobs} %install rm -rf %{buildroot} -%make_install - - - - -%post -p /sbin/ldconfig - -%postun -p /sbin/ldconfig - +%make_install +rm -f $RPM_BUILD_ROOT/%{_libdir}/libxcb-xprint.so* +rm -f $RPM_BUILD_ROOT/%{_includedir}/xcb/xprint.h +find $RPM_BUILD_ROOT -name '*.la' -delete +%remove_docs +%clean +rm -rf $RPM_BUILD_ROOT +%post -p /sbin/ldconfig +%postun -p /sbin/ldconfig %files %defattr(-,root,root,-) -%{_libdir}/*.so.* - +%{_libdir}/libxcb.so.1* +%{_libdir}/libxcb-* %files devel %defattr(-,root,root,-) @@ -84,6 +88,6 @@ rm -rf %{buildroot} %{_libdir}/*.so %{_libdir}/pkgconfig/*.pc -%files doc -%defattr(-,root,root,-) -%{_datadir}/doc/%{name} +#%files doc +#%defattr(-,root,root,-) +#%{_datadir}/doc/%{name} diff --git a/src/Makefile.am b/src/Makefile.am index f0bdeaa..950de5c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,7 @@ nodist_libxcb_la_SOURCES = xproto.c bigreq.c xc_misc.c # * If you add an interface, increment current and age and set revision to 0. # * If you change or remove an interface, increment current and set revision # and age to 0. -libxcb_la_LDFLAGS = -version-info 2:0:1 -no-undefined +libxcb_la_LDFLAGS = -version-info 2:0:1 -no-undefined @lt_enable_auto_import@ XCB_LIBS = libxcb.la @@ -27,7 +27,7 @@ XCB_LIBS = libxcb.la EXTSOURCES += composite.c if BUILD_COMPOSITE lib_LTLIBRARIES += libxcb-composite.la -libxcb_composite_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_composite_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_composite_la_LIBADD = $(XCB_LIBS) nodist_libxcb_composite_la_SOURCES = composite.c composite.h endif @@ -35,7 +35,7 @@ endif EXTSOURCES += damage.c if BUILD_DAMAGE lib_LTLIBRARIES += libxcb-damage.la -libxcb_damage_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_damage_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_damage_la_LIBADD = $(XCB_LIBS) nodist_libxcb_damage_la_SOURCES = damage.c damage.h endif @@ -43,7 +43,7 @@ endif EXTSOURCES += dpms.c if BUILD_DPMS lib_LTLIBRARIES += libxcb-dpms.la -libxcb_dpms_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_dpms_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_dpms_la_LIBADD = $(XCB_LIBS) nodist_libxcb_dpms_la_SOURCES = dpms.c dpms.h endif @@ -51,7 +51,7 @@ endif EXTSOURCES += dri2.c if BUILD_DRI2 lib_LTLIBRARIES += libxcb-dri2.la -libxcb_dri2_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_dri2_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_dri2_la_LIBADD = $(XCB_LIBS) nodist_libxcb_dri2_la_SOURCES = dri2.c dri2.h endif @@ -59,7 +59,7 @@ endif EXTSOURCES += glx.c if BUILD_GLX lib_LTLIBRARIES += libxcb-glx.la -libxcb_glx_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_glx_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_glx_la_LIBADD = $(XCB_LIBS) nodist_libxcb_glx_la_SOURCES = glx.c glx.h endif @@ -67,7 +67,7 @@ endif EXTSOURCES += randr.c if BUILD_RANDR lib_LTLIBRARIES += libxcb-randr.la -libxcb_randr_la_LDFLAGS = -version-info 1:0:1 -no-undefined +libxcb_randr_la_LDFLAGS = -version-info 1:0:1 -no-undefined @lt_enable_auto_import@ libxcb_randr_la_LIBADD = $(XCB_LIBS) nodist_libxcb_randr_la_SOURCES = randr.c randr.h endif @@ -75,7 +75,7 @@ endif EXTSOURCES += record.c if BUILD_RECORD lib_LTLIBRARIES += libxcb-record.la -libxcb_record_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_record_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_record_la_LIBADD = $(XCB_LIBS) nodist_libxcb_record_la_SOURCES = record.c record.h endif @@ -83,7 +83,7 @@ endif EXTSOURCES += render.c if BUILD_RENDER lib_LTLIBRARIES += libxcb-render.la -libxcb_render_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_render_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_render_la_LIBADD = $(XCB_LIBS) nodist_libxcb_render_la_SOURCES = render.c render.h endif @@ -91,7 +91,7 @@ endif EXTSOURCES += res.c if BUILD_RESOURCE lib_LTLIBRARIES += libxcb-res.la -libxcb_res_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_res_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_res_la_LIBADD = $(XCB_LIBS) nodist_libxcb_res_la_SOURCES = res.c res.h endif @@ -99,7 +99,7 @@ endif EXTSOURCES += screensaver.c if BUILD_SCREENSAVER lib_LTLIBRARIES += libxcb-screensaver.la -libxcb_screensaver_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_screensaver_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_screensaver_la_LIBADD = $(XCB_LIBS) nodist_libxcb_screensaver_la_SOURCES = screensaver.c screensaver.h endif @@ -107,7 +107,7 @@ endif EXTSOURCES += shape.c if BUILD_SHAPE lib_LTLIBRARIES += libxcb-shape.la -libxcb_shape_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_shape_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_shape_la_LIBADD = $(XCB_LIBS) nodist_libxcb_shape_la_SOURCES = shape.c shape.h endif @@ -115,7 +115,7 @@ endif EXTSOURCES += shm.c if BUILD_SHM lib_LTLIBRARIES += libxcb-shm.la -libxcb_shm_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_shm_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_shm_la_LIBADD = $(XCB_LIBS) nodist_libxcb_shm_la_SOURCES = shm.c shm.h endif @@ -123,7 +123,7 @@ endif EXTSOURCES += sync.c if BUILD_SYNC lib_LTLIBRARIES += libxcb-sync.la -libxcb_sync_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_sync_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_sync_la_LIBADD = $(XCB_LIBS) nodist_libxcb_sync_la_SOURCES = sync.c sync.h endif @@ -131,7 +131,7 @@ endif EXTSOURCES += xevie.c if BUILD_XEVIE lib_LTLIBRARIES += libxcb-xevie.la -libxcb_xevie_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_xevie_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_xevie_la_LIBADD = $(XCB_LIBS) nodist_libxcb_xevie_la_SOURCES = xevie.c xevie.h endif @@ -139,7 +139,7 @@ endif EXTSOURCES += xf86dri.c if BUILD_XFREE86_DRI lib_LTLIBRARIES += libxcb-xf86dri.la -libxcb_xf86dri_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_xf86dri_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_xf86dri_la_LIBADD = $(XCB_LIBS) nodist_libxcb_xf86dri_la_SOURCES = xf86dri.c xf86dri.h endif @@ -147,7 +147,7 @@ endif EXTSOURCES += xfixes.c if BUILD_XFIXES lib_LTLIBRARIES += libxcb-xfixes.la -libxcb_xfixes_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_xfixes_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_xfixes_la_LIBADD = $(XCB_LIBS) nodist_libxcb_xfixes_la_SOURCES = xfixes.c xfixes.h endif @@ -155,7 +155,7 @@ endif EXTSOURCES += xinerama.c if BUILD_XINERAMA lib_LTLIBRARIES += libxcb-xinerama.la -libxcb_xinerama_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_xinerama_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_xinerama_la_LIBADD = $(XCB_LIBS) nodist_libxcb_xinerama_la_SOURCES = xinerama.c xinerama.h endif @@ -163,15 +163,23 @@ endif EXTSOURCES += xinput.c if BUILD_XINPUT lib_LTLIBRARIES += libxcb-xinput.la -libxcb_xinput_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_xinput_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_xinput_la_LIBADD = $(XCB_LIBS) nodist_libxcb_xinput_la_SOURCES = xinput.c xinput.h endif +EXTSOURCES += xkb.c +if BUILD_XKB +lib_LTLIBRARIES += libxcb-xkb.la +libxcb_xkb_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_xkb_la_LIBADD = $(XCB_LIBS) +nodist_libxcb_xkb_la_SOURCES = xkb.c xkb.h +endif + EXTSOURCES += xprint.c if BUILD_XPRINT lib_LTLIBRARIES += libxcb-xprint.la -libxcb_xprint_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_xprint_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_xprint_la_LIBADD = $(XCB_LIBS) nodist_libxcb_xprint_la_SOURCES = xprint.c xprint.h endif @@ -179,7 +187,7 @@ endif EXTSOURCES += xselinux.c if BUILD_SELINUX lib_LTLIBRARIES += libxcb-xselinux.la -libxcb_xselinux_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_xselinux_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_xselinux_la_LIBADD = $(XCB_LIBS) nodist_libxcb_xselinux_la_SOURCES = xselinux.c xselinux.h endif @@ -187,7 +195,7 @@ endif EXTSOURCES += xtest.c if BUILD_XTEST lib_LTLIBRARIES += libxcb-xtest.la -libxcb_xtest_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_xtest_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_xtest_la_LIBADD = $(XCB_LIBS) nodist_libxcb_xtest_la_SOURCES = xtest.c xtest.h endif @@ -195,7 +203,7 @@ endif EXTSOURCES += xv.c if BUILD_XV lib_LTLIBRARIES += libxcb-xv.la -libxcb_xv_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_xv_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_xv_la_LIBADD = $(XCB_LIBS) nodist_libxcb_xv_la_SOURCES = xv.c xv.h endif @@ -203,7 +211,7 @@ endif EXTSOURCES += xvmc.c if BUILD_XVMC lib_LTLIBRARIES += libxcb-xvmc.la -libxcb_xvmc_la_LDFLAGS = -version-info 0:0:0 -no-undefined +libxcb_xvmc_la_LDFLAGS = -version-info 0:0:0 -no-undefined @lt_enable_auto_import@ libxcb_xvmc_la_LIBADD = $(XCB_LIBS) nodist_libxcb_xvmc_la_SOURCES = xvmc.c xvmc.h endif @@ -211,6 +219,9 @@ endif EXTHEADERS=$(EXTSOURCES:.c=.h) xcbinclude_HEADERS = xcb.h xcbext.h +if XCB_HAVE_WIN32 +xcbinclude_HEADERS += xcb_windefs.h +endif nodist_xcbinclude_HEADERS = $(EXTHEADERS) noinst_HEADERS = xcbint.h diff --git a/src/c-client.xsl b/src/c-client.xsl deleted file mode 100755 index a15d824..0000000 --- a/src/c-client.xsl +++ /dev/null @@ -1,1551 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { "" } - - - - - - - - - - xcb - - _ - - - - - - - - - - - - - _ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - char - - - - - - - - - - - - - - - - - - - - - - - - - - - - _ - - - - - - - - - - - _ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Multiple definitions of type " - - " found. - - - - - - - - - - - - No definitions of type " - - " found - - , and it is not a known core type - - . - - - - - - - - - - - - - - - - xcb_void - - - _cookie_t - - - - - - - - - _checked - _unchecked - - - - - - - /** - * Delivers a request to the X server - * @param c The connection - * @return A cookie - * - * Delivers a request to the X server. - * - - * This form can be used only if the request will not cause - * a reply to be generated. Any returned error will be - * saved for handling by xcb_request_check(). - - - * This form can be used only if the request will cause - * a reply to be generated. Any returned error will be - * placed in the event queue. - - */ - - - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** - * Return the reply - * @param c The connection - * @param cookie The cookie - * @param e The xcb_generic_error_t supplied - * - * Returns the reply of the request asked by - * - * The parameter @p e supplied to this function must be NULL if - * _unchecked(). is used. - * Otherwise, it stores the error if any. - * - * The returned value must be freed by the caller using free(). - */ - - - - - - - - return (_reply_t *) xcb_wait_for_reply(c, cookie.sequence, e); - - - - - - - - - - - - - - union - - - - - - - - - - - - _event_t - - - _error_t - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _t - - - - - - _t - - - - - - - - - - - - - - - - - - - - - - - - - - - _t - - - - - - - - - - - _t - - - - - - true - - - - - - - - - - - - - - - length - 2 - - - - - - - - - Encountered a list with no length expresssion outside a request or reply. - - - - - - - - - - - - - - _t - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _t - - - - - - - - - - - - - - - - - - const - - - _t - - * - - - - - - - - - - - - - - _t - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Unions must be fixed length. - - - - - - - - - - - - - - - - - - - - - - - static const xcb_protocol_request_t xcb_req = { - - /* count */ , - /* ext */ - - & - - _id - - 0 - , - /* opcode */ , - /* isvoid */ - - }; - - - struct iovec xcb_parts[]; - xcb_ret; - xcb_out; - - - - xcb_out.pad0 = 0; - - - - - xcb_parts[2].iov_base = (char *) &xcb_out; - xcb_parts[2].iov_len = sizeof(xcb_out); - xcb_parts[3].iov_base = 0; - xcb_parts[3].iov_len = -xcb_parts[2].iov_len & 3; - - - xcb_parts[].iov_base = (char *) ; - xcb_parts[].iov_len = - - * sizeof( - - - char - - - - - - ) - ; - xcb_parts[].iov_base = 0; - xcb_parts[].iov_len = -xcb_parts[].iov_len & 3; - - - xcb_ret.sequence = xcb_send_request(c, - XCB_REQUEST_CHECKED - 0 - , xcb_parts + 2, &xcb_req); - return xcb_ret; - - - - - xcb_out. - - = - - ; - - - - - - xcb_out. - - = - - ; - - - - - - - xcb_out.pad = 0; - memset(xcb_out.pad, 0, ); - - - - - - - - - - - - - - - - - R + 1 - - - - _end(__iterator(R)) - - - __end(R) - - - - - - - - - - - - - - - - - return ( *) (); - - - xcb_generic_iterator_t prev = ; - return ( *) ((char *) prev.data + XCB_TYPE_PAD(, prev.index)); - - - - - - - return - - ; - - - - - - _iterator_t i; - - - i.data = ( *) (); - - - xcb_generic_iterator_t prev = ; - i.data = ( *) ((char *) prev.data + XCB_TYPE_PAD(, prev.index)); - - - i.rem = - - ; - i.index = (char *) i.data - (char *) R; - return i; - - - - - - char - - - - - - xcb_generic_iterator_t i; - - - i.data = (( *) ()) + ( - - ); - - - xcb_generic_iterator_t child = ; - i.data = (( *) child.data) + ( - - ); - - - i.rem = 0; - i.index = (char *) i.data - (char *) R; - return i; - - - - - - - /** - * Get the next element of the iterator - * @param i Pointer to a _iterator_t - * - * Get the next element in the iterator. The member rem is - * decreased by one. The member data points to the next - * element. The member index is increased by sizeof(_t) - */ - - - - _t *R = i->data; - xcb_generic_iterator_t child = ; - --i->rem; - i->data = (_t *) child.data; - i->index = child.index; - - - --i->rem; - ++i->data; - i->index += sizeof(_t); - - - - - /** - * Return the iterator pointing to the last element - * @param i An _iterator_t - * @return The iterator pointing to the last element - * - * Set the current element in the iterator to the last element. - * The member rem is set to 0. The member data points to the - * last element. - */ - - xcb_generic_iterator_t ret; - - - while(i.rem > 0) - - _next(&i); - - ret.data = i.data; - ret.rem = i.rem; - ret.index = i.index; - - - ret.data = i.data + i.rem; - ret.index = i.index + ((char *) ret.data - (char *) i.data); - ret.rem = 0; - - - return ret; - - - - - - - - Error: This stylesheet requires the EXSL node-set extension. - - - - Error: Parameter "mode" must be "header" or "source". - - - - - - - ___H - -/* - * This file generated automatically from -.xml by c-client.xsl using XSLT. - * Edit at your peril. - */ - - -/** - * @defgroup XCB__API XCB API - * @brief XCB Protocol Implementation. - - * @{ - **/ - - - -#ifndef -#define - -#include "xcb.h" - -#include ".h" - - - - - - - - - #define XCB__MAJOR_VERSION - - #define XCB__MINOR_VERSION - - - - - - - - - -#include <string.h> - -#include <assert.h> -#include "xcbext.h" -#include ".h" - - - - - - - -#endif - -/** - * @} - */ - - - - - - - - - /** Opcode for . */ - - #define - - - - - - - - - - - extern - - const char - - [] - - = " - - " - - ; - - - - - - extern - - - - = - - - ; - - - - - - - - - typedef - - - - ; - - - - - - - - - - - - - /** - * @brief - **/ - - typedef - struct - - - { - - - - - - - ; /**< */ - - - } - - ; - - - - - - - - typedef enum - - { - - - , - - - - - - - = - - - - - - - -} ; - - - - - - - - - - - - - - - - - -/***************************************************************************** - ** - ** - - - ** - - - - - ** @param - - - - - - - - ** @returns - ** - *****************************************************************************/ - - - - - - - - , - - - - - - - - - - - - /**< */ - - - - - ) - - ; - - - - - -{ - - - - - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ( - - - - - - - - - - ) - - - - (1 << - - ) - - - - - - ( - - - - - - - - - - - ) - - - - - - Invalid element in expression: - - - - - - - - - - - - - - - - - [ - - ] - - - - - - - - - - - pad - - - - - - [ - - ] - - - - - - - - - 0 - 0 - - - - - - - - - - - - - - - - - - - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - - - - - - - - - - - - - - - - - - - - - - - 0 - - - - - - - - - - - - - - - - - - - - diff --git a/src/c_client.py b/src/c_client.py old mode 100755 new mode 100644 index 4f3b089..ad3ea22 --- a/src/c_client.py +++ b/src/c_client.py @@ -1,6 +1,7 @@ #!/usr/bin/env python from xml.etree.cElementTree import * from os.path import basename +from functools import reduce import getopt import sys import re @@ -16,6 +17,7 @@ _extension_special_cases = ['XPrint', 'XCMisc', 'BigRequests'] _cplusplus_annoyances = {'class' : '_class', 'new' : '_new', 'delete': '_delete'} +_c_keywords = {'default' : '_default'} _hlines = [] _hlevel = 0 @@ -23,6 +25,12 @@ _clines = [] _clevel = 0 _ns = None +# global variable to keep track of serializers and +# switch data types due to weird dependencies +finished_serializers = [] +finished_sizeof = [] +finished_switch = [] + def _h(fmt, *args): ''' Writes the given line to the header file. @@ -81,6 +89,8 @@ def _cpp(str): ''' if str in _cplusplus_annoyances: return _cplusplus_annoyances[str] + elif str in _c_keywords: + return _c_keywords[str] else: return str @@ -158,11 +168,16 @@ def c_open(self): _h('') _h('#include "xcb.h"') + _c('#include ') _c('#include ') _c('#include ') + _c('#include /* for offsetof() */') _c('#include "xcbext.h"') _c('#include "%s.h"', _ns.header) + _c('') + _c('#define ALIGNOF(type) offsetof(struct { char dummy; type member; }, member)') + if _ns.is_ext: for (n, h) in self.imports: _hc('#include "%s.h"', h) @@ -273,7 +288,30 @@ def _c_type_setup(self, name, postfix): self.c_reply_type = _t(name + ('reply',)) self.c_cookie_type = _t(name + ('cookie',)) - if self.is_container: + self.need_aux = False + self.need_serialize = False + self.need_sizeof = False + + self.c_aux_name = _n(name + ('aux',)) + self.c_aux_checked_name = _n(name + ('aux', 'checked')) + self.c_aux_unchecked_name = _n(name + ('aux', 'unchecked')) + self.c_serialize_name = _n(name + ('serialize',)) + self.c_unserialize_name = _n(name + ('unserialize',)) + self.c_unpack_name = _n(name + ('unpack',)) + self.c_sizeof_name = _n(name + ('sizeof',)) + + # special case: structs where variable size fields are followed by fixed size fields + self.var_followed_by_fixed_fields = False + + if self.is_switch: + self.need_serialize = True + self.c_container = 'struct' + for bitcase in self.bitcases: + bitcase.c_field_name = _cpp(bitcase.field_name) + bitcase_name = bitcase.field_type if bitcase.type.has_name else name + _c_type_setup(bitcase.type, bitcase_name, ()) + + elif self.is_container: self.c_container = 'union' if self.is_union else 'struct' prev_varsized_field = None @@ -284,13 +322,28 @@ def _c_type_setup(self, name, postfix): _c_type_setup(field.type, field.field_type, ()) if field.type.is_list: _c_type_setup(field.type.member, field.field_type, ()) + if (field.type.nmemb is None): + self.need_sizeof = True field.c_field_type = _t(field.field_type) field.c_field_const_type = ('' if field.type.nmemb == 1 else 'const ') + field.c_field_type field.c_field_name = _cpp(field.field_name) - field.c_subscript = '[%d]' % field.type.nmemb if (field.type.nmemb > 1) else '' + field.c_subscript = '[%d]' % field.type.nmemb if (field.type.nmemb and field.type.nmemb > 1) else '' field.c_pointer = ' ' if field.type.nmemb == 1 else '*' + # correct the c_pointer field for variable size non-list types + if not field.type.fixed_size() and field.c_pointer == ' ': + field.c_pointer = '*' + if field.type.is_list and not field.type.member.fixed_size(): + field.c_pointer = '*' + + if field.type.is_switch: + field.c_pointer = '*' + field.c_field_const_type = 'const ' + field.c_field_type + self.need_aux = True + elif not field.type.fixed_size() and not field.type.is_bitcase: + self.need_sizeof = True + field.c_iterator_type = _t(field.field_type + ('iterator',)) # xcb_fieldtype_iterator_t field.c_iterator_name = _n(name + (field.field_name, 'iterator')) # xcb_container_field_iterator field.c_accessor_name = _n(name + (field.field_name,)) # xcb_container_field @@ -306,10 +359,889 @@ def _c_type_setup(self, name, postfix): if field.type.fixed_size(): prev_varsized_offset += field.type.size + # special case: intermixed fixed and variable size fields + if prev_varsized_field is not None and not field.type.is_pad and field.wire: + if not self.is_union: + self.need_serialize = True + self.var_followed_by_fixed_fields = True else: self.last_varsized_field = field prev_varsized_field = field - prev_varsized_offset = 0 + prev_varsized_offset = 0 + + if self.var_followed_by_fixed_fields: + if field.type.fixed_size(): + field.prev_varsized_field = None + + if self.need_serialize: + # when _unserialize() is wanted, create _sizeof() as well for consistency reasons + self.need_sizeof = True + + # as switch does never appear at toplevel, + # continue here with type construction + if self.is_switch: + if self.c_type not in finished_switch: + finished_switch.append(self.c_type) + # special: switch C structs get pointer fields for variable-sized members + _c_complex(self) + for bitcase in self.bitcases: + bitcase_name = bitcase.type.name if bitcase.type.has_name else name + _c_accessors(bitcase.type, bitcase_name, bitcase_name) + # no list with switch as element, so no call to + # _c_iterator(field.type, field_name) necessary + + if not self.is_bitcase: + if self.need_serialize: + if self.c_serialize_name not in finished_serializers: + finished_serializers.append(self.c_serialize_name) + _c_serialize('serialize', self) + + # _unpack() and _unserialize() are only needed for special cases: + # switch -> unpack + # special cases -> unserialize + if self.is_switch or self.var_followed_by_fixed_fields: + _c_serialize('unserialize', self) + + if self.need_sizeof: + if self.c_sizeof_name not in finished_sizeof: + if not module.namespace.is_ext or self.name[:2] == module.namespace.prefix: + finished_sizeof.append(self.c_sizeof_name) + _c_serialize('sizeof', self) +# _c_type_setup() + +def _c_helper_absolute_name(prefix, field=None): + """ + turn prefix, which is a list of tuples (name, separator, Type obj) into a string + representing a valid name in C (based on the context) + if field is not None, append the field name as well + """ + prefix_str = '' + for name, sep, obj in prefix: + prefix_str += name + if '' == sep: + sep = '->' + if ((obj.is_bitcase and obj.has_name) or # named bitcase + (obj.is_switch and len(obj.parents)>1)): + sep = '.' + prefix_str += sep + if field is not None: + prefix_str += _cpp(field.field_name) + return prefix_str +# _c_absolute_name + +def _c_helper_field_mapping(complex_type, prefix, flat=False): + """ + generate absolute names, based on prefix, for all fields starting from complex_type + if flat == True, nested complex types are not taken into account + """ + all_fields = {} + if complex_type.is_switch: + for b in complex_type.bitcases: + if b.type.has_name: + switch_name, switch_sep, switch_type = prefix[-1] + bitcase_prefix = prefix + [(b.type.name[-1], '.', b.type)] + else: + bitcase_prefix = prefix + + if (True==flat and not b.type.has_name) or False==flat: + all_fields.update(_c_helper_field_mapping(b.type, bitcase_prefix, flat)) + else: + for f in complex_type.fields: + fname = _c_helper_absolute_name(prefix, f) + if f.field_name in all_fields: + raise Exception("field name %s has been registered before" % f.field_name) + + all_fields[f.field_name] = (fname, f) + if f.type.is_container and flat==False: + if f.type.is_bitcase and not f.type.has_name: + new_prefix = prefix + elif f.type.is_switch and len(f.type.parents)>1: + # nested switch gets another separator + new_prefix = prefix+[(f.c_field_name, '.', f.type)] + else: + new_prefix = prefix+[(f.c_field_name, '->', f.type)] + all_fields.update(_c_helper_field_mapping(f.type, new_prefix, flat)) + + return all_fields +# _c_field_mapping() + +def _c_helper_resolve_field_names (prefix): + """ + get field names for all objects in the prefix array + """ + all_fields = {} + tmp_prefix = [] + # look for fields in the remaining containers + for idx, p in enumerate(prefix): + name, sep, obj = p + if ''==sep: + # sep can be preset in prefix, if not, make a sensible guess + sep = '.' if (obj.is_switch or obj.is_bitcase) else '->' + # exception: 'toplevel' object (switch as well!) always have sep '->' + sep = '->' if idx<1 else sep + if not obj.is_bitcase or (obj.is_bitcase and obj.has_name): + tmp_prefix.append((name, sep, obj)) + all_fields.update(_c_helper_field_mapping(obj, tmp_prefix, flat=True)) + + return all_fields +# _c_helper_resolve_field_names + +def get_expr_fields(self): + """ + get the Fields referenced by switch or list expression + """ + def get_expr_field_names(expr): + if expr.op is None: + if expr.lenfield_name is not None: + return [expr.lenfield_name] + else: + # constant value expr + return [] + else: + if expr.op == '~': + return get_expr_field_names(expr.rhs) + elif expr.op == 'popcount': + return get_expr_field_names(expr.rhs) + elif expr.op == 'sumof': + # sumof expr references another list, + # we need that list's length field here + field = None + for f in expr.lenfield_parent.fields: + if f.field_name == expr.lenfield_name: + field = f + break + if field is None: + raise Exception("list field '%s' referenced by sumof not found" % expr.lenfield_name) + # referenced list + its length field + return [expr.lenfield_name] + get_expr_field_names(field.type.expr) + elif expr.op == 'enumref': + return [] + else: + return get_expr_field_names(expr.lhs) + get_expr_field_names(expr.rhs) + # get_expr_field_names() + + # resolve the field names with the parent structure(s) + unresolved_fields_names = get_expr_field_names(self.expr) + + # construct prefix from self + prefix = [('', '', p) for p in self.parents] + if self.is_container: + prefix.append(('', '', self)) + + all_fields = _c_helper_resolve_field_names (prefix) + resolved_fields_names = list(filter(lambda x: x in all_fields.keys(), unresolved_fields_names)) + if len(unresolved_fields_names) != len(resolved_fields_names): + raise Exception("could not resolve all fields for %s" % self.name) + + resolved_fields = [all_fields[n][1] for n in resolved_fields_names] + return resolved_fields +# get_expr_fields() + +def resolve_expr_fields(complex_obj): + """ + find expr fields appearing in complex_obj and descendents that cannot be resolved within complex_obj + these are normally fields that need to be given as function parameters + """ + all_fields = [] + expr_fields = [] + unresolved = [] + + for field in complex_obj.fields: + all_fields.append(field) + if field.type.is_switch or field.type.is_list: + expr_fields += get_expr_fields(field.type) + if field.type.is_container: + expr_fields += resolve_expr_fields(field.type) + + # try to resolve expr fields + for e in expr_fields: + if e not in all_fields and e not in unresolved: + unresolved.append(e) + return unresolved +# resolve_expr_fields() + +def get_serialize_params(context, self, buffer_var='_buffer', aux_var='_aux'): + """ + functions like _serialize(), _unserialize(), and _unpack() sometimes need additional parameters: + E.g. in order to unpack switch, extra parameters might be needed to evaluate the switch + expression. This function tries to resolve all fields within a structure, and returns the + unresolved fields as the list of external parameters. + """ + def add_param(params, param): + if param not in params: + params.append(param) + + # collect all fields into param_fields + param_fields = [] + wire_fields = [] + + for field in self.fields: + if field.visible: + # the field should appear as a parameter in the function call + param_fields.append(field) + if field.wire and not field.auto: + if field.type.fixed_size() and not self.is_switch: + # field in the xcb_out structure + wire_fields.append(field) + # fields like 'pad0' are skipped! + + # in case of switch, parameters always contain any fields referenced in the switch expr + # we do not need any variable size fields here, as the switch data type contains both + # fixed and variable size fields + if self.is_switch: + param_fields = get_expr_fields(self) + + # _serialize()/_unserialize()/_unpack() function parameters + # note: don't use set() for params, it is unsorted + params = [] + + # 1. the parameter for the void * buffer + if 'serialize' == context: + params.append(('void', '**', buffer_var)) + elif context in ('unserialize', 'unpack', 'sizeof'): + params.append(('const void', '*', buffer_var)) + + # 2. any expr fields that cannot be resolved within self and descendants + unresolved_fields = resolve_expr_fields(self) + for f in unresolved_fields: + add_param(params, (f.c_field_type, '', f.c_field_name)) + + # 3. param_fields contain the fields necessary to evaluate the switch expr or any other fields + # that do not appear in the data type struct + for p in param_fields: + if self.is_switch: + typespec = p.c_field_const_type + pointerspec = p.c_pointer + add_param(params, (typespec, pointerspec, p.c_field_name)) + else: + if p.visible and not p.wire and not p.auto: + typespec = p.c_field_type + pointerspec = '' + add_param(params, (typespec, pointerspec, p.c_field_name)) + + # 4. aux argument + if 'serialize' == context: + add_param(params, ('const %s' % self.c_type, '*', aux_var)) + elif 'unserialize' == context: + add_param(params, ('%s' % self.c_type, '**', aux_var)) + elif 'unpack' == context: + add_param(params, ('%s' % self.c_type, '*', aux_var)) + + # 5. switch contains all variable size fields as struct members + # for other data types though, these have to be supplied separately + # this is important for the special case of intermixed fixed and + # variable size fields + if not self.is_switch and 'serialize' == context: + for p in param_fields: + if not p.type.fixed_size(): + add_param(params, (p.c_field_const_type, '*', p.c_field_name)) + + return (param_fields, wire_fields, params) +# get_serialize_params() + +def _c_serialize_helper_insert_padding(context, code_lines, space, postpone): + code_lines.append('%s /* insert padding */' % space) + code_lines.append('%s xcb_pad = -xcb_block_len & (xcb_align_to - 1);' % space) +# code_lines.append('%s printf("automatically inserting padding: %%%%d\\n", xcb_pad);' % space) + code_lines.append('%s xcb_buffer_len += xcb_block_len + xcb_pad;' % space) + + if not postpone: + code_lines.append('%s if (0 != xcb_pad) {' % space) + + if 'serialize' == context: + code_lines.append('%s xcb_parts[xcb_parts_idx].iov_base = xcb_pad0;' % space) + code_lines.append('%s xcb_parts[xcb_parts_idx].iov_len = xcb_pad;' % space) + code_lines.append('%s xcb_parts_idx++;' % space) + elif context in ('unserialize', 'unpack', 'sizeof'): + code_lines.append('%s xcb_tmp += xcb_pad;' % space) + + code_lines.append('%s xcb_pad = 0;' % space) + code_lines.append('%s }' % space) + + code_lines.append('%s xcb_block_len = 0;' % space) + + # keep tracking of xcb_parts entries for serialize + return 1 +# _c_serialize_helper_insert_padding() + +def _c_serialize_helper_switch(context, self, complex_name, + code_lines, temp_vars, + space, prefix): + count = 0 + switch_expr = _c_accessor_get_expr(self.expr, None) + + for b in self.bitcases: + bitcase_expr = _c_accessor_get_expr(b.type.expr, None) + code_lines.append(' if(%s & %s) {' % (switch_expr, bitcase_expr)) +# code_lines.append(' printf("switch %s: entering bitcase section %s (mask=%%%%d)...\\n", %s);' % +# (self.name[-1], b.type.name[-1], bitcase_expr)) + b_prefix = prefix + if b.type.has_name: + b_prefix = prefix + [(b.c_field_name, '.', b.type)] + + count += _c_serialize_helper_fields(context, b.type, + code_lines, temp_vars, + "%s " % space, + b_prefix, + is_bitcase = True) + code_lines.append(' }') + +# if 'serialize' == context: +# count += _c_serialize_helper_insert_padding(context, code_lines, space, False) +# elif context in ('unserialize', 'unpack', 'sizeof'): +# # padding +# code_lines.append('%s xcb_pad = -xcb_block_len & 3;' % space) +# code_lines.append('%s xcb_buffer_len += xcb_block_len + xcb_pad;' % space) + + return count +# _c_serialize_helper_switch + +def _c_serialize_helper_switch_field(context, self, field, c_switch_variable, prefix): + """ + handle switch by calling _serialize() or _unpack(), depending on context + """ + # switch is handled by this function as a special case + param_fields, wire_fields, params = get_serialize_params(context, self) + field_mapping = _c_helper_field_mapping(self, prefix) + prefix_str = _c_helper_absolute_name(prefix) + + # find the parameters that need to be passed to _serialize()/_unpack(): + # all switch expr fields must be given as parameters + args = get_expr_fields(field.type) + # length fields for variable size types in switch, normally only some of need + # need to be passed as parameters + switch_len_fields = resolve_expr_fields(field.type) + + # a switch field at this point _must_ be a bitcase field + # we require that bitcases are "self-contiguous" + bitcase_unresolved = resolve_expr_fields(self) + if len(bitcase_unresolved) != 0: + raise Exception('unresolved fields within bitcase is not supported at this point') + + # get the C names for the parameters + c_field_names = '' + for a in switch_len_fields: + c_field_names += "%s, " % field_mapping[a.c_field_name][0] + for a in args: + c_field_names += "%s, " % field_mapping[a.c_field_name][0] + + # call _serialize()/_unpack() to determine the actual size + if 'serialize' == context: + length = "%s(&%s, %s&%s%s)" % (field.type.c_serialize_name, c_switch_variable, + c_field_names, prefix_str, field.c_field_name) + elif context in ('unserialize', 'unpack'): + length = "%s(xcb_tmp, %s&%s%s)" % (field.type.c_unpack_name, + c_field_names, prefix_str, field.c_field_name) + + return length +# _c_serialize_helper_switch_field() + +def _c_serialize_helper_list_field(context, self, field, + code_lines, temp_vars, + space, prefix): + """ + helper function to cope with lists of variable length + """ + expr = field.type.expr + prefix_str = _c_helper_absolute_name(prefix) + param_fields, wire_fields, params = get_serialize_params('sizeof', self) + param_names = [p[2] for p in params] + + expr_fields_names = [f.field_name for f in get_expr_fields(field.type)] + resolved = list(filter(lambda x: x in param_names, expr_fields_names)) + unresolved = list(filter(lambda x: x not in param_names, expr_fields_names)) + + field_mapping = {} + for r in resolved: + field_mapping[r] = (r, None) + + if len(unresolved)>0: + tmp_prefix = prefix + if len(tmp_prefix)==0: + raise Exception("found an empty prefix while resolving expr field names for list %s", + field.c_field_name) + + field_mapping.update(_c_helper_resolve_field_names(prefix)) + resolved += list(filter(lambda x: x in field_mapping, unresolved)) + unresolved = list(filter(lambda x: x not in field_mapping, unresolved)) + if len(unresolved)>0: + raise Exception('could not resolve the length fields required for list %s' % field.c_field_name) + + list_length = _c_accessor_get_expr(expr, field_mapping) + + # default: list with fixed size elements + length = '%s * sizeof(%s)' % (list_length, field.type.member.c_wiretype) + + # list with variable-sized elements + if not field.type.member.fixed_size(): + length = '' + if context in ('unserialize', 'sizeof', 'unpack'): + int_i = ' unsigned int i;' + xcb_tmp_len = ' unsigned int xcb_tmp_len;' + if int_i not in temp_vars: + temp_vars.append(int_i) + if xcb_tmp_len not in temp_vars: + temp_vars.append(xcb_tmp_len) + # loop over all list elements and call sizeof repeatedly + # this should be a bit faster than using the iterators + code_lines.append("%s for(i=0; i<%s; i++) {" % (space, list_length)) + code_lines.append("%s xcb_tmp_len = %s(xcb_tmp);" % + (space, field.type.c_sizeof_name)) + code_lines.append("%s xcb_block_len += xcb_tmp_len;" % space) + code_lines.append("%s xcb_tmp += xcb_tmp_len;" % space) + code_lines.append("%s }" % space) + + elif 'serialize' == context: + code_lines.append('%s xcb_parts[xcb_parts_idx].iov_len = 0;' % space) + code_lines.append('%s xcb_tmp = (char *) %s%s;' % (space, prefix_str, field.c_field_name)) + code_lines.append('%s for(i=0; i<%s; i++) { ' % (space, list_length)) + code_lines.append('%s xcb_block_len = %s(xcb_tmp);' % (space, field.type.c_sizeof_name)) + code_lines.append('%s xcb_parts[xcb_parts_idx].iov_len += xcb_block_len;' % space) + code_lines.append('%s }' % space) + code_lines.append('%s xcb_block_len = xcb_parts[xcb_parts_idx].iov_len;' % space) + + return length +# _c_serialize_helper_list_field() + +def _c_serialize_helper_fields_fixed_size(context, self, field, + code_lines, temp_vars, + space, prefix): + # keep the C code a bit more readable by giving the field name + if not self.is_bitcase: + code_lines.append('%s /* %s.%s */' % (space, self.c_type, field.c_field_name)) + else: + scoped_name = [p[2].c_type if idx==0 else p[0] for idx, p in enumerate(prefix)] + typename = reduce(lambda x,y: "%s.%s" % (x, y), scoped_name) + code_lines.append('%s /* %s.%s */' % (space, typename, field.c_field_name)) + + abs_field_name = _c_helper_absolute_name(prefix, field) + # default for simple cases: call sizeof() + length = "sizeof(%s)" % field.c_field_type + + if context in ('unserialize', 'unpack', 'sizeof'): + # default: simple cast + value = ' %s = *(%s *)xcb_tmp;' % (abs_field_name, field.c_field_type) + + # padding - we could probably just ignore it + if field.type.is_pad and field.type.nmemb > 1: + value = '' + for i in range(field.type.nmemb): + code_lines.append('%s %s[%d] = *(%s *)xcb_tmp;' % + (space, abs_field_name, i, field.c_field_type)) + # total padding = sizeof(pad0) * nmemb + length += " * %d" % field.type.nmemb + + if field.type.is_list: + # no such case in the protocol, cannot be tested and therefore ignored for now + raise Exception('list with fixed number of elemens unhandled in _unserialize()') + + elif 'serialize' == context: + value = ' xcb_parts[xcb_parts_idx].iov_base = (char *) ' + + if field.type.is_expr: + # need to register a temporary variable for the expression in case we know its type + if field.type.c_type is None: + raise Exception("type for field '%s' (expression '%s') unkown" % + (field.field_name, _c_accessor_get_expr(field.type.expr))) + + temp_vars.append(' %s xcb_expr_%s = %s;' % (field.type.c_type, _cpp(field.field_name), + _c_accessor_get_expr(field.type.expr, prefix))) + value += "&xcb_expr_%s;" % _cpp(field.field_name) + + elif field.type.is_pad: + if field.type.nmemb == 1: + value += "&xcb_pad;" + else: + # we could also set it to 0, see definition of xcb_send_request() + value = ' xcb_parts[xcb_parts_idx].iov_base = xcb_pad0;' + length += "*%d" % field.type.nmemb + + else: + # non-list type with fixed size + if field.type.nmemb == 1: + value += "&%s;" % (abs_field_name) + + # list with nmemb (fixed size) elements + else: + value += '%s;' % (abs_field_name) + length = '%d' % field.type.nmemb + + return (value, length) +# _c_serialize_helper_fields_fixed_size() + +def _c_serialize_helper_fields_variable_size(context, self, field, + code_lines, temp_vars, + space, prefix): + prefix_str = _c_helper_absolute_name(prefix) + + if context in ('unserialize', 'unpack', 'sizeof'): + value = '' + var_field_name = 'xcb_tmp' + + # special case: intermixed fixed and variable size fields + if self.var_followed_by_fixed_fields and 'unserialize' == context: + value = ' %s = (%s *)xcb_tmp;' % (field.c_field_name, field.c_field_type) + temp_vars.append(' %s *%s;' % (field.type.c_type, field.c_field_name)) + # special case: switch + if 'unpack' == context: + value = ' %s%s = (%s *)xcb_tmp;' % (prefix_str, field.c_field_name, field.c_field_type) + + elif 'serialize' == context: + # variable size fields appear as parameters to _serialize() if the + # 'toplevel' container is not a switch + prefix_string = prefix_str if prefix[0][2].is_switch else '' + var_field_name = "%s%s" % (prefix_string, field.c_field_name) + value = ' xcb_parts[xcb_parts_idx].iov_base = (char *) %s;' % var_field_name + + length = '' + + code_lines.append('%s /* %s */' % (space, field.c_field_name)) + + if field.type.is_list: + if value != '': + # in any context, list is already a pointer, so the default assignment is ok + code_lines.append("%s%s" % (space, value)) + value = '' + length = _c_serialize_helper_list_field(context, self, field, + code_lines, temp_vars, + space, prefix) + + elif field.type.is_switch: + value = '' + if context == 'serialize': + # the _serialize() function allocates the correct amount memory if given a NULL pointer + value = ' xcb_parts[xcb_parts_idx].iov_base = (char *)0;' + length = _c_serialize_helper_switch_field(context, self, field, + 'xcb_parts[xcb_parts_idx].iov_base', + prefix) + + else: + # in all remaining special cases - call _sizeof() + length = "%s(%s)" % (field.type.c_sizeof_name, var_field_name) + + return (value, length) +# _c_serialize_helper_fields_variable_size + +def _c_serialize_helper_fields(context, self, + code_lines, temp_vars, + space, prefix, is_bitcase): + count = 0 + need_padding = False + prev_field_was_variable = False + + for field in self.fields: + if not field.visible: + if not ((field.wire and not field.auto) or 'unserialize' == context): + continue + + # switch/bitcase: fixed size fields must be considered explicitly + if field.type.fixed_size(): + if self.is_bitcase or self.var_followed_by_fixed_fields: + if prev_field_was_variable and need_padding: + # insert padding +# count += _c_serialize_helper_insert_padding(context, code_lines, space, +# self.var_followed_by_fixed_fields) + prev_field_was_variable = False + + # prefix for fixed size fields + fixed_prefix = prefix + + value, length = _c_serialize_helper_fields_fixed_size(context, self, field, + code_lines, temp_vars, + space, fixed_prefix) + else: + continue + + # fields with variable size + else: + # switch/bitcase: always calculate padding before and after variable sized fields + if need_padding or is_bitcase: + count += _c_serialize_helper_insert_padding(context, code_lines, space, + self.var_followed_by_fixed_fields) + + value, length = _c_serialize_helper_fields_variable_size(context, self, field, + code_lines, temp_vars, + space, prefix) + prev_field_was_variable = True + + # save (un)serialization C code + if '' != value: + code_lines.append('%s%s' % (space, value)) + + if field.type.fixed_size(): + if is_bitcase or self.var_followed_by_fixed_fields: + # keep track of (un)serialized object's size + code_lines.append('%s xcb_block_len += %s;' % (space, length)) + if context in ('unserialize', 'unpack', 'sizeof'): + code_lines.append('%s xcb_tmp += %s;' % (space, length)) + else: + # variable size objects or bitcase: + # value & length might have been inserted earlier for special cases + if '' != length: + # special case: intermixed fixed and variable size fields + if (not field.type.fixed_size() and + self.var_followed_by_fixed_fields and 'unserialize' == context): + temp_vars.append(' int %s_len;' % field.c_field_name) + code_lines.append('%s %s_len = %s;' % (space, field.c_field_name, length)) + code_lines.append('%s xcb_block_len += %s_len;' % (space, field.c_field_name)) + code_lines.append('%s xcb_tmp += %s_len;' % (space, field.c_field_name)) + else: + code_lines.append('%s xcb_block_len += %s;' % (space, length)) + # increase pointer into the byte stream accordingly + if context in ('unserialize', 'sizeof', 'unpack'): + code_lines.append('%s xcb_tmp += xcb_block_len;' % space) + + if 'serialize' == context: + if '' != length: + code_lines.append('%s xcb_parts[xcb_parts_idx].iov_len = %s;' % (space, length)) + code_lines.append('%s xcb_parts_idx++;' % space) + count += 1 + + code_lines.append('%s xcb_align_to = ALIGNOF(%s);' % (space, 'char' if field.c_field_type == 'void' else field.c_field_type)) + + need_padding = True + if self.var_followed_by_fixed_fields: + need_padding = False + + return count +# _c_serialize_helper_fields() + +def _c_serialize_helper(context, complex_type, + code_lines, temp_vars, + space='', prefix=[]): + # count tracks the number of fields to serialize + count = 0 + + if hasattr(complex_type, 'type'): + self = complex_type.type + complex_name = complex_type.name + else: + self = complex_type + if self.var_followed_by_fixed_fields and 'unserialize' == context: + complex_name = 'xcb_out' + else: + complex_name = '_aux' + + # special case: switch is serialized by evaluating each bitcase separately + if self.is_switch: + count += _c_serialize_helper_switch(context, self, complex_name, + code_lines, temp_vars, + space, prefix) + + # all other data types can be evaluated one field a time + else: + # unserialize & fixed size fields: simply cast the buffer to the respective xcb_out type + if context in ('unserialize', 'unpack', 'sizeof') and not self.var_followed_by_fixed_fields: + code_lines.append('%s xcb_block_len += sizeof(%s);' % (space, self.c_type)) + code_lines.append('%s xcb_tmp += xcb_block_len;' % space) + # probably not needed + #_c_serialize_helper_insert_padding(context, code_lines, space, False) + + count += _c_serialize_helper_fields(context, self, + code_lines, temp_vars, + space, prefix, False) + # "final padding" + count += _c_serialize_helper_insert_padding(context, code_lines, space, False) + + return count +# _c_serialize_helper() + +def _c_serialize(context, self): + """ + depending on the context variable, generate _serialize(), _unserialize(), _unpack(), or _sizeof() + for the ComplexType variable self + """ + _h_setlevel(1) + _c_setlevel(1) + + _hc('') + # _serialize() returns the buffer size + _hc('int') + + if self.is_switch and 'unserialize' == context: + context = 'unpack' + + cases = { 'serialize' : self.c_serialize_name, + 'unserialize' : self.c_unserialize_name, + 'unpack' : self.c_unpack_name, + 'sizeof' : self.c_sizeof_name } + func_name = cases[context] + + param_fields, wire_fields, params = get_serialize_params(context, self) + variable_size_fields = 0 + # maximum space required for type definition of function arguments + maxtypelen = 0 + + # determine N(variable_fields) + for field in param_fields: + # if self.is_switch, treat all fields as if they are variable sized + if not field.type.fixed_size() or self.is_switch: + variable_size_fields += 1 + # determine maxtypelen + for p in params: + maxtypelen = max(maxtypelen, len(p[0]) + len(p[1])) + + # write to .c/.h + indent = ' '*(len(func_name)+2) + param_str = [] + for p in params: + typespec, pointerspec, field_name = p + spacing = ' '*(maxtypelen-len(typespec)-len(pointerspec)) + param_str.append("%s%s%s %s%s /**< */" % (indent, typespec, spacing, pointerspec, field_name)) + # insert function name + param_str[0] = "%s (%s" % (func_name, param_str[0].strip()) + param_str = list(map(lambda x: "%s," % x, param_str)) + for s in param_str[:-1]: + _hc(s) + _h("%s);" % param_str[-1].rstrip(',')) + _c("%s)" % param_str[-1].rstrip(',')) + _c('{') + + code_lines = [] + temp_vars = [] + prefix = [] + + if 'serialize' == context: + if not self.is_switch and not self.var_followed_by_fixed_fields: + _c(' %s *xcb_out = *_buffer;', self.c_type) + _c(' unsigned int xcb_out_pad = -sizeof(%s) & 3;', self.c_type) + _c(' unsigned int xcb_buffer_len = sizeof(%s) + xcb_out_pad;', self.c_type) + _c(' unsigned int xcb_align_to;') + else: + _c(' char *xcb_out = *_buffer;') + _c(' unsigned int xcb_buffer_len = 0;') + _c(' unsigned int xcb_align_to;') + prefix = [('_aux', '->', self)] + aux_ptr = 'xcb_out' + + elif context in ('unserialize', 'unpack'): + _c(' char *xcb_tmp = (char *)_buffer;') + if not self.is_switch: + if not self.var_followed_by_fixed_fields: + _c(' const %s *_aux = (%s *)_buffer;', self.c_type, self.c_type) + prefix = [('_aux', '->', self)] + else: + _c(' %s xcb_out;', self.c_type) + prefix = [('xcb_out', '.', self)] + else: + aux_var = '_aux' # default for unpack: single pointer + # note: unserialize not generated for switch + if 'unserialize' == context: + aux_var = '(*_aux)' # unserialize: double pointer (!) + prefix = [(aux_var, '->', self)] + aux_ptr = '*_aux' + _c(' unsigned int xcb_buffer_len = 0;') + _c(' unsigned int xcb_block_len = 0;') + _c(' unsigned int xcb_pad = 0;') + _c(' unsigned int xcb_align_to;') + + elif 'sizeof' == context: + param_names = [p[2] for p in params] + if self.is_switch: + # switch: call _unpack() + _c(' %s _aux;', self.c_type) + _c(' return %s(%s, &_aux);', self.c_unpack_name, reduce(lambda x,y: "%s, %s" % (x, y), param_names)) + _c('}') + return + elif self.var_followed_by_fixed_fields: + # special case: call _unserialize() + _c(' return %s(%s, NULL);', self.c_unserialize_name, reduce(lambda x,y: "%s, %s" % (x, y), param_names)) + _c('}') + return + else: + _c(' char *xcb_tmp = (char *)_buffer;') + prefix = [('_aux', '->', self)] + + count = _c_serialize_helper(context, self, code_lines, temp_vars, prefix=prefix) + # update variable size fields (only important for context=='serialize' + variable_size_fields = count + if 'serialize' == context: + temp_vars.append(' unsigned int xcb_pad = 0;') + temp_vars.append(' char xcb_pad0[3] = {0, 0, 0};') + temp_vars.append(' struct iovec xcb_parts[%d];' % count) + temp_vars.append(' unsigned int xcb_parts_idx = 0;') + temp_vars.append(' unsigned int xcb_block_len = 0;') + temp_vars.append(' unsigned int i;') + temp_vars.append(' char *xcb_tmp;') + elif 'sizeof' == context: + # neither switch nor intermixed fixed and variable size fields: + # evaluate parameters directly + if not (self.is_switch or self.var_followed_by_fixed_fields): + + # look if we have to declare an '_aux' variable at all + if len(list(filter(lambda x: x.find('_aux')!=-1, code_lines)))>0: + if not self.var_followed_by_fixed_fields: + _c(' const %s *_aux = (%s *)_buffer;', self.c_type, self.c_type) + else: + _c(' %s *_aux = malloc(sizeof(%s));', self.c_type, self.c_type) + + _c(' unsigned int xcb_buffer_len = 0;') + _c(' unsigned int xcb_block_len = 0;') + _c(' unsigned int xcb_pad = 0;') + _c(' unsigned int xcb_align_to;') + + _c('') + for t in temp_vars: + _c(t) + _c('') + for l in code_lines: + _c(l) + + # variable sized fields have been collected, now + # allocate memory and copy everything into a continuous memory area + # note: this is not necessary in case of unpack + if context in ('serialize', 'unserialize'): + # unserialize: check for sizeof-only invocation + if 'unserialize' == context: + _c('') + _c(' if (NULL == _aux)') + _c(' return xcb_buffer_len;') + + _c('') + _c(' if (NULL == %s) {', aux_ptr) + _c(' /* allocate memory */') + _c(' %s = malloc(xcb_buffer_len);', aux_ptr) + if 'serialize' == context: + _c(' *_buffer = xcb_out;') + _c(' }') + _c('') + + # serialize: handle variable size fields in a loop + if 'serialize' == context: + if not self.is_switch and not self.var_followed_by_fixed_fields: + if len(wire_fields)>0: + _c(' *xcb_out = *_aux;') + # copy variable size fields into the buffer + if variable_size_fields > 0: + # xcb_out padding + if not self.is_switch and not self.var_followed_by_fixed_fields: + _c(' xcb_tmp = (char*)++xcb_out;') + _c(' xcb_tmp += xcb_out_pad;') + else: + _c(' xcb_tmp = xcb_out;') + + # variable sized fields + _c(' for(i=0; idata;', self.c_type) - _c(' xcb_generic_iterator_t child = %s;', _c_iterator_get_end(self.last_varsized_field, 'R')) - _c(' --i->rem;') - _c(' i->data = (%s *) child.data;', self.c_type) - _c(' i->index = child.index;') + + if self.is_union: + # FIXME - how to determine the size of a variable size union?? + _c(' /* FIXME - determine the size of the union %s */', self.c_type) + else: + if self.need_sizeof: + _c(' xcb_generic_iterator_t child;') + _c(' child.data = (%s *)(((char *)R) + %s(R));', + self.c_type, self.c_sizeof_name) + _c(' i->index = (char *) child.data - (char *) i->data;') + else: + _c(' xcb_generic_iterator_t child = %s;', _c_iterator_get_end(self.last_varsized_field, 'R')) + _c(' i->index = child.index;') + _c(' --i->rem;') + _c(' i->data = (%s *) child.data;', self.c_type) + else: _c(' --i->rem;') _c(' ++i->data;') @@ -424,61 +1367,113 @@ def _c_iterator(self, name): _c(' return ret;') _c('}') -def _c_accessor_get_length(expr, prefix=''): +def _c_accessor_get_length(expr, field_mapping=None): ''' Figures out what C code is needed to get a length field. + The field_mapping parameter can be used to change the absolute name of a length field. For fields that follow a variable-length field, use the accessor. Otherwise, just reference the structure field directly. ''' - prefarrow = '' if prefix == '' else prefix + '->' - if expr.lenfield != None and expr.lenfield.prev_varsized_field != None: - return expr.lenfield.c_accessor_name + '(' + prefix + ')' - elif expr.lenfield_name != None: - return prefarrow + expr.lenfield_name + lenfield_name = expr.lenfield_name + if lenfield_name is not None: + if field_mapping is not None: + lenfield_name = field_mapping[lenfield_name][0] + + if expr.lenfield is not None and expr.lenfield.prev_varsized_field is not None: + # special case: variable and fixed size fields are intermixed + # if the lenfield is among the fixed size fields, there is no need + # to call a special accessor function like + return field_mapping(expr.lenfield_name) + elif expr.lenfield_name is not None: + return lenfield_name else: return str(expr.nmemb) -def _c_accessor_get_expr(expr, prefix=''): +def _c_accessor_get_expr(expr, field_mapping): ''' Figures out what C code is needed to get the length of a list field. + The field_mapping parameter can be used to change the absolute name of a length field. Recurses for math operations. Returns bitcount for value-mask fields. Otherwise, uses the value of the length field. ''' - lenexp = _c_accessor_get_length(expr, prefix) + lenexp = _c_accessor_get_length(expr, field_mapping) if expr.op == '~': - return '(' + '~' + _c_accessor_get_expr(expr.rhs, prefix) + ')' + return '(' + '~' + _c_accessor_get_expr(expr.rhs, field_mapping) + ')' + elif expr.op == 'popcount': + return 'xcb_popcount(' + _c_accessor_get_expr(expr.rhs, field_mapping) + ')' + elif expr.op == 'enumref': + enum_name = expr.lenfield_type.name + constant_name = expr.lenfield_name + c_name = _n(enum_name + (constant_name,)).upper() + return c_name + elif expr.op == 'sumof': + # locate the referenced list object + list_obj = expr.lenfield_type + field = None + for f in expr.lenfield_parent.fields: + if f.field_name == expr.lenfield_name: + field = f + break + + if field is None: + raise Exception("list field '%s' referenced by sumof not found" % expr.lenfield_name) + list_name = field_mapping[field.c_field_name][0] + c_length_func = "%s(%s)" % (field.c_length_name, list_name) + # note: xcb_sumof() has only been defined for integers + c_length_func = _c_accessor_get_expr(field.type.expr, field_mapping) + return 'xcb_sumof(%s, %s)' % (list_name, c_length_func) elif expr.op != None: - return '(' + _c_accessor_get_expr(expr.lhs, prefix) + ' ' + expr.op + ' ' + _c_accessor_get_expr(expr.rhs, prefix) + ')' + return ('(' + _c_accessor_get_expr(expr.lhs, field_mapping) + + ' ' + expr.op + ' ' + + _c_accessor_get_expr(expr.rhs, field_mapping) + ')') elif expr.bitfield: return 'xcb_popcount(' + lenexp + ')' else: return lenexp +def type_pad_type(type): + if type == 'void': + return 'char' + return type + def _c_accessors_field(self, field): ''' Declares the accessor functions for a non-list field that follows a variable-length field. ''' + c_type = self.c_type + + # special case: switch + switch_obj = self if self.is_switch else None + if self.is_bitcase: + switch_obj = self.parents[-1] + if switch_obj is not None: + c_type = switch_obj.c_type + if field.type.is_simple: _hc('') _hc('') _hc('/*****************************************************************************') - _hc(' **') + _hc(' ** ') _hc(' ** %s %s', field.c_field_type, field.c_accessor_name) _hc(' ** ') - _hc(' ** @param const %s *R', self.c_type) + _hc(' ** @param const %s *R', c_type) _hc(' ** @returns %s', field.c_field_type) _hc(' **') _hc(' *****************************************************************************/') _hc(' ') _hc('%s', field.c_field_type) - _h('%s (const %s *R /**< */);', field.c_accessor_name, self.c_type) - _c('%s (const %s *R /**< */)', field.c_accessor_name, self.c_type) + _h('%s (const %s *R /**< */);', field.c_accessor_name, c_type) + _c('%s (const %s *R /**< */)', field.c_accessor_name, c_type) _c('{') - _c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) - _c(' return * (%s *) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index) + %d);', field.c_field_type, field.first_field_after_varsized.type.c_type, field.prev_varsized_offset) + if field.prev_varsized_field is None: + _c(' return (%s *) (R + 1);', field.c_field_type) + else: + _c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) + _c(' return * (%s *) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index) + %d);', + field.c_field_type, type_pad_type(field.first_field_after_varsized.type.c_type), field.prev_varsized_offset) _c('}') else: _hc('') @@ -487,18 +1482,31 @@ def _c_accessors_field(self, field): _hc(' **') _hc(' ** %s * %s', field.c_field_type, field.c_accessor_name) _hc(' ** ') - _hc(' ** @param const %s *R', self.c_type) + _hc(' ** @param const %s *R', c_type) _hc(' ** @returns %s *', field.c_field_type) _hc(' **') _hc(' *****************************************************************************/') _hc(' ') - _hc('%s *', field.c_field_type) - _h('%s (const %s *R /**< */);', field.c_accessor_name, self.c_type) - _c('%s (const %s *R /**< */)', field.c_accessor_name, self.c_type) + if field.type.is_switch and switch_obj is None: + return_type = 'void *' + else: + return_type = '%s *' % field.c_field_type + + _hc(return_type) + _h('%s (const %s *R /**< */);', field.c_accessor_name, c_type) + _c('%s (const %s *R /**< */)', field.c_accessor_name, c_type) _c('{') - _c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) - _c(' return (%s *) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index) + %d);', field.c_field_type, field.first_field_after_varsized.type.c_type, field.prev_varsized_offset) + if field.prev_varsized_field is None: + _c(' return (%s) (R + 1);', return_type) + # note: the special case 'variable fields followed by fixed size fields' + # is not of any consequence here, since the ordering gets + # 'corrected' in the reply function + else: + _c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) + _c(' return (%s) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index) + %d);', + return_type, type_pad_type(field.first_field_after_varsized.type.c_type), field.prev_varsized_offset) _c('}') + def _c_accessors_list(self, field): ''' @@ -507,32 +1515,83 @@ def _c_accessors_list(self, field): Declares length and get-iterator functions always. ''' list = field.type + c_type = self.c_type + + # special case: switch + # in case of switch, 2 params have to be supplied to certain accessor functions: + # 1. the anchestor object (request or reply) + # 2. the (anchestor) switch object + # the reason is that switch is either a child of a request/reply or nested in another switch, + # so whenever we need to access a length field, we might need to refer to some anchestor type + switch_obj = self if self.is_switch else None + if self.is_bitcase: + switch_obj = self.parents[-1] + if switch_obj is not None: + c_type = switch_obj.c_type + + params = [] + fields = {} + parents = self.parents if hasattr(self, 'parents') else [self] + # 'R': parents[0] is always the 'toplevel' container type + params.append(('const %s *R' % parents[0].c_type, parents[0])) + fields.update(_c_helper_field_mapping(parents[0], [('R', '->', parents[0])], flat=True)) + # auxiliary object for 'R' parameters + R_obj = parents[0] + + if switch_obj is not None: + # now look where the fields are defined that are needed to evaluate + # the switch expr, and store the parent objects in accessor_params and + # the fields in switch_fields + + # 'S': name for the 'toplevel' switch + toplevel_switch = parents[1] + params.append(('const %s *S' % toplevel_switch.c_type, toplevel_switch)) + fields.update(_c_helper_field_mapping(toplevel_switch, [('S', '->', toplevel_switch)], flat=True)) + + # initialize prefix for everything "below" S + prefix_str = '/* %s */ S' % toplevel_switch.name[-1] + prefix = [(prefix_str, '->', toplevel_switch)] + + # look for fields in the remaining containers + for p in parents[2:] + [self]: + # the separator between parent and child is always '.' here, + # because of nested switch statements + if not p.is_bitcase or (p.is_bitcase and p.has_name): + prefix.append((p.name[-1], '.', p)) + fields.update(_c_helper_field_mapping(p, prefix, flat=True)) + + # auxiliary object for 'S' parameter + S_obj = parents[1] _h_setlevel(1) _c_setlevel(1) if list.member.fixed_size(): + idx = 1 if switch_obj is not None else 0 _hc('') _hc('') _hc('/*****************************************************************************') _hc(' **') _hc(' ** %s * %s', field.c_field_type, field.c_accessor_name) _hc(' ** ') - _hc(' ** @param const %s *R', self.c_type) + _hc(' ** @param %s', params[idx][0]) _hc(' ** @returns %s *', field.c_field_type) _hc(' **') _hc(' *****************************************************************************/') _hc(' ') _hc('%s *', field.c_field_type) - _h('%s (const %s *R /**< */);', field.c_accessor_name, self.c_type) - _c('%s (const %s *R /**< */)', field.c_accessor_name, self.c_type) - _c('{') - if field.prev_varsized_field == None: + _h('%s (%s /**< */);', field.c_accessor_name, params[idx][0]) + _c('%s (%s /**< */)', field.c_accessor_name, params[idx][0]) + + _c('{') + if switch_obj is not None: + _c(' return %s;', fields[field.c_field_name][0]) + elif field.prev_varsized_field is None: _c(' return (%s *) (R + 1);', field.c_field_type) else: _c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) - _c(' return (%s *) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index) + %d);', field.c_field_type, field.first_field_after_varsized.type.c_type, field.prev_varsized_offset) - + _c(' return (%s *) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index) + %d);', + field.c_field_type, type_pad_type(field.first_field_after_varsized.type.c_type), field.prev_varsized_offset) _c('}') _hc('') @@ -541,16 +1600,24 @@ def _c_accessors_list(self, field): _hc(' **') _hc(' ** int %s', field.c_length_name) _hc(' ** ') - _hc(' ** @param const %s *R', self.c_type) + _hc(' ** @param const %s *R', c_type) _hc(' ** @returns int') _hc(' **') _hc(' *****************************************************************************/') _hc(' ') _hc('int') - _h('%s (const %s *R /**< */);', field.c_length_name, self.c_type) - _c('%s (const %s *R /**< */)', field.c_length_name, self.c_type) + if switch_obj is not None: + _hc('%s (const %s *R /**< */,', field.c_length_name, R_obj.c_type) + spacing = ' '*(len(field.c_length_name)+2) + _h('%sconst %s *S /**< */);', spacing, S_obj.c_type) + _c('%sconst %s *S /**< */)', spacing, S_obj.c_type) + length = _c_accessor_get_expr(field.type.expr, fields) + else: + _h('%s (const %s *R /**< */);', field.c_length_name, c_type) + _c('%s (const %s *R /**< */)', field.c_length_name, c_type) + length = _c_accessor_get_expr(field.type.expr, fields) _c('{') - _c(' return %s;', _c_accessor_get_expr(field.type.expr, 'R')) + _c(' return %s;', length) _c('}') if field.type.member.is_simple: @@ -560,25 +1627,38 @@ def _c_accessors_list(self, field): _hc(' **') _hc(' ** xcb_generic_iterator_t %s', field.c_end_name) _hc(' ** ') - _hc(' ** @param const %s *R', self.c_type) + _hc(' ** @param const %s *R', c_type) _hc(' ** @returns xcb_generic_iterator_t') _hc(' **') _hc(' *****************************************************************************/') _hc(' ') _hc('xcb_generic_iterator_t') - _h('%s (const %s *R /**< */);', field.c_end_name, self.c_type) - _c('%s (const %s *R /**< */)', field.c_end_name, self.c_type) + if switch_obj is not None: + _hc('%s (const %s *R /**< */,', field.c_end_name, R_obj.c_type) + spacing = ' '*(len(field.c_end_name)+2) + _h('%sconst %s *S /**< */);', spacing, S_obj.c_type) + _c('%sconst %s *S /**< */)', spacing, S_obj.c_type) + else: + _h('%s (const %s *R /**< */);', field.c_end_name, c_type) + _c('%s (const %s *R /**< */)', field.c_end_name, c_type) _c('{') _c(' xcb_generic_iterator_t i;') - - if field.prev_varsized_field == None: - _c(' i.data = ((%s *) (R + 1)) + (%s);', field.type.c_wiretype, _c_accessor_get_expr(field.type.expr, 'R')) + + param = 'R' if switch_obj is None else 'S' + if switch_obj is not None: + _c(' i.data = %s + %s;', fields[field.c_field_name][0], + _c_accessor_get_expr(field.type.expr, fields)) + elif field.prev_varsized_field == None: + _c(' i.data = ((%s *) (R + 1)) + (%s);', field.type.c_wiretype, + _c_accessor_get_expr(field.type.expr, fields)) else: - _c(' xcb_generic_iterator_t child = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) - _c(' i.data = ((%s *) child.data) + (%s);', field.type.c_wiretype, _c_accessor_get_expr(field.type.expr, 'R')) + _c(' xcb_generic_iterator_t child = %s;', + _c_iterator_get_end(field.prev_varsized_field, 'R')) + _c(' i.data = ((%s *) child.data) + (%s);', field.type.c_wiretype, + _c_accessor_get_expr(field.type.expr, fields)) _c(' i.rem = 0;') - _c(' i.index = (char *) i.data - (char *) R;') + _c(' i.index = (char *) i.data - (char *) %s;', param) _c(' return i;') _c('}') @@ -589,25 +1669,36 @@ def _c_accessors_list(self, field): _hc(' **') _hc(' ** %s %s', field.c_iterator_type, field.c_iterator_name) _hc(' ** ') - _hc(' ** @param const %s *R', self.c_type) + _hc(' ** @param const %s *R', c_type) _hc(' ** @returns %s', field.c_iterator_type) _hc(' **') _hc(' *****************************************************************************/') _hc(' ') + _hc('%s', field.c_iterator_type) - _h('%s (const %s *R /**< */);', field.c_iterator_name, self.c_type) - _c('%s (const %s *R /**< */)', field.c_iterator_name, self.c_type) + if switch_obj is not None: + _hc('%s (const %s *R /**< */,', field.c_iterator_name, R_obj.c_type) + spacing = ' '*(len(field.c_iterator_name)+2) + _h('%sconst %s *S /**< */);', spacing, S_obj.c_type) + _c('%sconst %s *S /**< */)', spacing, S_obj.c_type) + else: + _h('%s (const %s *R /**< */);', field.c_iterator_name, c_type) + _c('%s (const %s *R /**< */)', field.c_iterator_name, c_type) _c('{') _c(' %s i;', field.c_iterator_type) - if field.prev_varsized_field == None: + if switch_obj is not None: + _c(' i.data = %s;', fields[field.c_field_name][0]) + _c(' i.rem = %s;', _c_accessor_get_expr(field.type.expr, fields)) + elif field.prev_varsized_field == None: _c(' i.data = (%s *) (R + 1);', field.c_field_type) else: _c(' xcb_generic_iterator_t prev = %s;', _c_iterator_get_end(field.prev_varsized_field, 'R')) - _c(' i.data = (%s *) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index));', field.c_field_type, field.c_field_type) - - _c(' i.rem = %s;', _c_accessor_get_expr(field.type.expr, 'R')) - _c(' i.index = (char *) i.data - (char *) R;') + _c(' i.data = (%s *) ((char *) prev.data + XCB_TYPE_PAD(%s, prev.index));', + field.c_field_type, type_pad_type(field.c_field_type)) + if switch_obj is None: + _c(' i.rem = %s;', _c_accessor_get_expr(field.type.expr, fields)) + _c(' i.index = (char *) i.data - (char *) %s;', 'R' if switch_obj is None else 'S' ) _c(' return i;') _c('}') @@ -615,11 +1706,17 @@ def _c_accessors(self, name, base): ''' Declares the accessor functions for the fields of a structure. ''' - for field in self.fields: - if field.type.is_list and not field.type.fixed_size(): - _c_accessors_list(self, field) - elif field.prev_varsized_field != None: - _c_accessors_field(self, field) + # no accessors for switch itself - + # switch always needs to be unpacked explicitly +# if self.is_switch: +# pass +# else: + if True: + for field in self.fields: + if field.type.is_list and not field.type.fixed_size(): + _c_accessors_list(self, field) + elif field.prev_varsized_field is not None or not field.type.fixed_size(): + _c_accessors_field(self, field) def c_simple(self, name): ''' @@ -655,23 +1752,43 @@ def _c_complex(self): varfield = None for field in self.fields: - if not field.type.fixed_size(): + if not field.type.fixed_size() and not self.is_switch and not self.is_union: varfield = field.c_field_name continue - if varfield != None and not field.type.is_pad and field.wire: - errmsg = '%s: warning: variable field %s followed by fixed field %s\n' % (self.c_type, varfield, field.c_field_name) - sys.stderr.write(errmsg) - # sys.exit(1) if field.wire: struct_fields.append(field) - + for field in struct_fields: - if len(field.c_field_type) > maxtypelen: - maxtypelen = len(field.c_field_type) + length = len(field.c_field_type) + # account for '*' pointer_spec + if not field.type.fixed_size(): + length += 1 + maxtypelen = max(maxtypelen, length) + + def _c_complex_field(self, field, space=''): + if (field.type.fixed_size() or + # in case of switch with switch children, don't make the field a pointer + # necessary for unserialize to work + (self.is_switch and field.type.is_switch)): + spacing = ' ' * (maxtypelen - len(field.c_field_type)) + _h('%s %s%s %s%s; /**< */', space, field.c_field_type, spacing, field.c_field_name, field.c_subscript) + else: + spacing = ' ' * (maxtypelen - (len(field.c_field_type) + 1)) + _h('%s %s%s *%s%s; /**< */', space, field.c_field_type, spacing, field.c_field_name, field.c_subscript) - for field in struct_fields: - spacing = ' ' * (maxtypelen - len(field.c_field_type)) - _h(' %s%s %s%s; /**< */', field.c_field_type, spacing, field.c_field_name, field.c_subscript) + if not self.is_switch: + for field in struct_fields: + _c_complex_field(self, field) + else: + for b in self.bitcases: + space = '' + if b.type.has_name: + _h(' struct _%s {', b.c_field_name) + space = ' ' + for field in b.type.fields: + _c_complex_field(self, field, space) + if b.type.has_name: + _h(' } %s;', b.c_field_name) _h('} %s;', self.c_type) @@ -692,7 +1809,7 @@ def c_union(self, name): _c_complex(self) _c_iterator(self, name) -def _c_request_helper(self, name, cookie_type, void, regular): +def _c_request_helper(self, name, cookie_type, void, regular, aux=False): ''' Declares a request function. ''' @@ -725,15 +1842,18 @@ def _c_request_helper(self, name, cookie_type, void, regular): func_ext_global = '&' + _ns.c_ext_global_name if _ns.is_ext else '0' # What our function name is - func_name = self.c_request_name + func_name = self.c_request_name if not aux else self.c_aux_name if checked: - func_name = self.c_checked_name + func_name = self.c_checked_name if not aux else self.c_aux_checked_name if unchecked: - func_name = self.c_unchecked_name + func_name = self.c_unchecked_name if not aux else self.c_aux_unchecked_name param_fields = [] wire_fields = [] maxtypelen = len('xcb_connection_t') + serial_fields = [] + # special case: list with variable size elements + list_with_var_size_elems = False for field in self.fields: if field.visible: @@ -742,10 +1862,17 @@ def _c_request_helper(self, name, cookie_type, void, regular): if field.wire and not field.auto: # We need to set the field up in the structure wire_fields.append(field) + if field.type.need_serialize or field.type.need_sizeof: + serial_fields.append(field) for field in param_fields: - if len(field.c_field_const_type) > maxtypelen: - maxtypelen = len(field.c_field_const_type) + c_field_const_type = field.c_field_const_type + if field.type.need_serialize and not aux: + c_field_const_type = "const void" + if len(c_field_const_type) > maxtypelen: + maxtypelen = len(c_field_const_type) + if field.type.is_list and not field.type.member.fixed_size(): + list_with_var_size_elems = True _h_setlevel(1) _c_setlevel(1) @@ -777,8 +1904,11 @@ def _c_request_helper(self, name, cookie_type, void, regular): _hc(' ** @param xcb_connection_t%s *c', spacing) for field in param_fields: - spacing = ' ' * (maxtypelen - len(field.c_field_const_type)) - _hc(' ** @param %s%s %s%s', field.c_field_const_type, spacing, field.c_pointer, field.c_field_name) + c_field_const_type = field.c_field_const_type + if field.type.need_serialize and not aux: + c_field_const_type = "const void" + spacing = ' ' * (maxtypelen - len(c_field_const_type)) + _hc(' ** @param %s%s %s%s', c_field_const_type, spacing, field.c_pointer, field.c_field_name) _hc(' ** @returns %s', cookie_type) _hc(' **') @@ -796,16 +1926,28 @@ def _c_request_helper(self, name, cookie_type, void, regular): count = len(param_fields) for field in param_fields: count = count - 1 - spacing = ' ' * (maxtypelen - len(field.c_field_const_type)) + c_field_const_type = field.c_field_const_type + c_pointer = field.c_pointer + if field.type.need_serialize and not aux: + c_field_const_type = "const void" + c_pointer = '*' + spacing = ' ' * (maxtypelen - len(c_field_const_type)) comma = ',' if count else ');' - _h('%s%s%s %s%s /**< */%s', func_spacing, field.c_field_const_type, spacing, field.c_pointer, field.c_field_name, comma) + _h('%s%s%s %s%s /**< */%s', func_spacing, c_field_const_type, + spacing, c_pointer, field.c_field_name, comma) comma = ',' if count else ')' - _c('%s%s%s %s%s /**< */%s', func_spacing, field.c_field_const_type, spacing, field.c_pointer, field.c_field_name, comma) + _c('%s%s%s %s%s /**< */%s', func_spacing, c_field_const_type, + spacing, c_pointer, field.c_field_name, comma) count = 2 - for field in param_fields: - if not field.type.fixed_size(): - count = count + 2 + if not self.var_followed_by_fixed_fields: + for field in param_fields: + if not field.type.fixed_size(): + count = count + 2 + if field.type.need_serialize: + # _serialize() keeps track of padding automatically + count -= 1 + dimension = count + 2 _c('{') _c(' static const xcb_protocol_request_t xcb_req = {') @@ -815,16 +1957,31 @@ def _c_request_helper(self, name, cookie_type, void, regular): _c(' /* isvoid */ %d', 1 if void else 0) _c(' };') _c(' ') - _c(' struct iovec xcb_parts[%d];', count + 2) + + _c(' struct iovec xcb_parts[%d];', dimension) _c(' %s xcb_ret;', func_cookie) _c(' %s xcb_out;', self.c_type) - _c(' ') + if self.var_followed_by_fixed_fields: + _c(' /* in the protocol description, variable size fields are followed by fixed size fields */') + _c(' void *xcb_aux = 0;') + + for idx, f in enumerate(serial_fields): + if aux: + _c(' void *xcb_aux%d = 0;' % (idx)) + if list_with_var_size_elems: + _c(' unsigned int i;') + _c(' unsigned int xcb_tmp_len;') + _c(' char *xcb_tmp;') + _c(' ') + # simple request call tracing +# _c(' printf("in function %s\\n");' % func_name) + + # fixed size fields for field in wire_fields: if field.type.fixed_size(): if field.type.is_expr: - _c(' xcb_out.%s = %s;', field.c_field_name, _c_accessor_get_expr(field.type.expr)) - + _c(' xcb_out.%s = %s;', field.c_field_name, _c_accessor_get_expr(field.type.expr, None)) elif field.type.is_pad: if field.type.nmemb == 1: _c(' xcb_out.%s = 0;', field.c_field_name) @@ -836,25 +1993,94 @@ def _c_request_helper(self, name, cookie_type, void, regular): else: _c(' memcpy(xcb_out.%s, %s, %d);', field.c_field_name, field.c_field_name, field.type.nmemb) - _c(' ') - _c(' xcb_parts[2].iov_base = (char *) &xcb_out;') - _c(' xcb_parts[2].iov_len = sizeof(xcb_out);') - _c(' xcb_parts[3].iov_base = 0;') - _c(' xcb_parts[3].iov_len = -xcb_parts[2].iov_len & 3;') + def get_serialize_args(type_obj, c_field_name, aux_var, context='serialize'): + serialize_args = get_serialize_params(context, type_obj, + c_field_name, + aux_var)[2] + return reduce(lambda x,y: "%s, %s" % (x,y), [a[2] for a in serialize_args]) - count = 4 - for field in param_fields: - if not field.type.fixed_size(): - _c(' xcb_parts[%d].iov_base = (char *) %s;', count, field.c_field_name) - if field.type.is_list: - _c(' xcb_parts[%d].iov_len = %s * sizeof(%s);', count, _c_accessor_get_expr(field.type.expr), field.type.member.c_wiretype) - else: - _c(' xcb_parts[%d].iov_len = %s * sizeof(%s);', count, 'Uh oh', field.type.c_wiretype) - _c(' xcb_parts[%d].iov_base = 0;', count + 1) - _c(' xcb_parts[%d].iov_len = -xcb_parts[%d].iov_len & 3;', count + 1, count) - count = count + 2 + # calls in order to free dyn. all. memory + free_calls = [] + _c(' ') + if not self.var_followed_by_fixed_fields: + _c(' xcb_parts[2].iov_base = (char *) &xcb_out;') + _c(' xcb_parts[2].iov_len = sizeof(xcb_out);') + _c(' xcb_parts[3].iov_base = 0;') + _c(' xcb_parts[3].iov_len = -xcb_parts[2].iov_len & 3;') + + count = 4 + + for field in param_fields: + if not field.type.fixed_size(): + _c(' /* %s %s */', field.type.c_type, field.c_field_name) + # default: simple cast to char * + if not field.type.need_serialize and not field.type.need_sizeof: + _c(' xcb_parts[%d].iov_base = (char *) %s;', count, field.c_field_name) + if field.type.is_list: + if field.type.member.fixed_size(): + _c(' xcb_parts[%d].iov_len = %s * sizeof(%s);', count, + _c_accessor_get_expr(field.type.expr, None), + field.type.member.c_wiretype) + else: + list_length = _c_accessor_get_expr(field.type.expr, None) + + length = '' + _c(" xcb_parts[%d].iov_len = 0;" % count) + _c(" xcb_tmp = (char *)%s;", field.c_field_name) + _c(" for(i=0; i<%s; i++) {" % list_length) + _c(" xcb_tmp_len = %s(xcb_tmp);" % + (field.type.c_sizeof_name)) + _c(" xcb_parts[%d].iov_len += xcb_tmp_len;" % count) + _c(" xcb_tmp += xcb_tmp_len;") + _c(" }") + else: + # not supposed to happen + raise Exception("unhandled variable size field %s" % field.c_field_name) + else: + if not aux: + _c(' xcb_parts[%d].iov_base = (char *) %s;', count, field.c_field_name) + idx = serial_fields.index(field) + aux_var = '&xcb_aux%d' % idx + context = 'serialize' if aux else 'sizeof' + _c(' xcb_parts[%d].iov_len = ', count) + if aux: + serialize_args = get_serialize_args(field.type, aux_var, field.c_field_name, context) + _c(' %s (%s);', field.type.c_serialize_name, serialize_args) + _c(' xcb_parts[%d].iov_base = xcb_aux%d;' % (count, idx)) + free_calls.append(' free(xcb_aux%d);' % idx) + else: + serialize_args = get_serialize_args(field.type, field.c_field_name, aux_var, context) + func_name = field.type.c_sizeof_name + _c(' %s (%s);', func_name, serialize_args) + + count += 1 + if not (field.type.need_serialize or field.type.need_sizeof): + # the _serialize() function keeps track of padding automatically + _c(' xcb_parts[%d].iov_base = 0;', count) + _c(' xcb_parts[%d].iov_len = -xcb_parts[%d].iov_len & 3;', count, count-1) + count += 1 + + # elif self.var_followed_by_fixed_fields: + else: + _c(' xcb_parts[2].iov_base = (char *) &xcb_out;') + # request header: opcodes + length + _c(' xcb_parts[2].iov_len = 2*sizeof(uint8_t) + sizeof(uint16_t);') + count += 1 + # call _serialize() + buffer_var = '&xcb_aux' + serialize_args = get_serialize_args(self, buffer_var, '&xcb_out', 'serialize') + _c(' xcb_parts[%d].iov_len = %s (%s);', count, self.c_serialize_name, serialize_args) + _c(' xcb_parts[%d].iov_base = (char *) xcb_aux;', count) + free_calls.append(' free(xcb_aux);') + # no padding necessary - _serialize() keeps track of padding automatically + + _c(' ') _c(' xcb_ret.sequence = xcb_send_request(c, %s, xcb_parts + 2, &xcb_req);', func_flags) + + # free dyn. all. data, if any + for f in free_calls: + _c(f) _c(' return xcb_ret;') _c('}') @@ -865,7 +2091,29 @@ def _c_reply(self, name): spacing1 = ' ' * (len(self.c_cookie_type) - len('xcb_connection_t')) spacing2 = ' ' * (len(self.c_cookie_type) - len('xcb_generic_error_t')) spacing3 = ' ' * (len(self.c_reply_name) + 2) - + + # check if _unserialize() has to be called for any field + def look_for_special_cases(complex_obj): + unserialize_fields = [] + # no unserialize call in case of switch + if not complex_obj.is_switch: + for field in complex_obj.fields: + # three cases: 1. field with special case + # 2. container that contains special case field + # 3. list with special case elements + if field.type.var_followed_by_fixed_fields: + unserialize_fields.append(field) + elif field.type.is_container: + unserialize_fields += look_for_special_cases(field.type) + elif field.type.is_list: + if field.type.member.var_followed_by_fixed_fields: + unserialize_fields.append(field) + if field.type.member.is_container: + unserialize_fields += look_for_special_cases(field.type.member) + return unserialize_fields + + unserialize_fields = look_for_special_cases(self.reply) + _h('') _h('/**') _h(' * Return the reply') @@ -900,7 +2148,35 @@ def _c_reply(self, name): _h('%sxcb_generic_error_t%s **e /**< */);', spacing3, spacing2) _c('%sxcb_generic_error_t%s **e /**< */)', spacing3, spacing2) _c('{') - _c(' return (%s *) xcb_wait_for_reply(c, cookie.sequence, e);', self.c_reply_type) + + if len(unserialize_fields)>0: + # certain variable size fields need to be unserialized explicitly + _c(' %s *reply = (%s *) xcb_wait_for_reply(c, cookie.sequence, e);', + self.c_reply_type, self.c_reply_type) + _c(' int i;') + for field in unserialize_fields: + if field.type.is_list: + _c(' %s %s_iter = %s(reply);', field.c_iterator_type, field.c_field_name, field.c_iterator_name) + _c(' int %s_len = %s(reply);', field.c_field_name, field.c_length_name) + _c(' %s *%s_data;', field.c_field_type, field.c_field_name) + else: + raise Exception('not implemented: call _unserialize() in reply for non-list type %s', field.c_field_type) + # call _unserialize(), using the reply as source and target buffer + _c(' /* special cases: transform parts of the reply to match XCB data structures */') + for field in unserialize_fields: + if field.type.is_list: + _c(' for(i=0; i<%s_len; i++) {', field.c_field_name) + _c(' %s_data = %s_iter.data;', field.c_field_name, field.c_field_name) + _c(' %s((const void *)%s_data, &%s_data);', field.type.c_unserialize_name, + field.c_field_name, field.c_field_name) + _c(' %s(&%s_iter);', field.type.c_next_name, field.c_field_name) + _c(' }') + # return the transformed reply + _c(' return reply;') + + else: + _c(' return (%s *) xcb_wait_for_reply(c, cookie.sequence, e);', self.c_reply_type) + _c('}') def _c_opcode(name, opcode): @@ -948,6 +2224,9 @@ def c_request(self, name): # Request prototypes _c_request_helper(self, name, self.c_cookie_type, False, True) _c_request_helper(self, name, self.c_cookie_type, False, False) + if self.need_aux: + _c_request_helper(self, name, self.c_cookie_type, False, True, True) + _c_request_helper(self, name, self.c_cookie_type, False, False, True) # Reply accessors _c_accessors(self.reply, name + ('reply',), name) _c_reply(self, name) @@ -955,6 +2234,10 @@ def c_request(self, name): # Request prototypes _c_request_helper(self, name, 'xcb_void_cookie_t', True, False) _c_request_helper(self, name, 'xcb_void_cookie_t', True, True) + if self.need_aux: + _c_request_helper(self, name, 'xcb_void_cookie_t', True, False, True) + _c_request_helper(self, name, 'xcb_void_cookie_t', True, True, True) + def c_event(self, name): ''' @@ -1002,7 +2285,7 @@ output = {'open' : c_open, 'union' : c_union, 'request' : c_request, 'event' : c_event, - 'error' : c_error + 'error' : c_error, } # Boilerplate below this point @@ -1010,26 +2293,26 @@ output = {'open' : c_open, # Check for the argument that specifies path to the xcbgen python package. try: opts, args = getopt.getopt(sys.argv[1:], 'p:') -except getopt.GetoptError, err: - print str(err) - print 'Usage: c_client.py [-p path] file.xml' +except getopt.GetoptError as err: + print(err) + print('Usage: c_client.py [-p path] file.xml') sys.exit(1) for (opt, arg) in opts: if opt == '-p': - sys.path.append(arg) + sys.path.insert(1, arg) # Import the module class try: from xcbgen.state import Module except ImportError: - print '' - print 'Failed to load the xcbgen Python package!' - print 'Make sure that xcb/proto installed it on your Python path.' - print 'If not, you will need to create a .pth file or define $PYTHONPATH' - print 'to extend the path.' - print 'Refer to the README file in xcb/proto for more info.' - print '' + print(''' +Failed to load the xcbgen Python package! +Make sure that xcb/proto installed it on your Python path. +If not, you will need to create a .pth file or define $PYTHONPATH +to extend the path. +Refer to the README file in xcb/proto for more info. +''') raise # Parse the xml header diff --git a/src/xcb.h b/src/xcb.h index 35d8768..44f650a 100644 --- a/src/xcb.h +++ b/src/xcb.h @@ -35,7 +35,11 @@ #include #endif +#ifndef _WIN32 #include +#else +#include "xcb_windefs.h" +#endif #include @@ -65,6 +69,21 @@ extern "C" { /** X_TCP_PORT + display number = server port for TCP transport */ #define X_TCP_PORT 6000 +/** xcb connection errors because of socket, pipe and other stream errors. */ +#define XCB_CONN_ERROR 1 + +/** xcb connection shutdown because of extension not sppported */ +#define XCB_CONN_CLOSED_EXT_NOTSUPPORTED 2 + +/** malloc(), calloc() and realloc() error upon failure, for eg ENOMEM */ +#define XCB_CONN_CLOSED_MEM_INSUFFICIENT 3 + +/** Connection closed, exceeding request length that server accepts. */ +#define XCB_CONN_CLOSED_REQ_LEN_EXCEED 4 + +/** Connection closed, error during parsing display string. */ +#define XCB_CONN_CLOSED_PARSE_ERR 5 + #define XCB_TYPE_PAD(T,I) (-(I) & (sizeof(T) > 4 ? 3 : sizeof(T) - 1)) /* Opaque structures */ @@ -268,6 +287,22 @@ xcb_generic_event_t *xcb_wait_for_event(xcb_connection_t *c); xcb_generic_event_t *xcb_poll_for_event(xcb_connection_t *c); /** + * @brief Returns the next event without reading from the connection. + * @param c: The connection to the X server. + * @return The next already queued event from the server. + * + * This is a version of xcb_poll_for_event that only examines the + * event queue for new events. The function doesn't try to read new + * events from the connection if no queued events are found. + * + * This function is useful for callers that know in advance that all + * interesting events have already been read from the connection. For + * example, callers might use xcb_wait_for_reply and be interested + * only of events that preceded a specific reply. + */ +xcb_generic_event_t *xcb_poll_for_queued_event(xcb_connection_t *c); + +/** * @brief Return the error for a request, or NULL if none can ever arrive. * @param c: The connection to the X server. * @param cookie: The request cookie. @@ -376,15 +411,18 @@ int xcb_get_file_descriptor(xcb_connection_t *c); /** * @brief Test whether the connection has shut down due to a fatal error. * @param c: The connection. - * @return 1 if the connection is in an error state; 0 otherwise. + * @return > 0 if the connection is in an error state; 0 otherwise. * * Some errors that occur in the context of an xcb_connection_t * are unrecoverable. When such an error occurs, the * connection is shut down and further operations on the * xcb_connection_t have no effect. * - * @todo Other functions should document the conditions in - * which they shut down the connection. + * @return XCB_CONN_ERROR, because of socket errors, pipe errors or other stream errors. + * @return XCB_CONN_CLOSED_EXT_NOTSUPPORTED, when extension not supported. + * @return XCB_CONN_CLOSED_MEM_INSUFFICIENT, when memory not available. + * @return XCB_CONN_CLOSED_REQ_LEN_EXCEED, exceeding request length that server accepts. + * @return XCB_CONN_CLOSED_PARSE_ERR, error during parsing display string. */ int xcb_connection_has_error(xcb_connection_t *c); diff --git a/src/xcb_auth.c b/src/xcb_auth.c old mode 100755 new mode 100644 index d774d10..a5b730c --- a/src/xcb_auth.c +++ b/src/xcb_auth.c @@ -25,15 +25,35 @@ /* Authorization systems for the X protocol. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include -#include -#include -#include #include #include #include +#ifdef __INTERIX +/* _don't_ ask. interix has INADDR_LOOPBACK in here. */ +#include +#endif + +#ifdef _WIN32 +#ifdef HASXDMAUTH +/* We must include the wrapped windows.h before any system header which includes + it unwrapped, to avoid conflicts with types defined in X headers */ +#include +#endif +#include "xcb_windefs.h" +#else +#include +#include +#include +#include +#endif /* _WIN32 */ + #include "xcb.h" #include "xcbint.h" @@ -256,7 +276,7 @@ static struct sockaddr *get_peer_sock_name(int (*socket_func)(int, { socklen_t socknamelen = sizeof(struct sockaddr) + INITIAL_SOCKNAME_SLACK; socklen_t actual_socknamelen = socknamelen; - struct sockaddr *sockname = malloc(socknamelen), *new_sockname = NULL; + struct sockaddr *sockname = malloc(socknamelen); if (sockname == NULL) return NULL; @@ -269,14 +289,17 @@ static struct sockaddr *get_peer_sock_name(int (*socket_func)(int, if (actual_socknamelen > socknamelen) { + struct sockaddr *new_sockname = NULL; socknamelen = actual_socknamelen; - if ((new_sockname = realloc(sockname, actual_socknamelen)) == NULL || - socket_func(fd, new_sockname, &actual_socknamelen) == -1 || - actual_socknamelen > socknamelen) + if ((new_sockname = realloc(sockname, actual_socknamelen)) == NULL) goto sock_or_realloc_error; sockname = new_sockname; + + if (socket_func(fd, sockname, &actual_socknamelen) == -1 || + actual_socknamelen > socknamelen) + goto sock_or_realloc_error; } return sockname; @@ -322,10 +345,15 @@ int _xcb_get_auth_info(int fd, xcb_auth_info_t *info, int display) if (!info->namelen) goto no_auth; /* out of memory */ - if (!gotsockname && (sockname = get_peer_sock_name(getsockname, fd)) == NULL) + if (!gotsockname) { - free(info->name); - goto no_auth; /* can only authenticate sockets */ + free(sockname); + + if ((sockname = get_peer_sock_name(getsockname, fd)) == NULL) + { + free(info->name); + goto no_auth; /* can only authenticate sockets */ + } } ret = compute_auth(info, authptr, sockname); diff --git a/src/xcb_conn.c b/src/xcb_conn.c old mode 100755 new mode 100644 index 7e18891..725502a --- a/src/xcb_conn.c +++ b/src/xcb_conn.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -38,20 +37,45 @@ #include "xcbint.h" #if USE_POLL #include -#else +#elif !defined _WIN32 #include #endif +#ifdef _WIN32 +#include "xcb_windefs.h" +#else +#include +#include +#endif /* _WIN32 */ + +/* SHUT_RDWR is fairly recent and is not available on all platforms */ +#if !defined(SHUT_RDWR) +#define SHUT_RDWR 2 +#endif + typedef struct { uint8_t status; uint8_t pad0[5]; uint16_t length; } xcb_setup_generic_t; -static const int error_connection = 1; +static const int xcb_con_error = XCB_CONN_ERROR; +static const int xcb_con_closed_mem_er = XCB_CONN_CLOSED_MEM_INSUFFICIENT; +static const int xcb_con_closed_parse_er = XCB_CONN_CLOSED_PARSE_ERR; static int set_fd_flags(const int fd) { +/* Win32 doesn't have file descriptors and the fcntl function. This block sets the socket in non-blocking mode */ + +#ifdef _WIN32 + u_long iMode = 1; /* non-zero puts it in non-blocking mode, 0 in blocking mode */ + int ret = 0; + + ret = ioctlsocket(fd, FIONBIO, &iMode); + if(ret != 0) + return 0; + return 1; +#else int flags = fcntl(fd, F_GETFL, 0); if(flags == -1) return 0; @@ -61,6 +85,7 @@ static int set_fd_flags(const int fd) if(fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) return 0; return 1; +#endif /* _WIN32 */ } static int write_setup(xcb_connection_t *c, xcb_auth_info_t *auth_info) @@ -153,12 +178,40 @@ static int write_vec(xcb_connection_t *c, struct iovec **vector, int *count) { int n; assert(!c->out.queue_len); + +#ifdef _WIN32 + int i = 0; + int ret = 0,err = 0; + struct iovec *vec; + n = 0; + + /* Could use the WSASend win32 function for scatter/gather i/o but setting up the WSABUF struct from + an iovec would require more work and I'm not sure of the benefit....works for now */ + vec = *vector; + while(i < *count) + { + ret = send(c->fd,vec->iov_base,vec->iov_len,0); + if(ret == SOCKET_ERROR) + { + err = WSAGetLastError(); + if(err == WSAEWOULDBLOCK) + { + return 1; + } + } + n += ret; + *vec++; + i++; + } +#else n = writev(c->fd, *vector, *count); if(n < 0 && errno == EAGAIN) return 1; +#endif /* _WIN32 */ + if(n <= 0) { - _xcb_conn_shutdown(c); + _xcb_conn_shutdown(c, XCB_CONN_ERROR); return 0; } @@ -207,18 +260,20 @@ xcb_connection_t *xcb_connect_to_fd(int fd, xcb_auth_info_t *auth_info) { xcb_connection_t* c; +#ifndef _WIN32 #ifndef USE_POLL if(fd >= FD_SETSIZE) /* would overflow in FD_SET */ { close(fd); - return (xcb_connection_t *) &error_connection; + return _xcb_conn_ret_error(XCB_CONN_ERROR); } #endif +#endif /* !_WIN32*/ c = calloc(1, sizeof(xcb_connection_t)); if(!c) { close(fd); - return (xcb_connection_t *) &error_connection; + return _xcb_conn_ret_error(XCB_CONN_CLOSED_MEM_INSUFFICIENT) ; } c->fd = fd; @@ -235,7 +290,7 @@ xcb_connection_t *xcb_connect_to_fd(int fd, xcb_auth_info_t *auth_info) )) { xcb_disconnect(c); - return (xcb_connection_t *) &error_connection; + return _xcb_conn_ret_error(XCB_CONN_ERROR); } return c; @@ -247,6 +302,9 @@ void xcb_disconnect(xcb_connection_t *c) return; free(c->setup); + + /* disallow further sends and receives */ + shutdown(c->fd, SHUT_RDWR); close(c->fd); pthread_mutex_destroy(&c->iolock); @@ -257,13 +315,42 @@ void xcb_disconnect(xcb_connection_t *c) _xcb_xid_destroy(c); free(c); + +#ifdef _WIN32 + WSACleanup(); +#endif } /* Private interface */ -void _xcb_conn_shutdown(xcb_connection_t *c) +void _xcb_conn_shutdown(xcb_connection_t *c, int err) { - c->has_error = 1; + c->has_error = err; +} + +/* Return connection error state. + * To make thread-safe, I need a seperate static + * variable for every possible error. + */ +xcb_connection_t *_xcb_conn_ret_error(int err) +{ + + switch(err) + { + case XCB_CONN_CLOSED_MEM_INSUFFICIENT: + { + return (xcb_connection_t *) &xcb_con_closed_mem_er; + } + case XCB_CONN_CLOSED_PARSE_ERR: + { + return (xcb_connection_t *) &xcb_con_closed_parse_er; + } + case XCB_CONN_ERROR: + default: + { + return (xcb_connection_t *) &xcb_con_error; + } + } } int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vector, int *count) @@ -311,13 +398,20 @@ int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vec do { #if USE_POLL ret = poll(&fd, 1, -1); + /* If poll() returns an event we didn't expect, such as POLLNVAL, treat + * it as if it failed. */ + if(ret >= 0 && (fd.revents & ~fd.events)) + { + ret = -1; + break; + } #else ret = select(c->fd + 1, &rfds, &wfds, 0, 0); #endif } while (ret == -1 && errno == EINTR); if(ret < 0) { - _xcb_conn_shutdown(c); + _xcb_conn_shutdown(c, XCB_CONN_ERROR); ret = 0; } pthread_mutex_lock(&c->iolock); diff --git a/src/xcb_in.c b/src/xcb_in.c old mode 100755 new mode 100644 index a644c63..4998cdd --- a/src/xcb_in.c +++ b/src/xcb_in.c @@ -37,10 +37,16 @@ #include "xcbint.h" #if USE_POLL #include -#else +#endif +#ifndef _WIN32 #include +#include #endif +#ifdef _WIN32 +#include "xcb_windefs.h" +#endif /* _WIN32 */ + #define XCB_ERROR 0 #define XCB_REPLY 1 #define XCB_XGE_EVENT 35 @@ -64,11 +70,22 @@ typedef struct pending_reply { } pending_reply; typedef struct reader_list { - unsigned int request; + uint64_t request; pthread_cond_t *data; struct reader_list *next; } reader_list; +static void remove_finished_readers(reader_list **prev_reader, uint64_t completed) +{ + while(*prev_reader && XCB_SEQUENCE_COMPARE((*prev_reader)->request, <=, completed)) + { + /* If you don't have what you're looking for now, you never + * will. Wake up and leave me alone. */ + pthread_cond_signal((*prev_reader)->data); + *prev_reader = (*prev_reader)->next; + } +} + static int read_packet(xcb_connection_t *c) { xcb_generic_reply_t genrep; @@ -119,6 +136,8 @@ static int read_packet(xcb_connection_t *c) if(genrep.response_type == XCB_ERROR) c->in.request_completed = c->in.request_read; + + remove_finished_readers(&c->in.readers, c->in.request_completed); } if(genrep.response_type == XCB_ERROR || genrep.response_type == XCB_REPLY) @@ -143,20 +162,14 @@ static int read_packet(xcb_connection_t *c) } /* XGE events may have sizes > 32 */ -#ifndef _F_ENABLE_XI2_SENDEVENT_ - if (genrep.response_type == XCB_XGE_EVENT) -#else//_F_ENABLE_XI2_SENDEVENT_ if ((genrep.response_type & 0x7f) == XCB_XGE_EVENT) -#endif//_F_ENABLE_XI2_SENDEVENT_ - { eventlength = genrep.length * 4; - } buf = malloc(length + eventlength + (genrep.response_type == XCB_REPLY ? 0 : sizeof(uint32_t))); if(!buf) { - _xcb_conn_shutdown(c); + _xcb_conn_shutdown(c, XCB_CONN_CLOSED_MEM_INSUFFICIENT); return 0; } @@ -189,11 +202,10 @@ static int read_packet(xcb_connection_t *c) if( genrep.response_type == XCB_REPLY || (genrep.response_type == XCB_ERROR && pend && (pend->flags & XCB_REQUEST_CHECKED))) { - reader_list *reader; struct reply_list *cur = malloc(sizeof(struct reply_list)); if(!cur) { - _xcb_conn_shutdown(c); + _xcb_conn_shutdown(c, XCB_CONN_CLOSED_MEM_INSUFFICIENT); free(buf); return 0; } @@ -201,17 +213,8 @@ static int read_packet(xcb_connection_t *c) cur->next = 0; *c->in.current_reply_tail = cur; c->in.current_reply_tail = &cur->next; - for(reader = c->in.readers; - reader && - XCB_SEQUENCE_COMPARE_32(reader->request, <=, c->in.request_read); - reader = reader->next) - { - if(XCB_SEQUENCE_COMPARE_32(reader->request, ==, c->in.request_read)) - { - pthread_cond_signal(reader->data); - break; - } - } + if(c->in.readers && c->in.readers->request == c->in.request_read) + pthread_cond_signal(c->in.readers->data); return 1; } @@ -219,7 +222,7 @@ static int read_packet(xcb_connection_t *c) event = malloc(sizeof(struct event_list)); if(!event) { - _xcb_conn_shutdown(c); + _xcb_conn_shutdown(c, XCB_CONN_CLOSED_MEM_INSUFFICIENT); free(buf); return 0; } @@ -261,10 +264,14 @@ static int read_block(const int fd, void *buf, const ssize_t len) int done = 0; while(done < len) { - int ret = read(fd, ((char *) buf) + done, len - done); + int ret = recv(fd, ((char *) buf) + done, len - done, 0); if(ret > 0) done += ret; +#ifndef _WIN32 if(ret < 0 && errno == EAGAIN) +#else + if(ret == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK) +#endif /* !_Win32 */ { #if USE_POLL struct pollfd pfd; @@ -278,10 +285,13 @@ static int read_block(const int fd, void *buf, const ssize_t len) fd_set fds; FD_ZERO(&fds); FD_SET(fd, &fds); + + /* Initializing errno here makes sure that for Win32 this loop will execute only once */ + errno = 0; do { ret = select(fd + 1, &fds, 0, 0, 0); } while (ret == -1 && errno == EINTR); -#endif +#endif /* USE_POLL */ } if(ret <= 0) return ret; @@ -289,7 +299,7 @@ static int read_block(const int fd, void *buf, const ssize_t len) return len; } -static int poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error) +static int poll_for_reply(xcb_connection_t *c, uint64_t request, void **reply, xcb_generic_error_t **error) { struct reply_list *head; @@ -298,7 +308,7 @@ static int poll_for_reply(xcb_connection_t *c, unsigned int request, void **repl head = 0; /* We've read requests past the one we want, so if it has replies we have * them all and they're in the replies map. */ - else if(XCB_SEQUENCE_COMPARE_32(request, <, c->in.request_read)) + else if(XCB_SEQUENCE_COMPARE(request, <, c->in.request_read)) { head = _xcb_map_remove(c->in.replies, request); if(head && head->next) @@ -306,7 +316,7 @@ static int poll_for_reply(xcb_connection_t *c, unsigned int request, void **repl } /* We're currently processing the responses to the request we want, and we * have a reply ready to return. So just return it without blocking. */ - else if(XCB_SEQUENCE_COMPARE_32(request, ==, c->in.request_read) && c->in.current_reply) + else if(request == c->in.request_read && c->in.current_reply) { head = c->in.current_reply; c->in.current_reply = head->next; @@ -315,7 +325,7 @@ static int poll_for_reply(xcb_connection_t *c, unsigned int request, void **repl } /* We know this request can't have any more replies, and we've already * established it doesn't have a reply now. Don't bother blocking. */ - else if(XCB_SEQUENCE_COMPARE_32(request, ==, c->in.request_completed)) + else if(request == c->in.request_completed) head = 0; /* We may have more replies on the way for this request: block until we're * sure. */ @@ -344,61 +354,70 @@ static int poll_for_reply(xcb_connection_t *c, unsigned int request, void **repl return 1; } -/* Public interface */ - -void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e) +static void insert_reader(reader_list **prev_reader, reader_list *reader, uint64_t request, pthread_cond_t *cond) { - uint64_t widened_request; - void *ret = 0; - if(e) - *e = 0; - if(c->has_error) - return 0; + while(*prev_reader && XCB_SEQUENCE_COMPARE((*prev_reader)->request, <=, request)) + prev_reader = &(*prev_reader)->next; + reader->request = request; + reader->data = cond; + reader->next = *prev_reader; + *prev_reader = reader; +} - pthread_mutex_lock(&c->iolock); +static void remove_reader(reader_list **prev_reader, reader_list *reader) +{ + while(*prev_reader && XCB_SEQUENCE_COMPARE((*prev_reader)->request, <=, reader->request)) + if(*prev_reader == reader) + { + *prev_reader = (*prev_reader)->next; + break; + } +} - widened_request = (c->out.request & UINT64_C(0xffffffff00000000)) | request; - if(widened_request > c->out.request) - widened_request -= UINT64_C(1) << 32; +static void *wait_for_reply(xcb_connection_t *c, uint64_t request, xcb_generic_error_t **e) +{ + void *ret = 0; /* If this request has not been written yet, write it. */ - if(c->out.return_socket || _xcb_out_flush_to(c, widened_request)) + if(c->out.return_socket || _xcb_out_flush_to(c, request)) { pthread_cond_t cond = PTHREAD_COND_INITIALIZER; reader_list reader; - reader_list **prev_reader; - - for(prev_reader = &c->in.readers; - *prev_reader && - XCB_SEQUENCE_COMPARE_32((*prev_reader)->request, <=, request); - prev_reader = &(*prev_reader)->next) - { - /* empty */; - } - reader.request = request; - reader.data = &cond; - reader.next = *prev_reader; - *prev_reader = &reader; + + insert_reader(&c->in.readers, &reader, request, &cond); while(!poll_for_reply(c, request, &ret, e)) if(!_xcb_conn_wait(c, &cond, 0, 0)) break; - for(prev_reader = &c->in.readers; - *prev_reader && - XCB_SEQUENCE_COMPARE_32((*prev_reader)->request, <=, request); - prev_reader = &(*prev_reader)->next) - { - if(*prev_reader == &reader) - { - *prev_reader = (*prev_reader)->next; - break; - } - } + remove_reader(&c->in.readers, &reader); pthread_cond_destroy(&cond); } _xcb_in_wake_up_next_reader(c); + return ret; +} + +static uint64_t widen(xcb_connection_t *c, unsigned int request) +{ + uint64_t widened_request = (c->out.request & UINT64_C(0xffffffff00000000)) | request; + if(widened_request > c->out.request) + widened_request -= UINT64_C(1) << 32; + return widened_request; +} + +/* Public interface */ + +void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e) +{ + void *ret; + if(e) + *e = 0; + if(c->has_error) + return 0; + + pthread_mutex_lock(&c->iolock); + ret = wait_for_reply(c, widen(c, request), e); pthread_mutex_unlock(&c->iolock); return ret; } @@ -409,7 +428,7 @@ static void insert_pending_discard(xcb_connection_t *c, pending_reply **prev_nex pend = malloc(sizeof(*pend)); if(!pend) { - _xcb_conn_shutdown(c); + _xcb_conn_shutdown(c, XCB_CONN_CLOSED_MEM_INSUFFICIENT); return; } @@ -424,66 +443,27 @@ static void insert_pending_discard(xcb_connection_t *c, pending_reply **prev_nex c->in.pending_replies_tail = &pend->next; } -static void discard_reply(xcb_connection_t *c, unsigned int request) +static void discard_reply(xcb_connection_t *c, uint64_t request) { - pending_reply *pend = 0; + void *reply; pending_reply **prev_pend; - uint64_t widened_request; - - /* We've read requests past the one we want, so if it has replies we have - * them all and they're in the replies map. */ - if(XCB_SEQUENCE_COMPARE_32(request, <, c->in.request_read)) - { - struct reply_list *head; - head = _xcb_map_remove(c->in.replies, request); - while (head) - { - struct reply_list *next = head->next; - free(head->reply); - free(head); - head = next; - } - return; - } - - /* We're currently processing the responses to the request we want, and we - * have a reply ready to return. Free it, and mark the pend to free any further - * replies. */ - if(XCB_SEQUENCE_COMPARE_32(request, ==, c->in.request_read) && c->in.current_reply) - { - struct reply_list *head; - head = c->in.current_reply; - c->in.current_reply = NULL; - c->in.current_reply_tail = &c->in.current_reply; - while (head) - { - struct reply_list *next = head->next; - free(head->reply); - free(head); - head = next; - } - pend = c->in.pending_replies; - if(pend && - !(XCB_SEQUENCE_COMPARE(pend->first_request, <=, c->in.request_read) && - (pend->workaround == WORKAROUND_EXTERNAL_SOCKET_OWNER || - XCB_SEQUENCE_COMPARE(c->in.request_read, <=, pend->last_request)))) - pend = 0; - if(pend) - pend->flags |= XCB_REQUEST_DISCARD_REPLY; - else - insert_pending_discard(c, &c->in.pending_replies, c->in.request_read); + /* Free any replies or errors that we've already read. Stop if + * xcb_wait_for_reply would block or we've run out of replies. */ + while(poll_for_reply(c, request, &reply, 0) && reply) + free(reply); + /* If we've proven there are no more responses coming, we're done. */ + if(XCB_SEQUENCE_COMPARE(request, <=, c->in.request_completed)) return; - } /* Walk the list of pending requests. Mark the first match for deletion. */ for(prev_pend = &c->in.pending_replies; *prev_pend; prev_pend = &(*prev_pend)->next) { - if(XCB_SEQUENCE_COMPARE_32((*prev_pend)->first_request, >, request)) + if(XCB_SEQUENCE_COMPARE((*prev_pend)->first_request, >, request)) break; - if(XCB_SEQUENCE_COMPARE_32((*prev_pend)->first_request, ==, request)) + if((*prev_pend)->first_request == request) { /* Pending reply found. Mark for discard: */ (*prev_pend)->flags |= XCB_REQUEST_DISCARD_REPLY; @@ -492,11 +472,7 @@ static void discard_reply(xcb_connection_t *c, unsigned int request) } /* Pending reply not found (likely due to _unchecked request). Create one: */ - widened_request = (c->out.request & UINT64_C(0xffffffff00000000)) | request; - if(widened_request > c->out.request) - widened_request -= UINT64_C(1) << 32; - - insert_pending_discard(c, prev_pend, widened_request); + insert_pending_discard(c, prev_pend, request); } void xcb_discard_reply(xcb_connection_t *c, unsigned int sequence) @@ -509,7 +485,7 @@ void xcb_discard_reply(xcb_connection_t *c, unsigned int sequence) return; pthread_mutex_lock(&c->iolock); - discard_reply(c, sequence); + discard_reply(c, widen(c, sequence)); pthread_mutex_unlock(&c->iolock); } @@ -525,7 +501,7 @@ int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, } assert(reply != 0); pthread_mutex_lock(&c->iolock); - ret = poll_for_reply(c, request, reply, error); + ret = poll_for_reply(c, widen(c, request), reply, error); pthread_mutex_unlock(&c->iolock); return ret; } @@ -546,7 +522,7 @@ xcb_generic_event_t *xcb_wait_for_event(xcb_connection_t *c) return ret; } -xcb_generic_event_t *xcb_poll_for_event(xcb_connection_t *c) +static xcb_generic_event_t *poll_for_next_event(xcb_connection_t *c, int queued) { xcb_generic_event_t *ret = 0; if(!c->has_error) @@ -554,30 +530,41 @@ xcb_generic_event_t *xcb_poll_for_event(xcb_connection_t *c) pthread_mutex_lock(&c->iolock); /* FIXME: follow X meets Z architecture changes. */ ret = get_event(c); - if(!ret && _xcb_in_read(c)) /* _xcb_in_read shuts down the connection on error */ + if(!ret && !queued && c->in.reading == 0 && _xcb_in_read(c)) /* _xcb_in_read shuts down the connection on error */ ret = get_event(c); pthread_mutex_unlock(&c->iolock); } return ret; } +xcb_generic_event_t *xcb_poll_for_event(xcb_connection_t *c) +{ + return poll_for_next_event(c, 0); +} + +xcb_generic_event_t *xcb_poll_for_queued_event(xcb_connection_t *c) +{ + return poll_for_next_event(c, 1); +} + xcb_generic_error_t *xcb_request_check(xcb_connection_t *c, xcb_void_cookie_t cookie) { - /* FIXME: this could hold the lock to avoid syncing unnecessarily, but - * that would require factoring the locking out of xcb_get_input_focus, - * xcb_get_input_focus_reply, and xcb_wait_for_reply. */ - xcb_generic_error_t *ret; + uint64_t request; + xcb_generic_error_t *ret = 0; void *reply; if(c->has_error) return 0; - if(XCB_SEQUENCE_COMPARE_32(cookie.sequence,>,c->in.request_expected) - && XCB_SEQUENCE_COMPARE_32(cookie.sequence,>,c->in.request_completed)) + pthread_mutex_lock(&c->iolock); + request = widen(c, cookie.sequence); + if(XCB_SEQUENCE_COMPARE(request, >=, c->in.request_expected) + && XCB_SEQUENCE_COMPARE(request, >, c->in.request_completed)) { - free(xcb_get_input_focus_reply(c, xcb_get_input_focus(c), &ret)); - assert(!ret); + _xcb_out_send_sync(c); + _xcb_out_flush_to(c, c->out.request); } - reply = xcb_wait_for_reply(c, cookie.sequence, &ret); + reply = wait_for_reply(c, request, &ret); assert(!reply); + pthread_mutex_unlock(&c->iolock); return ret; } @@ -641,7 +628,7 @@ int _xcb_in_expect_reply(xcb_connection_t *c, uint64_t request, enum workarounds assert(workaround != WORKAROUND_NONE || flags != 0); if(!pend) { - _xcb_conn_shutdown(c); + _xcb_conn_shutdown(c, XCB_CONN_CLOSED_MEM_INSUFFICIENT); return 0; } pend->first_request = pend->last_request = request; @@ -669,14 +656,18 @@ void _xcb_in_replies_done(xcb_connection_t *c) int _xcb_in_read(xcb_connection_t *c) { - int n = read(c->fd, c->in.queue + c->in.queue_len, sizeof(c->in.queue) - c->in.queue_len); + int n = recv(c->fd, c->in.queue + c->in.queue_len, sizeof(c->in.queue) - c->in.queue_len, 0); if(n > 0) c->in.queue_len += n; while(read_packet(c)) /* empty */; +#ifndef _WIN32 if((n > 0) || (n < 0 && errno == EAGAIN)) +#else + if((n > 0) || (n < 0 && WSAGetLastError() == WSAEWOULDBLOCK)) +#endif /* !_WIN32 */ return 1; - _xcb_conn_shutdown(c); + _xcb_conn_shutdown(c, XCB_CONN_ERROR); return 0; } @@ -695,7 +686,7 @@ int _xcb_in_read_block(xcb_connection_t *c, void *buf, int len) int ret = read_block(c->fd, (char *) buf + done, len - done); if(ret <= 0) { - _xcb_conn_shutdown(c); + _xcb_conn_shutdown(c, XCB_CONN_ERROR); return ret; } } diff --git a/src/xcb_out.c b/src/xcb_out.c old mode 100755 new mode 100644 index fbce7a0..c0601f2 --- a/src/xcb_out.c +++ b/src/xcb_out.c @@ -35,8 +35,17 @@ #include "xcbint.h" #include "bigreq.h" -static int write_block(xcb_connection_t *c, struct iovec *vector, int count) +static inline void send_request(xcb_connection_t *c, int isvoid, enum workarounds workaround, int flags, struct iovec *vector, int count) { + if(c->has_error) + return; + + ++c->out.request; + if(!isvoid) + c->in.request_expected = c->out.request; + if(workaround != WORKAROUND_NONE || flags != 0) + _xcb_in_expect_reply(c, c->out.request, workaround, flags); + while(count && c->out.queue_len + vector[0].iov_len <= sizeof(c->out.queue)) { memcpy(c->out.queue + c->out.queue_len, vector[0].iov_base, vector[0].iov_len); @@ -46,13 +55,29 @@ static int write_block(xcb_connection_t *c, struct iovec *vector, int count) ++vector, --count; } if(!count) - return 1; + return; --vector, ++count; vector[0].iov_base = c->out.queue; vector[0].iov_len = c->out.queue_len; c->out.queue_len = 0; - return _xcb_out_send(c, vector, count); + _xcb_out_send(c, vector, count); +} + +static void send_sync(xcb_connection_t *c) +{ + static const union { + struct { + uint8_t major; + uint8_t pad; + uint16_t len; + } fields; + uint32_t packet; + } sync_req = { { /* GetInputFocus */ 43, 0, 1 } }; + struct iovec vector[2]; + vector[1].iov_base = (char *) &sync_req; + vector[1].iov_len = sizeof(sync_req); + send_request(c, 0, WORKAROUND_NONE, XCB_REQUEST_DISCARD_REPLY, vector + 1, 1); } static void get_socket_back(xcb_connection_t *c) @@ -123,16 +148,8 @@ uint32_t xcb_get_maximum_request_length(xcb_connection_t *c) unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *req) { - static const union { - struct { - uint8_t major; - uint8_t pad; - uint16_t len; - } fields; - uint32_t packet; - } sync_req = { { /* GetInputFocus */ 43, 0, 1 } }; uint64_t request; - uint32_t prefix[3] = { 0 }; + uint32_t prefix[2]; int veclen = req->count; enum workarounds workaround = WORKAROUND_NONE; @@ -156,7 +173,7 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, req->ext); if(!(extension && extension->present)) { - _xcb_conn_shutdown(c); + _xcb_conn_shutdown(c, XCB_CONN_CLOSED_EXT_NOTSUPPORTED); return 0; } ((uint8_t *) vector[0].iov_base)[0] = extension->major_opcode; @@ -186,14 +203,22 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect } else if(longlen > xcb_get_maximum_request_length(c)) { - _xcb_conn_shutdown(c); + _xcb_conn_shutdown(c, XCB_CONN_CLOSED_REQ_LEN_EXCEED); return 0; /* server can't take this; maybe need BIGREQUESTS? */ } /* set the length field. */ ((uint16_t *) vector[0].iov_base)[1] = shortlen; if(!shortlen) - prefix[2] = ++longlen; + { + prefix[0] = ((uint32_t *) vector[0].iov_base)[0]; + prefix[1] = ++longlen; + vector[0].iov_base = (uint32_t *) vector[0].iov_base + 1; + vector[0].iov_len -= sizeof(uint32_t); + --vector, ++veclen; + vector[0].iov_base = prefix; + vector[0].iov_len = sizeof(prefix); + } } flags &= ~XCB_REQUEST_RAW; @@ -212,45 +237,21 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect pthread_cond_wait(&c->out.cond, &c->iolock); get_socket_back(c); - request = ++c->out.request; /* send GetInputFocus (sync_req) when 64k-2 requests have been sent without - * a reply. - * Also send sync_req (could use NoOp) at 32-bit wrap to avoid having + * a reply. */ + if(req->isvoid && c->out.request == c->in.request_expected + (1 << 16) - 2) + send_sync(c); + /* Also send sync_req (could use NoOp) at 32-bit wrap to avoid having * applications see sequence 0 as that is used to indicate * an error in sending the request */ - while((req->isvoid && - c->out.request == c->in.request_expected + (1 << 16) - 1) || - request == 0) - { - prefix[0] = sync_req.packet; - _xcb_in_expect_reply(c, request, WORKAROUND_NONE, XCB_REQUEST_DISCARD_REPLY); - c->in.request_expected = c->out.request; - request = ++c->out.request; - } - - if(workaround != WORKAROUND_NONE || flags != 0) - _xcb_in_expect_reply(c, request, workaround, flags); - if(!req->isvoid) - c->in.request_expected = c->out.request; - - if(prefix[0] || prefix[2]) - { - --vector, ++veclen; - if(prefix[2]) - { - prefix[1] = ((uint32_t *) vector[1].iov_base)[0]; - vector[1].iov_base = (uint32_t *) vector[1].iov_base + 1; - vector[1].iov_len -= sizeof(uint32_t); - } - vector[0].iov_len = sizeof(uint32_t) * ((prefix[0] ? 1 : 0) + (prefix[2] ? 2 : 0)); - vector[0].iov_base = prefix + !prefix[0]; - } - - if(!write_block(c, vector, veclen)) - { - _xcb_conn_shutdown(c); - request = 0; - } + if((unsigned int) (c->out.request + 1) == 0) + send_sync(c); + + /* The above send_sync calls could drop the I/O lock, but this + * thread will still exclude any other thread that tries to write, + * so the sequence number postconditions still hold. */ + send_request(c, req->isvoid, workaround, flags, vector, veclen); + request = c->has_error ? 0 : c->out.request; pthread_mutex_unlock(&c->iolock); return request; } @@ -262,7 +263,13 @@ int xcb_take_socket(xcb_connection_t *c, void (*return_socket)(void *closure), v return 0; pthread_mutex_lock(&c->iolock); get_socket_back(c); - ret = _xcb_out_flush_to(c, c->out.request); + + /* _xcb_out_flush may drop the iolock allowing other threads to + * write requests, so keep flushing until we're done + */ + do + ret = _xcb_out_flush_to(c, c->out.request); + while (ret && c->out.request != c->out.request_written); if(ret) { c->out.return_socket = return_socket; @@ -342,6 +349,15 @@ int _xcb_out_send(xcb_connection_t *c, struct iovec *vector, int count) return ret; } +void _xcb_out_send_sync(xcb_connection_t *c) +{ + /* wait for other writing threads to get out of my way. */ + while(c->out.writing) + pthread_cond_wait(&c->out.cond, &c->iolock); + get_socket_back(c); + send_sync(c); +} + int _xcb_out_flush_to(xcb_connection_t *c, uint64_t request) { assert(XCB_SEQUENCE_COMPARE(request, <=, c->out.request)); diff --git a/src/xcb_util.c b/src/xcb_util.c old mode 100755 new mode 100644 index 5a82ac1..f17aa3c --- a/src/xcb_util.c +++ b/src/xcb_util.c @@ -27,29 +27,35 @@ #include #include -#include #include -#include -#include -#include -#ifdef DNETCONN -#include -#include -#endif -#include #include #include #include #include #include -#include #include +#ifdef _WIN32 +#include "xcb_windefs.h" +#else +#include +#include +#include +#include +#include +#include +#include +#endif /* _WIN32 */ + #include "xcb.h" #include "xcbext.h" #include "xcbint.h" -static const int error_connection = 1; +/* must be after "xcbint.h" to get autoconf #defines */ +#if defined(HAVE_TSOL_LABEL_H) && defined(HAVE_IS_SYSTEM_LABELED) +# include +# include +#endif int xcb_popcount(uint32_t mask) { @@ -59,6 +65,16 @@ int xcb_popcount(uint32_t mask) return ((y + (y >> 3)) & 030707070707) % 077; } +int xcb_sumof(uint8_t *list, int len) +{ + int i, s = 0; + for(i=0; i= 0) fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif } return fd; } -#ifdef DNETCONN -static int _xcb_open_decnet(const char *host, const char *protocol, const unsigned short port) -{ - int fd; - struct sockaddr_dn addr; - struct accessdata_dn accessdata; - struct nodeent *nodeaddr = getnodebyname(host); - - if(!nodeaddr) - return -1; - if (protocol && strcmp("dnet",protocol)) - return -1; - addr.sdn_family = AF_DECnet; - addr.sdn_add.a_len = nodeaddr->n_length; - memcpy(addr.sdn_add.a_addr, nodeaddr->n_addr, addr.sdn_add.a_len); +static int _xcb_do_connect(int fd, const struct sockaddr* addr, int addrlen) { + int on = 1; - addr.sdn_objnamel = sprintf((char *)addr.sdn_objname, "X$X%d", port); - if(addr.sdn_objnamel < 0) - return -1; - addr.sdn_objnum = 0; + if(fd < 0) + return -1; - fd = _xcb_socket(PF_DECnet, SOCK_STREAM, 0); - if(fd == -1) - return -1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); - memset(&accessdata, 0, sizeof(accessdata)); - accessdata.acc_accl = sprintf((char*)accessdata.acc_acc, "%d", getuid()); - if(accessdata.acc_accl < 0) - return -1; - setsockopt(fd, DNPROTO_NSP, SO_CONACCESS, &accessdata, sizeof(accessdata)); - - if(connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { - close(fd); - return -1; - } - return fd; + return connect(fd, addr, addrlen); } -#endif static int _xcb_open_tcp(const char *host, char *protocol, const unsigned short port) { int fd = -1; +#if HAVE_GETADDRINFO struct addrinfo hints; char service[6]; /* "65535" with the trailing '\0' */ struct addrinfo *results, *addr; char *bracket; +#endif if (protocol && strcmp("tcp",protocol) && strcmp("inet",protocol) #ifdef AF_INET6 @@ -296,10 +296,8 @@ static int _xcb_open_tcp(const char *host, char *protocol, const unsigned short if (*host == '\0') host = "localhost"; +#if HAVE_GETADDRINFO memset(&hints, 0, sizeof(hints)); -#ifdef AI_ADDRCONFIG - hints.ai_flags |= AI_ADDRCONFIG; -#endif #ifdef AI_NUMERICSERV hints.ai_flags |= AI_NUMERICSERV; #endif @@ -325,21 +323,45 @@ static int _xcb_open_tcp(const char *host, char *protocol, const unsigned short for(addr = results; addr; addr = addr->ai_next) { fd = _xcb_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); - if(fd >= 0) { - int on = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); + if (_xcb_do_connect(fd, addr->ai_addr, addr->ai_addrlen) >= 0) + break; + close(fd); + fd = -1; + } + freeaddrinfo(results); + return fd; +#else + { + struct hostent* _h; + struct sockaddr_in _s; + struct in_addr ** _c; + + if((_h = gethostbyname(host)) == NULL) + return -1; + + _c = (struct in_addr**)_h->h_addr_list; + fd = -1; - if (connect(fd, addr->ai_addr, addr->ai_addrlen) >= 0) + while(*_c) { + _s.sin_family = AF_INET; + _s.sin_port = htons(port); + _s.sin_addr = *(*_c); + + fd = _xcb_socket(_s.sin_family, SOCK_STREAM, 0); + if(_xcb_do_connect(fd, (struct sockaddr*)&_s, sizeof(_s)) >= 0) break; + close(fd); fd = -1; + ++_c; } + + return fd; } - freeaddrinfo(results); - return fd; +#endif } +#ifndef _WIN32 static int _xcb_open_unix(char *protocol, const char *file) { int fd; @@ -362,6 +384,7 @@ static int _xcb_open_unix(char *protocol, const char *file) } return fd; } +#endif /* !_WIN32 */ #ifdef HAVE_ABSTRACT_SOCKETS static int _xcb_open_abstract(char *protocol, const char *file, size_t filelen) @@ -406,13 +429,24 @@ xcb_connection_t *xcb_connect_to_display_with_auth_info(const char *displayname, int parsed = _xcb_parse_display(displayname, &host, &protocol, &display, screenp); if(!parsed) { - c = (xcb_connection_t *) &error_connection; + c = _xcb_conn_ret_error(XCB_CONN_CLOSED_PARSE_ERR); goto out; - } else + } else { +#ifdef _WIN32 + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + c = _xcb_conn_ret_error(XCB_CONN_ERROR); + goto out; + } +#endif fd = _xcb_open(host, protocol, display); + } if(fd == -1) { - c = (xcb_connection_t *) &error_connection; + c = _xcb_conn_ret_error(XCB_CONN_ERROR); +#ifdef _WIN32 + WSACleanup(); +#endif goto out; } diff --git a/src/xcb_windefs.h b/src/xcb_windefs.h new file mode 100644 index 0000000..d6c7329 --- /dev/null +++ b/src/xcb_windefs.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2009 Jatin Golani. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the names of the authors or their + * institutions shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization from the authors. + */ + + +#ifndef _XCB_WINDEFS_H +#define _XCB_WINDEFS_H + +#ifndef WINVER +#define WINVER 0x0501 /* required for getaddrinfo/freeaddrinfo defined only for WinXP and above */ +#endif + +#include +#include +#include + +struct iovec { + void *iov_base; /* Pointer to data. */ + int iov_len; /* Length of data. */ +}; + +typedef unsigned int in_addr_t; + +#endif /* xcb_windefs.h */ diff --git a/src/xcbext.h b/src/xcbext.h index 2e10ba2..98b3c93 100644 --- a/src/xcbext.h +++ b/src/xcbext.h @@ -65,15 +65,22 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect * request XCB sent. The caller of xcb_take_socket must supply a * callback which XCB can call when it wants the write side of the * socket back to make a request. This callback synchronizes with the - * external socket owner, flushes any output queues if appropriate, and - * then returns the sequence number of the last request sent over the - * socket. */ + * external socket owner and flushes any output queues if appropriate. + * If you are sending requests which won't cause a reply, please note the + * comment for xcb_writev which explains some sequence number wrap issues. + * */ int xcb_take_socket(xcb_connection_t *c, void (*return_socket)(void *closure), void *closure, int flags, uint64_t *sent); /* You must own the write-side of the socket (you've called * xcb_take_socket, and haven't returned from return_socket yet) to call * xcb_writev. Also, the iovec must have at least 1 byte of data in it. - * */ + * You have to make sure that xcb can detect sequence number wraps correctly. + * This means that the first request you send after xcb_take_socket must cause a + * reply (e.g. just insert a GetInputFocus request). After every (1 << 16) - 1 + * requests without a reply, you have to insert a request which will cause a + * reply. You can again use GetInputFocus for this. You do not have to wait for + * any of the GetInputFocus replies, but can instead handle them via + * xcb_discard_reply(). */ int xcb_writev(xcb_connection_t *c, struct iovec *vector, int count, uint64_t requests); @@ -86,6 +93,7 @@ int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, /* xcb_util.c */ int xcb_popcount(uint32_t mask); +int xcb_sumof(uint8_t *list, int len); #ifdef __cplusplus } diff --git a/src/xcbint.h b/src/xcbint.h old mode 100755 new mode 100644 index f07add8..f9e5a52 --- a/src/xcbint.h +++ b/src/xcbint.h @@ -54,7 +54,6 @@ enum lazy_reply_tag #define XCB_PAD(i) (-(i) & 3) #define XCB_SEQUENCE_COMPARE(a,op,b) ((int64_t) ((a) - (b)) op 0) -#define XCB_SEQUENCE_COMPARE_32(a,op,b) (((int) (a) - (int) (b)) op 0) #ifndef offsetof #define offsetof(type,member) ((size_t) &((type *)0)->member) @@ -107,6 +106,7 @@ int _xcb_out_init(_xcb_out *out); void _xcb_out_destroy(_xcb_out *out); int _xcb_out_send(xcb_connection_t *c, struct iovec *vector, int count); +void _xcb_out_send_sync(xcb_connection_t *c); int _xcb_out_flush_to(xcb_connection_t *c, uint64_t request); @@ -191,7 +191,10 @@ struct xcb_connection_t { _xcb_xid xid; }; -void _xcb_conn_shutdown(xcb_connection_t *c); +void _xcb_conn_shutdown(xcb_connection_t *c, int err); + +xcb_connection_t *_xcb_conn_ret_error(int err); + int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vector, int *count); diff --git a/xcb-xkb.pc.in b/xcb-xkb.pc.in new file mode 100644 index 0000000..6ed0035 --- /dev/null +++ b/xcb-xkb.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: XCB XKB +Description: XCB Keyboard Extension (EXPERIMENTAL) +Version: @PACKAGE_VERSION@ +Requires: xcb +Libs: -L${libdir} -lxcb-xkb +Cflags: -I${includedir} -- 2.7.4