pax_global_header00006660000000000000000000000064136166012240014513gustar00rootroot0000000000000052 comment=52c421c650f8813665b31890df691b31fabc366a xserver-xorg-video-qxl-0.1.5+git20200331/000077500000000000000000000000001361660122400174715ustar00rootroot00000000000000xserver-xorg-video-qxl-0.1.5+git20200331/.gitignore000066400000000000000000000017451361660122400214700ustar00rootroot00000000000000# # X.Org module default exclusion patterns # The next section if for module specific patterns # # Do not edit the following section # GNU Build System (Autotools) aclocal.m4 autom4te.cache/ autoscan.log ChangeLog compile config.guess config.h config.h.in config.log config-ml.in config.py config.status config.status.lineno config.sub configure configure.scan depcomp .deps/ INSTALL install-sh .libs/ libtool libtool.m4 ltmain.sh lt~obsolete.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 Makefile Makefile.in mdate-sh missing mkinstalldirs *.pc py-compile stamp-h? symlink-tree texinfo.tex ylwrap # Do not edit the following section # Edit Compile Debug Document Distribute *~ *.[0-9] *.[0-9]x *.bak *.bin core *.dll *.exe *-ISO*.bdf *-JIS*.bdf *-KOI8*.bdf *.kld *.ko *.ko.cmd *.lai *.l[oa] *.[oa] *.obj *.patch *.so *.pcf.gz *.pdb *.tar.bz2 *.tar.gz # # Add & Override patterns for macros # # Edit the following section as needed # For example, !report.pc overrides *.pc. See 'man gitignore' # xserver-xorg-video-qxl-0.1.5+git20200331/COPYING000066400000000000000000000021121361660122400205200ustar00rootroot00000000000000Copyright 2008 Red Hat, Inc. Copyright 2009 Red Hat, Inc. 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 on the rights to use, copy, modify, merge, publish, distribute, sub license, 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 (including the next paragraph) 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 NON-INFRINGEMENT. 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. xserver-xorg-video-qxl-0.1.5+git20200331/Makefile.am000066400000000000000000000025621361660122400215320ustar00rootroot00000000000000# Copyright 2008 Red Hat, Inc. # # 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 # on the rights to use, copy, modify, merge, publish, distribute, sub # license, 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 (including the next # paragraph) 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 NON-INFRINGEMENT. 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. SUBDIRS = src scripts examples MAINTAINERCLEANFILES = ChangeLog INSTALL .PHONY: ChangeLog INSTALL INSTALL: $(INSTALL_CMD) ChangeLog: $(CHANGELOG_CMD) dist-hook: ChangeLog INSTALL EXTRA_DIST = \ README.md \ README.xspice DISTCHECK_CONFIGURE_FLAGS=--enable-xspice xserver-xorg-video-qxl-0.1.5+git20200331/NEWS000066400000000000000000000027541361660122400202000ustar00rootroot00000000000000Major changes in 0.1.5 ====================== Fix X restarts when kms is in use with multiple monitors Various Xspice fixes and improvements (agent fixes, command line handling, documentation, delayed fps, audio playback) Fix Xspice crashing after 11 days Add --numheads argument to Xspice script Allow to setup video codec for streaming when using Xspice (requires spice-server 0.13.2) Add support for X.Org 1.19 Fix compilation on arches which do not have outb (aarch64) Major changes in 0.1.4 ====================== Added smartcard support to XSpice Memleak fix which could cause X to run out of memory Expose randr properties for suggested display positions if kernel supports it Major changes in 0.1.3 ====================== Xspice performance/stability improvements Bug/crash fixes in the drm layer Major changes in 0.1.2 ====================== Support for server managed fds Xspice: use playback rate interface if available Bugs fixed: 1030531 - drm: restore cursor after resolution change fix cursor2 support in older kernels FD 79317 - prevent crash on dual head systems Major changes in 0.1.1 ====================== KMS support DFPS available to guest driver too Xspice audio & agent support GPL code removed (EDID) Warnings squash Bug fixes (numbers are Red Hat Bugzilla bug ids): 883578 - remote-viewer gets frozen after migration of guest with video playback 968931 - Crash in Xspice after closing tab with spice-html5 894421 - Small change in guests resolution results in wrong resolution xserver-xorg-video-qxl-0.1.5+git20200331/README.md000066400000000000000000000010231361660122400207440ustar00rootroot00000000000000The QXL virtual GPU is found in the Red Hat Enterprise Virtualisation system, and also in the spice project. All questions regarding this software should be directed at the Xorg mailing list: https://lists.x.org/mailman/listinfo/xorg The master development code repository can be found at: https://gitlab.freedesktop.org/xorg/driver/xf86-video-qxl Please submit bug reports and requests to merge patches there. For patch submission instructions, see: https://www.x.org/wiki/Development/Documentation/SubmittingPatches xserver-xorg-video-qxl-0.1.5+git20200331/README.xspice000066400000000000000000000134331361660122400216470ustar00rootroot000000000000001. Introduction 2. Building 2.1 Building from source on fedora 2.2 Building from source with your own Xserver 3. Running = 1. Introduction = Xspice is an X server and Spice server in one. It consists of a wrapper script for executing Xorg with the right parameters and environment variables, a module names spiceqxl_drv.so implementing three drivers: a video mostly code identical to the guest qxl X driver, and keyboard and mouse reading from the spice inputs channel. Xspice allows regular X connections, while a spice client provides the keyboard and mouse and video output. Spice client disconnections don't impact X client connections. Xserver's select loop is reused to service spice client sockets and the qxl driver is reused together with some of the qemu qxl device code The following changes have been done to the qxl driver. * it creates only one memslot, covering the whole of memory (much like spice does in simple display mode, i.e. vga, and the tester does) * it invokes the whole of the qxl device from qemu, patching in both directions. * io becomes a function call instead of iob * irq becomes a function call instead of setting a flag * it runs spice server directly * it is linked with spice-server. The protocol is unchanged. = 2. Building = == 2.1 Building from source on fedora == The changes for ubuntu/debian should be minimal: * location of drivers for Xorg (just where you put any qxl_drv.so etc.) * location of Xorg config files In fedora they are: (note the lib64 - replace with lib if running on 32 bit fedora) DRV_DIR=/usr/lib64/xorg/modules/drivers XORG_CONF_DIR=/etc/X11 git clone git://anongit.freedesktop.org/xorg/driver/xf86-video-qxl xspice sudo yum install spice-server-devel spice-protocol cd xspice autoreconf -i && ./configure --enable-xspice && make sudo cp src/.libs/spiceqxl_drv.so $DRV_DIR sudo cp spiceqxl.xorg $XORG_CONF_DIR Note: spiceqxl.org is copied to $XORG_CONF_DIR because Xorg only looks in a very particular config file path, and "." is not there (nor are absolute file names allowed unless Xorg is run as root). == 2.2 Building from source with your own Xserver == Building the whole xserver is lengthier but can be done without any root permissions. This assumes you already have spice-protocol and spice-server installed into $TEST prefix below. TEST=/store/test ( or TEST=$(pwd)/testxspice ) mkdir -p $TEST/src cd $TEST/src # grab xserver, xspice, xextproto, xkbcomp and more for src in \ git://anongit.freedesktop.org/xorg/proto/xextproto \ git://anongit.freedesktop.org/xorg/proto/x11proto \ git://anongit.freedesktop.org/xorg/proto/fontsproto \ git://anongit.freedesktop.org/xorg/app/xkbcomp \ git://anongit.freedesktop.org/xorg/xserver \ git://anongit.freedesktop.org/xorg/lib/libxtrans \ git://anongit.freedesktop.org/xorg/lib/libxkbfile \ git://anongit.freedesktop.org/xkeyboard-config \ git://git.freedesktop.org/git/spice/spice-protocol \ git://git.freedesktop.org/git/spice/spice ; do git clone $src; done git clone git://anongit.freedesktop.org/xorg/driver/xf86-video-qxl xspice build and install into some non common prefix (not to overwrite your existing server) - note that this is just for testing. This should all work with the default server as well, but that server requires root generally and this is undesirable for testing (and running actually). export PKG_CONFIG_PATH=${TEST}/lib/pkgconfig:${TEST}/share/pkgconfig export MAKEFLAGS="-j4" #first build spice (or sudo yum install spice-devel or spice-server-devel) sudo yum-builddep spice (or spice-server) (cd spice-protocol; ./autogen.sh --prefix=$TEST && make install) (cd spice; ./autogen.sh --prefix=$TEST && make install) # now build xserver sudo yum install xorg-x11-server-devel libxcb-util-devel xcb-util-keysyms-devel libXfont-devel (cd xextproto; ./autogen.sh --prefix=$TEST --without-xmlto && make install) (cd x11proto; ./autogen.sh --prefix=$TEST && make install) (cd fontsproto; ./autogen.sh --prefix=$TEST && make install) (cd libxtrans; ./autogen.sh --prefix=$TEST && make install) (cd libxkbfile; ./autogen.sh --prefix=$TEST && make install) (cd xkbcomp; ./autogen.sh --prefix=$TEST && make install) (cd xkeyboard-config; ./autogen.sh --prefix=$TEST && make install) # make sure DBUS_TYPE_UNIX_FD is defined (failed on my RHEL6 machine) ADDSTR="#ifndef DBUS_TYPE_UNIX_FD\n" ADDSTR+="#define DBUS_TYPE_UNIX_FD ((int) 'h')\n" ADDSTR+="#endif" sed -i "/define DBUS_TIMEOUT/ a$ADDSTR" \ xserver/hw/xfree86/os-support/linux/systemd-logind.c # need ACLOCAL for libxtrans export ACLOCAL="aclocal -I $TEST/share/aclocal" (cd xserver; ./autogen.sh --prefix=$TEST && make install) # and now xspice (cd xspice; ./autogen.sh --prefix=$TEST --enable-xspice && make install) mkdir -p $TEST/etc/X11 cp xspice/examples/spiceqxl.xorg.conf.example $TEST/etc/X11/spiceqxl.xorg.conf # Possibly also build spice-vdagent: sudo yum install libXrandr-devel libXinerama-devel git clone git://anongit.freedesktop.org/spice/linux/vd_agent spice-vdagent (cd spice-vdagent ; ./autogen.sh --prefix=$TEST && make install) = 3. Running = $XORG is either your own built $TEST/bin/Xorg or just the default Xorg If you built your own Xorg server: export PATH="$TEST/bin:$TEST/sbin:$PATH" export LD_LIBRARY_PATH=${TEST}/lib export XORG=$TEST/bin/Xorg # or /usr/bin/Xorg == 3.1 Run Xorg directly == Run server with: export XSPICE_PORT=5900 $XORG -noreset -config spiceqxl.xorg.conf :3.0 == 3.2 Run using the Xspice script == Or equivalently: xspice/scripts/Xspice --port 5900 --disable-ticketing --xorg $XORG :3.0 [--vdagent] (and many more options available -- see scripts/Xspice) Run X clients as usual by setting DISPLAY=:3.0, for example # DISPLAY=:3.0 firefox & or # DISPLAY=:3.0 twm & DISPLAY=:3.0 xterm & Run spice client: sudo yum install virt-viewer remote-viewer spice://localhost:5900 xserver-xorg-video-qxl-0.1.5+git20200331/TODO000066400000000000000000000146221361660122400201660ustar00rootroot00000000000000- Acceleration - Blits and solid fill - XAA and the shadow buffer will not work together, because the shadow buffer updates in the block handler, so if we got any XAA calls in between, things would get messed up. Current plan: - Add our own damage tracker that produces raw rectangles - Whenever it fires, submit the copy immediately - Wrap the necessary ops in such a way that the original implementation gets called first. The original implementation will use fb, which will produce damage, which will get submitted. If we decide to accelerate a particular operation, first set a flag that the immediately following damage event should not result in spice protocol being sent. Ie., on_op: qxl->enable_copying = FALSE call original; send acceleration command qxl->enable_copying = TRUE Note damage is added before the drawing hits the framebuffer, so it will have to be stored, then cleared - in a block handler - before accelerating Ie., on_op: clear damage disable damage reporting call original (this will generate unreported damage and paint to the shadow) submit command enable damage It may be possible to use the shadow code if we added a shadowReportNow() that would report any existing damage. Ie., basically export shadowRedisplay() 1. Get damage added, out of CreateScreenResources 2. Make sure it works 3. Submit copies and disable shadow 4. Delete shadow 5. Wrap some of the ops, or use XAA? The input we get is: - First a damage notification: "I am going to draw here" - Then maybe an exa notification So the algorithm is. Maintain a "to_copy" region to be copied into the device - in damage, if there is anything in to_copy, copy it - in block handler, if there is anything in to_copy, copy it - in exa, if we manage to accelerate, delete to_copy. Unfortunately, for core text, what happens is - damage is produced for the glyph box - solid fill is generated - the glyph is drawn And the algorithm above means the damage is thrown away. - Coding style fixes - Better malloc() implementation - Take malloc() from the windows driver? - Put blocks in a tree? - Find out why it picks 8x6 rather than a reasonable mode - Possibly has to do with the timings it reports. RandR only allows 8x6 and 6x4. - Only compile mmtest if glib is installed Or maybe just get rid of mmtest.c - Notes on offscreen pixmaps Yaniv says that PCI resources is a concern and that it would be better if we can use guest memory instead of video memory. I guess we can do that, given a kernel driver that can allocate pinned memory. - If/when we add hardware acceleration to pixman, pixman will need to generate QXL protocol. This could be tricky because DRM assumes that everything is a pixmap, but qxl explicitly has a framebuffer. Same goes for cairo-drm. - Hashing QXL has a feature where it can send hash codes for pixmaps. Unfortunately most of the pixmaps we use are very shortlived. But there may be a benefit for the root pixmap (and in general for the (few) windows that have a pixmap background). - When copying from pixmap to framebuffer, right now we just copy the bits from the fb allocated pixmap. - With hashing, we need to copy it to video memory, hash it, then set the "unique" field to that hash value (plus the QXL_CACHE flag). Presumably we'll get a normal remove on it when it is no longer in use. - If we know an image is available in video memory already, we should just submit it. There is no race condition here because the image is ultimately removed from vmem by the driver. (Note hash value could probably just be XID plus a serial number). - So for the proof of concept we'll be hashing complete pixmaps every time we submit them. - Tiles It may be beneficial to send pixmaps in smaller tiles, though Yaniv says we will need atomic drawing to prevent tearing. - Video We should certainly support Xv. The scaled blits should be sent as commands, rather than as software. Does spice support YUV images? If not, then it probably should. - Multi-monitor: - Windows may not support more than dual-head, but we do support more than dual-head in spice. This is why they do the multi-pci device. Ie,. the claim is that Yaniv did not find any API that would support more than two outputs per PCI device. (This seems dubious given that four-head cards do exist). - Linux multi-monitor configuration supports hotplug of monitors, and you can't make up PCI devices from inside the driver. - On windows the guest agent is responsible for setting the monitors and resolutions. - On linux we should support EDID information, and enabling and disabling PCI devices on the fly is pretty difficult to deal with in X. Ie., we would need working support for both GPU hotplug and for shatter. This is just not happening in RHEL 5 or 6. - Reading back EDID over the spice protocol would be necessary because when you hit detect displays, that's what needs to happen. Better acceleration: - Given offscreen pixmaps, we should get rid of the shadow framebuffer. If we have to fall back to software, we can use the drawing area to get the area in question, then copy them to qxl_malloced memory, then draw there, then finally send the bits. -=-=-=-=- Done: Question: - Submit cursor images - Note: when we set a mode, all allocated memory should be considered released. - What is the "vram" PCI range used for? As I read the Windows driver, it can be mapped with the ioctl VIDEO_MAP_VIDEO_MEMORY. In driver.c it is mapped as pdev->fb, but it is then never used for anything as far as I can tell. Does Windows itself use that ioctl, and if so, for what. The area is only 32K in size so it can't really be used for any realistic bitmaps. It's a required ioctl. I believe it's needed for DGA-like things. I have no idea how the Windows driver manages syncing for that, but I think we can safely ignore it. [ajax] - Hook up randr if it isn't already - Garbage collection - Before every allocation? - When we run out of memory? - Whenever we overflow some fixed pool? - Get rid of qxl_mem.h header; just use qxl.h - Split out ring code into qxl_ring.c - Don't keep the maps around that are just used in preinit (Is there any real reason to not just do the CheckDevice in ScreenInit?) xserver-xorg-video-qxl-0.1.5+git20200331/TODO.xspice000066400000000000000000000013271361660122400214560ustar00rootroot00000000000000TODO: OOM. Not releasing? Out of memory allocating 3145748 bytes Out of mem - stats Cursor channel missing. No copy paste. No agent at all. agent should probably be thought to run with Xspice too. That way mouse emulation in Xspice can be avoided entirely. But how do you get an input device to belong to Xspice :1.0 and not to Xorg :0.0? Keyboard: * repeat (xset r 100) does nothing. This is because spice client of course does repeat. But would be nice to transmit this. Video: youtube seemed choppy, some frames were repeating (cpu 30%~, so not cpu bound). Possible multimedia time? Isn't this set by the driver, i.e. should be identical with in vm qxl driver? Performance: didn't check. xserver-xorg-video-qxl-0.1.5+git20200331/autogen.sh000077500000000000000000000003031361660122400214660ustar00rootroot00000000000000#! /bin/sh srcdir=`dirname $0` test -z "$srcdir" && srcdir=. ORIGDIR=`pwd` cd $srcdir autoreconf -v --install || exit 1 cd $ORIGDIR || exit $? $srcdir/configure --enable-maintainer-mode "$@" xserver-xorg-video-qxl-0.1.5+git20200331/configure.ac000066400000000000000000000163431361660122400217660ustar00rootroot00000000000000# Copyright 2008 Red Hat, Inc. # # 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 # on the rights to use, copy, modify, merge, publish, distribute, sub # license, 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 (including the next # paragraph) 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 NON-INFRINGEMENT. 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. # # Process this file with autoconf to produce a configure script # Initialize Autoconf AC_PREREQ([2.60]) AC_INIT([xf86-video-qxl], [0.1.5], [https://gitlab.freedesktop.org/xorg/driver/xf86-video-qxl/issues], [xf86-video-qxl]) AC_CONFIG_SRCDIR([Makefile.am]) AC_CANONICAL_HOST # Initialize Automake AM_INIT_AUTOMAKE([foreign dist-bzip2]) AM_MAINTAINER_MODE AC_CONFIG_HEADERS([config.h]) AC_SYS_LARGEFILE # Require xorg-macros: XORG_DEFAULT_OPTIONS m4_ifndef([XORG_MACROS_VERSION], [m4_fatal([you must install X.Org util-macros 1.4 or later (or the corresponding package from your distribution) before running autoconf/autogen])]) XORG_MACROS_VERSION(1.4) XORG_DEFAULT_OPTIONS # Initialize libtool AC_DISABLE_STATIC AC_PROG_INSTALL AC_PROG_LIBTOOL AH_TOP([#include "xorg-server.h"]) # Define a configure option for an alternate module directory AC_ARG_WITH(xorg-module-dir, [ --with-xorg-module-dir=DIR ], [ moduledir="$withval" ], [ moduledir="$libdir/xorg/modules" ]) AC_SUBST(moduledir) # Store the list of server defined optional extensions in REQUIRED_MODULES XORG_DRIVER_CHECK_EXT(RANDR, randrproto) XORG_DRIVER_CHECK_EXT(RENDER, renderproto) XORG_DRIVER_CHECK_EXT(XV, videoproto) XORG_DRIVER_CHECK_EXT(XFreeXDGA, xf86dgaproto) # We can use either version 1 or version 2 of libXfont PKG_CHECK_EXISTS(xfont2, [xfont_pc=xfont2 AC_DEFINE(HAVE_XFONT2,1,[Version 2 of the libXfont library])], [xfont_pc=xfont]) # Obtain compiler/linker options for the driver dependencies PKG_CHECK_MODULES(XORG, [xorg-server >= 1.0.99.901] xproto fontsproto $xfont_pc $REQUIRED_MODULES) save_CFLAGS="$CFLAGS" CFLAGS="$XORG_CFLAGS" AC_CHECK_DECL(XSERVER_LIBPCIACCESS, [XSERVER_LIBPCIACCESS=yes], [XSERVER_LIBPCIACCESS=no], [#include "xorg-server.h"]) CFLAGS="$save_CFLAGS" save_CFLAGS="$CFLAGS" CFLAGS="$XORG_CFLAGS" AC_CHECK_HEADER(qxl_drm.h,[QXL_DRM=yes],[QXL_DRM=no],[#include ]) CFLAGS="$save_CFLAGS" AC_ARG_ENABLE(kms, AS_HELP_STRING([--disable-kms], [Disable KMS support [[default=enabled]]]), [DRM_MODE="$enableval"], [DRM_MODE=yes]) # Checks for libraries. if test "x$QXL_DRM" = xyes && test "x$DRM_MODE" = xyes; then # require support for kms cursor hotspot PKG_CHECK_MODULES(DRM, [libdrm >= 2.4.46]) else DRM_MODE=no fi if test "x$XSERVER_LIBPCIACCESS" = xyes; then PKG_CHECK_MODULES([PCIACCESS], [pciaccess >= 0.10]) fi AM_CONDITIONAL(XSERVER_LIBPCIACCESS, test "x$XSERVER_LIBPCIACCESS" = xyes) save_CFLAGS="$CFLAGS" CFLAGS="$DRM_CFLAGS $CFLAGS" if test "x$DRM_MODE" = xyes; then AC_CHECK_HEADER(xf86drmMode.h,[DRM_MODE=yes],[DRM_MODE=no],[#include #include ]) if test "x$DRM_MODE" = xyes; then AC_DEFINE(XF86DRM_MODE,1,[DRM kernel modesetting]) else DRM_MODE=no fi fi CFLAGS="$save_CFLAGS" AM_CONDITIONAL(DRM_MODE, test x$DRM_MODE = xyes) enable_qxl=yes AC_ARG_ENABLE(xspice, [ --enable-xspice[[=(no|yes|only)]] Build the xspice driver no (default) Do not build yes Build in addition to qxl only Only build xspice], [ if test "x$enableval" = "xyes"; then enable_xspice=yes elif test "x$enableval" = "xonly"; then enable_xspice=yes enable_qxl=no else enable_xspice=no fi ]) if test "x$enable_xspice" = "xyes"; then AC_CHECK_HEADERS(sys/inotify.h) AC_CHECK_FUNCS(inotify_init1) PKG_CHECK_MODULES([SPICE], [spice-server >= 0.6.3], [ AC_SUBST(SPICE_CFLAGS) AC_SUBST(SPICE_LIBS) ], ) else enable_xspice=no fi AC_ARG_ENABLE([ccid], [AS_HELP_STRING([--enable-ccid], [Build the spiceccid SmartCard driver (default is no)])], [enable_ccid=$enableval], [enable_ccid=no]) AC_ARG_WITH(ccid-module-dir, [AS_HELP_STRING([--with-ccid-module-dir=DIR ], [Specify the install path for spiceccid driver (default is $libdir/pcsc/drivers/serial)])], [ cciddir="$withval" ], [ cciddir="$libdir/pcsc/drivers/serial" ]) AC_SUBST(cciddir) if test "x$enable_ccid" != "xno"; then PKG_CHECK_MODULES(LIBPCSCLITE, [libpcsclite]) PKG_CHECK_MODULES(LIBCACARD, [libcacard]) if test "x$enable_xspice" = "xno"; then AC_MSG_ERROR([Building with ccid requires xspice, but xspice is not enabled]) fi fi AM_CONDITIONAL(BUILD_XSPICE, test "x$enable_xspice" = "xyes") AM_CONDITIONAL(BUILD_QXL, test "x$enable_qxl" = "xyes") AM_CONDITIONAL(BUILD_SPICECCID, test "x$enable_ccid" = "xyes") AC_ARG_ENABLE([udev], AS_HELP_STRING([--disable-udev], [Disable libudev support [default=auto]]), [enable_udev="$enableval"], [enable_udev=auto]) if test "x$enable_udev" != "xno"; then PKG_CHECK_MODULES(LIBUDEV, [libudev], [LIBUDEV=yes], [LIBUDEV=no]) if test "x$LIBUDEV" = xyes; then AC_DEFINE(HAVE_LIBUDEV, 1,[libudev support]) elif test "x$enable_udev" != "xauto"; then AC_MSG_ERROR([Building with udev requested but libudev not found]) fi fi AM_CONDITIONAL(LIBUDEV, test x$LIBUDEV = xyes) PKG_CHECK_MODULES([SPICE_PROTOCOL], [spice-protocol >= 0.12.0]) # AC_CHECK_FILE is not supported when cross compiling if test "$cross_compiling" = "no" ; then AC_CHECK_FILE(.git, [ GIT_VERSION=`git log -1 --format=%h` AC_DEFINE_UNQUOTED([GIT_VERSION], ["$GIT_VERSION"], [Defined if building from git]) ] ) fi AC_CONFIG_FILES([ Makefile src/Makefile src/spiceccid/Makefile src/uxa/Makefile scripts/Makefile examples/Makefile ]) AC_OUTPUT dnl ========================================================================== echo " xf86-video-qxl $VERSION ===================== prefix: ${prefix} c compiler: ${CC} drm: ${DRM_CFLAGS} KMS: ${DRM_MODE} Build qxl: ${enable_qxl} Build xspice: ${enable_xspice} Build spiceccid: ${enable_ccid} " xserver-xorg-video-qxl-0.1.5+git20200331/examples/000077500000000000000000000000001361660122400213075ustar00rootroot00000000000000xserver-xorg-video-qxl-0.1.5+git20200331/examples/Makefile.am000066400000000000000000000023051361660122400233430ustar00rootroot00000000000000# Copyright 2011 Red Hat, Inc. # # 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 # on the rights to use, copy, modify, merge, publish, distribute, sub # license, 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 (including the next # paragraph) 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 NON-INFRINGEMENT. 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. if BUILD_XSPICE doc_DATA = spiceqxl.xorg.conf.example endif EXTRA_DIST = spiceqxl.xorg.conf.example xserver-xorg-video-qxl-0.1.5+git20200331/examples/spiceqxl.xorg.conf.example000066400000000000000000000163721361660122400264270ustar00rootroot00000000000000Section "Device" Identifier "XSPICE" Driver "spiceqxl" # ---- Network and security options # Do not require a client password. # default: False #Option "SpiceDisableTicketing" "False" # Set the password required to connect to the Spice server. #Option "SpicePassword" "" # Enable usage of SASL supported by the spice-gtk client. Not required, # default: False #Option "SpiceSasl" "False" # Use Spice's regular unencrypted port. One of SpicePort or SpiceTlsPort # must be specified. SpicePort can also be set through the XSPICE_PORT # environment variable or the --port Xspice option. # Specify 0 to disable the use of the regular port # default: 5900 #Option "SpicePort" "5900" # Use a TLS (encrypted) port. One of SpicePort or SpiceTlsPort must be # specified. SpiceTlsPort can also be set through the XSPICE_TLS_PORT # environment variable or the --tls-port Xspice option. #Option "SpiceTlsPort" "5900" # Set the directory where the CA certificate, server key and server # certificate are searched for TLS, using the same predefined names QEMU # uses: # cacert.pem, server-key.pem, server-cert.pem #Option "SpiceX509Dir" "" # Set the CA certificate file location for TLS. #Option "SpiceCacertFile" "" # Set the server key file location for TLS. #Option "SpiceX509KeyFile" "" # Set the server key's password for TLS. #Option "SpiceX509KeyPassword" "" # Set the server certificate file location for TLS. #Option "SpiceX509CertFile" "" # Set the server DH file location for TLS. #Option "SpiceDhFile" "" # Set the TLS ciphers preference order. #Option "SpiceTlsCiphers" "" # Listen to a specific interface. # default: Listen to all (0.0.0.0). #Option "SpiceAddr" "" # Set to True to only listen on IPv4 interfaces. # default: False #Option "SpiceIPV4Only" "False" # Set to True to only listen on IPv6 interfaces. # default: False #Option "SpiceIPV6Only" "False" # If set, the Spice server will exit when the first client disconnects. # default: False #Option "SpiceExitOnDisconnect" "True" # ---- Monitor configuration options # The number of heads to allocate by default. # default: 4 #Option "NumHeads" "4" # ---- Compression options # Set zlib glz wan compression. Options are auto, never, always. # default: auto #Option "SpiceZlibGlzWanCompression" "" # Set jpeg wan compression. Options are auto, never, always. # default: auto #Option "SpiceJpegWanCompression" "" # Set image compression. Options are off, auto_glz, auto_lz, quic, glz, lz. # default: auto_glz #Option "SpiceImageCompression" "" # If non zero, the driver will render all operations to the frame buffer, # and keep track of a changed rectangle list. The changed rectangles # will be transmitted at the rate requested (e.g. 10 frames per second). # This can dramatically reduce network bandwidth for some use cases. #Option "SpiceDeferredFPS" "10" # Set the streaming video method. Options are filter, off, all. # default: filter #Option "SpiceStreamingVideo" "" # Sets a semicolon-separated list of preferred video codecs. # Each takes the form encoder:codec, with spice:mjpeg being the default, # and other options being provided by gstreamer for the mjpeg, vp8 and h264 # codecs. #Option "SpiceVideoCodecs" "" # Enable caching of images directly written with uxa->put_image. # default: True #Option "EnableImageCache" "True" # Enable caching of images created by uxa->prepare_access. # default: True #Option "EnableFallbackCache" "True" # Enable the use of off-screen surfaces. # default: True #Option "EnableSurfaces" "True" # ---- Xspice-specific buffer options # Buffer Size notes: # The following buffer sizes are used for Xspice only. # If you are using the DFPS mode, surface ram is not used, # and you can set it to 1. # Otherwise, the surface buffer should be at least as large # as the frame buffer, and probably a multiple like 8. # The command buffer ram should also be substantially larger # than the frame buffer, and note that the frame buffer occupies # the front of the command buffer. Hence, our default size # is a command buffer 7x the size of the frame buffer. # If you see 'Out of memory' errors in your xorg.log, you probably need # to increase the surface or command buffer sizes. # The amount of surface buffer ram, in megabytes, to allocate. # default: 128 #Option "SurfaceBufferSize" "128" # The amount of command buffer ram, in megabytes, to allocate. # default: 128 #Option "CommandBufferSize" "128" # The amount of frame buffer ram, in megabytes, to reserve # This is reserved out of the CommandBuffer RAM # This governs the maximum size the X screen can be; # 4 Heads at 1920x1080 require 32M of RAM # default: 16 #Option "FrameBufferSize" "16" # ---- VDAgent options # Whether or not to accept Vdagent connections. # default: False #Option "SpiceVdagentEnabled" "False" # Default path to listen for agent virtual io connections. # default: /tmp/xspice-virtio" #Option "SpiceVdagentVirtioPath" "/tmp/xspice-virtio" # Default path to listen for agent uinput connections. # default: /tmp/xspice-uinput #Option "SpiceVdagentUinputPath" "/tmp/xspice-uinput" # Default user id to set for the Virtio and Uinput sockets. # Note: both uid and gid must be given for either to take effect. # default: 0, or none #Option "SpiceVdagentUid" "0" # Default group id to set for the Virtio and Uinput sockets. # Note: both uid and gid must be given for either to take effect. # default: 0, or none #Option "SpiceVdagentGid" "0" # Set Spice Agent Mouse - Use Agent mouse if an agent connects. # default: True #Option "SpiceAgentMouse" "True" # Set Spice Playback compression. # default: True #Option "SpicePlaybackCompression" "True" # Disable copy and paste. # default: False #Option "SpiceDisableCopyPaste" "False" # If a directory is given, any file in that directory will be read # for audio data to be sent to the client. # default: Not set. #Option "SpicePlaybackFIFODir" "/tmp/" # A unix domain name for a unix domain socket to communicate with # a spiceccid smartcard driver. # default: Not set. #Option "SpiceSmartCardFile" "/tmp/spice.pcsc.comm" EndSection Section "InputDevice" Identifier "XSPICE POINTER" Driver "xspice pointer" EndSection Section "InputDevice" Identifier "XSPICE KEYBOARD" Driver "xspice keyboard" EndSection Section "Monitor" Identifier "Configured Monitor" EndSection Section "Screen" Identifier "XSPICE Screen" Monitor "Configured Monitor" Device "XSPICE" DefaultDepth 24 EndSection Section "ServerLayout" Identifier "XSPICE Example" Screen "XSPICE Screen" InputDevice "XSPICE KEYBOARD" InputDevice "XSPICE POINTER" EndSection # Prevent udev from loading vmmouse in a vm and crashing. Section "ServerFlags" Option "AutoAddDevices" "False" EndSection xserver-xorg-video-qxl-0.1.5+git20200331/scripts/000077500000000000000000000000001361660122400211605ustar00rootroot00000000000000xserver-xorg-video-qxl-0.1.5+git20200331/scripts/Makefile.am000066400000000000000000000022401361660122400232120ustar00rootroot00000000000000# Copyright 2011 Red Hat, Inc. # # 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 # on the rights to use, copy, modify, merge, publish, distribute, sub # license, 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 (including the next # paragraph) 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 NON-INFRINGEMENT. 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. if BUILD_XSPICE bin_SCRIPTS = Xspice endif EXTRA_DIST = Xspice xserver-xorg-video-qxl-0.1.5+git20200331/scripts/Xspice000077500000000000000000000333541361660122400223510ustar00rootroot00000000000000#!/usr/bin/python """ Xspice Xspice is a standard X server that is also a Spice server. It is implemented as a module with video, mouse and keyboard drivers. The video driver is mostly the same code as the qxl guest driver, hence Xspice is kept in the same repository. It can also be used to debug the qxl driver. Xspice (this executable) will set a bunch of environment variables that are used by spiceqxl_drv.so, and then spawn Xorg, giving it the default config file, which can be overridden as well. """ import argparse import os import sys import tempfile import atexit import time import signal from subprocess import Popen, PIPE def which(x): if not x: return x if os.path.exists(x): return x for p in os.environ['PATH'].split(':'): candidate = os.path.join(p, x) if os.path.exists(candidate): return candidate print('Warning: failed to find executable %s' % x) return None if 'XSPICE_ENABLE_GDB' in os.environ: cgdb = which('cgdb') if not cgdb: cgdb = which('gdb') else: cgdb = None def add_boolean(flag, *args, **kw): parser.add_argument(flag, action='store_const', const='1', *args, **kw) wan_compression_options = ['auto', 'never', 'always'] parser = argparse.ArgumentParser("Xspice", description="X and Spice server. example usage: Xspice --port 5900 --disable-ticketing :1.0", usage="Xspice [Xspice and Xorg options intermixed]", epilog="Any option not parsed by Xspice gets passed to Xorg as is.") # X-related options parser.add_argument('--xorg', default=which('Xorg'), help='specify the path to the Xorg binary') parser.add_argument('--config', default='spiceqxl.xorg.conf', help='specify the path to the Xspice configuration') parser.add_argument('--auto', action='store_true', help='automatically create a temporary xorg.conf and start the X server') parser.add_argument('--xsession', help='if given, will run after Xorg launch. Should be a program like x-session-manager') # Network and security options add_boolean('--disable-ticketing', help="do not require a client password") parser.add_argument('--password', help="set the password required to connect to the server") add_boolean('--sasl', help="use SASL to authenticate to the server") # Don't use any options that are already used by Xorg (unless we must) # specifically, don't use -p and -s. parser.add_argument('--port', type=int, help="use the specified port as Spice's regular unencrypted port") parser.add_argument('--tls-port', type=int, help='use the specified port as a TLS (encrypted) port', default=0) parser.add_argument('--x509-dir', help="set the directory where the CA certificate, server key and server certificate are searched for TLS, using the same predefined names QEMU uses") parser.add_argument('--cacert-file', help="set the CA certificate file location for TLS") parser.add_argument('--x509-key-file', help="set the server key file location for TLS") parser.add_argument('--x509-key-password', help="set the server key's password for TLS") parser.add_argument('--x509-cert-file', help="set the server certificate file location for TLS") parser.add_argument('--dh-file', help="set the server DH file location for TLS") parser.add_argument('--tls-ciphers', help="set the TLS ciphers preference order") add_boolean('--ipv4-only', help="only accept IP v4 connections") add_boolean('--ipv6-only', help="only accept IP v6 connections") parser.add_argument('--exit-on-disconnect', action='store_true', help='exit the X server when any client disconnects') # Monitor configuration options parser.add_argument('--numheads', type=int, help='number of virtual heads to create') # Compression options parser.add_argument('--jpeg-wan-compression', choices=wan_compression_options, help="set jpeg wan compression") parser.add_argument('--zlib-glz-wan-compression', choices=wan_compression_options, help="set zlib glz wan compressions") parser.add_argument('--image-compression', choices = ['off', 'auto_glz', 'auto_lz', 'quic', 'glz', 'lz'], help="set image compression") parser.add_argument('--deferred-fps', type=int, help='if non zero, the driver will render all operations to the frame buffer, and keep track of a changed rectangle list. The changed rectangles will be transmitted at the rate requested (e.g. 10 frames per second). This can dramatically reduce network bandwidth for some use cases') # TODO - sound support parser.add_argument('--streaming-video', choices=['off', 'all', 'filter'], help='set the streaming video method') parser.add_argument('--video-codecs', help='set a semicolon-separated list of preferred video codecs to use. Each takes the form encoder:codec, with spice:mjpeg being the default and other options being provided by gstreamer for the mjpeg, vp8 and h264 codecs') # VDAgent options parser.add_argument('--vdagent', action='store_true', dest='vdagent_enabled', default=False, help='launch vdagent & vdagentd. They provide clipboard & resolution automation') parser.add_argument('--vdagent-virtio-path', help='virtio socket path used by vdagentd') parser.add_argument('--vdagent-uinput-path', help='uinput socket path used by vdagent') parser.add_argument('--vdagent-udcs-path', help='Unix domain socket path used by vdagent and vdagentd') parser.add_argument('--vdagentd-exec', help='path to spice-vdagentd (used with --vdagent)') parser.add_argument('--vdagent-exec', help='path to spice-vdagent (used with --vdagent)') parser.add_argument('--vdagent-no-launch', default=True, action='store_false', dest='vdagent_launch', help='do not launch vdagent & vdagentd, used for debugging or if some external script wants to take care of that') parser.add_argument('--vdagent-uid', default=str(os.getuid()), help='set vdagent user id. changing it makes sense only in conjunction with --vdagent-no-launch') parser.add_argument('--vdagent-gid', default=str(os.getgid()), help='set vdagent group id. changing it makes sense only in conjunction with --vdagent-no-launch') parser.add_argument('--audio-fifo-dir', help="if a directory is given, any file in that directory will be read for audio data to be sent to the client. This is designed to work with PulseAudio's module-pipe-sink") #TODO #Option "SpiceAddr" "" #add_boolean('--agent-mouse') #Option "EnableImageCache" "True" #Option "EnableFallbackCache" "True" #Option "EnableSurfaces" "True" #parser.add_argument('--playback-compression', choices=['0', '1'], help='enabled by default') #Option "SpiceDisableCopyPaste" "False" if cgdb: parser.add_argument('--cgdb', action='store_true', default=False) args, xorg_args = parser.parse_known_args(sys.argv[1:]) def agents_new_enough(args): for f in [args.vdagent_exec, args.vdagentd_exec]: if not f: print('please specify path to vdagent/vdagentd executables') return False if not os.path.exists(f): print('error: file not found ', f) return False for f in [args.vdagent_exec, args.vdagentd_exec]: if Popen(args=[f, '-h'], stdout=PIPE, universal_newlines=True).stdout.read().find('-S') == -1: return False return True if args.vdagent_enabled: if not args.vdagent_exec: args.vdagent_exec = 'spice-vdagent' if not args.vdagentd_exec: args.vdagentd_exec = 'spice-vdagentd' args.vdagent_exec = which(args.vdagent_exec) args.vdagentd_exec = which(args.vdagentd_exec) if not agents_new_enough(args): if args.vdagent_enabled: print("error: vdagent is not new enough to support Xspice") raise SystemExit args.vdagent_enabled = False def tls_files(args): if args.tls_port == 0: return {} files = {} for k, var in [('ca-cert', 'cacert_file'), ('server-key', 'x509_key_file'), ('server-cert', 'x509_cert_file')]: files[k] = os.path.join(args.x509_dir, k + '.pem') if getattr(args, var): files[k] = getattr(args, var) return files # XXX spice-server aborts if it can't find the certificates - avoid by checking # ourselves. This isn't exhaustive - if the server key requires a password # and it isn't supplied spice will still abort, and Xorg with it. for key, filename in tls_files(args).items(): if not os.path.exists(filename): print("missing %s - %s does not exist" % (key, filename)) sys.exit(1) def error(msg, exit_code=1): print("Xspice: %s" % msg) sys.exit(exit_code) if not args.xorg: error("Xorg missing") cleanup_files = [] cleanup_dirs = [] cleanup_processes = [] def cleanup(*args): for f in cleanup_files: if os.path.exists(f): os.remove(f) for d in cleanup_dirs: if os.path.exists(d): os.rmdir(d) for p in cleanup_processes: try: p.kill() except OSError: pass for p in cleanup_processes: try: p.wait() except OSError: pass del cleanup_processes[:] def launch(*args, **kw): p = Popen(*args, **kw) cleanup_processes.append(p) return p signal.signal(signal.SIGTERM, cleanup) atexit.register(cleanup) if args.auto: temp_dir = tempfile.mkdtemp(prefix="Xspice-") cleanup_dirs.append(temp_dir) args.config = temp_dir + "/xorg.conf" cleanup_files.append(args.config) cf = open(args.config, "w+") logfile = temp_dir + "/xorg.log" cleanup_files.append(logfile) xorg_args = [ '-logfile', logfile ] + xorg_args if args.audio_fifo_dir: options = 'Option "SpicePlaybackFIFODir" "%s"' % args.audio_fifo_dir else: options = '' cf.write(""" Section "Device" Identifier "XSPICE" Driver "spiceqxl" %(options)s EndSection Section "InputDevice" Identifier "XSPICE POINTER" Driver "xspice pointer" EndSection Section "InputDevice" Identifier "XSPICE KEYBOARD" Driver "xspice keyboard" EndSection Section "Monitor" Identifier "Configured Monitor" EndSection Section "Screen" Identifier "XSPICE Screen" Monitor "Configured Monitor" Device "XSPICE" EndSection Section "ServerLayout" Identifier "XSPICE Example" Screen "XSPICE Screen" InputDevice "XSPICE KEYBOARD" InputDevice "XSPICE POINTER" EndSection # Prevent udev from loading vmmouse in a vm and crashing. Section "ServerFlags" Option "AutoAddDevices" "False" EndSection """ % locals()) cf.flush() if args.vdagent_enabled: for f in [args.vdagent_udcs_path, args.vdagent_virtio_path, args.vdagent_uinput_path]: if f and os.path.exists(f): os.unlink(f) if not temp_dir: temp_dir = tempfile.mkdtemp(prefix="Xspice-") cleanup_dirs.append(temp_dir) # Auto generate temporary files for vdagent if not args.vdagent_udcs_path: args.vdagent_udcs_path = temp_dir + "/vdagent.udcs" if not args.vdagent_virtio_path: args.vdagent_virtio_path = temp_dir + "/vdagent.virtio" if not args.vdagent_uinput_path: args.vdagent_uinput_path = temp_dir + "/vdagent.uinput" cleanup_files.extend([args.vdagent_udcs_path, args.vdagent_virtio_path, args.vdagent_uinput_path]) var_args = ['port', 'tls_port', 'disable_ticketing', 'x509_dir', 'sasl', 'cacert_file', 'x509_cert_file', 'x509_key_file', 'x509_key_password', 'tls_ciphers', 'dh_file', 'password', 'image_compression', 'jpeg_wan_compression', 'zlib_glz_wan_compression', 'streaming_video', 'video_codecs', 'deferred_fps', 'exit_on_disconnect', 'vdagent_enabled', 'vdagent_virtio_path', 'vdagent_uinput_path', 'vdagent_uid', 'vdagent_gid'] for arg in var_args: if getattr(args, arg) != None: # The Qxl code doesn't respect booleans, so pass them as 0/1 a = getattr(args, arg) if a == True: a = "1" elif a == False: a = "0" else: a = str(a) os.environ['XSPICE_' + arg.upper()] = a # A few arguments don't follow the XSPICE_ convention - handle them manually if args.numheads: os.environ['QXL_NUM_HEADS'] = str(args.numheads) display="" for arg in xorg_args: if arg.startswith(":"): display = arg if not display: print("Error: missing display on line (i.e. :3)") raise SystemExit os.environ ['DISPLAY'] = display exec_args = [args.xorg, '-config', args.config] if cgdb and args.cgdb: exec_args = [cgdb, '--args'] + exec_args args.xorg = cgdb # This is currently mandatory; the driver cannot survive a reset xorg_args = [ '-noreset' ] + xorg_args if args.vdagent_enabled: for f in [args.vdagent_udcs_path, args.vdagent_virtio_path, args.vdagent_uinput_path]: if os.path.exists(f): os.unlink(f) cleanup_files.extend([args.vdagent_udcs_path, args.vdagent_virtio_path, args.vdagent_uinput_path]) xorg = launch(executable=args.xorg, args=exec_args + xorg_args) time.sleep(2) retpid,rc = os.waitpid(xorg.pid, os.WNOHANG) if retpid != 0: print("Error: X server is not running") else: if args.vdagent_enabled and args.vdagent_launch: # XXX use systemd --user for this? vdagentd = launch(args=[args.vdagentd_exec, '-f', '-x', '-S', args.vdagent_udcs_path, '-s', args.vdagent_virtio_path, '-u', args.vdagent_uinput_path]) time.sleep(1) # TODO wait for uinput pipe open for write vdagent = launch(args=[args.vdagent_exec, '-x', '-s', args.vdagent_virtio_path, '-S', args.vdagent_udcs_path]) if args.xsession: environ = os.environ os.spawnlpe(os.P_NOWAIT, args.xsession, environ) try: xorg.wait() except KeyboardInterrupt: # Catch Ctrl-C as that is the common way of ending this script print("Keyboard Interrupt") xserver-xorg-video-qxl-0.1.5+git20200331/src/000077500000000000000000000000001361660122400202605ustar00rootroot00000000000000xserver-xorg-video-qxl-0.1.5+git20200331/src/Makefile.am000066400000000000000000000065601361660122400223230ustar00rootroot00000000000000# Copyright 2008 Red Hat, Inc. # # 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 # on the rights to use, copy, modify, merge, publish, distribute, sub # license, 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 (including the next # paragraph) 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 NON-INFRINGEMENT. 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. # this is obnoxious: # -module lets us name the module exactly how we want # -avoid-version prevents gratuitous .0.0.0 version numbers on the end # _ladir passes a dummy rpath to libtool so the thing will actually link # TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc. SUBDIRS = uxa spiceccid AM_CFLAGS = \ $(SPICE_PROTOCOL_CFLAGS) \ $(XORG_CFLAGS) \ $(PCIACCESS_CFLAGS) \ $(CWARNFLAGS) \ $(DRM_CFLAGS) \ $(LIBUDEV_CFLAGS) if BUILD_QXL qxl_drv_la_LTLIBRARIES = qxl_drv.la qxl_drv_la_LDFLAGS = -module -avoid-version qxl_drv_ladir = @moduledir@/drivers qxl_drv_la_LIBADD = uxa/libuxa.la if LIBUDEV qxl_drv_la_LIBADD += $(LIBUDEV_LIBS) endif qxl_drv_la_SOURCES = \ qxl.h \ qxl_driver.c \ qxl_image.c \ qxl_surface.c \ qxl_surface_ums.c \ qxl_surface.h \ qxl_ring.c \ qxl_mem.c \ mspace.c \ mspace.h \ murmurhash3.c \ murmurhash3.h \ qxl_cursor.c \ qxl_option_helpers.c \ qxl_option_helpers.h \ qxl_uxa.c \ qxl_ums_mode.c \ qxl_io.c \ dfps.c \ qxl_kms.c \ qxl_drmmode.c \ qxl_drmmode.h \ compat-api.h endif if BUILD_XSPICE spiceqxl_drv_la_LTLIBRARIES = spiceqxl_drv.la spiceqxl_drv_la_LDFLAGS = -module -avoid-version $(SPICE_LIBS) spiceqxl_drv_ladir = @moduledir@/drivers spiceqxl_drv_la_CFLAGS = -DXSPICE $(AM_CFLAGS) $(SPICE_CFLAGS) spiceqxl_drv_la_LIBADD = uxa/libuxa.la $(XORG_LIBS) spiceqxl_drv_la_SOURCES = \ qxl.h \ qxl_option_helpers.c \ qxl_option_helpers.h \ spiceqxl_util.h \ spiceqxl_util.c \ spiceqxl_spice_server.c \ spiceqxl_spice_server.h \ spiceqxl_io_port.c \ spiceqxl_io_port.h \ spiceqxl_driver.c \ spiceqxl_driver.h \ spiceqxl_main_loop.c \ spiceqxl_main_loop.h \ spiceqxl_display.c \ spiceqxl_display.h \ spiceqxl_vdagent.c \ spiceqxl_vdagent.h \ spiceqxl_uinput.c \ spiceqxl_uinput.h \ spiceqxl_audio.c \ spiceqxl_smartcard.c \ spiceqxl_smartcard.h \ spiceqxl_audio.h \ spiceqxl_inputs.c \ spiceqxl_inputs.h \ qxl_driver.c \ qxl_image.c \ qxl_surface.c \ qxl_surface_ums.c \ qxl_surface.h \ qxl_ring.c \ qxl_mem.c \ mspace.c \ mspace.h \ murmurhash3.c \ murmurhash3.h \ qxl_cursor.c \ dfps.c \ dfps.h \ qxl_uxa.c \ qxl_ums_mode.c \ qxl_io.c \ compat-api.h endif xserver-xorg-video-qxl-0.1.5+git20200331/src/compat-api.h000066400000000000000000000067431361660122400224750ustar00rootroot00000000000000/* * Copyright 2012 Red Hat, Inc. * * 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 (including the next * paragraph) 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 OR COPYRIGHT HOLDERS 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. * * Author: Dave Airlie */ /* this file provides API compat between server post 1.13 and pre it, it should be reused inside as many drivers as possible */ #ifndef COMPAT_API_H #define COMPAT_API_H #ifndef GLYPH_HAS_GLYPH_PICTURE_ACCESSOR #define GetGlyphPicture(g, s) GlyphPicture((g))[(s)->myNum] #define SetGlyphPicture(g, s, p) GlyphPicture((g))[(s)->myNum] = p #endif #ifndef XF86_HAS_SCRN_CONV #define xf86ScreenToScrn(s) xf86Screens[(s)->myNum] #define xf86ScrnToScreen(s) screenInfo.screens[(s)->scrnIndex] #endif #ifndef XF86_SCRN_INTERFACE #define SCRN_ARG_TYPE int #define SCRN_INFO_PTR(arg1) ScrnInfoPtr pScrn = xf86Screens[(arg1)] #define SCREEN_ARG_TYPE int #define SCREEN_PTR(arg1) ScreenPtr pScreen = screenInfo.screens[(arg1)] #define SCREEN_INIT_ARGS_DECL int i, ScreenPtr pScreen, int argc, char **argv #define BLOCKHANDLER_ARGS_DECL int arg, pointer blockData, pointer pTimeout, pointer pReadmask #define BLOCKHANDLER_ARGS arg, blockData, pTimeout, pReadmask #define CLOSE_SCREEN_ARGS_DECL int scrnIndex, ScreenPtr pScreen #define CLOSE_SCREEN_ARGS scrnIndex, pScreen #define ADJUST_FRAME_ARGS_DECL int arg, int x, int y, int flags #define ADJUST_FRAME_ARGS(arg, x, y) (arg)->scrnIndex, x, y, 0 #define SWITCH_MODE_ARGS_DECL int arg, DisplayModePtr mode, int flags #define SWITCH_MODE_ARGS(arg, m) (arg)->scrnIndex, m, 0 #define FREE_SCREEN_ARGS_DECL int arg, int flags #define VT_FUNC_ARGS_DECL int arg, int flags #define VT_FUNC_ARGS pScrn->scrnIndex, 0 #define XF86_SCRN_ARG(x) ((x)->scrnIndex) #else #define SCRN_ARG_TYPE ScrnInfoPtr #define SCRN_INFO_PTR(arg1) ScrnInfoPtr pScrn = (arg1) #define SCREEN_ARG_TYPE ScreenPtr #define SCREEN_PTR(arg1) ScreenPtr pScreen = (arg1) #define SCREEN_INIT_ARGS_DECL ScreenPtr pScreen, int argc, char **argv #define BLOCKHANDLER_ARGS_DECL ScreenPtr arg, pointer pTimeout, pointer pReadmask #define BLOCKHANDLER_ARGS arg, pTimeout, pReadmask #define CLOSE_SCREEN_ARGS_DECL ScreenPtr pScreen #define CLOSE_SCREEN_ARGS pScreen #define ADJUST_FRAME_ARGS_DECL ScrnInfoPtr arg, int x, int y #define ADJUST_FRAME_ARGS(arg, x, y) arg, x, y #define SWITCH_MODE_ARGS_DECL ScrnInfoPtr arg, DisplayModePtr mode #define SWITCH_MODE_ARGS(arg, m) arg, m #define FREE_SCREEN_ARGS_DECL ScrnInfoPtr arg #define VT_FUNC_ARGS_DECL ScrnInfoPtr arg #define VT_FUNC_ARGS pScrn #define XF86_SCRN_ARG(x) (x) #endif #endif xserver-xorg-video-qxl-0.1.5+git20200331/src/dfps.c000066400000000000000000000266661361660122400214000ustar00rootroot00000000000000/* * Copyright (C) 2012 CodeWeavers, Inc. * * 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 (including the next * paragraph) 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 OR COPYRIGHT HOLDERS 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. * * Authors: * Jeremy White */ /*---------------------------------------------------------------------------- Deferred Frames Per Second (dfps) Support File By default, The xorg-video-qxl driver transmits all of the video operation across the wire. While this has the greatest fidelity, and lends itself well to a variety of optimizations and behavior tuning, it does not always use bandwidth efficiently. This file implements a 'deferred frames' mode which instead renders everything to a frame buffer, and then periodically sends only updated portions of the screen. For some use cases, this proves to be far more efficient with network resources. ----------------------------------------------------------------------------*/ #include #include "qxl.h" #include "dfps.h" typedef struct _dfps_info_t { RegionRec updated_region; PixmapPtr copy_src; Pixel solid_pixel; GCPtr pgc; } dfps_info_t; static void dfps_ticker(void *opaque); static inline dfps_info_t *dfps_get_info (PixmapPtr pixmap) { #if HAS_DEVPRIVATEKEYREC return dixGetPrivate(&pixmap->devPrivates, &uxa_pixmap_index); #else return dixLookupPrivate(&pixmap->devPrivates, &uxa_pixmap_index); #endif } static inline void dfps_set_info (PixmapPtr pixmap, dfps_info_t *info) { dixSetPrivate(&pixmap->devPrivates, &uxa_pixmap_index, info); } typedef struct FrameTimer { OsTimerPtr xorg_timer; FrameTimerFunc func; void *opaque; // also stored in xorg_timer, but needed for timer_start } Timer; static CARD32 xorg_timer_callback( OsTimerPtr xorg_timer, CARD32 time, pointer arg) { FrameTimer *timer = (FrameTimer*)arg; timer->func(timer->opaque); return 0; // if non zero xorg does a TimerSet, we don't want that. } static FrameTimer* timer_add(FrameTimerFunc func, void *opaque) { FrameTimer *timer = calloc(sizeof(FrameTimer), 1); timer->xorg_timer = TimerSet(NULL, 0, 1e9 /* TODO: infinity? */, xorg_timer_callback, timer); timer->func = func; timer->opaque = opaque; return timer; } static void timer_start(FrameTimer *timer, uint32_t ms) { TimerSet(timer->xorg_timer, 0 /* flags */, ms, xorg_timer_callback, timer); } void dfps_start_ticker(qxl_screen_t *qxl) { qxl->frames_timer = timer_add(dfps_ticker, qxl); timer_start(qxl->frames_timer, 1000 / qxl->deferred_fps); } static void dfps_ticker(void *opaque) { qxl_screen_t *qxl = (qxl_screen_t *) opaque; dfps_info_t *info = NULL; PixmapPtr pixmap; pixmap = qxl->pScrn->pScreen->GetScreenPixmap(qxl->pScrn->pScreen); if (pixmap) info = dfps_get_info(pixmap); if (info) { qxl_surface_upload_primary_regions(qxl, pixmap, &info->updated_region); RegionUninit(&info->updated_region); RegionInit(&info->updated_region, NULL, 0); } timer_start(qxl->frames_timer, 1000 / qxl->deferred_fps); } static Bool unaccel (void) { return FALSE; } static Bool is_main_pixmap(PixmapPtr pixmap) { ScreenPtr screen = pixmap->drawable.pScreen; if (screen && pixmap == screen->GetScreenPixmap(screen)) return TRUE; return FALSE; } /* Establish a maximum number of disparate regions we'll track before we just treat the entire bounding rectangle as having changed. The number 20 seemed intuitive, and also produced the best results in benchmarking x11perf -circle10 -repeat 1 */ #define DFPS_MAX_UPDATE_REGIONS 20 static void dfps_update_box(RegionPtr dest, int x_1, int x_2, int y_1, int y_2); static void dfps_update_region(RegionPtr dest, RegionPtr src) { Bool throwaway_bool; RegionAppend(dest, src); RegionValidate(dest, &throwaway_bool); if (RegionNumRects(dest) > DFPS_MAX_UPDATE_REGIONS) { struct pixman_box16 box = * (RegionExtents(dest)); RegionUninit(dest); RegionInit(dest, NULL, 0); dfps_update_box(dest, box.x1, box.x2, box.y1, box.y2); } } static void dfps_update_box(RegionPtr dest, int x_1, int x_2, int y_1, int y_2) { struct pixman_box16 box; RegionPtr region; box.x1 = x_1; box.x2 = x_2; box.y1 = y_1; box.y2 = y_2; region = RegionCreate(&box, 1); dfps_update_region(dest, region); RegionUninit(region); RegionDestroy(region); } static Bool dfps_prepare_solid (PixmapPtr pixmap, int alu, Pixel planemask, Pixel fg) { dfps_info_t *info; if (!(info = dfps_get_info (pixmap))) return FALSE; info->solid_pixel = fg; info->pgc = GetScratchGC(pixmap->drawable.depth, pixmap->drawable.pScreen); if (! info->pgc) return FALSE; info->pgc->alu = alu; info->pgc->planemask = planemask; info->pgc->fgPixel = fg; info->pgc->fillStyle = FillSolid; fbValidateGC(info->pgc, GCForeground | GCPlaneMask, &pixmap->drawable); return TRUE; } static void dfps_solid (PixmapPtr pixmap, int x_1, int y_1, int x_2, int y_2) { dfps_info_t *info; if (!(info = dfps_get_info (pixmap))) return; /* Draw to the frame buffer */ fbFill(&pixmap->drawable, info->pgc, x_1, y_1, x_2 - x_1, y_2 - y_1); /* Track the updated region */ if (is_main_pixmap(pixmap)) dfps_update_box(&info->updated_region, x_1, x_2, y_1, y_2); return; } static void dfps_done_solid (PixmapPtr pixmap) { dfps_info_t *info; if ((info = dfps_get_info (pixmap))) { FreeScratchGC(info->pgc); info->pgc = NULL; } } static Bool dfps_prepare_copy (PixmapPtr source, PixmapPtr dest, int xdir, int ydir, int alu, Pixel planemask) { dfps_info_t *info; if (!(info = dfps_get_info (dest))) return FALSE; info->copy_src = source; info->pgc = GetScratchGC(dest->drawable.depth, dest->drawable.pScreen); if (! info->pgc) return FALSE; info->pgc->alu = alu; info->pgc->planemask = planemask; fbValidateGC(info->pgc, GCPlaneMask, &dest->drawable); return TRUE; } static void dfps_copy (PixmapPtr dest, int src_x1, int src_y1, int dest_x1, int dest_y1, int width, int height) { dfps_info_t *info; if (!(info = dfps_get_info (dest))) return; /* Render into to the frame buffer */ fbCopyArea(&info->copy_src->drawable, &dest->drawable, info->pgc, src_x1, src_y1, width, height, dest_x1, dest_y1); /* Update the tracking region */ if (is_main_pixmap(dest)) dfps_update_box(&info->updated_region, dest_x1, dest_x1 + width, dest_y1, dest_y1 + height); } static void dfps_done_copy (PixmapPtr dest) { dfps_info_t *info; if ((info = dfps_get_info (dest))) { FreeScratchGC(info->pgc); info->pgc = NULL; } } static Bool dfps_put_image (PixmapPtr dest, int x, int y, int w, int h, char *src, int src_pitch) { dfps_info_t *info; FbBits *dst; FbStride dst_stride; int dst_bpp; if (!(info = dfps_get_info (dest))) return FALSE; if (is_main_pixmap(dest)) dfps_update_box(&info->updated_region, x, x + w, y, y + h); fbPrepareAccess(dest); fbGetPixmapBitsData(dest, dst, dst_stride, dst_bpp); fbBlt((FbBits *) src, src_pitch / sizeof(FbStip), 0, dst + (y * dst_stride), dst_stride, x * dst_bpp, w * dst_bpp, h, GXcopy, FB_ALLONES, dst_bpp, FALSE, FALSE); fbFinishAccess(dest); return TRUE; } static Bool dfps_prepare_access (PixmapPtr pixmap, RegionPtr region, uxa_access_t requested_access) { fbPrepareAccess(pixmap); if (requested_access == UXA_ACCESS_RW) { dfps_info_t *info; if (!(info = dfps_get_info (pixmap))) return FALSE; if (is_main_pixmap(pixmap)) dfps_update_region(&info->updated_region, region); } return TRUE; } static void dfps_finish_access (PixmapPtr pixmap) { fbFinishAccess(pixmap); } static Bool dfps_pixmap_is_offscreen (PixmapPtr pixmap) { return !!dfps_get_info(pixmap); } static void dfps_set_screen_pixmap (PixmapPtr pixmap) { pixmap->drawable.pScreen->devPrivate = pixmap; } static void dfps_clear_pixmap(PixmapPtr pixmap, int w, int h) { GCPtr pgc; pgc = GetScratchGC(pixmap->drawable.depth, pixmap->drawable.pScreen); if (pgc) { fbFill(&pixmap->drawable, pgc, 0, 0, w, h); FreeScratchGC(pgc); } } static PixmapPtr dfps_create_pixmap (ScreenPtr screen, int w, int h, int depth, unsigned usage) { PixmapPtr pixmap; dfps_info_t *info; info = calloc(1, sizeof(*info)); if (!info) return FALSE; RegionInit(&info->updated_region, NULL, 0); pixmap = fbCreatePixmap (screen, w, h, depth, usage); if (pixmap) { dfps_clear_pixmap(pixmap, w, h); dfps_set_info(pixmap, info); } else free(info); return pixmap; } static Bool dfps_destroy_pixmap (PixmapPtr pixmap) { if (pixmap->refcnt == 1) { dfps_info_t *info = dfps_get_info (pixmap); if (info) free(info); dfps_set_info(pixmap, NULL); } return fbDestroyPixmap (pixmap); } void dfps_set_uxa_functions(qxl_screen_t *qxl, ScreenPtr screen) { /* Solid fill */ //qxl->uxa->check_solid = dfps_check_solid; qxl->uxa->prepare_solid = dfps_prepare_solid; qxl->uxa->solid = dfps_solid; qxl->uxa->done_solid = dfps_done_solid; /* Copy */ //qxl->uxa->check_copy = qxl_check_copy; qxl->uxa->prepare_copy = dfps_prepare_copy; qxl->uxa->copy = dfps_copy; qxl->uxa->done_copy = dfps_done_copy; /* Composite */ qxl->uxa->check_composite = (typeof(qxl->uxa->check_composite))unaccel; qxl->uxa->check_composite_target = (typeof(qxl->uxa->check_composite_target))unaccel; qxl->uxa->check_composite_texture = (typeof(qxl->uxa->check_composite_texture))unaccel; qxl->uxa->prepare_composite = (typeof(qxl->uxa->prepare_composite))unaccel; qxl->uxa->composite = (typeof(qxl->uxa->composite))unaccel; qxl->uxa->done_composite = (typeof(qxl->uxa->done_composite))unaccel; /* PutImage */ qxl->uxa->put_image = dfps_put_image; /* Prepare access */ qxl->uxa->prepare_access = dfps_prepare_access; qxl->uxa->finish_access = dfps_finish_access; /* General screen information */ qxl->uxa->pixmap_is_offscreen = dfps_pixmap_is_offscreen; screen->SetScreenPixmap = dfps_set_screen_pixmap; screen->CreatePixmap = dfps_create_pixmap; screen->DestroyPixmap = dfps_destroy_pixmap; } xserver-xorg-video-qxl-0.1.5+git20200331/src/dfps.h000066400000000000000000000023661361660122400213740ustar00rootroot00000000000000/* * Copyright (C) 2012 CodeWeavers, Inc. * * 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 (including the next * paragraph) 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 OR COPYRIGHT HOLDERS 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. * */ void dfps_start_ticker(qxl_screen_t *qxl); void dfps_set_uxa_functions(qxl_screen_t *qxl, ScreenPtr screen); xserver-xorg-video-qxl-0.1.5+git20200331/src/mspace.c000066400000000000000000002433141361660122400217030ustar00rootroot00000000000000// based on dlmalloc from Doug Lea // quote from the Doug Lea original file /* This is a version (aka dlmalloc) of malloc/free/realloc written by Doug Lea and released to the public domain, as explained at http://creativecommons.org/licenses/publicdomain. Send questions, comments, complaints, performance data, etc to dl@cs.oswego.edu * Version 2.8.3 Thu Sep 22 11:16:15 2005 Doug Lea (dl at gee) Note: There may be an updated version of this malloc obtainable at ftp://gee.cs.oswego.edu/pub/misc/malloc.c Check before installing! */ #include #include #include #include "mspace.h" #define MALLOC_ALIGNMENT ((size_t)8U) #define USE_LOCKS 0 #define malloc_getpagesize ((size_t)4096U) #define DEFAULT_GRANULARITY malloc_getpagesize #define MAX_SIZE_T (~(size_t)0) #define MALLOC_FAILURE_ACTION #define MALLINFO_FIELD_TYPE size_t #define FOOTERS 0 #define INSECURE 0 #define PROCEED_ON_ERROR 0 #define DEBUG 0 #define ABORT_ON_ASSERT_FAILURE 1 #define ABORT(user_data) abort_func(user_data) #define USE_BUILTIN_FFS 0 #define USE_DEV_RANDOM 0 #define PRINT(params) print_func params #define MEMCPY(dest, src, n) memcpy (dest, src, n) #define MEMCLEAR(dest, n) memset (dest, 0, n); #define M_GRANULARITY (-1) void __attribute__ ((__noreturn__)) default_abort_func(void *user_data) { for (;;); } void default_print_func(void *user_data, const char *format, ...) { } static mspace_abort_t abort_func = default_abort_func; static mspace_print_t print_func = default_print_func; void mspace_set_abort_func(mspace_abort_t f) { abort_func = f; } void mspace_set_print_func(mspace_print_t f) { print_func = f; } /* ------------------------ Mallinfo declarations ------------------------ */ #if !NO_MALLINFO /* This version of malloc supports the standard SVID/XPG mallinfo routine that returns a struct containing usage properties and statistics. It should work on any system that has a /usr/include/malloc.h defining struct mallinfo. The main declaration needed is the mallinfo struct that is returned (by-copy) by mallinfo(). The malloinfo struct contains a bunch of fields that are not even meaningful in this version of malloc. These fields are are instead filled by mallinfo() with other numbers that might be of interest. HAVE_USR_INCLUDE_MALLOC_H should be set if you have a /usr/include/malloc.h file that includes a declaration of struct mallinfo. If so, it is included; else a compliant version is declared below. These must be precisely the same for mallinfo() to work. The original SVID version of this struct, defined on most systems with mallinfo, declares all fields as ints. But some others define as unsigned long. If your system defines the fields using a type of different width than listed here, you MUST #include your system version and #define HAVE_USR_INCLUDE_MALLOC_H. */ /* #define HAVE_USR_INCLUDE_MALLOC_H */ struct mallinfo { MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ MALLINFO_FIELD_TYPE smblks; /* always 0 */ MALLINFO_FIELD_TYPE hblks; /* always 0 */ MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ MALLINFO_FIELD_TYPE fordblks; /* total free space */ MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ }; #endif /* NO_MALLINFO */ #ifdef DEBUG #if ABORT_ON_ASSERT_FAILURE #define assert(user_data, x) if(!(x)) ABORT(user_data) #else /* ABORT_ON_ASSERT_FAILURE */ #include #endif /* ABORT_ON_ASSERT_FAILURE */ #else /* DEBUG */ #define assert(user_data, x) #endif /* DEBUG */ /* ------------------- size_t and alignment properties -------------------- */ /* The byte and bit size of a size_t */ #define SIZE_T_SIZE (sizeof(size_t)) #define SIZE_T_BITSIZE (sizeof(size_t) << 3) /* Some constants coerced to size_t */ /* Annoying but necessary to avoid errors on some platforms */ #define SIZE_T_ZERO ((size_t)0) #define SIZE_T_ONE ((size_t)1) #define SIZE_T_TWO ((size_t)2) #define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) #define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) #define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) #define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) /* The bit mask value corresponding to MALLOC_ALIGNMENT */ #define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) /* True if address a has acceptable alignment */ #define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) /* the number of bytes to offset an address to align it */ #define align_offset(A)\ ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) /* --------------------------- Lock preliminaries ------------------------ */ #if USE_LOCKS /* When locks are defined, there are up to two global locks: * If HAVE_MORECORE, morecore_mutex protects sequences of calls to MORECORE. In many cases sys_alloc requires two calls, that should not be interleaved with calls by other threads. This does not protect against direct calls to MORECORE by other threads not using this lock, so there is still code to cope the best we can on interference. * magic_init_mutex ensures that mparams.magic and other unique mparams values are initialized only once. */ #define USE_LOCK_BIT (2U) #else /* USE_LOCKS */ #define USE_LOCK_BIT (0U) #define INITIAL_LOCK(l) #endif /* USE_LOCKS */ #if USE_LOCKS #define ACQUIRE_MAGIC_INIT_LOCK() ACQUIRE_LOCK(&magic_init_mutex); #define RELEASE_MAGIC_INIT_LOCK() RELEASE_LOCK(&magic_init_mutex); #else /* USE_LOCKS */ #define ACQUIRE_MAGIC_INIT_LOCK() #define RELEASE_MAGIC_INIT_LOCK() #endif /* USE_LOCKS */ /* ----------------------- Chunk representations ------------------------ */ /* (The following includes lightly edited explanations by Colin Plumb.) The malloc_chunk declaration below is misleading (but accurate and necessary). It declares a "view" into memory allowing access to necessary fields at known offsets from a given base. Chunks of memory are maintained using a `boundary tag' method as originally described by Knuth. (See the paper by Paul Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such techniques.) Sizes of free chunks are stored both in the front of each chunk and at the end. This makes consolidating fragmented chunks into bigger chunks fast. The head fields also hold bits representing whether chunks are free or in use. Here are some pictures to make it clearer. They are "exploded" to show that the state of a chunk can be thought of as extending from the high 31 bits of the head field of its header through the prev_foot and PINUSE_BIT bit of the following chunk header. A chunk that's in use looks like: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk (if P = 1) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| | Size of this chunk 1| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +- -+ | | +- -+ | : +- size - sizeof(size_t) available payload bytes -+ : | chunk-> +- -+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| | Size of next chunk (may or may not be in use) | +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ And if it's free, it looks like this: chunk-> +- -+ | User payload (must be in use, or we would have merged!) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| | Size of this chunk 0| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Next pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Prev pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : +- size - sizeof(struct chunk) unused bytes -+ : | chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of this chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| | Size of next chunk (must be in use, or we would have merged)| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : +- User payload -+ : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| +-+ Note that since we always merge adjacent free chunks, the chunks adjacent to a free chunk must be in use. Given a pointer to a chunk (which can be derived trivially from the payload pointer) we can, in O(1) time, find out whether the adjacent chunks are free, and if so, unlink them from the lists that they are on and merge them with the current chunk. Chunks always begin on even word boundaries, so the mem portion (which is returned to the user) is also on an even word boundary, and thus at least double-word aligned. The P (PINUSE_BIT) bit, stored in the unused low-order bit of the chunk size (which is always a multiple of two words), is an in-use bit for the *previous* chunk. If that bit is *clear*, then the word before the current chunk size contains the previous chunk size, and can be used to find the front of the previous chunk. The very first chunk allocated always has this bit set, preventing access to non-existent (or non-owned) memory. If pinuse is set for any given chunk, then you CANNOT determine the size of the previous chunk, and might even get a memory addressing fault when trying to do so. The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of the chunk size redundantly records whether the current chunk is inuse. This redundancy enables usage checks within free and realloc, and reduces indirection when freeing and consolidating chunks. Each freshly allocated chunk must have both cinuse and pinuse set. That is, each allocated chunk borders either a previously allocated and still in-use chunk, or the base of its memory arena. This is ensured by making all allocations from the `lowest' part of any found chunk. Further, no free chunk physically borders another one, so each free chunk is known to be preceded and followed by either inuse chunks or the ends of memory. Note that the `foot' of the current chunk is actually represented as the prev_foot of the NEXT chunk. This makes it easier to deal with alignments etc but can be very confusing when trying to extend or adapt this code. The exceptions to all this are 1. The special chunk `top' is the top-most available chunk (i.e., the one bordering the end of available memory). It is treated specially. Top is never included in any bin, is used only if no other chunk is available, and is released back to the system if it is very large (see M_TRIM_THRESHOLD). In effect, the top chunk is treated as larger (and thus less well fitting) than any other available chunk. The top chunk doesn't update its trailing size field since there is no next contiguous chunk that would have to index off it. However, space is still allocated for it (TOP_FOOT_SIZE) to enable separation or merging when space is extended. 3. Chunks allocated via mmap, which have the lowest-order bit (IS_MMAPPED_BIT) set in their prev_foot fields, and do not set PINUSE_BIT in their head fields. Because they are allocated one-by-one, each must carry its own prev_foot field, which is also used to hold the offset this chunk has within its mmapped region, which is needed to preserve alignment. Each mmapped chunk is trailed by the first two fields of a fake next-chunk for sake of usage checks. */ struct malloc_chunk { size_t prev_foot; /* Size of previous chunk (if free). */ size_t head; /* Size and inuse bits. */ struct malloc_chunk* fd; /* double links -- used only if free. */ struct malloc_chunk* bk; }; typedef struct malloc_chunk mchunk; typedef struct malloc_chunk* mchunkptr; typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ typedef unsigned int bindex_t; /* Described below */ typedef unsigned int binmap_t; /* Described below */ typedef unsigned int flag_t; /* The type of various bit flag sets */ /* ------------------- Chunks sizes and alignments ----------------------- */ #define MCHUNK_SIZE (sizeof(mchunk)) #if FOOTERS #define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) #else /* FOOTERS */ #define CHUNK_OVERHEAD (SIZE_T_SIZE) #endif /* FOOTERS */ /* The smallest size we can malloc is an aligned minimal chunk */ #define MIN_CHUNK_SIZE\ ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) /* conversion from malloc headers to user pointers, and back */ #define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) #define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) /* chunk associated with aligned address A */ #define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) /* Bounds on request (not chunk) sizes. */ #define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) #define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) /* pad request bytes into a usable size */ #define pad_request(req) \ (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) /* pad request, checking for minimum (but not maximum) */ #define request2size(req) \ (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) /* ------------------ Operations on head and foot fields ----------------- */ /* The head field of a chunk is or'ed with PINUSE_BIT when previous adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in use. If the chunk was obtained with mmap, the prev_foot field has IS_MMAPPED_BIT set, otherwise holding the offset of the base of the mmapped region to the base of the chunk. */ #define PINUSE_BIT (SIZE_T_ONE) #define CINUSE_BIT (SIZE_T_TWO) #define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) /* Head value for fenceposts */ #define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) /* extraction of fields from head words */ #define cinuse(p) ((p)->head & CINUSE_BIT) #define pinuse(p) ((p)->head & PINUSE_BIT) #define chunksize(p) ((p)->head & ~(INUSE_BITS)) #define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) #define clear_cinuse(p) ((p)->head &= ~CINUSE_BIT) /* Treat space at ptr +/- offset as a chunk */ #define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) #define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) /* Ptr to next or previous physical malloc_chunk. */ #define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~INUSE_BITS))) #define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) /* extract next chunk's pinuse bit */ #define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) /* Get/set size at footer */ #define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) #define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) /* Set size, pinuse bit, and foot */ #define set_size_and_pinuse_of_free_chunk(p, s)\ ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) /* Set size, pinuse bit, foot, and clear next pinuse */ #define set_free_with_pinuse(p, s, n)\ (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) /* Get the internal overhead associated with chunk p */ #define overhead_for(p) CHUNK_OVERHEAD /* Return true if malloced space is not necessarily cleared */ #define calloc_must_clear(p) (1) /* ---------------------- Overlaid data structures ----------------------- */ /* When chunks are not in use, they are treated as nodes of either lists or trees. "Small" chunks are stored in circular doubly-linked lists, and look like this: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk, in bytes |P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Forward pointer to next chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Back pointer to previous chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Unused space (may be 0 bytes long) . . . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk, in bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Larger chunks are kept in a form of bitwise digital trees (aka tries) keyed on chunksizes. Because malloc_tree_chunks are only for free chunks greater than 256 bytes, their size doesn't impose any constraints on user chunk sizes. Each node looks like: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk, in bytes |P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Forward pointer to next chunk of same size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Back pointer to previous chunk of same size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pointer to left child (child[0]) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pointer to right child (child[1]) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pointer to parent | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | bin index of this chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Unused space . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk, in bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Each tree holding treenodes is a tree of unique chunk sizes. Chunks of the same size are arranged in a circularly-linked list, with only the oldest chunk (the next to be used, in our FIFO ordering) actually in the tree. (Tree members are distinguished by a non-null parent pointer.) If a chunk with the same size as an existing node is inserted, it is linked off the existing node using pointers that work in the same way as fd/bk pointers of small chunks. Each tree contains a power of 2 sized range of chunk sizes (the smallest is 0x100 <= x < 0x180), which is divided in half at each tree level, with the chunks in the smaller half of the range (0x100 <= x < 0x140 for the top nose) in the left subtree and the larger half (0x140 <= x < 0x180) in the right subtree. This is, of course, done by inspecting individual bits. Using these rules, each node's left subtree contains all smaller sizes than its right subtree. However, the node at the root of each subtree has no particular ordering relationship to either. (The dividing line between the subtree sizes is based on trie relation.) If we remove the last chunk of a given size from the interior of the tree, we need to replace it with a leaf node. The tree ordering rules permit a node to be replaced by any leaf below it. The smallest chunk in a tree (a common operation in a best-fit allocator) can be found by walking a path to the leftmost leaf in the tree. Unlike a usual binary tree, where we follow left child pointers until we reach a null, here we follow the right child pointer any time the left one is null, until we reach a leaf with both child pointers null. The smallest chunk in the tree will be somewhere along that path. The worst case number of steps to add, find, or remove a node is bounded by the number of bits differentiating chunks within bins. Under current bin calculations, this ranges from 6 up to 21 (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case is of course much better. */ struct malloc_tree_chunk { /* The first four fields must be compatible with malloc_chunk */ size_t prev_foot; size_t head; struct malloc_tree_chunk* fd; struct malloc_tree_chunk* bk; struct malloc_tree_chunk* child[2]; struct malloc_tree_chunk* parent; bindex_t index; }; typedef struct malloc_tree_chunk tchunk; typedef struct malloc_tree_chunk* tchunkptr; typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ /* A little helper macro for trees */ #define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) /* ----------------------------- Segments -------------------------------- */ /* Each malloc space may include non-contiguous segments, held in a list headed by an embedded malloc_segment record representing the top-most space. Segments also include flags holding properties of the space. Large chunks that are directly allocated by mmap are not included in this list. They are instead independently created and destroyed without otherwise keeping track of them. Segment management mainly comes into play for spaces allocated by MMAP. Any call to MMAP might or might not return memory that is adjacent to an existing segment. MORECORE normally contiguously extends the current space, so this space is almost always adjacent, which is simpler and faster to deal with. (This is why MORECORE is used preferentially to MMAP when both are available -- see sys_alloc.) When allocating using MMAP, we don't use any of the hinting mechanisms (inconsistently) supported in various implementations of unix mmap, or distinguish reserving from committing memory. Instead, we just ask for space, and exploit contiguity when we get it. It is probably possible to do better than this on some systems, but no general scheme seems to be significantly better. Management entails a simpler variant of the consolidation scheme used for chunks to reduce fragmentation -- new adjacent memory is normally prepended or appended to an existing segment. However, there are limitations compared to chunk consolidation that mostly reflect the fact that segment processing is relatively infrequent (occurring only when getting memory from system) and that we don't expect to have huge numbers of segments: * Segments are not indexed, so traversal requires linear scans. (It would be possible to index these, but is not worth the extra overhead and complexity for most programs on most platforms.) * New segments are only appended to old ones when holding top-most memory; if they cannot be prepended to others, they are held in different segments. Except for the top-most segment of an mstate, each segment record is kept at the tail of its segment. Segments are added by pushing segment records onto the list headed by &mstate.seg for the containing mstate. Segment flags control allocation/merge/deallocation policies: * If EXTERN_BIT set, then we did not allocate this segment, and so should not try to deallocate or merge with others. (This currently holds only for the initial segment passed into create_mspace_with_base.) * If IS_MMAPPED_BIT set, the segment may be merged with other surrounding mmapped segments and trimmed/de-allocated using munmap. * If neither bit is set, then the segment was obtained using MORECORE so can be merged with surrounding MORECORE'd segments and deallocated/trimmed using MORECORE with negative arguments. */ struct malloc_segment { char* base; /* base address */ size_t size; /* allocated size */ struct malloc_segment* next; /* ptr to next segment */ }; typedef struct malloc_segment msegment; typedef struct malloc_segment* msegmentptr; /* ---------------------------- malloc_state ----------------------------- */ /* A malloc_state holds all of the bookkeeping for a space. The main fields are: Top The topmost chunk of the currently active segment. Its size is cached in topsize. The actual size of topmost space is topsize+TOP_FOOT_SIZE, which includes space reserved for adding fenceposts and segment records if necessary when getting more space from the system. The size at which to autotrim top is cached from mparams in trim_check, except that it is disabled if an autotrim fails. Designated victim (dv) This is the preferred chunk for servicing small requests that don't have exact fits. It is normally the chunk split off most recently to service another small request. Its size is cached in dvsize. The link fields of this chunk are not maintained since it is not kept in a bin. SmallBins An array of bin headers for free chunks. These bins hold chunks with sizes less than MIN_LARGE_SIZE bytes. Each bin contains chunks of all the same size, spaced 8 bytes apart. To simplify use in double-linked lists, each bin header acts as a malloc_chunk pointing to the real first node, if it exists (else pointing to itself). This avoids special-casing for headers. But to avoid waste, we allocate only the fd/bk pointers of bins, and then use repositioning tricks to treat these as the fields of a chunk. TreeBins Treebins are pointers to the roots of trees holding a range of sizes. There are 2 equally spaced treebins for each power of two from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything larger. Bin maps There is one bit map for small bins ("smallmap") and one for treebins ("treemap). Each bin sets its bit when non-empty, and clears the bit when empty. Bit operations are then used to avoid bin-by-bin searching -- nearly all "search" is done without ever looking at bins that won't be selected. The bit maps conservatively use 32 bits per map word, even if on 64bit system. For a good description of some of the bit-based techniques used here, see Henry S. Warren Jr's book "Hacker's Delight" (and supplement at http://hackersdelight.org/). Many of these are intended to reduce the branchiness of paths through malloc etc, as well as to reduce the number of memory locations read or written. Segments A list of segments headed by an embedded malloc_segment record representing the initial space. Address check support The least_addr field is the least address ever obtained from MORECORE or MMAP. Attempted frees and reallocs of any address less than this are trapped (unless INSECURE is defined). Magic tag A cross-check field that should always hold same value as mparams.magic. Flags Bits recording whether to use MMAP, locks, or contiguous MORECORE Statistics Each space keeps track of current and maximum system memory obtained via MORECORE or MMAP. Locking If USE_LOCKS is defined, the "mutex" lock is acquired and released around every public call using this mspace. */ /* Bin types, widths and sizes */ #define NSMALLBINS (32U) #define NTREEBINS (32U) #define SMALLBIN_SHIFT (3U) #define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) #define TREEBIN_SHIFT (8U) #define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) #define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) #define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) struct malloc_state { binmap_t smallmap; binmap_t treemap; size_t dvsize; size_t topsize; char* least_addr; mchunkptr dv; mchunkptr top; size_t magic; mchunkptr smallbins[(NSMALLBINS+1)*2]; tbinptr treebins[NTREEBINS]; size_t footprint; size_t max_footprint; flag_t mflags; void *user_data; #if USE_LOCKS MLOCK_T mutex; /* locate lock among fields that rarely change */ #endif /* USE_LOCKS */ msegment seg; }; typedef struct malloc_state* mstate; /* ------------- Global malloc_state and malloc_params ------------------- */ /* malloc_params holds global properties, including those that can be dynamically set using mallopt. There is a single instance, mparams, initialized in init_mparams. */ struct malloc_params { size_t magic; size_t page_size; size_t granularity; flag_t default_mflags; }; static struct malloc_params mparams; /* The global malloc_state used for all non-"mspace" calls */ //static struct malloc_state _gm_; //#define gm (&_gm_) //#define is_global(M) ((M) == &_gm_) #define is_initialized(M) ((M)->top != 0) /* -------------------------- system alloc setup ------------------------- */ /* Operations on mflags */ #define use_lock(M) ((M)->mflags & USE_LOCK_BIT) #define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) #define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) #define set_lock(M,L)\ ((M)->mflags = (L)?\ ((M)->mflags | USE_LOCK_BIT) :\ ((M)->mflags & ~USE_LOCK_BIT)) /* page-align a size */ #define page_align(S)\ (((S) + (mparams.page_size)) & ~(mparams.page_size - SIZE_T_ONE)) /* granularity-align a size */ #define granularity_align(S)\ (((S) + (mparams.granularity)) & ~(mparams.granularity - SIZE_T_ONE)) #define is_page_aligned(S)\ (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) #define is_granularity_aligned(S)\ (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) /* True if segment S holds address A */ #define segment_holds(S, A)\ ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) #if DEBUG /* Return segment holding given address */ static msegmentptr segment_holding(mstate m, char* addr) { msegmentptr sp = &m->seg; for (;;) { if (addr >= sp->base && addr < sp->base + sp->size) return sp; if ((sp = sp->next) == 0) return 0; } } /* Return true if segment contains a segment link */ static int has_segment_link(mstate m, msegmentptr ss) { msegmentptr sp = &m->seg; for (;;) { if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) return 1; if ((sp = sp->next) == 0) return 0; } } #endif /* TOP_FOOT_SIZE is padding at the end of a segment, including space that may be needed to place segment records and fenceposts when new noncontiguous segments are added. */ #define TOP_FOOT_SIZE\ (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) /* ------------------------------- Hooks -------------------------------- */ /* PREACTION should be defined to return 0 on success, and nonzero on failure. If you are not using locking, you can redefine these to do anything you like. */ #if USE_LOCKS /* Ensure locks are initialized */ #define GLOBALLY_INITIALIZE() (mparams.page_size == 0 && init_mparams()) #define PREACTION(M) ((GLOBALLY_INITIALIZE() || use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) #define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } #else /* USE_LOCKS */ #ifndef PREACTION #define PREACTION(M) (0) #endif /* PREACTION */ #ifndef POSTACTION #define POSTACTION(M) #endif /* POSTACTION */ #endif /* USE_LOCKS */ /* CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. USAGE_ERROR_ACTION is triggered on detected bad frees and reallocs. The argument p is an address that might have triggered the fault. It is ignored by the two predefined actions, but might be useful in custom actions that try to help diagnose errors. */ #if PROCEED_ON_ERROR /* A count of the number of corruption errors causing resets */ int malloc_corruption_error_count; /* default corruption action */ static void reset_on_error(mstate m); #define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) #define USAGE_ERROR_ACTION(m, p) #else /* PROCEED_ON_ERROR */ #ifndef CORRUPTION_ERROR_ACTION #define CORRUPTION_ERROR_ACTION(m) ABORT(m->user_data) #endif /* CORRUPTION_ERROR_ACTION */ #ifndef USAGE_ERROR_ACTION #define USAGE_ERROR_ACTION(m,p) ABORT(m->user_data) #endif /* USAGE_ERROR_ACTION */ #endif /* PROCEED_ON_ERROR */ /* -------------------------- Debugging setup ---------------------------- */ #if ! DEBUG #define check_free_chunk(M,P) #define check_inuse_chunk(M,P) #define check_malloced_chunk(M,P,N) #define check_malloc_state(M) #define check_top_chunk(M,P) #else /* DEBUG */ #define check_free_chunk(M,P) do_check_free_chunk(M,P) #define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) #define check_top_chunk(M,P) do_check_top_chunk(M,P) #define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) #define check_malloc_state(M) do_check_malloc_state(M) static void do_check_any_chunk(mstate m, mchunkptr p); static void do_check_top_chunk(mstate m, mchunkptr p); static void do_check_inuse_chunk(mstate m, mchunkptr p); static void do_check_free_chunk(mstate m, mchunkptr p); static void do_check_malloced_chunk(mstate m, void* mem, size_t s); static void do_check_tree(mstate m, tchunkptr t); static void do_check_treebin(mstate m, bindex_t i); static void do_check_smallbin(mstate m, bindex_t i); static void do_check_malloc_state(mstate m); static int bin_find(mstate m, mchunkptr x); static size_t traverse_and_check(mstate m); #endif /* DEBUG */ /* ---------------------------- Indexing Bins ---------------------------- */ #define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) #define small_index(s) ((s) >> SMALLBIN_SHIFT) #define small_index2size(i) ((i) << SMALLBIN_SHIFT) #define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) /* addressing by index. See above about smallbin repositioning */ #define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) #define treebin_at(M,i) (&((M)->treebins[i])) /* assign tree index for size S to variable I */ #if defined(__GNUC__) && defined(i386) #define compute_tree_index(S, I)\ {\ size_t X = S >> TREEBIN_SHIFT;\ if (X == 0)\ I = 0;\ else if (X > 0xFFFF)\ I = NTREEBINS-1;\ else {\ unsigned int K;\ __asm__("bsrl %1,%0\n\t" : "=r" (K) : "rm" (X));\ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ }\ } #else /* GNUC */ #define compute_tree_index(S, I)\ {\ size_t X = S >> TREEBIN_SHIFT;\ if (X == 0)\ I = 0;\ else if (X > 0xFFFF)\ I = NTREEBINS-1;\ else {\ unsigned int Y = (unsigned int)X;\ unsigned int N = ((Y - 0x100) >> 16) & 8;\ unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ N += K;\ N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ K = 14 - N + ((Y <<= K) >> 15);\ I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ }\ } #endif /* GNUC */ /* Bit representing maximum resolved size in a treebin at i */ #define bit_for_tree_index(i) \ (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) /* Shift placing maximum resolved bit in a treebin at i as sign bit */ #define leftshift_for_tree_index(i) \ ((i == NTREEBINS-1)? 0 : \ ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) /* The size of the smallest chunk held in bin with index i */ #define minsize_for_tree_index(i) \ ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) /* ------------------------ Operations on bin maps ----------------------- */ /* bit corresponding to given index */ #define idx2bit(i) ((binmap_t)(1) << (i)) /* Mark/Clear bits with given index */ #define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) #define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) #define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) #define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) #define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) #define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) /* index corresponding to given bit */ #if defined(__GNUC__) && defined(i386) #define compute_bit2idx(X, I)\ {\ unsigned int J;\ __asm__("bsfl %1,%0\n\t" : "=r" (J) : "rm" (X));\ I = (bindex_t)J;\ } #else /* GNUC */ #if USE_BUILTIN_FFS #define compute_bit2idx(X, I) I = ffs(X)-1 #else /* USE_BUILTIN_FFS */ #define compute_bit2idx(X, I)\ {\ unsigned int Y = X - 1;\ unsigned int K = Y >> (16-4) & 16;\ unsigned int N = K; Y >>= K;\ N += K = Y >> (8-3) & 8; Y >>= K;\ N += K = Y >> (4-2) & 4; Y >>= K;\ N += K = Y >> (2-1) & 2; Y >>= K;\ N += K = Y >> (1-0) & 1; Y >>= K;\ I = (bindex_t)(N + Y);\ } #endif /* USE_BUILTIN_FFS */ #endif /* GNUC */ /* isolate the least set bit of a bitmap */ #define least_bit(x) ((x) & -(x)) /* mask with all bits to left of least bit of x on */ #define left_bits(x) ((x<<1) | -(x<<1)) /* mask with all bits to left of or equal to least bit of x on */ #define same_or_left_bits(x) ((x) | -(x)) /* ----------------------- Runtime Check Support ------------------------- */ /* For security, the main invariant is that malloc/free/etc never writes to a static address other than malloc_state, unless static malloc_state itself has been corrupted, which cannot occur via malloc (because of these checks). In essence this means that we believe all pointers, sizes, maps etc held in malloc_state, but check all of those linked or offsetted from other embedded data structures. These checks are interspersed with main code in a way that tends to minimize their run-time cost. When FOOTERS is defined, in addition to range checking, we also verify footer fields of inuse chunks, which can be used guarantee that the mstate controlling malloc/free is intact. This is a streamlined version of the approach described by William Robertson et al in "Run-time Detection of Heap-based Overflows" LISA'03 http://www.usenix.org/events/lisa03/tech/robertson.html The footer of an inuse chunk holds the xor of its mstate and a random seed, that is checked upon calls to free() and realloc(). This is (probablistically) unguessable from outside the program, but can be computed by any code successfully malloc'ing any chunk, so does not itself provide protection against code that has already broken security through some other means. Unlike Robertson et al, we always dynamically check addresses of all offset chunks (previous, next, etc). This turns out to be cheaper than relying on hashes. */ #if !INSECURE /* Check if address a is at least as high as any from MORECORE or MMAP */ #define ok_address(M, a) ((char*)(a) >= (M)->least_addr) /* Check if address of next chunk n is higher than base chunk p */ #define ok_next(p, n) ((char*)(p) < (char*)(n)) /* Check if p has its cinuse bit on */ #define ok_cinuse(p) cinuse(p) /* Check if p has its pinuse bit on */ #define ok_pinuse(p) pinuse(p) #else /* !INSECURE */ #define ok_address(M, a) (1) #define ok_next(b, n) (1) #define ok_cinuse(p) (1) #define ok_pinuse(p) (1) #endif /* !INSECURE */ #if (FOOTERS && !INSECURE) /* Check if (alleged) mstate m has expected magic field */ #define ok_magic(M) ((M)->magic == mparams.magic) #else /* (FOOTERS && !INSECURE) */ #define ok_magic(M) (1) #endif /* (FOOTERS && !INSECURE) */ /* In gcc, use __builtin_expect to minimize impact of checks */ #if !INSECURE #if defined(__GNUC__) && __GNUC__ >= 3 #define RTCHECK(e) __builtin_expect(e, 1) #else /* GNUC */ #define RTCHECK(e) (e) #endif /* GNUC */ #else /* !INSECURE */ #define RTCHECK(e) (1) #endif /* !INSECURE */ /* macros to set up inuse chunks with or without footers */ #if !FOOTERS #define mark_inuse_foot(M,p,s) /* Set cinuse bit and pinuse bit of next chunk */ #define set_inuse(M,p,s)\ ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) /* Set cinuse and pinuse of this chunk and pinuse of next chunk */ #define set_inuse_and_pinuse(M,p,s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) /* Set size, cinuse and pinuse bit of this chunk */ #define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) #else /* FOOTERS */ /* Set foot of inuse chunk to be xor of mstate and seed */ #define mark_inuse_foot(M,p,s)\ (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) #define get_mstate_for(p)\ ((mstate)(((mchunkptr)((char*)(p) +\ (chunksize(p))))->prev_foot ^ mparams.magic)) #define set_inuse(M,p,s)\ ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ mark_inuse_foot(M,p,s)) #define set_inuse_and_pinuse(M,p,s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ mark_inuse_foot(M,p,s)) #define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ mark_inuse_foot(M, p, s)) #endif /* !FOOTERS */ /* ---------------------------- setting mparams -------------------------- */ /* Initialize mparams */ static int init_mparams(void) { if (mparams.page_size == 0) { size_t s; mparams.default_mflags = USE_LOCK_BIT; #if (FOOTERS && !INSECURE) { #if USE_DEV_RANDOM int fd; unsigned char buf[sizeof(size_t)]; /* Try to use /dev/urandom, else fall back on using time */ if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && read(fd, buf, sizeof(buf)) == sizeof(buf)) { s = *((size_t *) buf); close(fd); } else #endif /* USE_DEV_RANDOM */ s = (size_t)(time(0) ^ (size_t)0x55555555U); s |= (size_t)8U; /* ensure nonzero */ s &= ~(size_t)7U; /* improve chances of fault for bad values */ } #else /* (FOOTERS && !INSECURE) */ s = (size_t)0x58585858U; #endif /* (FOOTERS && !INSECURE) */ ACQUIRE_MAGIC_INIT_LOCK(); if (mparams.magic == 0) { mparams.magic = s; /* Set up lock for main malloc area */ //INITIAL_LOCK(&gm->mutex); //gm->mflags = mparams.default_mflags; } RELEASE_MAGIC_INIT_LOCK(); mparams.page_size = malloc_getpagesize; mparams.granularity = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : mparams.page_size); /* Sanity-check configuration: size_t must be unsigned and as wide as pointer type. ints must be at least 4 bytes. alignment must be at least 8. Alignment, min chunk size, and page size must all be powers of 2. */ if ((sizeof(size_t) != sizeof(char*)) || (MAX_SIZE_T < MIN_CHUNK_SIZE) || (sizeof(int) < 4) || (MALLOC_ALIGNMENT < (size_t)8U) || ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || ((mparams.granularity & (mparams.granularity-SIZE_T_ONE)) != 0) || ((mparams.page_size & (mparams.page_size-SIZE_T_ONE)) != 0)) ABORT(NULL); } return 0; } /* support for mallopt */ static int change_mparam(int param_number, int value) { size_t val = (size_t)value; init_mparams(); switch(param_number) { case M_GRANULARITY: if (val >= mparams.page_size && ((val & (val-1)) == 0)) { mparams.granularity = val; return 1; } else return 0; default: return 0; } } #if DEBUG /* ------------------------- Debugging Support --------------------------- */ /* Check properties of any chunk, whether free, inuse, mmapped etc */ static void do_check_any_chunk(mstate m, mchunkptr p) { assert(m->user_data, (is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(m->user_data, ok_address(m, p)); } /* Check properties of top chunk */ static void do_check_top_chunk(mstate m, mchunkptr p) { msegmentptr sp = segment_holding(m, (char*)p); size_t sz = chunksize(p); assert(m->user_data, sp != 0); assert(m->user_data, (is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(m->user_data, ok_address(m, p)); assert(m->user_data, sz == m->topsize); assert(m->user_data, sz > 0); assert(m->user_data, sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); assert(m->user_data, pinuse(p)); assert(m->user_data, !next_pinuse(p)); } /* Check properties of inuse chunks */ static void do_check_inuse_chunk(mstate m, mchunkptr p) { do_check_any_chunk(m, p); assert(m->user_data, cinuse(p)); assert(m->user_data, next_pinuse(p)); /* If not pinuse, previous chunk has OK offset */ assert(m->user_data, pinuse(p) || next_chunk(prev_chunk(p)) == p); } /* Check properties of free chunks */ static void do_check_free_chunk(mstate m, mchunkptr p) { size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT); mchunkptr next = chunk_plus_offset(p, sz); do_check_any_chunk(m, p); assert(m->user_data, !cinuse(p)); assert(m->user_data, !next_pinuse(p)); if (p != m->dv && p != m->top) { if (sz >= MIN_CHUNK_SIZE) { assert(m->user_data, (sz & CHUNK_ALIGN_MASK) == 0); assert(m->user_data, is_aligned(chunk2mem(p))); assert(m->user_data, next->prev_foot == sz); assert(m->user_data, pinuse(p)); assert(m->user_data, next == m->top || cinuse(next)); assert(m->user_data, p->fd->bk == p); assert(m->user_data, p->bk->fd == p); } else /* markers are always of size SIZE_T_SIZE */ assert(m->user_data, sz == SIZE_T_SIZE); } } /* Check properties of malloced chunks at the point they are malloced */ static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { if (mem != 0) { mchunkptr p = mem2chunk(mem); size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT); do_check_inuse_chunk(m, p); assert(m->user_data, (sz & CHUNK_ALIGN_MASK) == 0); assert(m->user_data, sz >= MIN_CHUNK_SIZE); assert(m->user_data, sz >= s); /* size is less than MIN_CHUNK_SIZE more than request */ assert(m->user_data, sz < (s + MIN_CHUNK_SIZE)); } } /* Check a tree and its subtrees. */ static void do_check_tree(mstate m, tchunkptr t) { tchunkptr head = 0; tchunkptr u = t; bindex_t tindex = t->index; size_t tsize = chunksize(t); bindex_t idx; compute_tree_index(tsize, idx); assert(m->user_data, tindex == idx); assert(m->user_data, tsize >= MIN_LARGE_SIZE); assert(m->user_data, tsize >= minsize_for_tree_index(idx)); assert(m->user_data, (idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); do { /* traverse through chain of same-sized nodes */ do_check_any_chunk(m, ((mchunkptr)u)); assert(m->user_data, u->index == tindex); assert(m->user_data, chunksize(u) == tsize); assert(m->user_data, !cinuse(u)); assert(m->user_data, !next_pinuse(u)); assert(m->user_data, u->fd->bk == u); assert(m->user_data, u->bk->fd == u); if (u->parent == 0) { assert(m->user_data, u->child[0] == 0); assert(m->user_data, u->child[1] == 0); } else { assert(m->user_data, head == 0); /* only one node on chain has parent */ head = u; assert(m->user_data, u->parent != u); assert(m->user_data, u->parent->child[0] == u || u->parent->child[1] == u || *((tbinptr*)(u->parent)) == u); if (u->child[0] != 0) { assert(m->user_data, u->child[0]->parent == u); assert(m->user_data, u->child[0] != u); do_check_tree(m, u->child[0]); } if (u->child[1] != 0) { assert(m->user_data, u->child[1]->parent == u); assert(m->user_data, u->child[1] != u); do_check_tree(m, u->child[1]); } if (u->child[0] != 0 && u->child[1] != 0) { assert(m->user_data, chunksize(u->child[0]) < chunksize(u->child[1])); } } u = u->fd; } while (u != t); assert(m->user_data, head != 0); } /* Check all the chunks in a treebin. */ static void do_check_treebin(mstate m, bindex_t i) { tbinptr* tb = treebin_at(m, i); tchunkptr t = *tb; int empty = (m->treemap & (1U << i)) == 0; if (t == 0) assert(m->user_data, empty); if (!empty) do_check_tree(m, t); } /* Check all the chunks in a smallbin. */ static void do_check_smallbin(mstate m, bindex_t i) { sbinptr b = smallbin_at(m, i); mchunkptr p = b->bk; unsigned int empty = (m->smallmap & (1U << i)) == 0; if (p == b) assert(m->user_data, empty); if (!empty) { for (; p != b; p = p->bk) { size_t size = chunksize(p); mchunkptr q; /* each chunk claims to be free */ do_check_free_chunk(m, p); /* chunk belongs in bin */ assert(m->user_data, small_index(size) == i); assert(m->user_data, p->bk == b || chunksize(p->bk) == chunksize(p)); /* chunk is followed by an inuse chunk */ q = next_chunk(p); if (q->head != FENCEPOST_HEAD) do_check_inuse_chunk(m, q); } } } /* Find x in a bin. Used in other check functions. */ static int bin_find(mstate m, mchunkptr x) { size_t size = chunksize(x); if (is_small(size)) { bindex_t sidx = small_index(size); sbinptr b = smallbin_at(m, sidx); if (smallmap_is_marked(m, sidx)) { mchunkptr p = b; do { if (p == x) return 1; } while ((p = p->fd) != b); } } else { bindex_t tidx; compute_tree_index(size, tidx); if (treemap_is_marked(m, tidx)) { tchunkptr t = *treebin_at(m, tidx); size_t sizebits = size << leftshift_for_tree_index(tidx); while (t != 0 && chunksize(t) != size) { t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; sizebits <<= 1; } if (t != 0) { tchunkptr u = t; do { if (u == (tchunkptr)x) return 1; } while ((u = u->fd) != t); } } } return 0; } /* Traverse each chunk and check it; return total */ static size_t traverse_and_check(mstate m) { size_t sum = 0; if (is_initialized(m)) { msegmentptr s = &m->seg; sum += m->topsize + TOP_FOOT_SIZE; while (s != 0) { mchunkptr q = align_as_chunk(s->base); mchunkptr lastq = 0; assert(m->user_data, pinuse(q)); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { sum += chunksize(q); if (cinuse(q)) { assert(m->user_data, !bin_find(m, q)); do_check_inuse_chunk(m, q); } else { assert(m->user_data, q == m->dv || bin_find(m, q)); assert(m->user_data, lastq == 0 || cinuse(lastq)); /* Not 2 consecutive free */ do_check_free_chunk(m, q); } lastq = q; q = next_chunk(q); } s = s->next; } } return sum; } /* Check all properties of malloc_state. */ static void do_check_malloc_state(mstate m) { bindex_t i; size_t total; /* check bins */ for (i = 0; i < NSMALLBINS; ++i) do_check_smallbin(m, i); for (i = 0; i < NTREEBINS; ++i) do_check_treebin(m, i); if (m->dvsize != 0) { /* check dv chunk */ do_check_any_chunk(m, m->dv); assert(m->user_data, m->dvsize == chunksize(m->dv)); assert(m->user_data, m->dvsize >= MIN_CHUNK_SIZE); assert(m->user_data, bin_find(m, m->dv) == 0); } if (m->top != 0) { /* check top chunk */ do_check_top_chunk(m, m->top); assert(m->user_data, m->topsize == chunksize(m->top)); assert(m->user_data, m->topsize > 0); assert(m->user_data, bin_find(m, m->top) == 0); } total = traverse_and_check(m); assert(m->user_data, total <= m->footprint); assert(m->user_data, m->footprint <= m->max_footprint); } #endif /* DEBUG */ /* ----------------------------- statistics ------------------------------ */ #if !NO_MALLINFO static struct mallinfo internal_mallinfo(mstate m) { struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (!PREACTION(m)) { check_malloc_state(m); if (is_initialized(m)) { size_t nfree = SIZE_T_ONE; /* top always free */ size_t mfree = m->topsize + TOP_FOOT_SIZE; size_t sum = mfree; msegmentptr s = &m->seg; while (s != 0) { mchunkptr q = align_as_chunk(s->base); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { size_t sz = chunksize(q); sum += sz; if (!cinuse(q)) { mfree += sz; ++nfree; } q = next_chunk(q); } s = s->next; } nm.arena = sum; nm.ordblks = nfree; nm.hblkhd = m->footprint - sum; nm.usmblks = m->max_footprint; nm.uordblks = m->footprint - mfree; nm.fordblks = mfree; nm.keepcost = m->topsize; } POSTACTION(m); } return nm; } #endif /* !NO_MALLINFO */ static void internal_malloc_stats(mstate m, size_t *ret_maxfp, size_t *ret_fp, size_t *ret_used) { if (!PREACTION(m)) { size_t maxfp = 0; size_t fp = 0; size_t used = 0; check_malloc_state(m); if (is_initialized(m)) { msegmentptr s = &m->seg; maxfp = m->max_footprint; fp = m->footprint; used = fp - (m->topsize + TOP_FOOT_SIZE); while (s != 0) { mchunkptr q = align_as_chunk(s->base); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { if (!cinuse(q)) used -= chunksize(q); q = next_chunk(q); } s = s->next; } } if (ret_maxfp || ret_fp || ret_used) { if (ret_maxfp) { *ret_maxfp = maxfp; } if (ret_fp) { *ret_fp = fp; } if (ret_used) { *ret_used = used; } } else { PRINT((m->user_data, "max system bytes = %10lu\n", (unsigned long)(maxfp))); PRINT((m->user_data, "system bytes = %10lu\n", (unsigned long)(fp))); PRINT((m->user_data, "in use bytes = %10lu\n", (unsigned long)(used))); } POSTACTION(m); } } /* ----------------------- Operations on smallbins ----------------------- */ /* Various forms of linking and unlinking are defined as macros. Even the ones for trees, which are very long but have very short typical paths. This is ugly but reduces reliance on inlining support of compilers. */ /* Link a free chunk into a smallbin */ #define insert_small_chunk(M, P, S) {\ bindex_t I = small_index(S);\ mchunkptr B = smallbin_at(M, I);\ mchunkptr F = B;\ assert((M)->user_data, S >= MIN_CHUNK_SIZE);\ if (!smallmap_is_marked(M, I))\ mark_smallmap(M, I);\ else if (RTCHECK(ok_address(M, B->fd)))\ F = B->fd;\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ B->fd = P;\ F->bk = P;\ P->fd = F;\ P->bk = B;\ } /* Unlink a chunk from a smallbin */ #define unlink_small_chunk(M, P, S) {\ mchunkptr F = P->fd;\ mchunkptr B = P->bk;\ bindex_t I = small_index(S);\ assert((M)->user_data, P != B);\ assert((M)->user_data, P != F);\ assert((M)->user_data, chunksize(P) == small_index2size(I));\ if (F == B)\ clear_smallmap(M, I);\ else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\ (B == smallbin_at(M,I) || ok_address(M, B)))) {\ F->bk = B;\ B->fd = F;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ } /* Unlink the first chunk from a smallbin */ #define unlink_first_small_chunk(M, B, P, I) {\ mchunkptr F = P->fd;\ assert((M)->user_data, P != B);\ assert((M)->user_data, P != F);\ assert((M)->user_data, chunksize(P) == small_index2size(I));\ if (B == F)\ clear_smallmap(M, I);\ else if (RTCHECK(ok_address(M, F))) {\ B->fd = F;\ F->bk = B;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ } /* Replace dv node, binning the old one */ /* Used only when dvsize known to be small */ #define replace_dv(M, P, S) {\ size_t DVS = M->dvsize;\ if (DVS != 0) {\ mchunkptr DV = M->dv;\ assert((M)->user_data, is_small(DVS));\ insert_small_chunk(M, DV, DVS);\ }\ M->dvsize = S;\ M->dv = P;\ } /* ------------------------- Operations on trees ------------------------- */ /* Insert chunk into tree */ #define insert_large_chunk(M, X, S) {\ tbinptr* H;\ bindex_t I;\ compute_tree_index(S, I);\ H = treebin_at(M, I);\ X->index = I;\ X->child[0] = X->child[1] = 0;\ if (!treemap_is_marked(M, I)) {\ mark_treemap(M, I);\ *H = X;\ X->parent = (tchunkptr)H;\ X->fd = X->bk = X;\ }\ else {\ tchunkptr T = *H;\ size_t K = S << leftshift_for_tree_index(I);\ for (;;) {\ if (chunksize(T) != S) {\ tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ K <<= 1;\ if (*C != 0)\ T = *C;\ else if (RTCHECK(ok_address(M, C))) {\ *C = X;\ X->parent = T;\ X->fd = X->bk = X;\ break;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ break;\ }\ }\ else {\ tchunkptr F = T->fd;\ if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ T->fd = F->bk = X;\ X->fd = F;\ X->bk = T;\ X->parent = 0;\ break;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ break;\ }\ }\ }\ }\ } /* Unlink steps: 1. If x is a chained node, unlink it from its same-sized fd/bk links and choose its bk node as its replacement. 2. If x was the last node of its size, but not a leaf node, it must be replaced with a leaf node (not merely one with an open left or right), to make sure that lefts and rights of descendents correspond properly to bit masks. We use the rightmost descendent of x. We could use any other leaf, but this is easy to locate and tends to counteract removal of leftmosts elsewhere, and so keeps paths shorter than minimally guaranteed. This doesn't loop much because on average a node in a tree is near the bottom. 3. If x is the base of a chain (i.e., has parent links) relink x's parent and children to x's replacement (or null if none). */ #define unlink_large_chunk(M, X) {\ tchunkptr XP = X->parent;\ tchunkptr R;\ if (X->bk != X) {\ tchunkptr F = X->fd;\ R = X->bk;\ if (RTCHECK(ok_address(M, F))) {\ F->bk = R;\ R->fd = F;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ else {\ tchunkptr* RP;\ if (((R = *(RP = &(X->child[1]))) != 0) ||\ ((R = *(RP = &(X->child[0]))) != 0)) {\ tchunkptr* CP;\ while ((*(CP = &(R->child[1])) != 0) ||\ (*(CP = &(R->child[0])) != 0)) {\ R = *(RP = CP);\ }\ if (RTCHECK(ok_address(M, RP)))\ *RP = 0;\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ }\ if (XP != 0) {\ tbinptr* H = treebin_at(M, X->index);\ if (X == *H) {\ if ((*H = R) == 0) \ clear_treemap(M, X->index);\ }\ else if (RTCHECK(ok_address(M, XP))) {\ if (XP->child[0] == X) \ XP->child[0] = R;\ else \ XP->child[1] = R;\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ if (R != 0) {\ if (RTCHECK(ok_address(M, R))) {\ tchunkptr C0, C1;\ R->parent = XP;\ if ((C0 = X->child[0]) != 0) {\ if (RTCHECK(ok_address(M, C0))) {\ R->child[0] = C0;\ C0->parent = R;\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ }\ if ((C1 = X->child[1]) != 0) {\ if (RTCHECK(ok_address(M, C1))) {\ R->child[1] = C1;\ C1->parent = R;\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ } /* Relays to large vs small bin operations */ #define insert_chunk(M, P, S)\ if (is_small(S)) insert_small_chunk(M, P, S)\ else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } #define unlink_chunk(M, P, S)\ if (is_small(S)) unlink_small_chunk(M, P, S)\ else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } /* Relays to internal calls to malloc/free from realloc, memalign etc */ #define internal_malloc(m, b) mspace_malloc(m, b) #define internal_free(m, mem) mspace_free(m,mem); /* -------------------------- mspace management -------------------------- */ /* Initialize top chunk and its size */ static void init_top(mstate m, mchunkptr p, size_t psize) { /* Ensure alignment */ size_t offset = align_offset(chunk2mem(p)); p = (mchunkptr)((char*)p + offset); psize -= offset; m->top = p; m->topsize = psize; p->head = psize | PINUSE_BIT; /* set size of fake trailing chunk holding overhead space only once */ chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; } /* Initialize bins for a new mstate that is otherwise zeroed out */ static void init_bins(mstate m) { /* Establish circular links for smallbins */ bindex_t i; for (i = 0; i < NSMALLBINS; ++i) { sbinptr bin = smallbin_at(m,i); bin->fd = bin->bk = bin; } } #if PROCEED_ON_ERROR /* default corruption action */ static void reset_on_error(mstate m) { int i; ++malloc_corruption_error_count; /* Reinitialize fields to forget about all memory */ m->smallbins = m->treebins = 0; m->dvsize = m->topsize = 0; m->seg.base = 0; m->seg.size = 0; m->seg.next = 0; m->top = m->dv = 0; for (i = 0; i < NTREEBINS; ++i) *treebin_at(m, i) = 0; init_bins(m); } #endif /* PROCEED_ON_ERROR */ #if 0 /* Allocate chunk and prepend remainder with chunk in successor base. */ static void* prepend_alloc(mstate m, char* newbase, char* oldbase, size_t nb) { mchunkptr p = align_as_chunk(newbase); mchunkptr oldfirst = align_as_chunk(oldbase); size_t psize = (char*)oldfirst - (char*)p; mchunkptr q = chunk_plus_offset(p, nb); size_t qsize = psize - nb; set_size_and_pinuse_of_inuse_chunk(m, p, nb); assert(m->user_data, (char*)oldfirst > (char*)q); assert(m->user_data, pinuse(oldfirst)); assert(m->user_data, qsize >= MIN_CHUNK_SIZE); /* consolidate remainder with first chunk of old base */ if (oldfirst == m->top) { size_t tsize = m->topsize += qsize; m->top = q; q->head = tsize | PINUSE_BIT; check_top_chunk(m, q); } else if (oldfirst == m->dv) { size_t dsize = m->dvsize += qsize; m->dv = q; set_size_and_pinuse_of_free_chunk(q, dsize); } else { if (!cinuse(oldfirst)) { size_t nsize = chunksize(oldfirst); unlink_chunk(m, oldfirst, nsize); oldfirst = chunk_plus_offset(oldfirst, nsize); qsize += nsize; } set_free_with_pinuse(q, qsize, oldfirst); insert_chunk(m, q, qsize); check_free_chunk(m, q); } check_malloced_chunk(m, chunk2mem(p), nb); return chunk2mem(p); } #endif /* -------------------------- System allocation -------------------------- */ /* Get memory from system using MORECORE or MMAP */ static void* sys_alloc(mstate m, size_t nb) { MALLOC_FAILURE_ACTION; return 0; } /* ---------------------------- malloc support --------------------------- */ /* allocate a large request from the best fitting chunk in a treebin */ static void* tmalloc_large(mstate m, size_t nb) { tchunkptr v = 0; size_t rsize = -nb; /* Unsigned negation */ tchunkptr t; bindex_t idx; compute_tree_index(nb, idx); if ((t = *treebin_at(m, idx)) != 0) { /* Traverse tree for this bin looking for node with size == nb */ size_t sizebits = nb << leftshift_for_tree_index(idx); tchunkptr rst = 0; /* The deepest untaken right subtree */ for (;;) { tchunkptr rt; size_t trem = chunksize(t) - nb; if (trem < rsize) { v = t; if ((rsize = trem) == 0) break; } rt = t->child[1]; t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; if (rt != 0 && rt != t) rst = rt; if (t == 0) { t = rst; /* set t to least subtree holding sizes > nb */ break; } sizebits <<= 1; } } if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; if (leftbits != 0) { bindex_t i; binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); t = *treebin_at(m, i); } } while (t != 0) { /* find smallest of tree or subtree */ size_t trem = chunksize(t) - nb; if (trem < rsize) { rsize = trem; v = t; } t = leftmost_child(t); } /* If dv is a better fit, return 0 so malloc will use it */ if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { if (RTCHECK(ok_address(m, v))) { /* split */ mchunkptr r = chunk_plus_offset(v, nb); assert(m->user_data, chunksize(v) == rsize + nb); if (RTCHECK(ok_next(v, r))) { unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(m, v, (rsize + nb)); else { set_size_and_pinuse_of_inuse_chunk(m, v, nb); set_size_and_pinuse_of_free_chunk(r, rsize); insert_chunk(m, r, rsize); } return chunk2mem(v); } } CORRUPTION_ERROR_ACTION(m); } return 0; } /* allocate a small request from the best fitting chunk in a treebin */ static void* tmalloc_small(mstate m, size_t nb) { tchunkptr t, v; size_t rsize; bindex_t i; binmap_t leastbit = least_bit(m->treemap); compute_bit2idx(leastbit, i); v = t = *treebin_at(m, i); rsize = chunksize(t) - nb; while ((t = leftmost_child(t)) != 0) { size_t trem = chunksize(t) - nb; if (trem < rsize) { rsize = trem; v = t; } } if (RTCHECK(ok_address(m, v))) { mchunkptr r = chunk_plus_offset(v, nb); assert(m->user_data, chunksize(v) == rsize + nb); if (RTCHECK(ok_next(v, r))) { unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(m, v, (rsize + nb)); else { set_size_and_pinuse_of_inuse_chunk(m, v, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(m, r, rsize); } return chunk2mem(v); } } CORRUPTION_ERROR_ACTION(m); return 0; } /* --------------------------- realloc support --------------------------- */ static void* internal_realloc(mstate m, void* oldmem, size_t bytes) { if (bytes >= MAX_REQUEST) { MALLOC_FAILURE_ACTION; return 0; } if (!PREACTION(m)) { mchunkptr oldp = mem2chunk(oldmem); size_t oldsize = chunksize(oldp); mchunkptr next = chunk_plus_offset(oldp, oldsize); mchunkptr newp = 0; void* extra = 0; /* Try to either shrink or extend into top. Else malloc-copy-free */ if (RTCHECK(ok_address(m, oldp) && ok_cinuse(oldp) && ok_next(oldp, next) && ok_pinuse(next))) { size_t nb = request2size(bytes); if (oldsize >= nb) { /* already big enough */ size_t rsize = oldsize - nb; newp = oldp; if (rsize >= MIN_CHUNK_SIZE) { mchunkptr remainder = chunk_plus_offset(newp, nb); set_inuse(m, newp, nb); set_inuse(m, remainder, rsize); extra = chunk2mem(remainder); } } else if (next == m->top && oldsize + m->topsize > nb) { /* Expand into top */ size_t newsize = oldsize + m->topsize; size_t newtopsize = newsize - nb; mchunkptr newtop = chunk_plus_offset(oldp, nb); set_inuse(m, oldp, nb); newtop->head = newtopsize |PINUSE_BIT; m->top = newtop; m->topsize = newtopsize; newp = oldp; } } else { USAGE_ERROR_ACTION(m, oldmem); POSTACTION(m); return 0; } POSTACTION(m); if (newp != 0) { if (extra != 0) { internal_free(m, extra); } check_inuse_chunk(m, newp); return chunk2mem(newp); } else { void* newmem = internal_malloc(m, bytes); if (newmem != 0) { size_t oc = oldsize - overhead_for(oldp); MEMCPY(newmem, oldmem, (oc < bytes)? oc : bytes); internal_free(m, oldmem); } return newmem; } } return 0; } /* --------------------------- memalign support -------------------------- */ static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { if (alignment <= MALLOC_ALIGNMENT) /* Can just use malloc */ return internal_malloc(m, bytes); if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ alignment = MIN_CHUNK_SIZE; if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ size_t a = MALLOC_ALIGNMENT << 1; while (a < alignment) a <<= 1; alignment = a; } if (bytes >= MAX_REQUEST - alignment) { if (m != 0) { /* Test isn't needed but avoids compiler warning */ MALLOC_FAILURE_ACTION; } } else { size_t nb = request2size(bytes); size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; char* mem = (char*)internal_malloc(m, req); if (mem != 0) { void* leader = 0; void* trailer = 0; mchunkptr p = mem2chunk(mem); if (PREACTION(m)) return 0; if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */ /* Find an aligned spot inside chunk. Since we need to give back leading space in a chunk of at least MIN_CHUNK_SIZE, if the first calculation places us at a spot with less than MIN_CHUNK_SIZE leader, we can move to the next aligned spot. We've allocated enough total room so that this is always possible. */ char* br = (char*)mem2chunk((size_t)(((size_t)(mem + alignment - SIZE_T_ONE)) & -alignment)); char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? br : br+alignment; mchunkptr newp = (mchunkptr)pos; size_t leadsize = pos - (char*)(p); size_t newsize = chunksize(p) - leadsize; /* Otherwise, give back leader, use the rest */ set_inuse(m, newp, newsize); set_inuse(m, p, leadsize); leader = chunk2mem(p); p = newp; } assert(m->user_data, chunksize(p) >= nb); assert(m->user_data, (((size_t)(chunk2mem(p))) % alignment) == 0); check_inuse_chunk(m, p); POSTACTION(m); if (leader != 0) { internal_free(m, leader); } if (trailer != 0) { internal_free(m, trailer); } return chunk2mem(p); } } return 0; } /* ----------------------------- user mspaces ---------------------------- */ static mstate init_user_mstate(char* tbase, size_t tsize, void *user_data) { size_t msize = pad_request(sizeof(struct malloc_state)); mchunkptr mn; mchunkptr msp = align_as_chunk(tbase); mstate m = (mstate)(chunk2mem(msp)); MEMCLEAR(m, msize); INITIAL_LOCK(&m->mutex); msp->head = (msize|PINUSE_BIT|CINUSE_BIT); m->seg.base = m->least_addr = tbase; m->seg.size = m->footprint = m->max_footprint = tsize; m->magic = mparams.magic; m->mflags = mparams.default_mflags; m->user_data = user_data; init_bins(m); mn = next_chunk(mem2chunk(m)); init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); check_top_chunk(m, m->top); return m; } mspace create_mspace_with_base(void* base, size_t capacity, int locked, void *user_data) { mstate m = 0; size_t msize = pad_request(sizeof(struct malloc_state)); init_mparams(); /* Ensure pagesize etc initialized */ if (capacity > msize + TOP_FOOT_SIZE && capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { m = init_user_mstate((char*)base, capacity, user_data); set_lock(m, locked); } return (mspace)m; } /* mspace versions of routines are near-clones of the global versions. This is not so nice but better than the alternatives. */ void* mspace_malloc(mspace msp, size_t bytes) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } if (!PREACTION(ms)) { void* mem; size_t nb; if (bytes <= MAX_SMALL_REQUEST) { bindex_t idx; binmap_t smallbits; nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); idx = small_index(nb); smallbits = ms->smallmap >> idx; if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ mchunkptr b, p; idx += ~smallbits & 1; /* Uses next bin if idx empty */ b = smallbin_at(ms, idx); p = b->fd; assert(ms->user_data, chunksize(p) == small_index2size(idx)); unlink_first_small_chunk(ms, b, p, idx); set_inuse_and_pinuse(ms, p, small_index2size(idx)); mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (nb > ms->dvsize) { if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ mchunkptr b, p, r; size_t rsize; bindex_t i; binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); b = smallbin_at(ms, i); p = b->fd; assert(ms->user_data, chunksize(p) == small_index2size(i)); unlink_first_small_chunk(ms, b, p, i); rsize = small_index2size(i) - nb; /* Fit here cannot be remainderless if 4byte sizes */ if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(ms, p, small_index2size(i)); else { set_size_and_pinuse_of_inuse_chunk(ms, p, nb); r = chunk_plus_offset(p, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(ms, r, rsize); } mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { check_malloced_chunk(ms, mem, nb); goto postaction; } } } else if (bytes >= MAX_REQUEST) nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ else { nb = pad_request(bytes); if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { check_malloced_chunk(ms, mem, nb); goto postaction; } } if (nb <= ms->dvsize) { size_t rsize = ms->dvsize - nb; mchunkptr p = ms->dv; if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ mchunkptr r = ms->dv = chunk_plus_offset(p, nb); ms->dvsize = rsize; set_size_and_pinuse_of_free_chunk(r, rsize); set_size_and_pinuse_of_inuse_chunk(ms, p, nb); } else { /* exhaust dv */ size_t dvs = ms->dvsize; ms->dvsize = 0; ms->dv = 0; set_inuse_and_pinuse(ms, p, dvs); } mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (nb < ms->topsize) { /* Split top */ size_t rsize = ms->topsize -= nb; mchunkptr p = ms->top; mchunkptr r = ms->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; set_size_and_pinuse_of_inuse_chunk(ms, p, nb); mem = chunk2mem(p); check_top_chunk(ms, ms->top); check_malloced_chunk(ms, mem, nb); goto postaction; } mem = sys_alloc(ms, nb); postaction: POSTACTION(ms); return mem; } return 0; } void mspace_free(mspace msp, void* mem) { if (mem != 0) { mchunkptr p = mem2chunk(mem); #if FOOTERS mstate fm = get_mstate_for(p); #else /* FOOTERS */ mstate fm = (mstate)msp; #endif /* FOOTERS */ if (!ok_magic(fm)) { USAGE_ERROR_ACTION(fm, p); return; } if (!PREACTION(fm)) { check_inuse_chunk(fm, p); if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) { size_t psize = chunksize(p); mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { size_t prevsize = p->prev_foot; mchunkptr prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ if (p != fm->dv) { unlink_chunk(fm, p, prevsize); } else if ((next->head & INUSE_BITS) == INUSE_BITS) { fm->dvsize = psize; set_free_with_pinuse(p, psize, next); goto postaction; } } else goto erroraction; } if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { if (!cinuse(next)) { /* consolidate forward */ if (next == fm->top) { size_t tsize = fm->topsize += psize; fm->top = p; p->head = tsize | PINUSE_BIT; if (p == fm->dv) { fm->dv = 0; fm->dvsize = 0; } goto postaction; } else if (next == fm->dv) { size_t dsize = fm->dvsize += psize; fm->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); goto postaction; } else { size_t nsize = chunksize(next); psize += nsize; unlink_chunk(fm, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == fm->dv) { fm->dvsize = psize; goto postaction; } } } else set_free_with_pinuse(p, psize, next); insert_chunk(fm, p, psize); check_free_chunk(fm, p); goto postaction; } } erroraction: USAGE_ERROR_ACTION(fm, p); postaction: POSTACTION(fm); } } } void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { void* mem; size_t req = 0; mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } if (n_elements != 0) { req = n_elements * elem_size; if (((n_elements | elem_size) & ~(size_t)0xffff) && (req / n_elements != elem_size)) req = MAX_SIZE_T; /* force downstream failure on overflow */ } mem = internal_malloc(ms, req); if (mem != 0 && calloc_must_clear(mem2chunk(mem))) MEMCLEAR(mem, req); return mem; } void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { if (oldmem == 0) return mspace_malloc(msp, bytes); #ifdef REALLOC_ZERO_BYTES_FREES if (bytes == 0) { mspace_free(msp, oldmem); return 0; } #endif /* REALLOC_ZERO_BYTES_FREES */ else { #if FOOTERS mchunkptr p = mem2chunk(oldmem); mstate ms = get_mstate_for(p); #else /* FOOTERS */ mstate ms = (mstate)msp; #endif /* FOOTERS */ if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } return internal_realloc(ms, oldmem, bytes); } } void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } return internal_memalign(ms, alignment, bytes); } void mspace_malloc_stats_return(mspace msp, size_t *ret_maxfp, size_t *ret_fp, size_t *ret_used) { mstate ms = (mstate)msp; if (ok_magic(ms)) { internal_malloc_stats(ms, ret_maxfp, ret_fp, ret_used); } else { USAGE_ERROR_ACTION(ms,ms); } } void mspace_malloc_stats(mspace msp) { mspace_malloc_stats_return(msp, NULL, NULL, NULL); } size_t mspace_footprint(mspace msp) { size_t result; mstate ms = (mstate)msp; if (ok_magic(ms)) { result = ms->footprint; } USAGE_ERROR_ACTION(ms,ms); return result; } size_t mspace_max_footprint(mspace msp) { size_t result; mstate ms = (mstate)msp; if (ok_magic(ms)) { result = ms->max_footprint; } USAGE_ERROR_ACTION(ms,ms); return result; } #if !NO_MALLINFO struct mallinfo mspace_mallinfo(mspace msp) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); } return internal_mallinfo(ms); } #endif /* NO_MALLINFO */ int mspace_mallopt(int param_number, int value) { return change_mparam(param_number, value); } xserver-xorg-video-qxl-0.1.5+git20200331/src/mspace.h000066400000000000000000000113021361660122400216760ustar00rootroot00000000000000#ifndef _H_MSPACE #define _H_MSPACE #define NO_MALLINFO 0 #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ //typedef unsigned long size_t; typedef void (*mspace_abort_t)(void *user_data); typedef void (*mspace_print_t)(void *user_data, const char *format, ...) __attribute__((format(gnu_printf, 2, 3))); void mspace_set_abort_func(mspace_abort_t f); void mspace_set_print_func(mspace_print_t f); void default_abort_func(void *user_data); void default_print_func(void *user_data, const char *format, ...); /* mspace is an opaque type representing an independent region of space that supports mspace_malloc, etc. */ typedef void* mspace; /* create_mspace creates and returns a new independent space with the given initial capacity, or, if 0, the default granularity size. It returns null if there is no system memory available to create the space. If argument locked is non-zero, the space uses a separate lock to control access. The capacity of the space will grow dynamically as needed to service mspace_malloc requests. You can control the sizes of incremental increases of this space by compiling with a different DEFAULT_GRANULARITY or dynamically setting with mallopt(M_GRANULARITY, value). */ //mspace create_mspace(size_t capacity, int locked); /* destroy_mspace destroys the given space, and attempts to return all of its memory back to the system, returning the total number of bytes freed. After destruction, the results of access to all memory used by the space become undefined. */ //size_t destroy_mspace(mspace msp); /* create_mspace_with_base uses the memory supplied as the initial base of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this space is used for bookkeeping, so the capacity must be at least this large. (Otherwise 0 is returned.) When this initial space is exhausted, additional memory will be obtained from the system. Destroying this space will deallocate all additionally allocated space (if possible) but not the initial base. */ mspace create_mspace_with_base(void* base, size_t capacity, int locked, void *user_data); /* mspace_malloc behaves as malloc, but operates within the given space. */ void* mspace_malloc(mspace msp, size_t bytes); /* mspace_free behaves as free, but operates within the given space. If compiled with FOOTERS==1, mspace_free is not actually needed. free may be called instead of mspace_free because freed chunks from any space are handled by their originating spaces. */ void mspace_free(mspace msp, void* mem); /* mspace_realloc behaves as realloc, but operates within the given space. If compiled with FOOTERS==1, mspace_realloc is not actually needed. realloc may be called instead of mspace_realloc because realloced chunks from any space are handled by their originating spaces. */ void* mspace_realloc(mspace msp, void* mem, size_t newsize); /* mspace_calloc behaves as calloc, but operates within the given space. */ void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); /* mspace_memalign behaves as memalign, but operates within the given space. */ void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); /* mspace_independent_calloc behaves as independent_calloc, but operates within the given space. */ //void** mspace_independent_calloc(mspace msp, size_t n_elements, // size_t elem_size, void* chunks[]); /* mspace_independent_comalloc behaves as independent_comalloc, but operates within the given space. */ //void** mspace_independent_comalloc(mspace msp, size_t n_elements, // size_t sizes[], void* chunks[]); /* mspace_footprint() returns the number of bytes obtained from the system for this space. */ size_t mspace_footprint(mspace msp); /* mspace_max_footprint() returns the peak number of bytes obtained from the system for this space. */ size_t mspace_max_footprint(mspace msp); #if !NO_MALLINFO /* mspace_mallinfo behaves as mallinfo, but reports properties of the given space. */ struct mallinfo mspace_mallinfo(mspace msp); #endif /* NO_MALLINFO */ /* mspace_malloc_stats behaves as malloc_stats, but reports properties of the given space. The return variant returns instead of printing the three quantities, maxfp, fp, and used. */ void mspace_malloc_stats(mspace msp); void mspace_malloc_stats_return(mspace msp, size_t *ret_maxfp, size_t *ret_fp, size_t *ret_used); /* mspace_trim behaves as malloc_trim, but operates within the given space. */ //int mspace_trim(mspace msp, size_t pad); /* An alias for mallopt. */ int mspace_mallopt(int, int); #ifdef __cplusplus }; /* end of extern "C" */ #endif /* __cplusplus */ #endif xserver-xorg-video-qxl-0.1.5+git20200331/src/murmurhash3.c000066400000000000000000000174541361660122400227150ustar00rootroot00000000000000//----------------------------------------------------------------------------- // MurmurHash3 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code. // Note - The x86 and x64 versions do _not_ produce the same results, as the // algorithms are optimized for their respective platforms. You can still // compile and run any of them on any platform, but your performance with the // non-native version will be less than optimal. #include "murmurhash3.h" //----------------------------------------------------------------------------- // Platform-specific functions and macros // Microsoft Visual Studio #if defined(_MSC_VER) #define FORCE_INLINE __forceinline #include #define ROTL32(x,y) _rotl(x,y) #define ROTL64(x,y) _rotl64(x,y) #define BIG_CONSTANT(x) (x) // Other compilers #else // defined(_MSC_VER) #define FORCE_INLINE __attribute__((always_inline)) static inline uint32_t rotl32 ( uint32_t x, int8_t r ) { return (x << r) | (x >> (32 - r)); } static inline uint64_t rotl64 ( uint64_t x, int8_t r ) { return (x << r) | (x >> (64 - r)); } #define ROTL32(x,y) rotl32(x,y) #define ROTL64(x,y) rotl64(x,y) #define BIG_CONSTANT(x) (x##LLU) #endif // !defined(_MSC_VER) //----------------------------------------------------------------------------- // Block read - if your platform needs to do endian-swapping or can only // handle aligned reads, do the conversion here inline static FORCE_INLINE uint32_t getblock_32 ( const uint32_t * p, int i ) { return p[i]; } inline static FORCE_INLINE uint64_t getblock_64 ( const uint64_t * p, int i ) { return p[i]; } //----------------------------------------------------------------------------- // Finalization mix - force all bits of a hash block to avalanche inline static FORCE_INLINE uint32_t fmix_32 ( uint32_t h ) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } //---------- inline static FORCE_INLINE uint64_t fmix_64 ( uint64_t k ) { k ^= k >> 33; k *= BIG_CONSTANT(0xff51afd7ed558ccd); k ^= k >> 33; k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); k ^= k >> 33; return k; } //----------------------------------------------------------------------------- void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out ) { const uint8_t * data = (const uint8_t*)key; const int nblocks = len / 4; uint32_t h1 = seed; uint32_t c1 = 0xcc9e2d51; uint32_t c2 = 0x1b873593; const uint32_t * blocks; const uint8_t * tail; uint32_t k1; int i; //---------- // body blocks = (const uint32_t *)(data + nblocks*4); for(i = -nblocks; i; i++) { k1 = getblock_32(blocks,i); k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; h1 = ROTL32(h1,13); h1 = h1*5+0xe6546b64; } //---------- // tail tail = (const uint8_t*)(data + nblocks*4); k1 = 0; switch(len & 3) { case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0]; k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; }; //---------- // finalization h1 ^= len; h1 = fmix_32(h1); *(uint32_t*)out = h1; } //----------------------------------------------------------------------------- void MurmurHash3_x86_128 ( const void * key, const int len, uint32_t seed, void * out ) { const uint8_t * data = (const uint8_t*)key; const int nblocks = len / 16; uint32_t h1 = seed; uint32_t h2 = seed; uint32_t h3 = seed; uint32_t h4 = seed; uint32_t c1 = 0x239b961b; uint32_t c2 = 0xab0e9789; uint32_t c3 = 0x38b34ae5; uint32_t c4 = 0xa1e38b93; uint32_t k1; uint32_t k2; uint32_t k3; uint32_t k4; const uint32_t * blocks; const uint8_t * tail; int i; //---------- // body blocks = (const uint32_t *)(data + nblocks*16); for(i = -nblocks; i; i++) { k1 = getblock_32(blocks,i*4+0); k2 = getblock_32(blocks,i*4+1); k3 = getblock_32(blocks,i*4+2); k4 = getblock_32(blocks,i*4+3); k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; } //---------- // tail tail = (const uint8_t*)(data + nblocks*16); k1 = 0; k2 = 0; k3 = 0; k4 = 0; switch(len & 15) { case 15: k4 ^= tail[14] << 16; case 14: k4 ^= tail[13] << 8; case 13: k4 ^= tail[12] << 0; k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; case 12: k3 ^= tail[11] << 24; case 11: k3 ^= tail[10] << 16; case 10: k3 ^= tail[ 9] << 8; case 9: k3 ^= tail[ 8] << 0; k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; case 8: k2 ^= tail[ 7] << 24; case 7: k2 ^= tail[ 6] << 16; case 6: k2 ^= tail[ 5] << 8; case 5: k2 ^= tail[ 4] << 0; k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; case 4: k1 ^= tail[ 3] << 24; case 3: k1 ^= tail[ 2] << 16; case 2: k1 ^= tail[ 1] << 8; case 1: k1 ^= tail[ 0] << 0; k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; }; //---------- // finalization h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; h1 = fmix_32(h1); h2 = fmix_32(h2); h3 = fmix_32(h3); h4 = fmix_32(h4); h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; ((uint32_t*)out)[0] = h1; ((uint32_t*)out)[1] = h2; ((uint32_t*)out)[2] = h3; ((uint32_t*)out)[3] = h4; } //----------------------------------------------------------------------------- void MurmurHash3_x64_128 ( const void * key, const int len, const uint32_t seed, void * out ) { const uint8_t * data = (const uint8_t*)key; const int nblocks = len / 16; uint64_t h1 = seed; uint64_t h2 = seed; uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); const uint64_t * blocks; const uint8_t * tail; uint64_t k1; uint64_t k2; int i; //---------- // body blocks = (const uint64_t *)(data); for(i = 0; i < nblocks; i++) { k1 = getblock_64(blocks,i*2+0); k2 = getblock_64(blocks,i*2+1); k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; } //---------- // tail tail = (const uint8_t*)(data + nblocks*16); k1 = 0; k2 = 0; switch(len & 15) { case 15: k2 ^= ((uint64_t)tail[14]) << 48; case 14: k2 ^= ((uint64_t)tail[13]) << 40; case 13: k2 ^= ((uint64_t)tail[12]) << 32; case 12: k2 ^= ((uint64_t)tail[11]) << 24; case 11: k2 ^= ((uint64_t)tail[10]) << 16; case 10: k2 ^= ((uint64_t)tail[ 9]) << 8; case 9: k2 ^= ((uint64_t)tail[ 8]) << 0; k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; case 8: k1 ^= ((uint64_t)tail[ 7]) << 56; case 7: k1 ^= ((uint64_t)tail[ 6]) << 48; case 6: k1 ^= ((uint64_t)tail[ 5]) << 40; case 5: k1 ^= ((uint64_t)tail[ 4]) << 32; case 4: k1 ^= ((uint64_t)tail[ 3]) << 24; case 3: k1 ^= ((uint64_t)tail[ 2]) << 16; case 2: k1 ^= ((uint64_t)tail[ 1]) << 8; case 1: k1 ^= ((uint64_t)tail[ 0]) << 0; k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; }; //---------- // finalization h1 ^= len; h2 ^= len; h1 += h2; h2 += h1; h1 = fmix_64(h1); h2 = fmix_64(h2); h1 += h2; h2 += h1; ((uint64_t*)out)[0] = h1; ((uint64_t*)out)[1] = h2; } //----------------------------------------------------------------------------- xserver-xorg-video-qxl-0.1.5+git20200331/src/murmurhash3.h000066400000000000000000000021751361660122400227140ustar00rootroot00000000000000// Source: http://code.google.com/p/smhasher/wiki/MurmurHash3 //----------------------------------------------------------------------------- // MurmurHash3 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code. #ifndef _MURMURHASH3_H_ #define _MURMURHASH3_H_ //----------------------------------------------------------------------------- // Platform-specific functions and macros // Microsoft Visual Studio #if defined(_MSC_VER) typedef unsigned char uint8_t; typedef unsigned long uint32_t; typedef unsigned __int64 uint64_t; // Other compilers #else // defined(_MSC_VER) #include #endif // !defined(_MSC_VER) //----------------------------------------------------------------------------- void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out ); void MurmurHash3_x86_128 ( const void * key, int len, uint32_t seed, void * out ); void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out ); //----------------------------------------------------------------------------- #endif // _MURMURHASH3_H_ xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl.h000066400000000000000000000457511361660122400212510ustar00rootroot00000000000000/* * Copyright 2008 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifndef QXL_H #define QXL_H #include #include #ifdef XSPICE #include #endif #include "compiler.h" #include "xf86.h" #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 6 #include "xf86Resources.h" #endif #include "xf86Cursor.h" #include "xf86_OSproc.h" #ifdef XV #include "xf86xv.h" #endif #include "xf86Crtc.h" #include "shadow.h" #include "micmap.h" #include "uxa/uxa.h" #include "list.h" #ifndef XSPICE #ifdef XSERVER_PCIACCESS #include "pciaccess.h" #endif #ifdef XSERVER_PLATFORM_BUS #include "xf86platformBus.h" #endif #include "fb.h" #include "vgaHW.h" #endif /* XSPICE */ #include "qxl_drmmode.h" #if (XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1, 11, 99, 903, 0)) typedef struct list xorg_list_t; #define xorg_list_init list_init #define xorg_list_add list_add #define xorg_list_del list_del #define xorg_list_for_each_entry list_for_each_entry #else typedef struct xorg_list xorg_list_t; #endif struct xf86_platform_device; #include "compat-api.h" #define hidden _X_HIDDEN #ifdef XSPICE #define QXL_NAME "spiceqxl" #define QXL_DRIVER_NAME "spiceqxl" #else #define QXL_NAME "qxl" #define QXL_DRIVER_NAME "qxl" #endif #define PCI_VENDOR_RED_HAT 0x1b36 #define PCI_CHIP_QXL_0100 0x0100 #define PCI_CHIP_QXL_01FF 0x01ff #pragma pack(push,1) struct qxl_ring_header { uint32_t num_items; uint32_t prod; uint32_t notify_on_prod; uint32_t cons; uint32_t notify_on_cons; }; #pragma pack(pop) typedef struct surface_cache_t surface_cache_t; typedef struct _qxl_screen_t qxl_screen_t; typedef struct { uint8_t generation; uint64_t start_phys_addr; uint64_t end_phys_addr; uint64_t start_virt_addr; uint64_t end_virt_addr; uint64_t high_bits; } qxl_memslot_t; typedef struct qxl_surface_t qxl_surface_t; /* * Config Options */ enum { OPTION_ENABLE_IMAGE_CACHE = 0, OPTION_ENABLE_FALLBACK_CACHE, OPTION_ENABLE_SURFACES, OPTION_DEBUG_RENDER_FALLBACKS, OPTION_NUM_HEADS, OPTION_SPICE_DEFERRED_FPS, #ifdef XSPICE OPTION_SPICE_PORT, OPTION_SPICE_TLS_PORT, OPTION_SPICE_ADDR, OPTION_SPICE_X509_DIR, OPTION_SPICE_SASL, OPTION_SPICE_AGENT_MOUSE, OPTION_SPICE_DISABLE_TICKETING, OPTION_SPICE_PASSWORD, OPTION_SPICE_X509_KEY_FILE, OPTION_SPICE_STREAMING_VIDEO, OPTION_SPICE_PLAYBACK_COMPRESSION, OPTION_SPICE_ZLIB_GLZ_WAN_COMPRESSION, OPTION_SPICE_JPEG_WAN_COMPRESSION, OPTION_SPICE_IMAGE_COMPRESSION, OPTION_SPICE_DISABLE_COPY_PASTE, OPTION_SPICE_IPV4_ONLY, OPTION_SPICE_IPV6_ONLY, OPTION_SPICE_X509_CERT_FILE, OPTION_SPICE_X509_KEY_PASSWORD, OPTION_SPICE_TLS_CIPHERS, OPTION_SPICE_CACERT_FILE, OPTION_SPICE_DH_FILE, OPTION_SPICE_EXIT_ON_DISCONNECT, OPTION_SPICE_PLAYBACK_FIFO_DIR, OPTION_SPICE_VDAGENT_ENABLED, OPTION_SPICE_VDAGENT_VIRTIO_PATH, OPTION_SPICE_VDAGENT_UINPUT_PATH, OPTION_SPICE_VDAGENT_UID, OPTION_SPICE_VDAGENT_GID, OPTION_FRAME_BUFFER_SIZE, OPTION_SURFACE_BUFFER_SIZE, OPTION_COMMAND_BUFFER_SIZE, OPTION_SPICE_SMARTCARD_FILE, OPTION_SPICE_VIDEO_CODECS, #endif OPTION_COUNT, }; enum { QXL_DEVICE_PRIMARY_UNDEFINED, QXL_DEVICE_PRIMARY_NONE, QXL_DEVICE_PRIMARY_CREATED, }; struct qxl_bo; /* * for relocations * dst_bo + dst_offset are the bo and offset into which the reloc is being written, * src_bo is the bo who's offset is being relocated. */ struct qxl_bo_funcs { struct qxl_bo *(*bo_alloc)(qxl_screen_t *qxl, unsigned long size, const char *name); struct qxl_bo *(*cmd_alloc)(qxl_screen_t *qxl, unsigned long size, const char *name); void *(*bo_map)(struct qxl_bo *bo); void (*bo_unmap)(struct qxl_bo *bo); void (*bo_decref)(qxl_screen_t *qxl, struct qxl_bo *bo); void (*bo_incref)(qxl_screen_t *qxl, struct qxl_bo *bo); void (*bo_output_bo_reloc)(qxl_screen_t *qxl, uint32_t dst_offset, struct qxl_bo *dst_bo, struct qxl_bo *src_bo); void (*write_command)(qxl_screen_t *qxl, uint32_t type, struct qxl_bo *bo); void (*update_area)(qxl_surface_t *surf, int x1, int y1, int x2, int y2); struct qxl_bo *(*create_primary)(qxl_screen_t *qxl, uint32_t width, uint32_t height, int32_t stride, uint32_t format); void (*destroy_primary)(qxl_screen_t *qxl, struct qxl_bo *primary_bo); qxl_surface_t *(*create_surface)(qxl_screen_t *qxl, int width, int height, int bpp); void (*destroy_surface)(qxl_surface_t *surf); void (*bo_output_surf_reloc)(qxl_screen_t *qxl, uint32_t dst_offset, struct qxl_bo *dst_bo, qxl_surface_t *surf); /* surface create / destroy */ }; void qxl_ums_setup_funcs(qxl_screen_t *qxl); void qxl_kms_setup_funcs(qxl_screen_t *qxl); /* ums specific functions */ struct qxl_bo *qxl_ums_surf_mem_alloc(qxl_screen_t *qxl, uint32_t size); struct qxl_bo *qxl_ums_lookup_phy_addr(qxl_screen_t *qxl, uint64_t phy_addr); typedef struct FrameTimer FrameTimer; typedef void (*FrameTimerFunc)(void *opaque); #ifdef XF86DRM_MODE #define MAX_RELOCS 96 #include "qxl_drm.h" struct qxl_cmd_stream { struct qxl_bo *reloc_bo[MAX_RELOCS]; int n_reloc_bos; struct drm_qxl_reloc relocs[MAX_RELOCS]; int n_relocs; }; #endif struct _qxl_screen_t { /* These are the names QXL uses */ void * ram; /* Command RAM */ void * ram_physical; void * vram; /* Surface RAM */ void * vram_physical; struct QXLRom * rom; /* Parameter RAM */ struct qxl_ring * command_ring; struct qxl_ring * cursor_ring; struct qxl_ring * release_ring; Bool screen_resources_created; int device_primary; struct qxl_bo * primary_bo; int num_modes; struct QXLMode * modes; int io_base; void * surface0_area; long surface0_size; long vram_size; long ram_size; DisplayModePtr x_modes; int virtual_x; int virtual_y; /* not the same as the heads mode for #head > 1 or virtual != head size */ struct QXLMode primary_mode; qxl_surface_t * primary; struct QXLMonitorsConfig *monitors_config; int monitors_config_size; int mem_size; int bytes_per_pixel; /* Commands */ struct qxl_mem * mem; /* Context for qxl_alloc/free */ /* Surfaces */ struct qxl_mem * surf_mem; /* Context for qxl_surf_alloc/free */ EntityInfoPtr entity; int num_heads; xf86CrtcPtr * crtcs; xf86OutputPtr * outputs; #ifndef XSPICE #ifdef XSERVER_LIBPCIACCESS struct pci_device * pci; struct pci_io_handle * io; #else pciVideoPtr pci; PCITAG pci_tag; #endif struct xf86_platform_device *platform_dev; vgaRegRec vgaRegs; #endif /* XSPICE */ uxa_driver_t * uxa; CreateScreenResourcesProcPtr create_screen_resources; CloseScreenProcPtr close_screen; CreateGCProcPtr create_gc; CopyWindowProcPtr copy_window; int16_t cur_x; int16_t cur_y; int16_t hot_x; int16_t hot_y; ScrnInfoPtr pScrn; qxl_memslot_t * mem_slots; uint8_t n_mem_slots; uint8_t main_mem_slot; uint8_t slot_id_bits; uint8_t slot_gen_bits; uint64_t va_slot_mask; uint8_t vram_mem_slot; surface_cache_t * surface_cache; /* Evacuated surfaces are stored here during VT switches */ void * vt_surfaces; OptionInfoRec options[OPTION_COUNT + 1]; int enable_image_cache; int enable_fallback_cache; int enable_surfaces; int debug_render_fallbacks; FrameTimer * frames_timer; #ifdef XSPICE /* XSpice specific */ struct QXLRom shadow_rom; /* Parameter RAM */ SpiceServer * spice_server; SpiceCoreInterface *core; QXLWorker * worker; int worker_running; QXLInstance display_sin; SpicePlaybackInstance playback_sin; /* XSpice specific, dragged from the Device */ QXLReleaseInfo *last_release; uint32_t cmdflags; uint32_t oom_running; uint32_t num_free_res; /* is having a release ring effective for Xspice? */ /* This is only touched from red worker thread - do not access * from Xorg threads. */ struct guest_primary { QXLSurfaceCreate surface; uint32_t commands; uint32_t resized; int32_t stride; uint32_t bits_pp; uint32_t bytes_pp; uint8_t *data, *flipped; } guest_primary; char playback_fifo_dir[PATH_MAX]; void *playback_opaque; char smartcard_file[PATH_MAX]; #endif /* XSPICE */ uint32_t deferred_fps; xorg_list_t ums_bos; struct qxl_bo_funcs *bo_funcs; Bool kms_enabled; #ifdef XF86DRM_MODE drmmode_rec drmmode; int drm_fd; struct qxl_cmd_stream cmds; #endif }; typedef struct qxl_output_private { qxl_screen_t *qxl; int head; xf86OutputStatus status; } qxl_output_private; typedef struct qxl_crtc_private { qxl_screen_t *qxl; int head; xf86OutputPtr output; } qxl_crtc_private; static inline uint64_t physical_address (qxl_screen_t *qxl, void *virtual, uint8_t slot_id) { qxl_memslot_t *p_slot = &(qxl->mem_slots[slot_id]); return p_slot->high_bits | ((unsigned long)virtual - p_slot->start_virt_addr); } static inline void * virtual_address (qxl_screen_t *qxl, void *physical, uint8_t slot_id) { qxl_memslot_t *p_slot = &(qxl->mem_slots[slot_id]); unsigned long virt; virt = ((unsigned long)physical) & qxl->va_slot_mask; virt += p_slot->start_virt_addr; return (void *)virt; } static inline void * u64_to_pointer (uint64_t u) { return (void *)(unsigned long)u; } static inline uint64_t pointer_to_u64 (void *p) { return (uint64_t)(unsigned long)p; } struct qxl_ring; /* * HW cursor */ void qxl_cursor_init (ScreenPtr pScreen); /* * Rings */ struct qxl_ring * qxl_ring_create (struct qxl_ring_header *header, int element_size, int n_elements, int prod_notify, qxl_screen_t *qxl); void qxl_ring_push (struct qxl_ring *ring, const void *element); Bool qxl_ring_pop (struct qxl_ring *ring, void *element); void qxl_ring_wait_idle (struct qxl_ring *ring); void qxl_ring_request_notify (struct qxl_ring *ring); int qxl_ring_prod (struct qxl_ring *ring); int qxl_ring_cons (struct qxl_ring *ring); /* * Surface */ surface_cache_t * qxl_surface_cache_create (qxl_screen_t *qxl); qxl_surface_t * qxl_surface_cache_create_primary (qxl_screen_t *qxl, struct QXLMode *mode); void * qxl_surface_get_host_bits(qxl_surface_t *surface); qxl_surface_t * qxl_surface_create (qxl_screen_t *qxl, int width, int height, int bpp); void qxl_surface_cache_sanity_check (surface_cache_t *qxl); void * qxl_surface_cache_evacuate_all (surface_cache_t *qxl); void qxl_surface_cache_replace_all (surface_cache_t *qxl, void *data); void qxl_surface_set_pixmap (qxl_surface_t *surface, PixmapPtr pixmap); /* Call this to indicate that the server is done with the surface */ void qxl_surface_kill (qxl_surface_t *surface); /* Call this when a notification comes back from the device * that the surface has been destroyed */ void qxl_surface_recycle (surface_cache_t *cache, uint32_t id); /* send anything pending to the other side */ void qxl_surface_flush (qxl_surface_t *surface); /* access */ Bool qxl_surface_prepare_access (qxl_surface_t *surface, PixmapPtr pixmap, RegionPtr region, uxa_access_t access); void qxl_surface_finish_access (qxl_surface_t *surface, PixmapPtr pixmap); /* solid */ Bool qxl_surface_prepare_solid (qxl_surface_t *destination, Pixel fg); void qxl_surface_solid (qxl_surface_t *destination, int x1, int y1, int x2, int y2); /* copy */ Bool qxl_surface_prepare_copy (qxl_surface_t *source, qxl_surface_t *dest); void qxl_surface_copy (qxl_surface_t *dest, int src_x1, int src_y1, int dest_x1, int dest_y1, int width, int height); Bool qxl_surface_put_image (qxl_surface_t *dest, int x, int y, int width, int height, const char *src, int src_pitch); void qxl_surface_unref (surface_cache_t *cache, uint32_t surface_id); /* composite */ Bool qxl_surface_prepare_composite (int op, PicturePtr src_picture, PicturePtr mask_picture, PicturePtr dst_picture, qxl_surface_t *src, qxl_surface_t *mask, qxl_surface_t *dest); void qxl_surface_composite (qxl_surface_t *dest, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, int width, int height); /* UXA */ #if HAS_DEVPRIVATEKEYREC extern DevPrivateKeyRec uxa_pixmap_index; #else extern int uxa_pixmap_index; #endif Bool qxl_uxa_init (qxl_screen_t *qxl, ScreenPtr screen); static inline qxl_surface_t *get_surface (PixmapPtr pixmap) { #if HAS_DEVPRIVATEKEYREC return dixGetPrivate(&pixmap->devPrivates, &uxa_pixmap_index); #else return dixLookupPrivate(&pixmap->devPrivates, &uxa_pixmap_index); #endif } static inline void set_surface (PixmapPtr pixmap, qxl_surface_t *surface) { dixSetPrivate(&pixmap->devPrivates, &uxa_pixmap_index, surface); } static inline struct QXLRam * get_ram_header (qxl_screen_t *qxl) { return (struct QXLRam *) ((uint8_t *)qxl->ram + qxl->rom->ram_header_offset); } void qxl_surface_upload_primary_regions(qxl_screen_t *qxl, PixmapPtr pixmap, RegionRec *r); /* ums randr code */ void qxl_init_randr (ScrnInfoPtr pScrn, qxl_screen_t *qxl); void qxl_initialize_x_modes (qxl_screen_t *qxl, ScrnInfoPtr pScrn, unsigned int *max_x, unsigned int *max_y); void qxl_update_edid (qxl_screen_t *qxl); Bool qxl_create_desired_modes (qxl_screen_t *qxl); Bool qxl_resize_primary (qxl_screen_t *qxl, uint32_t width, uint32_t height); void qxl_io_monitors_config_async (qxl_screen_t *qxl); void qxl_allocate_monitors_config (qxl_screen_t *qxl); /* * Images */ struct qxl_bo *qxl_image_create (qxl_screen_t *qxl, const uint8_t *data, int x, int y, int width, int height, int stride, int Bpp, Bool fallback); void qxl_image_destroy (qxl_screen_t *qxl, struct qxl_bo *bo); /* * Malloc */ void qxl_mem_init(void); int qxl_handle_oom (qxl_screen_t *qxl); struct qxl_mem * qxl_mem_create (void *base, unsigned long n_bytes); void qxl_mem_dump_stats (struct qxl_mem *mem, const char *header); void qxl_mem_free_all (struct qxl_mem *mem); int qxl_garbage_collect (qxl_screen_t *qxl); void qxl_reset_and_create_mem_slots (qxl_screen_t *qxl); void qxl_mark_mem_unverifiable (qxl_screen_t *qxl); #ifdef DEBUG_QXL_MEM void qxl_mem_unverifiable(struct qxl_mem *mem); #else static inline void qxl_mem_unverifiable(struct qxl_mem *mem) {} #endif /* * I/O port commands */ void qxl_update_area(qxl_screen_t *qxl); void qxl_io_memslot_add(qxl_screen_t *qxl, uint8_t id); void qxl_io_create_primary(qxl_screen_t *qxl); void qxl_io_destroy_primary(qxl_screen_t *qxl); void qxl_io_notify_oom(qxl_screen_t *qxl); void qxl_io_flush_surfaces(qxl_screen_t *qxl); void qxl_io_destroy_all_surfaces (qxl_screen_t *qxl); #ifdef QXLDRV_RESIZABLE_SURFACE0 void qxl_io_flush_release (qxl_screen_t *qxl); #endif Bool qxl_pre_init_common(ScrnInfoPtr pScrn); Bool qxl_fb_init (qxl_screen_t *qxl, ScreenPtr pScreen); Bool qxl_screen_init_kms(SCREEN_INIT_ARGS_DECL); Bool qxl_enter_vt_kms (VT_FUNC_ARGS_DECL); void qxl_leave_vt_kms (VT_FUNC_ARGS_DECL); void qxl_set_screen_pixmap_header (ScreenPtr pScreen); Bool qxl_resize_primary_to_virtual (qxl_screen_t *qxl); void qxl_get_formats (int bpp, SpiceSurfaceFmt *format, pixman_format_code_t *pformat); #ifdef XF86DRM_MODE Bool qxl_pre_init_kms(ScrnInfoPtr pScrn, int flags); Bool qxl_kms_check_cap(qxl_screen_t *qxl, int cap); uint32_t qxl_kms_bo_get_handle(struct qxl_bo *_bo); #else static inline Bool qxl_pre_init_kms(ScrnInfoPtr pScrn, int flags) { return FALSE; } static inline Bool qxl_kms_check_cap(qxl_screen_t *qxl, int cap) { return FALSE; } #endif #ifdef XSPICE /* device to spice-server, now xspice to spice-server */ void ioport_write(qxl_screen_t *qxl, uint32_t io_port, uint32_t val); #else static inline void ioport_write(qxl_screen_t *qxl, int port, int val) { pci_io_write8(qxl->io, port, val); } #endif #ifdef XSPICE #define MEMSLOT_GROUP 0 #define NUM_MEMSLOTS_GROUPS 1 // Taken from qemu's qxl.c, not sure the values make sense? we // only have a single slot, and it is never changed after being added, // so not a problem? #define NUM_MEMSLOTS 8 #define MEMSLOT_GENERATION_BITS 8 #define MEMSLOT_SLOT_BITS 1 // qemu/cpu-all.h #define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) // qemu/target-i386/cpu.h #define TARGET_PAGE_BITS 12 #define NUM_SURFACES 1024 /* initializes if required and returns the server singleton */ SpiceServer *xspice_get_spice_server(void); #endif /* XSPICE */ #ifdef WITH_CHECK_POINT #define CHECK_POINT() ErrorF ("%s: %d (%s)\n", __FILE__, __LINE__, __FUNCTION__); #else #define CHECK_POINT() #endif #endif // QXL_H xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_cursor.c000066400000000000000000000135321361660122400226310ustar00rootroot00000000000000/* * Copyright 2009, 2010 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. * */ /** \file qxl_cursor.c * \author Søren Sandmann */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "qxl.h" #include static void push_cursor (qxl_screen_t *qxl, struct qxl_bo *cursor_bo) { qxl->bo_funcs->write_command (qxl, QXL_CMD_CURSOR, cursor_bo); } static struct qxl_bo * qxl_alloc_cursor_cmd(qxl_screen_t *qxl) { struct qxl_bo *bo = qxl->bo_funcs->cmd_alloc (qxl, sizeof(struct QXLCursorCmd), "cursor command"); struct QXLCursorCmd *cmd = qxl->bo_funcs->bo_map(bo); cmd->release_info.id = pointer_to_u64 (bo) | 1; qxl->bo_funcs->bo_unmap(bo); return bo; } static void qxl_set_cursor_position(ScrnInfoPtr pScrn, int x, int y) { qxl_screen_t *qxl = pScrn->driverPrivate; struct qxl_bo *cmd_bo = qxl_alloc_cursor_cmd(qxl); struct QXLCursorCmd *cmd = qxl->bo_funcs->bo_map(cmd_bo); qxl->cur_x = x; qxl->cur_y = y; cmd->type = QXL_CURSOR_MOVE; cmd->u.position.x = qxl->cur_x + qxl->hot_x; cmd->u.position.y = qxl->cur_y + qxl->hot_y; qxl->bo_funcs->bo_unmap(cmd_bo); push_cursor(qxl, cmd_bo); } static void qxl_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *bits) { } static void qxl_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg) { /* Should not be called since UseHWCursor returned FALSE */ } static void qxl_load_cursor_argb (ScrnInfoPtr pScrn, CursorPtr pCurs) { qxl_screen_t *qxl = pScrn->driverPrivate; int w = pCurs->bits->width; int h = pCurs->bits->height; int size = w * h * sizeof (CARD32); struct qxl_bo *cmd_bo = qxl_alloc_cursor_cmd(qxl); struct QXLCursorCmd *cmd; struct qxl_bo *cursor_bo = qxl->bo_funcs->bo_alloc(qxl, sizeof(struct QXLCursor) + size, "cursor data"); struct QXLCursor *cursor = qxl->bo_funcs->bo_map(cursor_bo); cursor->header.unique = 0; cursor->header.type = SPICE_CURSOR_TYPE_ALPHA; cursor->header.width = w; cursor->header.height = h; /* I wonder if we can just tell the client that the hotspot is 0, 0 * always? The coordinates we are getting from X are for 0, 0 anyway, * so the question is if the client uses the hotspot for anything else? */ cursor->header.hot_spot_x = pCurs->bits->xhot; cursor->header.hot_spot_y = pCurs->bits->yhot; cursor->data_size = size; cursor->chunk.next_chunk = 0; cursor->chunk.prev_chunk = 0; cursor->chunk.data_size = size; memcpy (cursor->chunk.data, pCurs->bits->argb, size); #if 0 int i, j; for (j = 0; j < h; ++j) { for (i = 0; i < w; ++i) { ErrorF ("%c", (pCurs->bits->argb[j * w + i] & 0xff000000) == 0xff000000? '#' : '.'); } ErrorF ("\n"); } #endif qxl->bo_funcs->bo_unmap(cursor_bo); qxl->hot_x = pCurs->bits->xhot; qxl->hot_y = pCurs->bits->yhot; cmd = qxl->bo_funcs->bo_map(cmd_bo); cmd->type = QXL_CURSOR_SET; cmd->u.set.position.x = qxl->cur_x + qxl->hot_x; cmd->u.set.position.y = qxl->cur_y + qxl->hot_y; qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(struct QXLCursorCmd, u.set.shape), cmd_bo, cursor_bo); cmd->u.set.visible = TRUE; qxl->bo_funcs->bo_unmap(cmd_bo); push_cursor(qxl, cmd_bo); qxl->bo_funcs->bo_decref(qxl, cursor_bo); } static Bool qxl_use_hw_cursor (ScreenPtr pScrn, CursorPtr pCurs) { /* Old-school bitmap cursors are not * hardware accelerated for now. */ return FALSE; } static Bool qxl_use_hw_cursorARGB (ScreenPtr pScrn, CursorPtr pCurs) { return TRUE; } static void qxl_hide_cursor(ScrnInfoPtr pScrn) { qxl_screen_t *qxl = pScrn->driverPrivate; struct qxl_bo *cmd_bo = qxl_alloc_cursor_cmd(qxl); struct QXLCursorCmd *cursor = qxl->bo_funcs->bo_map(cmd_bo); cursor->type = QXL_CURSOR_HIDE; qxl->bo_funcs->bo_unmap(cmd_bo); push_cursor(qxl, cmd_bo); } static void qxl_show_cursor(ScrnInfoPtr pScrn) { /* * slightly hacky, but there's no QXL_CURSOR_SHOW. Could maybe do * QXL_CURSOR_SET? */ qxl_screen_t *qxl = pScrn->driverPrivate; qxl_set_cursor_position(pScrn, qxl->cur_x, qxl->cur_y); } hidden void qxl_cursor_init(ScreenPtr pScreen) { xf86CursorInfoPtr cursor; cursor = calloc(1, sizeof(xf86CursorInfoRec)); if (!cursor) return; cursor->MaxWidth = cursor->MaxHeight = 64; /* cursor->Flags; */ cursor->SetCursorPosition = qxl_set_cursor_position; cursor->LoadCursorARGB = qxl_load_cursor_argb; cursor->UseHWCursor = qxl_use_hw_cursor; cursor->UseHWCursorARGB = qxl_use_hw_cursorARGB; cursor->LoadCursorImage = qxl_load_cursor_image; cursor->SetCursorColors = qxl_set_cursor_colors; cursor->HideCursor = qxl_hide_cursor; cursor->ShowCursor = qxl_show_cursor; if (!xf86InitCursor(pScreen, cursor)) free(cursor); } xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_driver.c000066400000000000000000001250671361660122400226160ustar00rootroot00000000000000/* * Copyright 2008 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ /** \file qxl_driver.c * \author Adam Jackson * \author Søren Sandmann * * This is qxl, a driver for the Qumranet paravirtualized graphics device * in qemu. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include "qxl.h" #include "assert.h" #include "qxl_option_helpers.h" #include #ifdef XSPICE #include "spiceqxl_driver.h" #include "spiceqxl_main_loop.h" #include "spiceqxl_display.h" #include "spiceqxl_inputs.h" #include "spiceqxl_io_port.h" #include "spiceqxl_spice_server.h" #include "spiceqxl_audio.h" #include "spiceqxl_smartcard.h" #include "spiceqxl_vdagent.h" #endif /* XSPICE */ #include "dfps.h" extern void compat_init_scrn (ScrnInfoPtr); #define BREAKPOINT() do { __asm__ __volatile__ ("int $03"); } while (0) #ifdef XSPICE static char filter_str[] = "filter"; static char auto_str[] = "auto"; static char auto_glz_str[] = "auto_glz"; static char spice_vdagent_virtio_path_default[] = "/tmp/xspice-virtio"; static char spice_vdagent_uinput_path_default[] = "/tmp/xspice-uinput"; #endif static char driver_name[] = QXL_DRIVER_NAME; static const OptionInfoRec DefaultOptions[] = { { OPTION_ENABLE_IMAGE_CACHE, "EnableImageCache", OPTV_BOOLEAN, { 1 }, FALSE }, { OPTION_ENABLE_FALLBACK_CACHE, "EnableFallbackCache", OPTV_BOOLEAN, { 1 }, FALSE }, { OPTION_ENABLE_SURFACES, "EnableSurfaces", OPTV_BOOLEAN, { 1 }, FALSE }, { OPTION_DEBUG_RENDER_FALLBACKS, "DebugRenderFallbacks", OPTV_BOOLEAN, { 0 }, FALSE }, { OPTION_NUM_HEADS, "NumHeads", OPTV_INTEGER, { 4 }, FALSE }, { OPTION_SPICE_DEFERRED_FPS, "SpiceDeferredFPS", OPTV_INTEGER, { 0 }, FALSE}, #ifdef XSPICE { OPTION_SPICE_PORT, "SpicePort", OPTV_INTEGER, {5900}, FALSE }, { OPTION_SPICE_TLS_PORT, "SpiceTlsPort", OPTV_INTEGER, {0}, FALSE}, { OPTION_SPICE_ADDR, "SpiceAddr", OPTV_STRING, {0}, FALSE}, { OPTION_SPICE_X509_DIR, "SpiceX509Dir", OPTV_STRING, {0}, FALSE}, { OPTION_SPICE_SASL, "SpiceSasl", OPTV_BOOLEAN, {0}, FALSE}, { OPTION_SPICE_AGENT_MOUSE, "SpiceAgentMouse", OPTV_BOOLEAN, {0}, TRUE }, { OPTION_SPICE_DISABLE_TICKETING, "SpiceDisableTicketing", OPTV_BOOLEAN, {0}, FALSE}, { OPTION_SPICE_PASSWORD, "SpicePassword", OPTV_STRING, {0}, FALSE}, { OPTION_SPICE_X509_KEY_FILE, "SpiceX509KeyFile", OPTV_STRING, {0}, FALSE}, { OPTION_SPICE_STREAMING_VIDEO, "SpiceStreamingVideo", OPTV_STRING, {.str = filter_str}, FALSE}, { OPTION_SPICE_PLAYBACK_COMPRESSION, "SpicePlaybackCompression", OPTV_BOOLEAN, {1}, FALSE}, { OPTION_SPICE_ZLIB_GLZ_WAN_COMPRESSION, "SpiceZlibGlzWanCompression", OPTV_STRING, {.str = auto_str}, FALSE}, { OPTION_SPICE_JPEG_WAN_COMPRESSION, "SpiceJpegWanCompression", OPTV_STRING, {.str = auto_str}, FALSE}, { OPTION_SPICE_IMAGE_COMPRESSION, "SpiceImageCompression", OPTV_STRING, {.str = auto_glz_str}, FALSE}, { OPTION_SPICE_DISABLE_COPY_PASTE, "SpiceDisableCopyPaste", OPTV_BOOLEAN, {0}, FALSE}, { OPTION_SPICE_IPV4_ONLY, "SpiceIPV4Only", OPTV_BOOLEAN, {0}, FALSE}, { OPTION_SPICE_IPV6_ONLY, "SpiceIPV6Only", OPTV_BOOLEAN, {0}, FALSE}, { OPTION_SPICE_X509_CERT_FILE, "SpiceX509CertFile", OPTV_STRING, {0}, FALSE}, { OPTION_SPICE_X509_KEY_PASSWORD, "SpiceX509KeyPassword", OPTV_STRING, {0}, FALSE}, { OPTION_SPICE_TLS_CIPHERS, "SpiceTlsCiphers", OPTV_STRING, {0}, FALSE}, { OPTION_SPICE_CACERT_FILE, "SpiceCacertFile", OPTV_STRING, {0}, FALSE}, { OPTION_SPICE_DH_FILE, "SpiceDhFile", OPTV_STRING, {0}, FALSE}, { OPTION_SPICE_EXIT_ON_DISCONNECT, "SpiceExitOnDisconnect", OPTV_BOOLEAN, {0}, FALSE}, { OPTION_SPICE_PLAYBACK_FIFO_DIR, "SpicePlaybackFIFODir", OPTV_STRING, {0}, FALSE}, { OPTION_SPICE_VDAGENT_ENABLED, "SpiceVdagentEnabled", OPTV_BOOLEAN, {0}, FALSE}, { OPTION_SPICE_VDAGENT_VIRTIO_PATH, "SpiceVdagentVirtioPath", OPTV_STRING, {.str = spice_vdagent_virtio_path_default}, FALSE}, { OPTION_SPICE_VDAGENT_UINPUT_PATH, "SpiceVdagentUinputPath", OPTV_STRING, {.str = spice_vdagent_uinput_path_default}, FALSE}, { OPTION_SPICE_VDAGENT_UID, "SpiceVdagentUid", OPTV_INTEGER, {0}, FALSE}, { OPTION_SPICE_VDAGENT_GID, "SpiceVdagentGid", OPTV_INTEGER, {0}, FALSE}, { OPTION_FRAME_BUFFER_SIZE, "FrameBufferSize", OPTV_INTEGER, {DEFAULT_FRAME_BUFFER_SIZE}, FALSE}, { OPTION_SURFACE_BUFFER_SIZE, "SurfaceBufferSize", OPTV_INTEGER, {DEFAULT_SURFACE_BUFFER_SIZE}, FALSE}, { OPTION_COMMAND_BUFFER_SIZE, "CommandBufferSize", OPTV_INTEGER, {DEFAULT_COMMAND_BUFFER_SIZE}, FALSE}, { OPTION_SPICE_SMARTCARD_FILE, "SpiceSmartcardFile", OPTV_STRING, {0}, FALSE}, { OPTION_SPICE_VIDEO_CODECS, "SpiceVideoCodecs", OPTV_STRING, {0}, FALSE}, #endif { -1, NULL, OPTV_NONE, {0}, FALSE } }; static const OptionInfoRec * qxl_available_options (int chipid, int busid) { return DefaultOptions; } /* Having a single monitors config struct allocated on the device avoids any * possible fragmentation. Since X is single threaded there is no danger * in us changing it between issuing the io and getting the interrupt to signal * spice-server is done reading it. */ #define MAX_MONITORS_NUM 16 void qxl_allocate_monitors_config (qxl_screen_t *qxl) { qxl->monitors_config = (QXLMonitorsConfig *)(void *) ((unsigned long)qxl->ram + qxl->rom->ram_header_offset - qxl->monitors_config_size); } static Bool qxl_blank_screen (ScreenPtr pScreen, int mode) { return TRUE; } #ifdef XSPICE static void unmap_memory_helper (qxl_screen_t *qxl) { free (qxl->ram); free (qxl->vram); free (qxl->rom); } static void map_memory_helper (qxl_screen_t *qxl) { qxl->ram = calloc (qxl->ram_size, 1); qxl->ram_physical = qxl->ram; qxl->vram = calloc (qxl->vram_size, 1); qxl->vram_physical = qxl->vram; qxl->rom = calloc (ROM_SIZE, 1); init_qxl_rom (qxl, ROM_SIZE); } #else /* Default */ static void unmap_memory_helper (qxl_screen_t *qxl) { #ifdef XSERVER_LIBPCIACCESS if (qxl->ram) pci_device_unmap_range (qxl->pci, qxl->ram, qxl->pci->regions[0].size); if (qxl->vram) pci_device_unmap_range (qxl->pci, qxl->vram, qxl->pci->regions[1].size); if (qxl->rom) pci_device_unmap_range (qxl->pci, qxl->rom, qxl->pci->regions[2].size); if (qxl->io) pci_device_close_io (qxl->pci, qxl->io); #else if (qxl->ram) xf86UnMapVidMem (scrnIndex, qxl->ram, (1 << qxl->pci->size[0])); if (qxl->vram) xf86UnMapVidMem (scrnIndex, qxl->vram, (1 << qxl->pci->size[1])); if (qxl->rom) xf86UnMapVidMem (scrnIndex, qxl->rom, (1 << qxl->pci->size[2])); #endif } static void map_memory_helper (qxl_screen_t *qxl) { #ifdef XSERVER_LIBPCIACCESS pci_device_map_range (qxl->pci, qxl->pci->regions[0].base_addr, qxl->pci->regions[0].size, PCI_DEV_MAP_FLAG_WRITABLE | PCI_DEV_MAP_FLAG_WRITE_COMBINE, &qxl->ram); qxl->ram_physical = u64_to_pointer (qxl->pci->regions[0].base_addr); qxl->ram_size = qxl->pci->regions[0].size; pci_device_map_range (qxl->pci, qxl->pci->regions[1].base_addr, qxl->pci->regions[1].size, PCI_DEV_MAP_FLAG_WRITABLE, &qxl->vram); qxl->vram_physical = u64_to_pointer (qxl->pci->regions[1].base_addr); qxl->vram_size = qxl->pci->regions[1].size; pci_device_map_range (qxl->pci, qxl->pci->regions[2].base_addr, qxl->pci->regions[2].size, 0, (void **)&qxl->rom); qxl->io = pci_device_open_io(qxl->pci, qxl->pci->regions[3].base_addr, qxl->pci->regions[3].size); qxl->io_base = qxl->pci->regions[3].base_addr; #else qxl->ram = xf86MapPciMem (scrnIndex, VIDMEM_FRAMEBUFFER, qxl->pci_tag, qxl->pci->memBase[0], (1 << qxl->pci->size[0])); qxl->ram_physical = (void *)qxl->pci->memBase[0]; qxl->vram = xf86MapPciMem (scrnIndex, VIDMEM_MMIO | VIDMEM_MMIO_32BIT, qxl->pci_tag, qxl->pci->memBase[1], (1 << qxl->pci->size[1])); qxl->vram_physical = (void *)qxl->pci->memBase[1]; qxl->vram_size = (1 << qxl->pci->size[1]); qxl->rom = xf86MapPciMem (scrnIndex, VIDMEM_MMIO | VIDMEM_MMIO_32BIT, qxl->pci_tag, qxl->pci->memBase[2], (1 << qxl->pci->size[2])); qxl->io_base = qxl->pci->ioBase[3]; #endif } #endif /* XSPICE */ static void qxl_unmap_memory (qxl_screen_t *qxl) { #ifdef XSPICE if (qxl->worker) { spice_server_vm_stop(qxl->spice_server); qxl->worker_running = FALSE; } #endif if (qxl->mem) { qxl_mem_free_all (qxl->mem); free(qxl->mem); qxl->mem = NULL; } if (qxl->surf_mem) { qxl_mem_free_all (qxl->surf_mem); free(qxl->surf_mem); qxl->surf_mem = NULL; } unmap_memory_helper (qxl); qxl->ram = qxl->ram_physical = qxl->vram = qxl->rom = NULL; qxl->num_modes = 0; qxl->modes = NULL; } #ifdef QXLDRV_RESIZABLE_SURFACE0 static void qxl_dump_ring_stat (qxl_screen_t *qxl) { int cmd_prod, cursor_prod, cmd_cons, cursor_cons; int release_prod, release_cons; cmd_prod = qxl_ring_prod (qxl->command_ring); cursor_prod = qxl_ring_prod (qxl->cursor_ring); cmd_cons = qxl_ring_cons (qxl->command_ring); cursor_cons = qxl_ring_cons (qxl->cursor_ring); release_prod = qxl_ring_prod (qxl->release_ring); release_cons = qxl_ring_cons (qxl->release_ring); ErrorF ("%s: Cmd %d/%d, Cur %d/%d, Rel %d/%d\n", __func__, cmd_cons, cmd_prod, cursor_cons, cursor_prod, release_cons, release_prod); } #endif /* To resize surface0 we need to ensure qxl->mem is empty. We can do that by: * - fast: * - ooming until command ring is empty. * - flushing the release ring (>V10) * - slow: calling update_area on all surfaces. * This is done via already known code, so use that by default now. */ static int qxl_resize_surface0 (qxl_screen_t *qxl, long surface0_size) { long ram_header_size = qxl->ram_size - qxl->rom->ram_header_offset; long new_mem_size = qxl->ram_size - (surface0_size + ram_header_size + qxl->monitors_config_size); if (new_mem_size < 0) { ErrorF ("cannot resize surface0 to %ld, does not fit in BAR 0\n", surface0_size); return 0; } ErrorF ("resizing surface0 to %ld\n", surface0_size); if (qxl->mem) { #ifdef QXLDRV_RESIZABLE_SURFACE0 void *surfaces; qxl_dump_ring_stat (qxl); qxl_io_flush_surfaces (qxl); surfaces = qxl_surface_cache_evacuate_all (qxl->surface_cache); qxl_io_destroy_all_surfaces (qxl); // redundant? qxl_io_flush_release (qxl); qxl_dump_ring_stat (qxl); qxl_surface_cache_replace_all (qxl->surface_cache, surfaces); #else ErrorF ("resizing surface0 compiled out\n"); return 0; #endif } /* surface0_area is still fixed to start of ram BAR */ qxl->surface0_size = surface0_size; qxl->mem_size = new_mem_size; qxl->mem = qxl_mem_create ((void *)((unsigned long)qxl->surface0_area + qxl->surface0_size), qxl->mem_size); return 1; } static Bool qxl_map_memory (qxl_screen_t *qxl, int scrnIndex) { map_memory_helper (qxl); if (!qxl->ram || !qxl->vram || !qxl->rom) return FALSE; xf86DrvMsg (scrnIndex, X_INFO, "framebuffer at %p (%d KB)\n", qxl->ram, qxl->rom->surface0_area_size / 1024); xf86DrvMsg (scrnIndex, X_INFO, "command ram at %p (%d KB)\n", (void *)((unsigned long)qxl->ram + qxl->rom->surface0_area_size), (qxl->rom->num_pages * getpagesize ()) / 1024); xf86DrvMsg (scrnIndex, X_INFO, "vram at %p (%ld KB)\n", qxl->vram, qxl->vram_size / 1024); xf86DrvMsg (scrnIndex, X_INFO, "rom at %p\n", qxl->rom); /* * Keep a hole for MonitorsConfig. This is not part of QXLRam to ensure * the driver can change it without affecting the driver/device ABI. */ qxl->monitors_config_size = (sizeof (QXLMonitorsConfig) + sizeof (QXLHead) * MAX_MONITORS_NUM + getpagesize () - 1) & ~(getpagesize () - 1); qxl->num_modes = *(uint32_t *)((uint8_t *)qxl->rom + qxl->rom->modes_offset); qxl->modes = (struct QXLMode *)(((uint8_t *)qxl->rom) + qxl->rom->modes_offset + 4); qxl->surface0_area = qxl->ram; qxl->surface0_size = 0; qxl->mem = NULL; if (!qxl_resize_surface0 (qxl, qxl->rom->surface0_area_size)) return FALSE; qxl->surf_mem = qxl_mem_create ((void *)((unsigned long)qxl->vram), qxl->vram_size); qxl_allocate_monitors_config (qxl); return TRUE; } #ifdef XSPICE static void qxl_save_state (ScrnInfoPtr pScrn) { } static void qxl_restore_state (ScrnInfoPtr pScrn) { } #else /* QXL */ static void qxl_save_state (ScrnInfoPtr pScrn) { qxl_screen_t *qxl = pScrn->driverPrivate; if (xf86IsPrimaryPci (qxl->pci)) vgaHWSaveFonts (pScrn, &qxl->vgaRegs); } static void qxl_restore_state (ScrnInfoPtr pScrn) { qxl_screen_t *qxl = pScrn->driverPrivate; if (xf86IsPrimaryPci (qxl->pci)) vgaHWRestoreFonts (pScrn, &qxl->vgaRegs); } #endif /* XSPICE */ static Bool qxl_close_screen (CLOSE_SCREEN_ARGS_DECL) { ScrnInfoPtr pScrn = xf86ScreenToScrn (pScreen); qxl_screen_t *qxl = pScrn->driverPrivate; Bool result; ErrorF ("Disabling FB access for %d\n", pScrn->scrnIndex); #ifndef XF86_SCRN_INTERFACE pScrn->EnableDisableFBAccess (scrnIndex, FALSE); #else pScrn->EnableDisableFBAccess (pScrn, FALSE); #endif pScreen->CreateScreenResources = qxl->create_screen_resources; pScreen->CloseScreen = qxl->close_screen; result = pScreen->CloseScreen (CLOSE_SCREEN_ARGS); #ifndef XSPICE if (!xf86IsPrimaryPci (qxl->pci) && qxl->primary) qxl_reset_and_create_mem_slots (qxl); #endif if (pScrn->vtSema) { qxl_restore_state (pScrn); qxl_mark_mem_unverifiable (qxl); qxl_unmap_memory (qxl); } pScrn->vtSema = FALSE; return result; } void qxl_set_screen_pixmap_header (ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86ScreenToScrn (pScreen); qxl_screen_t *qxl = pScrn->driverPrivate; PixmapPtr pPixmap = pScreen->GetScreenPixmap (pScreen); // TODO: don't ModifyPixmapHeader too early? if (pPixmap) { pScreen->ModifyPixmapHeader (pPixmap, qxl->primary_mode.x_res, qxl->primary_mode.y_res, -1, -1, qxl->primary_mode.x_res * qxl->bytes_per_pixel, qxl_surface_get_host_bits(qxl->primary)); } else { ErrorF ("pix: %p;\n", pPixmap); } } static qxl_surface_t * qxl_create_primary(qxl_screen_t *qxl) { struct QXLMode *pm = &qxl->primary_mode; pm->id = 0x4242; pm->x_res = qxl->virtual_x; pm->y_res = qxl->virtual_y; pm->bits = qxl->pScrn->bitsPerPixel; pm->stride = qxl->virtual_x * pm->bits / 8; pm->x_mili = 0; // TODO pm->y_mili = 0; // TODO pm->orientation = 0; // ? supported by us for single head usage? more TODO return qxl_surface_cache_create_primary (qxl, &qxl->primary_mode); } Bool qxl_resize_primary_to_virtual (qxl_screen_t *qxl) { long new_surface0_size; if ((qxl->primary_mode.x_res == qxl->virtual_x && qxl->primary_mode.y_res == qxl->virtual_y) && qxl->device_primary == QXL_DEVICE_PRIMARY_CREATED) { return TRUE; /* empty Success */ } ErrorF ("resizing primary to %dx%d\n", qxl->virtual_x, qxl->virtual_y); if (!qxl->kms_enabled) { new_surface0_size = qxl->virtual_x * qxl->pScrn->bitsPerPixel / 8 * qxl->virtual_y; if (new_surface0_size > qxl->surface0_size) { if (!qxl_resize_surface0 (qxl, new_surface0_size)) { ErrorF ("not resizing primary to virtual, leaving old virtual\n"); return FALSE; } } } if (qxl->primary) { qxl_surface_kill (qxl->primary); qxl_surface_cache_sanity_check (qxl->surface_cache); qxl->bo_funcs->destroy_primary(qxl, qxl->primary_bo); } qxl->primary = qxl_create_primary(qxl); qxl->bytes_per_pixel = (qxl->pScrn->bitsPerPixel + 7) / 8; if (qxl->screen_resources_created) { ScreenPtr pScreen = qxl->pScrn->pScreen; PixmapPtr root = pScreen->GetScreenPixmap (pScreen); if (qxl->deferred_fps <= 0) { qxl_surface_t *surf; if ((surf = get_surface (root))) qxl_surface_kill (surf); set_surface (root, qxl->primary); } qxl_set_screen_pixmap_header (pScreen); } ErrorF ("primary is %p\n", qxl->primary); return TRUE; } Bool qxl_resize_primary (qxl_screen_t *qxl, uint32_t width, uint32_t height) { qxl->virtual_x = width; qxl->virtual_y = height; if (qxl->vt_surfaces) { ErrorF ("%s: ignoring resize due to not being in control of VT\n", __FUNCTION__); return FALSE; } return qxl_resize_primary_to_virtual (qxl); } static Bool qxl_switch_mode (SWITCH_MODE_ARGS_DECL) { SCRN_INFO_PTR (arg); qxl_screen_t *qxl = pScrn->driverPrivate; ErrorF ("Ignoring display mode, ensuring recreation of primary\n"); return qxl_resize_primary_to_virtual (qxl); } static Bool qxl_create_screen_resources (ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86ScreenToScrn (pScreen); qxl_screen_t * qxl = pScrn->driverPrivate; Bool ret; PixmapPtr pPixmap; qxl_surface_t *surf; pScreen->CreateScreenResources = qxl->create_screen_resources; ret = pScreen->CreateScreenResources (pScreen); pScreen->CreateScreenResources = qxl_create_screen_resources; if (!ret) return FALSE; pPixmap = pScreen->GetScreenPixmap (pScreen); if (qxl->deferred_fps <= 0) { qxl_set_screen_pixmap_header (pScreen); if ((surf = get_surface (pPixmap))) qxl_surface_kill (surf); set_surface (pPixmap, qxl->primary); } qxl_create_desired_modes (qxl); qxl_update_edid (qxl); qxl->screen_resources_created = TRUE; return TRUE; } #ifdef XSPICE static void spiceqxl_screen_init (ScrnInfoPtr pScrn, qxl_screen_t *qxl) { // Init spice if (!qxl->spice_server) { qxl->spice_server = xspice_get_spice_server (); xspice_set_spice_server_options (qxl->options); qxl->core = basic_event_loop_init (); if (spice_server_init (qxl->spice_server, qxl->core) < 0) { ErrorF ("failed to initialize server\n"); abort (); } qxl_add_spice_display_interface (qxl); qxl_add_spice_playback_interface (qxl); qxl_add_spice_smartcard_interface (qxl); spiceqxl_vdagent_init (qxl); } else { /* Crashes result from invalid xorg_timer pointers in our watch lists because Xorg clears all timers at server reset. We would require a fairly substantial revamp of how the spice server is started and operated to avoid this crash. */ ErrorF("WARNING: XSPICE requires -noreset; crashes are now likely.\n"); } if (! qxl->worker_running) { xspice_register_handlers(); spice_server_vm_start(qxl->spice_server); qxl->worker_running = TRUE; } } #endif Bool qxl_fb_init (qxl_screen_t *qxl, ScreenPtr pScreen) { ScrnInfoPtr pScrn = qxl->pScrn; if (!fbScreenInit (pScreen, qxl_surface_get_host_bits(qxl->primary), pScrn->virtualX, pScrn->virtualY, pScrn->xDpi, pScrn->yDpi, pScrn->virtualX, pScrn->bitsPerPixel)) return FALSE; fbPictureInit (pScreen, NULL, 0); return TRUE; } static Bool qxl_screen_init (SCREEN_INIT_ARGS_DECL) { ScrnInfoPtr pScrn = xf86ScreenToScrn (pScreen); qxl_screen_t * qxl = pScrn->driverPrivate; struct QXLRam *ram_header; VisualPtr visual; CHECK_POINT (); assert (qxl->pScrn == pScrn); if (!qxl_map_memory (qxl, pScrn->scrnIndex)) return FALSE; #ifdef XSPICE spiceqxl_screen_init (pScrn, qxl); #endif ram_header = (void *)((unsigned long)qxl->ram + (unsigned long)qxl->rom->ram_header_offset); printf ("ram_header at %d\n", qxl->rom->ram_header_offset); printf ("surf0 size: %d\n", qxl->rom->surface0_area_size); qxl_save_state (pScrn); qxl_blank_screen (pScreen, SCREEN_SAVER_ON); miClearVisualTypes (); if (!miSetVisualTypes (pScrn->depth, miGetDefaultVisualMask (pScrn->depth), pScrn->rgbBits, pScrn->defaultVisual)) goto out; if (!miSetPixmapDepths ()) goto out; #if 0 ErrorF ("allocated %d x %d %p\n", pScrn->virtualX, pScrn->virtualY, qxl->fb); #endif /* Set up resources */ qxl_reset_and_create_mem_slots (qxl); ErrorF ("done reset\n"); qxl->surface_cache = qxl_surface_cache_create (qxl); qxl->primary = qxl_create_primary(qxl); if (!qxl_fb_init (qxl, pScreen)) goto out; visual = pScreen->visuals + pScreen->numVisuals; while (--visual >= pScreen->visuals) { if ((visual->class | DynamicClass) == DirectColor) { visual->offsetRed = pScrn->offset.red; visual->offsetGreen = pScrn->offset.green; visual->offsetBlue = pScrn->offset.blue; visual->redMask = pScrn->mask.red; visual->greenMask = pScrn->mask.green; visual->blueMask = pScrn->mask.blue; } } qxl->command_ring = qxl_ring_create ((struct qxl_ring_header *)&(ram_header->cmd_ring), sizeof (struct QXLCommand), QXL_COMMAND_RING_SIZE, QXL_IO_NOTIFY_CMD, qxl); qxl->cursor_ring = qxl_ring_create ((struct qxl_ring_header *)&(ram_header->cursor_ring), sizeof (struct QXLCommand), QXL_CURSOR_RING_SIZE, QXL_IO_NOTIFY_CURSOR, qxl); qxl->release_ring = qxl_ring_create ((struct qxl_ring_header *)&(ram_header->release_ring), sizeof (uint64_t), QXL_RELEASE_RING_SIZE, 0, qxl); /* xf86DPMSInit (pScreen, xf86DPMSSet, 0); */ pScreen->SaveScreen = qxl_blank_screen; qxl_uxa_init (qxl, pScreen); uxa_set_fallback_debug (pScreen, qxl->debug_render_fallbacks); DamageSetup (pScreen); /* We need to set totalPixmapSize after setup_uxa and Damage, as the privates size is not computed correctly until then */ #if (XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1, 12, 99, 901, 0)) pScreen->totalPixmapSize = BitmapBytePad ((sizeof (PixmapRec) + dixPrivatesSize (PRIVATE_PIXMAP) ) * 8); #else pScreen->totalPixmapSize = BitmapBytePad((sizeof(PixmapRec) + dixScreenSpecificPrivatesSize(pScreen, PRIVATE_PIXMAP) ) * 8); #endif miDCInitialize (pScreen, xf86GetPointerScreenFuncs()); if (!miCreateDefColormap (pScreen)) goto out; qxl->create_screen_resources = pScreen->CreateScreenResources; pScreen->CreateScreenResources = qxl_create_screen_resources; qxl->close_screen = pScreen->CloseScreen; pScreen->CloseScreen = qxl_close_screen; qxl_cursor_init (pScreen); CHECK_POINT (); pScreen->width = pScrn->currentMode->HDisplay; pScreen->height = pScrn->currentMode->VDisplay; if (!xf86CrtcScreenInit (pScreen)) return FALSE; if (!qxl_resize_primary_to_virtual (qxl)) return FALSE; /* Note: this must be done after DamageSetup() because it calls * _dixInitPrivates. And if that has been called, DamageSetup() * will assert. */ if (!uxa_resources_init (pScreen)) return FALSE; CHECK_POINT (); /* fake transform support, to allow agent to switch crtc mode */ /* without X doing checks, see rrcrtc.c "Check screen size */ /* bounds" */ xf86RandR12SetTransformSupport (pScreen, TRUE); if (qxl->deferred_fps) dfps_start_ticker(qxl); return TRUE; out: return FALSE; } static Bool qxl_enter_vt (VT_FUNC_ARGS_DECL) { SCRN_INFO_PTR (arg); qxl_screen_t *qxl = pScrn->driverPrivate; qxl_save_state (pScrn); qxl_reset_and_create_mem_slots (qxl); if (!qxl_resize_primary_to_virtual (qxl)) return FALSE; if (qxl->mem) { qxl_mem_free_all (qxl->mem); } if (qxl->surf_mem) qxl_mem_free_all (qxl->surf_mem); if (qxl->vt_surfaces) { qxl_surface_cache_replace_all (qxl->surface_cache, qxl->vt_surfaces); qxl->vt_surfaces = NULL; } qxl_create_desired_modes (qxl); pScrn->EnableDisableFBAccess (XF86_SCRN_ARG (pScrn), TRUE); return TRUE; } static void qxl_leave_vt (VT_FUNC_ARGS_DECL) { SCRN_INFO_PTR (arg); qxl_screen_t *qxl = pScrn->driverPrivate; xf86_hide_cursors (pScrn); pScrn->EnableDisableFBAccess (XF86_SCRN_ARG (pScrn), FALSE); if (qxl->deferred_fps <= 0) qxl->vt_surfaces = qxl_surface_cache_evacuate_all (qxl->surface_cache); ioport_write (qxl, QXL_IO_RESET, 0); qxl_restore_state (pScrn); qxl->device_primary = QXL_DEVICE_PRIMARY_NONE; } static Bool qxl_color_setup (ScrnInfoPtr pScrn) { int scrnIndex = pScrn->scrnIndex; Gamma gzeros = { 0.0, 0.0, 0.0 }; rgb rzeros = { 0, 0, 0 }; if (!xf86SetDepthBpp (pScrn, 0, 0, 0, Support32bppFb)) return FALSE; if (pScrn->depth != 15 && pScrn->depth != 24) { xf86DrvMsg (scrnIndex, X_ERROR, "Depth %d is not supported\n", pScrn->depth); return FALSE; } xf86PrintDepthBpp (pScrn); if (!xf86SetWeight (pScrn, rzeros, rzeros)) return FALSE; if (!xf86SetDefaultVisual (pScrn, -1)) return FALSE; if (!xf86SetGamma (pScrn, gzeros)) return FALSE; return TRUE; } static void print_modes (qxl_screen_t *qxl, int scrnIndex) { int i; for (i = 0; i < qxl->num_modes; ++i) { struct QXLMode *m = qxl->modes + i; xf86DrvMsg (scrnIndex, X_INFO, "%d: %dx%d, %d bits, stride %d, %dmm x %dmm, orientation %d\n", m->id, m->x_res, m->y_res, m->bits, m->stride, m->x_mili, m->y_mili, m->orientation); } } #ifndef XSPICE static Bool qxl_check_device (ScrnInfoPtr pScrn, qxl_screen_t *qxl) { int scrnIndex = pScrn->scrnIndex; struct QXLRom *rom = qxl->rom; struct QXLRam *ram_header = (void *)((unsigned long)qxl->ram + rom->ram_header_offset); CHECK_POINT (); if (rom->magic != 0x4f525851) /* "QXRO" little-endian */ { xf86DrvMsg (scrnIndex, X_ERROR, "Bad ROM signature %x\n", rom->magic); return FALSE; } xf86DrvMsg (scrnIndex, X_INFO, "Device version %d.%d\n", rom->id, rom->update_id); xf86DrvMsg (scrnIndex, X_INFO, "Compression level %d, log level %d\n", rom->compression_level, rom->log_level); xf86DrvMsg (scrnIndex, X_INFO, "%d io pages at 0x%lx\n", rom->num_pages, (unsigned long)qxl->ram); xf86DrvMsg (scrnIndex, X_INFO, "RAM header offset: 0x%x\n", rom->ram_header_offset); if (ram_header->magic != 0x41525851) /* "QXRA" little-endian */ { xf86DrvMsg (scrnIndex, X_ERROR, "Bad RAM signature %x at %p\n", ram_header->magic, &ram_header->magic); return FALSE; } xf86DrvMsg (scrnIndex, X_INFO, "Correct RAM signature %x\n", ram_header->magic); return TRUE; } #endif /* !XSPICE */ Bool qxl_pre_init_common(ScrnInfoPtr pScrn) { int scrnIndex = pScrn->scrnIndex; qxl_screen_t *qxl = pScrn->driverPrivate; if (!qxl_color_setup (pScrn)) goto out; /* option parsing and card differentiation */ xf86CollectOptions (pScrn, NULL); memcpy (qxl->options, DefaultOptions, sizeof (DefaultOptions)); xf86ProcessOptions (scrnIndex, pScrn->options, qxl->options); qxl->enable_image_cache = get_bool_option (qxl->options, OPTION_ENABLE_IMAGE_CACHE, "QXL_ENABLE_IMAGE_CACHE"); qxl->enable_fallback_cache = get_bool_option (qxl->options, OPTION_ENABLE_FALLBACK_CACHE, "QXL_ENABLE_FALLBACK_CACHE"); qxl->enable_surfaces = get_bool_option (qxl->options, OPTION_ENABLE_SURFACES, "QXL_ENABLE_SURFACES"); qxl->debug_render_fallbacks = get_bool_option (qxl->options, OPTION_DEBUG_RENDER_FALLBACKS, "QXL_DEBUG_RENDER_FALLBACKS"); qxl->num_heads = get_int_option (qxl->options, OPTION_NUM_HEADS, "QXL_NUM_HEADS"); qxl->deferred_fps = get_int_option(qxl->options, OPTION_SPICE_DEFERRED_FPS, "XSPICE_DEFERRED_FPS"); if (qxl->deferred_fps > 0) xf86DrvMsg(scrnIndex, X_INFO, "Deferred FPS: %d\n", qxl->deferred_fps); else xf86DrvMsg(scrnIndex, X_INFO, "Deferred Frames: Disabled\n"); xf86DrvMsg (scrnIndex, X_INFO, "Offscreen Surfaces: %s\n", qxl->enable_surfaces ? "Enabled" : "Disabled"); xf86DrvMsg (scrnIndex, X_INFO, "Image Cache: %s\n", qxl->enable_image_cache ? "Enabled" : "Disabled"); xf86DrvMsg (scrnIndex, X_INFO, "Fallback Cache: %s\n", qxl->enable_fallback_cache ? "Enabled" : "Disabled"); return TRUE; out: return FALSE; } static Bool qxl_pre_init (ScrnInfoPtr pScrn, int flags) { int scrnIndex = pScrn->scrnIndex; qxl_screen_t *qxl = NULL; ClockRangePtr clockRanges = NULL; //int *linePitches = NULL; //DisplayModePtr mode; unsigned int max_x, max_y; #ifdef XSPICE const char *playback_fifo_dir; const char *smartcard_file; #endif /* In X server 1.7.5, Xorg -configure will cause this * function to get called without a confScreen. */ if (!pScrn->confScreen) return FALSE; CHECK_POINT (); qxl_mem_init(); /* zaphod mode is for suckers and i choose not to implement it */ if (xf86IsEntityShared (pScrn->entityList[0])) { xf86DrvMsg (scrnIndex, X_ERROR, "No Zaphod mode for you\n"); return FALSE; } if (!pScrn->driverPrivate) pScrn->driverPrivate = xnfcalloc (sizeof (qxl_screen_t), 1); qxl = pScrn->driverPrivate; qxl->device_primary = QXL_DEVICE_PRIMARY_UNDEFINED; qxl->pScrn = pScrn; qxl->x_modes = NULL; qxl->entity = xf86GetEntityInfo (pScrn->entityList[0]); qxl->kms_enabled = FALSE; xorg_list_init(&qxl->ums_bos); #ifndef XSPICE qxl->pci = xf86GetPciInfoForEntity (qxl->entity->index); #ifndef XSERVER_LIBPCIACCESS qxl->pci_tag = pciTag (qxl->pci->bus, qxl->pci->device, qxl->pci->func); #endif if (qxl->pci->revision < 4) { ErrorF ("Ignoring monitor config, device revision < 4\n"); } #endif /* XSPICE */ pScrn->monitor = pScrn->confScreen->monitor; qxl_ums_setup_funcs(qxl); if (!qxl_pre_init_common(pScrn)) goto out; #ifdef XSPICE playback_fifo_dir = get_str_option(qxl->options, OPTION_SPICE_PLAYBACK_FIFO_DIR, "XSPICE_PLAYBACK_FIFO_DIR"); if (playback_fifo_dir) strncpy(qxl->playback_fifo_dir, playback_fifo_dir, sizeof(qxl->playback_fifo_dir)); else qxl->playback_fifo_dir[0] = '\0'; smartcard_file = get_str_option(qxl->options, OPTION_SPICE_SMARTCARD_FILE, "XSPICE_SMARTCARD_FILE"); if (smartcard_file) strncpy(qxl->smartcard_file, smartcard_file, sizeof(qxl->smartcard_file)); else qxl->smartcard_file[0] = '\0'; qxl->surface0_size = get_int_option (qxl->options, OPTION_FRAME_BUFFER_SIZE, "QXL_FRAME_BUFFER_SIZE") << 20L; qxl->vram_size = get_int_option (qxl->options, OPTION_SURFACE_BUFFER_SIZE, "QXL_SURFACE_BUFFER_SIZE") << 20L; qxl->ram_size = get_int_option (qxl->options, OPTION_COMMAND_BUFFER_SIZE, "QXL_COMMAND_BUFFER_SIZE") << 20L; #endif if (!qxl_map_memory (qxl, scrnIndex)) goto out; #ifndef XSPICE if (!qxl_check_device (pScrn, qxl)) goto out; #else xspice_init_qxl_ram (qxl); /* initialize the rings */ #endif #define DIV_ROUND_UP(n, a) (((n) + (a) - 1) / (a)) #define BYTES_TO_KB(bytes) DIV_ROUND_UP(bytes, 1024) #define PAGES_TO_KB(pages) ((pages) * getpagesize() / 1024) pScrn->videoRam = PAGES_TO_KB(qxl->rom->num_pages) - BYTES_TO_KB(qxl->monitors_config_size); xf86DrvMsg (scrnIndex, X_INFO, "%d KB of video RAM\n", pScrn->videoRam); xf86DrvMsg (scrnIndex, X_INFO, "%d surfaces\n", qxl->rom->n_surfaces); /* ddc stuff here */ clockRanges = xnfcalloc (sizeof (ClockRange), 1); clockRanges->next = NULL; clockRanges->minClock = 10000; clockRanges->maxClock = 400000; clockRanges->clockIndex = -1; clockRanges->interlaceAllowed = clockRanges->doubleScanAllowed = 0; clockRanges->ClockMulFactor = clockRanges->ClockDivFactor = 1; pScrn->progClock = TRUE; /* override QXL monitor stuff */ if (pScrn->monitor->nHsync <= 0) { pScrn->monitor->hsync[0].lo = 29.0; pScrn->monitor->hsync[0].hi = 160.0; pScrn->monitor->nHsync = 1; } if (pScrn->monitor->nVrefresh <= 0) { pScrn->monitor->vrefresh[0].lo = 50; pScrn->monitor->vrefresh[0].hi = 75; pScrn->monitor->nVrefresh = 1; } qxl_initialize_x_modes (qxl, pScrn, &max_x, &max_y); CHECK_POINT (); xf86PruneDriverModes (pScrn); qxl_init_randr (pScrn, qxl); xf86SetDpi (pScrn, 0, 0); if (!xf86LoadSubModule (pScrn, "fb") #ifndef XSPICE || !xf86LoadSubModule (pScrn, "ramdac") || !xf86LoadSubModule (pScrn, "vgahw") #endif ) { goto out; } print_modes (qxl, scrnIndex); #ifndef XSPICE /* VGA hardware initialisation */ if (!vgaHWGetHWRec (pScrn)) return FALSE; vgaHWSetStdFuncs (VGAHWPTR (pScrn)); #endif /* hate */ qxl_unmap_memory (qxl); CHECK_POINT (); xf86DrvMsg (scrnIndex, X_INFO, "PreInit complete\n"); #ifdef GIT_VERSION xf86DrvMsg (scrnIndex, X_INFO, "git commit %s\n", GIT_VERSION); #endif return TRUE; out: if (clockRanges) free (clockRanges); if (qxl) free (qxl); return FALSE; } #ifndef XSPICE #ifdef XSERVER_LIBPCIACCESS enum qxl_class { CHIP_QXL_1, }; static const struct pci_id_match qxl_device_match[] = { { PCI_VENDOR_RED_HAT, PCI_CHIP_QXL_0100, PCI_MATCH_ANY, PCI_MATCH_ANY, 0x00000000, 0x00000000, CHIP_QXL_1 }, { PCI_VENDOR_RED_HAT, PCI_CHIP_QXL_01FF, PCI_MATCH_ANY, PCI_MATCH_ANY, 0x00000000, 0x00000000, CHIP_QXL_1 }, { 0 }, }; #endif static SymTabRec qxlChips[] = { { PCI_CHIP_QXL_0100, "QXL 1", }, { -1, NULL } }; #ifndef XSERVER_LIBPCIACCESS static PciChipsets qxlPciChips[] = { { PCI_CHIP_QXL_0100, PCI_CHIP_QXL_0100, RES_SHARED_VGA }, { -1, -1, RES_UNDEFINED } }; #endif #endif /* !XSPICE */ static void qxl_identify (int flags) { #ifndef XSPICE xf86PrintChipsets ("qxl", "Driver for QXL virtual graphics", qxlChips); #endif } static void qxl_init_scrn (ScrnInfoPtr pScrn, Bool kms) { pScrn->driverVersion = 0; pScrn->driverName = driver_name; pScrn->name = driver_name; if (kms) { #if !defined(XSPICE) && defined(XF86DRM_MODE) pScrn->PreInit = qxl_pre_init_kms; pScrn->ScreenInit = qxl_screen_init_kms; pScrn->EnterVT = qxl_enter_vt_kms; pScrn->LeaveVT = qxl_leave_vt_kms; #endif } else { pScrn->PreInit = qxl_pre_init; pScrn->ScreenInit = qxl_screen_init; pScrn->EnterVT = qxl_enter_vt; pScrn->LeaveVT = qxl_leave_vt; } pScrn->SwitchMode = qxl_switch_mode; pScrn->ValidMode = NULL; } #if defined(XF86DRM_MODE) && !defined(XSPICE) static char * CreatePCIBusID(const struct pci_device *dev) { char *busID; if (asprintf(&busID, "pci:%04x:%02x:%02x.%d", dev->domain, dev->bus, dev->dev, dev->func) == -1) return NULL; return busID; } static Bool qxl_kernel_mode_enabled(ScrnInfoPtr pScrn, struct pci_device *pci_dev) { char *busIdString; int ret; busIdString = CreatePCIBusID(pci_dev); ret = drmCheckModesettingSupported(busIdString); free(busIdString); if (ret) { xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0, "[KMS] drm report modesetting isn't supported.\n"); return FALSE; } xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 0, "[KMS] Kernel modesetting enabled.\n"); return TRUE; } #else #define qxl_kernel_mode_enabled(x, y) FALSE #endif #ifdef XSPICE static Bool qxl_probe (struct _DriverRec *drv, int flags) { ScrnInfoPtr pScrn; int entityIndex; EntityInfoPtr pEnt; GDevPtr* device; if (flags & PROBE_DETECT) return TRUE; pScrn = xf86AllocateScreen (drv, flags); qxl_init_scrn (pScrn, FALSE); xf86MatchDevice (driver_name, &device); entityIndex = xf86ClaimNoSlot (drv, 0, device[0], TRUE); pEnt = xf86GetEntityInfo (entityIndex); pEnt->driver = drv; xf86AddEntityToScreen (pScrn, entityIndex); return TRUE; } #else /* normal, not XSPICE */ #ifndef XSERVER_LIBPCIACCESS static Bool qxl_probe (DriverPtr drv, int flags) { int i, numUsed; int numDevSections; int * usedChips; GDevPtr *devSections; if ((numDevSections = xf86MatchDevice (QXL_NAME, &devSections)) <= 0) return FALSE; if (!xf86GetPciVideoInfo ()) return FALSE; numUsed = xf86MatchPciInstances (QXL_NAME, PCI_VENDOR_RED_HAT, qxlChips, qxlPciChips, devSections, numDevSections, drv, &usedChips); xfree (devSections); if (numUsed < 0) { xfree (usedChips); return FALSE; } if (flags & PROBE_DETECT) { xfree (usedChips); return TRUE; } for (i = 0; i < numUsed; i++) { ScrnInfoPtr pScrn = NULL; if ((pScrn = xf86ConfigPciEntity (pScrn, 0, usedChips[i], qxlPciChips, 0, 0, 0, 0, 0))) qxl_init_scrn (pScrn, FALSE); } xfree (usedChips); return TRUE; } #else /* pciaccess */ static Bool qxl_pci_probe (DriverPtr drv, int entity, struct pci_device *dev, intptr_t match) { qxl_screen_t *qxl; ScrnInfoPtr pScrn = xf86ConfigPciEntity (NULL, 0, entity, NULL, NULL, NULL, NULL, NULL, NULL); Bool kms = FALSE; if (!pScrn) return FALSE; if (dev) { if (qxl_kernel_mode_enabled(pScrn, dev)) { kms = TRUE; } } if (!pScrn->driverPrivate) pScrn->driverPrivate = xnfcalloc (sizeof (qxl_screen_t), 1); qxl = pScrn->driverPrivate; qxl->pci = dev; qxl_init_scrn (pScrn, kms); return TRUE; } #define qxl_probe NULL #endif #ifdef XSERVER_PLATFORM_BUS static Bool qxl_platform_probe(DriverPtr driver, int entity, int flags, struct xf86_platform_device *dev, intptr_t match_data) { qxl_screen_t *qxl; ScrnInfoPtr pScrn; int scrnFlag = 0; if (!dev->pdev) return FALSE; if (flags & PLATFORM_PROBE_GPU_SCREEN) scrnFlag = XF86_ALLOCATE_GPU_SCREEN; pScrn = xf86AllocateScreen(driver, scrnFlag); if (!pScrn) return FALSE; if (xf86IsEntitySharable(entity)) xf86SetEntityShared(entity); xf86AddEntityToScreen(pScrn, entity); qxl = pScrn->driverPrivate = xnfcalloc (sizeof (qxl_screen_t), 1); qxl->pci = dev->pdev; qxl->platform_dev = dev; qxl_init_scrn (pScrn, qxl_kernel_mode_enabled(pScrn, dev->pdev)); return TRUE; } #endif /* XSERVER_PLATFORM_BUS */ #endif /* XSPICE */ static Bool qxl_driver_func(ScrnInfoPtr pScrn, xorgDriverFuncOp op, void *data) { xorgHWFlags *hw_flags; switch (op) { case GET_REQUIRED_HW_INTERFACES: hw_flags = data; #ifdef XSPICE *hw_flags = HW_SKIP_CONSOLE; #else *hw_flags = HW_IO | HW_MMIO; #endif return TRUE; #if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,15,99,902,0) case SUPPORTS_SERVER_FDS: return TRUE; #endif default: return FALSE; } } static DriverRec qxl_driver = { 0, driver_name, qxl_identify, qxl_probe, qxl_available_options, NULL, 0, qxl_driver_func, #ifdef XSPICE NULL, NULL, NULL, #else #ifdef XSERVER_LIBPCIACCESS qxl_device_match, qxl_pci_probe, #else NULL, NULL, #endif #ifdef XSERVER_PLATFORM_BUS qxl_platform_probe, #else NULL, #endif #endif /* XSPICE */ }; static pointer qxl_setup (pointer module, pointer opts, int *errmaj, int *errmin) { static Bool loaded = FALSE; if (!loaded) { loaded = TRUE; xf86AddDriver (&qxl_driver, module, HaveDriverFuncs); #ifdef XSPICE xspice_add_input_drivers (module); #endif return (void *)1; } else { if (errmaj) *errmaj = LDR_ONCEONLY; return NULL; } } static XF86ModuleVersionInfo qxl_module_info = { driver_name, MODULEVENDORSTRING, MODINFOSTRING1, MODINFOSTRING2, XORG_VERSION_CURRENT, PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL, ABI_CLASS_VIDEODRV, ABI_VIDEODRV_VERSION, MOD_CLASS_VIDEODRV, { 0, 0, 0, 0 } }; _X_EXPORT XF86ModuleData #ifdef XSPICE spiceqxlModuleData #else qxlModuleData #endif = { &qxl_module_info, qxl_setup, NULL }; xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_drmmode.c000066400000000000000000000660021361660122400227430ustar00rootroot00000000000000/* * Copyright © 2007 Red Hat, Inc. * * 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 (including the next * paragraph) 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 OR COPYRIGHT HOLDERS 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. * * Authors: * Dave Airlie * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifdef XF86DRM_MODE #include #include "qxl_drmmode.h" #include "X11/Xatom.h" #include "xf86DDC.h" #include #include #include "qxl.h" #include "qxl_surface.h" static void drmmode_show_cursor (xf86CrtcPtr crtc); static void drmmode_ConvertFromKMode(ScrnInfoPtr scrn, drmModeModeInfo *kmode, DisplayModePtr mode) { memset(mode, 0, sizeof(DisplayModeRec)); mode->status = MODE_OK; mode->Clock = kmode->clock; mode->HDisplay = kmode->hdisplay; mode->HSyncStart = kmode->hsync_start; mode->HSyncEnd = kmode->hsync_end; mode->HTotal = kmode->htotal; mode->HSkew = kmode->hskew; mode->VDisplay = kmode->vdisplay; mode->VSyncStart = kmode->vsync_start; mode->VSyncEnd = kmode->vsync_end; mode->VTotal = kmode->vtotal; mode->VScan = kmode->vscan; mode->Flags = kmode->flags; //& FLAG_BITS; mode->name = strdup(kmode->name); if (kmode->type & DRM_MODE_TYPE_DRIVER) mode->type = M_T_DRIVER; if (kmode->type & DRM_MODE_TYPE_PREFERRED) mode->type |= M_T_PREFERRED; xf86SetModeCrtc (mode, scrn->adjustFlags); } static void drmmode_ConvertToKMode(ScrnInfoPtr scrn, drmModeModeInfo *kmode, DisplayModePtr mode) { memset(kmode, 0, sizeof(*kmode)); kmode->clock = mode->Clock; kmode->hdisplay = mode->HDisplay; kmode->hsync_start = mode->HSyncStart; kmode->hsync_end = mode->HSyncEnd; kmode->htotal = mode->HTotal; kmode->hskew = mode->HSkew; kmode->vdisplay = mode->VDisplay; kmode->vsync_start = mode->VSyncStart; kmode->vsync_end = mode->VSyncEnd; kmode->vtotal = mode->VTotal; kmode->vscan = mode->VScan; kmode->flags = mode->Flags; //& FLAG_BITS; if (mode->name) strncpy(kmode->name, mode->name, DRM_DISPLAY_MODE_LEN); kmode->name[DRM_DISPLAY_MODE_LEN-1] = 0; } static void drmmode_crtc_dpms(xf86CrtcPtr crtc, int mode) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; // drmmode_ptr drmmode = drmmode_crtc->drmmode; drmmode_crtc->dpms_mode = mode; #if 0 /* bonghits in the randr 1.2 - uses dpms to disable crtc - bad buzz */ if (mode == DPMSModeOff) { // drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, // 0, 0, 0, NULL, 0, NULL); } #endif } static Bool drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, Rotation rotation, int x, int y) { ScrnInfoPtr pScrn = crtc->scrn; CursorPtr cursor; xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; int saved_x, saved_y; Rotation saved_rotation; DisplayModeRec saved_mode; uint32_t *output_ids; int output_count = 0; Bool ret = TRUE; int i; int fb_id; drmModeModeInfo kmode; int pitch; int height; qxl_screen_t *qxl = crtc->scrn->driverPrivate; pitch = pScrn->displayWidth * ((pScrn->bitsPerPixel + 7) >> 3); height = pScrn->virtualY; if (drmmode->fb_id == 0) { ret = drmModeAddFB(drmmode->fd, pScrn->virtualX, height, pScrn->depth, pScrn->bitsPerPixel, pitch, qxl_kms_bo_get_handle(qxl->primary->bo), &drmmode->fb_id); if (ret < 0) { ErrorF("failed to add fb\n"); return FALSE; } } saved_mode = crtc->mode; saved_x = crtc->x; saved_y = crtc->y; saved_rotation = crtc->rotation; if (mode) { crtc->mode = *mode; crtc->x = x; crtc->y = y; crtc->rotation = rotation; #if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,5,99,0,0) crtc->transformPresent = FALSE; #endif } output_ids = calloc(sizeof(uint32_t), xf86_config->num_output); if (!output_ids) { ret = FALSE; goto done; } if (mode) { for (i = 0; i < xf86_config->num_output; i++) { xf86OutputPtr output = xf86_config->output[i]; drmmode_output_private_ptr drmmode_output; if (output->crtc != crtc) continue; drmmode_output = output->driver_private; output_ids[output_count] = drmmode_output->mode_output->connector_id; output_count++; } #if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,0,0,0) crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green, crtc->gamma_blue, crtc->gamma_size); #endif drmmode_ConvertToKMode(crtc->scrn, &kmode, mode); fb_id = drmmode->fb_id; if (drmmode_crtc->rotate_fb_id) { fb_id = drmmode_crtc->rotate_fb_id; x = y = 0; } ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, fb_id, x, y, output_ids, output_count, &kmode); if (ret) { xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, "failed to set mode: %s", strerror(-ret)); return FALSE; } else { ret = TRUE; } if (crtc->scrn->pScreen) xf86CrtcSetScreenSubpixelOrder(crtc->scrn->pScreen); /* go through all the outputs and force DPMS them back on? */ for (i = 0; i < xf86_config->num_output; i++) { xf86OutputPtr output = xf86_config->output[i]; if (output->crtc != crtc) continue; output->funcs->dpms(output, DPMSModeOn); } } #if 0 if (pScrn->pScreen && !xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE)) xf86_reload_cursors(pScrn->pScreen); #endif done: if (!ret) { crtc->x = saved_x; crtc->y = saved_y; crtc->rotation = saved_rotation; crtc->mode = saved_mode; } #if defined(XF86_CRTC_VERSION) && XF86_CRTC_VERSION >= 3 else crtc->active = TRUE; #endif cursor = xf86_config->cursor; if (cursor) drmmode_show_cursor(crtc); return ret; } static void drmmode_set_cursor_colors (xf86CrtcPtr crtc, int bg, int fg) { } static void drmmode_set_cursor_position (xf86CrtcPtr crtc, int x, int y) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y); } static void drmmode_show_cursor (xf86CrtcPtr crtc) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; uint32_t handle = qxl_kms_bo_get_handle(drmmode_crtc->cursor_bo); static Bool use_set_cursor2 = TRUE; if (use_set_cursor2) { xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); CursorPtr cursor = xf86_config->cursor; int ret; ret = drmModeSetCursor2(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, handle, 64, 64, cursor->bits->xhot, cursor->bits->yhot); if (ret == -EINVAL) use_set_cursor2 = FALSE; else return; } drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, handle, 64, 64); } static void drmmode_load_cursor_argb (xf86CrtcPtr crtc, CARD32 *image) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; int i; uint32_t *ptr; /* cursor should be mapped already */ ptr = (uint32_t *)(drmmode_crtc->cursor_ptr); for (i = 0; i < 64 * 64; i++) ptr[i] = image[i]; drmmode_show_cursor(crtc); } static void drmmode_hide_cursor (xf86CrtcPtr crtc) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, 0, 64, 64); } static void drmmode_crtc_gamma_set(xf86CrtcPtr crtc, uint16_t *red, uint16_t *green, uint16_t *blue, int size) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; drmModeCrtcSetGamma(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, size, red, green, blue); } static const xf86CrtcFuncsRec drmmode_crtc_funcs = { .dpms = drmmode_crtc_dpms, .set_mode_major = drmmode_set_mode_major, .set_cursor_colors = drmmode_set_cursor_colors, .set_cursor_position = drmmode_set_cursor_position, .show_cursor = drmmode_show_cursor, .hide_cursor = drmmode_hide_cursor, .load_cursor_argb = drmmode_load_cursor_argb, .gamma_set = drmmode_crtc_gamma_set, .destroy = NULL, /* XXX */ }; static void drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num) { qxl_screen_t *qxl = pScrn->driverPrivate; xf86CrtcPtr crtc; drmmode_crtc_private_ptr drmmode_crtc; crtc = xf86CrtcCreate(pScrn, &drmmode_crtc_funcs); if (crtc == NULL) return; crtc->driverIsPerformingTransform = FALSE; drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1); drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd, drmmode->mode_res->crtcs[num]); drmmode_crtc->drmmode = drmmode; crtc->driver_private = drmmode_crtc; { int cursor_size = 64*4*64; drmmode_crtc->cursor_bo = qxl->bo_funcs->bo_alloc(qxl, cursor_size, "cursor"); if (!drmmode_crtc->cursor_bo) { ErrorF("failed to allocate cursor buffer\n"); return; } drmmode_crtc->cursor_ptr = qxl->bo_funcs->bo_map(drmmode_crtc->cursor_bo); } return; } static xf86OutputStatus drmmode_output_detect(xf86OutputPtr output) { /* go to the hw and retrieve a new output struct */ drmmode_output_private_ptr drmmode_output = output->driver_private; drmmode_ptr drmmode = drmmode_output->drmmode; xf86OutputStatus status; drmModeFreeConnector(drmmode_output->mode_output); drmmode_output->mode_output = drmModeGetConnector(drmmode->fd, drmmode_output->output_id); switch (drmmode_output->mode_output->connection) { case DRM_MODE_CONNECTED: status = XF86OutputStatusConnected; break; case DRM_MODE_DISCONNECTED: status = XF86OutputStatusDisconnected; break; default: case DRM_MODE_UNKNOWNCONNECTION: status = XF86OutputStatusUnknown; break; } return status; } static Bool drmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr pModes) { return MODE_OK; } static DisplayModePtr drmmode_output_get_modes(xf86OutputPtr output) { drmmode_output_private_ptr drmmode_output = output->driver_private; drmModeConnectorPtr koutput = drmmode_output->mode_output; drmmode_ptr drmmode = drmmode_output->drmmode; int i; DisplayModePtr Modes = NULL, Mode; drmModePropertyPtr props; xf86MonPtr mon = NULL; /* look for an EDID property */ for (i = 0; i < koutput->count_props; i++) { props = drmModeGetProperty(drmmode->fd, koutput->props[i]); if (props && (props->flags & DRM_MODE_PROP_BLOB)) { if (!strcmp(props->name, "EDID")) { if (drmmode_output->edid_blob) drmModeFreePropertyBlob(drmmode_output->edid_blob); drmmode_output->edid_blob = drmModeGetPropertyBlob(drmmode->fd, koutput->prop_values[i]); } drmModeFreeProperty(props); } } if (drmmode_output->edid_blob) { mon = xf86InterpretEDID(output->scrn->scrnIndex, drmmode_output->edid_blob->data); if (mon && drmmode_output->edid_blob->length > 128) mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA; } xf86OutputSetEDID(output, mon); /* modes should already be available */ for (i = 0; i < koutput->count_modes; i++) { Mode = xnfalloc(sizeof(DisplayModeRec)); drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i], Mode); Modes = xf86ModesAdd(Modes, Mode); } return Modes; } static void drmmode_output_destroy(xf86OutputPtr output) { drmmode_output_private_ptr drmmode_output = output->driver_private; int i; if (drmmode_output->edid_blob) drmModeFreePropertyBlob(drmmode_output->edid_blob); for (i = 0; i < drmmode_output->num_props; i++) { drmModeFreeProperty(drmmode_output->props[i].mode_prop); free(drmmode_output->props[i].atoms); } for (i = 0; i < drmmode_output->mode_output->count_encoders; i++) { drmModeFreeEncoder(drmmode_output->mode_encoders[i]); free(drmmode_output->mode_encoders); } free(drmmode_output->props); drmModeFreeConnector(drmmode_output->mode_output); free(drmmode_output); output->driver_private = NULL; } static void drmmode_output_dpms(xf86OutputPtr output, int mode) { drmmode_output_private_ptr drmmode_output = output->driver_private; drmModeConnectorPtr koutput = drmmode_output->mode_output; drmmode_ptr drmmode = drmmode_output->drmmode; drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id, drmmode_output->dpms_enum_id, mode); return; } static Bool drmmode_property_ignore(drmModePropertyPtr prop) { if (!prop) return TRUE; /* ignore blob prop */ if (prop->flags & DRM_MODE_PROP_BLOB) return TRUE; /* ignore standard property */ if (!strcmp(prop->name, "EDID") || !strcmp(prop->name, "DPMS")) return TRUE; return FALSE; } static void drmmode_output_create_resources(xf86OutputPtr output) { drmmode_output_private_ptr drmmode_output = output->driver_private; drmModeConnectorPtr mode_output = drmmode_output->mode_output; drmmode_ptr drmmode = drmmode_output->drmmode; drmModePropertyPtr drmmode_prop; int i, j, err; drmmode_output->props = calloc(mode_output->count_props, sizeof(drmmode_prop_rec)); if (!drmmode_output->props) return; drmmode_output->num_props = 0; for (i = 0, j = 0; i < mode_output->count_props; i++) { drmmode_prop = drmModeGetProperty(drmmode->fd, mode_output->props[i]); if (drmmode_property_ignore(drmmode_prop)) { drmModeFreeProperty(drmmode_prop); continue; } drmmode_output->props[j].index = i; drmmode_output->props[j].mode_prop = drmmode_prop; drmmode_output->props[j].value = mode_output->prop_values[i]; drmmode_output->num_props++; j++; } for (i = 0; i < drmmode_output->num_props; i++) { drmmode_prop_ptr p = &drmmode_output->props[i]; drmmode_prop = p->mode_prop; if (drmmode_prop->flags & DRM_MODE_PROP_RANGE) { INT32 qrange[2]; INT32 value = p->value; p->num_atoms = 1; p->atoms = calloc(p->num_atoms, sizeof(Atom)); if (!p->atoms) continue; p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE); qrange[0] = drmmode_prop->values[0]; qrange[1] = drmmode_prop->values[1]; err = RRConfigureOutputProperty(output->randr_output, p->atoms[0], FALSE, TRUE, drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE, 2, qrange); if (err != 0) { xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } err = RRChangeOutputProperty(output->randr_output, p->atoms[0], XA_INTEGER, 32, PropModeReplace, 1, &value, FALSE, TRUE); if (err != 0) { xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } } else if (drmmode_prop->flags & DRM_MODE_PROP_ENUM) { p->num_atoms = drmmode_prop->count_enums + 1; p->atoms = calloc(p->num_atoms, sizeof(Atom)); if (!p->atoms) continue; p->atoms[0] = MakeAtom(drmmode_prop->name, strlen(drmmode_prop->name), TRUE); for (j = 1; j <= drmmode_prop->count_enums; j++) { struct drm_mode_property_enum *e = &drmmode_prop->enums[j-1]; p->atoms[j] = MakeAtom(e->name, strlen(e->name), TRUE); } err = RRConfigureOutputProperty(output->randr_output, p->atoms[0], FALSE, FALSE, drmmode_prop->flags & DRM_MODE_PROP_IMMUTABLE ? TRUE : FALSE, p->num_atoms - 1, (INT32 *)&p->atoms[1]); if (err != 0) { xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "RRConfigureOutputProperty error, %d\n", err); } for (j = 0; j < drmmode_prop->count_enums; j++) if (drmmode_prop->enums[j].value == p->value) break; /* there's always a matching value */ err = RRChangeOutputProperty(output->randr_output, p->atoms[0], XA_ATOM, 32, PropModeReplace, 1, &p->atoms[j+1], FALSE, TRUE); if (err != 0) { xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, "RRChangeOutputProperty error, %d\n", err); } } } } static Bool drmmode_output_set_property(xf86OutputPtr output, Atom property, RRPropertyValuePtr value) { drmmode_output_private_ptr drmmode_output = output->driver_private; drmmode_ptr drmmode = drmmode_output->drmmode; int i; for (i = 0; i < drmmode_output->num_props; i++) { drmmode_prop_ptr p = &drmmode_output->props[i]; if (p->atoms[0] != property) continue; if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) { uint32_t val; if (value->type != XA_INTEGER || value->format != 32 || value->size != 1) return FALSE; val = *(uint32_t *)value->data; drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id, p->mode_prop->prop_id, (uint64_t)val); return TRUE; } else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) { Atom atom; const char *name; int j; if (value->type != XA_ATOM || value->format != 32 || value->size != 1) return FALSE; memcpy(&atom, value->data, 4); if (!(name = NameForAtom(atom))) return FALSE; /* search for matching name string, then set its value down */ for (j = 0; j < p->mode_prop->count_enums; j++) { if (!strcmp(p->mode_prop->enums[j].name, name)) { drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id, p->mode_prop->prop_id, p->mode_prop->enums[j].value); return TRUE; } } } } return TRUE; } static Bool drmmode_output_get_property(xf86OutputPtr output, Atom property) { drmmode_output_private_ptr drmmode_output = output->driver_private; drmmode_ptr drmmode = drmmode_output->drmmode; uint32_t value; int err, i; if (output->scrn->vtSema) { drmModeFreeConnector(drmmode_output->mode_output); drmmode_output->mode_output = drmModeGetConnector(drmmode->fd, drmmode_output->output_id); } if (!drmmode_output->mode_output) return FALSE; for (i = 0; i < drmmode_output->num_props; i++) { drmmode_prop_ptr p = &drmmode_output->props[i]; if (p->atoms[0] != property) continue; value = drmmode_output->mode_output->prop_values[p->index]; if (p->mode_prop->flags & DRM_MODE_PROP_RANGE) { err = RRChangeOutputProperty(output->randr_output, property, XA_INTEGER, 32, PropModeReplace, 1, &value, FALSE, FALSE); return !err; } else if (p->mode_prop->flags & DRM_MODE_PROP_ENUM) { int j; /* search for matching name string, then set its value down */ for (j = 0; j < p->mode_prop->count_enums; j++) { if (p->mode_prop->enums[j].value == value) break; } err = RRChangeOutputProperty(output->randr_output, property, XA_ATOM, 32, PropModeReplace, 1, &p->atoms[j+1], FALSE, FALSE); return !err; } } return FALSE; } static const xf86OutputFuncsRec drmmode_output_funcs = { .dpms = drmmode_output_dpms, .create_resources = drmmode_output_create_resources, #ifdef RANDR_12_INTERFACE .set_property = drmmode_output_set_property, .get_property = drmmode_output_get_property, #endif #if 0 .save = drmmode_crt_save, .restore = drmmode_crt_restore, .mode_fixup = drmmode_crt_mode_fixup, .prepare = drmmode_output_prepare, .mode_set = drmmode_crt_mode_set, .commit = drmmode_output_commit, #endif .detect = drmmode_output_detect, .mode_valid = drmmode_output_mode_valid, .get_modes = drmmode_output_get_modes, .destroy = drmmode_output_destroy }; static int subpixel_conv_table[7] = { 0, SubPixelUnknown, SubPixelHorizontalRGB, SubPixelHorizontalBGR, SubPixelVerticalRGB, SubPixelVerticalBGR, SubPixelNone }; const char *output_names[] = { "None", "VGA", "DVI-I", "DVI-D", "DVI-A", "Composite", "SVIDEO", "LVDS", "Component", "DIN", "DP", "HDMI", "HDMI-B", "TV", "eDP", "Virtual", "DSI", "DPI", }; static void drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num) { xf86OutputPtr output; drmModeConnectorPtr koutput; drmModeEncoderPtr *kencoders = NULL; drmmode_output_private_ptr drmmode_output; drmModePropertyPtr props; char name[32]; int i; koutput = drmModeGetConnector(drmmode->fd, drmmode->mode_res->connectors[num]); if (!koutput) return; kencoders = calloc(sizeof(drmModeEncoderPtr), koutput->count_encoders); if (!kencoders) { goto out_free_encoders; } for (i = 0; i < koutput->count_encoders; i++) { kencoders[i] = drmModeGetEncoder(drmmode->fd, koutput->encoders[i]); if (!kencoders[i]) { goto out_free_encoders; } } snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id); output = xf86OutputCreate (pScrn, &drmmode_output_funcs, name); if (!output) { goto out_free_encoders; } drmmode_output = calloc(sizeof(drmmode_output_private_rec), 1); if (!drmmode_output) { xf86OutputDestroy(output); goto out_free_encoders; } drmmode_output->output_id = drmmode->mode_res->connectors[num]; drmmode_output->mode_output = koutput; drmmode_output->mode_encoders = kencoders; drmmode_output->drmmode = drmmode; output->mm_width = koutput->mmWidth; output->mm_height = koutput->mmHeight; output->subpixel_order = subpixel_conv_table[koutput->subpixel]; output->interlaceAllowed = TRUE; output->doubleScanAllowed = TRUE; output->driver_private = drmmode_output; output->possible_crtcs = 0xffffffff; for (i = 0; i < koutput->count_encoders; i++) { output->possible_crtcs &= kencoders[i]->possible_crtcs; } /* work out the possible clones later */ output->possible_clones = 0; for (i = 0; i < koutput->count_props; i++) { props = drmModeGetProperty(drmmode->fd, koutput->props[i]); if (props && (props->flags & DRM_MODE_PROP_ENUM)) { if (!strcmp(props->name, "DPMS")) { drmmode_output->dpms_enum_id = koutput->props[i]; drmModeFreeProperty(props); break; } drmModeFreeProperty(props); } } return; out_free_encoders: if (kencoders){ for (i = 0; i < koutput->count_encoders; i++) drmModeFreeEncoder(kencoders[i]); free(kencoders); } drmModeFreeConnector(koutput); } static Bool drmmode_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height) { xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); drmmode_crtc_private_ptr drmmode_crtc = xf86_config->crtc[0]->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; struct qxl_bo *old_front = NULL; struct qxl_bo *front_bo; qxl_screen_t *qxl = scrn->driverPrivate; int cpp = (scrn->bitsPerPixel + 7) / 8; int32_t pitch, old_pitch; int ret, i; uint32_t old_width, old_height, old_fb_id; if (scrn->virtualX == width && scrn->virtualY == height) return TRUE; xf86DrvMsg(scrn->scrnIndex, X_INFO, "Allocate new frame buffer %dx%d stride\n", width, height); front_bo = qxl->primary->bo; pitch = width * cpp; old_width = scrn->virtualX; old_height = scrn->virtualY; old_pitch = scrn->displayWidth; old_fb_id = drmmode->fb_id; old_front = front_bo; scrn->virtualX = width; scrn->virtualY = height; scrn->displayWidth = pitch / cpp; qxl->primary->bo = qxl->bo_funcs->create_primary(qxl, width, height, pitch, SPICE_SURFACE_FMT_32_xRGB); if (!qxl->primary->bo) goto fail; ret = drmModeAddFB(drmmode->fd, width, height, scrn->depth, scrn->bitsPerPixel, pitch, qxl_kms_bo_get_handle(qxl->primary->bo), &drmmode->fb_id); if (ret) goto fail; for (i = 0; i < xf86_config->num_crtc; i++) { xf86CrtcPtr crtc = xf86_config->crtc[i]; if (!crtc->enabled) continue; if (!drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation, crtc->x, crtc->y)) goto fail; } { void *dev_ptr = qxl->bo_funcs->bo_map(qxl->primary->bo); uint32_t *dev_addr; int format = scrn->bitsPerPixel == 16 ? PIXMAN_x1r5g5b5 : PIXMAN_x8r8g8b8; dev_addr = dev_ptr; pixman_image_unref(qxl->primary->dev_image); pixman_image_unref (qxl->primary->host_image); qxl->primary->dev_image = pixman_image_create_bits (format, width, height, (uint32_t *)dev_addr, pitch); qxl->primary->host_image = pixman_image_create_bits (format, width, height, NULL, pitch); } /* fixup the surfaces */ if (old_fb_id) drmModeRmFB(drmmode->fd, old_fb_id); if (old_front) qxl->bo_funcs->bo_decref(qxl, old_front); return TRUE; fail: qxl->primary->bo = old_front; scrn->virtualX = old_width; scrn->virtualY = old_height; scrn->displayWidth = old_pitch; drmmode->fb_id = old_fb_id; return FALSE; } static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = { drmmode_xf86crtc_resize, }; Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp) { int i; xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs); drmmode->scrn = pScrn; drmmode->cpp = cpp; drmmode->mode_res = drmModeGetResources(drmmode->fd); if (!drmmode->mode_res) return FALSE; xf86CrtcSetSizeRange(pScrn, 320, 200, drmmode->mode_res->max_width, drmmode->mode_res->max_height); for (i = 0; i < drmmode->mode_res->count_crtcs; i++) drmmode_crtc_init(pScrn, drmmode, i); for (i = 0; i < drmmode->mode_res->count_connectors; i++) drmmode_output_init(pScrn, drmmode, i); #if XF86_CRTC_VERSION >= 5 xf86ProviderSetup(pScrn, NULL, "qxl"); #endif xf86InitialConfiguration(pScrn, TRUE); return TRUE; } #ifdef HAVE_LIBUDEV static void drmmode_handle_uevents(int fd, void *closure) { drmmode_ptr drmmode = closure; ScrnInfoPtr scrn = drmmode->scrn; struct udev_device *dev; dev = udev_monitor_receive_device(drmmode->uevent_monitor); if (!dev) return; RRGetInfo(xf86ScrnToScreen(scrn), TRUE); udev_device_unref(dev); } #endif void qxl_drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode) { #ifdef HAVE_LIBUDEV struct udev *u; struct udev_monitor *mon; u = udev_new(); if (!u) return; mon = udev_monitor_new_from_netlink(u, "udev"); if (!mon) { udev_unref(u); return; } if (udev_monitor_filter_add_match_subsystem_devtype(mon, "drm", "drm_minor") < 0 || udev_monitor_enable_receiving(mon) < 0) { udev_monitor_unref(mon); udev_unref(u); return; } drmmode->uevent_handler = xf86AddGeneralHandler(udev_monitor_get_fd(mon), drmmode_handle_uevents, drmmode); drmmode->uevent_monitor = mon; #endif } void qxl_drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode) { #ifdef HAVE_LIBUDEV if (drmmode->uevent_handler) { struct udev *u = udev_monitor_get_udev(drmmode->uevent_monitor); xf86RemoveGeneralHandler(drmmode->uevent_handler); udev_monitor_unref(drmmode->uevent_monitor); udev_unref(u); } #endif } #endif xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_drmmode.h000066400000000000000000000055371361660122400227560ustar00rootroot00000000000000/* * Copyright © 2007 Red Hat, Inc. * * 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 (including the next * paragraph) 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 OR COPYRIGHT HOLDERS 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. * * Authors: * Dave Airlie * */ #ifndef DRMMODE_DISPLAY_H #define DRMMODE_DISPLAY_H #ifdef XF86DRM_MODE #include "xf86drm.h" #include "xf86drmMode.h" #include "xf86str.h" #include "randrstr.h" #include "xf86Crtc.h" #ifdef HAVE_LIBUDEV #include "libudev.h" #endif typedef struct { int fd; unsigned fb_id; drmModeResPtr mode_res; drmModeFBPtr mode_fb; int cpp; ScrnInfoPtr scrn; #ifdef HAVE_LIBUDEV struct udev_monitor *uevent_monitor; InputHandlerProc uevent_handler; #endif } drmmode_rec, *drmmode_ptr; typedef struct { drmmode_ptr drmmode; drmModeCrtcPtr mode_crtc; int hw_id; struct qxl_bo *cursor_bo; void *cursor_ptr; // struct radeon_bo *rotate_bo; unsigned rotate_fb_id; int dpms_mode; uint16_t lut_r[256], lut_g[256], lut_b[256]; } drmmode_crtc_private_rec, *drmmode_crtc_private_ptr; typedef struct { drmModePropertyPtr mode_prop; uint64_t value; int num_atoms; /* if range prop, num_atoms == 1; if enum prop, num_atoms == num_enums + 1 */ Atom *atoms; int index; /* index within the kernel-size property array */ } drmmode_prop_rec, *drmmode_prop_ptr; typedef struct { drmmode_ptr drmmode; int output_id; drmModeConnectorPtr mode_output; drmModeEncoderPtr *mode_encoders; drmModePropertyBlobPtr edid_blob; int dpms_enum_id; int num_props; drmmode_prop_ptr props; int enc_mask; int enc_clone_mask; } drmmode_output_private_rec, *drmmode_output_private_ptr; extern Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp); extern void qxl_drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode); extern void qxl_drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode); #endif #endif xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_image.c000066400000000000000000000135751361660122400224050ustar00rootroot00000000000000/* * Copyright 2009, 2010 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. * */ /** \file QXLImage.c * \author Søren Sandmann */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "qxl.h" #include "murmurhash3.h" static unsigned int hash_and_copy (const uint8_t *src, int src_stride, uint8_t *dest, int dest_stride, int bytes_per_pixel, int width, int height, uint32_t hash) { int i; for (i = 0; i < height; ++i) { const uint8_t *src_line = src + i * src_stride; uint8_t *dest_line = dest + i * dest_stride; int n_bytes = width * bytes_per_pixel; if (n_bytes > src_stride) n_bytes = src_stride; if (dest) memcpy (dest_line, src_line, n_bytes); MurmurHash3_x86_32 (src_line, n_bytes, hash, &hash); } return hash; } struct qxl_bo * qxl_image_create (qxl_screen_t *qxl, const uint8_t *data, int x, int y, int width, int height, int stride, int Bpp, Bool fallback) { uint32_t hash; struct QXLImage *image; struct qxl_bo *head_bo, *tail_bo; struct qxl_bo *image_bo; int dest_stride = (width * Bpp + 3) & (~3); int h; int chunk_size; data += y * stride + x * Bpp; #if 0 ErrorF ("Must create new image of size %d %d\n", width, height); #endif /* Chunk */ /* FIXME: Check integer overflow */ head_bo = tail_bo = NULL; hash = 0; h = height; chunk_size = MAX (512 * 512, dest_stride); #ifdef XF86DRM_MODE /* ensure we will not create too many pieces and overflow * the command buffer (MAX_RELOCS). if so, increase the chunk_size. * each loop creates at least 2 cmd buffer entries, and * we have to leave room when we're done. */ if (height / (chunk_size / dest_stride) > (MAX_RELOCS / 4)) { chunk_size = height / (MAX_RELOCS/4) * dest_stride; #if 0 ErrorF ("adjusted chunk_size to %d\n", chunk_size); #endif } #endif while (h) { int n_lines = MIN ((chunk_size / dest_stride), h); struct qxl_bo *bo = qxl->bo_funcs->bo_alloc (qxl, sizeof (QXLDataChunk) + n_lines * dest_stride, "image data"); QXLDataChunk *chunk = qxl->bo_funcs->bo_map(bo); chunk->data_size = n_lines * dest_stride; hash = hash_and_copy (data, stride, chunk->data, dest_stride, Bpp, width, n_lines, hash); if (tail_bo) { qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDataChunk, next_chunk), tail_bo, bo); qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDataChunk, prev_chunk), bo, tail_bo); chunk->next_chunk = 0; tail_bo = bo; } else { head_bo = tail_bo = bo; chunk->next_chunk = 0; chunk->prev_chunk = 0; } qxl->bo_funcs->bo_unmap(bo); if (bo != head_bo) qxl->bo_funcs->bo_decref(qxl, bo); data += n_lines * stride; h -= n_lines; } /* Image */ image_bo = qxl->bo_funcs->bo_alloc (qxl, sizeof *image, "image struct"); image = qxl->bo_funcs->bo_map(image_bo); image->descriptor.id = 0; image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP; image->descriptor.flags = 0; image->descriptor.width = width; image->descriptor.height = height; if (Bpp == 2) { image->bitmap.format = SPICE_BITMAP_FMT_16BIT; } else if (Bpp == 1) { image->bitmap.format = SPICE_BITMAP_FMT_8BIT_A; } else if (Bpp == 4) { image->bitmap.format = SPICE_BITMAP_FMT_RGBA; } else { abort(); } image->bitmap.flags = SPICE_BITMAP_FLAGS_TOP_DOWN; image->bitmap.x = width; image->bitmap.y = height; image->bitmap.stride = dest_stride; image->bitmap.palette = 0; qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLImage, bitmap.data), image_bo, head_bo); qxl->bo_funcs->bo_decref(qxl, head_bo); /* Add to hash table if caching is enabled */ if ((fallback && qxl->enable_fallback_cache) || (!fallback && qxl->enable_image_cache)) { image->descriptor.id = hash; image->descriptor.flags = QXL_IMAGE_CACHE; #if 0 ErrorF ("added with hash %u\n", hash); #endif } qxl->bo_funcs->bo_unmap(image_bo); return image_bo; } void qxl_image_destroy (qxl_screen_t *qxl, struct qxl_bo *image_bo) { struct QXLImage *image; uint64_t chunk, prev_chunk; image = qxl->bo_funcs->bo_map(image_bo); qxl->bo_funcs->bo_unmap(image_bo); image = qxl->bo_funcs->bo_map(image_bo); chunk = image->bitmap.data; while (chunk) { struct qxl_bo *bo; struct QXLDataChunk *virtual; bo = qxl_ums_lookup_phy_addr(qxl, chunk); assert(bo); virtual = qxl->bo_funcs->bo_map(bo); chunk = virtual->next_chunk; prev_chunk = virtual->prev_chunk; qxl->bo_funcs->bo_unmap(bo); qxl->bo_funcs->bo_decref (qxl, bo); if (prev_chunk) { bo = qxl_ums_lookup_phy_addr(qxl, prev_chunk); assert(bo); qxl->bo_funcs->bo_decref (qxl, bo); } } qxl->bo_funcs->bo_unmap(image_bo); qxl->bo_funcs->bo_decref (qxl, image_bo); } xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_io.c000066400000000000000000000114511361660122400217210ustar00rootroot00000000000000/* * Copyright 2008 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ /* all the IO routines for QXL userspace code */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "qxl.h" #ifdef XSPICE #include "spiceqxl_display.h" #endif #ifndef XSPICE static void qxl_wait_for_io_command (qxl_screen_t *qxl) { struct QXLRam *ram_header; ram_header = (void *)((unsigned long)qxl->ram + qxl->rom->ram_header_offset); while (!(ram_header->int_pending & (QXL_INTERRUPT_IO_CMD | QXL_INTERRUPT_ERROR))) usleep (1); assert(!(ram_header->int_pending & QXL_INTERRUPT_ERROR)); ram_header->int_pending &= ~QXL_INTERRUPT_IO_CMD; } #if 0 static void qxl_wait_for_display_interrupt (qxl_screen_t *qxl) { struct QXLRam *ram_header; ram_header = (void *)((unsigned long)qxl->ram + qxl->rom->ram_header_offset); while (!(ram_header->int_pending & QXL_INTERRUPT_DISPLAY)) usleep (1); ram_header->int_pending &= ~QXL_INTERRUPT_DISPLAY; } #endif #endif void qxl_update_area (qxl_screen_t *qxl) { #ifndef XSPICE if (qxl->pci->revision >= 3) { ioport_write (qxl, QXL_IO_UPDATE_AREA_ASYNC, 0); qxl_wait_for_io_command (qxl); } else { ioport_write (qxl, QXL_IO_UPDATE_AREA, 0); } #else ioport_write (qxl, QXL_IO_UPDATE_AREA, 0); #endif } void qxl_io_memslot_add (qxl_screen_t *qxl, uint8_t id) { #ifndef XSPICE if (qxl->pci->revision >= 3) { ioport_write (qxl, QXL_IO_MEMSLOT_ADD_ASYNC, id); qxl_wait_for_io_command (qxl); } else { ioport_write (qxl, QXL_IO_MEMSLOT_ADD, id); } #else ioport_write (qxl, QXL_IO_MEMSLOT_ADD, id); #endif } void qxl_io_create_primary (qxl_screen_t *qxl) { #ifndef XSPICE if (qxl->pci->revision >= 3) { ioport_write (qxl, QXL_IO_CREATE_PRIMARY_ASYNC, 0); qxl_wait_for_io_command (qxl); } else { ioport_write (qxl, QXL_IO_CREATE_PRIMARY, 0); } #else ioport_write (qxl, QXL_IO_CREATE_PRIMARY, 0); #endif qxl->device_primary = QXL_DEVICE_PRIMARY_CREATED; } void qxl_io_destroy_primary (qxl_screen_t *qxl) { #ifndef XSPICE if (qxl->pci->revision >= 3) { ioport_write (qxl, QXL_IO_DESTROY_PRIMARY_ASYNC, 0); qxl_wait_for_io_command (qxl); } else { ioport_write (qxl, QXL_IO_DESTROY_PRIMARY, 0); } #else ioport_write (qxl, QXL_IO_DESTROY_PRIMARY, 0); #endif qxl->device_primary = QXL_DEVICE_PRIMARY_NONE; } void qxl_io_notify_oom (qxl_screen_t *qxl) { ioport_write (qxl, QXL_IO_NOTIFY_OOM, 0); } void qxl_io_flush_surfaces (qxl_screen_t *qxl) { // FIXME: write individual update_area for revision < V10 #ifndef XSPICE ioport_write (qxl, QXL_IO_FLUSH_SURFACES_ASYNC, 0); qxl_wait_for_io_command (qxl); #else ioport_write (qxl, QXL_IO_FLUSH_SURFACES_ASYNC, 0); #endif } #ifdef QXLDRV_RESIZABLE_SURFACE0 void qxl_io_flush_release (qxl_screen_t *qxl) { #ifndef XSPICE int sum = 0; sum += qxl_garbage_collect (qxl); ioport_write (qxl, QXL_IO_FLUSH_RELEASE, 0); sum += qxl_garbage_collect (qxl); ErrorF ("%s: collected %d\n", __func__, sum); #else #endif } #endif void qxl_io_monitors_config_async (qxl_screen_t *qxl) { #ifndef XSPICE if (qxl->pci->revision < 4) return; ioport_write (qxl, QXL_IO_MONITORS_CONFIG_ASYNC, 0); qxl_wait_for_io_command (qxl); #else spiceqxl_display_monitors_config(qxl); #endif } void qxl_io_destroy_all_surfaces (qxl_screen_t *qxl) { #ifndef XSPICE if (qxl->pci->revision >= 3) { ioport_write (qxl, QXL_IO_DESTROY_ALL_SURFACES_ASYNC, 0); qxl_wait_for_io_command (qxl); } else { ioport_write (qxl, QXL_IO_DESTROY_ALL_SURFACES, 0); } #else ErrorF ("Xspice: error: UNIMPLEMENTED qxl_io_destroy_all_surfaces\n"); #endif qxl->device_primary = QXL_DEVICE_PRIMARY_NONE; } xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_kms.c000066400000000000000000000471761361660122400221210ustar00rootroot00000000000000/* * Copyright 2013-2014 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifdef HAVE_CONFIG_H #include #endif #ifdef XF86DRM_MODE #include #include #include #include "qxl.h" #include "qxl_surface.h" Bool qxl_kms_check_cap(qxl_screen_t *qxl, int idx) { int ret; struct drm_qxl_clientcap cap; cap.index = idx; ret = drmIoctl(qxl->drm_fd, DRM_IOCTL_QXL_CLIENTCAP, &cap); if (ret == 0) return TRUE; return FALSE; } #if 0 static Bool qxl_kms_getparam(qxl_screen_t *qxl, uint64_t param, uint64_t *value) { int ret; struct drm_qxl_getparam args = {0}; args.param = param; ret = drmIoctl(qxl->drm_fd, DRM_IOCTL_QXL_GETPARAM, &args); if (ret != 0) return FALSE; *value = args.value; return TRUE; } #endif static Bool qxl_open_drm_master(ScrnInfoPtr pScrn) { qxl_screen_t *qxl = pScrn->driverPrivate; struct pci_device *dev = qxl->pci; char *busid; drmSetVersion sv; int err; #if defined(ODEV_ATTRIB_FD) if (qxl->platform_dev) { qxl->drm_fd = xf86_get_platform_device_int_attrib(qxl->platform_dev, ODEV_ATTRIB_FD, -1); if (qxl->drm_fd != -1) { qxl->drmmode.fd = qxl->drm_fd; return TRUE; } } #endif #if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,9,99,901,0) XNFasprintf(&busid, "pci:%04x:%02x:%02x.%d", dev->domain, dev->bus, dev->dev, dev->func); #else busid = XNFprintf("pci:%04x:%02x:%02x.%d", dev->domain, dev->bus, dev->dev, dev->func); #endif qxl->drm_fd = drmOpen("qxl", busid); if (qxl->drm_fd == -1) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "[drm] Failed to open DRM device for %s: %s\n", busid, strerror(errno)); free(busid); return FALSE; } free(busid); /* Check that what we opened was a master or a master-capable FD, * by setting the version of the interface we'll use to talk to it. * (see DRIOpenDRMMaster() in DRI1) */ sv.drm_di_major = 1; sv.drm_di_minor = 1; sv.drm_dd_major = -1; sv.drm_dd_minor = -1; err = drmSetInterfaceVersion(qxl->drm_fd, &sv); if (err != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "[drm] failed to set drm interface version.\n"); drmClose(qxl->drm_fd); qxl->drm_fd = -1; return FALSE; } qxl->drmmode.fd = qxl->drm_fd; return TRUE; } static Bool qxl_close_screen_kms (CLOSE_SCREEN_ARGS_DECL) { ScrnInfoPtr pScrn = xf86ScreenToScrn (pScreen); qxl_screen_t *qxl = pScrn->driverPrivate; Bool result; qxl_drmmode_uevent_fini(pScrn, &qxl->drmmode); pScreen->CloseScreen = qxl->close_screen; result = pScreen->CloseScreen (CLOSE_SCREEN_ARGS); return result; } Bool qxl_pre_init_kms(ScrnInfoPtr pScrn, int flags) { int scrnIndex = pScrn->scrnIndex; qxl_screen_t *qxl = NULL; if (!pScrn->confScreen) return FALSE; /* zaphod mode is for suckers and i choose not to implement it */ if (xf86IsEntityShared (pScrn->entityList[0])) { xf86DrvMsg (scrnIndex, X_ERROR, "No Zaphod mode for you\n"); return FALSE; } if (!pScrn->driverPrivate) pScrn->driverPrivate = xnfcalloc (sizeof (qxl_screen_t), 1); qxl = pScrn->driverPrivate; qxl->device_primary = QXL_DEVICE_PRIMARY_UNDEFINED; qxl->pScrn = pScrn; qxl->x_modes = NULL; qxl->entity = xf86GetEntityInfo (pScrn->entityList[0]); qxl->kms_enabled = TRUE; xorg_list_init(&qxl->ums_bos); qxl_kms_setup_funcs(qxl); qxl->pci = xf86GetPciInfoForEntity (qxl->entity->index); pScrn->monitor = pScrn->confScreen->monitor; if (qxl_open_drm_master(pScrn) == FALSE) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Kernel modesetting setup failed\n"); goto out; } if (!qxl_pre_init_common(pScrn)) goto out; xf86SetDpi (pScrn, 0, 0); if (!xf86LoadSubModule (pScrn, "fb")) goto out; if (!xf86LoadSubModule (pScrn, "ramdac")) goto out; if (drmmode_pre_init(pScrn, &qxl->drmmode, pScrn->bitsPerPixel / 8) == FALSE) goto out; qxl->virtual_x = pScrn->virtualX; qxl->virtual_y = pScrn->virtualY; pScrn->display->virtualX = qxl->virtual_x; pScrn->display->virtualY = qxl->virtual_y; xf86DrvMsg (scrnIndex, X_INFO, "PreInit complete\n"); #ifdef GIT_VERSION xf86DrvMsg (scrnIndex, X_INFO, "git commit %s\n", GIT_VERSION); #endif return TRUE; out: if (qxl) free(qxl); return FALSE; } static Bool qxl_create_screen_resources_kms(ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86ScreenToScrn (pScreen); qxl_screen_t * qxl = pScrn->driverPrivate; Bool ret; PixmapPtr pPixmap; qxl_surface_t *surf; pScreen->CreateScreenResources = qxl->create_screen_resources; ret = pScreen->CreateScreenResources (pScreen); pScreen->CreateScreenResources = qxl_create_screen_resources_kms; if (!ret) return FALSE; pPixmap = pScreen->GetScreenPixmap (pScreen); qxl_set_screen_pixmap_header (pScreen); if ((surf = get_surface (pPixmap))) qxl->bo_funcs->destroy_surface(surf); set_surface (pPixmap, qxl->primary); qxl_drmmode_uevent_init(pScrn, &qxl->drmmode); if (!uxa_resources_init (pScreen)) return FALSE; qxl->screen_resources_created = TRUE; return TRUE; } static Bool qxl_blank_screen (ScreenPtr pScreen, int mode) { return TRUE; } Bool qxl_enter_vt_kms (VT_FUNC_ARGS_DECL) { SCRN_INFO_PTR (arg); qxl_screen_t *qxl = pScrn->driverPrivate; int ret; #ifdef XF86_PDEV_SERVER_FD if (!(qxl->platform_dev && (qxl->platform_dev->flags & XF86_PDEV_SERVER_FD))) #endif { ret = drmSetMaster(qxl->drm_fd); if (ret) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "drmSetMaster failed: %s\n", strerror(errno)); } } if (!xf86SetDesiredModes(pScrn)) return FALSE; // pScrn->EnableDisableFBAccess (XF86_SCRN_ARG (pScrn), TRUE); return TRUE; } void qxl_leave_vt_kms (VT_FUNC_ARGS_DECL) { SCRN_INFO_PTR (arg); int ret; qxl_screen_t *qxl = pScrn->driverPrivate; xf86_hide_cursors (pScrn); // pScrn->EnableDisableFBAccess (XF86_SCRN_ARG (pScrn), FALSE); #ifdef XF86_PDEV_SERVER_FD if (qxl->platform_dev && (qxl->platform_dev->flags & XF86_PDEV_SERVER_FD)) return; #endif ret = drmDropMaster(qxl->drm_fd); if (ret) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "drmDropMaster failed: %s\n", strerror(errno)); } } Bool qxl_screen_init_kms(SCREEN_INIT_ARGS_DECL) { ScrnInfoPtr pScrn = xf86ScreenToScrn (pScreen); qxl_screen_t * qxl = pScrn->driverPrivate; VisualPtr visual; miClearVisualTypes (); if (!miSetVisualTypes (pScrn->depth, miGetDefaultVisualMask (pScrn->depth), pScrn->rgbBits, pScrn->defaultVisual)) goto out; if (!miSetPixmapDepths ()) goto out; pScrn->displayWidth = pScrn->virtualX; if (!qxl_fb_init (qxl, pScreen)) goto out; visual = pScreen->visuals + pScreen->numVisuals; while (--visual >= pScreen->visuals) { if ((visual->class | DynamicClass) == DirectColor) { visual->offsetRed = pScrn->offset.red; visual->offsetGreen = pScrn->offset.green; visual->offsetBlue = pScrn->offset.blue; visual->redMask = pScrn->mask.red; visual->greenMask = pScrn->mask.green; visual->blueMask = pScrn->mask.blue; } } qxl->uxa = uxa_driver_alloc (); // GETPARAM /* no surface cache for kms surfaces for now */ #if 0 if (!qxl_kms_getparam(qxl, QXL_PARAM_NUM_SURFACES, &n_surf)) n_surf = 1024; qxl->surface_cache = qxl_surface_cache_create (qxl, n_surf); #endif pScreen->SaveScreen = qxl_blank_screen; qxl_uxa_init (qxl, pScreen); DamageSetup (pScreen); miDCInitialize (pScreen, xf86GetPointerScreenFuncs()); xf86_cursors_init (pScreen, 64, 64, (HARDWARE_CURSOR_TRUECOLOR_AT_8BPP | HARDWARE_CURSOR_AND_SOURCE_WITH_MASK | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 | HARDWARE_CURSOR_UPDATE_UNHIDDEN | HARDWARE_CURSOR_ARGB)); if (!miCreateDefColormap (pScreen)) goto out; if (!xf86CrtcScreenInit (pScreen)) return FALSE; if (!qxl_resize_primary_to_virtual (qxl)) return FALSE; qxl->create_screen_resources = pScreen->CreateScreenResources; pScreen->CreateScreenResources = qxl_create_screen_resources_kms; qxl->close_screen = pScreen->CloseScreen; pScreen->CloseScreen = qxl_close_screen_kms; return qxl_enter_vt_kms(VT_FUNC_ARGS); out: return FALSE; } #define QXL_BO_DATA 1 #define QXL_BO_SURF 2 #define QXL_BO_CMD 4 #define QXL_BO_SURF_PRIMARY 8 struct qxl_kms_bo { uint32_t handle; const char *name; uint32_t size; int type; xorg_list_t bos; void *mapping; qxl_screen_t *qxl; int refcnt; }; static struct qxl_bo *qxl_bo_alloc(qxl_screen_t *qxl, unsigned long size, const char *name) { struct qxl_kms_bo *bo; struct drm_qxl_alloc alloc; int ret; bo = calloc(1, sizeof(struct qxl_kms_bo)); if (!bo) return NULL; alloc.size = size; alloc.handle = 0; ret = drmIoctl(qxl->drm_fd, DRM_IOCTL_QXL_ALLOC, &alloc); if (ret) { xf86DrvMsg(qxl->pScrn->scrnIndex, X_ERROR, "error doing QXL_ALLOC\n"); free(bo); return NULL; // an invalid handle } bo->name = name; bo->size = size; bo->type = QXL_BO_DATA; bo->handle = alloc.handle; bo->qxl = qxl; bo->refcnt = 1; return (struct qxl_bo *)bo; } static struct qxl_bo *qxl_cmd_alloc(qxl_screen_t *qxl, unsigned long size, const char *name) { struct qxl_kms_bo *bo; bo = calloc(1, sizeof(struct qxl_kms_bo)); if (!bo) return NULL; bo->mapping = malloc(size); if (!bo->mapping) { free(bo); return NULL; } bo->name = name; bo->size = size; bo->type = QXL_BO_CMD; bo->handle = 0; bo->qxl = qxl; bo->refcnt = 1; return (struct qxl_bo *)bo; } static void *qxl_bo_map(struct qxl_bo *_bo) { struct qxl_kms_bo *bo = (struct qxl_kms_bo *)_bo; void *map; struct drm_qxl_map qxl_map; qxl_screen_t *qxl; if (!bo) return NULL; qxl = bo->qxl; if (bo->mapping) return bo->mapping; memset(&qxl_map, 0, sizeof(qxl_map)); qxl_map.handle = bo->handle; if (drmIoctl(qxl->drm_fd, DRM_IOCTL_QXL_MAP, &qxl_map)) { xf86DrvMsg(qxl->pScrn->scrnIndex, X_ERROR, "error doing QXL_MAP: %s\n", strerror(errno)); return NULL; } map = mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, qxl->drm_fd, qxl_map.offset); if (map == MAP_FAILED) { xf86DrvMsg(qxl->pScrn->scrnIndex, X_ERROR, "mmap failure: %s\n", strerror(errno)); return NULL; } bo->mapping = map; return bo->mapping; } static void qxl_bo_unmap(struct qxl_bo *_bo) { } static void qxl_bo_incref(qxl_screen_t *qxl, struct qxl_bo *_bo) { struct qxl_kms_bo *bo = (struct qxl_kms_bo *)_bo; bo->refcnt++; } static void qxl_bo_decref(qxl_screen_t *qxl, struct qxl_bo *_bo) { struct qxl_kms_bo *bo = (struct qxl_kms_bo *)_bo; struct drm_gem_close args; int ret; bo->refcnt--; if (bo->refcnt > 0) return; if (bo->type == QXL_BO_CMD) { free(bo->mapping); goto out; } else if (bo->mapping) munmap(bo->mapping, bo->size); /* just close the handle */ args.handle = bo->handle; ret = drmIoctl(qxl->drm_fd, DRM_IOCTL_GEM_CLOSE, &args); if (ret) { xf86DrvMsg(qxl->pScrn->scrnIndex, X_ERROR, "error doing QXL_DECREF\n"); } out: free(bo); } static void qxl_bo_output_bo_reloc(qxl_screen_t *qxl, uint32_t dst_offset, struct qxl_bo *_dst_bo, struct qxl_bo *_src_bo) { struct qxl_kms_bo *dst_bo = (struct qxl_kms_bo *)_dst_bo; struct qxl_kms_bo *src_bo = (struct qxl_kms_bo *)_src_bo; struct drm_qxl_reloc *r = &qxl->cmds.relocs[qxl->cmds.n_relocs]; if (qxl->cmds.n_reloc_bos >= MAX_RELOCS || qxl->cmds.n_relocs >= MAX_RELOCS) assert(0); qxl->cmds.reloc_bo[qxl->cmds.n_reloc_bos] = _src_bo; qxl->cmds.n_reloc_bos++; src_bo->refcnt++; /* fix the kernel names */ r->reloc_type = QXL_RELOC_TYPE_BO; r->dst_handle = dst_bo->handle; r->src_handle = src_bo->handle; r->dst_offset = dst_offset; r->src_offset = 0; qxl->cmds.n_relocs++; } static void qxl_bo_write_command(qxl_screen_t *qxl, uint32_t cmd_type, struct qxl_bo *_bo) { struct qxl_kms_bo *bo = (struct qxl_kms_bo *)_bo; struct drm_qxl_execbuffer eb; struct drm_qxl_command c; int ret; int i; c.type = cmd_type; c.command_size = bo->size - sizeof(union QXLReleaseInfo); c.command = pointer_to_u64(((uint8_t *)bo->mapping + sizeof(union QXLReleaseInfo))); if (qxl->cmds.n_relocs) { c.relocs_num = qxl->cmds.n_relocs; c.relocs = pointer_to_u64(qxl->cmds.relocs); } else { c.relocs_num = 0; c.relocs = 0; } eb.flags = 0; eb.commands_num = 1; eb.commands = pointer_to_u64(&c); ret = drmIoctl(qxl->drm_fd, DRM_IOCTL_QXL_EXECBUFFER, &eb); if (ret) { xf86DrvMsg(qxl->pScrn->scrnIndex, X_ERROR, "EXECBUFFER failed\n"); } qxl->cmds.n_relocs = 0; qxl->bo_funcs->bo_decref(qxl, _bo); for (i = 0; i < qxl->cmds.n_reloc_bos; i++) qxl->bo_funcs->bo_decref(qxl, qxl->cmds.reloc_bo[i]); qxl->cmds.n_reloc_bos = 0; } static void qxl_bo_update_area(qxl_surface_t *surf, int x1, int y1, int x2, int y2) { int ret; struct qxl_kms_bo *bo = (struct qxl_kms_bo *)surf->bo; struct drm_qxl_update_area update_area = { .handle = bo->handle, .left = x1, .top = y1, .right = x2, .bottom = y2 }; ret = drmIoctl(surf->qxl->drm_fd, DRM_IOCTL_QXL_UPDATE_AREA, &update_area); if (ret) { fprintf(stderr, "error doing QXL_UPDATE_AREA %d %d %d\n", ret, errno, surf->id); } } static struct qxl_bo *qxl_bo_create_primary(qxl_screen_t *qxl, uint32_t width, uint32_t height, int32_t stride, uint32_t format) { struct qxl_kms_bo *bo; struct drm_qxl_alloc_surf param; int ret; bo = calloc(1, sizeof(struct qxl_kms_bo)); if (!bo) return NULL; param.format = SPICE_SURFACE_FMT_32_xRGB; param.width = width; param.height = height; param.stride = stride; param.handle = 0; ret = drmIoctl(qxl->drm_fd, DRM_IOCTL_QXL_ALLOC_SURF, ¶m); if (ret) return NULL; bo->name = "surface memory"; bo->size = stride * param.height; bo->type = QXL_BO_SURF_PRIMARY; bo->handle = param.handle; bo->qxl = qxl; bo->refcnt = 1; qxl->primary_bo = (struct qxl_bo *)bo; qxl->device_primary = QXL_DEVICE_PRIMARY_CREATED; return (struct qxl_bo *)bo; } static void qxl_bo_destroy_primary(qxl_screen_t *qxl, struct qxl_bo *bo) { qxl_bo_decref(qxl, bo); qxl->primary_bo = NULL; qxl->device_primary = QXL_DEVICE_PRIMARY_NONE; } static qxl_surface_t * qxl_kms_surface_create(qxl_screen_t *qxl, int width, int height, int bpp) { SpiceSurfaceFmt format; qxl_surface_t *surface; int stride; struct qxl_kms_bo *bo; pixman_format_code_t pformat; void *dev_ptr; int ret; uint32_t *dev_addr; struct drm_qxl_alloc_surf param; if (!qxl->enable_surfaces) return NULL; if ((bpp & 3) != 0) { ErrorF ("%s: Bad bpp: %d (%d)\n", __FUNCTION__, bpp, bpp & 7); return NULL; } if (bpp != 8 && bpp != 16 && bpp != 32 && bpp != 24) { ErrorF ("%s: Unknown bpp\n", __FUNCTION__); return NULL; } if (width == 0 || height == 0) { ErrorF ("%s: Zero width or height\n", __FUNCTION__); return NULL; } qxl_get_formats (bpp, &format, &pformat); stride = width * PIXMAN_FORMAT_BPP (pformat) / 8; stride = (stride + 3) & ~3; bo = calloc(1, sizeof(struct qxl_kms_bo)); if (!bo) return NULL; param.format = format; param.width = width; param.height = height; param.stride = -stride; param.handle = 0; ret = drmIoctl(qxl->drm_fd, DRM_IOCTL_QXL_ALLOC_SURF, ¶m); if (ret) return NULL; bo->name = "surface memory"; bo->size = stride * height + stride; bo->type = QXL_BO_SURF; bo->handle = param.handle; bo->qxl = qxl; bo->refcnt = 1; /* then fill out the driver surface */ surface = calloc(1, sizeof *surface); surface->bo = (struct qxl_bo *)bo; surface->qxl = qxl; surface->id = bo->handle; surface->image_bo = NULL; dev_ptr = qxl->bo_funcs->bo_map(surface->bo); dev_addr = (uint32_t *)((uint8_t *)dev_ptr + stride * (height - 1)); surface->dev_image = pixman_image_create_bits ( pformat, width, height, dev_addr, - stride); surface->host_image = pixman_image_create_bits ( pformat, width, height, NULL, -1); REGION_INIT (NULL, &(surface->access_region), (BoxPtr)NULL, 0); qxl->bo_funcs->bo_unmap(surface->bo); surface->access_type = UXA_ACCESS_RO; surface->bpp = bpp; return surface; } static void qxl_kms_surface_destroy(qxl_surface_t *surf) { qxl_screen_t *qxl = surf->qxl; if (surf->dev_image) pixman_image_unref (surf->dev_image); if (surf->host_image) pixman_image_unref (surf->host_image); if (surf->image_bo) qxl->bo_funcs->bo_decref(qxl, surf->image_bo); qxl->bo_funcs->bo_decref(qxl, surf->bo); free(surf); } static void qxl_bo_output_surf_reloc(qxl_screen_t *qxl, uint32_t dst_offset, struct qxl_bo *_dst_bo, qxl_surface_t *surf) { struct qxl_kms_bo *dst_bo = (struct qxl_kms_bo *)_dst_bo; struct drm_qxl_reloc *r = &qxl->cmds.relocs[qxl->cmds.n_relocs]; struct qxl_kms_bo *bo = (struct qxl_kms_bo *)surf->bo; if (qxl->cmds.n_reloc_bos >= MAX_RELOCS || qxl->cmds.n_relocs >= MAX_RELOCS) assert(0); qxl->cmds.reloc_bo[qxl->cmds.n_reloc_bos] = surf->bo; qxl->cmds.n_reloc_bos++; bo->refcnt++; /* fix the kernel names */ r->reloc_type = QXL_RELOC_TYPE_SURF; r->dst_handle = dst_bo->handle; r->src_handle = bo->handle; r->dst_offset = dst_offset; r->src_offset = 0; qxl->cmds.n_relocs++; } static struct qxl_bo_funcs qxl_kms_bo_funcs = { qxl_bo_alloc, qxl_cmd_alloc, qxl_bo_map, qxl_bo_unmap, qxl_bo_decref, qxl_bo_incref, qxl_bo_output_bo_reloc, qxl_bo_write_command, qxl_bo_update_area, qxl_bo_create_primary, qxl_bo_destroy_primary, qxl_kms_surface_create, qxl_kms_surface_destroy, qxl_bo_output_surf_reloc, }; void qxl_kms_setup_funcs(qxl_screen_t *qxl) { qxl->bo_funcs = &qxl_kms_bo_funcs; } uint32_t qxl_kms_bo_get_handle(struct qxl_bo *_bo) { struct qxl_kms_bo *bo = (struct qxl_kms_bo *)_bo; return bo->handle; } #endif xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_mem.c000066400000000000000000000443751361660122400221030ustar00rootroot00000000000000/* * Copyright 2009 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "qxl.h" #include "mspace.h" #include "qxl_surface.h" #ifdef DEBUG_QXL_MEM #include #endif #define QXL_BO_DATA 1 #define QXL_BO_SURF 2 #define QXL_BO_CMD 4 #define QXL_BO_SURF_PRIMARY 8 #define QXL_BO_FLAG_FAIL 1 struct qxl_mem { mspace space; void * base; unsigned long n_bytes; #ifdef DEBUG_QXL_MEM size_t used_initial; int unverifiable; int missing; #endif }; #ifdef DEBUG_QXL_MEM void qxl_mem_unverifiable(struct qxl_mem *mem) { mem->unverifiable = 1; } #endif static void __attribute__ ((format (gnu_printf, 2, 3))) errout (void *data, const char *format, ...) { va_list va; va_start (va, format); VErrorF (format, va); va_end (va); } static void __attribute__ ((__noreturn__)) qxl_mspace_abort_func (void *user_data) { abort (); } void qxl_mem_init(void) { mspace_set_print_func (errout); mspace_set_abort_func (qxl_mspace_abort_func); } struct qxl_mem * qxl_mem_create (void *base, unsigned long n_bytes) { struct qxl_mem *mem; mem = calloc (sizeof (*mem), 1); if (!mem) goto out; ErrorF ("memory space from %p to %p\n", base, (char *)base + n_bytes); mem->space = create_mspace_with_base (base, n_bytes, 0, NULL); mem->base = base; mem->n_bytes = n_bytes; #ifdef DEBUG_QXL_MEM { size_t used; mspace_malloc_stats_return(mem->space, NULL, NULL, &used); mem->used_initial = used; mem->unverifiable = 0; mem->missing = 0; } #endif out: return mem; } void qxl_mem_dump_stats (struct qxl_mem *mem, const char *header) { ErrorF ("%s\n", header); mspace_malloc_stats (mem->space); } static void * qxl_alloc (struct qxl_mem *mem, unsigned long n_bytes, const char *name) { void *addr = mspace_malloc (mem->space, n_bytes); #ifdef DEBUG_QXL_MEM VALGRIND_MALLOCLIKE_BLOCK(addr, n_bytes, 0, 0); #ifdef DEBUG_QXL_MEM_VERBOSE fprintf(stderr, "alloc %p: %ld (%s)\n", addr, n_bytes, name); #endif #endif return addr; } static void qxl_free (struct qxl_mem *mem, void *d, const char * name) { #if 0 ErrorF ("%p <= free %s\n", d, name); #endif mspace_free (mem->space, d); #ifdef DEBUG_QXL_MEM #ifdef DEBUG_QXL_MEM_VERBOSE fprintf(stderr, "free %p %s\n", d, name); #endif VALGRIND_FREELIKE_BLOCK(d, 0); #endif } void qxl_mem_free_all (struct qxl_mem *mem) { #ifdef DEBUG_QXL_MEM size_t maxfp, fp, used; if (mem->space) { mspace_malloc_stats_return(mem->space, &maxfp, &fp, &used); mem->missing = used - mem->used_initial; ErrorF ("untracked %zd bytes (%s)", used - mem->used_initial, mem->unverifiable ? "marked unverifiable" : "oops"); } #endif mem->space = create_mspace_with_base (mem->base, mem->n_bytes, 0, NULL); } static uint8_t setup_slot (qxl_screen_t *qxl, uint8_t slot_index_offset, unsigned long start_phys_addr, unsigned long end_phys_addr, uint64_t start_virt_addr, uint64_t end_virt_addr) { uint64_t high_bits; qxl_memslot_t *slot; uint8_t slot_index; struct QXLRam *ram_header; ram_header = (void *)((unsigned long)qxl->ram + (unsigned long)qxl->rom->ram_header_offset); slot_index = qxl->rom->slots_start + slot_index_offset; slot = &qxl->mem_slots[slot_index]; slot->start_phys_addr = start_phys_addr; slot->end_phys_addr = end_phys_addr; slot->start_virt_addr = start_virt_addr; slot->end_virt_addr = end_virt_addr; ram_header->mem_slot.mem_start = slot->start_phys_addr; ram_header->mem_slot.mem_end = slot->end_phys_addr; qxl_io_memslot_add (qxl, slot_index); slot->generation = qxl->rom->slot_generation; high_bits = slot_index << qxl->slot_gen_bits; high_bits |= slot->generation; high_bits <<= (64 - (qxl->slot_gen_bits + qxl->slot_id_bits)); slot->high_bits = high_bits; return slot_index; } void qxl_reset_and_create_mem_slots (qxl_screen_t *qxl) { ioport_write (qxl, QXL_IO_RESET, 0); qxl->device_primary = QXL_DEVICE_PRIMARY_NONE; /* Mem slots */ ErrorF ("slots start: %d, slots end: %d\n", qxl->rom->slots_start, qxl->rom->slots_end); /* Main slot */ qxl->n_mem_slots = qxl->rom->slots_end; qxl->slot_gen_bits = qxl->rom->slot_gen_bits; qxl->slot_id_bits = qxl->rom->slot_id_bits; qxl->va_slot_mask = (~(uint64_t)0) >> (qxl->slot_id_bits + qxl->slot_gen_bits); qxl->mem_slots = xnfalloc (qxl->n_mem_slots * sizeof (qxl_memslot_t)); #ifdef XSPICE qxl->main_mem_slot = qxl->vram_mem_slot = setup_slot (qxl, 0, 0, ~0, 0, ~0); #else /* QXL */ qxl->main_mem_slot = setup_slot (qxl, 0, (unsigned long)qxl->ram_physical, (unsigned long)qxl->ram_physical + qxl->surface0_size + (unsigned long)qxl->rom->num_pages * getpagesize (), (uint64_t)(uintptr_t)qxl->ram, (uint64_t)(uintptr_t)qxl->ram + qxl->surface0_size + (unsigned long)qxl->rom->num_pages * getpagesize () ); qxl->vram_mem_slot = setup_slot (qxl, 1, (unsigned long)qxl->vram_physical, (unsigned long)qxl->vram_physical + (unsigned long)qxl->vram_size, (uint64_t)(uintptr_t)qxl->vram, (uint64_t)(uintptr_t)qxl->vram + (uint64_t)qxl->vram_size); #endif qxl_allocate_monitors_config(qxl); } void qxl_mark_mem_unverifiable (qxl_screen_t *qxl) { qxl_mem_unverifiable (qxl->mem); qxl_mem_unverifiable (qxl->surf_mem); } static uint64_t qxl_garbage_collect_internal (qxl_screen_t *qxl, uint64_t id) { /* We assume that there the two low bits of a pointer are * available. If the low one is set, then the command in * question is a cursor command */ #define POINTER_MASK ((1 << 2) - 1) struct qxl_bo *info_bo = (struct qxl_bo *)u64_to_pointer(id & ~POINTER_MASK); union QXLReleaseInfo *info = qxl->bo_funcs->bo_map(info_bo); struct QXLCursorCmd *cmd = (struct QXLCursorCmd *)info; struct QXLDrawable *drawable = (struct QXLDrawable *)info; struct QXLSurfaceCmd *surface_cmd = (struct QXLSurfaceCmd *)info; int is_cursor = FALSE; int is_surface = FALSE; int is_drawable = FALSE; struct qxl_bo *to_free; if ((id & POINTER_MASK) == 1) is_cursor = TRUE; else if ((id & POINTER_MASK) == 2) is_surface = TRUE; else is_drawable = TRUE; if (is_cursor && cmd->type == QXL_CURSOR_SET) { to_free = qxl_ums_lookup_phy_addr(qxl, cmd->u.set.shape); qxl->bo_funcs->bo_decref (qxl, to_free); } else if (is_drawable && drawable->type == QXL_DRAW_COPY) { struct QXLImage *image; to_free = qxl_ums_lookup_phy_addr(qxl, drawable->u.copy.src_bitmap); image = qxl->bo_funcs->bo_map(to_free); if (image->descriptor.type == SPICE_IMAGE_TYPE_SURFACE) { qxl_surface_unref (qxl->surface_cache, image->surface_image.surface_id); qxl_surface_cache_sanity_check (qxl->surface_cache); qxl->bo_funcs->bo_unmap(to_free); qxl->bo_funcs->bo_decref (qxl, to_free); } else { qxl->bo_funcs->bo_unmap(to_free); qxl_image_destroy (qxl, to_free); } } else if (is_drawable && drawable->type == QXL_DRAW_COMPOSITE) { struct qxl_bo *bo; struct QXLComposite *composite = &drawable->u.composite; /* Source */ bo = qxl_ums_lookup_phy_addr(qxl, drawable->u.composite.src); qxl->bo_funcs->bo_decref (qxl, bo); if (composite->src_transform) { bo = qxl_ums_lookup_phy_addr(qxl, composite->src_transform); qxl->bo_funcs->bo_decref (qxl, bo); } /* Mask */ if (drawable->u.composite.mask) { if (drawable->u.composite.mask_transform) { bo = qxl_ums_lookup_phy_addr(qxl, drawable->u.composite.mask_transform); qxl->bo_funcs->bo_decref (qxl, bo); } bo = qxl_ums_lookup_phy_addr(qxl, drawable->u.composite.mask); qxl->bo_funcs->bo_decref (qxl, bo); } } else if (is_surface && surface_cmd->type == QXL_SURFACE_CMD_DESTROY) { qxl_surface_recycle (qxl->surface_cache, surface_cmd->surface_id); qxl_surface_cache_sanity_check (qxl->surface_cache); } id = info->next; qxl->bo_funcs->bo_unmap(info_bo); qxl->bo_funcs->bo_decref(qxl, info_bo); return id; } int qxl_garbage_collect (qxl_screen_t *qxl) { uint64_t id; int i = 0; while (qxl_ring_pop (qxl->release_ring, &id)) { while (id) { id = qxl_garbage_collect_internal (qxl, id); i++; } } return i; } static void qxl_usleep (int useconds) { struct timespec t; t.tv_sec = useconds / 1000000; t.tv_nsec = (useconds - (t.tv_sec * 1000000)) * 1000; errno = 0; while (nanosleep (&t, &t) == -1 && errno == EINTR) ; } int qxl_handle_oom (qxl_screen_t *qxl) { qxl_io_notify_oom (qxl); #if 0 ErrorF ("."); qxl_usleep (10000); #endif if (!(qxl_garbage_collect (qxl))) qxl_usleep (10000); return qxl_garbage_collect (qxl); } static void * qxl_allocnf (qxl_screen_t *qxl, unsigned long size, const char *name) { void *result; int n_attempts = 0; #if 0 static int nth_oom = 1; #endif qxl_garbage_collect (qxl); while (!(result = qxl_alloc (qxl->mem, size, name))) { #if 0 ErrorF ("eliminated memory (%d)\n", nth_oom++); #endif if (!qxl_garbage_collect (qxl)) { if (qxl_handle_oom (qxl)) { n_attempts = 0; } else if (++n_attempts == 1000) { ErrorF ("Out of memory allocating %ld bytes\n", size); qxl_mem_dump_stats (qxl->mem, "Out of mem - stats\n"); fprintf (stderr, "Out of memory\n"); exit (1); } } } return result; } struct qxl_ums_bo { void *virt_addr; const char *name; int type; uint32_t size; void *internal_virt_addr; int refcnt; qxl_screen_t *qxl; xorg_list_t bos; }; static struct qxl_bo *qxl_bo_alloc_internal(qxl_screen_t *qxl, int type, int flags, unsigned long size, const char *name) { struct qxl_ums_bo *bo; struct qxl_mem *mptr; bo = calloc(1, sizeof(struct qxl_ums_bo)); if (!bo) return NULL; bo->size = size; bo->name = name; bo->type = type; bo->qxl = qxl; bo->refcnt = 1; if (type == QXL_BO_SURF) mptr = qxl->surf_mem; else mptr = qxl->mem; if (flags & QXL_BO_FLAG_FAIL) { bo->internal_virt_addr = qxl_alloc(mptr, size, name); if (!bo->internal_virt_addr) { free(bo); return NULL; } } else bo->internal_virt_addr = qxl_allocnf(qxl, size, name); if (type != QXL_BO_SURF) { xorg_list_add(&bo->bos, &qxl->ums_bos); } return (struct qxl_bo *)bo; } static struct qxl_bo *qxl_bo_alloc(qxl_screen_t *qxl, unsigned long size, const char *name) { return qxl_bo_alloc_internal(qxl, QXL_BO_DATA, 0, size, name); } static struct qxl_bo *qxl_cmd_alloc(qxl_screen_t *qxl, unsigned long size, const char *name) { return qxl_bo_alloc_internal(qxl, QXL_BO_CMD, 0, size, name); } static void *qxl_bo_map(struct qxl_bo *_bo) { struct qxl_ums_bo *bo = (struct qxl_ums_bo *)_bo; if (bo->virt_addr) ErrorF("recursive map %p\n", bo); bo->virt_addr = bo->internal_virt_addr; return bo->virt_addr; } static void qxl_bo_unmap(struct qxl_bo *_bo) { struct qxl_ums_bo *bo = (struct qxl_ums_bo *)_bo; if (!bo->virt_addr) ErrorF("unbalanced unmap %p\n", bo); bo->virt_addr = NULL; } static void qxl_bo_output_bo_reloc(qxl_screen_t *qxl, uint32_t dst_offset, struct qxl_bo *_dst_bo, struct qxl_bo *_src_bo) { struct qxl_ums_bo *src_bo = (struct qxl_ums_bo *)_src_bo; struct qxl_ums_bo *dst_bo = (struct qxl_ums_bo *)_dst_bo; uint8_t slot_id; uint64_t value; /* take a reference on the bo */ src_bo->refcnt++; slot_id = src_bo->type == QXL_BO_SURF ? qxl->vram_mem_slot : qxl->main_mem_slot; value = physical_address(qxl, src_bo->internal_virt_addr, slot_id); *(uint64_t *)((char *)dst_bo->internal_virt_addr + dst_offset) = value; } static void qxl_bo_output_cmd_reloc(qxl_screen_t *qxl, QXLCommand *command, struct qxl_bo *_src_bo) { struct qxl_ums_bo *src_bo = (struct qxl_ums_bo *)_src_bo; uint64_t value; uint8_t slot_id; src_bo->refcnt++; slot_id = src_bo->type == QXL_BO_SURF ? qxl->vram_mem_slot : qxl->main_mem_slot; value = physical_address(qxl, src_bo->internal_virt_addr, slot_id); command->data = value; } struct qxl_bo *qxl_ums_lookup_phy_addr(qxl_screen_t *qxl, uint64_t phy_addr) { struct qxl_ums_bo *bo, *found = NULL; uint8_t slot_id; void *virt_addr; slot_id = qxl->main_mem_slot; virt_addr = (void *)virtual_address(qxl, u64_to_pointer(phy_addr), slot_id); xorg_list_for_each_entry(bo, &qxl->ums_bos, bos) { if (bo->internal_virt_addr == virt_addr && bo->type == QXL_BO_DATA) { found = bo; break; } } return (struct qxl_bo *)found; } static void qxl_bo_incref(qxl_screen_t *qxl, struct qxl_bo *_bo) { struct qxl_ums_bo *bo = (struct qxl_ums_bo *)_bo; bo->refcnt++; } static void qxl_bo_decref(qxl_screen_t *qxl, struct qxl_bo *_bo) { struct qxl_ums_bo *bo = (struct qxl_ums_bo *)_bo; struct qxl_mem *mptr; bo->refcnt--; if (bo->refcnt > 0) return; if (bo->type == QXL_BO_SURF_PRIMARY) goto out_free; if (bo->type == QXL_BO_SURF) mptr = qxl->surf_mem; else mptr = qxl->mem; qxl_free(mptr, bo->internal_virt_addr, bo->name); if (bo->type != QXL_BO_SURF) xorg_list_del(&bo->bos); out_free: free(bo); } static void qxl_bo_write_command(qxl_screen_t *qxl, uint32_t cmd_type, struct qxl_bo *bo) { struct QXLCommand cmd; /* When someone runs "init 3", the device will be * switched into VGA mode and there is nothing we * can do about it. We get no notification. * * However, if commands are submitted when the device * is in VGA mode, they will be queued up, and then * the next time a mode set set, an assertion in the * device will take down the entire virtual machine. * * For surface commands this is not relevant, we send * them regardless. */ if (!qxl->pScrn->vtSema && cmd_type != QXL_CMD_SURFACE) return; cmd.type = cmd_type; qxl_bo_output_cmd_reloc(qxl, &cmd, bo); if (cmd_type == QXL_CMD_CURSOR) qxl_ring_push (qxl->cursor_ring, &cmd); else qxl_ring_push (qxl->command_ring, &cmd); qxl_bo_decref(qxl, bo); } static void qxl_bo_update_area(qxl_surface_t *surf, int x1, int y1, int x2, int y2) { struct QXLRam *ram_header = get_ram_header(surf->qxl); ram_header->update_area.top = y1; ram_header->update_area.bottom = y2; ram_header->update_area.left = x1; ram_header->update_area.right = x2; ram_header->update_surface = surf->id; qxl_update_area(surf->qxl); } /* create a fake bo for the primary */ static struct qxl_bo *qxl_bo_create_primary(qxl_screen_t *qxl, uint32_t width, uint32_t height, int32_t stride, uint32_t format) { struct qxl_ums_bo *bo; struct QXLRam *ram_header = (void *)((unsigned long)qxl->ram + qxl->rom->ram_header_offset); struct QXLSurfaceCreate *create = &(ram_header->create_surface); create->width = width; create->height = height; create->stride = - stride; create->format = format; create->position = 0; /* What is this? The Windows driver doesn't use it */ create->flags = 0; create->type = QXL_SURF_TYPE_PRIMARY; create->mem = physical_address (qxl, qxl->ram, qxl->main_mem_slot); qxl_io_create_primary(qxl); bo = calloc(1, sizeof(struct qxl_ums_bo)); if (!bo) return NULL; bo->size = stride * height; bo->name = "primary"; bo->type = QXL_BO_SURF_PRIMARY; bo->qxl = qxl; bo->refcnt = 1; bo->internal_virt_addr = (uint8_t *)qxl->ram + stride * (height - 1); qxl->primary_bo = (struct qxl_bo *)bo; return (struct qxl_bo *)bo; } static void qxl_bo_destroy_primary(qxl_screen_t *qxl, struct qxl_bo *bo) { free(bo); qxl->primary_bo = NULL; qxl_io_destroy_primary (qxl); } static void qxl_bo_output_surf_reloc(qxl_screen_t *qxl, uint32_t dst_offset, struct qxl_bo *_dst_bo, qxl_surface_t *surf) { struct qxl_ums_bo *dst_bo = (struct qxl_ums_bo *)_dst_bo; *(uint32_t *)((char *)dst_bo->internal_virt_addr + dst_offset) = surf->id; } static struct qxl_bo_funcs qxl_ums_bo_funcs = { qxl_bo_alloc, qxl_cmd_alloc, qxl_bo_map, qxl_bo_unmap, qxl_bo_decref, qxl_bo_incref, qxl_bo_output_bo_reloc, qxl_bo_write_command, qxl_bo_update_area, qxl_bo_create_primary, qxl_bo_destroy_primary, qxl_surface_create, qxl_surface_kill, qxl_bo_output_surf_reloc, }; void qxl_ums_setup_funcs(qxl_screen_t *qxl) { qxl->bo_funcs = &qxl_ums_bo_funcs; } struct qxl_bo *qxl_ums_surf_mem_alloc(qxl_screen_t *qxl, uint32_t size) { struct qxl_bo *bo; bo = qxl_bo_alloc_internal (qxl, QXL_BO_SURF, QXL_BO_FLAG_FAIL, size, "surface memory"); return bo; } xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_option_helpers.c000066400000000000000000000024331361660122400243440ustar00rootroot00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "qxl_option_helpers.h" int get_int_option(OptionInfoPtr options, int option_index, const char *env_name) { if (env_name && getenv(env_name)) { return atoi(getenv(env_name)); } return options[option_index].value.num; } const char *get_str_option(OptionInfoPtr options, int option_index, const char *env_name) { if (getenv(env_name)) { return getenv(env_name); } return options[option_index].value.str; } int get_bool_option(OptionInfoPtr options, int option_index, const char *env_name) { const char* value = getenv(env_name); if (!value) { return options[option_index].value.bool; } if (strcmp(value, "0") == 0 || strcasecmp(value, "off") == 0 || strcasecmp(value, "false") == 0 || strcasecmp(value, "no") == 0) { return FALSE; } if (strcmp(value, "1") == 0 || strcasecmp(value, "on") == 0 || strcasecmp(value, "true") == 0 || strcasecmp(value, "yes") == 0) { return TRUE; } fprintf(stderr, "spice: invalid %s: %s\n", env_name, value); exit(1); } xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_option_helpers.h000066400000000000000000000007021361660122400243460ustar00rootroot00000000000000#ifndef OPTION_HELPERS_H #define OPTION_HELPERS_H #include #include int get_int_option(OptionInfoPtr options, int option_index, const char *env_name); const char *get_str_option(OptionInfoPtr options, int option_index, const char *env_name); int get_bool_option(OptionInfoPtr options, int option_index, const char *env_name); #endif // OPTION_HELPERS_H xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_ring.c000066400000000000000000000100271361660122400222470ustar00rootroot00000000000000/* * Copyright 2009, 2010 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. * */ /** \file qxl_ring.c * \author Søren Sandmann */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "qxl.h" struct ring { struct qxl_ring_header header; uint8_t elements[0]; }; struct qxl_ring { volatile struct ring *ring; int element_size; int n_elements; int io_port_prod_notify; qxl_screen_t *qxl; }; struct qxl_ring * qxl_ring_create (struct qxl_ring_header *header, int element_size, int n_elements, int io_port_prod_notify, qxl_screen_t *qxl) { struct qxl_ring *ring; ring = malloc (sizeof *ring); if (!ring) return NULL; ring->ring = (volatile struct ring *)header; ring->element_size = element_size; ring->n_elements = n_elements; ring->io_port_prod_notify = io_port_prod_notify; ring->qxl = qxl; return ring; } void qxl_ring_push (struct qxl_ring *ring, const void *new_elt) { volatile struct qxl_ring_header *header = &(ring->ring->header); volatile uint8_t *elt; int idx; while (header->prod - header->cons == header->num_items) { header->notify_on_cons = header->cons + 1; #ifdef XSPICE /* in gtkperf, circles, this is a major bottleneck. Can't be that good in a vm either * Adding the yield reduces cpu usage, but doesn't improve throughput. */ sched_yield(); #endif mem_barrier(); } idx = header->prod & (ring->n_elements - 1); elt = ring->ring->elements + idx * ring->element_size; /* TODO: We should use proper MMIO accessors; the use of volatile leads to a gcc warning. See commit f7ba4bae */ memcpy((void *)elt, new_elt, ring->element_size); header->prod++; mem_barrier(); if (header->prod == header->notify_on_prod) { ioport_write (ring->qxl, ring->io_port_prod_notify, 0); } } Bool qxl_ring_pop (struct qxl_ring *ring, void *element) { volatile struct qxl_ring_header *header = &(ring->ring->header); volatile uint8_t *ring_elt; int idx; if (header->cons == header->prod) return FALSE; idx = header->cons & (ring->n_elements - 1); ring_elt = ring->ring->elements + idx * ring->element_size; memcpy (element, (void *)ring_elt, ring->element_size); header->cons++; return TRUE; } void qxl_ring_wait_idle (struct qxl_ring *ring) { while (ring->ring->header.cons != ring->ring->header.prod) { usleep (1000); mem_barrier(); } } void qxl_ring_request_notify (struct qxl_ring *ring) { ring->ring->header.notify_on_prod = ring->ring->header.prod + 1; ErrorF("%s: requesting notify on prod %d\n", __func__, ring->ring->header.notify_on_prod); } int qxl_ring_cons (struct qxl_ring *ring) { return ring->ring->header.cons; } int qxl_ring_prod (struct qxl_ring *ring) { return ring->ring->header.prod; } xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_surface.c000066400000000000000000000547421361660122400227540ustar00rootroot00000000000000/* * Copyright 2010 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "qxl.h" #include "qxl_surface.h"/* send anything pending to the other side */ enum ROPDescriptor { ROPD_INVERS_SRC = (1 << 0), ROPD_INVERS_BRUSH = (1 << 1), ROPD_INVERS_DEST = (1 << 2), ROPD_OP_PUT = (1 << 3), ROPD_OP_OR = (1 << 4), ROPD_OP_AND = (1 << 5), ROPD_OP_XOR = (1 << 6), ROPD_OP_BLACKNESS = (1 << 7), ROPD_OP_WHITENESS = (1 << 8), ROPD_OP_INVERS = (1 << 9), ROPD_INVERS_RES = (1 <<10), }; static struct qxl_bo * make_drawable (qxl_screen_t *qxl, qxl_surface_t *surf, uint8_t type, const struct QXLRect *rect /* , pRegion clip */) { struct QXLDrawable *drawable; struct qxl_bo *draw_bo; int i; draw_bo = qxl->bo_funcs->cmd_alloc (qxl, sizeof *drawable, "drawable command"); assert(draw_bo); drawable = qxl->bo_funcs->bo_map(draw_bo); assert(drawable); drawable->release_info.id = pointer_to_u64 (draw_bo); drawable->type = type; qxl->bo_funcs->bo_output_surf_reloc(qxl, offsetof(struct QXLDrawable, surface_id), draw_bo, surf); drawable->effect = QXL_EFFECT_OPAQUE; drawable->self_bitmap = 0; drawable->self_bitmap_area.top = 0; drawable->self_bitmap_area.left = 0; drawable->self_bitmap_area.bottom = 0; drawable->self_bitmap_area.right = 0; /* FIXME: add clipping */ drawable->clip.type = SPICE_CLIP_TYPE_NONE; /* * surfaces_dest[i] should apparently be filled out with the * surfaces that we depend on, and surface_rects should be * filled with the rectangles of those surfaces that we * are going to use. */ for (i = 0; i < 3; ++i) drawable->surfaces_dest[i] = -1; if (rect) drawable->bbox = *rect; /* No longer needed since spice-server commit c541d7e29 */ if (!qxl->kms_enabled) drawable->mm_time = qxl->rom->mm_clock; else drawable->mm_time = 0; qxl->bo_funcs->bo_unmap(draw_bo); return draw_bo; } static void push_drawable (qxl_screen_t *qxl, struct qxl_bo *drawable_bo) { qxl->bo_funcs->write_command (qxl, QXL_CMD_DRAW, drawable_bo); } static void submit_fill (qxl_screen_t *qxl, qxl_surface_t *surf, const struct QXLRect *rect, uint32_t color) { struct qxl_bo *drawable_bo; struct QXLDrawable *drawable; drawable_bo = make_drawable (qxl, surf, QXL_DRAW_FILL, rect); drawable = qxl->bo_funcs->bo_map(drawable_bo); drawable->u.fill.brush.type = SPICE_BRUSH_TYPE_SOLID; drawable->u.fill.brush.u.color = color; drawable->u.fill.rop_descriptor = ROPD_OP_PUT; drawable->u.fill.mask.flags = 0; drawable->u.fill.mask.pos.x = 0; drawable->u.fill.mask.pos.y = 0; drawable->u.fill.mask.bitmap = 0; qxl->bo_funcs->bo_unmap(drawable_bo); push_drawable (qxl, drawable_bo); } void qxl_surface_flush (qxl_surface_t *surface) { ; } /* access */ static void download_box_no_update (qxl_surface_t *surface, int x1, int y1, int x2, int y2) { pixman_image_composite (PIXMAN_OP_SRC, surface->dev_image, NULL, surface->host_image, x1, y1, 0, 0, x1, y1, x2 - x1, y2 - y1); } void qxl_download_box (qxl_surface_t *surface, int x1, int y1, int x2, int y2) { assert (x2 >= x1 && y2 >= y1); if (x1 == x2 || y1 == y2) return; surface->qxl->bo_funcs->update_area(surface, x1, y1, x2, y2); download_box_no_update(surface, x1, y1, x2, y2); } Bool qxl_surface_prepare_access (qxl_surface_t *surface, PixmapPtr pixmap, RegionPtr region, uxa_access_t access) { int n_boxes; BoxPtr boxes; ScreenPtr pScreen = pixmap->drawable.pScreen; ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); RegionRec new; if (!pScrn->vtSema) return FALSE; REGION_INIT (NULL, &new, (BoxPtr)NULL, 0); REGION_SUBTRACT (NULL, &new, region, &surface->access_region); if (access == UXA_ACCESS_RW) surface->access_type = UXA_ACCESS_RW; region = &new; n_boxes = REGION_NUM_RECTS (region); boxes = REGION_RECTS (region); if (n_boxes < 25) { while (n_boxes--) { qxl_download_box (surface, boxes->x1, boxes->y1, boxes->x2, boxes->y2); boxes++; } } else { qxl_download_box ( surface, new.extents.x1, new.extents.y1, new.extents.x2, new.extents.y2); } REGION_UNION (pScreen, &(surface->access_region), &(surface->access_region), region); REGION_UNINIT (NULL, &new); pScreen->ModifyPixmapHeader( pixmap, pixmap->drawable.width, pixmap->drawable.height, -1, -1, -1, pixman_image_get_data (surface->host_image)); pixmap->devKind = pixman_image_get_stride (surface->host_image); return TRUE; } static void translate_rect (struct QXLRect *rect) { rect->right -= rect->left; rect->bottom -= rect->top; rect->left = rect->top = 0; } static void real_upload_box (qxl_surface_t *surface, int x1, int y1, int x2, int y2) { struct QXLRect rect; struct QXLDrawable *drawable; struct qxl_bo *image_bo, *drawable_bo; qxl_screen_t *qxl = surface->qxl; uint32_t *data; int stride; rect.left = x1; rect.right = x2; rect.top = y1; rect.bottom = y2; drawable_bo = make_drawable (qxl, surface, QXL_DRAW_COPY, &rect); drawable = qxl->bo_funcs->bo_map(drawable_bo); drawable->u.copy.src_area = rect; translate_rect (&drawable->u.copy.src_area); drawable->u.copy.rop_descriptor = ROPD_OP_PUT; drawable->u.copy.scale_mode = 0; drawable->u.copy.mask.flags = 0; drawable->u.copy.mask.pos.x = 0; drawable->u.copy.mask.pos.y = 0; drawable->u.copy.mask.bitmap = 0; qxl->bo_funcs->bo_unmap(drawable_bo); data = pixman_image_get_data (surface->host_image); stride = pixman_image_get_stride (surface->host_image); image_bo = qxl_image_create ( qxl, (const uint8_t *)data, x1, y1, x2 - x1, y2 - y1, stride, surface->bpp == 24 ? 4 : surface->bpp / 8, TRUE); qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDrawable, u.copy.src_bitmap), drawable_bo, image_bo); push_drawable (qxl, drawable_bo); qxl->bo_funcs->bo_decref(qxl, image_bo); } #define TILE_WIDTH 512 #define TILE_HEIGHT 512 void qxl_upload_box (qxl_surface_t *surface, int x1, int y1, int x2, int y2) { int tile_x1, tile_y1; for (tile_y1 = y1; tile_y1 < y2; tile_y1 += TILE_HEIGHT) { for (tile_x1 = x1; tile_x1 < x2; tile_x1 += TILE_WIDTH) { int tile_x2 = tile_x1 + TILE_WIDTH; int tile_y2 = tile_y1 + TILE_HEIGHT; if (tile_x2 > x2) tile_x2 = x2; if (tile_y2 > y2) tile_y2 = y2; real_upload_box (surface, tile_x1, tile_y1, tile_x2, tile_y2); } } } static void upload_one_primary_region(qxl_screen_t *qxl, PixmapPtr pixmap, BoxPtr b) { struct QXLRect rect; struct qxl_bo *drawable_bo, *image_bo; struct QXLDrawable *drawable; FbBits *data; int stride; int bpp; if (b->x1 >= qxl->virtual_x || b->y1 >= qxl->virtual_y) return; rect.left = b->x1; rect.right = min(b->x2, qxl->virtual_x); rect.top = b->y1; rect.bottom = min(b->y2, qxl->virtual_y); drawable_bo = make_drawable (qxl, qxl->primary, QXL_DRAW_COPY, &rect); drawable = qxl->bo_funcs->bo_map(drawable_bo); drawable->u.copy.src_area = rect; translate_rect (&drawable->u.copy.src_area); drawable->u.copy.rop_descriptor = ROPD_OP_PUT; drawable->u.copy.scale_mode = 0; drawable->u.copy.mask.flags = 0; drawable->u.copy.mask.pos.x = 0; drawable->u.copy.mask.pos.y = 0; drawable->u.copy.mask.bitmap = 0; qxl->bo_funcs->bo_unmap(drawable_bo); fbGetPixmapBitsData(pixmap, data, stride, bpp); image_bo = qxl_image_create ( qxl, (const uint8_t *)data, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, stride * sizeof(*data), bpp == 24 ? 4 : bpp / 8, TRUE); qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDrawable, u.copy.src_bitmap), drawable_bo, image_bo); push_drawable (qxl, drawable_bo); qxl->bo_funcs->bo_decref(qxl, image_bo); } void qxl_surface_upload_primary_regions(qxl_screen_t *qxl, PixmapPtr pixmap, RegionRec *r) { int n_boxes; BoxPtr boxes; n_boxes = RegionNumRects(r); boxes = RegionRects(r); while (n_boxes--) { upload_one_primary_region(qxl, pixmap, boxes); boxes++; } } void qxl_surface_finish_access (qxl_surface_t *surface, PixmapPtr pixmap) { ScreenPtr pScreen = pixmap->drawable.pScreen; int w = pixmap->drawable.width; int h = pixmap->drawable.height; int n_boxes; BoxPtr boxes; n_boxes = REGION_NUM_RECTS (&surface->access_region); boxes = REGION_RECTS (&surface->access_region); if (surface->access_type == UXA_ACCESS_RW) { if (n_boxes < 25) { while (n_boxes--) { qxl_upload_box (surface, boxes->x1, boxes->y1, boxes->x2, boxes->y2); boxes++; } } else { qxl_upload_box (surface, surface->access_region.extents.x1, surface->access_region.extents.y1, surface->access_region.extents.x2, surface->access_region.extents.y2); } } REGION_EMPTY (pScreen, &surface->access_region); surface->access_type = UXA_ACCESS_RO; pScreen->ModifyPixmapHeader(pixmap, w, h, -1, -1, 0, NULL); } #ifdef DEBUG_REGIONS static void print_region (const char *header, RegionPtr pRegion) { int nbox = REGION_NUM_RECTS (pRegion); BoxPtr pbox = REGION_RECTS (pRegion); ErrorF ("%s", header); if (nbox == 0) ErrorF (" (empty)\n"); else ErrorF ("\n"); while (nbox--) { ErrorF (" %d %d %d %d (size: %d %d)\n", pbox->x1, pbox->y1, pbox->x2, pbox->y2, pbox->x2 - pbox->x1, pbox->y2 - pbox->y1); pbox++; } } #endif // DEBUG_REGIONS /* solid */ Bool qxl_surface_prepare_solid (qxl_surface_t *destination, Pixel fg) { if (!REGION_NIL (&(destination->access_region))) { ErrorF (" solid not in vmem\n"); } #ifdef DEBUG_REGIONS print_region ("prepare solid", &(destination->access_region)); #endif destination->u.solid_pixel = fg; // ^ (rand() >> 16); return TRUE; } void qxl_surface_solid (qxl_surface_t *destination, int x1, int y1, int x2, int y2) { qxl_screen_t *qxl = destination->qxl; struct QXLRect qrect; uint32_t p; qrect.top = y1; qrect.bottom = y2; qrect.left = x1; qrect.right = x2; p = destination->u.solid_pixel; submit_fill (qxl, destination, &qrect, p); } /* copy */ Bool qxl_surface_prepare_copy (qxl_surface_t *dest, qxl_surface_t *source) { if (!REGION_NIL (&(dest->access_region)) || !REGION_NIL (&(source->access_region))) { return FALSE; } dest->u.copy_src = source; return TRUE; } static struct qxl_bo * image_from_surface_internal(qxl_screen_t *qxl, qxl_surface_t *surface) { struct qxl_bo *image_bo = qxl->bo_funcs->bo_alloc (qxl, sizeof(struct QXLImage), "image struct for surface"); struct QXLImage *image = qxl->bo_funcs->bo_map(image_bo); image->descriptor.id = 0; image->descriptor.type = SPICE_IMAGE_TYPE_SURFACE; image->descriptor.width = 0; image->descriptor.height = 0; qxl->bo_funcs->bo_unmap(image_bo); return image_bo; } static struct qxl_bo *image_from_surface(qxl_screen_t *qxl, qxl_surface_t *dest) { if (!dest->image_bo) dest->image_bo = image_from_surface_internal(qxl, dest); qxl->bo_funcs->bo_incref(qxl, dest->image_bo); qxl->bo_funcs->bo_output_surf_reloc(qxl, offsetof(struct QXLImage, surface_image.surface_id), dest->image_bo, dest); return dest->image_bo; } void qxl_surface_copy (qxl_surface_t *dest, int src_x1, int src_y1, int dest_x1, int dest_y1, int width, int height) { qxl_screen_t *qxl = dest->qxl; struct qxl_bo *drawable_bo; struct QXLDrawable *drawable; struct QXLRect qrect; #ifdef DEBUG_REGIONS print_region (" copy src", &(dest->u.copy_src->access_region)); print_region (" copy dest", &(dest->access_region)); #endif qrect.top = dest_y1; qrect.bottom = dest_y1 + height; qrect.left = dest_x1; qrect.right = dest_x1 + width; if (dest->id == dest->u.copy_src->id) { drawable_bo = make_drawable (qxl, dest, QXL_COPY_BITS, &qrect); drawable = qxl->bo_funcs->bo_map(drawable_bo); drawable->u.copy_bits.src_pos.x = src_x1; drawable->u.copy_bits.src_pos.y = src_y1; qxl->bo_funcs->bo_unmap(drawable_bo); push_drawable (qxl, drawable_bo); } else { struct qxl_bo *image_bo; dest->u.copy_src->ref_count++; image_bo = image_from_surface(qxl, dest->u.copy_src); drawable_bo = make_drawable (qxl, dest, QXL_DRAW_COPY, &qrect); drawable = qxl->bo_funcs->bo_map(drawable_bo); qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDrawable, u.copy.src_bitmap), drawable_bo, image_bo); drawable->u.copy.src_area.left = src_x1; drawable->u.copy.src_area.top = src_y1; drawable->u.copy.src_area.right = src_x1 + width; drawable->u.copy.src_area.bottom = src_y1 + height; drawable->u.copy.rop_descriptor = ROPD_OP_PUT; drawable->u.copy.scale_mode = 0; drawable->u.copy.mask.flags = 0; drawable->u.copy.mask.pos.x = 0; drawable->u.copy.mask.pos.y = 0; drawable->u.copy.mask.bitmap = 0; qxl->bo_funcs->bo_output_surf_reloc(qxl, offsetof(struct QXLDrawable, surfaces_dest[0]), drawable_bo, dest->u.copy_src); drawable->surfaces_rects[0] = drawable->u.copy.src_area; assert (src_x1 >= 0); assert (src_y1 >= 0); if (width > pixman_image_get_width (dest->u.copy_src->host_image)) { ErrorF ("dest w: %d src w: %d\n", width, pixman_image_get_width (dest->u.copy_src->host_image)); } assert (width <= pixman_image_get_width (dest->u.copy_src->host_image)); assert (height <= pixman_image_get_height (dest->u.copy_src->host_image)); qxl->bo_funcs->bo_unmap(drawable_bo); push_drawable (qxl, drawable_bo); qxl->bo_funcs->bo_decref(qxl, image_bo); } } /* composite */ Bool qxl_surface_prepare_composite (int op, PicturePtr src_picture, PicturePtr mask_picture, PicturePtr dest_picture, qxl_surface_t * src, qxl_surface_t * mask, qxl_surface_t * dest) { dest->u.composite.op = op; dest->u.composite.src_picture = src_picture; dest->u.composite.mask_picture = mask_picture; dest->u.composite.dest_picture = dest_picture; dest->u.composite.src = src; dest->u.composite.mask = mask; dest->u.composite.dest = dest; return TRUE; } static struct qxl_bo * image_from_picture (qxl_screen_t *qxl, PicturePtr picture, qxl_surface_t *surface, int *force_opaque) { if (picture->format == PICT_x8r8g8b8) *force_opaque = TRUE; else *force_opaque = FALSE; return image_from_surface(qxl, surface); } static struct qxl_bo * get_transform (qxl_screen_t *qxl, PictTransform *transform) { if (transform) { struct qxl_bo *qxform_bo = qxl->bo_funcs->bo_alloc (qxl, sizeof (QXLTransform), "transform"); QXLTransform *qxform = qxl->bo_funcs->bo_map(qxform_bo); qxform->t00 = transform->matrix[0][0]; qxform->t01 = transform->matrix[0][1]; qxform->t02 = transform->matrix[0][2]; qxform->t10 = transform->matrix[1][0]; qxform->t11 = transform->matrix[1][1]; qxform->t12 = transform->matrix[1][2]; qxl->bo_funcs->bo_unmap(qxform_bo); return qxform_bo; } else { return NULL; } } static QXLRect full_rect (qxl_surface_t *surface) { QXLRect r; int w = pixman_image_get_width (surface->host_image); int h = pixman_image_get_height (surface->host_image); r.left = r.top = 0; r.right = w; r.bottom = h; return r; } void qxl_surface_composite (qxl_surface_t *dest, int src_x, int src_y, int mask_x, int mask_y, int dest_x, int dest_y, int width, int height) { qxl_screen_t *qxl = dest->qxl; PicturePtr src = dest->u.composite.src_picture; qxl_surface_t *qsrc = dest->u.composite.src; PicturePtr mask = dest->u.composite.mask_picture; qxl_surface_t *qmask = dest->u.composite.mask; int op = dest->u.composite.op; struct QXLDrawable *drawable; struct qxl_bo *drawable_bo; QXLComposite *composite; QXLRect rect; struct qxl_bo *trans_bo, *img_bo; int n_deps = 0; int force_opaque; struct qxl_bo *derefs[4]; int n_derefs = 0, i; #if 0 ErrorF ("QXL Composite: src: %x (%d %d) id: %d; \n" " mask: id: %d\n" " dest: %x %d %d %d %d (id: %d)\n", dest->u.composite.src_picture->format, dest->u.composite.src_picture->pDrawable->width, dest->u.composite.src_picture->pDrawable->height, dest->u.composite.src->id, dest->u.composite.mask? dest->u.composite.mask->id : -1, dest->u.composite.dest_picture->format, dest_x, dest_y, width, height, dest->id ); #endif rect.left = dest_x; rect.right = dest_x + width; rect.top = dest_y; rect.bottom = dest_y + height; drawable_bo = make_drawable (qxl, dest, QXL_DRAW_COMPOSITE, &rect); drawable = qxl->bo_funcs->bo_map(drawable_bo); composite = &drawable->u.composite; composite->flags = 0; if (dest->u.composite.dest_picture->format == PICT_x8r8g8b8) composite->flags |= SPICE_COMPOSITE_DEST_OPAQUE; composite->flags |= (op & 0xff); img_bo = image_from_picture (qxl, src, qsrc, &force_opaque); if (force_opaque) composite->flags |= SPICE_COMPOSITE_SOURCE_OPAQUE; qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDrawable, u.composite.src), drawable_bo, img_bo); derefs[n_derefs++] = img_bo; composite->flags |= (src->filter << 8); composite->flags |= (src->repeat << 14); trans_bo = get_transform (qxl, src->transform); if (trans_bo) { qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDrawable, u.composite.src_transform), drawable_bo, trans_bo); derefs[n_derefs++] = trans_bo; } else composite->src_transform = 0; qxl->bo_funcs->bo_output_surf_reloc(qxl, offsetof(struct QXLDrawable, surfaces_dest[n_deps]), drawable_bo, qsrc); drawable->surfaces_rects[n_deps] = full_rect (qsrc); n_deps++; if (mask) { img_bo = image_from_picture (qxl, mask, qmask, &force_opaque); if (force_opaque) composite->flags |= SPICE_COMPOSITE_MASK_OPAQUE; qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDrawable, u.composite.mask), drawable_bo, img_bo); derefs[n_derefs++] = img_bo; composite->flags |= (mask->filter << 11); composite->flags |= (mask->repeat << 16); composite->flags |= (mask->componentAlpha << 18); qxl->bo_funcs->bo_output_surf_reloc(qxl, offsetof(struct QXLDrawable, surfaces_dest[n_deps]), drawable_bo, qmask); drawable->surfaces_rects[n_deps] = full_rect (qmask); n_deps++; trans_bo = get_transform (qxl, src->transform); if (trans_bo) { qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDrawable, u.composite.mask_transform), drawable_bo, trans_bo); derefs[n_derefs++] = trans_bo; } else composite->mask_transform = 0; } else { composite->mask = 0x00000000; composite->mask_transform = 0x00000000; } qxl->bo_funcs->bo_output_surf_reloc(qxl, offsetof(struct QXLDrawable, surfaces_dest[n_deps]), drawable_bo, dest); drawable->surfaces_rects[n_deps] = full_rect (dest); composite->src_origin.x = src_x; composite->src_origin.y = src_y; composite->mask_origin.x = mask_x; composite->mask_origin.y = mask_y; drawable->effect = QXL_EFFECT_BLEND; qxl->bo_funcs->bo_unmap(drawable_bo); push_drawable (qxl, drawable_bo); for (i = 0; i < n_derefs; i++) qxl->bo_funcs->bo_decref(qxl, derefs[i]); } Bool qxl_surface_put_image (qxl_surface_t *dest, int x, int y, int width, int height, const char *src, int src_pitch) { struct qxl_bo *drawable_bo; struct QXLDrawable *drawable; qxl_screen_t *qxl = dest->qxl; struct QXLRect rect; struct qxl_bo *image_bo; rect.left = x; rect.right = x + width; rect.top = y; rect.bottom = y + height; drawable_bo = make_drawable (qxl, dest, QXL_DRAW_COPY, &rect); drawable = qxl->bo_funcs->bo_map(drawable_bo); drawable->u.copy.src_area.top = 0; drawable->u.copy.src_area.bottom = height; drawable->u.copy.src_area.left = 0; drawable->u.copy.src_area.right = width; drawable->u.copy.rop_descriptor = ROPD_OP_PUT; drawable->u.copy.scale_mode = 0; drawable->u.copy.mask.flags = 0; drawable->u.copy.mask.pos.x = 0; drawable->u.copy.mask.pos.y = 0; drawable->u.copy.mask.bitmap = 0; image_bo = qxl_image_create ( qxl, (const uint8_t *)src, 0, 0, width, height, src_pitch, dest->bpp == 24 ? 4 : dest->bpp / 8, FALSE); qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDrawable, u.copy.src_bitmap), drawable_bo, image_bo); qxl->bo_funcs->bo_unmap(drawable_bo); push_drawable (qxl, drawable_bo); qxl->bo_funcs->bo_decref(qxl, image_bo); return TRUE; } void qxl_get_formats (int bpp, SpiceSurfaceFmt *format, pixman_format_code_t *pformat) { switch (bpp) { case 8: *format = SPICE_SURFACE_FMT_8_A; *pformat = PIXMAN_a8; break; case 16: *format = SPICE_SURFACE_FMT_16_565; *pformat = PIXMAN_r5g6b5; break; case 24: *format = SPICE_SURFACE_FMT_32_xRGB; *pformat = PIXMAN_a8r8g8b8; break; case 32: *format = SPICE_SURFACE_FMT_32_ARGB; *pformat = PIXMAN_a8r8g8b8; break; default: *format = -1; *pformat = -1; break; } } xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_surface.h000066400000000000000000000021511361660122400227440ustar00rootroot00000000000000#ifndef QXL_SURFACE_H #define QXL_SURFACE_H struct evacuated_surface_t; struct qxl_surface_t { surface_cache_t *cache; qxl_screen_t *qxl; uint32_t id; pixman_image_t * dev_image; pixman_image_t * host_image; uxa_access_t access_type; RegionRec access_region; struct qxl_bo *bo; struct qxl_surface_t * next; struct qxl_surface_t * prev; /* Only used in the 'live' * chain in the surface cache */ int in_use; int bpp; /* bpp of the pixmap */ int ref_count; PixmapPtr pixmap; struct evacuated_surface_t *evacuated; union { struct qxl_surface_t *copy_src; Pixel solid_pixel; struct { int op; PicturePtr src_picture; PicturePtr mask_picture; PicturePtr dest_picture; struct qxl_surface_t *src; struct qxl_surface_t *mask; struct qxl_surface_t *dest; } composite; } u; struct qxl_bo *image_bo; }; void qxl_download_box (qxl_surface_t *surface, int x1, int y1, int x2, int y2); void qxl_upload_box (qxl_surface_t *surface, int x1, int y1, int x2, int y2); #endif xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_surface_ums.c000066400000000000000000000500221361660122400236230ustar00rootroot00000000000000/* * Copyright 2010 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ /* The life cycle of surfaces * * free => live => dead => destroyed => free * * A 'free' surface is one that is not allocated on the device. These * are stored on the 'free_surfaces' list. * * A 'live' surface is one that the X server is using for something. It * has an associated pixmap. It is allocated in the device. These are stored on * the "live_surfaces" list. * * A 'dead' surface is one that the X server is no using any more, but * is still allocated in the device. These surfaces may be stored in the * cache, from where they can be resurrected. The cache holds a ref on the * surfaces. * * A 'destroyed' surface is one whose ref count has reached 0. It is no * longer being referenced by either the server or the device or the cache. * When a surface enters this state, the associated pixman images are freed, and * a destroy command is sent. This will eventually trigger a 'recycle' call, * which puts the surface into the 'free' state. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "qxl.h" #include "qxl_surface.h" #ifdef DEBUG_SURFACE_LIFECYCLE #include static FILE* surface_log; #endif typedef struct evacuated_surface_t evacuated_surface_t; struct evacuated_surface_t { pixman_image_t *image; PixmapPtr pixmap; int bpp; evacuated_surface_t *prev; evacuated_surface_t *next; }; #define N_CACHED_SURFACES 64 /* * Surface cache */ struct surface_cache_t { qxl_screen_t *qxl; /* Array of surfaces (not a linked list). * All surfaces, excluding the primary one, indexed by surface id. */ qxl_surface_t *all_surfaces; /* All surfaces that the driver is currently using (linked through next/prev) */ qxl_surface_t *live_surfaces; /* All surfaces that need to be allocated (linked through next, but not prev) */ qxl_surface_t *free_surfaces; /* Surfaces that are already allocated, but not in used by the driver, * linked through next */ qxl_surface_t *cached_surfaces[N_CACHED_SURFACES]; }; #ifdef DEBUG_SURFACE_LIFECYCLE static void debug_surface_open(void) { if (surface_log) return; surface_log = fopen("/tmp/xf86-video-qxl.surface.log", "w+"); if (!surface_log) { fprintf(stderr, "error creating surface log file (DEBUG_SURFACE_LIFECYCLE)\n"); exit(-1); } } static int surface_count(qxl_surface_t *surface) { int i; for (i = 0; surface ;++i, surface = surface->next); return i; } static void debug_surface_log(surface_cache_t *cache) { int live_n, free_n; debug_surface_open(); live_n = surface_count(cache->live_surfaces); free_n = surface_count(cache->free_surfaces); fprintf(surface_log, "live,free,sum = %d, %d, %d\n", live_n, free_n, live_n + free_n); fflush(surface_log); } #else #define debug_surface_log(cache) #endif static Bool surface_cache_init (surface_cache_t *cache, qxl_screen_t *qxl) { int n_surfaces = qxl->rom->n_surfaces; int i; if (!cache->all_surfaces) { /* all_surfaces is not freed when evacuating, since surfaces are still * tied to pixmaps that may be destroyed after evacuation before * recreation */ cache->all_surfaces = calloc (n_surfaces, sizeof (qxl_surface_t)); if (!cache->all_surfaces) return FALSE; } memset (cache->all_surfaces, 0, n_surfaces * sizeof (qxl_surface_t)); memset (cache->cached_surfaces, 0, N_CACHED_SURFACES * sizeof (qxl_surface_t *)); cache->free_surfaces = NULL; cache->live_surfaces = NULL; for (i = 0; i < n_surfaces; ++i) { cache->all_surfaces[i].id = i; cache->all_surfaces[i].cache = cache; cache->all_surfaces[i].qxl = qxl; cache->all_surfaces[i].dev_image = NULL; cache->all_surfaces[i].host_image = NULL; cache->all_surfaces[i].evacuated = NULL; REGION_INIT ( NULL, &(cache->all_surfaces[i].access_region), (BoxPtr)NULL, 0); cache->all_surfaces[i].access_type = UXA_ACCESS_RO; if (i) /* surface 0 is the primary surface */ { cache->all_surfaces[i].next = cache->free_surfaces; cache->free_surfaces = &(cache->all_surfaces[i]); cache->all_surfaces[i].in_use = FALSE; } } return TRUE; } surface_cache_t * qxl_surface_cache_create (qxl_screen_t *qxl) { surface_cache_t *cache = malloc (sizeof *cache); if (!cache) return NULL; memset(cache, 0, sizeof(*cache)); cache->qxl = qxl; if (!surface_cache_init (cache, qxl)) { free (cache); return NULL; } return cache; } void qxl_surface_cache_sanity_check (surface_cache_t *qxl) { #if 0 qxl_surface_t *s; for (s = qxl->live_surfaces; s != NULL; s = s->next) { PixmapPtr pixmap = s->pixmap; if (! (get_surface (pixmap) == s) ) { ErrorF ("Surface %p has pixmap %p, but pixmap %p has surface %p\n", s, pixmap, pixmap, get_surface (pixmap)); assert (0); } } #endif } static void print_cache_info (surface_cache_t *cache) { int i; int n_surfaces = 0; ErrorF ("Cache contents: "); for (i = 0; i < N_CACHED_SURFACES; ++i) { if (cache->cached_surfaces[i]) { ErrorF ("%4d ", cache->cached_surfaces[i]->id); n_surfaces++; } else ErrorF ("null "); } ErrorF (" total: %d\n", n_surfaces); } static qxl_surface_t * surface_get_from_cache (surface_cache_t *cache, int width, int height, int bpp) { int i; for (i = 0; i < N_CACHED_SURFACES; ++i) { qxl_surface_t *s = cache->cached_surfaces[i]; if (s && bpp == s->bpp) { int w = pixman_image_get_width (s->host_image); int h = pixman_image_get_height (s->host_image); if (width <= w && width * 4 > w && height <= h && height * 4 > h) { cache->cached_surfaces[i] = NULL; return s; } } } return NULL; } static int n_live; void qxl_surface_recycle (surface_cache_t *cache, uint32_t id) { qxl_surface_t *surface = cache->all_surfaces + id; n_live--; if (surface->bo) cache->qxl->bo_funcs->bo_decref (cache->qxl, surface->bo); surface->bo = NULL; surface->next = cache->free_surfaces; cache->free_surfaces = surface; } /* * mode is used for the whole virtual screen, not for a specific head. * For a single head where virtual size is equal to the head size, they are * equal. For multiple heads this mode will not match any existing heads and * will be the containing virtual size. */ qxl_surface_t * qxl_surface_cache_create_primary (qxl_screen_t *qxl, struct QXLMode *mode) { pixman_format_code_t format; uint8_t *dev_addr; pixman_image_t *dev_image, *host_image; qxl_surface_t *surface; surface_cache_t *cache = qxl->surface_cache; struct qxl_bo *bo; if (mode->bits == 16) { format = PIXMAN_x1r5g5b5; } else if (mode->bits == 32) { format = PIXMAN_x8r8g8b8; } else { xf86DrvMsg (qxl->pScrn->scrnIndex, X_ERROR, "Unknown bit depth %d\n", mode->bits); return NULL; } bo = qxl->bo_funcs->create_primary(qxl, mode->x_res, mode->y_res, mode->stride, mode->bits); dev_addr = qxl->bo_funcs->bo_map(bo); dev_image = pixman_image_create_bits (format, mode->x_res, mode->y_res, (uint32_t *)dev_addr, (qxl->kms_enabled ? mode->stride : -mode->stride)); host_image = pixman_image_create_bits (format, qxl->virtual_x, qxl->virtual_y, NULL, mode->stride); #if 0 xf86DrvMsg(cache->qxl->pScrn->scrnIndex, X_ERROR, "testing dev_image memory (%d x %d)\n", mode->x_res, mode->y_res); memset(qxl->ram, 0, mode->stride * mode->y_res); xf86DrvMsg(cache->qxl->pScrn->scrnIndex, X_ERROR, "testing host_image memory\n"); memset(qxl->fb, 0, mode->stride * mode->y_res); #endif surface = malloc (sizeof *surface); surface->id = 0; surface->dev_image = dev_image; surface->host_image = host_image; surface->cache = cache; surface->qxl = qxl; surface->bpp = mode->bits; surface->next = NULL; surface->prev = NULL; surface->evacuated = NULL; surface->bo = bo; surface->image_bo = NULL; REGION_INIT (NULL, &(surface->access_region), (BoxPtr)NULL, 0); surface->access_type = UXA_ACCESS_RO; return surface; } void * qxl_surface_get_host_bits(qxl_surface_t *surface) { if (!surface) return NULL; return (void *) pixman_image_get_data(surface->host_image); } static struct qxl_bo * make_surface_cmd (surface_cache_t *cache, uint32_t id, QXLSurfaceCmdType type) { struct qxl_bo *cmd_bo; struct QXLSurfaceCmd *cmd; qxl_screen_t *qxl = cache->qxl; cmd_bo = qxl->bo_funcs->cmd_alloc (qxl, sizeof *cmd, "surface command"); cmd = qxl->bo_funcs->bo_map(cmd_bo); cmd->release_info.id = pointer_to_u64 (cmd_bo) | 2; cmd->type = type; cmd->flags = 0; cmd->surface_id = id; qxl->bo_funcs->bo_unmap(cmd_bo); return cmd_bo; } static void push_surface_cmd (surface_cache_t *cache, struct qxl_bo *cmd_bo) { qxl_screen_t *qxl = cache->qxl; qxl->bo_funcs->write_command (qxl, QXL_CMD_SURFACE, cmd_bo); } static qxl_surface_t * surface_get_from_free_list (surface_cache_t *cache) { qxl_surface_t *result = NULL; if (cache->free_surfaces) { qxl_surface_t *s; result = cache->free_surfaces; cache->free_surfaces = cache->free_surfaces->next; result->next = NULL; result->in_use = TRUE; result->ref_count = 1; result->pixmap = NULL; for (s = cache->free_surfaces; s; s = s->next) { if (s->id == result->id) ErrorF ("huh: %d to be returned, but %d is in list\n", s->id, result->id); assert (s->id != result->id); } } return result; } static int align (int x) { return x; } static qxl_surface_t * surface_send_create (surface_cache_t *cache, int width, int height, int bpp) { SpiceSurfaceFmt format; pixman_format_code_t pformat; struct QXLSurfaceCmd *cmd; int stride; uint32_t *dev_addr; int n_attempts = 0; qxl_screen_t *qxl = cache->qxl; qxl_surface_t *surface; struct qxl_bo *bo, *cmd_bo; void *dev_ptr; qxl_get_formats (bpp, &format, &pformat); width = align (width); height = align (height); stride = width * PIXMAN_FORMAT_BPP (pformat) / 8; stride = (stride + 3) & ~3; /* the final + stride is to work around a bug where the device apparently * scribbles after the end of the image */ qxl_garbage_collect (qxl); retry2: bo = qxl_ums_surf_mem_alloc(qxl, stride * height + stride); if (!bo) { ErrorF ("- %dth attempt\n", n_attempts++); if (qxl_garbage_collect (qxl)) goto retry2; ErrorF ("- OOM at %d %d %d (= %d bytes)\n", width, height, bpp, width * height * (bpp / 8)); print_cache_info (cache); if (qxl_handle_oom (qxl)) { while (qxl_garbage_collect (qxl)) ; goto retry2; } ErrorF ("Out of video memory: Could not allocate %d bytes\n", stride * height + stride); return NULL; } retry: surface = surface_get_from_free_list (cache); if (!surface) { if (!qxl_handle_oom (cache->qxl)) { ErrorF (" Out of surfaces\n"); qxl->bo_funcs->bo_decref (qxl, bo); return NULL; } else goto retry; } surface->bo = bo; cmd_bo = make_surface_cmd (cache, surface->id, QXL_SURFACE_CMD_CREATE); cmd = qxl->bo_funcs->bo_map(cmd_bo); cmd->u.surface_create.format = format; cmd->u.surface_create.width = width; cmd->u.surface_create.height = height; cmd->u.surface_create.stride = - stride; qxl->bo_funcs->bo_unmap(cmd_bo); qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(struct QXLSurfaceCmd, u.surface_create.data), cmd_bo, surface->bo); push_surface_cmd (cache, cmd_bo); dev_ptr = qxl->bo_funcs->bo_map(surface->bo); dev_addr = (uint32_t *)((uint8_t *)dev_ptr + stride * (height - 1)); surface->dev_image = pixman_image_create_bits ( pformat, width, height, dev_addr, - stride); surface->host_image = pixman_image_create_bits ( pformat, width, height, NULL, -1); qxl->bo_funcs->bo_unmap(surface->bo); surface->bpp = bpp; n_live++; return surface; } qxl_surface_t * qxl_surface_create (qxl_screen_t *qxl, int width, int height, int bpp) { qxl_surface_t *surface; surface_cache_t *cache = qxl->surface_cache; if (!qxl->enable_surfaces) return NULL; if ((bpp & 3) != 0) { ErrorF ("%s: Bad bpp: %d (%d)\n", __FUNCTION__, bpp, bpp & 7); return NULL; } #if 0 if (bpp == 8) { static int warned; if (!warned) { warned = 1; ErrorF ("bpp == 8 triggers bugs in spice apparently\n"); } return NULL; } #endif if (bpp != 8 && bpp != 16 && bpp != 32 && bpp != 24) { ErrorF ("%s: Unknown bpp\n", __FUNCTION__); return NULL; } if (width == 0 || height == 0) { ErrorF ("%s: Zero width or height\n", __FUNCTION__); return NULL; } if (!(surface = surface_get_from_cache (cache, width, height, bpp))) if (!(surface = surface_send_create (cache, width, height, bpp))) return NULL; surface->next = cache->live_surfaces; surface->prev = NULL; if (cache->live_surfaces) cache->live_surfaces->prev = surface; cache->live_surfaces = surface; return surface; } void qxl_surface_set_pixmap (qxl_surface_t *surface, PixmapPtr pixmap) { surface->pixmap = pixmap; assert (get_surface (pixmap) == surface); } static void unlink_surface (qxl_surface_t *surface) { if (surface->id != 0) { if (surface->prev) surface->prev->next = surface->next; else surface->cache->live_surfaces = surface->next; } debug_surface_log(surface->cache); if (surface->next) surface->next->prev = surface->prev; surface->pixmap = NULL; surface->prev = NULL; surface->next = NULL; } static void surface_destroy (qxl_surface_t *surface) { struct qxl_bo *cmd_bo; if (surface->dev_image) pixman_image_unref (surface->dev_image); if (surface->host_image) pixman_image_unref (surface->host_image); #if 0 ErrorF("destroy %ld\n", (long int)surface->end - (long int)surface->address); #endif cmd_bo = make_surface_cmd (surface->cache, surface->id, QXL_SURFACE_CMD_DESTROY); push_surface_cmd (surface->cache, cmd_bo); surface->cache->qxl->bo_funcs->bo_decref(surface->cache->qxl, surface->bo); } static void surface_add_to_cache (qxl_surface_t *surface) { surface_cache_t *cache = surface->cache; int oldest = -1; int n_surfaces = 0; int i, delta; int destroy_id = -1; qxl_surface_t *destroy_surface = NULL; surface->ref_count++; for (i = 0; i < N_CACHED_SURFACES; ++i) { if (cache->cached_surfaces[i]) { oldest = i; n_surfaces++; } } if (n_surfaces == N_CACHED_SURFACES) { destroy_id = cache->cached_surfaces[oldest]->id; destroy_surface = cache->cached_surfaces[oldest]; cache->cached_surfaces[oldest] = NULL; for (i = 0; i < N_CACHED_SURFACES; ++i) assert (!cache->cached_surfaces[i] || cache->cached_surfaces[i]->id != destroy_id); } delta = 0; for (i = N_CACHED_SURFACES - 1; i >= 0; i--) { if (cache->cached_surfaces[i]) { if (delta > 0) { cache->cached_surfaces[i + delta] = cache->cached_surfaces[i]; assert (cache->cached_surfaces[i + delta]->id != destroy_id); cache->cached_surfaces[i] = NULL; } } else { delta++; } } assert (delta > 0); cache->cached_surfaces[i + delta] = surface; for (i = 0; i < N_CACHED_SURFACES; ++i) assert (!cache->cached_surfaces[i] || cache->cached_surfaces[i]->id != destroy_id); /* Note that sending a destroy command can trigger callbacks into * this function (due to memory management), so we have to * do this after updating the cache */ if (destroy_surface) qxl_surface_unref (destroy_surface->cache, destroy_surface->id); } void qxl_surface_unref (surface_cache_t *cache, uint32_t id) { if (id != 0) { qxl_surface_t *surface = cache->all_surfaces + id; if (--surface->ref_count == 0) surface_destroy (surface); } } void qxl_surface_kill (qxl_surface_t *surface) { struct evacuated_surface_t *ev = surface->evacuated; if (ev) { /* server side surface is already destroyed (via reset), don't * resend a destroy. Just mark surface as not to be recreated */ ev->pixmap = NULL; if (ev->image) pixman_image_unref (ev->image); if (ev->next) ev->next->prev = ev->prev; if (ev->prev) ev->prev->next = ev->next; free(ev); surface->evacuated = NULL; return; } unlink_surface (surface); if (!surface->cache->all_surfaces) { return; } if (surface->id != 0 && surface->host_image && pixman_image_get_width (surface->host_image) >= 128 && pixman_image_get_height (surface->host_image) >= 128) { surface_add_to_cache (surface); } qxl_surface_unref (surface->cache, surface->id); } void * qxl_surface_cache_evacuate_all (surface_cache_t *cache) { evacuated_surface_t *evacuated_surfaces = NULL; qxl_surface_t *s; int i; for (i = 0; i < N_CACHED_SURFACES; ++i) { if (cache->cached_surfaces[i]) { surface_destroy (cache->cached_surfaces[i]); cache->cached_surfaces[i] = NULL; } } s = cache->live_surfaces; while (s != NULL) { qxl_surface_t *next = s->next; evacuated_surface_t *evacuated = malloc (sizeof (evacuated_surface_t)); int width, height; width = pixman_image_get_width (s->host_image); height = pixman_image_get_height (s->host_image); qxl_download_box (s, 0, 0, width, height); evacuated->image = s->host_image; evacuated->pixmap = s->pixmap; assert (get_surface (evacuated->pixmap) == s); evacuated->bpp = s->bpp; s->host_image = NULL; unlink_surface (s); evacuated->prev = NULL; evacuated->next = evacuated_surfaces; if (evacuated_surfaces) evacuated_surfaces->prev = evacuated; evacuated_surfaces = evacuated; s->evacuated = evacuated; s = next; } cache->live_surfaces = NULL; cache->free_surfaces = NULL; return evacuated_surfaces; } void qxl_surface_cache_replace_all (surface_cache_t *cache, void *data) { evacuated_surface_t *ev; if (!surface_cache_init (cache, cache->qxl)) { /* FIXME: report the error */ return; } ev = data; while (ev != NULL) { evacuated_surface_t *next = ev->next; int width = pixman_image_get_width (ev->image); int height = pixman_image_get_height (ev->image); qxl_surface_t *surface; surface = qxl_surface_create (cache->qxl, width, height, ev->bpp); assert (surface->host_image); assert (surface->dev_image); pixman_image_unref (surface->host_image); surface->host_image = ev->image; qxl_upload_box (surface, 0, 0, width, height); set_surface (ev->pixmap, surface); qxl_surface_set_pixmap (surface, ev->pixmap); free (ev); ev = next; } qxl_surface_cache_sanity_check (cache); } xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_ums_mode.c000066400000000000000000000314701361660122400231250ustar00rootroot00000000000000/* * Copyright 2008 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ /* code to handle UMS modesetting */ #ifdef HAVE_CONFIG_H #include #endif #include "qxl.h" /* These constants govern which modes are reported to X as preferred */ #define DEFAULT_WIDTH 1024 #define DEFAULT_HEIGHT 768 static void qxl_update_monitors_config (qxl_screen_t *qxl); static DisplayModePtr screen_create_mode (ScrnInfoPtr pScrn, int width, int height, int type) { DisplayModePtr mode; mode = xnfcalloc (1, sizeof (DisplayModeRec)); mode->status = MODE_OK; mode->type = type; mode->HDisplay = width; mode->HSyncStart = (width * 105 / 100 + 7) & ~7; mode->HSyncEnd = (width * 115 / 100 + 7) & ~7; mode->HTotal = (width * 130 / 100 + 7) & ~7; mode->VDisplay = height; mode->VSyncStart = height + 1; mode->VSyncEnd = height + 4; mode->VTotal = height * 1035 / 1000; mode->Clock = mode->HTotal * mode->VTotal * 60 / 1000; mode->Flags = V_NHSYNC | V_PVSYNC; xf86SetModeDefaultName (mode); xf86SetModeCrtc (mode, pScrn->adjustFlags); /* needed? xf86-video-modesetting does this */ return mode; } static DisplayModePtr qxl_add_mode (qxl_screen_t *qxl, ScrnInfoPtr pScrn, int width, int height, int type) { DisplayModePtr mode; mode = screen_create_mode (pScrn, width, height, type); qxl->x_modes = xf86ModesAdd (qxl->x_modes, mode); return mode; } static int check_crtc (qxl_screen_t *qxl) { int i, count = 0; xf86CrtcPtr crtc; if (qxl->crtcs == NULL) { return 0; } for (i = 0 ; i < qxl->num_heads; ++i) { crtc = qxl->crtcs[i]; if (!crtc->enabled || crtc->mode.CrtcHDisplay == 0 || crtc->mode.CrtcVDisplay == 0) { continue; } count++; } #if 0 if (count == 0) { ErrorF ("check crtc failed, count == 0!!\n"); BREAKPOINT (); } #endif return count; } static void qxl_update_monitors_config (qxl_screen_t *qxl) { int i; QXLHead *head; xf86CrtcPtr crtc; qxl_output_private *qxl_output; QXLRam * ram = get_ram_header (qxl); if (check_crtc (qxl) == 0) return; qxl->monitors_config->count = 0; qxl->monitors_config->max_allowed = qxl->num_heads; for (i = 0 ; i < qxl->num_heads; ++i) { head = &qxl->monitors_config->heads[qxl->monitors_config->count]; crtc = qxl->crtcs[i]; qxl_output = qxl->outputs[i]->driver_private; head->id = i; head->surface_id = 0; head->flags = 0; if (!crtc->enabled || crtc->mode.CrtcHDisplay == 0 || crtc->mode.CrtcVDisplay == 0) { head->width = head->height = head->x = head->y = 0; qxl_output->status = XF86OutputStatusDisconnected; } else { head->width = crtc->mode.CrtcHDisplay; head->height = crtc->mode.CrtcVDisplay; head->x = crtc->x; head->y = crtc->y; qxl->monitors_config->count++; qxl_output->status = XF86OutputStatusConnected; } } /* initialize when actually used, memslots should be initialized by now */ if (ram->monitors_config == 0) { ram->monitors_config = physical_address (qxl, qxl->monitors_config, qxl->main_mem_slot); } qxl_io_monitors_config_async (qxl); } static Bool crtc_set_mode_major (xf86CrtcPtr crtc, DisplayModePtr mode, Rotation rotation, int x, int y) { qxl_crtc_private *crtc_private = crtc->driver_private; qxl_screen_t * qxl = crtc_private->qxl; if (crtc == qxl->crtcs[0] && mode == NULL) { /* disallow disabling of monitor 0 mode */ ErrorF ("%s: not allowing crtc 0 disablement\n", __func__); return FALSE; } crtc->mode = *mode; crtc->x = x; crtc->y = y; crtc->rotation = rotation; #if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC (1, 5, 99, 0, 0) crtc->transformPresent = FALSE; #endif /* TODO set EDID here */ return TRUE; } Bool qxl_create_desired_modes (qxl_screen_t *qxl) { int i; xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR (qxl->pScrn); CHECK_POINT (); for (i = 0 ; i < config->num_crtc; ++i) { xf86CrtcPtr crtc = config->crtc[i]; if (!crtc->enabled) continue; if (!crtc_set_mode_major ( crtc, &crtc->desiredMode, crtc->desiredRotation, crtc->desiredX, crtc->desiredY)) { return FALSE; } } qxl_update_monitors_config(qxl); return TRUE; } void qxl_update_edid (qxl_screen_t *qxl) { xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR (qxl->pScrn); int i; for (i = 0; i < config->num_crtc; ++i) { xf86CrtcPtr crtc = config->crtc[i]; if (!crtc->enabled) continue; /* TODO set EDID here */ } } static DisplayModePtr qxl_output_get_modes (xf86OutputPtr output) { qxl_output_private *qxl_output = output->driver_private; DisplayModePtr modes = xf86DuplicateModes (qxl_output->qxl->pScrn, qxl_output->qxl->x_modes); /* xf86ProbeOutputModes owns this memory */ return modes; } static void qxl_output_destroy (xf86OutputPtr output) { qxl_output_private *qxl_output = output->driver_private; xf86DrvMsg (qxl_output->qxl->pScrn->scrnIndex, X_INFO, "%s", __func__); } static void qxl_output_dpms (xf86OutputPtr output, int mode) { } static void qxl_output_create_resources (xf86OutputPtr output) { } static Bool qxl_output_set_property (xf86OutputPtr output, Atom property, RRPropertyValuePtr value) { /* EDID data is stored in the "EDID" atom property, we must return * TRUE here for that. No penalty to say ok to everything else. */ return TRUE; } static Bool qxl_output_get_property (xf86OutputPtr output, Atom property) { return TRUE; } static xf86OutputStatus qxl_output_detect (xf86OutputPtr output) { qxl_output_private *qxl_output = output->driver_private; return qxl_output->status; } static Bool qxl_output_mode_valid (xf86OutputPtr output, DisplayModePtr pModes) { return MODE_OK; } static const xf86OutputFuncsRec qxl_output_funcs = { .dpms = qxl_output_dpms, .create_resources = qxl_output_create_resources, #ifdef RANDR_12_INTERFACE .set_property = qxl_output_set_property, .get_property = qxl_output_get_property, #endif .detect = qxl_output_detect, .mode_valid = qxl_output_mode_valid, .get_modes = qxl_output_get_modes, .destroy = qxl_output_destroy }; static void qxl_crtc_dpms (xf86CrtcPtr crtc, int mode) { } static Bool qxl_crtc_set_mode_major (xf86CrtcPtr crtc, DisplayModePtr mode, Rotation rotation, int x, int y) { qxl_crtc_private *crtc_private = crtc->driver_private; qxl_screen_t * qxl = crtc_private->qxl; CHECK_POINT (); if (!crtc_set_mode_major (crtc, mode, rotation, x, y)) return FALSE; qxl_update_monitors_config (qxl); return TRUE; } static void qxl_crtc_set_cursor_colors (xf86CrtcPtr crtc, int bg, int fg) { } static void qxl_crtc_set_cursor_position (xf86CrtcPtr crtc, int x, int y) { } static void qxl_crtc_load_cursor_argb (xf86CrtcPtr crtc, CARD32 *image) { } static void qxl_crtc_hide_cursor (xf86CrtcPtr crtc) { } static void qxl_crtc_show_cursor (xf86CrtcPtr crtc) { } static void qxl_crtc_gamma_set (xf86CrtcPtr crtc, uint16_t *red, uint16_t *green, uint16_t *blue, int size) { } static void qxl_crtc_destroy (xf86CrtcPtr crtc) { qxl_crtc_private *crtc_private = crtc->driver_private; qxl_screen_t * qxl = crtc_private->qxl; xf86DrvMsg (qxl->pScrn->scrnIndex, X_INFO, "%s\n", __func__); } static Bool qxl_crtc_lock (xf86CrtcPtr crtc) { qxl_crtc_private *crtc_private = crtc->driver_private; qxl_screen_t * qxl = crtc_private->qxl; xf86DrvMsg (qxl->pScrn->scrnIndex, X_INFO, "%s\n", __func__); return TRUE; } static void qxl_crtc_unlock (xf86CrtcPtr crtc) { qxl_crtc_private *crtc_private = crtc->driver_private; qxl_screen_t * qxl = crtc_private->qxl; xf86DrvMsg (qxl->pScrn->scrnIndex, X_INFO, "%s\n", __func__); qxl_update_monitors_config (qxl); } static const xf86CrtcFuncsRec qxl_crtc_funcs = { .dpms = qxl_crtc_dpms, .set_mode_major = qxl_crtc_set_mode_major, .set_cursor_colors = qxl_crtc_set_cursor_colors, .set_cursor_position = qxl_crtc_set_cursor_position, .show_cursor = qxl_crtc_show_cursor, .hide_cursor = qxl_crtc_hide_cursor, .load_cursor_argb = qxl_crtc_load_cursor_argb, .lock = qxl_crtc_lock, .unlock = qxl_crtc_unlock, .gamma_set = qxl_crtc_gamma_set, .destroy = qxl_crtc_destroy, }; static Bool qxl_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height) { qxl_screen_t *qxl = scrn->driverPrivate; xf86DrvMsg (scrn->scrnIndex, X_INFO, "%s: Placeholder resize %dx%d\n", __func__, width, height); if (!qxl_resize_primary (qxl, width, height)) return FALSE; scrn->virtualX = width; scrn->virtualY = height; // when starting, no monitor is enabled, and count == 0 // we want to avoid server/client freaking out with temporary config qxl_update_monitors_config (qxl); return TRUE; } static const xf86CrtcConfigFuncsRec qxl_xf86crtc_config_funcs = { qxl_xf86crtc_resize }; void qxl_initialize_x_modes (qxl_screen_t *qxl, ScrnInfoPtr pScrn, unsigned int *max_x, unsigned int *max_y) { int i; int size; int preferred_flag; *max_x = *max_y = 0; /* Create a list of modes used by the qxl_output_get_modes */ for (i = 0; i < qxl->num_modes; i++) { if (qxl->modes[i].orientation == 0) { size = qxl->modes[i].y_res * qxl->modes[i].stride; if (size > qxl->surface0_size) { ErrorF ("skipping mode %dx%d not fitting in surface0\n", qxl->modes[i].x_res, qxl->modes[i].y_res); continue; } if (qxl->modes[i].x_res == DEFAULT_WIDTH && qxl->modes[i].y_res == DEFAULT_HEIGHT) preferred_flag = M_T_PREFERRED; else preferred_flag = 0; qxl_add_mode (qxl, pScrn, qxl->modes[i].x_res, qxl->modes[i].y_res, M_T_DRIVER | preferred_flag); if (qxl->modes[i].x_res > *max_x) *max_x = qxl->modes[i].x_res; if (qxl->modes[i].y_res > *max_y) *max_y = qxl->modes[i].y_res; } } } void qxl_init_randr (ScrnInfoPtr pScrn, qxl_screen_t *qxl) { char name[32]; qxl_output_private *qxl_output; qxl_crtc_private * qxl_crtc; int i; xf86OutputPtr output; xf86CrtcConfigInit (pScrn, &qxl_xf86crtc_config_funcs); /* CHECKME: This is actually redundant, it's overwritten by a later call via * xf86InitialConfiguration */ xf86CrtcSetSizeRange (pScrn, 320, 200, 8192, 8192); qxl->crtcs = xnfcalloc (sizeof (xf86CrtcPtr), qxl->num_heads); qxl->outputs = xnfcalloc (sizeof (xf86OutputPtr), qxl->num_heads); for (i = 0 ; i < qxl->num_heads; ++i) { qxl->crtcs[i] = xf86CrtcCreate (pScrn, &qxl_crtc_funcs); if (!qxl->crtcs[i]) xf86DrvMsg (pScrn->scrnIndex, X_ERROR, "failed to create Crtc %d", i); qxl_crtc = xnfcalloc (sizeof (qxl_crtc_private), 1); qxl->crtcs[i]->driver_private = qxl_crtc; qxl_crtc->head = i; qxl_crtc->qxl = qxl; snprintf (name, sizeof (name), "qxl-%d", i); qxl->outputs[i] = output = xf86OutputCreate (pScrn, &qxl_output_funcs, name); if (!output) xf86DrvMsg (pScrn->scrnIndex, X_ERROR, "failed to create Output %d", i); output->possible_crtcs = (1 << i); /* bitrange of allowed outputs - do a 1:1 */ output->possible_clones = 0; /* TODO: not? */ qxl_output = xnfcalloc (sizeof (qxl_output_private), 1); output->driver_private = qxl_output; qxl_output->head = i; qxl_output->qxl = qxl; qxl_output->status = i ? XF86OutputStatusDisconnected : XF86OutputStatusConnected; qxl_crtc->output = output; } xf86InitialConfiguration (pScrn, TRUE); qxl->virtual_x = pScrn->virtualX; qxl->virtual_y = pScrn->virtualY; /* all crtcs are enabled here, but their mode is 0, resulting monitor config empty atm */ } xserver-xorg-video-qxl-0.1.5+git20200331/src/qxl_uxa.c000066400000000000000000000327551361660122400221210ustar00rootroot00000000000000/* * Copyright 2008 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ /** \file qxl_driver.c * \author Adam Jackson * \author Søren Sandmann * * This is qxl, a driver for the Qumranet paravirtualized graphics device * in qemu. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "qxl.h" #include "dfps.h" #include #if HAS_DEVPRIVATEKEYREC DevPrivateKeyRec uxa_pixmap_index; #else int uxa_pixmap_index; #endif static Bool qxl_prepare_access (PixmapPtr pixmap, RegionPtr region, uxa_access_t access) { return qxl_surface_prepare_access (get_surface (pixmap), pixmap, region, access); } static void qxl_finish_access (PixmapPtr pixmap) { qxl_surface_finish_access (get_surface (pixmap), pixmap); } static Bool qxl_pixmap_is_offscreen (PixmapPtr pixmap) { return !!get_surface (pixmap); } static Bool good_alu_and_pm (DrawablePtr drawable, int alu, Pixel planemask) { if (!UXA_PM_IS_SOLID (drawable, planemask)) return FALSE; if (alu != GXcopy) return FALSE; return TRUE; } /* * Solid fill */ static Bool qxl_check_solid (DrawablePtr drawable, int alu, Pixel planemask) { if (!good_alu_and_pm (drawable, alu, planemask)) return FALSE; return TRUE; } static Bool qxl_prepare_solid (PixmapPtr pixmap, int alu, Pixel planemask, Pixel fg) { qxl_surface_t *surface; if (!(surface = get_surface (pixmap))) return FALSE; return qxl_surface_prepare_solid (surface, fg); } static void qxl_solid (PixmapPtr pixmap, int x1, int y1, int x2, int y2) { qxl_surface_solid (get_surface (pixmap), x1, y1, x2, y2); } static void qxl_done_solid (PixmapPtr pixmap) { } /* * Copy */ static Bool qxl_check_copy (PixmapPtr source, PixmapPtr dest, int alu, Pixel planemask) { if (!good_alu_and_pm ((DrawablePtr)source, alu, planemask)) return FALSE; if (source->drawable.bitsPerPixel != dest->drawable.bitsPerPixel) { ErrorF ("differing bitsperpixel - this shouldn't happen\n"); return FALSE; } return TRUE; } static Bool qxl_prepare_copy (PixmapPtr source, PixmapPtr dest, int xdir, int ydir, int alu, Pixel planemask) { return qxl_surface_prepare_copy (get_surface (dest), get_surface (source)); } static void qxl_copy (PixmapPtr dest, int src_x1, int src_y1, int dest_x1, int dest_y1, int width, int height) { qxl_surface_copy (get_surface (dest), src_x1, src_y1, dest_x1, dest_y1, width, height); } static void qxl_done_copy (PixmapPtr dest) { } /* * Composite */ static Bool can_accelerate_picture (qxl_screen_t *qxl, PicturePtr pict) { if (!pict) return TRUE; if (pict->format != PICT_a8r8g8b8 && pict->format != PICT_x8r8g8b8 && pict->format != PICT_a8) { if (qxl->debug_render_fallbacks) { ErrorF ("Image with format %x can't be accelerated \n", pict->format); } return FALSE; } if (!pict->pDrawable) { if (qxl->debug_render_fallbacks) { ErrorF ("Source image (of type %d) can't be accelerated\n", pict->pSourcePict->type); } return FALSE; } if (pict->transform) { if (pict->transform->matrix[2][0] != 0 || pict->transform->matrix[2][1] != 0 || pict->transform->matrix[2][2] != pixman_int_to_fixed (1)) { if (qxl->debug_render_fallbacks) ErrorF ("Image with non-affine transform can't be accelerated\n"); return FALSE; } } if (pict->filter != PictFilterBilinear && pict->filter != PictFilterNearest) { if (qxl->debug_render_fallbacks) { ErrorF ("Image with filter type %d can't be accelerated\n", pict->filter); } return FALSE; } return TRUE; } #define QXL_HAS_CAP(qxl, cap) \ (((qxl)->rom->client_capabilities[(cap) / 8]) & (1 << ((cap) % 8))) static Bool qxl_has_composite (qxl_screen_t *qxl) { #ifdef XF86DRM_MODE if (qxl->kms_enabled) { #if 0 /* KMS Composite support seems broken - needs better hw support */ static Bool result, checked; if (!checked) { result = qxl_kms_check_cap(qxl, SPICE_DISPLAY_CAP_COMPOSITE); checked = TRUE; } return result; #else return FALSE; #endif } #endif #ifndef XSPICE return qxl->pci->revision >= 4 && QXL_HAS_CAP (qxl, SPICE_DISPLAY_CAP_COMPOSITE); #else /* FIXME */ return FALSE; #endif } static Bool qxl_has_a8_surfaces (qxl_screen_t *qxl) { #ifdef XF86DRM_MODE if (qxl->kms_enabled) { #if 0 /* KMS Composite support seems broken - needs better hw support */ static Bool result, checked; if (!checked) { result = qxl_kms_check_cap(qxl, SPICE_DISPLAY_CAP_A8_SURFACE); checked = TRUE; } return result; #else return FALSE; #endif } #endif #ifndef XSPICE if (qxl->pci->revision < 4) { if (qxl->debug_render_fallbacks) { ErrorF ("No a8 surface due to revision being %d, which is < 4\n", qxl->pci->revision); } return FALSE; } if (!QXL_HAS_CAP (qxl, SPICE_DISPLAY_CAP_COMPOSITE)) { if (qxl->debug_render_fallbacks) { ErrorF ("No composite due to client not providing SPICE_DISPLAY_CAP_A8_SURFACE\n"); } return FALSE; } return TRUE; #else /* FIXME */ return FALSE; #endif } static Bool qxl_check_composite (int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture, int width, int height) { int i; ScreenPtr pScreen = pDstPicture->pDrawable->pScreen; ScrnInfoPtr pScrn = xf86ScreenToScrn (pScreen); qxl_screen_t *qxl = pScrn->driverPrivate; static const int accelerated_ops[] = { PictOpClear, PictOpSrc, PictOpDst, PictOpOver, PictOpOverReverse, PictOpIn, PictOpInReverse, PictOpOut, PictOpOutReverse, PictOpAtop, PictOpAtopReverse, PictOpXor, PictOpAdd, PictOpSaturate, PictOpMultiply, PictOpScreen, PictOpOverlay, PictOpDarken, PictOpLighten, PictOpColorDodge, PictOpColorBurn, PictOpHardLight, PictOpSoftLight, PictOpDifference, PictOpExclusion, PictOpHSLHue, PictOpHSLSaturation, PictOpHSLColor, PictOpHSLLuminosity, }; if (!qxl_has_composite (qxl)) return FALSE; if (!can_accelerate_picture (qxl, pSrcPicture) || !can_accelerate_picture (qxl, pMaskPicture) || !can_accelerate_picture (qxl, pDstPicture)) { return FALSE; } for (i = 0; i < sizeof (accelerated_ops) / sizeof (accelerated_ops[0]); ++i) { if (accelerated_ops[i] == op) goto found; } if (qxl->debug_render_fallbacks) ErrorF ("Compositing operator %d can't be accelerated\n", op); return FALSE; found: return TRUE; } static Bool qxl_check_composite_target (PixmapPtr pixmap) { return TRUE; } static Bool qxl_check_composite_texture (ScreenPtr screen, PicturePtr pPicture) { return TRUE; } static Bool qxl_prepare_composite (int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture, PixmapPtr pSrc, PixmapPtr pMask, PixmapPtr pDst) { return qxl_surface_prepare_composite ( op, pSrcPicture, pMaskPicture, pDstPicture, get_surface (pSrc), pMask? get_surface (pMask) : NULL, get_surface (pDst)); } static void qxl_composite (PixmapPtr pDst, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, int width, int height) { qxl_surface_composite ( get_surface (pDst), src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); } static void qxl_done_composite (PixmapPtr pDst) { ; } static Bool qxl_put_image (PixmapPtr pDst, int x, int y, int w, int h, char *src, int src_pitch) { qxl_surface_t *surface = get_surface (pDst); if (surface) return qxl_surface_put_image (surface, x, y, w, h, src, src_pitch); return FALSE; } static void qxl_set_screen_pixmap (PixmapPtr pixmap) { pixmap->drawable.pScreen->devPrivate = pixmap; } static PixmapPtr qxl_create_pixmap (ScreenPtr screen, int w, int h, int depth, unsigned usage) { ScrnInfoPtr scrn = xf86ScreenToScrn (screen); PixmapPtr pixmap; qxl_screen_t * qxl = scrn->driverPrivate; qxl_surface_t *surface; if (w > 32767 || h > 32767) return NULL; qxl_surface_cache_sanity_check (qxl->surface_cache); #if 0 ErrorF ("Create pixmap: %d %d @ %d (usage: %d)\n", w, h, depth, usage); #endif if (qxl->kms_enabled) goto fallback; if (uxa_swapped_out (screen)) goto fallback; if (depth == 8 && !qxl_has_a8_surfaces (qxl)) { /* FIXME: When we detect a _change_ in the property of having a8 * surfaces, we should copy all existing a8 surface to host memory * and then destroy the ones on the device. */ goto fallback; } if (!w || !h) goto fallback; surface = qxl->bo_funcs->create_surface (qxl, w, h, depth); if (surface) { /* ErrorF (" Successfully created surface in video memory\n"); */ pixmap = fbCreatePixmap (screen, 0, 0, depth, usage); screen->ModifyPixmapHeader (pixmap, w, h, -1, -1, -1, NULL); #if 0 ErrorF ("Create pixmap %p with surface %p\n", pixmap, surface); #endif set_surface (pixmap, surface); qxl_surface_set_pixmap (surface, pixmap); qxl_surface_cache_sanity_check (qxl->surface_cache); } else { #if 0 ErrorF (" Couldn't allocate %d x %d @ %d surface in video memory\n", w, h, depth); #endif fallback: pixmap = fbCreatePixmap (screen, w, h, depth, usage); #if 0 ErrorF ("Create pixmap %p without surface\n", pixmap); #endif } return pixmap; } static Bool qxl_destroy_pixmap (PixmapPtr pixmap) { ScreenPtr screen = pixmap->drawable.pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn (screen); qxl_screen_t * qxl = scrn->driverPrivate; qxl_surface_t *surface = NULL; qxl_surface_cache_sanity_check (qxl->surface_cache); if (pixmap->refcnt == 1) { surface = get_surface (pixmap); #if 0 ErrorF ("- Destroy %p (had surface %p)\n", pixmap, surface); #endif if (surface) { qxl->bo_funcs->destroy_surface(surface); set_surface (pixmap, NULL); qxl_surface_cache_sanity_check (qxl->surface_cache); } } fbDestroyPixmap (pixmap); return TRUE; } static void set_uxa_functions(qxl_screen_t *qxl, ScreenPtr screen) { /* Solid fill */ qxl->uxa->check_solid = qxl_check_solid; qxl->uxa->prepare_solid = qxl_prepare_solid; qxl->uxa->solid = qxl_solid; qxl->uxa->done_solid = qxl_done_solid; /* Copy */ qxl->uxa->check_copy = qxl_check_copy; qxl->uxa->prepare_copy = qxl_prepare_copy; qxl->uxa->copy = qxl_copy; qxl->uxa->done_copy = qxl_done_copy; /* Composite */ qxl->uxa->check_composite = qxl_check_composite; qxl->uxa->check_composite_target = qxl_check_composite_target; qxl->uxa->check_composite_texture = qxl_check_composite_texture; qxl->uxa->prepare_composite = qxl_prepare_composite; qxl->uxa->composite = qxl_composite; qxl->uxa->done_composite = qxl_done_composite; /* PutImage */ qxl->uxa->put_image = qxl_put_image; /* Prepare access */ qxl->uxa->prepare_access = qxl_prepare_access; qxl->uxa->finish_access = qxl_finish_access; qxl->uxa->pixmap_is_offscreen = qxl_pixmap_is_offscreen; screen->SetScreenPixmap = qxl_set_screen_pixmap; screen->CreatePixmap = qxl_create_pixmap; screen->DestroyPixmap = qxl_destroy_pixmap; } Bool qxl_uxa_init (qxl_screen_t *qxl, ScreenPtr screen) { ScrnInfoPtr scrn = xf86ScreenToScrn (screen); #if HAS_DIXREGISTERPRIVATEKEY if (!dixRegisterPrivateKey (&uxa_pixmap_index, PRIVATE_PIXMAP, 0)) return FALSE; #else if (!dixRequestPrivate (&uxa_pixmap_index, 0)) return FALSE; #endif qxl->uxa = uxa_driver_alloc (); if (qxl->uxa == NULL) return FALSE; memset (qxl->uxa, 0, sizeof (*qxl->uxa)); qxl->uxa->uxa_major = 1; qxl->uxa->uxa_minor = 0; if (qxl->deferred_fps) dfps_set_uxa_functions(qxl, screen); else set_uxa_functions(qxl, screen); if (!uxa_driver_init (screen, qxl->uxa)) { xf86DrvMsg (scrn->scrnIndex, X_ERROR, "UXA initialization failed\n"); free (qxl->uxa); return FALSE; } #if 0 uxa_set_fallback_debug (screen, FALSE); #endif #if 0 if (!uxa_driver_init (screen, qxl->uxa)) return FALSE; #endif return TRUE; } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceccid/000077500000000000000000000000001361660122400222065ustar00rootroot00000000000000xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceccid/Makefile.am000066400000000000000000000026441361660122400242500ustar00rootroot00000000000000# Copyright 2014 Jeremy White for CodeWeavers, Inc. # # 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 # on the rights to use, copy, modify, merge, publish, distribute, sub # license, 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 (including the next # paragraph) 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 NON-INFRINGEMENT. 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. if BUILD_SPICECCID libspiceccid_la_LTLIBRARIES = libspiceccid.la libspiceccid_la_CFLAGS = $(LIBPCSCLITE_CFLAGS) $(LIBCACARD_CFLAGS) libspiceccid_la_LDFLAGS = $(LIBPCSCLITE_LDFLAGS) $(LIBCACARD_LDFLAGS) libspiceccid_la_SOURCES = spiceccid.c libspiceccid_ladir = @cciddir@/ endif xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceccid/spice.pcsc.conf.template000066400000000000000000000005011361660122400267150ustar00rootroot00000000000000# Spice CCID Reader # This configuration file is the format required by the pcscd daemon for # serial devices; so the qxl driver looks like a serial device to pcscd. FRIENDLYNAME "Spice ccid" DEVICENAME /tmp/spice.pcsc.comm LIBPATH /usr/lib/pcsc/drivers/serial/libspiceccid.so CHANNELID 1 xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceccid/spiceccid.c000066400000000000000000000322261361660122400243050ustar00rootroot00000000000000/* * Copyright (C) 2014 CodeWeavers, Inc. * * 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 (including the next * paragraph) 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 OR COPYRIGHT HOLDERS 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. * * Authors: * Jeremy White */ /*---------------------------------------------------------------------------- Chip/Smart Card Interface Devices driver for Spice This driver is built to interface to pcsc-lite as a serial smartcard device. It translates the IFD (Interface device) ABI into the Spice protocol. ----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include "vscard_common.h" #include "ifdhandler.h" #include typedef struct apdu_list { void *data; int len; struct apdu_list *next; } apdu_t; #define MAX_LUNS 2 typedef struct smartcard_ccid { int fd; int lun; pthread_t tid; int state; char atr[36]; int atr_len; pthread_mutex_t apdu_lock; apdu_t *apdu_list; } smartcard_ccid_t; #define STATE_OPEN 1 #define STATE_READER_ADDED 2 #define STATE_READER_REMOVED 4 #if ! defined(MIN) #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #endif smartcard_ccid_t luns[MAX_LUNS] = { { -1 }, { -1 } }; RESPONSECODE IFDHCloseChannel(DWORD Lun); static void push_apdu(smartcard_ccid_t *ccid, void *data, int len) { apdu_t *a = malloc(sizeof(*a)); apdu_t **p; a->data = malloc(len); a->len = len; a->next = NULL; memcpy(a->data, data, len); pthread_mutex_lock(&ccid->apdu_lock); for (p = &ccid->apdu_list; *p; p = &(*p)->next) ; *p = a; pthread_mutex_unlock(&ccid->apdu_lock); } static apdu_t * pop_apdu(smartcard_ccid_t *ccid) { apdu_t *p; pthread_mutex_lock(&ccid->apdu_lock); p = ccid->apdu_list; if (ccid->apdu_list) ccid->apdu_list = p->next; pthread_mutex_unlock(&ccid->apdu_lock); return p; } static void free_apdu(apdu_t *a) { free(a->data); free(a); } static void send_reply(smartcard_ccid_t *ccid, uint32_t code) { uint32_t reply[4]; reply[0] = htonl(VSC_Error); // type reply[1] = htonl(ccid->lun); // reader id reply[2] = htonl(sizeof(uint32_t)); // length reply[3] = htonl(code); // Error code if (write(ccid->fd, (char *) reply, sizeof(reply)) != sizeof(reply)) { fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno); IFDHCloseChannel(ccid->lun); } } static int send_tx_buffer(smartcard_ccid_t *ccid, void *data, int len) { uint32_t *reply, *p; int write_len = sizeof(*reply) * 3 + len; reply = malloc(write_len); p = reply; *p++ = htonl(VSC_APDU); // type *p++ = htonl(ccid->lun); // reader id *p++ = htonl(len); memcpy(p, data, len); if (write(ccid->fd, (char *) reply, write_len) != write_len) { fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno); IFDHCloseChannel(ccid->lun); free(reply); return 0; } free(reply); return 1; } static void process_reader_add(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data) { if (ccid->state & STATE_READER_ADDED) { send_reply(ccid, VSC_GENERAL_ERROR); return; } ccid->state |= STATE_READER_ADDED; ccid->state &= ~STATE_READER_REMOVED; pthread_mutex_init(&ccid->apdu_lock, NULL); ccid->apdu_list = NULL; send_reply(ccid, VSC_SUCCESS); } static void process_reader_remove(smartcard_ccid_t *ccid, VSCMsgHeader *h) { apdu_t *p; if (ccid->state & STATE_READER_REMOVED) { send_reply(ccid, VSC_GENERAL_ERROR); return; } ccid->state |= STATE_READER_REMOVED; ccid->state &= ~STATE_READER_ADDED; while (p = pop_apdu(ccid)) free_apdu(p); pthread_mutex_destroy(&ccid->apdu_lock); send_reply(ccid, VSC_SUCCESS); } static void process_atr(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data) { ccid->atr_len = h->length; if (h->length > sizeof(ccid->atr)) { fprintf(stderr, "Supplied ATR of length %d exceeds %d maximum\n", h->length, sizeof(ccid->atr)); send_reply(ccid, VSC_GENERAL_ERROR); return; } memset(ccid->atr, 0, sizeof(ccid->atr)); memcpy(ccid->atr, data, ccid->atr_len); send_reply(ccid, VSC_SUCCESS); } static void process_apdu(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data) { if (ccid->state & STATE_READER_ADDED) push_apdu(ccid, data, h->length); else fprintf(stderr, "apdu of length %d discarded; inactive reader\n", h->length); } static void process_card_remove(smartcard_ccid_t *ccid, VSCMsgHeader *h) { ccid->atr_len = 0; memset(ccid->atr, 0, sizeof(ccid->atr)); send_reply(ccid, VSC_SUCCESS); } static int process_message(smartcard_ccid_t *ccid, char *buf, int len) { VSCMsgHeader h; uint32_t *p = (uint32_t *) buf; h.type = ntohl(*p++); h.reader_id = ntohl(*p++); h.length = ntohl(*p++); if (len < sizeof(h) || len < sizeof(h) + h.length) return 0; switch (h.type) { case VSC_ReaderAdd: process_reader_add(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL); break; case VSC_ReaderRemove: process_reader_remove(ccid, &h); break; case VSC_ATR: process_atr(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL); break; case VSC_CardRemove: process_card_remove(ccid, &h); break; case VSC_APDU: process_apdu(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL); break; default: fprintf(stderr, "spiceccid %s: unknown smartcard message %d / %d\n", __FUNCTION__, h.type, sizeof(h) + h.length); } return(h.length + sizeof(h)); } static void * lun_thread(void *arg) { char buf[8096]; int pos = 0; smartcard_ccid_t *ccid = (smartcard_ccid_t *) arg; int rc; while (1) { rc = read(ccid->fd, buf + pos, sizeof(buf) - pos); if (rc == -1) if (errno == EINTR) continue; else break; if (rc == 0) break; pos += rc; do { rc = process_message(ccid, buf, pos); pos -= rc; if (rc > 0 && pos > 0) memmove(buf, buf + rc, pos); } while (rc > 0 && pos > 0); } fprintf(stderr, "LUN %d thread exiting: %s\n", ccid->lun, rc == 0 ? "normally" : strerror(errno)); close(ccid->fd); ccid->fd = -1; ccid->lun = 0; ccid->atr_len = 0; ccid->state &= ~STATE_OPEN; return NULL; } static void send_init(smartcard_ccid_t *ccid) { uint32_t msg[6]; msg[0] = htonl(VSC_Init); // type msg[1] = htonl(ccid->lun); // reader id msg[2] = htonl(sizeof(uint32_t) * 3); // length msg[3] = htonl(VSCARD_MAGIC); // VSCD msg[4] = htonl(VSCARD_VERSION); // VSCD msg[5] = 0; // capabilities if (write(ccid->fd, (char *) msg, sizeof(msg)) != sizeof(msg)) { fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno); IFDHCloseChannel(ccid->lun); } } /*---------------------------------------------------------------------------- IFDHCreateChannelByName The pcsc daemon should invoke this function passing in the path name configured in reader.conf. */ RESPONSECODE IFDHCreateChannelByName(DWORD Lun, LPSTR DeviceName) { int i; struct sockaddr_un addr; for (i = 0; i < MAX_LUNS; i++) if (luns[i].fd != -1 && luns[i].lun == Lun) return IFD_COMMUNICATION_ERROR; for (i = 0; i < MAX_LUNS; i++) if (luns[i].fd == -1) break; if (i >= MAX_LUNS) return IFD_COMMUNICATION_ERROR; luns[i].fd = socket(AF_UNIX, SOCK_STREAM, 0); if (luns[i].fd < 0) return IFD_NO_SUCH_DEVICE; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, DeviceName, sizeof(addr.sun_path) - 1); if (connect(luns[i].fd, (struct sockaddr *) &addr, sizeof(addr))) { close(luns[i].fd); return IFD_COMMUNICATION_ERROR; } if (pthread_create(&luns[i].tid, NULL, &lun_thread, &luns[i])) { close(luns[i].fd); return IFD_COMMUNICATION_ERROR; } luns[i].lun = Lun; luns[i].state = STATE_OPEN; return IFD_SUCCESS; } RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel) { fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Channel %ld\n", __FUNCTION__, Lun, Channel); return IFD_ERROR_NOT_SUPPORTED; } RESPONSECODE IFDHCloseChannel(DWORD Lun) { int i; for (i = 0; i < MAX_LUNS; i++) { if (luns[i].fd != -1 && luns[i].lun == Lun) { pthread_cancel(luns[i].tid); close(luns[i].fd); luns[i].fd = -1; luns[i].lun = 0; luns[i].atr_len = 0; luns[i].state &= ~STATE_OPEN; break; } } if (i == MAX_LUNS) return IFD_NO_SUCH_DEVICE; return IFD_SUCCESS; } RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value) { fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Tag %ld, Length %ld, Value %p\n", __FUNCTION__, Lun, Tag, *Length, Value); /* TODO - explore supporting TAG_IFD_POLLING_THREAD */ return IFD_ERROR_NOT_SUPPORTED; } RESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag, DWORD Length, PUCHAR Value) { return IFD_ERROR_NOT_SUPPORTED; } RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength) { int i; for (i = 0; i < MAX_LUNS; i++) if (luns[i].fd != -1 && luns[i].lun == Lun) if (Action == IFD_POWER_UP || Action == IFD_RESET) { if (*AtrLength >= luns[i].atr_len) { memcpy(Atr, luns[i].atr, luns[i].atr_len); *AtrLength = luns[i].atr_len; } send_init(&luns[i]); return IFD_SUCCESS; } fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Action %ld\n", __FUNCTION__, Lun, Action); return IFD_ERROR_NOT_SUPPORTED; } #define TX_MAX_SLEEP 5000 #define TX_SLEEP_INTERVAL 1000 RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci, PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, PDWORD RxLength, PSCARD_IO_HEADER RecvPci) { apdu_t *p; int i, j; for (i = 0; i < MAX_LUNS; i++) if (luns[i].fd != -1 && luns[i].lun == Lun) { while (p = pop_apdu(&luns[i])) free_apdu(p); if (send_tx_buffer(&luns[i], TxBuffer, TxLength)) { for (j = 0; j < TX_MAX_SLEEP; j++) if (p = pop_apdu(&luns[i])) break; else usleep(TX_SLEEP_INTERVAL); if (p) { memcpy(RxBuffer, p->data, MIN(p->len, *RxLength)); *RxLength = MIN(p->len, *RxLength); free_apdu(p); return IFD_SUCCESS; } return IFD_RESPONSE_TIMEOUT; } } return IFD_NO_SUCH_DEVICE; } RESPONSECODE IFDHICCPresence(DWORD Lun) { int i; for (i = 0; i < MAX_LUNS; i++) if (luns[i].fd != -1 && luns[i].lun == Lun) { if (luns[i].atr_len > 0 && luns[i].state & STATE_READER_ADDED) return IFD_SUCCESS; return IFD_ICC_NOT_PRESENT; } return IFD_NO_SUCH_DEVICE; } RESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol, UCHAR Flags, UCHAR PTS1, UCHAR PTS2, UCHAR PTS3) { if (Protocol == SCARD_PROTOCOL_T1) return IFD_SUCCESS; return IFD_NOT_SUPPORTED; } RESPONSECODE IFDHControl(DWORD Lun, DWORD dwControlCode, PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, DWORD RxLength, LPDWORD pdwBytesReturned) { fprintf(stderr, "spiceccid %s unsupported: Lun %ld\n", __FUNCTION__, Lun); return IFD_ERROR_NOT_SUPPORTED; } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_audio.c000066400000000000000000000371641361660122400234500ustar00rootroot00000000000000/* * Copyright 2012 Andrew Eikum for CodeWeavers Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ /* XSpice based audio feed; reads from files (presumably fifos) in a configured directory, and mixes their raw data on to the spice playback channel. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "spiceqxl_audio.h" #include #include #include #include #include #include #if defined(HAVE_SYS_INOTIFY_H) #include #endif /* mplayer + pulse will write data to the fifo as fast as we can read it. So we need to pace both how quickly we consume the data and how quickly we feed the data in to Spice. We will read ahead (up to READ_BUFFER_PERIODS), and feed ahead into the Spice server (up to FEED_BUFFER_PERIODS). */ #define IDLE_MS 300 #define PERIOD_MS 10 #define READ_BUFFER_PERIODS 2 #define FEED_BUFFER_PERIODS 8 #define MAX_FIFOS 16 struct fifo_data { char *buffer; int size; int len; int add_to; int fd; SpiceWatch *watch; }; struct audio_data { struct fifo_data fifos[MAX_FIFOS]; int active; uint32_t *spice_buffer; int spice_buffer_bytes; int period_bytes; struct timeval fed_through_time; int remainder; int fifo_count; int closed_fifos; SpiceTimer *wall_timer; int wall_timer_type; int dir_watch; int fifo_dir_watch; SpiceWatch *fifo_dir_qxl_watch; }; /* We maintain a ring buffer for each file we are reading from; these helper functions facilitate adding data to the buffer, and removing it. */ static inline void fifo_data_added(struct fifo_data *f, int n) { f->add_to = (f->add_to + n) % f->size; f->len += n; } static inline int fifo_read(struct fifo_data *f) { int rc; int len = min(f->size - f->len, f->size - f->add_to); rc = read(f->fd, f->buffer + f->add_to, len); if (rc > 0) fifo_data_added(f, rc); if (rc > 0 && rc == len && f->size - f->len > 0) { rc = read(f->fd, f->buffer + f->add_to, f->size - f->len); if (rc > 0) fifo_data_added(f, rc); } return rc; } static inline void fifo_remove_data(struct fifo_data *f, unsigned char *dest, int len) { int remove_from = f->add_to >= f->len ? f->add_to - f->len : f->add_to + f->size - f->len; int remain = f->size - remove_from; if (remain < len) { if (dest) { memcpy(dest, f->buffer + remove_from, remain); dest += remain; } len -= remain; f->len -= remain; remove_from = 0; } if (dest) { memcpy(dest, f->buffer + remove_from, len); } f->len -= len; } static void mix_in_one_fifo(struct fifo_data *f, int16_t *out, int len) { int s; int16_t *in; if (len > f->len) len = f->len; in = calloc(1, len); fifo_remove_data(f, (unsigned char *) in, len); for (s = 0; s < (len / sizeof(int16_t)); s++) { /* FIXME: Ehhh, this'd be better as floats. With this algorithm, * samples mixed after being clipped will have undue weight. But * if we're clipping, then we're distorted anyway, so whatever. */ if (out[s] + in[s] > INT16_MAX) out[s] = INT16_MAX; else if (out[s] + in[s] < -INT16_MAX) out[s] = -INT16_MAX; else out[s] += in[s]; } free(in); } /* a helper for process_fifos() */ static void mix_in_fifos(qxl_screen_t *qxl) { int i; struct audio_data *data = qxl->playback_opaque; struct fifo_data *f; if (data->spice_buffer) { memset(data->spice_buffer, 0, data->spice_buffer_bytes); } if (data->fifo_count == 0) return; /* First fifo can just be copied */ f = &data->fifos[0]; fifo_remove_data(f, (unsigned char *) data->spice_buffer, min(data->spice_buffer_bytes, f->len)); /* Extra fifos need to be mixed in */ for (i = 1; i < data->fifo_count; i++) { f = &data->fifos[i]; if (f->len > 0) { if (data->spice_buffer) { mix_in_one_fifo(f, (int16_t *) data->spice_buffer, data->spice_buffer_bytes); } else { fifo_remove_data(f, NULL, min(data->spice_buffer_bytes, f->len)); } } } } /* a helper for process_fifos() */ static int can_feed(struct audio_data *data) { struct timeval end, diff; gettimeofday(&end, NULL); if (end.tv_sec > data->fed_through_time.tv_sec || (end.tv_sec == data->fed_through_time.tv_sec && end.tv_usec >= data->fed_through_time.tv_usec)) { data->fed_through_time.tv_sec = data->fed_through_time.tv_usec = 0; data->remainder = 0; return 1; } timersub(&data->fed_through_time, &end, &diff); if (diff.tv_sec == 0 && diff.tv_usec < PERIOD_MS * 1000 * FEED_BUFFER_PERIODS) return 1; return 0; } /* a helper for process_fifos() */ static void did_feed(struct audio_data *data, int len) { struct timeval diff; if (data->fed_through_time.tv_sec == 0 && data->fed_through_time.tv_usec == 0) gettimeofday(&data->fed_through_time, NULL); diff.tv_sec = 0; diff.tv_usec = (data->remainder + (len * PERIOD_MS * 1000)) / data->period_bytes; data->remainder = (data->remainder + (len * PERIOD_MS * 1000)) % data->period_bytes; timeradd(&data->fed_through_time, &diff, &data->fed_through_time); } static int process_fifos(qxl_screen_t *qxl, struct audio_data *data, int maxlen) { while (maxlen > 0) { if (! data->spice_buffer) { uint32_t chunk_frames; spice_server_playback_get_buffer(&qxl->playback_sin, &data->spice_buffer, &chunk_frames); data->spice_buffer_bytes = data->spice_buffer ? chunk_frames * sizeof(int16_t) * SPICE_INTERFACE_PLAYBACK_CHAN : data->period_bytes * READ_BUFFER_PERIODS; } if (! can_feed(data)) { return FALSE; } mix_in_fifos(qxl); did_feed(data, data->spice_buffer_bytes); maxlen -= data->spice_buffer_bytes; if (data->spice_buffer) { spice_server_playback_put_samples(&qxl->playback_sin, data->spice_buffer); data->spice_buffer = NULL; } } return TRUE; } /* a helper for read_from_fifos() */ static void condense_fifos(qxl_screen_t *qxl) { struct audio_data *data = qxl->playback_opaque; int i; for (i = 0; i < data->fifo_count; i++) { struct fifo_data *f = &data->fifos[i]; if (f->fd == -1 && f->len == 0) { if ((i + 1) < data->fifo_count) { struct fifo_data tmp = *f; *f = data->fifos[data->fifo_count - 1]; data->fifos[data->fifo_count - 1] = tmp; } data->fifo_count--; i--; if (!--data->closed_fifos) { break; } } } } static void start_watching(qxl_screen_t *qxl); static void read_from_fifos(int fd, int event, void *opaque) { qxl_screen_t *qxl = opaque; struct audio_data *data = qxl->playback_opaque; int i; int maxlen = 0; if (data->wall_timer_type) { qxl->core->timer_cancel(data->wall_timer); data->wall_timer_type = 0; } for (i = 0; i < data->fifo_count; i++) { struct fifo_data *f = &data->fifos[i]; if (f->size - f->len > 0 && f->fd >= 0) { int rc; rc = fifo_read(f); if (rc == -1 && (errno == EAGAIN || errno == EINTR)) /* no new data to read */; else if (rc <= 0) { if (rc == 0) ErrorF("fifo %d closed\n", f->fd); else ErrorF("fifo %d error %d: %s\n", f->fd, errno, strerror(errno)); if (f->watch) qxl->core->watch_remove(f->watch); f->watch = NULL; close(f->fd); f->fd = -1; /* Setting closed_fifos will only have an effect once * the closed fifo's buffer is empty. */ data->closed_fifos++; } if (f->size == f->len) { if (f->watch) qxl->core->watch_remove(f->watch); f->watch = NULL; } } if (f->len > maxlen) maxlen = f->len; } if (data->closed_fifos) { condense_fifos(qxl); } if (maxlen && !data->active) { spice_server_playback_start(&qxl->playback_sin); data->active = 1; } if (!process_fifos(qxl, data, maxlen)) { /* There is still some fifo data to process */ qxl->core->timer_start(data->wall_timer, PERIOD_MS); data->wall_timer_type = PERIOD_MS; } else if (data->fifo_count) { /* All the fifo data was processed. Wait for more */ start_watching(qxl); /* But none may arrive so stop processing if that happens */ qxl->core->timer_start(data->wall_timer, IDLE_MS); data->wall_timer_type = IDLE_MS; } else if (data->active) { /* There is no open fifo anymore */ spice_server_playback_stop(&qxl->playback_sin); data->active = 0; } } /* a helper for read_from_fifos() */ static void start_watching(qxl_screen_t *qxl) { struct audio_data *data = qxl->playback_opaque; int i; for (i = 0; i < data->fifo_count; i++) { struct fifo_data *f = &data->fifos[i]; if (f->watch || f->size == f->len || f->fd == -1) continue; f->watch = qxl->core->watch_add(f->fd, SPICE_WATCH_EVENT_READ, read_from_fifos, qxl); } } /* a helper for read_from_fifos() */ static void wall_ticker(void *opaque) { qxl_screen_t *qxl = opaque; struct audio_data *data = qxl->playback_opaque; if (data->wall_timer_type == IDLE_MS) { /* The audio is likely paused in the application(s) */ if (data->active) { spice_server_playback_stop(&qxl->playback_sin); data->active = 0; } data->wall_timer_type = 0; } else { data->wall_timer_type = 0; read_from_fifos(-1, 0, qxl); } } #if defined(HAVE_SYS_INOTIFY_H) static void handle_one_change(qxl_screen_t *qxl, struct inotify_event *e) { if (e->mask & (IN_CREATE | IN_MOVED_TO)) { struct audio_data *data = qxl->playback_opaque; struct fifo_data *f; char *fname; f = &data->fifos[data->fifo_count]; if (data->fifo_count == MAX_FIFOS) { static int once = 0; if (!once) { ErrorF("playback: Too many FIFOs already open\n"); ++once; } return; } fname = xnfalloc(strlen(e->name) + strlen(qxl->playback_fifo_dir) + 1 + 1); strcpy(fname, qxl->playback_fifo_dir); strcat(fname, "/"); strcat(fname, e->name); f->fd = open(fname, O_RDONLY | O_RSYNC | O_NONBLOCK); free(fname); if (f->fd < 0) { ErrorF("playback: open FIFO '%s' failed: %s\n", e->name, strerror(errno)); return; } ErrorF("playback: opened FIFO '%s' as %d:%d\n", e->name, data->fifo_count, f->fd); data->fifo_count++; f->watch = qxl->core->watch_add(f->fd, SPICE_WATCH_EVENT_READ, read_from_fifos, qxl); } } static void playback_dir_changed(int fd, int event, void *opaque) { qxl_screen_t *qxl = opaque; static unsigned char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; static int offset = 0; struct inotify_event *e; int rc; do { rc = read(fd, buf + offset, sizeof(buf) - offset); if (rc > 0) { offset += rc; if (offset >= sizeof(*e)) { int len; e = (struct inotify_event *) buf; len = sizeof(*e) + e->len; if (offset >= len) { handle_one_change(qxl, e); if (offset > len) memmove(buf, buf + offset, offset - len); offset -= len; } } } } while (rc > 0); } #endif static const SpicePlaybackInterface playback_sif = { { SPICE_INTERFACE_PLAYBACK, "playback", SPICE_INTERFACE_PLAYBACK_MAJOR, SPICE_INTERFACE_PLAYBACK_MINOR } }; static void audio_initialize (qxl_screen_t *qxl) { int i; struct audio_data *data = qxl->playback_opaque; int freq = SPICE_INTERFACE_PLAYBACK_FREQ; int period_frames; int frame_bytes; #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 freq = spice_server_get_best_playback_rate(&qxl->playback_sin); #endif period_frames = freq * PERIOD_MS / 1000; frame_bytes = sizeof(int16_t) * SPICE_INTERFACE_PLAYBACK_CHAN; data->period_bytes = period_frames * frame_bytes; for (i = 0; i < MAX_FIFOS; ++i) { data->fifos[i].fd = -1; data->fifos[i].size = data->period_bytes * READ_BUFFER_PERIODS; data->fifos[i].buffer = calloc(1, data->fifos[i].size); } } int qxl_add_spice_playback_interface (qxl_screen_t *qxl) { int ret; struct audio_data *data = calloc(1, sizeof(*data)); #if defined(HAVE_SYS_INOTIFY_H) && defined(HAVE_INOTIFY_INIT1) if (qxl->playback_fifo_dir[0] == 0) { ErrorF("playback: no audio FIFO directory, audio is disabled\n"); free(data); return 0; } qxl->playback_sin.base.sif = &playback_sif.base; ret = spice_server_add_interface(qxl->spice_server, &qxl->playback_sin.base); if (ret < 0) { free(data); return errno; } #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 spice_server_set_playback_rate(&qxl->playback_sin, spice_server_get_best_playback_rate(&qxl->playback_sin)); #else /* disable CELT */ ret = spice_server_set_playback_compression(qxl->spice_server, 0); if (ret < 0) { free(data); return errno; } #endif qxl->playback_opaque = data; audio_initialize(qxl); data->wall_timer = qxl->core->timer_add(wall_ticker, qxl); data->dir_watch = inotify_init1(IN_NONBLOCK); data->fifo_dir_watch = -1; if (data->dir_watch >= 0) data->fifo_dir_watch = inotify_add_watch(data->dir_watch, qxl->playback_fifo_dir, IN_CREATE | IN_MOVE); if (data->fifo_dir_watch == -1) { ErrorF("Error %s(%d) watching the fifo dir\n", strerror(errno), errno); return errno; } data->fifo_dir_qxl_watch = qxl->core->watch_add(data->dir_watch, SPICE_WATCH_EVENT_READ, playback_dir_changed, qxl); #else ErrorF("inotify not available; audio disabled.\n"); #endif return 0; } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_audio.h000066400000000000000000000024321361660122400234430ustar00rootroot00000000000000/* * Copyright 2012 Andrew Eikum for CodeWeavers Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifndef QXL_SPICE_AUDIO_H #define QXL_SPICE_AUDIO_H #include "qxl.h" #include int qxl_add_spice_playback_interface(qxl_screen_t *qxl); #endif xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_display.c000066400000000000000000000301001361660122400237730ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "qxl.h" #include "spiceqxl_display.h" #ifndef container_of #define container_of(ptr, type, member) ({ \ const typeof(((type *) 0)->member) *__mptr = (ptr); \ (type *) ((char *) __mptr - offsetof(type, member));}) #endif /* TODO: these is copied from qemu/hw/qxl.c . It shouldn't be there * either, these ugly undef just remove the definitions from spice-protocol/spice/ipc_ring.h * What should happen is using one definition, or a rename, and both in spice-protocol (because * all the others are there). * Practically speaking the only difference between the two is extra checking in this version, * and usage (this one takes an extra parameter, the previous is meant to be used by assignment) */ #undef SPICE_RING_PROD_ITEM #define SPICE_RING_PROD_ITEM(r, ret) { \ typeof(r) start = r; \ typeof(r) end = r + 1; \ uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \ typeof(&(r)->items[prod]) m_item = &(r)->items[prod]; \ if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \ abort(); \ } \ ret = &m_item->el; \ } #undef SPICE_RING_CONS_ITEM #define SPICE_RING_CONS_ITEM(r, ret) { \ typeof(r) start = r; \ typeof(r) end = r + 1; \ uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \ typeof(&(r)->items[cons]) m_item = &(r)->items[cons]; \ if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \ abort(); \ } \ ret = &m_item->el; \ } /* XSpice: * We only need a single static identity slot. * We actually need no slots, but less changes if we use one. * We currently add it during attache_worker - should not be called more * then once during lifetime (but we don't check) */ static QXLDevMemSlot slot = { .slot_group_id = MEMSLOT_GROUP, .slot_id = 0, .generation = 0, .virt_start = 0, .virt_end = ~0, .addr_delta = 0, .qxl_ram_size = ~0, }; // TODO - reall dprint, this is just to get it compiling #define dprint(qxl, lvl, fmt, ...) printf(fmt, __VA_ARGS__) static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) { static int count = 0; qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); if (++count > 1) { dprint(qxl, 0, "%s ignored\n", __FUNCTION__); return; } dprint(qxl, 1, "%s:\n", __FUNCTION__); spice_qxl_add_memslot(sin, &slot); qxl->worker = qxl_worker; } static void interface_set_compression_level(QXLInstance *sin, int level) { qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); dprint(qxl, 1, "%s: %d\n", __FUNCTION__, level); qxl->shadow_rom.compression_level = level; qxl->rom->compression_level = level; } static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) { qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); qxl->shadow_rom.mm_clock = mm_time; qxl->rom->mm_clock = mm_time; } static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) { qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); dprint(qxl, 1, "%s:\n", __FUNCTION__); info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; info->memslot_id_bits = MEMSLOT_SLOT_BITS; info->num_memslots = NUM_MEMSLOTS; info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; info->internal_groupslot_id = 0; info->qxl_ram_size = qxl->shadow_rom.num_pages << TARGET_PAGE_BITS; info->n_surfaces = NUM_SURFACES; } void qxl_send_events(qxl_screen_t *qxl, int events) { #if 0 ErrorF("qxl_send_events %d\n", events); qxl_garbage_collect(qxl); #endif /* we should trigger a garbage collection, but via a pipe. TODO */ } /* called from spice server thread context only */ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) { qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); QXLRam *ram = get_ram_header(qxl); QXLCommandRing *ring; QXLCommand *cmd; int notify; dprint(qxl, 2, "%s: %s\n", __FUNCTION__, qxl->cmdflags ? "compat" : "native"); ring = &ram->cmd_ring; if (SPICE_RING_IS_EMPTY(ring)) { return FALSE; } SPICE_RING_CONS_ITEM(ring, cmd); ext->cmd = *cmd; ext->group_id = MEMSLOT_GROUP; ext->flags = qxl->cmdflags; SPICE_RING_POP(ring, notify); if (notify) { qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY); } qxl->guest_primary.commands++; // TODO: re-enable, useful //qxl_track_command(qxl, ext); //qxl_log_command(qxl, "cmd", ext); return TRUE; } /* called from spice server thread context only */ static int interface_req_cmd_notification(QXLInstance *sin) { qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); QXLRam *header = get_ram_header(qxl); int wait = 1; SPICE_RING_CONS_WAIT(&header->cmd_ring, wait); return wait; } /* called from spice server thread context only */ static inline void qxl_push_free_res(qxl_screen_t *qxl, int flush) { QXLRam *header = get_ram_header(qxl); QXLReleaseRing *ring = &header->release_ring; uint64_t *item; int notify; #define QXL_FREE_BUNCH_SIZE 32 if (ring->prod - ring->cons + 1 == ring->num_items) { /* ring full -- can't push */ return; } if (!flush && qxl->oom_running) { /* collect everything from oom handler before pushing */ return; } if (!flush && qxl->num_free_res < QXL_FREE_BUNCH_SIZE) { /* collect a bit more before pushing */ return; } SPICE_RING_PUSH(ring, notify); dprint(qxl, 2, "free: push %d items, notify %s, ring %d/%d [%d,%d]\n", qxl->num_free_res, notify ? "yes" : "no", ring->prod - ring->cons, ring->num_items, ring->prod, ring->cons); if (notify) { qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY); } SPICE_RING_PROD_ITEM(ring, item); *item = 0; qxl->num_free_res = 0; qxl->last_release = NULL; } /* called from spice server thread context only */ static void interface_release_resource(QXLInstance *sin, struct QXLReleaseInfoExt ext) { qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); QXLRam *ram = get_ram_header(qxl); QXLReleaseRing *ring; uint64_t *item, id; /* * ext->info points into guest-visible memory * pci bar 0, $command.release_info */ ring = &ram->release_ring; SPICE_RING_PROD_ITEM(ring, item); if (*item == 0) { /* stick head into the ring */ id = ext.info->id; ext.info->next = 0; *item = id; } else { /* append item to the list */ qxl->last_release->next = ext.info->id; ext.info->next = 0; } qxl->last_release = ext.info; qxl->num_free_res++; dprint(qxl, 3, "%4d\r", qxl->num_free_res); qxl_push_free_res(qxl, 0); } /* called from spice server thread context only */ static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext) { qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); QXLCursorRing *ring; QXLCommand *cmd; QXLRam *ram = get_ram_header(qxl); int notify; ring = &ram->cursor_ring; if (SPICE_RING_IS_EMPTY(ring)) { return FALSE; } SPICE_RING_CONS_ITEM(ring, cmd); ext->cmd = *cmd; ext->group_id = MEMSLOT_GROUP; ext->flags = qxl->cmdflags; SPICE_RING_POP(ring, notify); if (notify) { qxl_send_events(qxl, QXL_INTERRUPT_CURSOR); } qxl->guest_primary.commands++; //qxl_track_command(qxl, ext); // TODO - copy me //qxl_log_command(qxl, "csr", ext); // TODO - copy me return TRUE; } /* called from spice server thread context only */ static int interface_req_cursor_notification(QXLInstance *sin) { qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); QXLRam *ram = get_ram_header(qxl); int wait = 1; SPICE_RING_CONS_WAIT(&ram->cursor_ring, wait); return wait; } /* called from spice server thread context */ static void __attribute__ ((__noreturn__)) interface_notify_update(QXLInstance *sin, uint32_t update_id) { fprintf(stderr, "%s: abort()\n", __FUNCTION__); abort(); } /* called from spice server thread context only */ static int interface_flush_resources(QXLInstance *sin) { qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); int ret; dprint(qxl, 1, "free: guest flush (have %d)\n", qxl->num_free_res); ret = qxl->num_free_res; if (ret) { qxl_push_free_res(qxl, 1); } return ret; } static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) { } static const QXLInterface qxl_interface = { .base.type = SPICE_INTERFACE_QXL, .base.description = "qxl gpu", .base.major_version = SPICE_INTERFACE_QXL_MAJOR, .base.minor_version = SPICE_INTERFACE_QXL_MINOR, .attache_worker = interface_attach_worker, .set_compression_level = interface_set_compression_level, .set_mm_time = interface_set_mm_time, .get_init_info = interface_get_init_info, /* the callbacks below are called from spice server thread context */ .get_command = interface_get_command, .req_cmd_notification = interface_req_cmd_notification, .release_resource = interface_release_resource, .get_cursor_command = interface_get_cursor_command, .req_cursor_notification = interface_req_cursor_notification, .notify_update = interface_notify_update, .flush_resources = interface_flush_resources, .async_complete = interface_async_complete, }; void qxl_add_spice_display_interface(qxl_screen_t *qxl) { /* use this function to initialize the parts of qxl_screen_t * that were added directly from qemu/hw/qxl.c */ qxl->cmdflags = 0; qxl->oom_running = 0; qxl->num_free_res = 0; qxl->display_sin.base.sif = &qxl_interface.base; qxl->display_sin.id = 0; qxl->display_sin.st = (struct QXLState*)qxl; spice_server_add_interface(qxl->spice_server, &qxl->display_sin.base); } void spiceqxl_display_monitors_config(qxl_screen_t *qxl) { spice_qxl_monitors_config_async(&qxl->display_sin, physical_address(qxl, qxl->monitors_config, 0), MEMSLOT_GROUP, 0); } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_display.h000066400000000000000000000027121361660122400240100ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifndef QXL_SPICE_DISPLAY_H #define QXL_SPICE_DISPLAY_H #include "qxl.h" #include void qxl_add_spice_display_interface(qxl_screen_t *qxl); /* spice-server to device, now spice-server to xspice */ void qxl_send_events(qxl_screen_t *qxl, int events); void spiceqxl_display_monitors_config(qxl_screen_t *qxl); #endif // QXL_SPICE_DISPLAY_H xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_driver.c000066400000000000000000000122721361660122400236330ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ /** \file spiceqxl_driver.c * \author Alon Levy * * most of the code is still in qxl_driver.c, but for clarity parts are moved * here, and only used / compiled if XSPICE is defined */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "qxl.h" #include "spiceqxl_driver.h" #define SPICE_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define QXL_MODE_EX(x_res, y_res) \ QXL_MODE_16_32(x_res, y_res, 0), \ QXL_MODE_16_32(y_res, x_res, 1), \ QXL_MODE_16_32(x_res, y_res, 2), \ QXL_MODE_16_32(y_res, x_res, 3) #define QXL_MODE_16_32(x_res, y_res, orientation) \ QXL_MODE(x_res, y_res, 16, orientation), \ QXL_MODE(x_res, y_res, 32, orientation) #define QXL_MODE(_x, _y, _b, _o) \ { .x_res = _x, \ .y_res = _y, \ .bits = _b, \ .stride = (_x) * (_b) / 8, \ .x_mili = PIXEL_SIZE * (_x), \ .y_mili = PIXEL_SIZE * (_y), \ .orientation = _o, \ } #define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9" #define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) static QXLMode qxl_modes[] = { QXL_MODE_EX(640, 480), QXL_MODE_EX(800, 480), QXL_MODE_EX(800, 600), QXL_MODE_EX(832, 624), QXL_MODE_EX(960, 640), QXL_MODE_EX(1024, 600), QXL_MODE_EX(1024, 768), QXL_MODE_EX(1152, 864), QXL_MODE_EX(1152, 870), QXL_MODE_EX(1280, 720), QXL_MODE_EX(1280, 760), QXL_MODE_EX(1280, 768), QXL_MODE_EX(1280, 800), QXL_MODE_EX(1280, 960), QXL_MODE_EX(1280, 1024), QXL_MODE_EX(1360, 768), QXL_MODE_EX(1366, 768), QXL_MODE_EX(1400, 1050), QXL_MODE_EX(1440, 900), QXL_MODE_EX(1600, 900), QXL_MODE_EX(1600, 1200), QXL_MODE_EX(1680, 1050), QXL_MODE_EX(1920, 1080), QXL_MODE_EX(1920, 1200), QXL_MODE_EX(1920, 1440), QXL_MODE_EX(2048, 1536), QXL_MODE_EX(2560, 1440), QXL_MODE_EX(2560, 1600), QXL_MODE_EX(3840, 1080), QXL_MODE_EX(2560, 2048), QXL_MODE_EX(2800, 2100), QXL_MODE_EX(3200, 2400), QXL_MODE_EX(5760, 1080), QXL_MODE_EX(7680, 1080), }; // TODO - reuse code from qxl.c? void init_qxl_rom(qxl_screen_t* qxl, uint32_t rom_size) { QXLRom *rom = qxl->rom; struct QXLModes *modes = (struct QXLModes *)(rom + 1); uint32_t ram_header_size; uint32_t num_pages; uint32_t fb; int i, m; memset(rom, 0, rom_size); rom->magic = QXL_ROM_MAGIC; rom->id = 0; // TODO - multihead? rom->log_level = 3; rom->modes_offset = (sizeof(QXLRom)); rom->slot_gen_bits = MEMSLOT_GENERATION_BITS; rom->slot_id_bits = MEMSLOT_SLOT_BITS; rom->slots_start = 0; rom->slots_end = 1; rom->n_surfaces = (NUM_SURFACES); for (i = 0, m = 0; i < (SPICE_ARRAY_SIZE(qxl_modes)); i++) { fb = qxl_modes[i].y_res * qxl_modes[i].stride; if (fb > qxl->surface0_size) continue; modes->modes[m].id = m; modes->modes[m].x_res = qxl_modes[i].x_res; modes->modes[m].y_res = qxl_modes[i].y_res; modes->modes[m].bits = qxl_modes[i].bits; modes->modes[m].stride = qxl_modes[i].stride; modes->modes[m].x_mili = qxl_modes[i].x_mili; modes->modes[m].y_mili = qxl_modes[i].y_mili; modes->modes[m].orientation = qxl_modes[i].orientation; m++; } modes->n_modes = m; ram_header_size = ALIGN(sizeof(struct QXLRam), 4096); num_pages = qxl->vram_size; num_pages -= ram_header_size; num_pages -= qxl->surface0_size; num_pages = num_pages / TARGET_PAGE_SIZE; rom->draw_area_offset = 0; rom->surface0_area_size = qxl->surface0_size; rom->pages_offset = rom->surface0_area_size; rom->num_pages = num_pages; rom->ram_header_offset = qxl->vram_size - ram_header_size; qxl->shadow_rom = *qxl->rom; // TODO - do we need this? } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_driver.h000066400000000000000000000026461361660122400236440ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifndef SPICEQXL_DRIVER_H #define SPICEQXL_DRIVER_H 1 #define DEFAULT_FRAME_BUFFER_SIZE 16 #define DEFAULT_SURFACE_BUFFER_SIZE 128 #define DEFAULT_COMMAND_BUFFER_SIZE 128 #define ROM_SIZE (1<<20) // TODO - put correct size void init_qxl_rom(qxl_screen_t* qxl, uint32_t rom_size); #endif /* SPICEQXL_DRIVER_H */ xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_inputs.c000066400000000000000000000317111361660122400236610ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ /* Handle inputs channel for spice, and register the X parts, * a mouse and a keyboard device pair. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "qxl.h" #include "spiceqxl_inputs.h" static int XSpicePointerPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags); static int XSpiceKeyboardPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags); static void XSpicePointerUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags); static void XSpiceKeyboardUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags); static char xspice_pointer_name[] = "xspice pointer"; static InputDriverRec XSPICE_POINTER = { 1, xspice_pointer_name, NULL, XSpicePointerPreInit, XSpicePointerUnInit, NULL, NULL /* defaults */ }; static char xspice_keyboard_name[] = "xspice keyboard"; static InputDriverRec XSPICE_KEYBOARD = { 1, xspice_keyboard_name, NULL, XSpiceKeyboardPreInit, XSpiceKeyboardUnInit, NULL, NULL }; #define BUTTONS 5 typedef struct XSpiceKbd { SpiceKbdInstance sin; uint8_t ledstate; InputInfoPtr pInfo; /* xf86 device handle to post events */ /* Note: spice sends some of the keys escaped by this. * This is supposed to be AT key codes, but I can't figure out where that * thing is defined after looking at xf86-input-keyboard. Ended up reverse * engineering a escaped table using xev. */ int escape; } XSpiceKbd; static int xspice_pointer_proc(DeviceIntPtr pDevice, int onoff) { DevicePtr pDev = (DevicePtr)pDevice; BYTE map[BUTTONS + 1]; Atom btn_labels[BUTTONS]; Atom axes_labels[2]; int i; switch (onoff) { case DEVICE_INIT: for (i = 0; i < BUTTONS + 1; i++) { map[i] = i; } btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT); btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE); btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT); btn_labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP); btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN); axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X); axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y); InitPointerDeviceStruct(pDev, map, BUTTONS,btn_labels,(PtrCtrlProcPtr)NoopDDA, GetMotionHistorySize(), 2, axes_labels); break; case DEVICE_ON: pDev->on = TRUE; break; case DEVICE_OFF: pDev->on = FALSE; break; } return Success; } static void xspice_keyboard_bell(int percent, DeviceIntPtr device, pointer ctrl, int class_) { } #define CAPSFLAG 1 #define NUMFLAG 2 #define SCROLLFLAG 4 /* MODEFLAG and COMPOSEFLAG currently unused (reminder for future) */ #define MODEFLAG 8 #define COMPOSEFLAG 16 #define ArrayLength(a) (sizeof(a) / (sizeof((a)[0]))) static void xspice_keyboard_control(DeviceIntPtr device, KeybdCtrl *ctrl) { static struct { int xbit, code; } bits[] = { { CAPSFLAG, SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK }, { NUMFLAG, SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK }, { SCROLLFLAG, SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK }, /* TODO: there is no MODEFLAG nor COMPOSEFLAG in SPICE. */ }; XSpiceKbd *kbd; InputInfoPtr pInfo; int i; pInfo = device->public.devicePrivate; kbd = pInfo->private; kbd->ledstate = 0; for (i = 0; i < ArrayLength(bits); i++) { if (ctrl->leds & bits[i].xbit) { kbd->ledstate |= bits[i].code; } else { kbd->ledstate &= ~bits[i].code; } } } static char xspice_keyboard_rules[] = "evdev"; static char xspice_keyboard_model[] = "pc105"; static char xspice_keyboard_layout[] = "us"; static char xspice_keyboard_variant[] = ""; static char xspice_keyboard_options[] = ""; static int xspice_keyboard_proc(DeviceIntPtr pDevice, int onoff) { DevicePtr pDev = (DevicePtr)pDevice; XkbRMLVOSet rmlvo = { .rules = xspice_keyboard_rules, .model = xspice_keyboard_model, .layout = xspice_keyboard_layout, .variant = xspice_keyboard_variant, .options = xspice_keyboard_options, }; switch (onoff) { case DEVICE_INIT: InitKeyboardDeviceStruct( pDevice, &rmlvo, xspice_keyboard_bell, xspice_keyboard_control ); break; case DEVICE_ON: pDev->on = TRUE; break; case DEVICE_OFF: pDev->on = FALSE; break; } return Success; } /* from spice-input.c */ /* keyboard bits */ static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag); static uint8_t kbd_get_leds(SpiceKbdInstance *sin); static const SpiceKbdInterface kbd_interface = { .base.type = SPICE_INTERFACE_KEYBOARD, .base.description = "xspice keyboard", .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR, .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR, .push_scan_freg = kbd_push_key, .get_leds = kbd_get_leds, }; /* spice sends AT scancodes (with a strange escape). * But xf86PostKeyboardEvent expects scancodes. Apparently most of the time * you just need to add MIN_KEYCODE, see xf86-input-keyboard/src/atKeynames * and xf86-input-keyboard/src/kbd.c:PostKbdEvent: * xf86PostKeyboardEvent(device, scanCode + MIN_KEYCODE, down); */ #define MIN_KEYCODE 8 static uint8_t escaped_map[256] = { [0x1c] = 104, //KEY_KP_Enter, [0x1d] = 105, //KEY_RCtrl, [0x2a] = 0,//KEY_LMeta, // REDKEY_FAKE_L_SHIFT [0x35] = 106,//KEY_KP_Divide, [0x36] = 0,//KEY_RMeta, // REDKEY_FAKE_R_SHIFT [0x37] = 107,//KEY_Print, [0x38] = 108,//KEY_AltLang, [0x46] = 127,//KEY_Break, [0x47] = 110,//KEY_Home, [0x48] = 111,//KEY_Up, [0x49] = 112,//KEY_PgUp, [0x4b] = 113,//KEY_Left, [0x4d] = 114,//KEY_Right, [0x4f] = 115,//KEY_End, [0x50] = 116,//KEY_Down, [0x51] = 117,//KEY_PgDown, [0x52] = 118,//KEY_Insert, [0x53] = 119,//KEY_Delete, [0x5b] = 133,//0, // REDKEY_LEFT_CMD, [0x5c] = 134,//0, // REDKEY_RIGHT_CMD, [0x5d] = 135,//KEY_Menu, }; static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag) { XSpiceKbd *kbd = container_of(sin, XSpiceKbd, sin); int is_down; if (frag == 224) { kbd->escape = frag; return; } is_down = frag & 0x80 ? FALSE : TRUE; frag = frag & 0x7f; if (kbd->escape == 224) { kbd->escape = 0; if (escaped_map[frag] == 0) { fprintf(stderr, "spiceqxl_inputs.c: kbd_push_key: escaped_map[%d] == 0\n", frag); } frag = escaped_map[frag]; } else { frag += MIN_KEYCODE; } xf86PostKeyboardEvent(kbd->pInfo->dev, frag, is_down); } static uint8_t kbd_get_leds(SpiceKbdInstance *sin) { XSpiceKbd *kbd = container_of(sin, XSpiceKbd, sin); return kbd->ledstate; } /* mouse bits */ typedef struct XSpicePointer { SpiceMouseInstance mouse; SpiceTabletInstance tablet; int width, height, x, y; Bool absolute; InputInfoPtr pInfo; /* xf86 device handle to post events */ } XSpicePointer; static XSpicePointer *g_xspice_pointer; static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz, uint32_t buttons_state) { // TODO } static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state) { // TODO } static const SpiceMouseInterface mouse_interface = { .base.type = SPICE_INTERFACE_MOUSE, .base.description = "xspice mouse", .base.major_version = SPICE_INTERFACE_MOUSE_MAJOR, .base.minor_version = SPICE_INTERFACE_MOUSE_MINOR, .motion = mouse_motion, .buttons = mouse_buttons, }; static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height) { XSpicePointer *spice_pointer = container_of(sin, XSpicePointer, tablet); if (height < 16) { height = 16; } if (width < 16) { width = 16; } spice_pointer->width = width; spice_pointer->height = height; } void spiceqxl_tablet_position(int x, int y, uint32_t buttons_state) { // TODO: don't ignore buttons_state xf86PostMotionEvent(g_xspice_pointer->pInfo->dev, 1, 0, 2, x, y); } static void tablet_position(SpiceTabletInstance* sin, int x, int y, uint32_t buttons_state) { spiceqxl_tablet_position(x, y, buttons_state); } void spiceqxl_tablet_buttons(uint32_t buttons_state) { static uint32_t old_buttons_state = 0; int i; for (i = 0; i < BUTTONS; i++) { if ((buttons_state ^ old_buttons_state) & (1 << i)) { int action = (buttons_state & (1 << i)); xf86PostButtonEvent(g_xspice_pointer->pInfo->dev, 0, i + 1, action, 0, 0); } } old_buttons_state = buttons_state; } static void tablet_buttons(SpiceTabletInstance *sin, uint32_t buttons_state) { // For some reason spice switches the second and third button, undo that. // basically undo RED_MOUSE_STATE_TO_LOCAL buttons_state = (buttons_state & SPICE_MOUSE_BUTTON_MASK_LEFT) | ((buttons_state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) << 1) | ((buttons_state & SPICE_MOUSE_BUTTON_MASK_RIGHT) >> 1) | (buttons_state & ~(SPICE_MOUSE_BUTTON_MASK_LEFT | SPICE_MOUSE_BUTTON_MASK_MIDDLE |SPICE_MOUSE_BUTTON_MASK_RIGHT)); spiceqxl_tablet_buttons(buttons_state); } static void tablet_wheel(SpiceTabletInstance* sin, int wheel, uint32_t buttons_state) { // convert wheel into fourth and fifth buttons tablet_buttons(sin, buttons_state | (wheel > 0 ? (1<<4) : 0) | (wheel < 0 ? (1<<3) : 0)); } static const SpiceTabletInterface tablet_interface = { .base.type = SPICE_INTERFACE_TABLET, .base.description = "xspice tablet", .base.major_version = SPICE_INTERFACE_TABLET_MAJOR, .base.minor_version = SPICE_INTERFACE_TABLET_MINOR, .set_logical_size = tablet_set_logical_size, .position = tablet_position, .wheel = tablet_wheel, .buttons = tablet_buttons, }; static char unknown_type_string[] = "UNKNOWN"; static int XSpiceKeyboardPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags) { XSpiceKbd *kbd; kbd = calloc(sizeof(*kbd), 1); kbd->sin.base.sif = &kbd_interface.base; kbd->pInfo = pInfo; pInfo->private = kbd; pInfo->type_name = unknown_type_string; pInfo->device_control = xspice_keyboard_proc; pInfo->read_input = NULL; pInfo->switch_mode = NULL; spice_server_add_interface(xspice_get_spice_server(), &kbd->sin.base); return Success; } static int XSpicePointerPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags) { XSpicePointer *spice_pointer; g_xspice_pointer = spice_pointer = calloc(sizeof(*spice_pointer), 1); spice_pointer->mouse.base.sif = &mouse_interface.base; spice_pointer->tablet.base.sif = &tablet_interface.base; spice_pointer->absolute = TRUE; spice_pointer->pInfo = pInfo; pInfo->private = NULL; pInfo->type_name = unknown_type_string; pInfo->device_control = xspice_pointer_proc; pInfo->read_input = NULL; pInfo->switch_mode = NULL; spice_server_add_interface(xspice_get_spice_server(), &spice_pointer->tablet.base); return Success; } static void XSpicePointerUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags) { } static void XSpiceKeyboardUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags) { } void xspice_add_input_drivers(pointer module) { xf86AddInputDriver(&XSPICE_POINTER, module, 0); xf86AddInputDriver(&XSPICE_KEYBOARD, module, 0); } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_inputs.h000066400000000000000000000025741361660122400236730ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifndef QXL_SPICE_INPUTS_H #define QXL_SPICE_INPUTS_H #include "qxl.h" void xspice_add_input_drivers(pointer module); void spiceqxl_tablet_buttons(uint32_t buttons_state); void spiceqxl_tablet_position(int x, int y, uint32_t buttons_state); #endif // QXL_SPICE_INPUTS_H xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_io_port.c000066400000000000000000000211131361660122400240050ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "qxl.h" #include "spiceqxl_io_port.h" /* TODO: taken from qemu qxl.c, try to remove duplication */ #undef SPICE_RING_PROD_ITEM #define SPICE_RING_PROD_ITEM(r, ret) { \ typeof(r) start = r; \ typeof(r) end = r + 1; \ uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \ typeof(&(r)->items[prod]) m_item = &(r)->items[prod]; \ if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \ abort(); \ } \ ret = &m_item->el; \ } static int spiceqxl_io_port_debug_level = -1; static void __attribute__ ((format (printf, 2, 3))) dprint(int _level, const char *_fmt, ...) { if (spiceqxl_io_port_debug_level == -1) { if (getenv("XSPICE_IO_PORT_DEBUG_LEVEL")) { spiceqxl_io_port_debug_level = atoi( getenv("XSPICE_IO_PORT_DEBUG_LEVEL")); } else { spiceqxl_io_port_debug_level = 0; } } if (spiceqxl_io_port_debug_level >= _level) { va_list ap; va_start(ap, _fmt); vfprintf(stderr, _fmt, ap); va_end(ap); } } void xspice_init_qxl_ram(qxl_screen_t *qxl) { QXLRam *ram = get_ram_header(qxl); uint64_t *item; ram->magic = QXL_RAM_MAGIC; ram->int_pending = 0; ram->int_mask = 0; SPICE_RING_INIT(&ram->cmd_ring); SPICE_RING_INIT(&ram->cursor_ring); SPICE_RING_INIT(&ram->release_ring); SPICE_RING_PROD_ITEM(&ram->release_ring, item); *item = 0; } static void qxl_reset_state(qxl_screen_t *qxl) { QXLRam *ram = get_ram_header(qxl); assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring)); assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring)); qxl->shadow_rom.update_id = 0; *qxl->rom = qxl->shadow_rom; xspice_init_qxl_ram(qxl); qxl->num_free_res = 0; qxl->last_release = NULL; // TODO - dirty ? //memset(&qxl->ssd.dirty, 0, sizeof(qxl->ssd.dirty)); } static void qxl_check_state(qxl_screen_t *qxl) { QXLRam *ram = get_ram_header(qxl); assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring)); assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring)); } static void qxl_soft_reset(qxl_screen_t *qxl) { dprint(1, "%s:\n", __FUNCTION__); qxl_check_state(qxl); } static void qxl_reset_surfaces(qxl_screen_t *qxl) { dprint(1, "%s:\n", __FUNCTION__); spice_qxl_destroy_surfaces(&qxl->display_sin); // TODO - do we have guest_surfaces? //memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds)); } static void qxl_hard_reset(qxl_screen_t *qxl) { dprint(1, "%s: start\n", __FUNCTION__); spice_qxl_reset_cursor(&qxl->display_sin); spice_qxl_reset_image_cache(&qxl->display_sin); qxl_reset_surfaces(qxl); qxl_reset_state(qxl); qxl_soft_reset(qxl); dprint(1, "%s: done\n", __FUNCTION__); } static void qxl_create_guest_primary(qxl_screen_t *qxl) { QXLDevSurfaceCreate surface; QXLSurfaceCreate *sc = &qxl->guest_primary.surface; dprint(1, "%s: %dx%d\n", __FUNCTION__, sc->width, sc->height); surface.format = sc->format; surface.height = sc->height; surface.mem = sc->mem; surface.position = sc->position; surface.stride = sc->stride; surface.width = sc->width; surface.type = sc->type; surface.flags = sc->flags; surface.mouse_mode = TRUE; surface.group_id = 0; qxl->cmdflags = 0; spice_qxl_create_primary_surface(&qxl->display_sin, 0, &surface); } static void qxl_destroy_primary(qxl_screen_t *qxl) { dprint(1, "%s\n", __FUNCTION__); spice_qxl_destroy_primary_surface(&qxl->display_sin, 0); } static void qxl_set_mode(qxl_screen_t *qxl, int modenr) { struct QXLMode *mode = qxl->modes + modenr; uint64_t devmem = pointer_to_u64(qxl->ram); QXLSurfaceCreate surface = { .width = mode->x_res, .height = mode->y_res, .stride = -mode->x_res * 4, .format = SPICE_SURFACE_FMT_32_xRGB, .flags = 0, .mouse_mode = TRUE, .mem = devmem + qxl->shadow_rom.draw_area_offset, }; dprint(1, "%s: mode %d [ %d x %d @ %d bpp devmem 0x%llx ]\n", __FUNCTION__, modenr, mode->x_res, mode->y_res, mode->bits, (unsigned long long) devmem); qxl_hard_reset(qxl); qxl->guest_primary.surface = surface; qxl_create_guest_primary(qxl); qxl->cmdflags = QXL_COMMAND_FLAG_COMPAT; #ifdef QXL_COMMAND_FLAG_COMPAT_16BPP /* new in spice 0.6.1 */ if (mode->bits == 16) { qxl->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP; } #endif qxl->shadow_rom.mode = modenr; qxl->rom->mode = modenr; } /* called from Xorg thread - not worker thread! */ void ioport_write(qxl_screen_t *qxl, uint32_t io_port, uint32_t val) { QXLRam *header = get_ram_header(qxl); if (!qxl->worker_running) { return; } switch (io_port) { case QXL_IO_UPDATE_AREA: { QXLRect update = *(QXLRect*)&header->update_area; spice_qxl_update_area(&qxl->display_sin, header->update_surface, &update, NULL, 0, 0); break; } case QXL_IO_NOTIFY_CMD: spice_qxl_wakeup(&qxl->display_sin); break; case QXL_IO_NOTIFY_CURSOR: spice_qxl_wakeup(&qxl->display_sin); break; case QXL_IO_UPDATE_IRQ: /* qxl_set_irq(d); */ printf("QXL_IO_UPDATE_IRQ not implemented\n"); break; case QXL_IO_NOTIFY_OOM: if (!SPICE_RING_IS_EMPTY(&header->release_ring)) { break; } sched_yield(); if (!SPICE_RING_IS_EMPTY(&header->release_ring)) { break; } spice_qxl_oom(&qxl->display_sin); break; case QXL_IO_SET_MODE: dprint(1, "QXL_SET_MODE %d\n", val); qxl_set_mode(qxl, val); break; case QXL_IO_LOG: fprintf(stderr, "qxl/guest: %s", header->log_buf); break; case QXL_IO_RESET: dprint(1, "QXL_IO_RESET\n"); qxl_hard_reset(qxl); break; case QXL_IO_MEMSLOT_ADD: dprint(1, "QXL_IO_MEMSLOT_ADD - should not be called (this is Xspice)\n"); break; case QXL_IO_MEMSLOT_DEL: dprint(1, "QXL_IO_MEMSLOT_DEL - should not be called (this is Xspice)\n"); break; case QXL_IO_CREATE_PRIMARY: assert(val == 0); dprint(1, "QXL_IO_CREATE_PRIMARY\n"); qxl->guest_primary.surface = *(QXLSurfaceCreate*)&header->create_surface; qxl_create_guest_primary(qxl); break; case QXL_IO_DESTROY_PRIMARY: assert(val == 0); dprint(1, "QXL_IO_DESTROY_PRIMARY\n"); qxl_destroy_primary(qxl); break; case QXL_IO_DESTROY_SURFACE_WAIT: spice_qxl_destroy_surface_wait(&qxl->display_sin, val); break; case QXL_IO_DESTROY_ALL_SURFACES: spice_qxl_destroy_surfaces(&qxl->display_sin); break; case QXL_IO_FLUSH_SURFACES_ASYNC: fprintf(stderr, "ERROR: async callback Unimplemented\n"); spice_qxl_flush_surfaces_async(&qxl->display_sin, 0); break; default: fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port); abort(); } } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_io_port.h000066400000000000000000000025261361660122400240210ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifndef SPICEQXL_IO_PORT_H #define SPICEQXL_IO_PORT_H #include "qxl.h" /* used to initialize the rings before the first reset, avoid a valgrind * warning */ void xspice_init_qxl_ram(qxl_screen_t *qxl); #endif // SPICEQXL_IO_PORT_H xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_main_loop.c000066400000000000000000000270271361660122400243210ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "spiceqxl_main_loop.h" static int spiceqxl_main_loop_debug = 0; #define DPRINTF(x, format, ...) { \ if (x <= spiceqxl_main_loop_debug) { \ printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \ } \ } /* From ring.h */ typedef struct Ring RingItem; typedef struct Ring { RingItem *prev; RingItem *next; } Ring; static inline void ring_init(Ring *ring) { ring->next = ring->prev = ring; } static inline void ring_item_init(RingItem *item) { item->next = item->prev = NULL; } static inline int ring_item_is_linked(RingItem *item) { return !!item->next; } static inline int ring_is_empty(Ring *ring) { assert(ring->next != NULL && ring->prev != NULL); return ring == ring->next; } static inline void ring_add(Ring *ring, RingItem *item) { assert(ring->next != NULL && ring->prev != NULL); assert(item->next == NULL && item->prev == NULL); item->next = ring->next; item->prev = ring; ring->next = item->next->prev = item; } static inline void __ring_remove(RingItem *item) { item->next->prev = item->prev; item->prev->next = item->next; item->prev = item->next = 0; } static inline void ring_remove(RingItem *item) { assert(item->next != NULL && item->prev != NULL); assert(item->next != item); __ring_remove(item); } static inline RingItem *ring_get_head(Ring *ring) { RingItem *ret; assert(ring->next != NULL && ring->prev != NULL); if (ring_is_empty(ring)) { return NULL; } ret = ring->next; return ret; } static inline RingItem *ring_get_tail(Ring *ring) { RingItem *ret; assert(ring->next != NULL && ring->prev != NULL); if (ring_is_empty(ring)) { return NULL; } ret = ring->prev; return ret; } static inline RingItem *ring_next(Ring *ring, RingItem *pos) { RingItem *ret; assert(ring->next != NULL && ring->prev != NULL); assert(pos); assert(pos->next != NULL && pos->prev != NULL); ret = pos->next; return (ret == ring) ? NULL : ret; } static inline RingItem *ring_prev(Ring *ring, RingItem *pos) { RingItem *ret; assert(ring->next != NULL && ring->prev != NULL); assert(pos); assert(pos->next != NULL && pos->prev != NULL); ret = pos->prev; return (ret == ring) ? NULL : ret; } #define RING_FOREACH_SAFE(var, next, ring) \ for ((var) = ring_get_head(ring), \ (next) = (var) ? ring_next(ring, (var)) : NULL; \ (var); \ (var) = (next), \ (next) = (var) ? ring_next(ring, (var)) : NULL) /**/ #define NOT_IMPLEMENTED printf("%s not implemented\n", __func__); static SpiceCoreInterface core; typedef struct SpiceTimer { OsTimerPtr xorg_timer; SpiceTimerFunc func; void *opaque; // also stored in xorg_timer, but needed for timer_start } Timer; static CARD32 xorg_timer_callback( OsTimerPtr xorg_timer, CARD32 time, pointer arg) { SpiceTimer *timer = (SpiceTimer*)arg; timer->func(timer->opaque); return 0; // if non zero xorg does a TimerSet, we don't want that. } static SpiceTimer* timer_add(SpiceTimerFunc func, void *opaque) { SpiceTimer *timer = calloc(sizeof(SpiceTimer), 1); timer->func = func; timer->opaque = opaque; return timer; } static void timer_start(SpiceTimer *timer, uint32_t ms) { timer->xorg_timer = TimerSet(timer->xorg_timer, 0 /* flags */, ms, xorg_timer_callback, timer); } static void timer_cancel(SpiceTimer *timer) { TimerCancel(timer->xorg_timer); } static void timer_remove(SpiceTimer *timer) { TimerFree(timer->xorg_timer); free(timer); } #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 23 struct SpiceWatch { RingItem link; int fd; int event_mask; SpiceWatchFunc func; void *opaque; int remove; }; Ring watches; int watch_count = 0; static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque) { SpiceWatch *watch = xnfalloc(sizeof(SpiceWatch)); DPRINTF(0, "adding %p, fd=%d at %d", watch, fd, watch_count); watch->fd = fd; watch->event_mask = event_mask; watch->func = func; watch->opaque = opaque; watch->remove = FALSE; ring_item_init(&watch->link); ring_add(&watches, &watch->link); watch_count++; return watch; } static void watch_update_mask(SpiceWatch *watch, int event_mask) { DPRINTF(0, "fd %d to %d", watch->fd, event_mask); watch->event_mask = event_mask; } static void watch_remove(SpiceWatch *watch) { DPRINTF(0, "remove %p (fd %d)", watch, watch->fd); watch->remove = TRUE; watch_count--; } static int set_watch_fds(fd_set *rfds, fd_set *wfds) { SpiceWatch *watch; RingItem *link; RingItem *next; int max_fd = -1; RING_FOREACH_SAFE(link, next, &watches) { watch = (SpiceWatch*)link; if (watch->event_mask & SPICE_WATCH_EVENT_READ) { FD_SET(watch->fd, rfds); max_fd = watch->fd > max_fd ? watch->fd : max_fd; } if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) { FD_SET(watch->fd, wfds); max_fd = watch->fd > max_fd ? watch->fd : max_fd; } } return max_fd; } /* * called just before the X server goes into select() * readmask is just an fdset on linux, but something totally different on windows (etc). * DIX has a comment about it using a special type to hide this (so we break that here) */ static void xspice_block_handler(pointer data, OSTimePtr timeout, pointer readmask) { /* set all our fd's */ set_watch_fds((fd_set*)readmask, (fd_set*)readmask); } /* * xserver only calls wakeup_handler with the read fd_set, so we * must either patch it or do a polling select ourselves, this is the * latter approach. Since we are already doing a polling select, we * already select on all (we could avoid selecting on the read since * that *is* actually taken care of by the wakeup handler). */ static void select_and_check_watches(void) { fd_set rfds, wfds; int max_fd = -1; SpiceWatch *watch; RingItem *link; RingItem *next; struct timeval timeout; int retval; FD_ZERO(&rfds); FD_ZERO(&wfds); max_fd = set_watch_fds(&rfds, &wfds); watch = (SpiceWatch*)watches.next; timeout.tv_sec = timeout.tv_usec = 0; retval = select(max_fd + 1, &rfds, &wfds, NULL, &timeout); if (retval > 0) { RING_FOREACH_SAFE(link, next, &watches) { watch = (SpiceWatch*)link; if (!watch->remove && (watch->event_mask & SPICE_WATCH_EVENT_READ) && FD_ISSET(watch->fd, &rfds)) { watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque); } if (!watch->remove && (watch->event_mask & SPICE_WATCH_EVENT_WRITE) && FD_ISSET(watch->fd, &wfds)) { watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque); } if (watch->remove) { ring_remove(&watch->link); free(watch); } } } } static int no_write_watches(Ring *w) { SpiceWatch *watch; RingItem *link; RingItem *next; RING_FOREACH_SAFE(link, next, w) { watch = (SpiceWatch*)link; if (!watch->remove && (watch->event_mask & SPICE_WATCH_EVENT_WRITE)) return 0; } return 1; } static void xspice_wakeup_handler(pointer data, int nfds, pointer readmask) { if (!nfds && no_write_watches(&watches)) { return; } select_and_check_watches(); } #else /* GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 23 */ struct SpiceWatch { int fd; int event_mask; SpiceWatchFunc func; void *opaque; }; static void watch_fd_notified(int fd, int xevents, void *data) { SpiceWatch *watch = (SpiceWatch *)data; if ((watch->event_mask & SPICE_WATCH_EVENT_READ) && (xevents & X_NOTIFY_READ)) { watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque); } if ((watch->event_mask & SPICE_WATCH_EVENT_WRITE) && (xevents & X_NOTIFY_WRITE)) { watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque); } } static int watch_update_mask_internal(SpiceWatch *watch, int event_mask) { int x_event_mask = 0; SetNotifyFd(watch->fd, NULL, X_NOTIFY_NONE, NULL); watch->event_mask = 0; if (event_mask & SPICE_WATCH_EVENT_READ) { x_event_mask |= X_NOTIFY_READ; } if (event_mask & SPICE_WATCH_EVENT_WRITE) { x_event_mask |= X_NOTIFY_WRITE; } if (x_event_mask == 0) { DPRINTF(0, "Unexpected watch event_mask: %i", event_mask); return -1; } SetNotifyFd(watch->fd, watch_fd_notified, x_event_mask, watch); watch->event_mask = event_mask; return 0; } static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque) { SpiceWatch *watch = xnfalloc(sizeof(SpiceWatch)); DPRINTF(0, "adding %p, fd=%d", watch, fd); watch->fd = fd; watch->func = func; watch->opaque = opaque; if (watch_update_mask_internal(watch, event_mask) != 0) { free(watch); return NULL; } return watch; } static void watch_update_mask(SpiceWatch *watch, int event_mask) { DPRINTF(0, "fd %d to %d", watch->fd, event_mask); watch_update_mask_internal(watch, event_mask); } static void watch_remove(SpiceWatch *watch) { DPRINTF(0, "remove %p (fd %d)", watch, watch->fd); RemoveNotifyFd(watch->fd); free(watch); } #endif static void channel_event(int event, SpiceChannelEventInfo *info) { NOT_IMPLEMENTED } SpiceCoreInterface *basic_event_loop_init(void) { #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 23 ring_init(&watches); #endif bzero(&core, sizeof(core)); core.base.major_version = SPICE_INTERFACE_CORE_MAJOR; core.base.minor_version = SPICE_INTERFACE_CORE_MINOR; // anything less than 3 and channel_event isn't called core.timer_add = timer_add; core.timer_start = timer_start; core.timer_cancel = timer_cancel; core.timer_remove = timer_remove; core.watch_add = watch_add; core.watch_update_mask = watch_update_mask; core.watch_remove = watch_remove; core.channel_event = channel_event; return &core; } void xspice_register_handlers(void) { #if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 23 RegisterBlockAndWakeupHandlers(xspice_block_handler, xspice_wakeup_handler, 0); #endif } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_main_loop.h000066400000000000000000000025301361660122400243160ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifndef QXL_MAIN_LOOP_H #define QXL_MAIN_LOOP_H #include "qxl.h" #include SpiceCoreInterface *basic_event_loop_init(void); void basic_event_loop_mainloop(void); void xspice_register_handlers(void); #endif // QXL_MAIN_LOOP_H xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_smartcard.c000066400000000000000000000133411361660122400243160ustar00rootroot00000000000000/* * Copyright 2014 Jeremy White for CodeWeavers Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "spiceqxl_smartcard.h" typedef struct XSpiceSmartcardCharDeviceInstance { SpiceCharDeviceInstance base; qxl_screen_t *qxl; int listen_fd; int fd; SpiceWatch *listen_watch; SpiceWatch *watch; } XSpiceSmartcardCharDeviceInstance; static XSpiceSmartcardCharDeviceInstance smartcard_sin = { .base = { .subtype = "smartcard" } }; static int smartcard_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) { int written; if (smartcard_sin.fd == -1) return 0; written = write(smartcard_sin.fd, buf, len); if (written != len) ErrorF("%s: ERROR: short write to smartcard socket - TODO buffering\n", __FUNCTION__); return written; } static int smartcard_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) { int rc; if (smartcard_sin.fd == -1) return 0; rc = read(smartcard_sin.fd, buf, len); if (rc <= 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { return 0; } ErrorF("smartcard socket died: %s\n", strerror(errno)); smartcard_sin.qxl->core->watch_remove(smartcard_sin.watch); close(smartcard_sin.fd); smartcard_sin.fd = -1; smartcard_sin.watch = NULL; } return rc; } static void on_read_available(int fd, int event, void *opaque) { spice_server_char_device_wakeup(&smartcard_sin.base); } static void on_accept_available(int fd, int event, void *opaque) { qxl_screen_t *qxl = (qxl_screen_t *) opaque; int flags; int client_fd; client_fd = accept(fd, NULL, NULL); if (client_fd < 0) return; if (smartcard_sin.fd != -1) { ErrorF("smartcard error: a new connection came in while an old one was active.\n"); close(client_fd); return; } flags = fcntl(client_fd, F_GETFL, 0); if (flags < 0) flags = 0; flags |= O_NONBLOCK; fcntl(client_fd, F_SETFL, flags); smartcard_sin.fd = client_fd; smartcard_sin.watch = qxl->core->watch_add(smartcard_sin.fd, SPICE_WATCH_EVENT_READ, on_read_available, qxl); } #if SPICE_SERVER_VERSION >= 0x000c02 static void smartcard_event(SpiceCharDeviceInstance *sin, uint8_t event) { ErrorF("%s: unimplemented; event is %d\n", __FUNCTION__, event); } #endif static void smartcard_state(SpiceCharDeviceInstance *sin, int connected) { ErrorF("%s: unimplemented; connected is %d\n", __FUNCTION__, connected); } static SpiceCharDeviceInterface smartcard_interface = { .base.type = SPICE_INTERFACE_CHAR_DEVICE, .base.description = "Xspice virtual channel char device", .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR, .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR, .state = smartcard_state, .write = smartcard_write, .read = smartcard_read, #if SPICE_SERVER_VERSION >= 0x000c02 .event = smartcard_event, #endif }; int qxl_add_spice_smartcard_interface (qxl_screen_t *qxl) { int rc; struct sockaddr_un addr; if (qxl->smartcard_file[0] == 0) { xf86DrvMsg(qxl->pScrn->scrnIndex, X_INFO, "smartcard: no file given, smartcard is disabled\n"); return 0; } smartcard_sin.fd = -1; smartcard_sin.listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (smartcard_sin.listen_fd < 0) { ErrorF("smartcard: unable to open socket: %s\n", strerror(errno)); return errno; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, qxl->smartcard_file, sizeof(addr.sun_path) - 1); unlink(qxl->smartcard_file); if (bind(smartcard_sin.listen_fd, (struct sockaddr *) &addr, sizeof(addr))) { ErrorF("smartcard: unable to bind to unix domain %s: %s\n", qxl->smartcard_file, strerror(errno)); close(smartcard_sin.listen_fd); return errno; } if (listen(smartcard_sin.listen_fd, 1)) { ErrorF("smartcard: unable to listen to unix domain %s: %s\n", qxl->smartcard_file, strerror(errno)); close(smartcard_sin.listen_fd); return errno; } smartcard_sin.listen_watch = qxl->core->watch_add(smartcard_sin.listen_fd, SPICE_WATCH_EVENT_READ, on_accept_available, qxl); smartcard_sin.base.base.sif = &smartcard_interface.base; smartcard_sin.qxl = qxl; rc = spice_server_add_interface(qxl->spice_server, &smartcard_sin.base.base); if (rc < 0) return errno; return 0; } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_smartcard.h000066400000000000000000000024431361660122400243240ustar00rootroot00000000000000/* * Copyright 2014 Jeremy White for CodeWeavers Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifndef QXL_SPICE_SMARTCARD_H #define QXL_SPICE_SMARTCARD_H #include "qxl.h" #include int qxl_add_spice_smartcard_interface(qxl_screen_t *qxl); #endif xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_spice_server.c000066400000000000000000000264621361660122400250370ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ /** \file spiceqxl_spice_server.c * \author Alon Levy * * spice server helpers for spiceqxl. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "qxl.h" #include "qxl_option_helpers.h" #include "spiceqxl_spice_server.h" /* Single instance of spice server per Xorg executable. */ SpiceServer *xspice_get_spice_server(void) { static SpiceServer *spice_server; if (!spice_server) { spice_server = spice_server_new(); } return spice_server; } #define X509_CA_CERT_FILE "ca-cert.pem" #define X509_SERVER_KEY_FILE "server-key.pem" #define X509_SERVER_CERT_FILE "server-cert.pem" /* config string parsing */ #define SPICE_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static int name2enum(const char *string, const char *table[], int entries) { int i; if (string) { for (i = 0; i < entries; i++) { if (!table[i]) { continue; } if (strcmp(string, table[i]) != 0) { continue; } return i; } } return -1; } static int parse_name(const char *string, const char *optname, const char *table[], int entries) { int value = name2enum(string, table, entries); if (value != -1) { return value; } fprintf(stderr, "spice: invalid %s: %s\n", optname, string); exit(1); } static const char *stream_video_names[] = { [ SPICE_STREAM_VIDEO_OFF ] = "off", [ SPICE_STREAM_VIDEO_ALL ] = "all", [ SPICE_STREAM_VIDEO_FILTER ] = "filter", }; #define parse_stream_video(_name) \ name2enum(_name, stream_video_names, SPICE_ARRAY_SIZE(stream_video_names)) static const char *compression_names[] = { [ SPICE_IMAGE_COMPRESS_OFF ] = "off", [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz", [ SPICE_IMAGE_COMPRESS_AUTO_LZ ] = "auto_lz", [ SPICE_IMAGE_COMPRESS_QUIC ] = "quic", [ SPICE_IMAGE_COMPRESS_GLZ ] = "glz", [ SPICE_IMAGE_COMPRESS_LZ ] = "lz", }; #define parse_compression(_name) \ parse_name(_name, "image compression", \ compression_names, SPICE_ARRAY_SIZE(compression_names)) static const char *wan_compression_names[] = { [ SPICE_WAN_COMPRESSION_AUTO ] = "auto", [ SPICE_WAN_COMPRESSION_NEVER ] = "never", [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always", }; #define parse_wan_compression(_name) \ parse_name(_name, "wan compression", \ wan_compression_names, SPICE_ARRAY_SIZE(wan_compression_names)) void xspice_set_spice_server_options(OptionInfoPtr options) { /* environment variables take precedence. If not then take * parameters from the config file. */ int addr_flags; int len; spice_image_compression_t compression; spice_wan_compression_t wan_compr; int port = get_int_option(options, OPTION_SPICE_PORT, "XSPICE_PORT"); int tls_port = get_int_option(options, OPTION_SPICE_TLS_PORT, "XSPICE_TLS_PORT"); const char *password = get_str_option(options, OPTION_SPICE_PASSWORD, "XSPICE_PASSWORD"); int disable_ticketing = get_bool_option(options, OPTION_SPICE_DISABLE_TICKETING, "XSPICE_DISABLE_TICKETING"); const char *x509_dir = get_str_option(options, OPTION_SPICE_X509_DIR, "XSPICE_X509_DIR"); int sasl = get_bool_option(options, OPTION_SPICE_SASL, "XSPICE_SASL"); const char *x509_key_file_base = get_str_option(options, OPTION_SPICE_X509_KEY_FILE, "XSPICE_X509_KEY_FILE"); char *x509_key_file = NULL; const char *x509_cert_file_base = get_str_option(options, OPTION_SPICE_X509_CERT_FILE, "XSPICE_X509_CERT_FILE"); char *x509_cert_file = NULL; const char *x509_key_password = get_str_option(options, OPTION_SPICE_X509_KEY_PASSWORD, "XSPICE_X509_KEY_PASSWORD"); const char *tls_ciphers = get_str_option(options, OPTION_SPICE_TLS_CIPHERS, "XSPICE_TLS_CIPHERS"); const char *x509_cacert_file_base = get_str_option(options, OPTION_SPICE_CACERT_FILE, "XSPICE_CACERT_FILE"); char *x509_cacert_file = NULL; const char * addr = get_str_option(options, OPTION_SPICE_ADDR, "XSPICE_ADDR"); int ipv4 = get_bool_option(options, OPTION_SPICE_IPV4_ONLY, "XSPICE_IPV4_ONLY"); int ipv6 = get_bool_option(options, OPTION_SPICE_IPV6_ONLY, "XSPICE_IPV6_ONLY"); const char *x509_dh_file = get_str_option(options, OPTION_SPICE_DH_FILE, "XSPICE_DH_FILE"); int disable_copy_paste = get_bool_option(options, OPTION_SPICE_DISABLE_COPY_PASTE, "XSPICE_DISABLE_COPY_PASTE"); int exit_on_disconnect = get_bool_option(options, OPTION_SPICE_EXIT_ON_DISCONNECT, "XSPICE_EXIT_ON_DISCONNECT"); const char *image_compression = get_str_option(options, OPTION_SPICE_IMAGE_COMPRESSION, "XSPICE_IMAGE_COMPRESSION"); const char *jpeg_wan_compression = get_str_option(options, OPTION_SPICE_JPEG_WAN_COMPRESSION, "XSPICE_JPEG_WAN_COMPRESSION"); const char *zlib_glz_wan_compression = get_str_option(options, OPTION_SPICE_ZLIB_GLZ_WAN_COMPRESSION, "XSPICE_ZLIB_GLZ_WAN_COMPRESSION"); const char *streaming_video = get_str_option(options, OPTION_SPICE_STREAMING_VIDEO, "XSPICE_STREAMING_VIDEO"); const char *video_codecs = get_str_option(options, OPTION_SPICE_VIDEO_CODECS, "XSPICE_VIDEO_CODECS"); int agent_mouse = get_bool_option(options, OPTION_SPICE_AGENT_MOUSE, "XSPICE_AGENT_MOUSE"); int playback_compression = get_bool_option(options, OPTION_SPICE_PLAYBACK_COMPRESSION, "XSPICE_PLAYBACK_COMPRESSION"); SpiceServer *spice_server = xspice_get_spice_server(); if (!port && !tls_port) { printf("one of tls-port or port must be set\n"); exit(1); } printf("xspice: port = %d, tls_port = %d\n", port, tls_port); if (disable_ticketing) { spice_server_set_noauth(spice_server); } if (tls_port) { if (NULL == x509_dir) { x509_dir = "."; } len = strlen(x509_dir) + 32; if (x509_key_file_base) { x509_key_file = xnfstrdup(x509_key_file_base); } else { x509_key_file = xnfalloc(len); snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE); } if (x509_cert_file_base) { x509_cert_file = xnfstrdup(x509_cert_file_base); } else { x509_cert_file = xnfalloc(len); snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE); } if (x509_cacert_file_base) { x509_cacert_file = xnfstrdup(x509_cert_file_base); } else { x509_cacert_file = xnfalloc(len); snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE); } } addr_flags = 0; if (ipv4) { addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY; } else if (ipv6) { addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY; } spice_server_set_addr(spice_server, addr ? addr : "", addr_flags); if (port) { spice_server_set_port(spice_server, port); } if (tls_port) { spice_server_set_tls(spice_server, tls_port, x509_cacert_file, x509_cert_file, x509_key_file, x509_key_password, x509_dh_file, tls_ciphers); } if (password) { spice_server_set_ticket(spice_server, password, 0, 0, 0); } if (sasl) { #if SPICE_SERVER_VERSION >= 0x000802 /* 0.8.2 */ if (spice_server_set_sasl_appname(spice_server, "xspice") == -1 || spice_server_set_sasl(spice_server, 1) == -1) { fprintf(stderr, "spice: failed to enable sasl\n"); exit(1); } #else fprintf(stderr, "spice: sasl is not available (spice >= 0.8.2 required)\n"); exit(1); #endif } #if SPICE_SERVER_VERSION >= 0x000801 /* we still don't actually support agent in xspice, but this * can't hurt for later, just copied from qemn/ui/spice-core.c */ if (disable_copy_paste) { spice_server_set_agent_copypaste(spice_server, 0); } #endif if (exit_on_disconnect) { #if SPICE_SERVER_VERSION >= 0x000b04 /* 0.11.4 */ spice_server_set_exit_on_disconnect(spice_server, exit_on_disconnect); #else fprintf(stderr, "spice: cannot set exit_on_disconnect (spice >= 0.11.4 required)\n"); exit(1); #endif } compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ; if (image_compression) { compression = parse_compression(image_compression); } spice_server_set_image_compression(spice_server, compression); wan_compr = SPICE_WAN_COMPRESSION_AUTO; if (jpeg_wan_compression) { wan_compr = parse_wan_compression(jpeg_wan_compression); } spice_server_set_jpeg_compression(spice_server, wan_compr); wan_compr = SPICE_WAN_COMPRESSION_AUTO; if (zlib_glz_wan_compression) { wan_compr = parse_wan_compression(zlib_glz_wan_compression); } spice_server_set_zlib_glz_compression(spice_server, wan_compr); if (streaming_video) { int streaming_video_opt = parse_stream_video(streaming_video); spice_server_set_streaming_video(spice_server, streaming_video_opt); } if (video_codecs) { #if SPICE_SERVER_VERSION >= 0x000d02 /* 0.13.2 */ if (spice_server_set_video_codecs(spice_server, video_codecs)) { fprintf(stderr, "spice: invalid video encoder %s\n", video_codecs); exit(1); } #else fprintf(stderr, "spice: video_codecs are not available (spice >= 0.13.2 required)\n"); exit(1); #endif } spice_server_set_agent_mouse(spice_server, agent_mouse); spice_server_set_playback_compression(spice_server, playback_compression); free(x509_key_file); free(x509_cert_file); free(x509_cacert_file); } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_spice_server.h000066400000000000000000000026041361660122400250340ustar00rootroot00000000000000/* * Copyright 2011 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ /** \file spiceqxl_spice_server.h * \author Alon Levy * * spice server helpers for spiceqxl. */ #ifndef SPICEQXL_SPICE_SERVER_H #define SPICEQXL_SPICE_SERVER_H void xspice_set_spice_server_options(OptionInfoPtr options); #endif // SPICEQXL_SPICE_SERVER_H xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_uinput.c000066400000000000000000000072331361660122400236650ustar00rootroot00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include "qxl_option_helpers.h" #include "spiceqxl_util.h" #include "spiceqxl_inputs.h" #include "spiceqxl_uinput.h" static const char *uinput_filename; static int uinput_fd; static SpiceWatch *uinput_watch; static struct input_event inp_event; static int offset; static void spiceqxl_uinput_read_cb(int fd, int event, void *opaque) { int n; static int x = -1; static int y = -1; static int buttons_state = 0; int button = -1; n = read(uinput_fd, (char *)&inp_event + offset, sizeof(inp_event) - offset); if (n == -1) { if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) { fprintf(stderr, "spice: uinput read failed: %s\n", strerror(errno)); } return; } offset += n; if (offset < sizeof(inp_event)) { return; } offset = 0; switch (inp_event.type) { case EV_KEY: /* XXX Here we hardcode vdagent-uinput.c mapping since we don't support ioctls. * We could replace the ioctls with additional non uinput messages * used in vdagentd fake uinput mode. */ switch (inp_event.code) { case BTN_LEFT: button = 1 << 0; break; case BTN_MIDDLE: button = 1 << 1; break; case BTN_RIGHT: button = 1 << 2; break; } if (inp_event.value > 0) { buttons_state |= button; } else { buttons_state &= ~button; } spiceqxl_tablet_buttons(buttons_state); break; case EV_REL: button = 1; if (inp_event.value == 1) { button = 1 << 3; } else { button = 1 << 4; } buttons_state |= button; spiceqxl_tablet_buttons(buttons_state); buttons_state &= ~button; spiceqxl_tablet_buttons(buttons_state); break; case EV_ABS: switch (inp_event.code) { case ABS_X: x = inp_event.value; break; case ABS_Y: y = inp_event.value; break; default: fprintf(stderr, "%s: unknown axis %d, ignoring\n", __func__, inp_event.code); return; break; } spiceqxl_tablet_position(x, y, buttons_state); break; } } void spiceqxl_uinput_init(qxl_screen_t *qxl) { int ret; int enabled; uinput_filename = get_str_option(qxl->options, OPTION_SPICE_VDAGENT_UINPUT_PATH, "XSPICE_VDAGENT_UINPUT_PATH"); enabled = get_bool_option(qxl->options, OPTION_SPICE_VDAGENT_ENABLED, "XSPICE_VDAGENT_ENABLED"); if (!enabled || uinput_filename == NULL) { return; } ret = mkfifo(uinput_filename, 0666); if (ret != 0) { fprintf(stderr, "spice: failed to create uinput fifo %s: %s\n", uinput_filename, strerror(errno)); return; } spiceqxl_chown_agent_file(qxl, uinput_filename); uinput_fd = open(uinput_filename, O_RDONLY | O_NONBLOCK, 0666); if (uinput_fd == -1) { fprintf(stderr, "spice: failed creating uinput file %s: %s\n", uinput_filename, strerror(errno)); return; } } void spiceqxl_uinput_watch(qxl_screen_t *qxl, Bool on) { if (uinput_watch) { qxl->core->watch_remove(uinput_watch); uinput_watch = NULL; } if (on) uinput_watch = qxl->core->watch_add(uinput_fd, SPICE_WATCH_EVENT_READ, spiceqxl_uinput_read_cb, qxl); } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_uinput.h000066400000000000000000000002651361660122400236700ustar00rootroot00000000000000#ifndef SPICEQXL_UINPUT_H #define SPICEQXL_UINPUT_H #include "qxl.h" void spiceqxl_uinput_init(qxl_screen_t *qxl); void spiceqxl_uinput_watch(qxl_screen_t *qxl, Bool on); #endif xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_util.c000066400000000000000000000033541361660122400233160ustar00rootroot00000000000000/* * Copyright 2013 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #include "config.h" #include #include #include #include "qxl_option_helpers.h" #include "spiceqxl_util.h" void spiceqxl_chown_agent_file(qxl_screen_t *qxl, const char *filename) { int uid, gid; uid = get_int_option(qxl->options, OPTION_SPICE_VDAGENT_UID, "XSPICE_VDAGENT_UID"); gid = get_int_option(qxl->options, OPTION_SPICE_VDAGENT_GID, "XSPICE_VDAGENT_GID"); if (uid && gid) { if (chown(filename, uid, gid) != 0) { fprintf(stderr, "spice: failed to chain ownership of '%s' to %d/%d: %s\n", filename, uid, gid, strerror(errno)); } } } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_util.h000066400000000000000000000023771361660122400233270ustar00rootroot00000000000000/* * Copyright 2013 Red Hat, Inc. * * 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 (including the next * paragraph) 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 NON-INFRINGEMENT. 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. */ #ifndef SPICEQXL_UTIL_H #define SPICEQXL_UTIL_H #include "qxl.h" void spiceqxl_chown_agent_file(qxl_screen_t *qxl, const char *filename); #endif xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_vdagent.c000066400000000000000000000121101361660122400237570ustar00rootroot00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "qxl_option_helpers.h" #include "spiceqxl_util.h" #include "spiceqxl_uinput.h" #include "spiceqxl_vdagent.h" static const char *vdagent_virtio_filename; static int virtio_fd; static int virtio_client_fd = -1; static SpiceWatch *virtio_client_watch; typedef struct XSpiceVdagentCharDeviceInstance { SpiceCharDeviceInstance base; qxl_screen_t *qxl; } XSpiceVdagentCharDeviceInstance; static XSpiceVdagentCharDeviceInstance vdagent_sin = { .base = { .subtype = "vdagent" } }; static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) { int written; if (virtio_client_fd == -1) { return 0; } written = send(virtio_client_fd, buf, len, 0); if (written != len) { fprintf(stderr, "%s: ERROR: short write to vdagentd - TODO buffering\n", __func__); } return written; } static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) { int nbytes; if (virtio_client_fd == -1) { return 0; } nbytes = recv(virtio_client_fd, buf, len, 0); if (nbytes <= 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { return 0; } fprintf(stderr, "ERROR: vdagent died\n"); close(virtio_client_fd); virtio_client_fd = -1; vdagent_sin.qxl->core->watch_remove(virtio_client_watch); virtio_client_watch = NULL; spice_server_remove_interface(&vdagent_sin.base.base); spiceqxl_uinput_watch(vdagent_sin.qxl, FALSE); } return nbytes; } static void on_read_available(int fd, int event, void *opaque) { if (virtio_client_fd == -1) { return; } spice_server_char_device_wakeup(&vdagent_sin.base); } #if SPICE_SERVER_VERSION >= 0x000c02 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event) { } #endif static void vmc_state(SpiceCharDeviceInstance *sin, int connected) { } static SpiceCharDeviceInterface vmc_interface = { .base.type = SPICE_INTERFACE_CHAR_DEVICE, .base.description = "Xspice virtual channel char device", .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR, .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR, .state = vmc_state, .write = vmc_write, .read = vmc_read, #if SPICE_SERVER_VERSION >= 0x000c02 .event = vmc_event, #endif }; static void on_accept(int fd, int event, void *opaque) { qxl_screen_t *qxl = opaque; struct sockaddr_un address; socklen_t length = sizeof(address); int flags; virtio_client_fd = accept(virtio_fd, (struct sockaddr *)&address, &length); if (virtio_client_fd == -1) { fprintf(stderr, "error accepting on unix domain socket: %s\n", strerror(errno)); return; } flags = fcntl(virtio_client_fd, F_GETFL); if (flags == -1) { fprintf(stderr, "error getting flags from uds client fd: %s\n", strerror(errno)); goto error; } if (fcntl(virtio_client_fd, F_SETFL, flags | O_NONBLOCK | O_CLOEXEC) == -1) { fprintf(stderr, "error setting CLOEXEC & NONBLOCK flags from uds client fd: %s\n", strerror(errno)); goto error; } virtio_client_watch = qxl->core->watch_add(virtio_client_fd, SPICE_WATCH_EVENT_READ /* TODO - SPICE_WATCH_EVENT_WRITE */, on_read_available, qxl); spice_server_add_interface(qxl->spice_server, &vdagent_sin.base.base); spiceqxl_uinput_watch(qxl, TRUE); return; error: if (virtio_client_fd != -1) { close(virtio_client_fd); virtio_client_fd = -1; } } void spiceqxl_vdagent_init(qxl_screen_t *qxl) { struct sockaddr_un address; int c; int enabled; vdagent_sin.qxl = qxl; vdagent_virtio_filename = get_str_option(qxl->options, OPTION_SPICE_VDAGENT_VIRTIO_PATH, "XSPICE_VDAGENT_VIRTIO_PATH"); enabled = get_bool_option(qxl->options, OPTION_SPICE_VDAGENT_ENABLED, "XSPICE_VDAGENT_ENABLED"); if (!enabled || !vdagent_virtio_filename) { return; } virtio_fd = socket(PF_UNIX, SOCK_STREAM, 0); if (virtio_fd == -1) { fprintf(stderr, "error creating unix domain socket\n"); return; } address.sun_family = AF_UNIX; snprintf(address.sun_path, sizeof(address.sun_path), "%s", vdagent_virtio_filename); c = bind(virtio_fd, (struct sockaddr *)&address, sizeof(address)); if (c != 0) { fprintf(stderr, "error binding unix domain socket to %s: %s\n", vdagent_virtio_filename, strerror(errno)); return; } spiceqxl_chown_agent_file(qxl, vdagent_virtio_filename); c = listen(virtio_fd, 1); if (c != 0) { fprintf(stderr, "error listening to unix domain socket: %s\n", strerror(errno)); return; } qxl->core->watch_add(virtio_fd, SPICE_WATCH_EVENT_READ /* TODO - SPICE_WATCH_EVENT_WRITE */, on_accept, qxl); vdagent_sin.base.base.sif = &vmc_interface.base; spiceqxl_uinput_init(qxl); } xserver-xorg-video-qxl-0.1.5+git20200331/src/spiceqxl_vdagent.h000066400000000000000000000002001361660122400237610ustar00rootroot00000000000000#ifndef SPICEQXL_VDAGENT_H #define SPICEQXL_VDAGENT_H #include "qxl.h" void spiceqxl_vdagent_init(qxl_screen_t *qxl); #endif xserver-xorg-video-qxl-0.1.5+git20200331/src/uxa/000077500000000000000000000000001361660122400210555ustar00rootroot00000000000000xserver-xorg-video-qxl-0.1.5+git20200331/src/uxa/Makefile.am000066400000000000000000000006021361660122400231070ustar00rootroot00000000000000noinst_LTLIBRARIES = libuxa.la # Override these since UXA doesn't need them and the needed files aren't # built (in hw/xfree86/os-support/solaris) until after UXA is built SOLARIS_ASM_CFLAGS="" AM_CFLAGS = $(CWARNFLAGS) $(XORG_CFLAGS) libuxa_la_SOURCES = \ uxa.c \ uxa.h \ uxa-accel.c \ uxa-glyphs.c \ uxa-render.c \ uxa-priv.h \ uxa-unaccel.c \ uxa-damage.c \ uxa-damage.h xserver-xorg-video-qxl-0.1.5+git20200331/src/uxa/uxa-accel.c000066400000000000000000001023461361660122400230710ustar00rootroot00000000000000/* * Copyright ® 2001 Keith Packard * * Partly based on code that is Copyright ® The XFree86 Project Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * * Authors: * Eric Anholt * Michel Dänzer * */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include "uxa-priv.h" #include "uxa.h" #include "mipict.h" static CARD32 format_for_depth(int depth) { switch (depth) { case 1: return PICT_a1; case 4: return PICT_a4; case 8: return PICT_a8; case 15: return PICT_x1r5g5b5; case 16: return PICT_r5g6b5; default: case 24: return PICT_x8r8g8b8; case 32: return PICT_a8r8g8b8; } } static void uxa_fill_spans(DrawablePtr pDrawable, GCPtr pGC, int n, DDXPointPtr ppt, int *pwidth, int fSorted) { ScreenPtr screen = pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(screen); RegionPtr pClip = fbGetCompositeClip(pGC); PixmapPtr dst_pixmap, src_pixmap = NULL; BoxPtr pextent, pbox; int nbox; int extentX1, extentX2, extentY1, extentY2; int fullX1, fullX2, fullY1; int partX1, partX2; int off_x, off_y; xRenderColor color; PictFormatPtr format; PicturePtr dst, src; int error; if (uxa_screen->swappedOut || uxa_screen->force_fallback) goto fallback; if (pGC->fillStyle != FillSolid) goto fallback; dst_pixmap = uxa_get_offscreen_pixmap(pDrawable, &off_x, &off_y); if (!dst_pixmap) goto fallback; if (pGC->alu != GXcopy || pGC->planemask != FB_ALLONES) goto solid; format = PictureMatchFormat(screen, dst_pixmap->drawable.depth, format_for_depth(dst_pixmap->drawable.depth)); dst = CreatePicture(0, &dst_pixmap->drawable, format, 0, 0, serverClient, &error); if (!dst) goto solid; ValidatePicture(dst); uxa_get_rgba_from_pixel(pGC->fgPixel, &color.red, &color.green, &color.blue, &color.alpha, format_for_depth(dst_pixmap->drawable.depth)); src = CreateSolidPicture(0, &color, &error); if (!src) { FreePicture(dst, 0); goto solid; } if (!uxa_screen->info->check_composite(PictOpSrc, src, NULL, dst, 0, 0)) { FreePicture(src, 0); FreePicture(dst, 0); goto solid; } if (!uxa_screen->info->check_composite_texture || !uxa_screen->info->check_composite_texture(screen, src)) { PicturePtr solid; int src_off_x, src_off_y; solid = uxa_acquire_solid(screen, src->pSourcePict); FreePicture(src, 0); src = solid; src_pixmap = uxa_get_offscreen_pixmap(src->pDrawable, &src_off_x, &src_off_y); if (!src_pixmap) { FreePicture(src, 0); FreePicture(dst, 0); goto solid; } } if (!uxa_screen->info->prepare_composite(PictOpSrc, src, NULL, dst, src_pixmap, NULL, dst_pixmap)) { FreePicture(src, 0); FreePicture(dst, 0); goto solid; } pextent = REGION_EXTENTS(pGC->screen, pClip); extentX1 = pextent->x1; extentY1 = pextent->y1; extentX2 = pextent->x2; extentY2 = pextent->y2; while (n--) { fullX1 = ppt->x; fullY1 = ppt->y; fullX2 = fullX1 + (int)*pwidth; ppt++; pwidth++; if (fullY1 < extentY1 || extentY2 <= fullY1) continue; if (fullX1 < extentX1) fullX1 = extentX1; if (fullX2 > extentX2) fullX2 = extentX2; if (fullX1 >= fullX2) continue; nbox = REGION_NUM_RECTS(pClip); if (nbox == 1) { uxa_screen->info->composite(dst_pixmap, 0, 0, 0, 0, fullX1 + off_x, fullY1 + off_y, fullX2 - fullX1, 1); } else { pbox = REGION_RECTS(pClip); while (nbox--) { if (pbox->y1 > fullY1) break; if (pbox->y1 <= fullY1) { partX1 = pbox->x1; if (partX1 < fullX1) partX1 = fullX1; partX2 = pbox->x2; if (partX2 > fullX2) partX2 = fullX2; if (partX2 > partX1) { uxa_screen->info->composite(dst_pixmap, 0, 0, 0, 0, partX1 + off_x, fullY1 + off_y, partX2 - partX1, 1); } } pbox++; } } } uxa_screen->info->done_composite(dst_pixmap); FreePicture(src, 0); FreePicture(dst, 0); return; solid: if (uxa_screen->info->check_solid && !uxa_screen->info->check_solid(pDrawable, pGC->alu, pGC->planemask)) goto fallback; if (!(*uxa_screen->info->prepare_solid) (dst_pixmap, pGC->alu, pGC->planemask, pGC->fgPixel)) goto fallback; pextent = REGION_EXTENTS(pGC->screen, pClip); extentX1 = pextent->x1; extentY1 = pextent->y1; extentX2 = pextent->x2; extentY2 = pextent->y2; while (n--) { fullX1 = ppt->x; fullY1 = ppt->y; fullX2 = fullX1 + (int)*pwidth; ppt++; pwidth++; if (fullY1 < extentY1 || extentY2 <= fullY1) continue; if (fullX1 < extentX1) fullX1 = extentX1; if (fullX2 > extentX2) fullX2 = extentX2; if (fullX1 >= fullX2) continue; nbox = REGION_NUM_RECTS(pClip); if (nbox == 1) { (*uxa_screen->info->solid) (dst_pixmap, fullX1 + off_x, fullY1 + off_y, fullX2 + off_x, fullY1 + 1 + off_y); } else { pbox = REGION_RECTS(pClip); while (nbox--) { if (pbox->y1 <= fullY1 && fullY1 < pbox->y2) { partX1 = pbox->x1; if (partX1 < fullX1) partX1 = fullX1; partX2 = pbox->x2; if (partX2 > fullX2) partX2 = fullX2; if (partX2 > partX1) { (*uxa_screen->info-> solid) (dst_pixmap, partX1 + off_x, fullY1 + off_y, partX2 + off_x, fullY1 + 1 + off_y); } } pbox++; } } } (*uxa_screen->info->done_solid) (dst_pixmap); return; fallback: uxa_check_fill_spans(pDrawable, pGC, n, ppt, pwidth, fSorted); } static Bool uxa_do_put_image(DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, int w, int h, int format, char *bits, int src_stride) { uxa_screen_t *uxa_screen = uxa_get_screen(pDrawable->pScreen); PixmapPtr pPix; RegionPtr pClip; BoxPtr pbox; int nbox; int xoff, yoff; int bpp = pDrawable->bitsPerPixel; /* Don't bother with under 8bpp, XYPixmaps. */ if (format != ZPixmap || bpp < 8) return FALSE; if (uxa_screen->swappedOut || uxa_screen->force_fallback) return FALSE; if (!uxa_screen->info->put_image) return FALSE; /* Only accelerate copies: no rop or planemask. */ if (!UXA_PM_IS_SOLID(pDrawable, pGC->planemask) || pGC->alu != GXcopy) return FALSE; pPix = uxa_get_offscreen_pixmap(pDrawable, &xoff, &yoff); if (!pPix) return FALSE; x += pDrawable->x; y += pDrawable->y; pClip = fbGetCompositeClip(pGC); for (nbox = REGION_NUM_RECTS(pClip), pbox = REGION_RECTS(pClip); nbox--; pbox++) { int x1 = x; int y1 = y; int x2 = x + w; int y2 = y + h; char *src; Bool ok; if (x1 < pbox->x1) x1 = pbox->x1; if (y1 < pbox->y1) y1 = pbox->y1; if (x2 > pbox->x2) x2 = pbox->x2; if (y2 > pbox->y2) y2 = pbox->y2; if (x1 >= x2 || y1 >= y2) continue; src = bits + (y1 - y) * src_stride + (x1 - x) * (bpp / 8); ok = uxa_screen->info->put_image(pPix, x1 + xoff, y1 + yoff, x2 - x1, y2 - y1, src, src_stride); /* If we fail to accelerate the upload, fall back to using * unaccelerated fb calls. */ if (!ok) { FbStip *dst; FbStride dst_stride; int dstBpp; int dstXoff, dstYoff; if (!uxa_prepare_access(pDrawable, NULL, UXA_ACCESS_RW)) return FALSE; fbGetStipDrawable(pDrawable, dst, dst_stride, dstBpp, dstXoff, dstYoff); fbBltStip((FbStip *) bits + (y1 - y) * (src_stride / sizeof(FbStip)), src_stride / sizeof(FbStip), (x1 - x) * dstBpp, dst + (y1 + dstYoff) * dst_stride, dst_stride, (x1 + dstXoff) * dstBpp, (x2 - x1) * dstBpp, y2 - y1, GXcopy, FB_ALLONES, dstBpp); uxa_finish_access(pDrawable); } } return TRUE; } static void uxa_put_image(DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, int w, int h, int leftPad, int format, char *bits) { if (!uxa_do_put_image(pDrawable, pGC, depth, x, y, w, h, format, bits, PixmapBytePad(w, pDrawable->depth))) { uxa_check_put_image(pDrawable, pGC, depth, x, y, w, h, leftPad, format, bits); } } static Bool inline uxa_copy_n_to_n_two_dir(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC, BoxPtr pbox, int nbox, int dx, int dy) { uxa_screen_t *uxa_screen = uxa_get_screen(pDstDrawable->pScreen); PixmapPtr pSrcPixmap, pDstPixmap; int src_off_x, src_off_y, dst_off_x, dst_off_y; int dirsetup; /* Need to get both pixmaps to call the driver routines */ pSrcPixmap = uxa_get_offscreen_pixmap(pSrcDrawable, &src_off_x, &src_off_y); pDstPixmap = uxa_get_offscreen_pixmap(pDstDrawable, &dst_off_x, &dst_off_y); if (!pSrcPixmap || !pDstPixmap) return FALSE; /* * Now the case of a chip that only supports xdir = ydir = 1 or * xdir = ydir = -1, but we have xdir != ydir. */ dirsetup = 0; /* No direction set up yet. */ for (; nbox; pbox++, nbox--) { if (dx >= 0 && (src_off_y + pbox->y1 + dy) != pbox->y1) { /* Do a xdir = ydir = -1 blit instead. */ if (dirsetup != -1) { if (dirsetup != 0) uxa_screen->info->done_copy(pDstPixmap); dirsetup = -1; if (!(*uxa_screen->info->prepare_copy) (pSrcPixmap, pDstPixmap, -1, -1, pGC ? pGC->alu : GXcopy, pGC ? pGC->planemask : FB_ALLONES)) return FALSE; } (*uxa_screen->info->copy) (pDstPixmap, src_off_x + pbox->x1 + dx, src_off_y + pbox->y1 + dy, dst_off_x + pbox->x1, dst_off_y + pbox->y1, pbox->x2 - pbox->x1, pbox->y2 - pbox->y1); } else if (dx < 0 && (src_off_y + pbox->y1 + dy) != pbox->y1) { /* Do a xdir = ydir = 1 blit instead. */ if (dirsetup != 1) { if (dirsetup != 0) uxa_screen->info->done_copy(pDstPixmap); dirsetup = 1; if (!(*uxa_screen->info->prepare_copy) (pSrcPixmap, pDstPixmap, 1, 1, pGC ? pGC->alu : GXcopy, pGC ? pGC->planemask : FB_ALLONES)) return FALSE; } (*uxa_screen->info->copy) (pDstPixmap, src_off_x + pbox->x1 + dx, src_off_y + pbox->y1 + dy, dst_off_x + pbox->x1, dst_off_y + pbox->y1, pbox->x2 - pbox->x1, pbox->y2 - pbox->y1); } else if (dx >= 0) { /* * xdir = 1, ydir = -1. * Perform line-by-line xdir = ydir = 1 blits, going up. */ int i; if (dirsetup != 1) { if (dirsetup != 0) uxa_screen->info->done_copy(pDstPixmap); dirsetup = 1; if (!(*uxa_screen->info->prepare_copy) (pSrcPixmap, pDstPixmap, 1, 1, pGC ? pGC->alu : GXcopy, pGC ? pGC->planemask : FB_ALLONES)) return FALSE; } for (i = pbox->y2 - pbox->y1 - 1; i >= 0; i--) (*uxa_screen->info->copy) (pDstPixmap, src_off_x + pbox->x1 + dx, src_off_y + pbox->y1 + dy + i, dst_off_x + pbox->x1, dst_off_y + pbox->y1 + i, pbox->x2 - pbox->x1, 1); } else { /* * xdir = -1, ydir = 1. * Perform line-by-line xdir = ydir = -1 blits, * going down. */ int i; if (dirsetup != -1) { if (dirsetup != 0) uxa_screen->info->done_copy(pDstPixmap); dirsetup = -1; if (!(*uxa_screen->info->prepare_copy) (pSrcPixmap, pDstPixmap, -1, -1, pGC ? pGC->alu : GXcopy, pGC ? pGC->planemask : FB_ALLONES)) return FALSE; } for (i = 0; i < pbox->y2 - pbox->y1; i++) (*uxa_screen->info->copy) (pDstPixmap, src_off_x + pbox->x1 + dx, src_off_y + pbox->y1 + dy + i, dst_off_x + pbox->x1, dst_off_y + pbox->y1 + i, pbox->x2 - pbox->x1, 1); } } if (dirsetup != 0) uxa_screen->info->done_copy(pDstPixmap); return TRUE; } void uxa_copy_n_to_n(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC, BoxPtr pbox, int nbox, int dx, int dy, Bool reverse, Bool upsidedown, Pixel bitplane, void *closure) { ScreenPtr screen = pDstDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(screen); int src_off_x, src_off_y; int dst_off_x, dst_off_y; PixmapPtr pSrcPixmap, pDstPixmap; RegionRec src_region; RegionRec dst_region; pSrcPixmap = uxa_get_drawable_pixmap(pSrcDrawable); pDstPixmap = uxa_get_drawable_pixmap(pDstDrawable); if (!pSrcPixmap || !pDstPixmap) goto fallback; if (uxa_screen->info->check_copy && !uxa_screen->info->check_copy(pSrcPixmap, pDstPixmap, pGC ? pGC->alu : GXcopy, pGC ? pGC->planemask : FB_ALLONES)) goto fallback; uxa_get_drawable_deltas(pSrcDrawable, pSrcPixmap, &src_off_x, &src_off_y); uxa_get_drawable_deltas(pDstDrawable, pDstPixmap, &dst_off_x, &dst_off_y); /* Mixed directions must be handled specially if the card is lame */ if ((uxa_screen->info->flags & UXA_TWO_BITBLT_DIRECTIONS) && reverse != upsidedown) { if (uxa_copy_n_to_n_two_dir (pSrcDrawable, pDstDrawable, pGC, pbox, nbox, dx, dy)) return; goto fallback; } if (!uxa_pixmap_is_offscreen(pDstPixmap)) goto fallback; if (uxa_pixmap_is_offscreen(pSrcPixmap)) { if (!(*uxa_screen->info->prepare_copy) (pSrcPixmap, pDstPixmap, reverse ? -1 : 1, upsidedown ? -1 : 1, pGC ? pGC->alu : GXcopy, pGC ? pGC-> planemask : FB_ALLONES)) goto fallback; while (nbox--) { (*uxa_screen->info->copy) (pDstPixmap, pbox->x1 + dx + src_off_x, pbox->y1 + dy + src_off_y, pbox->x1 + dst_off_x, pbox->y1 + dst_off_y, pbox->x2 - pbox->x1, pbox->y2 - pbox->y1); pbox++; } (*uxa_screen->info->done_copy) (pDstPixmap); } else { int stride, bpp; char *src; if (!uxa_screen->info->put_image) goto fallback; /* Don't bother with under 8bpp, XYPixmaps. */ bpp = pSrcPixmap->drawable.bitsPerPixel; if (bpp != pDstDrawable->bitsPerPixel || bpp < 8) goto fallback; /* Only accelerate copies: no rop or planemask. */ if (pGC && (!UXA_PM_IS_SOLID(pSrcDrawable, pGC->planemask) || pGC->alu != GXcopy)) goto fallback; src = pSrcPixmap->devPrivate.ptr; stride = pSrcPixmap->devKind; bpp /= 8; while (nbox--) { if (!uxa_screen->info->put_image(pDstPixmap, pbox->x1 + dst_off_x, pbox->y1 + dst_off_y, pbox->x2 - pbox->x1, pbox->y2 - pbox->y1, (char *) src + (pbox->y1 + dy + src_off_y) * stride + (pbox->x1 + dx + src_off_x) * bpp, stride)) goto fallback; pbox++; } } return; fallback: #if 0 ErrorF ("falling back: %d boxes\n", nbox); #endif pixman_region_init_rects (&dst_region, pbox, nbox); REGION_INIT (NULL, &src_region, (BoxPtr)NULL, 0); REGION_COPY (NULL, &src_region, &dst_region); REGION_TRANSLATE (NULL, &src_region, dx, dy); UXA_FALLBACK(("from %p to %p (%c,%c)\n", pSrcDrawable, pDstDrawable, uxa_drawable_location(pSrcDrawable), uxa_drawable_location(pDstDrawable))); if (uxa_prepare_access(pDstDrawable, &dst_region, UXA_ACCESS_RW)) { if (uxa_prepare_access(pSrcDrawable, &src_region, UXA_ACCESS_RO)) { fbCopyNtoN(pSrcDrawable, pDstDrawable, pGC, pbox, nbox, dx, dy, reverse, upsidedown, bitplane, closure); uxa_finish_access(pSrcDrawable); } uxa_finish_access(pDstDrawable); } REGION_UNINIT (NULL, &src_region); REGION_UNINIT (NULL, &dst_region); } RegionPtr uxa_copy_area(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC, int srcx, int srcy, int width, int height, int dstx, int dsty) { uxa_screen_t *uxa_screen = uxa_get_screen(pDstDrawable->pScreen); if (uxa_screen->swappedOut || uxa_screen->force_fallback) { return uxa_check_copy_area(pSrcDrawable, pDstDrawable, pGC, srcx, srcy, width, height, dstx, dsty); } return miDoCopy(pSrcDrawable, pDstDrawable, pGC, srcx, srcy, width, height, dstx, dsty, uxa_copy_n_to_n, 0, NULL); } static void uxa_poly_point(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr ppt) { int i; xRectangle *prect; /* If we can't reuse the current GC as is, don't bother accelerating the * points. */ if (pGC->fillStyle != FillSolid) { uxa_check_poly_point(pDrawable, pGC, mode, npt, ppt); return; } prect = malloc(sizeof(xRectangle) * npt); if (!prect) return; for (i = 0; i < npt; i++) { prect[i].x = ppt[i].x; prect[i].y = ppt[i].y; if (i > 0 && mode == CoordModePrevious) { prect[i].x += prect[i - 1].x; prect[i].y += prect[i - 1].y; } prect[i].width = 1; prect[i].height = 1; } pGC->ops->PolyFillRect(pDrawable, pGC, npt, prect); free(prect); } /** * uxa_poly_lines() checks if it can accelerate the lines as a group of * horizontal or vertical lines (rectangles), and uses existing rectangle fill * acceleration if so. */ static void uxa_poly_lines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr ppt) { xRectangle *prect; int x1, x2, y1, y2; int i; /* Don't try to do wide lines or non-solid fill style. */ if (pGC->lineWidth != 0 || pGC->lineStyle != LineSolid || pGC->fillStyle != FillSolid) { uxa_check_poly_lines(pDrawable, pGC, mode, npt, ppt); return; } prect = malloc(sizeof(xRectangle) * (npt - 1)); if (!prect) return; x1 = ppt[0].x; y1 = ppt[0].y; /* If we have any non-horizontal/vertical, fall back. */ for (i = 0; i < npt - 1; i++) { if (mode == CoordModePrevious) { x2 = x1 + ppt[i + 1].x; y2 = y1 + ppt[i + 1].y; } else { x2 = ppt[i + 1].x; y2 = ppt[i + 1].y; } if (x1 != x2 && y1 != y2) { free(prect); uxa_check_poly_lines(pDrawable, pGC, mode, npt, ppt); return; } if (x1 < x2) { prect[i].x = x1; prect[i].width = x2 - x1 + 1; } else { prect[i].x = x2; prect[i].width = x1 - x2 + 1; } if (y1 < y2) { prect[i].y = y1; prect[i].height = y2 - y1 + 1; } else { prect[i].y = y2; prect[i].height = y1 - y2 + 1; } x1 = x2; y1 = y2; } pGC->ops->PolyFillRect(pDrawable, pGC, npt - 1, prect); free(prect); } /** * uxa_poly_segment() checks if it can accelerate the lines as a group of * horizontal or vertical lines (rectangles), and uses existing rectangle fill * acceleration if so. */ static void uxa_poly_segment(DrawablePtr pDrawable, GCPtr pGC, int nseg, xSegment * pSeg) { xRectangle *prect; int i; /* Don't try to do wide lines or non-solid fill style. */ if (pGC->lineWidth != 0 || pGC->lineStyle != LineSolid || pGC->fillStyle != FillSolid) { uxa_check_poly_segment(pDrawable, pGC, nseg, pSeg); return; } /* If we have any non-horizontal/vertical, fall back. */ for (i = 0; i < nseg; i++) { if (pSeg[i].x1 != pSeg[i].x2 && pSeg[i].y1 != pSeg[i].y2) { uxa_check_poly_segment(pDrawable, pGC, nseg, pSeg); return; } } prect = malloc(sizeof(xRectangle) * nseg); if (!prect) return; for (i = 0; i < nseg; i++) { if (pSeg[i].x1 < pSeg[i].x2) { prect[i].x = pSeg[i].x1; prect[i].width = pSeg[i].x2 - pSeg[i].x1 + 1; } else { prect[i].x = pSeg[i].x2; prect[i].width = pSeg[i].x1 - pSeg[i].x2 + 1; } if (pSeg[i].y1 < pSeg[i].y2) { prect[i].y = pSeg[i].y1; prect[i].height = pSeg[i].y2 - pSeg[i].y1 + 1; } else { prect[i].y = pSeg[i].y2; prect[i].height = pSeg[i].y1 - pSeg[i].y2 + 1; } /* don't paint last pixel */ if (pGC->capStyle == CapNotLast) { if (prect[i].width == 1) prect[i].height--; else prect[i].width--; } } pGC->ops->PolyFillRect(pDrawable, pGC, nseg, prect); free(prect); } static Bool uxa_fill_region_solid(DrawablePtr pDrawable, RegionPtr pRegion, Pixel pixel, CARD32 planemask, CARD32 alu); static void uxa_poly_fill_rect(DrawablePtr pDrawable, GCPtr pGC, int nrect, xRectangle * prect) { uxa_screen_t *uxa_screen = uxa_get_screen(pDrawable->pScreen); RegionPtr pClip = fbGetCompositeClip(pGC); PixmapPtr pPixmap; register BoxPtr pbox; BoxPtr pextent; int extentX1, extentX2, extentY1, extentY2; int fullX1, fullX2, fullY1, fullY2; int partX1, partX2, partY1, partY2; int xoff, yoff; int xorg, yorg; int n; RegionPtr pReg = RECTS_TO_REGION(pScreen, nrect, prect, CT_UNSORTED); /* Compute intersection of rects and clip region */ REGION_TRANSLATE(pScreen, pReg, pDrawable->x, pDrawable->y); REGION_INTERSECT(pScreen, pReg, pClip, pReg); if (!REGION_NUM_RECTS(pReg)) goto out; if (uxa_screen->swappedOut || uxa_screen->force_fallback) goto fallback; pPixmap = uxa_get_offscreen_pixmap (pDrawable, &xoff, &yoff); if (!pPixmap) goto fallback; /* For ROPs where overlaps don't matter, convert rectangles to region * and call uxa_fill_region_{solid,tiled}. */ if ((pGC->fillStyle == FillSolid || pGC->fillStyle == FillTiled) && (nrect == 1 || pGC->alu == GXcopy || pGC->alu == GXclear || pGC->alu == GXnoop || pGC->alu == GXcopyInverted || pGC->alu == GXset)) { if (((pGC->fillStyle == FillSolid || pGC->tileIsPixel) && uxa_fill_region_solid(pDrawable, pReg, pGC->fillStyle == FillSolid ? pGC->fgPixel : pGC->tile. pixel, pGC->planemask, pGC->alu)) || (pGC->fillStyle == FillTiled && !pGC->tileIsPixel && uxa_fill_region_tiled(pDrawable, pReg, pGC->tile.pixmap, &pGC->patOrg, pGC->planemask, pGC->alu))) { goto out; } } if (pGC->fillStyle != FillSolid && !(pGC->tileIsPixel && pGC->fillStyle == FillTiled)) { goto fallback; } if (uxa_screen->info->check_solid && !uxa_screen->info->check_solid(pDrawable, pGC->alu, pGC->planemask)) { goto fallback; } if (!(*uxa_screen->info->prepare_solid) (pPixmap, pGC->alu, pGC->planemask, pGC->fgPixel)) { fallback: uxa_check_poly_fill_rect(pDrawable, pGC, nrect, prect); goto out; } xorg = pDrawable->x; yorg = pDrawable->y; pextent = REGION_EXTENTS(pGC->pScreen, pClip); extentX1 = pextent->x1; extentY1 = pextent->y1; extentX2 = pextent->x2; extentY2 = pextent->y2; while (nrect--) { fullX1 = prect->x + xorg; fullY1 = prect->y + yorg; fullX2 = fullX1 + (int)prect->width; fullY2 = fullY1 + (int)prect->height; prect++; if (fullX1 < extentX1) fullX1 = extentX1; if (fullY1 < extentY1) fullY1 = extentY1; if (fullX2 > extentX2) fullX2 = extentX2; if (fullY2 > extentY2) fullY2 = extentY2; if ((fullX1 >= fullX2) || (fullY1 >= fullY2)) continue; n = REGION_NUM_RECTS(pClip); if (n == 1) { (*uxa_screen->info->solid) (pPixmap, fullX1 + xoff, fullY1 + yoff, fullX2 + xoff, fullY2 + yoff); } else { pbox = REGION_RECTS(pClip); /* * clip the rectangle to each box in the clip region * this is logically equivalent to calling Intersect(), * but rectangles may overlap each other here. */ while (n--) { partX1 = pbox->x1; if (partX1 < fullX1) partX1 = fullX1; partY1 = pbox->y1; if (partY1 < fullY1) partY1 = fullY1; partX2 = pbox->x2; if (partX2 > fullX2) partX2 = fullX2; partY2 = pbox->y2; if (partY2 > fullY2) partY2 = fullY2; pbox++; if (partX1 < partX2 && partY1 < partY2) { (*uxa_screen->info->solid) (pPixmap, partX1 + xoff, partY1 + yoff, partX2 + xoff, partY2 + yoff); } } } } (*uxa_screen->info->done_solid) (pPixmap); out: REGION_UNINIT(pScreen, pReg); REGION_DESTROY(pScreen, pReg); } GCOps uxa_ops = { uxa_fill_spans, uxa_check_set_spans, uxa_put_image, uxa_copy_area, uxa_check_copy_plane, uxa_poly_point, uxa_poly_lines, uxa_poly_segment, miPolyRectangle, uxa_check_poly_arc, miFillPolygon, uxa_poly_fill_rect, miPolyFillArc, miPolyText8, miPolyText16, miImageText8, miImageText16, uxa_check_image_glyph_blt, uxa_check_poly_glyph_blt, uxa_check_push_pixels, }; void uxa_copy_window(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc) { RegionRec rgnDst; int dx, dy; PixmapPtr pPixmap = (*pWin->drawable.pScreen->GetWindowPixmap) (pWin); dx = ptOldOrg.x - pWin->drawable.x; dy = ptOldOrg.y - pWin->drawable.y; REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, -dx, -dy); REGION_INIT(pWin->drawable.pScreen, &rgnDst, NullBox, 0); REGION_INTERSECT(pWin->drawable.pScreen, &rgnDst, &pWin->borderClip, prgnSrc); #ifdef COMPOSITE if (pPixmap->screen_x || pPixmap->screen_y) REGION_TRANSLATE(pWin->drawable.pScreen, &rgnDst, -pPixmap->screen_x, -pPixmap->screen_y); #endif miCopyRegion(&pPixmap->drawable, &pPixmap->drawable, NULL, &rgnDst, dx, dy, uxa_copy_n_to_n, 0, NULL); REGION_UNINIT(pWin->drawable.pScreen, &rgnDst); } static Bool uxa_fill_region_solid(DrawablePtr pDrawable, RegionPtr pRegion, Pixel pixel, CARD32 planemask, CARD32 alu) { ScreenPtr screen = pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(screen); PixmapPtr pixmap; int xoff, yoff; int nbox; BoxPtr pBox, extents; Bool ret = FALSE; pixmap = uxa_get_offscreen_pixmap(pDrawable, &xoff, &yoff); if (!pixmap) return FALSE; REGION_TRANSLATE(screen, pRegion, xoff, yoff); nbox = REGION_NUM_RECTS(pRegion); pBox = REGION_RECTS(pRegion); extents = REGION_EXTENTS(screen, pRegion); /* Using GEM, the relocation costs outweigh the advantages of the blitter */ if (nbox == 1 || (alu != GXcopy && alu != GXclear) || planemask != FB_ALLONES) { try_solid: if (uxa_screen->info->check_solid && !uxa_screen->info->check_solid(&pixmap->drawable, alu, planemask)) goto err; if (!uxa_screen->info->prepare_solid(pixmap, alu, planemask, pixel)) goto err; while (nbox--) { uxa_screen->info->solid(pixmap, pBox->x1, pBox->y1, pBox->x2, pBox->y2); pBox++; } uxa_screen->info->done_solid(pixmap); } else { PicturePtr dst, src; PixmapPtr src_pixmap = NULL; xRenderColor color; int error; dst = CreatePicture(0, &pixmap->drawable, PictureMatchFormat(screen, pixmap->drawable.depth, format_for_depth(pixmap->drawable.depth)), 0, 0, serverClient, &error); if (!dst) goto err; ValidatePicture(dst); uxa_get_rgba_from_pixel(pixel, &color.red, &color.green, &color.blue, &color.alpha, format_for_depth(pixmap->drawable.depth)); src = CreateSolidPicture(0, &color, &error); if (!src) { FreePicture(dst, 0); goto err; } if (!uxa_screen->info->check_composite(PictOpSrc, src, NULL, dst, extents->x2 - extents->x1, extents->y2 - extents->y1)) { FreePicture(src, 0); FreePicture(dst, 0); goto try_solid; } if (!uxa_screen->info->check_composite_texture || !uxa_screen->info->check_composite_texture(screen, src)) { PicturePtr solid; int src_off_x, src_off_y; solid = uxa_acquire_solid(screen, src->pSourcePict); FreePicture(src, 0); src = solid; src_pixmap = uxa_get_offscreen_pixmap(src->pDrawable, &src_off_x, &src_off_y); if (!src_pixmap) { FreePicture(src, 0); FreePicture(dst, 0); goto err; } } if (!uxa_screen->info->prepare_composite(PictOpSrc, src, NULL, dst, src_pixmap, NULL, pixmap)) { FreePicture(src, 0); FreePicture(dst, 0); goto err; } while (nbox--) { uxa_screen->info->composite(pixmap, 0, 0, 0, 0, pBox->x1, pBox->y1, pBox->x2 - pBox->x1, pBox->y2 - pBox->y1); pBox++; } uxa_screen->info->done_composite(pixmap); FreePicture(src, 0); FreePicture(dst, 0); } ret = TRUE; err: REGION_TRANSLATE(screen, pRegion, -xoff, -yoff); return ret; } /* Try to do an accelerated tile of the pTile into pRegion of pDrawable. * Based on fbFillRegionTiled(), fbTile(). */ Bool uxa_fill_region_tiled(DrawablePtr pDrawable, RegionPtr pRegion, PixmapPtr pTile, DDXPointPtr pPatOrg, CARD32 planemask, CARD32 alu) { uxa_screen_t *uxa_screen = uxa_get_screen(pDrawable->pScreen); PixmapPtr pPixmap; int xoff, yoff; int tileWidth, tileHeight; int nbox = REGION_NUM_RECTS(pRegion); BoxPtr pBox = REGION_RECTS(pRegion); Bool ret = FALSE; int i; tileWidth = pTile->drawable.width; tileHeight = pTile->drawable.height; /* If we're filling with a solid color, grab it out and go to * FillRegionsolid, saving numerous copies. */ if (tileWidth == 1 && tileHeight == 1) return uxa_fill_region_solid(pDrawable, pRegion, uxa_get_pixmap_first_pixel(pTile), planemask, alu); pPixmap = uxa_get_offscreen_pixmap(pDrawable, &xoff, &yoff); if (!pPixmap || !uxa_pixmap_is_offscreen(pTile)) goto out; if (uxa_screen->info->check_copy && !uxa_screen->info->check_copy(pTile, pPixmap, alu, planemask)) return FALSE; if ((*uxa_screen->info->prepare_copy) (pTile, pPixmap, 1, 1, alu, planemask)) { if (xoff || yoff) REGION_TRANSLATE(pScreen, pRegion, xoff, yoff); for (i = 0; i < nbox; i++) { int height = pBox[i].y2 - pBox[i].y1; int dstY = pBox[i].y1; int tileY; if (alu == GXcopy) height = min(height, tileHeight); modulus(dstY - yoff - pDrawable->y - pPatOrg->y, tileHeight, tileY); while (height > 0) { int width = pBox[i].x2 - pBox[i].x1; int dstX = pBox[i].x1; int tileX; int h = tileHeight - tileY; if (alu == GXcopy) width = min(width, tileWidth); if (h > height) h = height; height -= h; modulus(dstX - xoff - pDrawable->x - pPatOrg->x, tileWidth, tileX); while (width > 0) { int w = tileWidth - tileX; if (w > width) w = width; width -= w; (*uxa_screen->info->copy) (pPixmap, tileX, tileY, dstX, dstY, w, h); dstX += w; tileX = 0; } dstY += h; tileY = 0; } } (*uxa_screen->info->done_copy) (pPixmap); if (alu != GXcopy) ret = TRUE; else { Bool more_copy = FALSE; for (i = 0; i < nbox; i++) { int dstX = pBox[i].x1 + tileWidth; int dstY = pBox[i].y1 + tileHeight; if ((dstX < pBox[i].x2) || (dstY < pBox[i].y2)) { more_copy = TRUE; break; } } if (more_copy == FALSE) ret = TRUE; if (more_copy && (*uxa_screen->info->prepare_copy) (pPixmap, pPixmap, 1, 1, alu, planemask)) { for (i = 0; i < nbox; i++) { int dstX = pBox[i].x1 + tileWidth; int dstY = pBox[i].y1 + tileHeight; int width = min(pBox[i].x2 - dstX, tileWidth); int height = min(pBox[i].y2 - pBox[i].y1, tileHeight); while (dstX < pBox[i].x2) { (*uxa_screen->info->copy) (pPixmap, pBox[i].x1, pBox[i].y1, dstX, pBox[i].y1, width, height); dstX += width; width = min(pBox[i].x2 - dstX, width * 2); } width = pBox[i].x2 - pBox[i].x1; height = min(pBox[i].y2 - dstY, tileHeight); while (dstY < pBox[i].y2) { (*uxa_screen->info->copy) (pPixmap, pBox[i].x1, pBox[i].y1, pBox[i].x1, dstY, width, height); dstY += height; height = min(pBox[i].y2 - dstY, height * 2); } } (*uxa_screen->info->done_copy) (pPixmap); ret = TRUE; } } if (xoff || yoff) REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff); } out: return ret; } /** * Accelerates GetImage for solid ZPixmap downloads from framebuffer memory. * * This is probably the only case we actually care about. The rest fall through * to migration and fbGetImage, which hopefully will result in migration pushing * the pixmap out of framebuffer. */ void uxa_get_image(DrawablePtr pDrawable, int x, int y, int w, int h, unsigned int format, unsigned long planeMask, char *d) { ScreenPtr screen = pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(screen); BoxRec Box; PixmapPtr pPix = uxa_get_drawable_pixmap(pDrawable); int xoff, yoff; Bool ok; RegionRec region; uxa_get_drawable_deltas(pDrawable, pPix, &xoff, &yoff); Box.x1 = pDrawable->y + x + xoff; Box.y1 = pDrawable->y + y + yoff; Box.x2 = Box.x1 + w; Box.y2 = Box.y1 + h; if (uxa_screen->swappedOut || uxa_screen->force_fallback) goto fallback; pPix = uxa_get_offscreen_pixmap(pDrawable, &xoff, &yoff); if (pPix == NULL || uxa_screen->info->get_image == NULL) goto fallback; /* Only cover the ZPixmap, solid copy case. */ if (format != ZPixmap || !UXA_PM_IS_SOLID(pDrawable, planeMask)) goto fallback; /* Only try to handle the 8bpp and up cases, since we don't want to * think about <8bpp. */ if (pDrawable->bitsPerPixel < 8) goto fallback; ok = uxa_screen->info->get_image(pPix, pDrawable->x + x + xoff, pDrawable->y + y + yoff, w, h, d, PixmapBytePad(w, pDrawable->depth)); if (ok) return; fallback: UXA_FALLBACK(("from %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); REGION_INIT(screen, ®ion, &Box, 1); if (uxa_prepare_access(pDrawable, ®ion, UXA_ACCESS_RO)) { fbGetImage(pDrawable, x, y, w, h, format, planeMask, d); uxa_finish_access(pDrawable); } REGION_UNINIT(screen, ®ion); return; } xserver-xorg-video-qxl-0.1.5+git20200331/src/uxa/uxa-damage.c000066400000000000000000000654231361660122400232440ustar00rootroot00000000000000/* * Copyright © 2003 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include "uxa-priv.h" #include #include #include #include #include #include #include #include #ifdef HAVE_XFONT2 #include #else #include #endif #include "uxa-damage.h" typedef struct _damageGCPriv { GCOps *ops; GCFuncs *funcs; } DamageGCPrivRec, *DamageGCPrivPtr; #define DAMAGE_VALIDATE_ENABLE 0 #define DAMAGE_DEBUG_ENABLE 0 #if DAMAGE_DEBUG_ENABLE #define DAMAGE_DEBUG(x) ErrorF x #else #define DAMAGE_DEBUG(x) #endif #define TRIM_BOX(box, pGC) if (pGC->pCompositeClip) { \ BoxPtr extents = &pGC->pCompositeClip->extents; \ if(box.x1 < extents->x1) box.x1 = extents->x1; \ if(box.x2 > extents->x2) box.x2 = extents->x2; \ if(box.y1 < extents->y1) box.y1 = extents->y1; \ if(box.y2 > extents->y2) box.y2 = extents->y2; \ } #define TRANSLATE_BOX(box, pDrawable) { \ box.x1 += pDrawable->x; \ box.x2 += pDrawable->x; \ box.y1 += pDrawable->y; \ box.y2 += pDrawable->y; \ } #define TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC) { \ TRANSLATE_BOX(box, pDrawable); \ TRIM_BOX(box, pGC); \ } #define BOX_NOT_EMPTY(box) \ (((box.x2 - box.x1) > 0) && ((box.y2 - box.y1) > 0)) #define checkGCDamage(g) ((!g->pCompositeClip || \ REGION_NOTEMPTY(d->pScreen, \ g->pCompositeClip))) #define TRIM_PICTURE_BOX(box, pDst) { \ BoxPtr extents = &pDst->pCompositeClip->extents; \ if(box.x1 < extents->x1) box.x1 = extents->x1; \ if(box.x2 > extents->x2) box.x2 = extents->x2; \ if(box.y1 < extents->y1) box.y1 = extents->y1; \ if(box.y2 > extents->y2) box.y2 = extents->y2; \ } #define checkPictureDamage(p) (REGION_NOTEMPTY(pScreen, p->pCompositeClip)) static void trim_region (RegionPtr pRegion, DrawablePtr pDrawable, int subWindowMode) { RegionRec pixClip; int draw_x = 0; int draw_y = 0; #ifdef COMPOSITE int screen_x = 0, screen_y = 0; #endif /* short circuit for empty regions */ if (!REGION_NOTEMPTY(pScreen, pRegion)) return; #ifdef COMPOSITE /* * When drawing to a pixmap which is storing window contents, * the region presented is in pixmap relative coordinates which * need to be converted to screen relative coordinates */ if (pDrawable->type != DRAWABLE_WINDOW) { screen_x = ((PixmapPtr) pDrawable)->screen_x - pDrawable->x; screen_y = ((PixmapPtr) pDrawable)->screen_y - pDrawable->y; } if (screen_x || screen_y) REGION_TRANSLATE (pScreen, pRegion, screen_x, screen_y); #endif /* Clip against any children */ if (pDrawable->type == DRAWABLE_WINDOW && ((WindowPtr)(pDrawable))->backingStore == NotUseful) { if (subWindowMode == ClipByChildren) { REGION_INTERSECT(pScreen, pRegion, pRegion, &((WindowPtr)(pDrawable))->clipList); } else if (subWindowMode == IncludeInferiors) { RegionPtr pTempRegion = NotClippedByChildren((WindowPtr)(pDrawable)); REGION_INTERSECT(pScreen, pRegion, pRegion, pTempRegion); REGION_DESTROY(pScreen, pTempRegion); } /* If subWindowMode is set to an invalid value, don't perform * any drawable-based clipping. */ } /* Clip against border or pixmap bounds */ if (pDrawable->type == DRAWABLE_WINDOW) { REGION_INTERSECT (pScreen, pRegion, pRegion, &((WindowPtr)(pDrawable))->borderClip); } else { BoxRec box; draw_x = pDrawable->x; draw_y = pDrawable->y; #ifdef COMPOSITE /* * Need to move everyone to screen coordinates * XXX what about off-screen pixmaps with non-zero x/y? */ if (!WindowDrawable(pDrawable->type)) { draw_x += ((PixmapPtr) pDrawable)->screen_x; draw_y += ((PixmapPtr) pDrawable)->screen_y; } #endif box.x1 = draw_x; box.y1 = draw_y; box.x2 = draw_x + pDrawable->width; box.y2 = draw_y + pDrawable->height; REGION_INIT(pScreen, &pixClip, &box, 1); REGION_INTERSECT (pScreen, pRegion, pRegion, &pixClip); REGION_UNINIT(pScreen, &pixClip); } /* * Move region to target coordinate space */ if (draw_x || draw_y) REGION_TRANSLATE (pScreen, pRegion, -draw_x, -draw_y); /* Now do something with the damage */ } static void add_region (RegionPtr existing, RegionPtr new, DrawablePtr pDrawable, int subWindowMode) { trim_region (new, pDrawable, subWindowMode); REGION_UNION (pDrawable->pScreen, existing, existing, new); } static void add_box (RegionPtr existing, BoxPtr box, DrawablePtr drawable, int subwindow_mode) { RegionRec region; REGION_INIT (pDrawable->pScreen, ®ion, box, 1); add_region (existing, ®ion, drawable, subwindow_mode); REGION_UNINIT (pDrawable->pScreen, ®ion); } void uxa_damage_composite (RegionPtr region, CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { if (checkPictureDamage (pDst)) { BoxRec box; box.x1 = xDst + pDst->pDrawable->x; box.y1 = yDst + pDst->pDrawable->y; box.x2 = box.x1 + width; box.y2 = box.y1 + height; TRIM_PICTURE_BOX(box, pDst); if (BOX_NOT_EMPTY(box)) add_box (region, &box, pDst->pDrawable, pDst->subWindowMode); } } void uxa_damage_glyphs (RegionPtr region, CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr *glyphs) { if (checkPictureDamage (pDst)) { int nlistTmp = nlist; GlyphListPtr listTmp = list; GlyphPtr *glyphsTmp = glyphs; int x, y; int n; GlyphPtr glyph; BoxRec box; int x1, y1, x2, y2; box.x1 = 32767; box.y1 = 32767; box.x2 = -32767; box.y2 = -32767; x = pDst->pDrawable->x; y = pDst->pDrawable->y; while (nlistTmp--) { x += listTmp->xOff; y += listTmp->yOff; n = listTmp->len; while (n--) { glyph = *glyphsTmp++; x1 = x - glyph->info.x; y1 = y - glyph->info.y; x2 = x1 + glyph->info.width; y2 = y1 + glyph->info.height; if (x1 < box.x1) box.x1 = x1; if (y1 < box.y1) box.y1 = y1; if (x2 > box.x2) box.x2 = x2; if (y2 > box.y2) box.y2 = y2; x += glyph->info.xOff; y += glyph->info.yOff; } listTmp++; } TRIM_PICTURE_BOX (box, pDst); if (BOX_NOT_EMPTY(box)) add_box (region, &box, pDst->pDrawable, pDst->subWindowMode); } } void uxa_damage_add_traps (RegionPtr region, PicturePtr pPicture, INT16 x_off, INT16 y_off, int ntrap, xTrap *traps) { if (checkPictureDamage (pPicture)) { BoxRec box; int i; int x, y; xTrap *t = traps; box.x1 = 32767; box.y1 = 32767; box.x2 = -32767; box.y2 = -32767; x = pPicture->pDrawable->x + x_off; y = pPicture->pDrawable->y + y_off; for (i = 0; i < ntrap; i++) { pixman_fixed_t l = min (t->top.l, t->bot.l); pixman_fixed_t r = max (t->top.r, t->bot.r); int x1 = x + pixman_fixed_to_int (l); int x2 = x + pixman_fixed_to_int (pixman_fixed_ceil (r)); int y1 = y + pixman_fixed_to_int (t->top.y); int y2 = y + pixman_fixed_to_int (pixman_fixed_ceil (t->bot.y)); if (x1 < box.x1) box.x1 = x1; if (x2 > box.x2) box.x2 = x2; if (y1 < box.y1) box.y1 = y1; if (y2 > box.y2) box.y2 = y2; } TRIM_PICTURE_BOX (box, pPicture); if (BOX_NOT_EMPTY(box)) add_box (region, &box, pPicture->pDrawable, pPicture->subWindowMode); } } /**********************************************************/ void uxa_damage_fill_spans (RegionPtr region, DrawablePtr pDrawable, GC *pGC, int npt, DDXPointPtr ppt, int *pwidth, int fSorted) { if (npt && checkGCDamage (pGC)) { int nptTmp = npt; DDXPointPtr pptTmp = ppt; int *pwidthTmp = pwidth; BoxRec box; box.x1 = pptTmp->x; box.x2 = box.x1 + *pwidthTmp; box.y2 = box.y1 = pptTmp->y; while(--nptTmp) { pptTmp++; pwidthTmp++; if(box.x1 > pptTmp->x) box.x1 = pptTmp->x; if(box.x2 < (pptTmp->x + *pwidthTmp)) box.x2 = pptTmp->x + *pwidthTmp; if(box.y1 > pptTmp->y) box.y1 = pptTmp->y; else if(box.y2 < pptTmp->y) box.y2 = pptTmp->y; } box.y2++; if(!pGC->miTranslate) { TRANSLATE_BOX(box, pDrawable); } TRIM_BOX(box, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); } } void uxa_damage_set_spans (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, char *pcharsrc, DDXPointPtr ppt, int *pwidth, int npt, int fSorted) { if (npt && checkGCDamage (pGC)) { DDXPointPtr pptTmp = ppt; int *pwidthTmp = pwidth; int nptTmp = npt; BoxRec box; box.x1 = pptTmp->x; box.x2 = box.x1 + *pwidthTmp; box.y2 = box.y1 = pptTmp->y; while(--nptTmp) { pptTmp++; pwidthTmp++; if(box.x1 > pptTmp->x) box.x1 = pptTmp->x; if(box.x2 < (pptTmp->x + *pwidthTmp)) box.x2 = pptTmp->x + *pwidthTmp; if(box.y1 > pptTmp->y) box.y1 = pptTmp->y; else if(box.y2 < pptTmp->y) box.y2 = pptTmp->y; } box.y2++; if(!pGC->miTranslate) { TRANSLATE_BOX(box, pDrawable); } TRIM_BOX(box, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); } } void uxa_damage_put_image (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, int w, int h, int leftPad, int format, char *pImage) { if (checkGCDamage (pGC)) { BoxRec box; box.x1 = x + pDrawable->x; box.x2 = box.x1 + w; box.y1 = y + pDrawable->y; box.y2 = box.y1 + h; TRIM_BOX(box, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); } } void uxa_damage_copy_area(RegionPtr region, DrawablePtr pSrc, DrawablePtr pDst, GC *pGC, int srcx, int srcy, int width, int height, int dstx, int dsty) { if (checkGCDamage (pGC)) { BoxRec box; box.x1 = dstx + pDst->x; box.x2 = box.x1 + width; box.y1 = dsty + pDst->y; box.y2 = box.y1 + height; TRIM_BOX(box, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDst, pGC->subWindowMode); } } void uxa_damage_copy_plane (RegionPtr region, DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, int srcx, int srcy, int width, int height, int dstx, int dsty, unsigned long bitPlane) { if (checkGCDamage (pGC)) { BoxRec box; box.x1 = dstx + pDst->x; box.x2 = box.x1 + width; box.y1 = dsty + pDst->y; box.y2 = box.y1 + height; TRIM_BOX(box, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDst, pGC->subWindowMode); } } void uxa_damage_poly_point (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, xPoint *ppt) { if (npt && checkGCDamage (pGC)) { BoxRec box; int nptTmp = npt; xPoint *pptTmp = ppt; box.x2 = box.x1 = pptTmp->x; box.y2 = box.y1 = pptTmp->y; /* this could be slow if the points were spread out */ while(--nptTmp) { pptTmp++; if(box.x1 > pptTmp->x) box.x1 = pptTmp->x; else if(box.x2 < pptTmp->x) box.x2 = pptTmp->x; if(box.y1 > pptTmp->y) box.y1 = pptTmp->y; else if(box.y2 < pptTmp->y) box.y2 = pptTmp->y; } box.x2++; box.y2++; TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); } } void uxa_damage_poly_lines (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr ppt) { if (npt && checkGCDamage (pGC)) { int nptTmp = npt; DDXPointPtr pptTmp = ppt; BoxRec box; int extra = pGC->lineWidth >> 1; box.x2 = box.x1 = pptTmp->x; box.y2 = box.y1 = pptTmp->y; if(nptTmp > 1) { if(pGC->joinStyle == JoinMiter) extra = 6 * pGC->lineWidth; else if(pGC->capStyle == CapProjecting) extra = pGC->lineWidth; } if(mode == CoordModePrevious) { int x = box.x1; int y = box.y1; while(--nptTmp) { pptTmp++; x += pptTmp->x; y += pptTmp->y; if(box.x1 > x) box.x1 = x; else if(box.x2 < x) box.x2 = x; if(box.y1 > y) box.y1 = y; else if(box.y2 < y) box.y2 = y; } } else { while(--nptTmp) { pptTmp++; if(box.x1 > pptTmp->x) box.x1 = pptTmp->x; else if(box.x2 < pptTmp->x) box.x2 = pptTmp->x; if(box.y1 > pptTmp->y) box.y1 = pptTmp->y; else if(box.y2 < pptTmp->y) box.y2 = pptTmp->y; } } box.x2++; box.y2++; if(extra) { box.x1 -= extra; box.x2 += extra; box.y1 -= extra; box.y2 += extra; } TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); } } void uxa_damage_poly_segment (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int nSeg, xSegment *pSeg) { if (nSeg && checkGCDamage (pGC)) { BoxRec box; int extra = pGC->lineWidth; int nsegTmp = nSeg; xSegment *pSegTmp = pSeg; if(pGC->capStyle != CapProjecting) extra >>= 1; if(pSegTmp->x2 > pSegTmp->x1) { box.x1 = pSegTmp->x1; box.x2 = pSegTmp->x2; } else { box.x2 = pSegTmp->x1; box.x1 = pSegTmp->x2; } if(pSegTmp->y2 > pSegTmp->y1) { box.y1 = pSegTmp->y1; box.y2 = pSegTmp->y2; } else { box.y2 = pSegTmp->y1; box.y1 = pSegTmp->y2; } while(--nsegTmp) { pSegTmp++; if(pSegTmp->x2 > pSegTmp->x1) { if(pSegTmp->x1 < box.x1) box.x1 = pSegTmp->x1; if(pSegTmp->x2 > box.x2) box.x2 = pSegTmp->x2; } else { if(pSegTmp->x2 < box.x1) box.x1 = pSegTmp->x2; if(pSegTmp->x1 > box.x2) box.x2 = pSegTmp->x1; } if(pSegTmp->y2 > pSegTmp->y1) { if(pSegTmp->y1 < box.y1) box.y1 = pSegTmp->y1; if(pSegTmp->y2 > box.y2) box.y2 = pSegTmp->y2; } else { if(pSegTmp->y2 < box.y1) box.y1 = pSegTmp->y2; if(pSegTmp->y1 > box.y2) box.y2 = pSegTmp->y1; } } box.x2++; box.y2++; if(extra) { box.x1 -= extra; box.x2 += extra; box.y1 -= extra; box.y2 += extra; } TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); } } void uxa_damage_poly_rectangle (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int nRects, xRectangle *pRects) { if (nRects && checkGCDamage (pGC)) { BoxRec box; int offset1, offset2, offset3; int nRectsTmp = nRects; xRectangle *pRectsTmp = pRects; offset2 = pGC->lineWidth; if(!offset2) offset2 = 1; offset1 = offset2 >> 1; offset3 = offset2 - offset1; while(nRectsTmp--) { box.x1 = pRectsTmp->x - offset1; box.y1 = pRectsTmp->y - offset1; box.x2 = box.x1 + pRectsTmp->width + offset2; box.y2 = box.y1 + offset2; TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); box.x1 = pRectsTmp->x - offset1; box.y1 = pRectsTmp->y + offset3; box.x2 = box.x1 + offset2; box.y2 = box.y1 + pRectsTmp->height - offset2; TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); box.x1 = pRectsTmp->x + pRectsTmp->width - offset1; box.y1 = pRectsTmp->y + offset3; box.x2 = box.x1 + offset2; box.y2 = box.y1 + pRectsTmp->height - offset2; TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); box.x1 = pRectsTmp->x - offset1; box.y1 = pRectsTmp->y + pRectsTmp->height - offset1; box.x2 = box.x1 + pRectsTmp->width + offset2; box.y2 = box.y1 + offset2; TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); pRectsTmp++; } } } void uxa_damage_poly_arc (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int nArcs, xArc *pArcs) { if (nArcs && checkGCDamage (pGC)) { int extra = pGC->lineWidth >> 1; BoxRec box; int nArcsTmp = nArcs; xArc *pArcsTmp = pArcs; box.x1 = pArcsTmp->x; box.x2 = box.x1 + pArcsTmp->width; box.y1 = pArcsTmp->y; box.y2 = box.y1 + pArcsTmp->height; while(--nArcsTmp) { pArcsTmp++; if(box.x1 > pArcsTmp->x) box.x1 = pArcsTmp->x; if(box.x2 < (pArcsTmp->x + pArcsTmp->width)) box.x2 = pArcsTmp->x + pArcsTmp->width; if(box.y1 > pArcsTmp->y) box.y1 = pArcsTmp->y; if(box.y2 < (pArcsTmp->y + pArcsTmp->height)) box.y2 = pArcsTmp->y + pArcsTmp->height; } if(extra) { box.x1 -= extra; box.x2 += extra; box.y1 -= extra; box.y2 += extra; } box.x2++; box.y2++; TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); } } void uxa_damage_fill_polygon (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int shape, int mode, int npt, DDXPointPtr ppt) { if (npt > 2 && checkGCDamage (pGC)) { DDXPointPtr pptTmp = ppt; int nptTmp = npt; BoxRec box; box.x2 = box.x1 = pptTmp->x; box.y2 = box.y1 = pptTmp->y; if(mode != CoordModeOrigin) { int x = box.x1; int y = box.y1; while(--nptTmp) { pptTmp++; x += pptTmp->x; y += pptTmp->y; if(box.x1 > x) box.x1 = x; else if(box.x2 < x) box.x2 = x; if(box.y1 > y) box.y1 = y; else if(box.y2 < y) box.y2 = y; } } else { while(--nptTmp) { pptTmp++; if(box.x1 > pptTmp->x) box.x1 = pptTmp->x; else if(box.x2 < pptTmp->x) box.x2 = pptTmp->x; if(box.y1 > pptTmp->y) box.y1 = pptTmp->y; else if(box.y2 < pptTmp->y) box.y2 = pptTmp->y; } } box.x2++; box.y2++; TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); } } void uxa_damage_poly_fill_rect (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int nRects, xRectangle *pRects) { if (nRects && checkGCDamage (pGC)) { int i; for (i = 0; i < nRects; ++i) { xRectangle *rect = &(pRects[i]); BoxRec box; box.x1 = rect->x; box.x2 = rect->x + rect->width; box.y1 = rect->y; box.y2 = rect->y + rect->height; TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); } } } void uxa_damage_poly_fill_arc (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int nArcs, xArc *pArcs) { if (nArcs && checkGCDamage (pGC)) { BoxRec box; int nArcsTmp = nArcs; xArc *pArcsTmp = pArcs; box.x1 = pArcsTmp->x; box.x2 = box.x1 + pArcsTmp->width; box.y1 = pArcsTmp->y; box.y2 = box.y1 + pArcsTmp->height; while(--nArcsTmp) { pArcsTmp++; if(box.x1 > pArcsTmp->x) box.x1 = pArcsTmp->x; if(box.x2 < (pArcsTmp->x + pArcsTmp->width)) box.x2 = pArcsTmp->x + pArcsTmp->width; if(box.y1 > pArcsTmp->y) box.y1 = pArcsTmp->y; if(box.y2 < (pArcsTmp->y + pArcsTmp->height)) box.y2 = pArcsTmp->y + pArcsTmp->height; } TRIM_AND_TRANSLATE_BOX(box, pDrawable, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); } } /* * general Poly/Image text function. Extract glyph information, * compute bounding box and remove cursor if it is overlapped. */ void uxa_damage_chars (RegionPtr region, DrawablePtr pDrawable, FontPtr font, int x, int y, unsigned int n, CharInfoPtr *charinfo, Bool imageblt, int subWindowMode) { ExtentInfoRec extents; BoxRec box; #ifdef HAVE_XFONT2 xfont2_query_glyph_extents(font, charinfo, n, &extents); #else QueryGlyphExtents(font, charinfo, n, &extents); #endif if (imageblt) { if (extents.overallWidth > extents.overallRight) extents.overallRight = extents.overallWidth; if (extents.overallWidth < extents.overallLeft) extents.overallLeft = extents.overallWidth; if (extents.overallLeft > 0) extents.overallLeft = 0; if (extents.fontAscent > extents.overallAscent) extents.overallAscent = extents.fontAscent; if (extents.fontDescent > extents.overallDescent) extents.overallDescent = extents.fontDescent; } box.x1 = x + extents.overallLeft; box.y1 = y - extents.overallAscent; box.x2 = x + extents.overallRight; box.y2 = y + extents.overallDescent; add_box (region, &box, pDrawable, subWindowMode); } /* * values for textType: */ #define TT_POLY8 0 #define TT_IMAGE8 1 #define TT_POLY16 2 #define TT_IMAGE16 3 int uxa_damage_text (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned long count, char *chars, FontEncoding fontEncoding, Bool textType) { CharInfoPtr *charinfo; CharInfoPtr *info; unsigned long i; unsigned int n; int w; Bool imageblt; imageblt = (textType == TT_IMAGE8) || (textType == TT_IMAGE16); charinfo = malloc(count * sizeof(CharInfoPtr)); if (!charinfo) return x; GetGlyphs(pGC->font, count, (unsigned char *)chars, fontEncoding, &i, charinfo); n = (unsigned int)i; w = 0; if (!imageblt) for (info = charinfo; i--; info++) w += (*info)->metrics.characterWidth; if (n != 0) { uxa_damage_chars (region, pDrawable, pGC->font, x + pDrawable->x, y + pDrawable->y, n, charinfo, imageblt, pGC->subWindowMode); } free(charinfo); return x + w; } int uxa_damage_poly_text_8(RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, char *chars) { if (checkGCDamage (pGC)) x = uxa_damage_text (region, pDrawable, pGC, x, y, (unsigned long) count, chars, Linear8Bit, TT_POLY8); return x; } int uxa_damage_poly_text_16 (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, unsigned short *chars) { if (checkGCDamage (pGC)) x = uxa_damage_text (region, pDrawable, pGC, x, y, (unsigned long) count, (char *) chars, FONTLASTROW(pGC->font) == 0 ? Linear16Bit : TwoD16Bit, TT_POLY16); return x; } void uxa_damage_image_text_8(RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, char *chars) { if (checkGCDamage (pGC)) uxa_damage_text (region, pDrawable, pGC, x, y, (unsigned long) count, chars, Linear8Bit, TT_IMAGE8); } void uxa_damage_image_text_16 (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, unsigned short *chars) { if (checkGCDamage (pGC)) uxa_damage_text (region, pDrawable, pGC, x, y, (unsigned long) count, (char *) chars, FONTLASTROW(pGC->font) == 0 ? Linear16Bit : TwoD16Bit, TT_IMAGE16); } void uxa_damage_image_glyph_blt(RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr *ppci, pointer pglyphBase) { uxa_damage_chars (region, pDrawable, pGC->font, x + pDrawable->x, y + pDrawable->y, nglyph, ppci, TRUE, pGC->subWindowMode); } void uxa_damage_poly_glyph_blt(RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr *ppci, pointer pglyphBase) { uxa_damage_chars (region, pDrawable, pGC->font, x + pDrawable->x, y + pDrawable->y, nglyph, ppci, FALSE, pGC->subWindowMode); } void uxa_damage_push_pixels (RegionPtr region, GCPtr pGC, PixmapPtr pBitMap, DrawablePtr pDrawable, int dx, int dy, int xOrg, int yOrg) { if(checkGCDamage (pGC)) { BoxRec box; box.x1 = xOrg; box.y1 = yOrg; if(!pGC->miTranslate) { box.x1 += pDrawable->x; box.y1 += pDrawable->y; } box.x2 = box.x1 + dx; box.y2 = box.y1 + dy; TRIM_BOX(box, pGC); if(BOX_NOT_EMPTY(box)) add_box (region, &box, pDrawable, pGC->subWindowMode); } } void uxa_damage_copy_window(RegionPtr region, WindowPtr pWindow, DDXPointRec ptOldOrg, RegionPtr prgnSrc) { #if 0 ScreenPtr pScreen = pWindow->drawable.pScreen; damageScrPriv(pScreen); int dx = pWindow->drawable.x - ptOldOrg.x; int dy = pWindow->drawable.y - ptOldOrg.y; /* * The region comes in source relative, but the damage occurs * at the destination location. Translate back and forth. */ REGION_TRANSLATE (pScreen, prgnSrc, dx, dy); damageRegionAppend (&pWindow->drawable, prgnSrc, FALSE, -1); REGION_TRANSLATE (pScreen, prgnSrc, -dx, -dy); #endif /* FIXME */ } xserver-xorg-video-qxl-0.1.5+git20200331/src/uxa/uxa-damage.h000066400000000000000000000115351361660122400232440ustar00rootroot00000000000000void uxa_damage_composite (RegionPtr region, CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height); void uxa_damage_glyphs (RegionPtr region, CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr *glyphs); void uxa_damage_add_traps (RegionPtr region, PicturePtr pPicture, INT16 x_off, INT16 y_off, int ntrap, xTrap *traps); void uxa_damage_fill_spans (RegionPtr region, DrawablePtr pDrawable, GC *pGC, int npt, DDXPointPtr ppt, int *pwidth, int fSorted); void uxa_damage_set_spans (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, char *pcharsrc, DDXPointPtr ppt, int *pwidth, int npt, int fSorted); void uxa_damage_put_image (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, int w, int h, int leftPad, int format, char *pImage); void uxa_damage_copy_area(RegionPtr region, DrawablePtr pSrc, DrawablePtr pDst, GC *pGC, int srcx, int srcy, int width, int height, int dstx, int dsty); void uxa_damage_copy_plane (RegionPtr region, DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, int srcx, int srcy, int width, int height, int dstx, int dsty, unsigned long bitPlane); void uxa_damage_poly_point (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, xPoint *ppt); void uxa_damage_poly_lines (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr ppt); void uxa_damage_poly_segment (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int nSeg, xSegment *pSeg); void uxa_damage_poly_rectangle (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int nRects, xRectangle *pRects); void uxa_damage_poly_arc (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int nArcs, xArc *pArcs); void uxa_damage_fill_polygon (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int shape, int mode, int npt, DDXPointPtr ppt); void uxa_damage_poly_fill_rect (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int nRects, xRectangle *pRects); void uxa_damage_poly_fill_arc (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int nArcs, xArc *pArcs); void uxa_damage_chars (RegionPtr region, DrawablePtr pDrawable, FontPtr font, int x, int y, unsigned int n, CharInfoPtr *charinfo, Bool imageblt, int subWindowMode); int uxa_damage_text (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned long count, char *chars, FontEncoding fontEncoding, Bool textType); int uxa_damage_poly_text_8(RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, char *chars); int uxa_damage_poly_text_16 (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, unsigned short *chars); void uxa_damage_image_text_8(RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, char *chars); void uxa_damage_image_text_16 (RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, unsigned short *chars); void uxa_damage_image_glyph_blt(RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr *ppci, pointer pglyphBase); void uxa_damage_poly_glyph_blt(RegionPtr region, DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr *ppci, pointer pglyphBase); void uxa_damage_push_pixels (RegionPtr region, GCPtr pGC, PixmapPtr pBitMap, DrawablePtr pDrawable, int dx, int dy, int xOrg, int yOrg); void uxa_damage_copy_window (RegionPtr region, WindowPtr pWindow, DDXPointRec ptOldOrg, RegionPtr prgnSrc); xserver-xorg-video-qxl-0.1.5+git20200331/src/uxa/uxa-glyphs.c000066400000000000000000000732461361660122400233360ustar00rootroot00000000000000/* * Copyright © 2010 Intel Corporation * Partly based on code Copyright © 2008 Red Hat, Inc. * Partly based on code Copyright © 2000 SuSE, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Intel not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Intel makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INTEL * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * Red Hat DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL Red Hat * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Chris Wilson * Based on code by: Keith Packard and Owen Taylor */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include "uxa-priv.h" #include "mipict.h" /* Width of the pixmaps we use for the caches; this should be less than * max texture size of the driver; this may need to actually come from * the driver. */ #define CACHE_PICTURE_SIZE 1024 #define GLYPH_MIN_SIZE 8 #define GLYPH_MAX_SIZE 64 #define GLYPH_CACHE_SIZE (CACHE_PICTURE_SIZE * CACHE_PICTURE_SIZE / (GLYPH_MIN_SIZE * GLYPH_MIN_SIZE)) struct uxa_glyph { uxa_glyph_cache_t *cache; uint16_t x, y; uint16_t size, pos; }; #if HAS_DEVPRIVATEKEYREC static DevPrivateKeyRec uxa_glyph_key; #else static int uxa_glyph_key; #endif static inline struct uxa_glyph *uxa_glyph_get_private(GlyphPtr glyph) { #if HAS_DEVPRIVATEKEYREC return dixGetPrivate(&glyph->devPrivates, &uxa_glyph_key); #else return dixLookupPrivate(&glyph->devPrivates, &uxa_glyph_key); #endif } static inline void uxa_glyph_set_private(GlyphPtr glyph, struct uxa_glyph *priv) { dixSetPrivate(&glyph->devPrivates, &uxa_glyph_key, priv); } #define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0) static void uxa_unrealize_glyph_caches(ScreenPtr pScreen) { uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); int i; for (i = 0; i < UXA_NUM_GLYPH_CACHE_FORMATS; i++) { uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[i]; if (cache->picture) FreePicture(cache->picture, 0); if (cache->glyphs) free(cache->glyphs); } } void uxa_glyphs_fini(ScreenPtr pScreen) { uxa_unrealize_glyph_caches(pScreen); } /* All caches for a single format share a single pixmap for glyph storage, * allowing mixing glyphs of different sizes without paying a penalty * for switching between source pixmaps. (Note that for a size of font * right at the border between two sizes, we might be switching for almost * every glyph.) * * This function allocates the storage pixmap, and then fills in the * rest of the allocated structures for all caches with the given format. */ static Bool uxa_realize_glyph_caches(ScreenPtr pScreen) { uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); unsigned int formats[] = { PIXMAN_a8, PIXMAN_a8r8g8b8, }; int i; memset(uxa_screen->glyphCaches, 0, sizeof(uxa_screen->glyphCaches)); for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) { uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[i]; PixmapPtr pixmap; PicturePtr picture; CARD32 component_alpha; int depth = PIXMAN_FORMAT_DEPTH(formats[i]); int error; PictFormatPtr pPictFormat = PictureMatchFormat(pScreen, depth, formats[i]); if (!pPictFormat) goto bail; /* Now allocate the pixmap and picture */ pixmap = pScreen->CreatePixmap(pScreen, CACHE_PICTURE_SIZE, CACHE_PICTURE_SIZE, depth, 0 /* INTEL_CREATE_PIXMAP_TILING_X -- FIXME */); if (!pixmap) goto bail; #if 0 assert (uxa_pixmap_is_offscreen(pixmap)); #endif component_alpha = NeedsComponent(pPictFormat->format); picture = CreatePicture(0, &pixmap->drawable, pPictFormat, CPComponentAlpha, &component_alpha, serverClient, &error); pScreen->DestroyPixmap(pixmap); if (!picture) goto bail; ValidatePicture(picture); cache->picture = picture; cache->glyphs = calloc(sizeof(GlyphPtr), GLYPH_CACHE_SIZE); if (!cache->glyphs) goto bail; cache->evict = rand() % GLYPH_CACHE_SIZE; } assert(i == UXA_NUM_GLYPH_CACHE_FORMATS); return TRUE; bail: uxa_unrealize_glyph_caches(pScreen); return FALSE; } Bool uxa_glyphs_init(ScreenPtr pScreen) { #if HAS_DIXREGISTERPRIVATEKEY if (!dixRegisterPrivateKey(&uxa_glyph_key, PRIVATE_GLYPH, 0)) return FALSE; #else if (!dixRequestPrivate(&uxa_glyph_key, 0)) return FALSE; #endif if (!uxa_realize_glyph_caches(pScreen)) return FALSE; return TRUE; } /* The most efficient thing to way to upload the glyph to the screen * is to use CopyArea; uxa pixmaps are always offscreen. */ static void uxa_glyph_cache_upload_glyph(ScreenPtr screen, uxa_glyph_cache_t * cache, GlyphPtr glyph, int x, int y) { PicturePtr pGlyphPicture = GetGlyphPicture(glyph, screen); PixmapPtr pGlyphPixmap = (PixmapPtr) pGlyphPicture->pDrawable; PixmapPtr pCachePixmap = (PixmapPtr) cache->picture->pDrawable; PixmapPtr scratch; GCPtr gc; gc = GetScratchGC(pCachePixmap->drawable.depth, screen); if (!gc) return; ValidateGC(&pCachePixmap->drawable, gc); scratch = pGlyphPixmap; /* Create a temporary bo to stream the updates to the cache */ if (pGlyphPixmap->drawable.depth != pCachePixmap->drawable.depth || !uxa_pixmap_is_offscreen(scratch)) { scratch = screen->CreatePixmap(screen, glyph->info.width, glyph->info.height, pCachePixmap->drawable.depth, UXA_CREATE_PIXMAP_FOR_MAP); if (scratch) { if (pGlyphPixmap->drawable.depth != pCachePixmap->drawable.depth) { PicturePtr picture; int error; picture = CreatePicture(0, &scratch->drawable, PictureMatchFormat(screen, pCachePixmap->drawable.depth, cache->picture->format), 0, NULL, serverClient, &error); if (picture) { ValidatePicture(picture); uxa_composite(PictOpSrc, pGlyphPicture, NULL, picture, 0, 0, 0, 0, 0, 0, glyph->info.width, glyph->info.height); FreePicture(picture, 0); } } else { uxa_copy_area(&pGlyphPixmap->drawable, &scratch->drawable, gc, 0, 0, glyph->info.width, glyph->info.height, 0, 0); } } else { scratch = pGlyphPixmap; } } uxa_copy_area(&scratch->drawable, &pCachePixmap->drawable, gc, 0, 0, glyph->info.width, glyph->info.height, x, y); if (scratch != pGlyphPixmap) screen->DestroyPixmap(scratch); FreeScratchGC(gc); } void uxa_glyph_unrealize(ScreenPtr pScreen, GlyphPtr pGlyph) { struct uxa_glyph *priv; priv = uxa_glyph_get_private(pGlyph); if (priv == NULL) return; priv->cache->glyphs[priv->pos] = NULL; uxa_glyph_set_private(pGlyph, NULL); free(priv); } /* Cut and paste from render/glyph.c - probably should export it instead */ static void uxa_glyph_extents(int nlist, GlyphListPtr list, GlyphPtr * glyphs, BoxPtr extents) { int x1, x2, y1, y2; int x, y, n; x1 = y1 = MAXSHORT; x2 = y2 = MINSHORT; x = y = 0; while (nlist--) { x += list->xOff; y += list->yOff; n = list->len; list++; while (n--) { GlyphPtr glyph = *glyphs++; int v; v = x - glyph->info.x; if (v < x1) x1 = v; v += glyph->info.width; if (v > x2) x2 = v; v = y - glyph->info.y; if (v < y1) y1 = v; v += glyph->info.height; if (v > y2) y2 = v; x += glyph->info.xOff; y += glyph->info.yOff; } } extents->x1 = x1 < MINSHORT ? MINSHORT : x1; extents->x2 = x2 > MAXSHORT ? MAXSHORT : x2; extents->y1 = y1 < MINSHORT ? MINSHORT : y1; extents->y2 = y2 > MAXSHORT ? MAXSHORT : y2; } /** * Returns TRUE if the glyphs in the lists intersect. Only checks based on * bounding box, which appears to be good enough to catch most cases at least. */ static Bool uxa_glyphs_intersect(int nlist, GlyphListPtr list, GlyphPtr * glyphs) { int x1, x2, y1, y2; int n; int x, y; BoxRec extents; Bool first = TRUE; x = 0; y = 0; extents.x1 = 0; extents.y1 = 0; extents.x2 = 0; extents.y2 = 0; while (nlist--) { x += list->xOff; y += list->yOff; n = list->len; list++; while (n--) { GlyphPtr glyph = *glyphs++; if (glyph->info.width == 0 || glyph->info.height == 0) { x += glyph->info.xOff; y += glyph->info.yOff; continue; } x1 = x - glyph->info.x; if (x1 < MINSHORT) x1 = MINSHORT; y1 = y - glyph->info.y; if (y1 < MINSHORT) y1 = MINSHORT; x2 = x1 + glyph->info.width; if (x2 > MAXSHORT) x2 = MAXSHORT; y2 = y1 + glyph->info.height; if (y2 > MAXSHORT) y2 = MAXSHORT; if (first) { extents.x1 = x1; extents.y1 = y1; extents.x2 = x2; extents.y2 = y2; first = FALSE; } else { if (x1 < extents.x2 && x2 > extents.x1 && y1 < extents.y2 && y2 > extents.y1) { return TRUE; } if (x1 < extents.x1) extents.x1 = x1; if (x2 > extents.x2) extents.x2 = x2; if (y1 < extents.y1) extents.y1 = y1; if (y2 > extents.y2) extents.y2 = y2; } x += glyph->info.xOff; y += glyph->info.yOff; } } return FALSE; } static void uxa_check_glyphs(CARD8 op, PicturePtr src, PicturePtr dst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr * glyphs) { ScreenPtr pScreen = dst->pDrawable->pScreen; pixman_image_t *image; PixmapPtr scratch; PicturePtr mask; int width = 0, height = 0; int x, y, n; int xDst = list->xOff, yDst = list->yOff; BoxRec extents = { 0, 0, 0, 0 }; if (maskFormat) { pixman_format_code_t format; CARD32 component_alpha; int error; uxa_glyph_extents(nlist, list, glyphs, &extents); if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1) return; width = extents.x2 - extents.x1; height = extents.y2 - extents.y1; format = maskFormat->format | (BitsPerPixel(maskFormat->depth) << 24); image = pixman_image_create_bits(format, width, height, NULL, 0); if (!image) return; scratch = GetScratchPixmapHeader(dst->pDrawable->pScreen, width, height, PIXMAN_FORMAT_DEPTH(format), PIXMAN_FORMAT_BPP(format), pixman_image_get_stride(image), pixman_image_get_data(image)); if (!scratch) { pixman_image_unref(image); return; } component_alpha = NeedsComponent(maskFormat->format); mask = CreatePicture(0, &scratch->drawable, maskFormat, CPComponentAlpha, &component_alpha, serverClient, &error); if (!mask) { FreeScratchPixmapHeader(scratch); pixman_image_unref(image); return; } ValidatePicture(mask); x = -extents.x1; y = -extents.y1; } else { mask = dst; x = 0; y = 0; } while (nlist--) { x += list->xOff; y += list->yOff; n = list->len; while (n--) { GlyphPtr glyph = *glyphs++; PicturePtr g = GetGlyphPicture(glyph, pScreen); if (g) { if (maskFormat) { CompositePicture(PictOpAdd, g, NULL, mask, 0, 0, 0, 0, x - glyph->info.x, y - glyph->info.y, glyph->info.width, glyph->info.height); } else { CompositePicture(op, src, g, dst, xSrc + (x - glyph->info.x) - xDst, ySrc + (y - glyph->info.y) - yDst, 0, 0, x - glyph->info.x, y - glyph->info.y, glyph->info.width, glyph->info.height); } } x += glyph->info.xOff; y += glyph->info.yOff; } list++; } if (maskFormat) { x = extents.x1; y = extents.y1; CompositePicture(op, src, mask, dst, xSrc + x - xDst, ySrc + y - yDst, 0, 0, x, y, width, height); FreePicture(mask, 0); FreeScratchPixmapHeader(scratch); pixman_image_unref(image); } } static inline unsigned int uxa_glyph_size_to_count(int size) { size /= GLYPH_MIN_SIZE; return size * size; } static inline unsigned int uxa_glyph_count_to_mask(int count) { return ~(count - 1); } static inline unsigned int uxa_glyph_size_to_mask(int size) { return uxa_glyph_count_to_mask(uxa_glyph_size_to_count(size)); } static PicturePtr uxa_glyph_cache(ScreenPtr screen, GlyphPtr glyph, int *out_x, int *out_y) { uxa_screen_t *uxa_screen = uxa_get_screen(screen); PicturePtr glyph_picture = GetGlyphPicture(glyph, screen); uxa_glyph_cache_t *cache = &uxa_screen->glyphCaches[PICT_FORMAT_RGB(glyph_picture->format) != 0]; struct uxa_glyph *priv = NULL; int size, mask, pos, s; if (glyph->info.width > GLYPH_MAX_SIZE || glyph->info.height > GLYPH_MAX_SIZE) return NULL; for (size = GLYPH_MIN_SIZE; size <= GLYPH_MAX_SIZE; size *= 2) if (glyph->info.width <= size && glyph->info.height <= size) break; s = uxa_glyph_size_to_count(size); mask = uxa_glyph_count_to_mask(s); pos = (cache->count + s - 1) & mask; if (pos < GLYPH_CACHE_SIZE) { cache->count = pos + s; } else { for (s = size; s <= GLYPH_MAX_SIZE; s *= 2) { int i = cache->evict & uxa_glyph_size_to_mask(s); GlyphPtr evicted = cache->glyphs[i]; if (evicted == NULL) continue; priv = uxa_glyph_get_private(evicted); if (priv->size >= s) { cache->glyphs[i] = NULL; uxa_glyph_set_private(evicted, NULL); pos = cache->evict & uxa_glyph_size_to_mask(size); } else priv = NULL; break; } if (priv == NULL) { int count = uxa_glyph_size_to_count(size); mask = uxa_glyph_count_to_mask(count); pos = cache->evict & mask; for (s = 0; s < count; s++) { GlyphPtr evicted = cache->glyphs[pos + s]; if (evicted != NULL) { if (priv != NULL) free(priv); priv = uxa_glyph_get_private(evicted); uxa_glyph_set_private(evicted, NULL); cache->glyphs[pos + s] = NULL; } } } /* And pick a new eviction position */ cache->evict = rand() % GLYPH_CACHE_SIZE; } if (priv == NULL) { priv = malloc(sizeof(struct uxa_glyph)); if (priv == NULL) return NULL; } uxa_glyph_set_private(glyph, priv); cache->glyphs[pos] = glyph; priv->cache = cache; priv->size = size; priv->pos = pos; s = pos / ((GLYPH_MAX_SIZE / GLYPH_MIN_SIZE) * (GLYPH_MAX_SIZE / GLYPH_MIN_SIZE)); priv->x = s % (CACHE_PICTURE_SIZE / GLYPH_MAX_SIZE) * GLYPH_MAX_SIZE; priv->y = (s / (CACHE_PICTURE_SIZE / GLYPH_MAX_SIZE)) * GLYPH_MAX_SIZE; for (s = GLYPH_MIN_SIZE; s < GLYPH_MAX_SIZE; s *= 2) { if (pos & 1) priv->x += s; if (pos & 2) priv->y += s; pos >>= 2; } uxa_glyph_cache_upload_glyph(screen, cache, glyph, priv->x, priv->y); *out_x = priv->x; *out_y = priv->y; return cache->picture; } static int uxa_glyphs_to_dst(CARD8 op, PicturePtr pSrc, PicturePtr pDst, INT16 src_x, INT16 src_y, INT16 xDst, INT16 yDst, int nlist, GlyphListPtr list, GlyphPtr * glyphs, BoxPtr extents) { ScreenPtr screen = pDst->pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(screen); PixmapPtr src_pixmap, dst_pixmap; PicturePtr localSrc, glyph_atlas; int x, y, n, nrect; BoxRec box; if (uxa_screen->info->check_composite_texture && uxa_screen->info->check_composite_texture(screen, pSrc)) { if (pSrc->pDrawable) { int src_off_x, src_off_y; src_pixmap = uxa_get_offscreen_pixmap(pSrc->pDrawable, &src_off_x, &src_off_y); if (src_pixmap == NULL) return -1; src_x += pSrc->pDrawable->x + src_off_x; src_y += pSrc->pDrawable->y + src_off_y; } else { src_pixmap = NULL; } localSrc = pSrc; } else { int width, height; if (extents == NULL) { uxa_glyph_extents(nlist, list, glyphs, &box); extents = &box; } width = extents->x2 - extents->x1; height = extents->y2 - extents->y1; if (width == 0 || height == 0) return 0; if (pSrc->pDrawable) { int src_off_x, src_off_y; src_off_x = extents->x1 - xDst; src_off_y = extents->y1 - yDst; localSrc = uxa_acquire_drawable(screen, pSrc, src_x + src_off_x, src_y + src_off_y, width, height, &src_x, &src_y); if (uxa_screen->info->check_composite_texture && !uxa_screen->info->check_composite_texture(screen, localSrc)) { if (localSrc != pSrc) FreePicture(localSrc, 0); return -1; } src_pixmap = uxa_get_offscreen_pixmap(localSrc->pDrawable, &src_off_x, &src_off_y); if (src_pixmap == NULL) { if (localSrc != pSrc) FreePicture(localSrc, 0); return -1; } src_x += localSrc->pDrawable->x + src_off_x; src_y += localSrc->pDrawable->y + src_off_y; } else { localSrc = uxa_acquire_pattern(screen, pSrc, PIXMAN_a8r8g8b8, 0, 0, width, height); if (!localSrc) return 1; src_pixmap = uxa_get_drawable_pixmap(localSrc->pDrawable); if (src_pixmap == NULL) { FreePicture(localSrc, 0); return -1; } src_x = src_y = 0; } } dst_pixmap = uxa_get_offscreen_pixmap(pDst->pDrawable, &x, &y); x += xDst + pDst->pDrawable->x - list->xOff; y += yDst + pDst->pDrawable->y - list->yOff; glyph_atlas = NULL; while (nlist--) { x += list->xOff; y += list->yOff; n = list->len; while (n--) { GlyphPtr glyph = *glyphs++; PicturePtr this_atlas; int mask_x, mask_y; struct uxa_glyph *priv; if (glyph->info.width == 0 || glyph->info.height == 0) goto next_glyph; priv = uxa_glyph_get_private(glyph); if (priv != NULL) { mask_x = priv->x; mask_y = priv->y; this_atlas = priv->cache->picture; } else { if (glyph_atlas) { uxa_screen->info->done_composite(dst_pixmap); glyph_atlas = NULL; } this_atlas = uxa_glyph_cache(screen, glyph, &mask_x, &mask_y); if (this_atlas == NULL) { /* no cache for this glyph */ this_atlas = GetGlyphPicture(glyph, screen); mask_x = mask_y = 0; } } if (this_atlas != glyph_atlas) { PixmapPtr mask_pixmap; if (glyph_atlas) uxa_screen->info->done_composite(dst_pixmap); mask_pixmap = uxa_get_drawable_pixmap(this_atlas->pDrawable); assert (uxa_pixmap_is_offscreen(mask_pixmap)); if (!uxa_screen->info->prepare_composite(op, localSrc, this_atlas, pDst, src_pixmap, mask_pixmap, dst_pixmap)) return -1; glyph_atlas = this_atlas; } nrect = REGION_NUM_RECTS(pDst->pCompositeClip); if (nrect == 1) { uxa_screen->info->composite(dst_pixmap, x + src_x, y + src_y, mask_x, mask_y, x - glyph->info.x, y - glyph->info.y, glyph->info.width, glyph->info.height); } else { BoxPtr rects = REGION_RECTS(pDst->pCompositeClip); do { int x1 = x - glyph->info.x, dx = 0; int y1 = y - glyph->info.y, dy = 0; int x2 = x1 + glyph->info.width; int y2 = y1 + glyph->info.height; if (x1 < rects->x1) dx = rects->x1 - x1, x1 = rects->x1; if (x2 > rects->x2) x2 = rects->x2; if (y1 < rects->y1) dy = rects->y1 - y1, y1 = rects->y1; if (y2 > rects->y2) y2 = rects->y2; if (x1 < x2 && y1 < y2) { uxa_screen->info->composite(dst_pixmap, x1 + src_x, y1 + src_y, dx + mask_x, dy + mask_y, x1, y1, x2 - x1, y2 - y1); } rects++; } while (--nrect); } next_glyph: x += glyph->info.xOff; y += glyph->info.yOff; } list++; } if (glyph_atlas) uxa_screen->info->done_composite(dst_pixmap); if (localSrc != pSrc) FreePicture(localSrc, 0); return 0; } static void uxa_clear_pixmap(ScreenPtr screen, uxa_screen_t *uxa_screen, PixmapPtr pixmap) { if (uxa_screen->info->check_solid && !uxa_screen->info->check_solid(&pixmap->drawable, GXcopy, FB_ALLONES)) goto fallback; if (!uxa_screen->info->prepare_solid(pixmap, GXcopy, FB_ALLONES, 0)) goto fallback; uxa_screen->info->solid(pixmap, 0, 0, pixmap->drawable.width, pixmap->drawable.height); uxa_screen->info->done_solid(pixmap); return; fallback: { GCPtr gc; gc = GetScratchGC(pixmap->drawable.depth, screen); if (gc) { xRectangle rect; ValidateGC(&pixmap->drawable, gc); rect.x = 0; rect.y = 0; rect.width = pixmap->drawable.width; rect.height = pixmap->drawable.height; gc->ops->PolyFillRect(&pixmap->drawable, gc, 1, &rect); FreeScratchGC(gc); } } } static int uxa_glyphs_via_mask(CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, INT16 xDst, INT16 yDst, int nlist, GlyphListPtr list, GlyphPtr * glyphs, BoxPtr extents) { ScreenPtr screen = pDst->pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(screen); CARD32 component_alpha; PixmapPtr pixmap; PicturePtr glyph_atlas, mask; int x, y, width, height; int dst_off_x, dst_off_y; int n, error; BoxRec box; if (!extents) { uxa_glyph_extents(nlist, list, glyphs, &box); if (box.x2 <= box.x1 || box.y2 <= box.y1) return 0; extents = &box; dst_off_x = box.x1; dst_off_y = box.y1; } else { dst_off_x = dst_off_y = 0; } width = extents->x2 - extents->x1; height = extents->y2 - extents->y1; x = -extents->x1; y = -extents->y1; if (maskFormat->depth == 1) { PictFormatPtr a8Format = PictureMatchFormat(screen, 8, PICT_a8); if (!a8Format) return -1; maskFormat = a8Format; } pixmap = screen->CreatePixmap(screen, width, height, maskFormat->depth, CREATE_PIXMAP_USAGE_SCRATCH); if (!pixmap) return 1; uxa_clear_pixmap(screen, uxa_screen, pixmap); if (!uxa_pixmap_is_offscreen(pixmap)) { screen->DestroyPixmap(pixmap); return 1; } component_alpha = NeedsComponent(maskFormat->format); mask = CreatePicture(0, &pixmap->drawable, maskFormat, CPComponentAlpha, &component_alpha, serverClient, &error); screen->DestroyPixmap(pixmap); if (!mask) return 1; ValidatePicture(mask); glyph_atlas = NULL; while (nlist--) { x += list->xOff; y += list->yOff; n = list->len; while (n--) { GlyphPtr glyph = *glyphs++; PicturePtr this_atlas; int src_x, src_y; struct uxa_glyph *priv; if (glyph->info.width == 0 || glyph->info.height == 0) goto next_glyph; priv = uxa_glyph_get_private(glyph); if (priv != NULL) { src_x = priv->x; src_y = priv->y; this_atlas = priv->cache->picture; } else { if (glyph_atlas) { uxa_screen->info->done_composite(pixmap); glyph_atlas = NULL; } this_atlas = uxa_glyph_cache(screen, glyph, &src_x, &src_y); if (this_atlas == NULL) { /* no cache for this glyph */ this_atlas = GetGlyphPicture(glyph, screen); src_x = src_y = 0; } } if (this_atlas != glyph_atlas) { PixmapPtr src_pixmap; if (glyph_atlas) uxa_screen->info->done_composite(pixmap); src_pixmap = uxa_get_drawable_pixmap(this_atlas->pDrawable); assert (uxa_pixmap_is_offscreen(src_pixmap)); if (!uxa_screen->info->prepare_composite(PictOpAdd, this_atlas, NULL, mask, src_pixmap, NULL, pixmap)) { FreePicture(mask, 0); return -1; } glyph_atlas = this_atlas; } uxa_screen->info->composite(pixmap, src_x, src_y, 0, 0, x - glyph->info.x, y - glyph->info.y, glyph->info.width, glyph->info.height); next_glyph: x += glyph->info.xOff; y += glyph->info.yOff; } list++; } if (glyph_atlas) uxa_screen->info->done_composite(pixmap); uxa_composite(op, pSrc, mask, pDst, dst_off_x + xSrc - xDst, dst_off_y + ySrc - yDst, 0, 0, dst_off_x, dst_off_y, width, height); FreePicture(mask, 0); return 0; } void uxa_glyphs(CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr * glyphs) { ScreenPtr screen = pDst->pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(screen); int xDst = list->xOff, yDst = list->yOff; BoxRec extents = { 0, 0, 0, 0 }; Bool have_extents = FALSE; int width = 0, height = 0, ret; PicturePtr localDst = pDst; if (!uxa_screen->info->prepare_composite || uxa_screen->swappedOut || uxa_screen->force_fallback || !uxa_drawable_is_offscreen(pDst->pDrawable) || pDst->alphaMap || pSrc->alphaMap) { fallback: uxa_check_glyphs(op, pSrc, pDst, maskFormat, xSrc, ySrc, nlist, list, glyphs); return; } /* basic sanity check */ if (uxa_screen->info->check_composite && !uxa_screen->info->check_composite(op, pSrc, NULL, pDst, 0, 0)) { goto fallback; } ValidatePicture(pSrc); ValidatePicture(pDst); if (!maskFormat) { /* If we don't have a mask format but all the glyphs have the same format, * require ComponentAlpha and don't intersect, use the glyph format as mask * format for the full benefits of the glyph cache. */ if (NeedsComponent(list[0].format->format)) { Bool sameFormat = TRUE; int i; maskFormat = list[0].format; for (i = 0; i < nlist; i++) { if (maskFormat->format != list[i].format->format) { sameFormat = FALSE; break; } } if (!sameFormat || uxa_glyphs_intersect(nlist, list, glyphs)) maskFormat = NULL; } } if (!maskFormat && uxa_screen->info->check_composite_target && !uxa_screen->info->check_composite_target(uxa_get_drawable_pixmap(pDst->pDrawable))) { int depth = pDst->pDrawable->depth; PixmapPtr pixmap; int x, y, error; GCPtr gc; pixmap = uxa_get_drawable_pixmap(pDst->pDrawable); if (uxa_screen->info->check_copy && !uxa_screen->info->check_copy(pixmap, pixmap, GXcopy, FB_ALLONES)) goto fallback; uxa_glyph_extents(nlist, list, glyphs, &extents); /* clip against dst bounds */ if (extents.x1 < 0) extents.x1 = 0; if (extents.y1 < 0) extents.y1 = 0; if (extents.x2 > pDst->pDrawable->width) extents.x2 = pDst->pDrawable->width; if (extents.y2 > pDst->pDrawable->height) extents.y2 = pDst->pDrawable->height; if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1) return; width = extents.x2 - extents.x1; height = extents.y2 - extents.y1; x = -extents.x1; y = -extents.y1; have_extents = TRUE; xDst += x; yDst += y; pixmap = screen->CreatePixmap(screen, width, height, depth, CREATE_PIXMAP_USAGE_SCRATCH); if (!pixmap) return; gc = GetScratchGC(depth, screen); if (!gc) { screen->DestroyPixmap(pixmap); return; } ValidateGC(&pixmap->drawable, gc); gc->ops->CopyArea(pDst->pDrawable, &pixmap->drawable, gc, extents.x1, extents.y1, width, height, 0, 0); FreeScratchGC(gc); localDst = CreatePicture(0, &pixmap->drawable, PictureMatchFormat(screen, depth, pDst->format), 0, 0, serverClient, &error); screen->DestroyPixmap(pixmap); if (!localDst) return; ValidatePicture(localDst); } if (maskFormat) { ret = uxa_glyphs_via_mask(op, pSrc, localDst, maskFormat, xSrc, ySrc, xDst, yDst, nlist, list, glyphs, have_extents ? &extents : NULL); } else { ret = uxa_glyphs_to_dst(op, pSrc, localDst, xSrc, ySrc, xDst, yDst, nlist, list, glyphs, have_extents ? &extents : NULL); } if (ret) { if (localDst != pDst) FreePicture(localDst, 0); goto fallback; } if (localDst != pDst) { GCPtr gc; gc = GetScratchGC(pDst->pDrawable->depth, screen); if (gc) { ValidateGC(pDst->pDrawable, gc); gc->ops->CopyArea(localDst->pDrawable, pDst->pDrawable, gc, 0, 0, width, height, extents.x1, extents.y1); FreeScratchGC(gc); } FreePicture(localDst, 0); } } xserver-xorg-video-qxl-0.1.5+git20200331/src/uxa/uxa-priv.h000066400000000000000000000264601361660122400230110ustar00rootroot00000000000000/* * * Copyright © 2000,2008 Keith Packard * 2005 Zack Rusin, Trolltech * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifndef UXAPRIV_H #define UXAPRIV_H #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_DIX_CONFIG_H #include #else #include #endif #include "xf86.h" #include "uxa.h" #include #define NEED_EVENTS #include #include "scrnintstr.h" #include "pixmapstr.h" #include "windowstr.h" #include "servermd.h" #include "colormapst.h" #include "gcstruct.h" #include "input.h" #include "mipointer.h" #include "mi.h" #include "dix.h" #include "fb.h" #include "fboverlay.h" #ifdef RENDER //#include "fbpict.h" #include "glyphstr.h" #endif #include "damage.h" #include "../compat-api.h" /* Provide substitutes for gcc's __FUNCTION__ on other compilers */ #if !defined(__GNUC__) && !defined(__FUNCTION__) # if defined(__STDC__) && (__STDC_VERSION__>=199901L) /* C99 */ # define __FUNCTION__ __func__ # else # define __FUNCTION__ "" # endif #endif /* 1.6 and earlier server compat */ #ifndef miGetCompositeClip #define miCopyRegion fbCopyRegion #define miDoCopy fbDoCopy #endif #define DEBUG_MIGRATE 0 #define DEBUG_PIXMAP 0 #define DEBUG_OFFSCREEN 0 #define DEBUG_GLYPH_CACHE 0 #define UXA_FALLBACK(x) \ if (uxa_get_screen(screen)->fallback_debug) { \ ErrorF("UXA fallback at %s: ", __FUNCTION__); \ ErrorF x; \ } char uxa_drawable_location(DrawablePtr pDrawable); #if DEBUG_PIXMAP #define DBG_PIXMAP(a) ErrorF a #else #define DBG_PIXMAP(a) #endif typedef struct { PicturePtr picture; /* Where the glyphs of the cache are stored */ GlyphPtr *glyphs; uint16_t count; uint16_t evict; } uxa_glyph_cache_t; #define UXA_NUM_GLYPH_CACHE_FORMATS 2 typedef struct { uint32_t color; PicturePtr picture; } uxa_solid_cache_t; #define UXA_NUM_SOLID_CACHE 16 typedef void (*EnableDisableFBAccessProcPtr) (SCRN_ARG_TYPE, Bool); typedef struct { uxa_driver_t *info; CreateGCProcPtr SavedCreateGC; CloseScreenProcPtr SavedCloseScreen; GetImageProcPtr SavedGetImage; GetSpansProcPtr SavedGetSpans; CreatePixmapProcPtr SavedCreatePixmap; DestroyPixmapProcPtr SavedDestroyPixmap; CopyWindowProcPtr SavedCopyWindow; ChangeWindowAttributesProcPtr SavedChangeWindowAttributes; BitmapToRegionProcPtr SavedBitmapToRegion; #ifdef RENDER CompositeProcPtr SavedComposite; CompositeRectsProcPtr SavedCompositeRects; TrianglesProcPtr SavedTriangles; GlyphsProcPtr SavedGlyphs; TrapezoidsProcPtr SavedTrapezoids; AddTrapsProcPtr SavedAddTraps; UnrealizeGlyphProcPtr SavedUnrealizeGlyph; #endif EnableDisableFBAccessProcPtr SavedEnableDisableFBAccess; Bool force_fallback; Bool fallback_debug; Bool swappedOut; unsigned disableFbCount; unsigned offScreenCounter; uxa_glyph_cache_t glyphCaches[UXA_NUM_GLYPH_CACHE_FORMATS]; PicturePtr solid_clear, solid_black, solid_white; uxa_solid_cache_t solid_cache[UXA_NUM_SOLID_CACHE]; int solid_cache_size; } uxa_screen_t; /* * This is the only completely portable way to * compute this info. */ #ifndef BitsPerPixel #define BitsPerPixel(d) (\ PixmapWidthPaddingInfo[d].notPower2 ? \ (PixmapWidthPaddingInfo[d].bytesPerPixel * 8) : \ ((1 << PixmapWidthPaddingInfo[d].padBytesLog2) * 8 / \ (PixmapWidthPaddingInfo[d].padRoundUp+1))) #endif #if HAS_DEVPRIVATEKEYREC extern DevPrivateKeyRec uxa_screen_index; #else extern int uxa_screen_index; #endif static inline uxa_screen_t *uxa_get_screen(ScreenPtr screen) { #if HAS_DEVPRIVATEKEYREC return dixGetPrivate (&screen->devPrivates, &uxa_screen_index); #else return dixLookupPrivate(&screen->devPrivates, &uxa_screen_index); #endif } /** Align an offset to an arbitrary alignment */ #define UXA_ALIGN(offset, align) (((offset) + (align) - 1) - \ (((offset) + (align) - 1) % (align))) /** Align an offset to a power-of-two alignment */ #define UXA_ALIGN2(offset, align) (((offset) + (align) - 1) & ~((align) - 1)) typedef struct { INT16 xSrc; INT16 ySrc; INT16 xDst; INT16 yDst; INT16 width; INT16 height; } uxa_composite_rect_t; /** * exaDDXDriverInit must be implemented by the DDX using EXA, and is the place * to set EXA options or hook in screen functions to handle using EXA as the AA. */ void exaDDXDriverInit(ScreenPtr pScreen); Bool uxa_prepare_access_window(WindowPtr pWin); void uxa_finish_access_window(WindowPtr pWin); /* uxa-unaccel.c */ Bool uxa_prepare_access_gc(GCPtr pGC); void uxa_finish_access_gc(GCPtr pGC); void uxa_check_fill_spans(DrawablePtr pDrawable, GCPtr pGC, int nspans, DDXPointPtr ppt, int *pwidth, int fSorted); void uxa_check_set_spans(DrawablePtr pDrawable, GCPtr pGC, char *psrc, DDXPointPtr ppt, int *pwidth, int nspans, int fSorted); void uxa_check_put_image(DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, int w, int h, int leftPad, int format, char *bits); RegionPtr uxa_check_copy_area(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty); RegionPtr uxa_check_copy_plane(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty, unsigned long bitPlane); void uxa_check_poly_point(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr pptInit); void uxa_check_poly_lines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr ppt); void uxa_check_poly_segment(DrawablePtr pDrawable, GCPtr pGC, int nsegInit, xSegment * pSegInit); void uxa_check_poly_arc(DrawablePtr pDrawable, GCPtr pGC, int narcs, xArc * pArcs); void uxa_check_poly_fill_rect(DrawablePtr pDrawable, GCPtr pGC, int nrect, xRectangle * prect); void uxa_check_image_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr * ppci, pointer pglyphBase); void uxa_check_poly_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr * ppci, pointer pglyphBase); void uxa_check_push_pixels(GCPtr pGC, PixmapPtr pBitmap, DrawablePtr pDrawable, int w, int h, int x, int y); void uxa_check_get_spans(DrawablePtr pDrawable, int wMax, DDXPointPtr ppt, int *pwidth, int nspans, char *pdstStart); void uxa_check_paint_window(WindowPtr pWin, RegionPtr pRegion, int what); void uxa_check_add_traps(PicturePtr pPicture, INT16 x_off, INT16 y_off, int ntrap, xTrap * traps); /* uxa-accel.c */ static _X_INLINE Bool uxa_gc_reads_destination(DrawablePtr pDrawable, unsigned long planemask, unsigned int fillStyle, unsigned char alu) { return ((alu != GXcopy && alu != GXclear && alu != GXset && alu != GXcopyInverted) || fillStyle == FillStippled || !UXA_PM_IS_SOLID(pDrawable, planemask)); } void uxa_copy_window(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc); Bool uxa_fill_region_tiled(DrawablePtr pDrawable, RegionPtr pRegion, PixmapPtr pTile, DDXPointPtr pPatOrg, CARD32 planemask, CARD32 alu); void uxa_paint_window(WindowPtr pWin, RegionPtr pRegion, int what); void uxa_get_image(DrawablePtr pDrawable, int x, int y, int w, int h, unsigned int format, unsigned long planeMask, char *d); extern GCOps uxa_ops; #ifdef RENDER /* XXX these are in fbpict.h, which is not installed */ void fbComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height); void fbAddTraps(PicturePtr pPicture, INT16 xOff, INT16 yOff, int ntrap, xTrap * traps); void uxa_check_composite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height); #endif /* uxa.c */ Bool uxa_prepare_access(DrawablePtr pDrawable, RegionPtr region, uxa_access_t access); void uxa_finish_access(DrawablePtr pDrawable); void uxa_get_drawable_deltas(DrawablePtr pDrawable, PixmapPtr pPixmap, int *xp, int *yp); Bool uxa_drawable_is_offscreen(DrawablePtr pDrawable); Bool uxa_pixmap_is_offscreen(PixmapPtr p); PixmapPtr uxa_get_offscreen_pixmap(DrawablePtr pDrawable, int *xp, int *yp); PixmapPtr uxa_get_drawable_pixmap(DrawablePtr pDrawable); RegionPtr uxa_copy_area(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC, int srcx, int srcy, int width, int height, int dstx, int dsty); void uxa_copy_n_to_n(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC, BoxPtr pbox, int nbox, int dx, int dy, Bool reverse, Bool upsidedown, Pixel bitplane, void *closure); /* uxa_render.c */ Bool uxa_op_reads_destination(CARD8 op); void uxa_composite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height); void uxa_composite_rects(CARD8 op, PicturePtr pSrc, PicturePtr pDst, int nrect, uxa_composite_rect_t * rects); void uxa_solid_rects (CARD8 op, PicturePtr dst, xRenderColor *color, int num_rects, xRectangle *rects); void uxa_trapezoids(CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int ntrap, xTrapezoid * traps); void uxa_triangles(CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int ntri, xTriangle * tris); PicturePtr uxa_acquire_solid(ScreenPtr screen, SourcePict *source); PicturePtr uxa_acquire_drawable(ScreenPtr pScreen, PicturePtr pSrc, INT16 x, INT16 y, CARD16 width, CARD16 height, INT16 * out_x, INT16 * out_y); PicturePtr uxa_acquire_pattern(ScreenPtr pScreen, PicturePtr pSrc, pixman_format_code_t format, INT16 x, INT16 y, CARD16 width, CARD16 height); Bool uxa_get_rgba_from_pixel(CARD32 pixel, CARD16 * red, CARD16 * green, CARD16 * blue, CARD16 * alpha, CARD32 format); /* uxa_glyph.c */ Bool uxa_glyphs_init(ScreenPtr pScreen); void uxa_glyphs_fini(ScreenPtr pScreen); void uxa_glyphs(CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr * glyphs); void uxa_glyph_unrealize(ScreenPtr pScreen, GlyphPtr pGlyph); #endif /* UXAPRIV_H */ xserver-xorg-video-qxl-0.1.5+git20200331/src/uxa/uxa-render.c000066400000000000000000001410001361660122400232670ustar00rootroot00000000000000/* * Copyright © 2001 Keith Packard * * Partly based on code that is Copyright © The XFree86 Project Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include "uxa-priv.h" #include #ifdef RENDER #include "mipict.h" static void uxa_composite_fallback_pict_desc(PicturePtr pict, char *string, int n) { char format[20]; char size[20]; char loc; if (!pict) { snprintf(string, n, "None"); return; } if (pict->pDrawable == NULL) { snprintf(string, n, "source-only"); return; } switch (pict->format) { case PICT_a8r8g8b8: snprintf(format, 20, "ARGB8888"); break; case PICT_x8r8g8b8: snprintf(format, 20, "XRGB8888"); break; case PICT_r5g6b5: snprintf(format, 20, "RGB565 "); break; case PICT_x1r5g5b5: snprintf(format, 20, "RGB555 "); break; case PICT_a8: snprintf(format, 20, "A8 "); break; case PICT_a1: snprintf(format, 20, "A1 "); break; default: snprintf(format, 20, "0x%x", (int)pict->format); break; } loc = uxa_drawable_is_offscreen(pict->pDrawable) ? 's' : 'm'; snprintf(size, 20, "%dx%d%s", pict->pDrawable->width, pict->pDrawable->height, pict->repeat ? " R" : ""); snprintf(string, n, "%p:%c fmt %s (%s)%s", pict->pDrawable, loc, format, size, pict->alphaMap ? " with alpha map" :""); } static const char * op_to_string(CARD8 op) { switch (op) { #define C(x) case PictOp##x: return #x C(Clear); C(Src); C(Dst); C(Over); C(OverReverse); C(In); C(InReverse); C(Out); C(OutReverse); C(Atop); C(AtopReverse); C(Xor); C(Add); C(Saturate); /* * Operators only available in version 0.2 */ C(DisjointClear); C(DisjointSrc); C(DisjointDst); C(DisjointOver); C(DisjointOverReverse); C(DisjointIn); C(DisjointInReverse); C(DisjointOut); C(DisjointOutReverse); C(DisjointAtop); C(DisjointAtopReverse); C(DisjointXor); C(ConjointClear); C(ConjointSrc); C(ConjointDst); C(ConjointOver); C(ConjointOverReverse); C(ConjointIn); C(ConjointInReverse); C(ConjointOut); C(ConjointOutReverse); C(ConjointAtop); C(ConjointAtopReverse); C(ConjointXor); /* * Operators only available in version 0.11 */ C(Multiply); C(Screen); C(Overlay); C(Darken); C(Lighten); C(ColorDodge); C(ColorBurn); C(HardLight); C(SoftLight); C(Difference); C(Exclusion); C(HSLHue); C(HSLSaturation); C(HSLColor); C(HSLLuminosity); default: return "garbage"; #undef C } } static void uxa_print_composite_fallback(const char *func, CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst) { uxa_screen_t *uxa_screen = uxa_get_screen(pDst->pDrawable->pScreen); char srcdesc[40], maskdesc[40], dstdesc[40]; if (! uxa_screen->fallback_debug) return; /* Limit the noise if fallbacks are expected. */ if (uxa_screen->force_fallback) return; uxa_composite_fallback_pict_desc(pSrc, srcdesc, 40); uxa_composite_fallback_pict_desc(pMask, maskdesc, 40); uxa_composite_fallback_pict_desc(pDst, dstdesc, 40); ErrorF("Composite fallback at %s:\n" " op %s, \n" " src %s, \n" " mask %s, \n" " dst %s, \n" " screen %s\n", func, op_to_string (op), srcdesc, maskdesc, dstdesc, uxa_screen->swappedOut ? "swapped out" : "normal"); } Bool uxa_op_reads_destination(CARD8 op) { /* FALSE (does not read destination) is the list of ops in the protocol * document with "0" in the "Fb" column and no "Ab" in the "Fa" column. * That's just Clear and Src. ReduceCompositeOp() will already have * converted con/disjoint clear/src to Clear or Src. */ switch (op) { case PictOpClear: case PictOpSrc: return FALSE; default: return TRUE; } } static Bool uxa_get_pixel_from_rgba(CARD32 * pixel, CARD16 red, CARD16 green, CARD16 blue, CARD16 alpha, CARD32 format) { int rbits, bbits, gbits, abits; int rshift, bshift, gshift, ashift; rbits = PICT_FORMAT_R(format); gbits = PICT_FORMAT_G(format); bbits = PICT_FORMAT_B(format); abits = PICT_FORMAT_A(format); if (abits == 0) abits = PICT_FORMAT_BPP(format) - (rbits+gbits+bbits); if (PICT_FORMAT_TYPE(format) == PICT_TYPE_A) { *pixel = alpha >> (16 - abits); return TRUE; } if (!PICT_FORMAT_COLOR(format)) return FALSE; if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) { bshift = 0; gshift = bbits; rshift = gshift + gbits; ashift = rshift + rbits; } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ABGR) { rshift = 0; gshift = rbits; bshift = gshift + gbits; ashift = bshift + bbits; } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_BGRA) { ashift = 0; rshift = abits; gshift = rshift + rbits; bshift = gshift + gbits; } else { return FALSE; } *pixel = 0; *pixel |= (blue >> (16 - bbits)) << bshift; *pixel |= (green >> (16 - gbits)) << gshift; *pixel |= (red >> (16 - rbits)) << rshift; *pixel |= (alpha >> (16 - abits)) << ashift; return TRUE; } Bool uxa_get_rgba_from_pixel(CARD32 pixel, CARD16 * red, CARD16 * green, CARD16 * blue, CARD16 * alpha, CARD32 format) { int rbits, bbits, gbits, abits; int rshift, bshift, gshift, ashift; rbits = PICT_FORMAT_R(format); gbits = PICT_FORMAT_G(format); bbits = PICT_FORMAT_B(format); abits = PICT_FORMAT_A(format); if (PICT_FORMAT_TYPE(format) == PICT_TYPE_A) { rshift = gshift = bshift = ashift = 0; } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) { bshift = 0; gshift = bbits; rshift = gshift + gbits; ashift = rshift + rbits; } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ABGR) { rshift = 0; gshift = rbits; bshift = gshift + gbits; ashift = bshift + bbits; } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_BGRA) { ashift = 0; rshift = abits; if (abits == 0) rshift = PICT_FORMAT_BPP(format) - (rbits+gbits+bbits); gshift = rshift + rbits; bshift = gshift + gbits; } else { return FALSE; } if (rbits) { *red = ((pixel >> rshift) & ((1 << rbits) - 1)) << (16 - rbits); while (rbits < 16) { *red |= *red >> rbits; rbits <<= 1; } } else *red = 0; if (gbits) { *green = ((pixel >> gshift) & ((1 << gbits) - 1)) << (16 - gbits); while (gbits < 16) { *green |= *green >> gbits; gbits <<= 1; } } else *green = 0; if (bbits) { *blue = ((pixel >> bshift) & ((1 << bbits) - 1)) << (16 - bbits); while (bbits < 16) { *blue |= *blue >> bbits; bbits <<= 1; } } else *blue = 0; if (abits) { *alpha = ((pixel >> ashift) & ((1 << abits) - 1)) << (16 - abits); while (abits < 16) { *alpha |= *alpha >> abits; abits <<= 1; } } else *alpha = 0xffff; return TRUE; } Bool uxa_get_color_for_pixmap (PixmapPtr pixmap, CARD32 src_format, CARD32 dst_format, CARD32 *pixel) { CARD16 red, green, blue, alpha; *pixel = uxa_get_pixmap_first_pixel(pixmap); if (src_format != dst_format) { if (!uxa_get_rgba_from_pixel(*pixel, &red, &green, &blue, &alpha, src_format)) return FALSE; if (!uxa_get_pixel_from_rgba(pixel, red, green, blue, alpha, dst_format)) return FALSE; } return TRUE; } static int uxa_try_driver_solid_fill(PicturePtr pSrc, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { uxa_screen_t *uxa_screen = uxa_get_screen(pDst->pDrawable->pScreen); RegionRec region; BoxPtr pbox; int nbox; int dst_off_x, dst_off_y; PixmapPtr pSrcPix = NULL, pDstPix; CARD32 pixel; if (uxa_screen->info->check_solid && !uxa_screen->info->check_solid(pDst->pDrawable, GXcopy, FB_ALLONES)) return -1; pDstPix = uxa_get_offscreen_pixmap(pDst->pDrawable, &dst_off_x, &dst_off_y); if (!pDstPix) return -1; xDst += pDst->pDrawable->x; yDst += pDst->pDrawable->y; if (pSrc->pDrawable) { pSrcPix = uxa_get_drawable_pixmap(pSrc->pDrawable); xSrc += pSrc->pDrawable->x; ySrc += pSrc->pDrawable->y; } if (!miComputeCompositeRegion(®ion, pSrc, NULL, pDst, xSrc, ySrc, 0, 0, xDst, yDst, width, height)) return 1; if (pSrcPix) { if (! uxa_get_color_for_pixmap (pSrcPix, pSrc->format, pDst->format, &pixel)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } } else { SourcePict *source = pSrc->pSourcePict; PictSolidFill *solid = &source->solidFill; if (source == NULL || source->type != SourcePictTypeSolidFill) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } if (pDst->format == PICT_a8r8g8b8) { pixel = solid->color; } else if (pDst->format == PICT_x8r8g8b8) { pixel = solid->color | 0xff000000; } else { CARD16 red, green, blue, alpha; if (!uxa_get_rgba_from_pixel(solid->color, &red, &green, &blue, &alpha, PIXMAN_a8r8g8b8) || !uxa_get_pixel_from_rgba(&pixel, red, green, blue, alpha, pDst->format)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } } } if (!(*uxa_screen->info->prepare_solid) (pDstPix, GXcopy, FB_ALLONES, pixel)) { REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return -1; } REGION_TRANSLATE(pScreen, ®ion, dst_off_x, dst_off_y); nbox = REGION_NUM_RECTS(®ion); pbox = REGION_RECTS(®ion); while (nbox--) { (*uxa_screen->info->solid) (pDstPix, pbox->x1, pbox->y1, pbox->x2, pbox->y2); pbox++; } (*uxa_screen->info->done_solid) (pDstPix); REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); return 1; } static PicturePtr uxa_picture_for_pixman_format(ScreenPtr pScreen, pixman_format_code_t format, int width, int height) { PicturePtr pPicture; PixmapPtr pPixmap; int error; if (format == PIXMAN_a1) format = PIXMAN_a8; /* fill alpha if unset */ if (PIXMAN_FORMAT_A(format) == 0) format = PIXMAN_a8r8g8b8; pPixmap = (*pScreen->CreatePixmap)(pScreen, width, height, PIXMAN_FORMAT_DEPTH(format), UXA_CREATE_PIXMAP_FOR_MAP); if (!pPixmap) return 0; pPicture = CreatePicture(0, &pPixmap->drawable, PictureMatchFormat(pScreen, PIXMAN_FORMAT_DEPTH(format), format), 0, 0, serverClient, &error); (*pScreen->DestroyPixmap) (pPixmap); if (!pPicture) return 0; ValidatePicture(pPicture); return pPicture; } static PicturePtr uxa_picture_from_pixman_image(ScreenPtr screen, pixman_image_t * image, pixman_format_code_t format) { uxa_screen_t *uxa_screen = uxa_get_screen(screen); PicturePtr picture; PixmapPtr pixmap; int width, height; width = pixman_image_get_width(image); height = pixman_image_get_height(image); picture = uxa_picture_for_pixman_format(screen, format, width, height); if (!picture) return 0; if (uxa_screen->info->put_image && ((picture->pDrawable->depth << 24) | picture->format) == format && uxa_screen->info->put_image((PixmapPtr)picture->pDrawable, 0, 0, width, height, (char *)pixman_image_get_data(image), pixman_image_get_stride(image))) return picture; pixmap = GetScratchPixmapHeader(screen, width, height, PIXMAN_FORMAT_DEPTH(format), PIXMAN_FORMAT_BPP(format), pixman_image_get_stride(image), pixman_image_get_data(image)); if (!pixmap) { FreePicture(picture, 0); return 0; } if (((picture->pDrawable->depth << 24) | picture->format) == format) { GCPtr gc; gc = GetScratchGC(PIXMAN_FORMAT_DEPTH(format), screen); if (!gc) { FreeScratchPixmapHeader(pixmap); FreePicture(picture, 0); return 0; } ValidateGC(picture->pDrawable, gc); (*gc->ops->CopyArea) (&pixmap->drawable, picture->pDrawable, gc, 0, 0, width, height, 0, 0); FreeScratchGC(gc); } else { PicturePtr src; int error; src = CreatePicture(0, &pixmap->drawable, PictureMatchFormat(screen, PIXMAN_FORMAT_DEPTH(format), format), 0, 0, serverClient, &error); if (!src) { FreeScratchPixmapHeader(pixmap); FreePicture(picture, 0); return 0; } ValidatePicture(src); if (uxa_prepare_access(picture->pDrawable, NULL, UXA_ACCESS_RW)) { fbComposite(PictOpSrc, src, NULL, picture, 0, 0, 0, 0, 0, 0, width, height); uxa_finish_access(picture->pDrawable); } FreePicture(src, 0); } FreeScratchPixmapHeader(pixmap); return picture; } static PicturePtr uxa_create_solid(ScreenPtr screen, uint32_t color) { PixmapPtr pixmap; PicturePtr picture; XID repeat = RepeatNormal; int error = 0; pixmap = (*screen->CreatePixmap)(screen, 1, 1, 32, UXA_CREATE_PIXMAP_FOR_MAP); if (!pixmap) return 0; if (!uxa_prepare_access((DrawablePtr)pixmap, NULL, UXA_ACCESS_RW)) { (*screen->DestroyPixmap)(pixmap); return 0; } *((uint32_t *)pixmap->devPrivate.ptr) = color; uxa_finish_access((DrawablePtr)pixmap); picture = CreatePicture(0, &pixmap->drawable, PictureMatchFormat(screen, 32, PICT_a8r8g8b8), CPRepeat, &repeat, serverClient, &error); (*screen->DestroyPixmap)(pixmap); return picture; } static PicturePtr uxa_solid_clear(ScreenPtr screen) { uxa_screen_t *uxa_screen = uxa_get_screen(screen); PicturePtr picture; if (!uxa_screen->solid_clear) { uxa_screen->solid_clear = uxa_create_solid(screen, 0); if (!uxa_screen->solid_clear) return 0; } picture = uxa_screen->solid_clear; return picture; } PicturePtr uxa_acquire_solid(ScreenPtr screen, SourcePict *source) { uxa_screen_t *uxa_screen = uxa_get_screen(screen); PictSolidFill *solid = &source->solidFill; PicturePtr picture; int i; if ((solid->color >> 24) == 0) { picture = uxa_solid_clear(screen); if (!picture) return 0; goto DONE; } else if (solid->color == 0xff000000) { if (!uxa_screen->solid_black) { uxa_screen->solid_black = uxa_create_solid(screen, 0xff000000); if (!uxa_screen->solid_black) return 0; } picture = uxa_screen->solid_black; goto DONE; } else if (solid->color == 0xffffffff) { if (!uxa_screen->solid_white) { uxa_screen->solid_white = uxa_create_solid(screen, 0xffffffff); if (!uxa_screen->solid_white) return 0; } picture = uxa_screen->solid_white; goto DONE; } for (i = 0; i < uxa_screen->solid_cache_size; i++) { if (uxa_screen->solid_cache[i].color == solid->color) { picture = uxa_screen->solid_cache[i].picture; goto DONE; } } picture = uxa_create_solid(screen, solid->color); if (!picture) return 0; if (uxa_screen->solid_cache_size == UXA_NUM_SOLID_CACHE) { i = rand() % UXA_NUM_SOLID_CACHE; FreePicture(uxa_screen->solid_cache[i].picture, 0); } else uxa_screen->solid_cache_size++; uxa_screen->solid_cache[i].picture = picture; uxa_screen->solid_cache[i].color = solid->color; DONE: picture->refcnt++; return picture; } PicturePtr uxa_acquire_pattern(ScreenPtr pScreen, PicturePtr pSrc, pixman_format_code_t format, INT16 x, INT16 y, CARD16 width, CARD16 height) { PicturePtr pDst; if (pSrc->pSourcePict) { SourcePict *source = pSrc->pSourcePict; if (source->type == SourcePictTypeSolidFill) return uxa_acquire_solid (pScreen, source); } pDst = uxa_picture_for_pixman_format(pScreen, format, width, height); if (!pDst) return 0; if (uxa_prepare_access(pDst->pDrawable, NULL, UXA_ACCESS_RW)) { fbComposite(PictOpSrc, pSrc, NULL, pDst, x, y, 0, 0, 0, 0, width, height); uxa_finish_access(pDst->pDrawable); return pDst; } else { FreePicture(pDst, 0); return 0; } } static Bool transform_is_integer_translation(PictTransformPtr t, int *tx, int *ty) { if (t == NULL) { *tx = *ty = 0; return TRUE; } if (t->matrix[0][0] != IntToxFixed(1) || t->matrix[0][1] != 0 || t->matrix[1][0] != 0 || t->matrix[1][1] != IntToxFixed(1) || t->matrix[2][0] != 0 || t->matrix[2][1] != 0 || t->matrix[2][2] != IntToxFixed(1)) return FALSE; if (xFixedFrac(t->matrix[0][2]) != 0 || xFixedFrac(t->matrix[1][2]) != 0) return FALSE; *tx = xFixedToInt(t->matrix[0][2]); *ty = xFixedToInt(t->matrix[1][2]); return TRUE; } static PicturePtr uxa_render_picture(ScreenPtr screen, PicturePtr src, pixman_format_code_t format, INT16 x, INT16 y, CARD16 width, CARD16 height) { PicturePtr picture; int ret = 0; /* XXX we need a mechanism for the card to choose the fallback format */ /* force alpha channel in case source does not entirely cover the extents */ if (PIXMAN_FORMAT_A(format) == 0) format = PIXMAN_a8r8g8b8; /* available on all hardware */ picture = uxa_picture_for_pixman_format(screen, format, width, height); if (!picture) return 0; if (uxa_prepare_access(picture->pDrawable, NULL, UXA_ACCESS_RW)) { if (uxa_prepare_access(src->pDrawable, NULL, UXA_ACCESS_RO)) { ret = 1; fbComposite(PictOpSrc, src, NULL, picture, x, y, 0, 0, 0, 0, width, height); uxa_finish_access(src->pDrawable); } uxa_finish_access(picture->pDrawable); } if (!ret) { FreePicture(picture, 0); return 0; } return picture; } PicturePtr uxa_acquire_drawable(ScreenPtr pScreen, PicturePtr pSrc, INT16 x, INT16 y, CARD16 width, CARD16 height, INT16 * out_x, INT16 * out_y) { PixmapPtr pPixmap; PicturePtr pDst; GCPtr pGC; int depth, error; int tx, ty; depth = pSrc->pDrawable->depth; if (depth == 1 || pSrc->filter == PictFilterConvolution || /* XXX */ !transform_is_integer_translation(pSrc->transform, &tx, &ty)) { /* XXX extract the sample extents and do the transformation on the GPU */ pDst = uxa_render_picture(pScreen, pSrc, pSrc->format | (BitsPerPixel(pSrc->pDrawable->depth) << 24), x, y, width, height); goto done; } else { if (width == pSrc->pDrawable->width && height == pSrc->pDrawable->depth) { *out_x = x + pSrc->pDrawable->x; *out_y = y + pSrc->pDrawable->y; return pSrc; } } pPixmap = pScreen->CreatePixmap(pScreen, width, height, depth, CREATE_PIXMAP_USAGE_SCRATCH); if (!pPixmap) return 0; /* Skip the copy if the result remains in memory and not a bo */ if (!uxa_drawable_is_offscreen(&pPixmap->drawable)) { pScreen->DestroyPixmap(pPixmap); return 0; } pGC = GetScratchGC(depth, pScreen); if (!pGC) { pScreen->DestroyPixmap(pPixmap); return 0; } ValidateGC(&pPixmap->drawable, pGC); pGC->ops->CopyArea(pSrc->pDrawable, &pPixmap->drawable, pGC, x + tx, y + ty, width, height, 0, 0); FreeScratchGC(pGC); pDst = CreatePicture(0, &pPixmap->drawable, PictureMatchFormat(pScreen, depth, pSrc->format), 0, 0, serverClient, &error); pScreen->DestroyPixmap(pPixmap); ValidatePicture(pDst); done: pDst->componentAlpha = pSrc->componentAlpha; *out_x = x; *out_y = y; return pDst; } static PicturePtr uxa_acquire_picture(ScreenPtr screen, PicturePtr src, pixman_format_code_t format, INT16 x, INT16 y, CARD16 width, CARD16 height, INT16 * out_x, INT16 * out_y) { uxa_screen_t *uxa_screen = uxa_get_screen(screen); if (uxa_screen->info->check_composite_texture && uxa_screen->info->check_composite_texture(screen, src)) { if (src->pDrawable) { *out_x = x + src->pDrawable->x; *out_y = y + src->pDrawable->y; } else { *out_x = 0; *out_y = 0; } return src; } if (src->pDrawable) { PicturePtr dst; dst = uxa_acquire_drawable(screen, src, x, y, width, height, out_x, out_y); if (uxa_screen->info->check_composite_texture && !uxa_screen->info->check_composite_texture(screen, dst)) { if (dst != src) FreePicture(dst, 0); return 0; } return dst; } *out_x = 0; *out_y = 0; return uxa_acquire_pattern(screen, src, format, x, y, width, height); } static PicturePtr uxa_acquire_source(ScreenPtr screen, PicturePtr pict, INT16 x, INT16 y, CARD16 width, CARD16 height, INT16 * out_x, INT16 * out_y) { return uxa_acquire_picture (screen, pict, PIXMAN_a8r8g8b8, x, y, width, height, out_x, out_y); } static PicturePtr uxa_acquire_mask(ScreenPtr screen, PicturePtr pict, INT16 x, INT16 y, INT16 width, INT16 height, INT16 * out_x, INT16 * out_y) { return uxa_acquire_picture (screen, pict, PIXMAN_a8, x, y, width, height, out_x, out_y); } static Bool _pixman_region_init_rectangles(pixman_region16_t *region, int num_rects, xRectangle *rects, int tx, int ty) { pixman_box16_t stack_boxes[64], *boxes = stack_boxes; pixman_bool_t ret; int i; if (num_rects > sizeof(stack_boxes) / sizeof(stack_boxes[0])) { boxes = malloc(sizeof(pixman_box16_t) * num_rects); if (boxes == NULL) return FALSE; } for (i = 0; i < num_rects; i++) { boxes[i].x1 = rects[i].x + tx; boxes[i].y1 = rects[i].y + ty; boxes[i].x2 = rects[i].x + tx + rects[i].width; boxes[i].y2 = rects[i].y + ty + rects[i].height; } ret = pixman_region_init_rects(region, boxes, num_rects); if (boxes != stack_boxes) free(boxes); return ret; } void uxa_solid_rects (CARD8 op, PicturePtr dst, xRenderColor *color, int num_rects, xRectangle *rects) { ScreenPtr screen = dst->pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(screen); PixmapPtr dst_pixmap, src_pixmap = NULL; pixman_region16_t region; pixman_box16_t *boxes, *extents; PicturePtr src; int dst_x, dst_y; int num_boxes; if (!pixman_region_not_empty(dst->pCompositeClip)) return; if (dst->alphaMap) goto fallback; dst_pixmap = uxa_get_offscreen_pixmap(dst->pDrawable, &dst_x, &dst_y); if (!dst_pixmap) goto fallback; if (!_pixman_region_init_rectangles(®ion, num_rects, rects, dst->pDrawable->x, dst->pDrawable->y)) goto fallback; if (!pixman_region_intersect(®ion, ®ion, dst->pCompositeClip)) { pixman_region_fini(®ion); return; } /* XXX xserver-1.8: CompositeRects is not tracked by Damage, so we must * manually append the damaged regions ourselves. */ DamageRegionAppend(dst->pDrawable, ®ion); pixman_region_translate(®ion, dst_x, dst_y); boxes = pixman_region_rectangles(®ion, &num_boxes); extents = pixman_region_extents (®ion); if (op == PictOpClear) color->red = color->green = color->blue = color->alpha = 0; if (color->alpha >= 0xff00 && op == PictOpOver) { color->alpha = 0xffff; op = PictOpSrc; } /* Using GEM, the relocation costs outweigh the advantages of the blitter */ if (num_boxes == 1 && (op == PictOpSrc || op == PictOpClear)) { CARD32 pixel; try_solid: if (uxa_screen->info->check_solid && !uxa_screen->info->check_solid(&dst_pixmap->drawable, GXcopy, FB_ALLONES)) goto err_region; if (!uxa_get_pixel_from_rgba(&pixel, color->red, color->green, color->blue, color->alpha, dst->format)) goto err_region; if (!uxa_screen->info->prepare_solid(dst_pixmap, GXcopy, FB_ALLONES, pixel)) goto err_region; while (num_boxes--) { uxa_screen->info->solid(dst_pixmap, boxes->x1, boxes->y1, boxes->x2, boxes->y2); boxes++; } uxa_screen->info->done_solid(dst_pixmap); } else { int error; src = CreateSolidPicture(0, color, &error); if (!src) goto err_region; if (!uxa_screen->info->check_composite(op, src, NULL, dst, extents->x2 - extents->x1, extents->y2 - extents->y1)) { if (op == PictOpSrc || op == PictOpClear) { FreePicture(src, 0); goto try_solid; } goto err_src; } if (!uxa_screen->info->check_composite_texture || !uxa_screen->info->check_composite_texture(screen, src)) { PicturePtr solid; int src_off_x, src_off_y; solid = uxa_acquire_solid(screen, src->pSourcePict); FreePicture(src, 0); src = solid; src_pixmap = uxa_get_offscreen_pixmap(src->pDrawable, &src_off_x, &src_off_y); if (!src_pixmap) goto err_src; } if (!uxa_screen->info->prepare_composite(op, src, NULL, dst, src_pixmap, NULL, dst_pixmap)) goto err_src; while (num_boxes--) { uxa_screen->info->composite(dst_pixmap, 0, 0, 0, 0, boxes->x1, boxes->y1, boxes->x2 - boxes->x1, boxes->y2 - boxes->y1); boxes++; } uxa_screen->info->done_composite(dst_pixmap); FreePicture(src, 0); } pixman_region_fini(®ion); return; err_src: FreePicture(src, 0); err_region: pixman_region_fini(®ion); fallback: uxa_screen->SavedCompositeRects(op, dst, color, num_rects, rects); } static int uxa_try_driver_composite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { ScreenPtr screen = pDst->pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(screen); RegionRec region; BoxPtr pbox; int nbox; int xDst_copy = 0, yDst_copy = 0; int src_off_x, src_off_y, mask_off_x, mask_off_y, dst_off_x, dst_off_y; PixmapPtr pSrcPix, pMaskPix = NULL, pDstPix; PicturePtr localSrc, localMask = NULL; PicturePtr localDst = pDst; if (uxa_screen->info->check_composite && !(*uxa_screen->info->check_composite) (op, pSrc, pMask, pDst, width, height)) return -1; if (uxa_screen->info->check_composite_target && !uxa_screen->info->check_composite_target(uxa_get_drawable_pixmap(pDst->pDrawable))) { int depth = pDst->pDrawable->depth; PixmapPtr pixmap; int error; GCPtr gc; pixmap = uxa_get_drawable_pixmap(pDst->pDrawable); if (uxa_screen->info->check_copy && !uxa_screen->info->check_copy(pixmap, pixmap, GXcopy, FB_ALLONES)) return -1; pixmap = screen->CreatePixmap(screen, width, height, depth, CREATE_PIXMAP_USAGE_SCRATCH); if (!pixmap) return 0; gc = GetScratchGC(depth, screen); if (!gc) { screen->DestroyPixmap(pixmap); return 0; } ValidateGC(&pixmap->drawable, gc); gc->ops->CopyArea(pDst->pDrawable, &pixmap->drawable, gc, xDst, yDst, width, height, 0, 0); FreeScratchGC(gc); xDst_copy = xDst; xDst = 0; yDst_copy = yDst; yDst = 0; localDst = CreatePicture(0, &pixmap->drawable, PictureMatchFormat(screen, depth, pDst->format), 0, 0, serverClient, &error); screen->DestroyPixmap(pixmap); if (!localDst) return 0; ValidatePicture(localDst); } pDstPix = uxa_get_offscreen_pixmap(localDst->pDrawable, &dst_off_x, &dst_off_y); if (!pDstPix) { if (localDst != pDst) FreePicture(localDst, 0); return -1; } xDst += localDst->pDrawable->x; yDst += localDst->pDrawable->y; localSrc = uxa_acquire_source(screen, pSrc, xSrc, ySrc, width, height, &xSrc, &ySrc); if (!localSrc) { if (localDst != pDst) FreePicture(localDst, 0); return 0; } if (pMask) { localMask = uxa_acquire_mask(screen, pMask, xMask, yMask, width, height, &xMask, &yMask); if (!localMask) { if (localSrc != pSrc) FreePicture(localSrc, 0); if (localDst != pDst) FreePicture(localDst, 0); return 0; } } if (!miComputeCompositeRegion(®ion, localSrc, localMask, localDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height)) { if (localSrc != pSrc) FreePicture(localSrc, 0); if (localMask && localMask != pMask) FreePicture(localMask, 0); if (localDst != pDst) FreePicture(localDst, 0); return 1; } if (localSrc->pDrawable) { pSrcPix = uxa_get_offscreen_pixmap(localSrc->pDrawable, &src_off_x, &src_off_y); if (!pSrcPix) { REGION_UNINIT(screen, ®ion); if (localSrc != pSrc) FreePicture(localSrc, 0); if (localMask && localMask != pMask) FreePicture(localMask, 0); if (localDst != pDst) FreePicture(localDst, 0); return 0; } } else { pSrcPix = NULL; } if (localMask) { if (localMask->pDrawable) { pMaskPix = uxa_get_offscreen_pixmap(localMask->pDrawable, &mask_off_x, &mask_off_y); if (!pMaskPix) { REGION_UNINIT(screen, ®ion); if (localSrc != pSrc) FreePicture(localSrc, 0); if (localMask && localMask != pMask) FreePicture(localMask, 0); if (localDst != pDst) FreePicture(localDst, 0); return 0; } } else { pMaskPix = NULL; } } if (!(*uxa_screen->info->prepare_composite) (op, localSrc, localMask, localDst, pSrcPix, pMaskPix, pDstPix)) { REGION_UNINIT(screen, ®ion); if (localSrc != pSrc) FreePicture(localSrc, 0); if (localMask && localMask != pMask) FreePicture(localMask, 0); if (localDst != pDst) FreePicture(localDst, 0); return -1; } if (pMask) { xMask = xMask + mask_off_x - xDst; yMask = yMask + mask_off_y - yDst; } xSrc = xSrc + src_off_x - xDst; ySrc = ySrc + src_off_y - yDst; nbox = REGION_NUM_RECTS(®ion); pbox = REGION_RECTS(®ion); while (nbox--) { (*uxa_screen->info->composite) (pDstPix, pbox->x1 + xSrc, pbox->y1 + ySrc, pbox->x1 + xMask, pbox->y1 + yMask, pbox->x1 + dst_off_x, pbox->y1 + dst_off_y, pbox->x2 - pbox->x1, pbox->y2 - pbox->y1); pbox++; } (*uxa_screen->info->done_composite) (pDstPix); REGION_UNINIT(screen, ®ion); if (localSrc != pSrc) FreePicture(localSrc, 0); if (localMask && localMask != pMask) FreePicture(localMask, 0); if (localDst != pDst) { GCPtr gc; gc = GetScratchGC(pDst->pDrawable->depth, screen); if (gc) { ValidateGC(pDst->pDrawable, gc); gc->ops->CopyArea(localDst->pDrawable, pDst->pDrawable, gc, 0, 0, width, height, xDst_copy, yDst_copy); FreeScratchGC(gc); } FreePicture(localDst, 0); } return 1; } /** * uxa_try_magic_two_pass_composite_helper implements PictOpOver using two passes of * simpler operations PictOpOutReverse and PictOpAdd. Mainly used for component * alpha and limited 1-tmu cards. * * From http://anholt.livejournal.com/32058.html: * * The trouble is that component-alpha rendering requires two different sources * for blending: one for the source value to the blender, which is the * per-channel multiplication of source and mask, and one for the source alpha * for multiplying with the destination channels, which is the multiplication * of the source channels by the mask alpha. So the equation for Over is: * * dst.A = src.A * mask.A + (1 - (src.A * mask.A)) * dst.A * dst.R = src.R * mask.R + (1 - (src.A * mask.R)) * dst.R * dst.G = src.G * mask.G + (1 - (src.A * mask.G)) * dst.G * dst.B = src.B * mask.B + (1 - (src.A * mask.B)) * dst.B * * But we can do some simpler operations, right? How about PictOpOutReverse, * which has a source factor of 0 and dest factor of (1 - source alpha). We * can get the source alpha value (srca.X = src.A * mask.X) out of the texture * blenders pretty easily. So we can do a component-alpha OutReverse, which * gets us: * * dst.A = 0 + (1 - (src.A * mask.A)) * dst.A * dst.R = 0 + (1 - (src.A * mask.R)) * dst.R * dst.G = 0 + (1 - (src.A * mask.G)) * dst.G * dst.B = 0 + (1 - (src.A * mask.B)) * dst.B * * OK. And if an op doesn't use the source alpha value for the destination * factor, then we can do the channel multiplication in the texture blenders * to get the source value, and ignore the source alpha that we wouldn't use. * We've supported this in the Radeon driver for a long time. An example would * be PictOpAdd, which does: * * dst.A = src.A * mask.A + dst.A * dst.R = src.R * mask.R + dst.R * dst.G = src.G * mask.G + dst.G * dst.B = src.B * mask.B + dst.B * * Hey, this looks good! If we do a PictOpOutReverse and then a PictOpAdd right * after it, we get: * * dst.A = src.A * mask.A + ((1 - (src.A * mask.A)) * dst.A) * dst.R = src.R * mask.R + ((1 - (src.A * mask.R)) * dst.R) * dst.G = src.G * mask.G + ((1 - (src.A * mask.G)) * dst.G) * dst.B = src.B * mask.B + ((1 - (src.A * mask.B)) * dst.B) */ static int uxa_try_magic_two_pass_composite_helper(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { ScreenPtr screen = pDst->pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(screen); PicturePtr localDst = pDst; int xDst_copy = 0, yDst_copy = 0; assert(op == PictOpOver); if (uxa_screen->info->check_composite && (!(*uxa_screen->info->check_composite) (PictOpOutReverse, pSrc, pMask, pDst, width, height) || !(*uxa_screen->info->check_composite) (PictOpAdd, pSrc, pMask, pDst, width, height))) { return -1; } if (uxa_screen->info->check_composite_target && !uxa_screen->info->check_composite_target(uxa_get_drawable_pixmap(pDst->pDrawable))) { int depth = pDst->pDrawable->depth; PixmapPtr pixmap; int error; GCPtr gc; pixmap = uxa_get_drawable_pixmap(pDst->pDrawable); if (uxa_screen->info->check_copy && !uxa_screen->info->check_copy(pixmap, pixmap, GXcopy, FB_ALLONES)) return -1; pixmap = screen->CreatePixmap(screen, width, height, depth, CREATE_PIXMAP_USAGE_SCRATCH); if (!pixmap) return 0; gc = GetScratchGC(depth, screen); if (!gc) { screen->DestroyPixmap(pixmap); return 0; } ValidateGC(&pixmap->drawable, gc); gc->ops->CopyArea(pDst->pDrawable, &pixmap->drawable, gc, xDst, yDst, width, height, 0, 0); FreeScratchGC(gc); xDst_copy = xDst; xDst = 0; yDst_copy = yDst; yDst = 0; localDst = CreatePicture(0, &pixmap->drawable, PictureMatchFormat(screen, depth, pDst->format), 0, 0, serverClient, &error); screen->DestroyPixmap(pixmap); if (!localDst) return 0; ValidatePicture(localDst); } /* Now, we think we should be able to accelerate this operation. First, * composite the destination to be the destination times the source alpha * factors. */ uxa_composite(PictOpOutReverse, pSrc, pMask, localDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); /* Then, add in the source value times the destination alpha factors (1.0). */ uxa_composite(PictOpAdd, pSrc, pMask, localDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); if (localDst != pDst) { GCPtr gc; gc = GetScratchGC(pDst->pDrawable->depth, screen); if (gc) { ValidateGC(pDst->pDrawable, gc); gc->ops->CopyArea(localDst->pDrawable, pDst->pDrawable, gc, 0, 0, width, height, xDst_copy, yDst_copy); FreeScratchGC(gc); } FreePicture(localDst, 0); } return 1; } static int compatible_formats (CARD8 op, PicturePtr dst, PicturePtr src) { if (op == PictOpSrc) { if (src->format == dst->format) return 1; /* Is the destination an alpha-less version of source? */ if (dst->format == PICT_FORMAT(PICT_FORMAT_BPP(src->format), PICT_FORMAT_TYPE(src->format), 0, PICT_FORMAT_R(src->format), PICT_FORMAT_G(src->format), PICT_FORMAT_B(src->format))) return 1; /* XXX xrgb is promoted to argb during image upload... */ #if 0 if (dst->format == PICT_a8r8g8b8 && src->format == PICT_x8r8g8b8) return 1; #endif } else if (op == PictOpOver) { if (PICT_FORMAT_A(src->format)) return 0; return src->format == dst->format; } return 0; } static int drawable_contains (DrawablePtr drawable, int x, int y, int w, int h) { if (x < 0 || y < 0) return FALSE; if (x + w > drawable->width) return FALSE; if (y + h > drawable->height) return FALSE; return TRUE; } void uxa_composite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { uxa_screen_t *uxa_screen = uxa_get_screen(pDst->pDrawable->pScreen); int ret = -1; Bool saveSrcRepeat = pSrc->repeat; Bool saveMaskRepeat = pMask ? pMask->repeat : 0; RegionRec region; int tx, ty; if (uxa_screen->swappedOut || uxa_screen->force_fallback) goto fallback; if (!uxa_drawable_is_offscreen(pDst->pDrawable)) goto fallback; if (pDst->alphaMap || pSrc->alphaMap || (pMask && pMask->alphaMap)) goto fallback; /* Remove repeat in source if useless */ if (pSrc->pDrawable && pSrc->repeat && pSrc->filter != PictFilterConvolution && transform_is_integer_translation(pSrc->transform, &tx, &ty) && (pSrc->pDrawable->width > 1 || pSrc->pDrawable->height > 1) && drawable_contains(pSrc->pDrawable, xSrc + tx, ySrc + ty, width, height)) pSrc->repeat = 0; if (!pMask) { if (op == PictOpClear) { PicturePtr clear = uxa_solid_clear(pDst->pDrawable->pScreen); if (clear && uxa_try_driver_solid_fill(clear, pDst, xSrc, ySrc, xDst, yDst, width, height) == 1) goto done; } if (pSrc->pDrawable == NULL) { if (pSrc->pSourcePict) { SourcePict *source = pSrc->pSourcePict; if (source->type == SourcePictTypeSolidFill) { if (op == PictOpSrc || (op == PictOpOver && (source->solidFill.color & 0xff000000) == 0xff000000)) { ret = uxa_try_driver_solid_fill(pSrc, pDst, xSrc, ySrc, xDst, yDst, width, height); if (ret == 1) goto done; } } } } else if (pSrc->pDrawable->width == 1 && pSrc->pDrawable->height == 1 && pSrc->repeat && (op == PictOpSrc || (op == PictOpOver && !PICT_FORMAT_A(pSrc->format)))) { ret = uxa_try_driver_solid_fill(pSrc, pDst, xSrc, ySrc, xDst, yDst, width, height); if (ret == 1) goto done; } else if (compatible_formats (op, pDst, pSrc) && pSrc->filter != PictFilterConvolution && transform_is_integer_translation(pSrc->transform, &tx, &ty)) { if (!pSrc->repeat && drawable_contains(pSrc->pDrawable, xSrc + tx, ySrc + ty, width, height)) { xDst += pDst->pDrawable->x; yDst += pDst->pDrawable->y; xSrc += pSrc->pDrawable->x + tx; ySrc += pSrc->pDrawable->y + ty; if (!miComputeCompositeRegion (®ion, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height)) goto done; uxa_copy_n_to_n(pSrc->pDrawable, pDst->pDrawable, NULL, REGION_RECTS(®ion), REGION_NUM_RECTS(®ion), xSrc - xDst, ySrc - yDst, FALSE, FALSE, 0, NULL); REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); goto done; } else if (pSrc->repeat && pSrc->repeatType == RepeatNormal && pSrc->pDrawable->type == DRAWABLE_PIXMAP) { DDXPointRec patOrg; /* Let's see if the driver can do the repeat * in one go */ if (uxa_screen->info->prepare_composite) { ret = uxa_try_driver_composite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); if (ret == 1) goto done; } /* Now see if we can use * uxa_fill_region_tiled() */ xDst += pDst->pDrawable->x; yDst += pDst->pDrawable->y; xSrc += pSrc->pDrawable->x + tx; ySrc += pSrc->pDrawable->y + ty; if (!miComputeCompositeRegion (®ion, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height)) goto done; /* pattern origin is the point in the * destination drawable * corresponding to (0,0) in the source */ patOrg.x = xDst - xSrc; patOrg.y = yDst - ySrc; ret = uxa_fill_region_tiled(pDst->pDrawable, ®ion, (PixmapPtr) pSrc-> pDrawable, &patOrg, FB_ALLONES, GXcopy); REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); if (ret) goto done; } } } /* Remove repeat in mask if useless */ if (pMask && pMask->pDrawable && pMask->repeat && pMask->filter != PictFilterConvolution && transform_is_integer_translation(pMask->transform, &tx, &ty) && (pMask->pDrawable->width > 1 || pMask->pDrawable->height > 1) && drawable_contains(pMask->pDrawable, xMask + tx, yMask + ty, width, height)) pMask->repeat = 0; if (uxa_screen->info->prepare_composite) { Bool isSrcSolid; ret = uxa_try_driver_composite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); if (ret == 1) goto done; /* For generic masks and solid src pictures, mach64 can do * Over in two passes, similar to the component-alpha case. */ isSrcSolid = pSrc->pDrawable ? pSrc->pDrawable->width == 1 && pSrc->pDrawable->height == 1 && pSrc->repeat : pSrc->pSourcePict ? pSrc->pSourcePict->type == SourcePictTypeSolidFill : 0; /* If we couldn't do the Composite in a single pass, and it * was a component-alpha Over, see if we can do it in two * passes with an OutReverse and then an Add. */ if (ret == -1 && op == PictOpOver && pMask && (pMask->componentAlpha || isSrcSolid)) { ret = uxa_try_magic_two_pass_composite_helper(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); if (ret == 1) goto done; } } fallback: uxa_print_composite_fallback("uxa_composite", op, pSrc, pMask, pDst); uxa_check_composite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); done: pSrc->repeat = saveSrcRepeat; if (pMask) pMask->repeat = saveMaskRepeat; } #endif /** * Same as miCreateAlphaPicture, except it uses uxa_check_poly_fill_rect instead * of PolyFillRect to initialize the pixmap after creating it, to prevent * the pixmap from being migrated. * * See the comments about uxa_trapezoids and uxa_triangles. */ static PicturePtr uxa_create_alpha_picture(ScreenPtr pScreen, PicturePtr pDst, PictFormatPtr pPictFormat, CARD16 width, CARD16 height) { PixmapPtr pPixmap; PicturePtr pPicture; int error; if (width > 32767 || height > 32767) return 0; if (!pPictFormat) { if (pDst->polyEdge == PolyEdgeSharp) pPictFormat = PictureMatchFormat(pScreen, 1, PICT_a1); else pPictFormat = PictureMatchFormat(pScreen, 8, PICT_a8); if (!pPictFormat) return 0; } pPixmap = (*pScreen->CreatePixmap) (pScreen, width, height, pPictFormat->depth, UXA_CREATE_PIXMAP_FOR_MAP); if (!pPixmap) return 0; pPicture = CreatePicture(0, &pPixmap->drawable, pPictFormat, 0, 0, serverClient, &error); (*pScreen->DestroyPixmap) (pPixmap); return pPicture; } /** * uxa_trapezoids is essentially a copy of miTrapezoids that uses * uxa_create_alpha_picture instead of miCreateAlphaPicture. * * The problem with miCreateAlphaPicture is that it calls PolyFillRect * to initialize the contents after creating the pixmap, which * causes the pixmap to be moved in for acceleration. The subsequent * call to RasterizeTrapezoid won't be accelerated however, which * forces the pixmap to be moved out again. * * uxa_create_alpha_picture avoids this roundtrip by using * uxa_check_poly_fill_rect to initialize the contents. */ void uxa_trapezoids(CARD8 op, PicturePtr src, PicturePtr dst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int ntrap, xTrapezoid * traps) { ScreenPtr screen = dst->pDrawable->pScreen; BoxRec bounds; Bool direct; direct = op == PictOpAdd && miIsSolidAlpha(src); if (maskFormat || direct) { miTrapezoidBounds(ntrap, traps, &bounds); if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2) return; } /* * Check for solid alpha add */ if (direct) { DrawablePtr pDraw = dst->pDrawable; PixmapPtr pixmap = uxa_get_drawable_pixmap(pDraw); int xoff, yoff; uxa_get_drawable_deltas(pDraw, pixmap, &xoff, &yoff); xoff += pDraw->x; yoff += pDraw->y; if (uxa_prepare_access(pDraw, NULL, UXA_ACCESS_RW)) { PictureScreenPtr ps = GetPictureScreen(screen); for (; ntrap; ntrap--, traps++) (*ps->RasterizeTrapezoid) (dst, traps, 0, 0); uxa_finish_access(pDraw); } } else if (maskFormat) { PixmapPtr scratch = NULL; PicturePtr mask; INT16 xDst, yDst; INT16 xRel, yRel; int width, height; pixman_image_t *image; pixman_format_code_t format; xDst = traps[0].left.p1.x >> 16; yDst = traps[0].left.p1.y >> 16; width = bounds.x2 - bounds.x1; height = bounds.y2 - bounds.y1; format = maskFormat->format | (BitsPerPixel(maskFormat->depth) << 24); image = pixman_image_create_bits(format, width, height, NULL, 0); if (!image) return; for (; ntrap; ntrap--, traps++) pixman_rasterize_trapezoid(image, (pixman_trapezoid_t *) traps, -bounds.x1, -bounds.y1); if (uxa_drawable_is_offscreen(dst->pDrawable)) { mask = uxa_picture_from_pixman_image(screen, image, format); } else { int error; scratch = GetScratchPixmapHeader(screen, width, height, PIXMAN_FORMAT_DEPTH(format), PIXMAN_FORMAT_BPP(format), pixman_image_get_stride(image), pixman_image_get_data(image)); mask = CreatePicture(0, &scratch->drawable, PictureMatchFormat(screen, PIXMAN_FORMAT_DEPTH(format), format), 0, 0, serverClient, &error); } if (!mask) { if (scratch) FreeScratchPixmapHeader(scratch); pixman_image_unref(image); return; } xRel = bounds.x1 + xSrc - xDst; yRel = bounds.y1 + ySrc - yDst; CompositePicture(op, src, mask, dst, xRel, yRel, 0, 0, bounds.x1, bounds.y1, width, height); FreePicture(mask, 0); if (scratch) FreeScratchPixmapHeader(scratch); pixman_image_unref(image); } else { if (dst->polyEdge == PolyEdgeSharp) maskFormat = PictureMatchFormat(screen, 1, PICT_a1); else maskFormat = PictureMatchFormat(screen, 8, PICT_a8); for (; ntrap; ntrap--, traps++) uxa_trapezoids(op, src, dst, maskFormat, xSrc, ySrc, 1, traps); } } /** * uxa_triangles is essentially a copy of miTriangles that uses * uxa_create_alpha_picture instead of miCreateAlphaPicture. * * The problem with miCreateAlphaPicture is that it calls PolyFillRect * to initialize the contents after creating the pixmap, which * causes the pixmap to be moved in for acceleration. The subsequent * call to AddTriangles won't be accelerated however, which forces the pixmap * to be moved out again. * * uxa_create_alpha_picture avoids this roundtrip by using * uxa_check_poly_fill_rect to initialize the contents. */ void uxa_triangles(CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int ntri, xTriangle * tris) { ScreenPtr pScreen = pDst->pDrawable->pScreen; PictureScreenPtr ps = GetPictureScreen(pScreen); BoxRec bounds; Bool direct = op == PictOpAdd && miIsSolidAlpha(pSrc); if (maskFormat || direct) { miTriangleBounds(ntri, tris, &bounds); if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2) return; } /* * Check for solid alpha add */ if (direct) { DrawablePtr pDraw = pDst->pDrawable; if (uxa_prepare_access(pDraw, NULL, UXA_ACCESS_RW)) { (*ps->AddTriangles) (pDst, 0, 0, ntri, tris); uxa_finish_access(pDraw); } } else if (maskFormat) { PicturePtr pPicture; INT16 xDst, yDst; INT16 xRel, yRel; int width = bounds.x2 - bounds.x1; int height = bounds.y2 - bounds.y1; GCPtr pGC; xRectangle rect; xDst = tris[0].p1.x >> 16; yDst = tris[0].p1.y >> 16; pPicture = uxa_create_alpha_picture(pScreen, pDst, maskFormat, width, height); if (!pPicture) return; /* Clear the alpha picture to 0. */ pGC = GetScratchGC(pPicture->pDrawable->depth, pScreen); if (!pGC) { FreePicture(pPicture, 0); return; } ValidateGC(pPicture->pDrawable, pGC); rect.x = 0; rect.y = 0; rect.width = width; rect.height = height; uxa_check_poly_fill_rect(pPicture->pDrawable, pGC, 1, &rect); FreeScratchGC(pGC); if (uxa_prepare_access(pPicture->pDrawable, NULL, UXA_ACCESS_RW)) { (*ps->AddTriangles) (pPicture, -bounds.x1, -bounds.y1, ntri, tris); uxa_finish_access(pPicture->pDrawable); } xRel = bounds.x1 + xSrc - xDst; yRel = bounds.y1 + ySrc - yDst; CompositePicture(op, pSrc, pPicture, pDst, xRel, yRel, 0, 0, bounds.x1, bounds.y1, bounds.x2 - bounds.x1, bounds.y2 - bounds.y1); FreePicture(pPicture, 0); } else { if (pDst->polyEdge == PolyEdgeSharp) maskFormat = PictureMatchFormat(pScreen, 1, PICT_a1); else maskFormat = PictureMatchFormat(pScreen, 8, PICT_a8); for (; ntri; ntri--, tris++) uxa_triangles(op, pSrc, pDst, maskFormat, xSrc, ySrc, 1, tris); } } xserver-xorg-video-qxl-0.1.5+git20200331/src/uxa/uxa-unaccel.c000066400000000000000000000340601361660122400234310ustar00rootroot00000000000000/* * * Copyright © 1999 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "uxa-priv.h" #include "uxa-damage.h" #ifdef RENDER #include "mipict.h" #endif /* * These functions wrap the low-level fb rendering functions and * synchronize framebuffer/accelerated drawing by stalling until * the accelerator is idle */ /** * Calls uxa_prepare_access with UXA_PREPARE_SRC for the tile, if that is the * current fill style. * * Solid doesn't use an extra pixmap source, and Stippled/OpaqueStippled are * 1bpp and never in fb, so we don't worry about them. * We should worry about them for completeness sake and going forward. */ Bool uxa_prepare_access_gc(GCPtr pGC) { if (pGC->stipple) if (!uxa_prepare_access(&pGC->stipple->drawable, NULL, UXA_ACCESS_RO)) return FALSE; if (pGC->fillStyle == FillTiled) if (!uxa_prepare_access (&pGC->tile.pixmap->drawable, NULL, UXA_ACCESS_RO)) { if (pGC->stipple) uxa_finish_access(&pGC->stipple->drawable); return FALSE; } return TRUE; } /** * Finishes access to the tile in the GC, if used. */ void uxa_finish_access_gc(GCPtr pGC) { if (pGC->fillStyle == FillTiled) uxa_finish_access(&pGC->tile.pixmap->drawable); if (pGC->stipple) uxa_finish_access(&pGC->stipple->drawable); } char uxa_drawable_location(DrawablePtr pDrawable) { return uxa_drawable_is_offscreen(pDrawable) ? 's' : 'm'; } void uxa_check_fill_spans(DrawablePtr pDrawable, GCPtr pGC, int nspans, DDXPointPtr ppt, int *pwidth, int fSorted) { ScreenPtr screen = pDrawable->pScreen; RegionRec region; REGION_INIT (screen, ®ion, (BoxPtr)NULL, 0); uxa_damage_fill_spans (®ion, pDrawable, pGC, nspans, ppt, pwidth, fSorted); UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); if (uxa_prepare_access(pDrawable, ®ion, UXA_ACCESS_RW)) { if (uxa_prepare_access_gc(pGC)) { fbFillSpans(pDrawable, pGC, nspans, ppt, pwidth, fSorted); uxa_finish_access_gc(pGC); } uxa_finish_access(pDrawable); } REGION_UNINIT (screen, ®ion); } void uxa_check_set_spans(DrawablePtr pDrawable, GCPtr pGC, char *psrc, DDXPointPtr ppt, int *pwidth, int nspans, int fSorted) { ScreenPtr screen = pDrawable->pScreen; UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); if (uxa_prepare_access(pDrawable, NULL, UXA_ACCESS_RW)) { fbSetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted); uxa_finish_access(pDrawable); } } void uxa_check_put_image(DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, int w, int h, int leftPad, int format, char *bits) { ScreenPtr screen = pDrawable->pScreen; UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); if (uxa_prepare_access(pDrawable, NULL, UXA_ACCESS_RW)) { fbPutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format, bits); uxa_finish_access(pDrawable); } } RegionPtr uxa_check_copy_area(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty) { ScreenPtr screen = pSrc->pScreen; RegionPtr ret = NULL; RegionRec src_region; RegionRec dst_region; BoxRec src_box = { srcx, srcy, srcx + w, srcy + h }; BoxRec dst_box = { dstx, dsty, dstx + w, dsty + h }; REGION_INIT (screen, &src_region, &src_box, 1); REGION_INIT (screen, &dst_region, &dst_box, 1); /* FIXME: Hmm, it's not totally clear what to do in this case. In fact, * all cases where more than one drawable can get prepare_access() called * on it multiple times is kinda bad. */ UXA_FALLBACK(("from %p to %p (%c,%c)\n", pSrc, pDst, uxa_drawable_location(pSrc), uxa_drawable_location(pDst))); if (uxa_prepare_access(pDst, &dst_region, UXA_ACCESS_RW)) { if (uxa_prepare_access(pSrc, &src_region, UXA_ACCESS_RO)) { ret = fbCopyArea(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty); uxa_finish_access(pSrc); } uxa_finish_access(pDst); } REGION_UNINIT (screen, &src_region); REGION_UNINIT (screen, &dst_region); return ret; } RegionPtr uxa_check_copy_plane(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty, unsigned long bitPlane) { ScreenPtr screen = pSrc->pScreen; RegionPtr ret = NULL; UXA_FALLBACK(("from %p to %p (%c,%c)\n", pSrc, pDst, uxa_drawable_location(pSrc), uxa_drawable_location(pDst))); if (uxa_prepare_access(pDst, NULL, UXA_ACCESS_RW)) { if (uxa_prepare_access(pSrc, NULL, UXA_ACCESS_RO)) { ret = fbCopyPlane(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, bitPlane); uxa_finish_access(pSrc); } uxa_finish_access(pDst); } return ret; } void uxa_check_poly_point(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr pptInit) { ScreenPtr screen = pDrawable->pScreen; UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); if (uxa_prepare_access(pDrawable, NULL, UXA_ACCESS_RW)) { fbPolyPoint(pDrawable, pGC, mode, npt, pptInit); uxa_finish_access(pDrawable); } } void uxa_check_poly_lines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr ppt) { ScreenPtr screen = pDrawable->pScreen; RegionRec region; REGION_INIT (screen, ®ion, (BoxPtr)NULL, 0); uxa_damage_poly_lines (®ion, pDrawable, pGC, mode, npt, ppt); UXA_FALLBACK(("to %p (%c), width %d, mode %d, count %d\n", pDrawable, uxa_drawable_location(pDrawable), pGC->lineWidth, mode, npt)); if (pGC->lineWidth == 0) { if (uxa_prepare_access(pDrawable, ®ion, UXA_ACCESS_RW)) { if (uxa_prepare_access_gc(pGC)) { fbPolyLine(pDrawable, pGC, mode, npt, ppt); uxa_finish_access_gc(pGC); } uxa_finish_access(pDrawable); } goto out; } /* fb calls mi functions in the lineWidth != 0 case. */ fbPolyLine(pDrawable, pGC, mode, npt, ppt); out: REGION_UNINIT (screen, ®ion); } void uxa_check_poly_segment(DrawablePtr pDrawable, GCPtr pGC, int nsegInit, xSegment * pSegInit) { ScreenPtr screen = pDrawable->pScreen; RegionRec region; REGION_INIT (screen, ®ion, (BoxPtr)NULL, 0); uxa_damage_poly_segment (®ion, pDrawable, pGC, nsegInit, pSegInit); UXA_FALLBACK(("to %p (%c) width %d, count %d\n", pDrawable, uxa_drawable_location(pDrawable), pGC->lineWidth, nsegInit)); if (pGC->lineWidth == 0) { if (uxa_prepare_access(pDrawable, ®ion, UXA_ACCESS_RW)) { if (uxa_prepare_access_gc(pGC)) { fbPolySegment(pDrawable, pGC, nsegInit, pSegInit); uxa_finish_access_gc(pGC); } uxa_finish_access(pDrawable); } goto out; } /* fb calls mi functions in the lineWidth != 0 case. */ fbPolySegment(pDrawable, pGC, nsegInit, pSegInit); out: REGION_UNINIT (screen, ®ion); } void uxa_check_poly_arc(DrawablePtr pDrawable, GCPtr pGC, int narcs, xArc * pArcs) { ScreenPtr screen = pDrawable->pScreen; UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); /* Disable this as fbPolyArc can call miZeroPolyArc which in turn * can call accelerated functions, that as yet, haven't been notified * with uxa_finish_access(). */ #if 0 if (pGC->lineWidth == 0) { if (uxa_prepare_access(pDrawable, UXA_ACCESS_RW)) { if (uxa_prepare_access_gc(pGC)) { fbPolyArc(pDrawable, pGC, narcs, pArcs); uxa_finish_access_gc(pGC); } uxa_finish_access(pDrawable); } return; } #endif miPolyArc(pDrawable, pGC, narcs, pArcs); } void uxa_check_poly_fill_rect(DrawablePtr pDrawable, GCPtr pGC, int nrect, xRectangle * prect) { ScreenPtr screen = pDrawable->pScreen; RegionRec region; REGION_INIT (screen, ®ion, (BoxPtr)NULL, 0); uxa_damage_poly_fill_rect (®ion, pDrawable, pGC, nrect, prect); UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); if (uxa_prepare_access(pDrawable, ®ion, UXA_ACCESS_RW)) { if (uxa_prepare_access_gc(pGC)) { fbPolyFillRect(pDrawable, pGC, nrect, prect); uxa_finish_access_gc(pGC); } uxa_finish_access(pDrawable); } REGION_UNINIT (screen, ®ion); } void uxa_check_image_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr * ppci, pointer pglyphBase) { ScreenPtr screen = pDrawable->pScreen; RegionRec region; REGION_INIT (screen, ®ion, (BoxPtr)NULL, 0); uxa_damage_image_glyph_blt (®ion, pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); UXA_FALLBACK(("to %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); if (uxa_prepare_access(pDrawable, ®ion, UXA_ACCESS_RW)) { if (uxa_prepare_access_gc(pGC)) { fbImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); uxa_finish_access_gc(pGC); } uxa_finish_access(pDrawable); } REGION_UNINIT (screen, ®ion); } void uxa_check_poly_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr * ppci, pointer pglyphBase) { ScreenPtr screen = pDrawable->pScreen; RegionRec region; REGION_INIT (screen, ®ion, (BoxPtr)NULL, 0); uxa_damage_poly_glyph_blt (®ion, pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); UXA_FALLBACK(("to %p (%c), style %d alu %d\n", pDrawable, uxa_drawable_location(pDrawable), pGC->fillStyle, pGC->alu)); if (uxa_prepare_access(pDrawable, ®ion, UXA_ACCESS_RW)) { if (uxa_prepare_access_gc(pGC)) { fbPolyGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); uxa_finish_access_gc(pGC); } uxa_finish_access(pDrawable); } REGION_UNINIT (screen, ®ion); } void uxa_check_push_pixels(GCPtr pGC, PixmapPtr pBitmap, DrawablePtr pDrawable, int w, int h, int x, int y) { ScreenPtr screen = pDrawable->pScreen; RegionRec region; REGION_INIT (screen, ®ion, (BoxPtr)NULL, 0); uxa_damage_push_pixels (®ion, pGC, pBitmap, pDrawable, w, h, x, y); UXA_FALLBACK(("from %p to %p (%c,%c)\n", pBitmap, pDrawable, uxa_drawable_location(&pBitmap->drawable), uxa_drawable_location(pDrawable))); if (uxa_prepare_access(pDrawable, ®ion, UXA_ACCESS_RW)) { if (uxa_prepare_access(&pBitmap->drawable, NULL, UXA_ACCESS_RO)) { if (uxa_prepare_access_gc(pGC)) { fbPushPixels(pGC, pBitmap, pDrawable, w, h, x, y); uxa_finish_access_gc(pGC); } uxa_finish_access(&pBitmap->drawable); } uxa_finish_access(pDrawable); } REGION_UNINIT (screen, ®ion); } void uxa_check_get_spans(DrawablePtr pDrawable, int wMax, DDXPointPtr ppt, int *pwidth, int nspans, char *pdstStart) { ScreenPtr screen = pDrawable->pScreen; UXA_FALLBACK(("from %p (%c)\n", pDrawable, uxa_drawable_location(pDrawable))); if (uxa_prepare_access(pDrawable, NULL, UXA_ACCESS_RO)) { fbGetSpans(pDrawable, wMax, ppt, pwidth, nspans, pdstStart); uxa_finish_access(pDrawable); } } void uxa_check_composite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { ScreenPtr screen = pDst->pDrawable->pScreen; RegionRec region; UXA_FALLBACK(("from picts %p/%p to pict %p\n", pSrc, pMask, pDst)); REGION_INIT (screen, ®ion, (BoxPtr)NULL, 0); uxa_damage_composite (®ion, op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); #if 0 ErrorF ("destination: %p\n", pDst->pDrawable); ErrorF ("source: %p\n", pSrc->pDrawable); ErrorF ("mask: %p\n", pMask? pMask->pDrawable : NULL); #endif if (uxa_prepare_access(pDst->pDrawable, ®ion, UXA_ACCESS_RW)) { if (pSrc->pDrawable == NULL || uxa_prepare_access(pSrc->pDrawable, NULL, UXA_ACCESS_RO)) { if (!pMask || pMask->pDrawable == NULL || uxa_prepare_access(pMask->pDrawable, NULL, UXA_ACCESS_RO)) { fbComposite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); if (pMask && pMask->pDrawable != NULL) uxa_finish_access(pMask->pDrawable); } if (pSrc->pDrawable != NULL) uxa_finish_access(pSrc->pDrawable); } uxa_finish_access(pDst->pDrawable); } } void uxa_check_add_traps(PicturePtr pPicture, INT16 x_off, INT16 y_off, int ntrap, xTrap * traps) { ScreenPtr screen = pPicture->pDrawable->pScreen; UXA_FALLBACK(("to pict %p (%c)\n", pPicture, uxa_drawable_location(pPicture->pDrawable))); if (uxa_prepare_access(pPicture->pDrawable, NULL, UXA_ACCESS_RW)) { fbAddTraps(pPicture, x_off, y_off, ntrap, traps); uxa_finish_access(pPicture->pDrawable); } } /** * Gets the 0,0 pixel of a pixmap. Used for doing solid fills of tiled pixmaps * that happen to be 1x1. Pixmap must be at least 8bpp. * * XXX This really belongs in fb, so it can be aware of tiling and etc. */ CARD32 uxa_get_pixmap_first_pixel(PixmapPtr pPixmap) { CARD32 pixel; void *fb; if (!uxa_prepare_access(&pPixmap->drawable, NULL, UXA_ACCESS_RO)) return 0; fb = pPixmap->devPrivate.ptr; switch (pPixmap->drawable.bitsPerPixel) { case 32: pixel = *(CARD32 *) fb; break; case 16: pixel = *(CARD16 *) fb; break; default: pixel = *(CARD8 *) fb; break; } uxa_finish_access(&pPixmap->drawable); return pixel; } xserver-xorg-video-qxl-0.1.5+git20200331/src/uxa/uxa.c000066400000000000000000000423101361660122400220160ustar00rootroot00000000000000/* * Copyright © 2001 Keith Packard * * Partly based on code that is Copyright © The XFree86 Project Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /** @file * This file covers the initialization and teardown of UXA, and has various * functions not responsible for performing rendering, pixmap migration, or * memory management. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include "uxa-priv.h" #include "uxa.h" #if HAS_DEVPRIVATEKEYREC DevPrivateKeyRec uxa_screen_index; #else int uxa_screen_index; #endif /** * uxa_get_drawable_pixmap() returns a backing pixmap for a given drawable. * * @param pDrawable the drawable being requested. * * This function returns the backing pixmap for a drawable, whether it is a * redirected window, unredirected window, or already a pixmap. Note that * coordinate translation is needed when drawing to the backing pixmap of a * redirected window, and the translation coordinates are provided by calling * uxa_get_drawable_pixmap() on the drawable. */ PixmapPtr uxa_get_drawable_pixmap(DrawablePtr pDrawable) { if (pDrawable->type == DRAWABLE_WINDOW) return pDrawable->pScreen->GetWindowPixmap((WindowPtr) pDrawable); else return (PixmapPtr) pDrawable; } /** * Sets the offsets to add to coordinates to make them address the same bits in * the backing drawable. These coordinates are nonzero only for redirected * windows. */ void uxa_get_drawable_deltas(DrawablePtr pDrawable, PixmapPtr pPixmap, int *xp, int *yp) { #ifdef COMPOSITE if (pDrawable->type == DRAWABLE_WINDOW) { *xp = -pPixmap->screen_x; *yp = -pPixmap->screen_y; return; } #endif *xp = 0; *yp = 0; } /** * uxa_pixmap_is_offscreen() is used to determine if a pixmap is in offscreen * memory, meaning that acceleration could probably be done to it, and that it * will need to be wrapped by PrepareAccess()/FinishAccess() when accessing it * with the CPU. * * Note that except for UploadToScreen()/DownloadFromScreen() (which explicitly * deal with moving pixmaps in and out of system memory), UXA will give drivers * pixmaps as arguments for which uxa_pixmap_is_offscreen() is TRUE. * * @return TRUE if the given drawable is in framebuffer memory. */ Bool uxa_pixmap_is_offscreen(PixmapPtr p) { ScreenPtr pScreen = p->drawable.pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); if (uxa_screen->info->pixmap_is_offscreen) return uxa_screen->info->pixmap_is_offscreen(p); return FALSE; } /** * uxa_drawable_is_offscreen() is a convenience wrapper for * uxa_pixmap_is_offscreen(). */ Bool uxa_drawable_is_offscreen(DrawablePtr pDrawable) { return uxa_pixmap_is_offscreen(uxa_get_drawable_pixmap(pDrawable)); } /** * Returns the pixmap which backs a drawable, and the offsets to add to * coordinates to make them address the same bits in the backing drawable. */ PixmapPtr uxa_get_offscreen_pixmap(DrawablePtr drawable, int *xp, int *yp) { PixmapPtr pixmap = uxa_get_drawable_pixmap(drawable); uxa_get_drawable_deltas(drawable, pixmap, xp, yp); if (uxa_pixmap_is_offscreen(pixmap)) return pixmap; else return NULL; } /** * uxa_prepare_access() is UXA's wrapper for the driver's PrepareAccess() handler. * * It deals with waiting for synchronization with the card, determining if * PrepareAccess() is necessary, and working around PrepareAccess() failure. */ Bool uxa_prepare_access(DrawablePtr pDrawable, RegionPtr region, uxa_access_t access) { ScreenPtr pScreen = pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); int xoff, yoff; PixmapPtr pPixmap = uxa_get_offscreen_pixmap(pDrawable, &xoff, &yoff); BoxRec box; RegionRec region_rec; Bool result; if (!pPixmap) return TRUE; if (!region) { box.x1 = 0; box.y1 = 0; box.x2 = pDrawable->width; box.y2 = pDrawable->height; REGION_INIT (pScreen, ®ion_rec, &box, 1); region = ®ion_rec; } else { /* The driver expects a region in drawable coordinates */ REGION_TRANSLATE (pScreen, region, xoff, yoff); } result = TRUE; if (uxa_screen->info->prepare_access) result = (*uxa_screen->info->prepare_access) (pPixmap, region, access); if (region == ®ion_rec) REGION_UNINIT (pScreen, ®ion_rec); return result; } /** * uxa_finish_access() is UXA's wrapper for the driver's finish_access() handler. * * It deals with calling the driver's finish_access() only if necessary. */ void uxa_finish_access(DrawablePtr pDrawable) { ScreenPtr pScreen = pDrawable->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); PixmapPtr pPixmap = uxa_get_drawable_pixmap(pDrawable); if (uxa_screen->info->finish_access == NULL) return; if (!uxa_pixmap_is_offscreen(pPixmap)) return; (*uxa_screen->info->finish_access) (pPixmap); } /** * uxa_validate_gc() sets the ops to UXA's implementations, which may be * accelerated or may sync the card and fall back to fb. */ static void uxa_validate_gc(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable) { /* fbValidateGC will do direct access to pixmaps if the tiling has * changed. * Preempt fbValidateGC by doing its work and masking the change out, so * that we can do the Prepare/finish_access. */ #ifdef FB_24_32BIT if ((changes & GCTile) && fbGetRotatedPixmap(pGC)) { (*pGC->pScreen->DestroyPixmap) (fbGetRotatedPixmap(pGC)); fbGetRotatedPixmap(pGC) = 0; } if (pGC->fillStyle == FillTiled) { PixmapPtr pOldTile, pNewTile; pOldTile = pGC->tile.pixmap; if (pOldTile->drawable.bitsPerPixel != pDrawable->bitsPerPixel) { pNewTile = fbGetRotatedPixmap(pGC); if (!pNewTile || pNewTile->drawable.bitsPerPixel != pDrawable->bitsPerPixel) { if (pNewTile) (*pGC->pScreen-> DestroyPixmap) (pNewTile); /* fb24_32ReformatTile will do direct access * of a newly-allocated pixmap. This isn't a * problem yet, since we don't put pixmaps in * FB until at least one accelerated UXA op. */ if (uxa_prepare_access (&pOldTile->drawable, NULL, UXA_ACCESS_RO)) { pNewTile = fb24_32ReformatTile(pOldTile, pDrawable-> bitsPerPixel); uxa_finish_access(&pOldTile->drawable); } } if (pNewTile) { fbGetRotatedPixmap(pGC) = pOldTile; pGC->tile.pixmap = pNewTile; changes |= GCTile; } } } #endif if (changes & GCTile) { if (!pGC->tileIsPixel && FbEvenTile(pGC->tile.pixmap->drawable.width * pDrawable->bitsPerPixel)) { if (uxa_prepare_access (&pGC->tile.pixmap->drawable, NULL, UXA_ACCESS_RW)) { fbPadPixmap(pGC->tile.pixmap); uxa_finish_access(&pGC->tile.pixmap->drawable); } } /* Mask out the GCTile change notification, now that we've * done FB's job for it. */ changes &= ~GCTile; } if (changes & GCStipple && pGC->stipple) { /* We can't inline stipple handling like we do for GCTile * because it sets fbgc privates. */ if (uxa_prepare_access(&pGC->stipple->drawable, NULL, UXA_ACCESS_RW)) { fbValidateGC(pGC, changes, pDrawable); uxa_finish_access(&pGC->stipple->drawable); } } else { fbValidateGC(pGC, changes, pDrawable); } pGC->ops = (GCOps *)&uxa_ops; } static GCFuncs uxaGCFuncs = { uxa_validate_gc, miChangeGC, miCopyGC, miDestroyGC, miChangeClip, miDestroyClip, miCopyClip }; /** * uxa_create_gc makes a new GC and hooks up its funcs handler, so that * uxa_validate_gc() will get called. */ static int uxa_create_gc(GCPtr pGC) { if (!fbCreateGC(pGC)) return FALSE; pGC->funcs = &uxaGCFuncs; return TRUE; } Bool uxa_prepare_access_window(WindowPtr pWin) { if (pWin->backgroundState == BackgroundPixmap) { if (!uxa_prepare_access (&pWin->background.pixmap->drawable, NULL, UXA_ACCESS_RO)) return FALSE; } if (pWin->borderIsPixel == FALSE) { if (!uxa_prepare_access (&pWin->border.pixmap->drawable, NULL, UXA_ACCESS_RO)) { if (pWin->backgroundState == BackgroundPixmap) uxa_finish_access(&pWin->background.pixmap-> drawable); return FALSE; } } return TRUE; } void uxa_finish_access_window(WindowPtr pWin) { if (pWin->backgroundState == BackgroundPixmap) uxa_finish_access(&pWin->background.pixmap->drawable); if (pWin->borderIsPixel == FALSE) uxa_finish_access(&pWin->border.pixmap->drawable); } static Bool uxa_change_window_attributes(WindowPtr pWin, unsigned long mask) { Bool ret; Bool need_access = !!(mask & (CWBackPixmap | CWBorderPixmap)); if (need_access) { if (!uxa_prepare_access_window(pWin)) return FALSE; } ret = fbChangeWindowAttributes(pWin, mask); if (need_access) uxa_finish_access_window(pWin); return ret; } static RegionPtr uxa_bitmap_to_region(PixmapPtr pPix) { RegionPtr ret; if (!uxa_prepare_access(&pPix->drawable, NULL, UXA_ACCESS_RO)) return NULL; ret = fbPixmapToRegion(pPix); uxa_finish_access(&pPix->drawable); return ret; } static void uxa_xorg_enable_disable_fb_access(SCRN_ARG_TYPE arg, Bool enable) { SCRN_INFO_PTR(arg); ScreenPtr pScreen = pScrn->pScreen; uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); if (!enable && uxa_screen->disableFbCount++ == 0) uxa_screen->swappedOut = TRUE; if (enable && --uxa_screen->disableFbCount == 0) uxa_screen->swappedOut = FALSE; if (uxa_screen->SavedEnableDisableFBAccess) uxa_screen->SavedEnableDisableFBAccess(arg, enable); } void uxa_set_fallback_debug(ScreenPtr screen, Bool enable) { uxa_screen_t *uxa_screen = uxa_get_screen(screen); uxa_screen->fallback_debug = enable; } void uxa_set_force_fallback(ScreenPtr screen, Bool value) { uxa_screen_t *uxa_screen = uxa_get_screen(screen); uxa_screen->force_fallback = value; } Bool uxa_swapped_out(ScreenPtr screen) { uxa_screen_t *uxa_screen = uxa_get_screen (screen); return uxa_screen->swappedOut; } /** * uxa_close_screen() unwraps its wrapped screen functions and tears down UXA's * screen private, before calling down to the next CloseSccreen. */ static Bool uxa_close_screen(CLOSE_SCREEN_ARGS_DECL) { uxa_screen_t *uxa_screen = uxa_get_screen(pScreen); ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen); #ifdef RENDER PictureScreenPtr ps = GetPictureScreenIfSet(pScreen); #endif int n; if (uxa_screen->solid_clear) FreePicture(uxa_screen->solid_clear, 0); if (uxa_screen->solid_black) FreePicture(uxa_screen->solid_black, 0); if (uxa_screen->solid_white) FreePicture(uxa_screen->solid_white, 0); for (n = 0; n < uxa_screen->solid_cache_size; n++) FreePicture(uxa_screen->solid_cache[n].picture, 0); uxa_glyphs_fini(pScreen); pScreen->CreateGC = uxa_screen->SavedCreateGC; pScreen->CloseScreen = uxa_screen->SavedCloseScreen; pScreen->GetImage = uxa_screen->SavedGetImage; pScreen->GetSpans = uxa_screen->SavedGetSpans; pScreen->CreatePixmap = uxa_screen->SavedCreatePixmap; pScreen->DestroyPixmap = uxa_screen->SavedDestroyPixmap; pScreen->CopyWindow = uxa_screen->SavedCopyWindow; pScreen->ChangeWindowAttributes = uxa_screen->SavedChangeWindowAttributes; pScreen->BitmapToRegion = uxa_screen->SavedBitmapToRegion; scrn->EnableDisableFBAccess = uxa_screen->SavedEnableDisableFBAccess; #ifdef RENDER if (ps) { ps->Composite = uxa_screen->SavedComposite; ps->CompositeRects = uxa_screen->SavedCompositeRects; ps->Glyphs = uxa_screen->SavedGlyphs; ps->Trapezoids = uxa_screen->SavedTrapezoids; ps->AddTraps = uxa_screen->SavedAddTraps; ps->Triangles = uxa_screen->SavedTriangles; ps->UnrealizeGlyph = uxa_screen->SavedUnrealizeGlyph; } #endif free(uxa_screen); return (*pScreen->CloseScreen) (CLOSE_SCREEN_ARGS); } /** * This function allocates a driver structure for UXA drivers to fill in. By * having UXA allocate the structure, the driver structure can be extended * without breaking ABI between UXA and the drivers. The driver's * responsibility is to check beforehand that the UXA module has a matching * major number and sufficient minor. Drivers are responsible for freeing the * driver structure using xfree(). * * @return a newly allocated, zero-filled driver structure */ uxa_driver_t *uxa_driver_alloc(void) { return calloc(1, sizeof(uxa_driver_t)); } /** * @param pScreen screen being initialized * @param pScreenInfo UXA driver record * * uxa_driver_init sets up UXA given a driver record filled in by the driver. * pScreenInfo should have been allocated by uxa_driver_alloc(). See the * comments in _UxaDriver for what must be filled in and what is optional. * * @return TRUE if UXA was successfully initialized. */ Bool uxa_driver_init(ScreenPtr screen, uxa_driver_t * uxa_driver) { uxa_screen_t *uxa_screen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); if (!uxa_driver) return FALSE; if (uxa_driver->uxa_major != UXA_VERSION_MAJOR || uxa_driver->uxa_minor > UXA_VERSION_MINOR) { LogMessage(X_ERROR, "UXA(%d): driver's UXA version requirements " "(%d.%d) are incompatible with UXA version (%d.%d)\n", screen->myNum, uxa_driver->uxa_major, uxa_driver->uxa_minor, UXA_VERSION_MAJOR, UXA_VERSION_MINOR); return FALSE; } if (!uxa_driver->prepare_solid) { LogMessage(X_ERROR, "UXA(%d): uxa_driver_t::prepare_solid must be " "non-NULL\n", screen->myNum); return FALSE; } if (!uxa_driver->prepare_copy) { LogMessage(X_ERROR, "UXA(%d): uxa_driver_t::prepare_copy must be " "non-NULL\n", screen->myNum); return FALSE; } #if HAS_DIXREGISTERPRIVATEKEY if (!dixRegisterPrivateKey(&uxa_screen_index, PRIVATE_SCREEN, 0)) return FALSE; #endif uxa_screen = calloc(sizeof(uxa_screen_t), 1); if (!uxa_screen) { LogMessage(X_WARNING, "UXA(%d): Failed to allocate screen private\n", screen->myNum); return FALSE; } uxa_screen->info = uxa_driver; dixSetPrivate(&screen->devPrivates, &uxa_screen_index, uxa_screen); uxa_screen->force_fallback = FALSE; uxa_screen->solid_cache_size = 0; uxa_screen->solid_clear = 0; uxa_screen->solid_black = 0; uxa_screen->solid_white = 0; // exaDDXDriverInit(screen); /* * Replace various fb screen functions */ uxa_screen->SavedCloseScreen = screen->CloseScreen; screen->CloseScreen = uxa_close_screen; uxa_screen->SavedCreateGC = screen->CreateGC; screen->CreateGC = uxa_create_gc; uxa_screen->SavedGetImage = screen->GetImage; screen->GetImage = uxa_get_image; uxa_screen->SavedGetSpans = screen->GetSpans; screen->GetSpans = uxa_check_get_spans; uxa_screen->SavedCopyWindow = screen->CopyWindow; screen->CopyWindow = uxa_copy_window; uxa_screen->SavedChangeWindowAttributes = screen->ChangeWindowAttributes; screen->ChangeWindowAttributes = uxa_change_window_attributes; uxa_screen->SavedBitmapToRegion = screen->BitmapToRegion; screen->BitmapToRegion = uxa_bitmap_to_region; uxa_screen->SavedEnableDisableFBAccess = scrn->EnableDisableFBAccess; scrn->EnableDisableFBAccess = uxa_xorg_enable_disable_fb_access; #ifdef RENDER { PictureScreenPtr ps = GetPictureScreenIfSet(screen); if (ps) { uxa_screen->SavedComposite = ps->Composite; ps->Composite = uxa_composite; uxa_screen->SavedCompositeRects = ps->CompositeRects; ps->CompositeRects = uxa_solid_rects; uxa_screen->SavedGlyphs = ps->Glyphs; ps->Glyphs = uxa_glyphs; uxa_screen->SavedUnrealizeGlyph = ps->UnrealizeGlyph; ps->UnrealizeGlyph = uxa_glyph_unrealize; uxa_screen->SavedTriangles = ps->Triangles; ps->Triangles = uxa_triangles; uxa_screen->SavedTrapezoids = ps->Trapezoids; ps->Trapezoids = uxa_trapezoids; uxa_screen->SavedAddTraps = ps->AddTraps; ps->AddTraps = uxa_check_add_traps; } } #endif LogMessage(X_INFO, "UXA(%d): Driver registered support for the following" " operations:\n", screen->myNum); assert(uxa_driver->prepare_solid != NULL); LogMessage(X_INFO, " solid\n"); assert(uxa_driver->prepare_copy != NULL); LogMessage(X_INFO, " copy\n"); if (uxa_driver->prepare_composite != NULL) { LogMessage(X_INFO, " composite (RENDER acceleration)\n"); } if (uxa_driver->put_image != NULL) { LogMessage(X_INFO, " put_image\n"); } if (uxa_driver->get_image != NULL) { LogMessage(X_INFO, " get_image\n"); } return TRUE; } /** * uxa_driver_fini tears down UXA on a given screen. * * @param pScreen screen being torn down. */ void uxa_driver_fini(ScreenPtr pScreen) { /*right now does nothing */ } Bool uxa_resources_init(ScreenPtr screen) { if (!uxa_glyphs_init(screen)) return FALSE; return TRUE; } xserver-xorg-video-qxl-0.1.5+git20200331/src/uxa/uxa.h000066400000000000000000000526401361660122400220320ustar00rootroot00000000000000/* * Copyright © 2000, 2008 Keith Packard * 2004 Eric Anholt * 2005 Zack Rusin * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of copyright holders not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Copyright holders make no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /** @file * UXA - the unified memory acceleration architecture. * * This is the header containing the public API of UXA for uxa drivers. */ #ifndef UXA_H #define UXA_H #include "scrnintstr.h" #include "pixmapstr.h" #include "windowstr.h" #include "gcstruct.h" #include "picturestr.h" #include "fb.h" #define UXA_VERSION_MAJOR 1 #define UXA_VERSION_MINOR 0 #define UXA_VERSION_RELEASE 0 typedef enum { UXA_ACCESS_RO, UXA_ACCESS_RW } uxa_access_t; /** * The UxaDriver structure is allocated through uxa_driver_alloc(), and then * fllled in by drivers. */ typedef struct _UxaDriver { /** * uxa_major and uxa_minor should be set by the driver to the version of * UXA which the driver was compiled for (or configures itself at * runtime to support). This allows UXA to extend the structure for * new features without breaking ABI for drivers compiled against * older versions. */ int uxa_major, uxa_minor; /** * The flags field is bitfield of boolean values controlling UXA's * behavior. * * The flags include UXA_TWO_BITBLT_DIRECTIONS. */ int flags; /** @name solid * @{ */ /** * check_solid() checks whether the driver can do a solid fill to this drawable. * @param pDrawable Destination drawable * @param alu raster operation * @param planemask write mask for the fill * * The check_solid() call is recommended if prepare_solid() is * implemented, but is not required. */ Bool(*check_solid) (DrawablePtr pDrawable, int alu, Pixel planemask); /** * prepare_solid() sets up the driver for doing a solid fill. * @param pPixmap Destination pixmap * @param alu raster operation * @param planemask write mask for the fill * @param fg "foreground" color for the fill * * This call should set up the driver for doing a series of solid fills * through the solid() call. The alu raster op is one of the GX* * graphics functions listed in X.h, and typically maps to a similar * single-byte "ROP" setting in all hardware. The planemask controls * which bits of the destination should be affected, and will only * represent the bits up to the depth of pPixmap. The fg is the pixel * value of the foreground color referred to in ROP descriptions. * * Note that many drivers will need to store some of the data in the * driver private record, for sending to the hardware with each * drawing command. * * The prepare_solid() call is required of all drivers, but it may fail * for any reason. Failure results in a fallback to software rendering. */ Bool(*prepare_solid) (PixmapPtr pPixmap, int alu, Pixel planemask, Pixel fg); /** * solid() performs a solid fill set up in the last prepare_solid() * call. * * @param pPixmap destination pixmap * @param x1 left coordinate * @param y1 top coordinate * @param x2 right coordinate * @param y2 bottom coordinate * * Performs the fill set up by the last prepare_solid() call, * covering the area from (x1,y1) to (x2,y2) in pPixmap. Note that * the coordinates are in the coordinate space of the destination * pixmap, so the driver will need to set up the hardware's offset * and pitch for the destination coordinates according to the pixmap's * offset and pitch within framebuffer. * * This call is required if prepare_solid() ever succeeds. */ void (*solid) (PixmapPtr pPixmap, int x1, int y1, int x2, int y2); /** * done_solid() finishes a set of solid fills. * * @param pPixmap destination pixmap. * * The done_solid() call is called at the end of a series of consecutive * solid() calls following a successful prepare_solid(). This allows * drivers to finish up emitting drawing commands that were buffered, or * clean up state from prepare_solid(). * * This call is required if prepare_solid() ever succeeds. */ void (*done_solid) (PixmapPtr pPixmap); /** @} */ /** @name copy * @{ */ /** * check_copy() checks whether the driver can blit between the two Pictures */ Bool(*check_copy) (PixmapPtr pSrc, PixmapPtr pDst, int alu, Pixel planemask); /** * prepare_copy() sets up the driver for doing a copy within video * memory. - * * @param pSrcPixmap source pixmap * @param pDstPixmap destination pixmap * @param dx X copy direction * @param dy Y copy direction * @param alu raster operation * @param planemask write mask for the fill * * This call should set up the driver for doing a series of copies * from the pSrcPixmap to the pDstPixmap. The dx flag will be * positive if the * hardware should do the copy from the left to the right, and dy will * be positive if the copy should be done from the top to the bottom. * This is to deal with self-overlapping copies when * pSrcPixmap == pDstPixmap. * * If your hardware can only support blits that are (left to right, * top to bottom) or (right to left, bottom to top), then you should * set #UXA_TWO_BITBLT_DIRECTIONS, and UXA will break down copy * operations to ones that meet those requirements. The alu raster * op is one of the GX* graphics functions listed in X.h, and * typically maps to a similar single-byte "ROP" setting in all * hardware. The planemask controls which bits of the destination * should be affected, and will only represent the bits up to the * depth of pPixmap. * * Note that many drivers will need to store some of the data in the * driver private record, for sending to the hardware with each * drawing command. * * The prepare_copy() call is required of all drivers, but it may fail * for any reason. Failure results in a fallback to software rendering. */ Bool(*prepare_copy) (PixmapPtr pSrcPixmap, PixmapPtr pDstPixmap, int dx, int dy, int alu, Pixel planemask); /** * copy() performs a copy set up in the last prepare_copy call. * * @param pDstPixmap destination pixmap * @param srcX source X coordinate * @param srcY source Y coordinate * @param dstX destination X coordinate * @param dstY destination Y coordinate * @param width width of the rectangle to be copied * @param height height of the rectangle to be copied. * * Performs the copy set up by the last prepare_copy() call, copying the * rectangle from (srcX, srcY) to (srcX + width, srcY + width) in the * source pixmap to the same-sized rectangle at (dstX, dstY) in the * destination pixmap. Those rectangles may overlap in memory, if * pSrcPixmap == pDstPixmap. Note that this call does not receive the * pSrcPixmap as an argument -- if it's needed in this function, it * should be stored in the driver private during prepare_copy(). As * with solid(), the coordinates are in the coordinate space of each * pixmap, so the driver will need to set up source and destination * pitches and offsets from those pixmaps, probably using * uxaGetPixmapOffset() and uxa_get_pixmap_pitch(). * * This call is required if prepare_copy ever succeeds. */ void (*copy) (PixmapPtr pDstPixmap, int srcX, int srcY, int dstX, int dstY, int width, int height); /** * done_copy() finishes a set of copies. * * @param pPixmap destination pixmap. * * The done_copy() call is called at the end of a series of consecutive * copy() calls following a successful prepare_copy(). This allows * drivers to finish up emitting drawing commands that were buffered, * or clean up state from prepare_copy(). * * This call is required if prepare_copy() ever succeeds. */ void (*done_copy) (PixmapPtr pDstPixmap); /** @} */ /** @name composite * @{ */ /** * check_composite() checks to see if a composite operation could be * accelerated. * * @param op Render operation * @param pSrcPicture source Picture * @param pMaskPicture mask picture * @param pDstPicture destination Picture * @param width The width of the composite operation * @param height The height of the composite operation * * The check_composite() call checks if the driver could handle * acceleration of op with the given source, mask, and destination * pictures. This allows drivers to check source and destination * formats, supported operations, transformations, and component * alpha state, and send operations it can't support to software * rendering early on. * * See prepare_composite() for more details on likely issues that * drivers will have in accelerating composite operations. * * The check_composite() call is recommended if prepare_composite() is * implemented, but is not required. */ Bool(*check_composite) (int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture, int width, int height); /** * check_composite_target() checks to see if the destination of the composite * operation can be used without midification. * * @param pixmap Destination Pixmap * * The check_composite_target() call is recommended if prepare_composite() is * implemented, but is not required. */ Bool(*check_composite_target) (PixmapPtr pixmap); /** * check_composite_texture() checks to see if a source to the composite * operation can be used without midification. * * @param pScreen Screen * @param pPicture Picture * * The check_composite_texture() call is recommended if prepare_composite() is * implemented, but is not required. */ Bool(*check_composite_texture) (ScreenPtr pScreen, PicturePtr pPicture); /** * prepare_composite() sets up the driver for doing a composite * operation described in the Render extension protocol spec. * * @param op Render operation * @param pSrcPicture source Picture * @param pMaskPicture mask picture * @param pDstPicture destination Picture * @param pSrc source pixmap * @param pMask mask pixmap * @param pDst destination pixmap * * This call should set up the driver for doing a series of composite * operations, as described in the Render protocol spec, with the given * pSrcPicture, pMaskPicture, and pDstPicture. The pSrc, pMask, and * pDst are the pixmaps containing the pixel data, and should be used * for setting the offset and pitch used for the coordinate spaces for * each of the Pictures. * * Notes on interpreting Picture structures: * - The Picture structures will always have a valid pDrawable. * - The Picture structures will never have alphaMap set. * - The mask Picture (and therefore pMask) may be NULL, in which case * the operation is simply src OP dst instead of src IN mask OP dst, * and mask coordinates should be ignored. * - pMarkPicture may have componentAlpha set, which greatly changes * the behavior of the composite operation. componentAlpha has no * effect when set on pSrcPicture or pDstPicture. * - The source and mask Pictures may have a transformation set * (Picture->transform != NULL), which means that the source * coordinates should be transformed by that transformation, * resulting in scaling, rotation, etc. The PictureTransformPoint() * call can transform coordinates for you. Transforms have no * effect on Pictures when used as a destination. * - The source and mask pictures may have a filter set. * PictFilterNearest and PictFilterBilinear are defined in the * Render protocol, but others may be encountered, and must be * handled correctly (usually by prepare_composite failing, and * falling back to software). Filters have * no effect on Pictures when used as a destination. * - The source and mask Pictures may have repeating set, which must be * respected. Many chipsets will be unable to support repeating on * pixmaps that have a width or height that is not a power of two. * * If your hardware can't support source pictures (textures) with * non-power-of-two pitches, you should set #UXA_OFFSCREEN_ALIGN_POT. * * Note that many drivers will need to store some of the data in the * driver private record, for sending to the hardware with each * drawing command. * * The prepare_composite() call is not required. However, it is highly * recommended for performance of antialiased font rendering and * performance of cairo applications. Failure results in a fallback * to software rendering. */ Bool(*prepare_composite) (int op, PicturePtr pSrcPicture, PicturePtr pMaskPicture, PicturePtr pDstPicture, PixmapPtr pSrc, PixmapPtr pMask, PixmapPtr pDst); /** * composite() performs a composite operation set up in the last * prepare_composite() call. * * @param pDstPixmap destination pixmap * @param srcX source X coordinate * @param srcY source Y coordinate * @param maskX source X coordinate * @param maskY source Y coordinate * @param dstX destination X coordinate * @param dstY destination Y coordinate * @param width destination rectangle width * @param height destination rectangle height * * Performs the composite operation set up by the last * prepare_composite() call, to the rectangle from (dstX, dstY) to * (dstX + width, dstY + height) in the destination Pixmap. Note that * if a transformation was set on the source or mask Pictures, the * source rectangles may not be the same size as the destination * rectangles and filtering. Getting the coordinate transformation * right at the subpixel level can be tricky, and rendercheck * can test this for you. * * This call is required if prepare_composite() ever succeeds. */ void (*composite) (PixmapPtr pDst, int srcX, int srcY, int maskX, int maskY, int dstX, int dstY, int width, int height); /** * done_composite() finishes a set of composite operations. * * @param pPixmap destination pixmap. * * The done_composite() call is called at the end of a series of * consecutive composite() calls following a successful * prepare_composite(). This allows drivers to finish up emitting * drawing commands that were buffered, or clean up state from * prepare_composite(). * * This call is required if prepare_composite() ever succeeds. */ void (*done_composite) (PixmapPtr pDst); /** @} */ /** * put_image() loads a rectangle of data from src into pDst. * * @param pDst destination pixmap * @param x destination X coordinate. * @param y destination Y coordinate * @param width width of the rectangle to be copied * @param height height of the rectangle to be copied * @param src pointer to the beginning of the source data * @param src_pitch pitch (in bytes) of the lines of source data. * * put_image() copies data in system memory beginning at src (with * pitch src_pitch) into the destination pixmap from (x, y) to * (x + width, y + height). This is typically done with hostdata * uploads, where the CPU sets up a blit command on the hardware with * instructions that the blit data will be fed through some sort of * aperture on the card. * * put_image() is most important for the performance of uxa_glyphs() * (antialiased font drawing) by allowing pipelining of data uploads, * avoiding a sync of the card after each glyph. * * @return TRUE if the driver successfully uploaded the data. FALSE * indicates that UXA should fall back to doing the upload in software. * * put_image() is not required, but is recommended if composite * acceleration is supported. */ Bool(*put_image) (PixmapPtr pDst, int x, int y, int w, int h, char *src, int src_pitch); /** * get_image() loads a rectangle of data from pSrc into dst * * @param pSrc source pixmap * @param x source X coordinate. * @param y source Y coordinate * @param width width of the rectangle to be copied * @param height height of the rectangle to be copied * @param dst pointer to the beginning of the destination data * @param dst_pitch pitch (in bytes) of the lines of destination data. * * get_image() copies data from offscreen memory in pSrc from * (x, y) to (x + width, y + height), to system memory starting at * dst (with pitch dst_pitch). This would usually be done * using scatter-gather DMA, supported by a DRM call, or by blitting * to AGP and then synchronously reading from AGP. * * @return TRUE if the driver successfully downloaded the data. FALSE * indicates that UXA should fall back to doing the download in * software. * * get_image() is not required, but is highly recommended. */ Bool(*get_image) (PixmapPtr pSrc, int x, int y, int w, int h, char *dst, int dst_pitch); /** @{ */ /** * prepare_access() is called before CPU access to an offscreen pixmap. * * @param pPix the pixmap being accessed * @param index the index of the pixmap being accessed. * * prepare_access() will be called before CPU access to an offscreen * pixmap. * * This can be used to set up hardware surfaces for byteswapping or * untiling, or to adjust the pixmap's devPrivate.ptr for the purpose of * making CPU access use a different aperture. * * The index is one of #UXA_PREPARE_DEST, #UXA_PREPARE_SRC, or * #UXA_PREPARE_MASK, indicating which pixmap is in question. Since * only up to three pixmaps will have prepare_access() called on them * per operation, drivers can have a small, statically-allocated space * to maintain state for prepare_access() and finish_access() in. * Note that the same pixmap may have prepare_access() called on it * more than once, for uxample when doing a copy within the same * pixmap (so it gets prepare_access as * #UXA_PREPARE_DEST and then as #UXA_PREPARE_SRC). * * prepare_access() may fail. An example might be the case of * hardware that can set up 1 or 2 surfaces for CPU access, but not * 3. If prepare_access() * fails, UXA will migrate the pixmap to system memory. * get_image() must be implemented and must not fail if a driver * wishes to fail in prepare_access(). prepare_access() must not * fail when pPix is the visible screen, because the visible screen * cannot be migrated. * * @return TRUE if prepare_access() successfully prepared the pixmap * for CPU drawing. * @return FALSE if prepare_access() is unsuccessful and UXA should use * get_image() to migate the pixmap out. */ Bool(*prepare_access) (PixmapPtr pPix, RegionPtr region, uxa_access_t access); /** * finish_access() is called after CPU access to an offscreen pixmap. * * @param pPix the pixmap being accessed * @param index the index of the pixmap being accessed. * * finish_access() will be called after finishing CPU access of an * offscreen pixmap set up by prepare_access(). Note that the * finish_access() will not be called if prepare_access() failed. */ void (*finish_access) (PixmapPtr pPix); /** * PixmapIsOffscreen() is an optional driver replacement to * uxa_pixmap_is_offscreen(). Set to NULL if you want the standard * behaviour of uxa_pixmap_is_offscreen(). * * @param pPix the pixmap * @return TRUE if the given drawable is in framebuffer memory. * * uxa_pixmap_is_offscreen() is used to determine if a pixmap is in * offscreen memory, meaning that acceleration could probably be done * to it, and that it will need to be wrapped by * prepare_access()/finish_access() when accessing it with the CPU. */ Bool(*pixmap_is_offscreen) (PixmapPtr pPix); /** @} */ } uxa_driver_t; /** @name UXA driver flags * @{ */ /** * UXA_TWO_BITBLT_DIRECTIONS indicates to UXA that the driver can only * support copies that are (left-to-right, top-to-bottom) or * (right-to-left, bottom-to-top). */ #define UXA_TWO_BITBLT_DIRECTIONS (1 << 2) /** @} */ /** @name UXA CreatePixmap hint flags * @{ */ /** * Flag to hint that the first operation on the pixmap will be a * prepare_access. */ #define UXA_CREATE_PIXMAP_FOR_MAP 0x20000000 /** @} */ uxa_driver_t *uxa_driver_alloc(void); Bool uxa_driver_init(ScreenPtr screen, uxa_driver_t * uxa_driver); Bool uxa_resources_init(ScreenPtr screen); void uxa_driver_fini(ScreenPtr pScreen); CARD32 uxa_get_pixmap_first_pixel(PixmapPtr pPixmap); Bool uxa_get_color_for_pixmap (PixmapPtr pixmap, CARD32 src_format, CARD32 dst_format, CARD32 *pixel); void uxa_set_fallback_debug(ScreenPtr screen, Bool enable); void uxa_set_force_fallback(ScreenPtr screen, Bool enable); Bool uxa_swapped_out (ScreenPtr screen); /** * Returns TRUE if the given planemask covers all the significant bits in the * pixel values for pDrawable. */ #define UXA_PM_IS_SOLID(_pDrawable, _pm) \ (((_pm) & FbFullMask((_pDrawable)->depth)) == \ FbFullMask((_pDrawable)->depth)) #endif /* UXA_H */ xserver-xorg-video-qxl-0.1.5+git20200331/tests/000077500000000000000000000000001361660122400206335ustar00rootroot00000000000000xserver-xorg-video-qxl-0.1.5+git20200331/tests/xspice_audio_test.py000077500000000000000000000006141361660122400247240ustar00rootroot00000000000000#!/usr/bin/python from time import sleep from xspice_audio_test_helper import produce_audio from xspice_util import launch_xspice, launch_client def main(): port = 8000 xspice = launch_xspice(port) sleep(2) client = launch_client(port) sleep(1) produce_audio(xspice.audio_fifo_dir) sleep(2) client.kill() xspice.kill() if __name__ == '__main__': main() xserver-xorg-video-qxl-0.1.5+git20200331/tests/xspice_audio_test_helper.py000077500000000000000000000012221361660122400262570ustar00rootroot00000000000000#!/usr/bin/python # coding: utf-8 import os import sys import struct from math import sin, pi def produce_audio(basedir): filename = os.path.join(basedir, 'testaudio') os.system(u'mkfifo %s' % filename) if not os.path.exists(basedir): print "missing fifo dir %s" % repr(basedir) f=open(filename,'w') singen = lambda f: lambda t: (int(min(32767, 32768*sin(t*f/1000.0*pi))), int(min(32767, 32768*sin(t*f/1000.0*pi)))) f.write(''.join( struct.pack('hh', *singen(40)(t)) for t in xrange(44100) ) ) os.unlink(filename) if __name__ == '__main__': produce_audio(sys.argv[-1] if len(sys.argv) > 1 else '/tmp/xspice-audio/') xserver-xorg-video-qxl-0.1.5+git20200331/tests/xspice_util.py000077500000000000000000000036701361660122400235460ustar00rootroot00000000000000#!/usr/bin/python import os from time import sleep import subprocess import atexit class Process(object): processes = [] @classmethod def new(clazz, *args, **kw): clazz.processes.append(subprocess.Popen(*args, **kw)) return clazz.processes[-1] @classmethod def atexit(clazz): for p in reversed(clazz.processes): print "child %s" % p.pid if False: slp = subprocess.Popen(['/usr/bin/sleep', '10000']) print "wait on %d" % slp.pid slp.wait() if len(clazz.processes) == 0: return for p in reversed(clazz.processes): print "kill %s" % p.pid try: p.kill() except: pass if not any(p.poll() for p in clazz.processes): return sleep(1) for p in reversed(clazz.processes): if not p.poll(): print "terminate %s" % p.pid try: p.terminate() except: pass atexit.register(Process.atexit) def which(prog): for path_element in os.environ['PATH'].split(':'): candidate = os.path.join(path_element, prog) if os.path.exists(candidate): return candidate return None client_executable = which('remote-viewer') if not client_executable: raise SystemExit('missing remote-viewer in path') def launch_xspice(port): basedir = '/tmp/xspice_test_audio' if not os.path.exists(basedir): os.mkdir(basedir) assert(os.path.exists(basedir)) xspice = Process.new(['../scripts/Xspice', '--port', str(port), '--auto', '--audio-fifo-dir', basedir, '--disable-ticketing', ':15.0']) xspice.audio_fifo_dir = basedir return xspice def launch_client(port): client = Process.new([client_executable, 'spice://localhost:%s' % port]) return client if __name__ == '__main__': launch_xspice(port=8000)